From 7ed5345e74d8abc8ca2d249c21148491a513a702 Mon Sep 17 00:00:00 2001 From: John Wiseman Date: Fri, 28 Mar 2025 21:40:36 +0000 Subject: [PATCH] 6.0.24.69 --- ...ec6a47e062a4f7f1c098c31c1e17d2eae.svn-base | 3696 +- ...783c5b8e5a6c55b1369dfe5459e2f71f2.svn-base | 486 +- ...4258aeb1aaca3b61c3205e84a4ae6371b.svn-base | 222 +- ...1a9a62b41bdee83bd30d4b81006bcecf1.svn-base | 3120 +- ...19d295623ca3bf53396185358ecec2f30.svn-base | 462 +- ...525d8fb5c544330e2b48b108f54a00e3d.svn-base | 142 +- ...3de728f7306ffe553ec00689c462798f9.svn-base | 3352 +- ...3d55be8cd16e65e2edc8dcf1aab6277a1.svn-base | 6286 +-- ...9ff4898bc77bda06f9cff0f0a53df0c80.svn-base | 398 +- ...6b3e6a6eafccc305825e33cd8c8621f75.svn-base | 9166 ++--- ...4fc9a479a4b97e349b069851ef8cd3fc2.svn-base | 270 +- ...7593ec9c79302aca324e8bd42b0ed4e56.svn-base | 13502 +++---- ...e1866e8f5b2214eab03930e5891967d4f.svn-base | 848 +- ...1861e575d751841ff418413f30b2490aa.svn-base | 9102 ++--- ...e8ae70b991986eaaa9e579a14e878d39f.svn-base | 3304 +- ...a079aeb6cc3c978273fe57af90ded386a.svn-base | 3712 +- ...978fa349027d73174ce2a1e0fd2560283.svn-base | 1928 +- ...013214695f1c2c29f5492cd4f17755b62.svn-base | 44 +- ...b03646836ba24c40da664584b044dd1fc.svn-base | 164 +- ...25d8096af91b6003047c846fb996c508a.svn-base | 4402 +-- ...35d73a332d9a80b3904b723c535bcd9c1.svn-base | 438 +- ...f5aabaa27f5c011eb6fba97496d3ca296.svn-base | 2658 +- ...6660d3eb378f376ef8fa9a0acfad8af7a.svn-base | 474 +- ...d90c53fc02930e5a4a63a91f73204a63a.svn-base | 1976 +- ...4beec11a2c197551503067c2e0ff303a1.svn-base | 90 +- ...4536f7829e65ade1ec3c7a09edd84765d.svn-base | 3814 +- ...8aa8a9b46d989b00b887bcc82fd050d7a.svn-base | 1412 +- ...e0b72cb1376626ed0c9e0e37f9edb65c4.svn-base | 6465 ++++ ...88c0f452c2993299caa5d0cd96da1e8d9.svn-base | 3028 +- ...1a59761e7403099936a17146f4e764172.svn-base | 3220 +- ...d0fad7f42f44a09f68afaa938d90fbc93.svn-base | 2574 +- ...7f9da54ae568c4610eb9d64a2dca18075.svn-base | 380 +- ...2974a7cafbcfdcf70890fae584e4def61.svn-base | 798 +- ...ef566540674fb61ab096d58e5c4dcda69.svn-base | 1204 +- ...958d95c251edd4e02577dcfaa6e219619.svn-base | 250 +- ...246cafe227e02586bf3fd98dda0a476eb.svn-base | 3440 +- ...4c88e7e28f322dfadaad63b36d53bbdf5.svn-base | 40 +- ...10373273fcceb1edcbff19b7f253dbcb3.svn-base | 2980 +- ...1defb7927e51be30ee9cfb4231368e7aa.svn-base | 446 +- ...e9d6a2a3cfdf8eb22dcc7d5ecb7d02723.svn-base | 11700 +++--- ...ff9874914969464dfcddd2892a304118d.svn-base | 2179 ++ ...5e8af7548d8963b9fbca1744118169e60.svn-base | 136 +- ...9b44371b4549b7942ae8eb8c94fbf6754.svn-base | 142 +- ...6dcda9b9b4a0602d14ab046e62d27818b.svn-base | 1936 +- ...b0d99b917501d60345425a3cdb5f8b794.svn-base | 1156 +- ...01636a643d41ed32ea57b94ee15224e86.svn-base | 1308 +- ...032114a3cd15551100fce50097c1a52a3.svn-base | 1144 +- ...c19911fc81af53c19f4fba3e074f7f0cc.svn-base | 130 +- ...f4ae824ccda85ec76fa6d085b8bf2a8f0.svn-base | 1680 +- ...20b3dcb5480289ddc559e08196347c052.svn-base | 124 +- ...64148305a76e411fb064479a2c2461872.svn-base | 82 +- ...3fe6e7d0580931894198b0468dbc0ff02.svn-base | 868 +- ...dd0ba153cdd0cdefd6afbf91f5c308631.svn-base | 458 +- ...ed54be8c1bdae4ab00a9a94641691b9c0.svn-base | 3240 +- ...50a5f41b2b2a4efa5caea0caa40d24084.svn-base | 386 +- ...afbaeda649d1ccf0e5299f0773143ff4d.svn-base | 31868 ++++++++-------- ...70d68e89d8d3544729e7a81d5d2178a11.svn-base | 6344 +-- ...6747e7365bac8d7147933abf30a627270.svn-base | 1346 +- ...9c63e4ced75d5879b2622bb44addc8eba.svn-base | 12866 +++---- ...c0f944271559fe515c79887edfbf4e58f.svn-base | 3462 +- ...533e0ceec265db1cfb113f0a2c9ff72ca.svn-base | 1500 +- ...ea156cd928626feb7046dab8dae8bb9b7.svn-base | 864 +- ...8b6be34db07679db777d6dd18a5f1e709.svn-base | 18450 ++++----- ...d04d8b99a143f1b8c9d89c80a40119ff6.svn-base | 11742 +++--- ...84118da08690f9f31593baf88e3087406.svn-base | 910 +- ...e26a2bde01b2f65c4d63bf015d20de865.svn-base | 6234 +-- ...a7dfe998700e61ca2ab05d19f8a20efa4.svn-base | 3108 +- ...802995891cc71bd70f75248612a42fbf9.svn-base | 10366 ++--- ...9c3ad72e785ad5cd64b6a65acf68aaf1f.svn-base | 3332 +- ...8f5cd6816e967bb11684382274ca29881.svn-base | 1826 +- ...b8ae964f75a7534193b4fec7e4e89a309.svn-base | 21010 +++++----- ...dcd812375e98c9979c2d0124116c9b402.svn-base | 294 +- ...78f986f01be589e9bc858f3b5c3192cc6.svn-base | 1974 +- ...43d5e4708e2726a75e32f6bc05c254e92.svn-base | 5618 +-- ...002d31c4a18f25bcb2dc2bc8c825c37c0.svn-base | 3600 +- ...9d12b536369a2d1328a95ffc5063f1284.svn-base | 118 +- ...eae965003bd581c06a45005d76c51491c.svn-base | 64 +- ...f220dcdbebe6f1bce5dc07f1555aaf463.svn-base | 3688 +- ...b1a049395a1e7c3cec2f3096f674dd935.svn-base | 6678 ++-- ...a19b271799ebffb67ffb68fa72e9e66ee.svn-base | 3530 +- ...116d4883496ff1d52b8178d600ee5c5f5.svn-base | 58 +- ...0d9c555171d7b3b6720f74d2f90cf4afc.svn-base | 6910 ++-- ...38b142c4a6d93af6a6d0b68e17b3e68a6.svn-base | 3114 +- ...22ab7d38afa5dce4d1b4fd14822a9f3d5.svn-base | 2934 +- ...393198d8536d0b23a74c1560215b2b693.svn-base | 680 +- ...b6492bc4d49d3f6b8f0fbb0f250776fc2.svn-base | 1182 + ...c3b43670977b4daa03f05596f67ac4941.svn-base | 2270 +- ...61f1f5939e66004e769a816b5c28f9907.svn-base | 6244 +-- ...f8f487b8936b3931a13f84cccbd62335f.svn-base | 3810 +- ...c5d5f8aa880e881391f05668625bf23d3.svn-base | 14370 +++---- ...7fa6a02765ac89b40ca4dffa3b1f95c8a.svn-base | 64 +- ...7c086ef89df25fa16ce36362d4ee4c8dd.svn-base | 8738 ++--- ...eb6662c51d5cc27b12a29e271c9436d2a.svn-base | 18444 ++++----- ...bb3d447af83958f365655048d0a2d2adb.svn-base | 460 +- ...61072dddd0ba0a93abb8cfc407fe961b9.svn-base | 96 +- ...c34a423e4ad51456cb4aebe961ff91f38.svn-base | 4754 +-- ...ca71cf31e07e2ceca7a24d40506cb14d2.svn-base | 46 +- ...d29fd0ceb285dc7f74ac10e124d924e7a.svn-base | 812 +- ...7fd9329d88b41f4a233ee9e9dddfca8ea.svn-base | 126 +- ...875f703bdfb3fadbef68c402f2992afc3.svn-base | 31868 ++++++++-------- ...384e74f7d8289e97e8f6a16d891fb3edf.svn-base | 10572 ++--- ...9b78754181ecbe72395a1461c981a146a.svn-base | 7930 ++-- ...eaa9b4bb07b019e931a9652ca162c97cf.svn-base | 3738 ++ ...9bebe4a04aa92870a68fe7cc4b148007b.svn-base | 4308 +-- ...108fa7fe05fa63e7648cbea6e1b656b1e.svn-base | 900 +- ...82b027c59133143d65476478c5087911a.svn-base | 3254 +- ...6b78020e00b54e3e16146bf66c1df728c.svn-base | 9102 ++--- ...379015ba0f5eb82e31ee59f95954d5d07.svn-base | 3118 +- ...6a883df8b69e1305d32be8ec239af0906.svn-base | 12872 +++---- ...f8ff6238683e574f1e7ab69e9cb62b75d.svn-base | 12670 +++--- ...a54bc0fb67589918999c034177f771774.svn-base | 248 +- ...5d592bc137ef999087fcb7563bfd56754.svn-base | 408 +- ...7368a2a606d45b2867c443d3940a89ead.svn-base | 8366 ++-- ...64a1922bbb87b8d6993512d32803d1e06.svn-base | 14378 +++---- ...46aa89f0ab6061df704ffdb0605b9262a.svn-base | 1512 +- ...eb241e6e692683c60a59ab822444e0e71.svn-base | 5314 +-- ...d8af886b0f39039fd87bed0edf1bf332e.svn-base | 4260 +-- ...1f245f61f7594ba2efdb2df59892e4536.svn-base | 748 +- ...d9842da716a1fc97bd1dc8242754b7cbb.svn-base | 8744 ++--- ...4f1057a429b62f8c4cfeb694594aebf9e.svn-base | 3428 +- ...01b830a929f2dbea7b30524b79b93beee.svn-base | 6242 +-- ...63e5606a646809b8809054112b6617881.svn-base | 5674 +-- ...221936ab9d8860adeec25b105aca40538.svn-base | 414 +- ...6e25e931eb307d0d5e6038763954edd02.svn-base | 402 +- ...7e3206fe1e66791c05c3cf7f31b24ccdb.svn-base | 5762 +-- ...f25c1a7c2b76a9d2712c1775b2c1692aa.svn-base | 21022 +++++----- ...c6ebdd6f7911227c29d2ec894198eed8c.svn-base | 3248 +- ...83d5437dd5eed1ff09f6e5be43d327916.svn-base | 274 +- ...a22402c21c6afb0c32ea0bcad5b484818.svn-base | 4032 +- ...7027a699172386f0b8291ee7d2d60c56c.svn-base | 866 +- ...25f8263c54ea5116ea0a8e36bc9e8a8ab.svn-base | 416 +- ...95a74275dae71ad1eb436b347e70eefe7.svn-base | 2378 +- ...bfaf21d049d6fa71f4fa4818ac8a4dc49.svn-base | 12612 +++--- ...7df565cfafc821a72a552ff8b77c93aad.svn-base | 1324 +- ...082281408be8a877a994df2167455456a.svn-base | 118 +- ...8f946918abd0bce6839b2291eb560d49b.svn-base | 456 +- ...deb223b884b149be3f943302ddbb3ba6f.svn-base | 12662 +++--- ...501c1eda5e39f43025b1018867b1cd89e.svn-base | 856 +- ...2568cb2c9564f3af21a70c5dc4d1b19c6.svn-base | 11322 +++--- ...7468aeb4dc0f0f8c9d1c59c5c3b3bb285.svn-base | 346 +- ...44b299049b9b5811f1e18cfb6e4a594b1.svn-base | 450 +- ...7572d867dc7dee4a7af939b872a1a42e9.svn-base | 492 +- ...2faeeafba5f108aa4d83b22c70b1dc34a.svn-base | 46 +- ...afea0ed89e20a3d6ccae78a312b140c09.svn-base | 1744 +- ...a9af0b528fc3def5ccffef5c0f78ae81a.svn-base | 4172 +- ...ebc973deb75bf1378e2e350a72b369c21.svn-base | 1658 + ...0bc0e745d3dca66a9762242c6dac82d4c.svn-base | 1693 + ...5a36d79877c048e3cf7192fc4e55960c5.svn-base | 354 +- ...ae99f9d6ec74378b5ad0523aae2f170b7.svn-base | 854 +- ...0c0df24fedf103adcb0e542b35c109df0.svn-base | 8278 ++-- ...16605584c36262c7c751e8c3a2a2d2fc5.svn-base | 126 + ...92396d8df8a11bfbc1f32ea7eaddb6a1c.svn-base | 470 +- ...f7a302f3937ba99deeafe3df7812c6331.svn-base | 986 +- ...21adc59b73cb71b674d29dc25fd41348a.svn-base | 3392 +- ...503dac33e44480e1b206ae77184a240ed.svn-base | 1669 + ...e3ceff994bcfc0c0d68244262df1511ec.svn-base | 7646 ++-- ...3d0a86281ea3312af8210f650c697be78.svn-base | 10912 +++--- ...232b36b3cfffdfa06648f32c31fcf8209.svn-base | 548 +- ...9fa5539284e41bcd4b77b73bd892cd8c5.svn-base | 1120 +- ...e6eca99f723ce71a80087186e739eca94.svn-base | 6596 ++-- ...8eafbd55af1fab1ad3e9120e3fe57418c.svn-base | 326 +- ...b1594a2d27f0bc65ff58fc628760b9555.svn-base | 3292 +- ...62527676aaa7acf31fd2106fbd4f8c918.svn-base | 1232 +- ...5dfba31121f0a5c8db349daaf318aaff8.svn-base | 306 +- ...f9934956b98fd073be6c37c09544bda22.svn-base | 1428 +- ...9c5d27b8d6356c3ede70be2bfeb390bd9.svn-base | 1562 +- ...543f5bb7bd5fd97bd2de99f7b38ead2e9.svn-base | 902 +- ...4cb2a14e81ada7714f7a3a91b5cfb306b.svn-base | 326 +- ...dfbb69d85ba868088ac9e2c5b5df0814d.svn-base | 148 +- ...8b2d80d7f0426d70bc94b5f134df091ea.svn-base | 1742 +- ...2711252e160ee27d91c08b2195d73476d.svn-base | 7460 ++-- ...b3718506f8e1506fbebe60aba09ef72ce.svn-base | 4514 +-- ...d4cc301e9fb6dd3c17d2f52f43671e00f.svn-base | 4402 +-- ...7564852c7dbbbd2c3bd5ce38859e0d626.svn-base | 6644 ++-- ...e01ca392203f905b243390b31ed14704e.svn-base | 1621 + ...977b8ee5d91f5335a2facde559e7b13b0.svn-base | 1392 +- ...d72c4365a4961b57503316bb901694b16.svn-base | 3086 +- ...af059df80cbc1be6b555e167bc3855ede.svn-base | 12 +- ...654206e06790768565f0061330a45b86a.svn-base | 270 +- ...65f80f78935c36639c62f927b9ba99056.svn-base | 1294 +- ...b13c7893f88ca3c614fbb6d8bb5b7ea3f.svn-base | 8278 ++-- ...e8bcbb0855a4cb7f568c92348d300bcf1.svn-base | 148 +- ...e9faea0753a62de1ecda146486c2e5478.svn-base | 2208 +- ...9dd89f3745784af2d042b28b7c71cb78d.svn-base | 9708 ++--- ...c282aa7469c44bc66be3a3a3c9d0ea501.svn-base | 1520 +- ...e09eb6744209f09fd38e64dd3d20f3577.svn-base | 15970 ++++++++ ...8a7e742ec78c3d0d226dace89ca8ddeff.svn-base | 6280 +-- ...f50e1774db1019d439026ef54c2aaa902.svn-base | 406 +- ...cf1479870d3498f294212975127fc1710.svn-base | 7462 ++-- ...99915b967a2888cbcd58d2b3890491fd9.svn-base | 74 +- ...28778f2363c30502899c6ec654798eb29.svn-base | 666 +- ...148f7ebf98001a6a3b3a89e59120c0e5e.svn-base | 5648 +-- ...ce0321e48fcc96f1f25d1adea58dbc3a2.svn-base | 128 +- ...f7a36508cb7aee91de09a85b75aa73e0e.svn-base | 640 +- ...372966c2566a9e64323c810705aded75a.svn-base | 3524 +- ...5bd2c1a652796e98b7000602047949658.svn-base | 4780 +-- ...a8d7cb500b60c0dd9666cd4ed861a2aef.svn-base | 214 +- ...75c61d3d89adb15380d5648f639d6cbaa.svn-base | 986 +- ...5b0ae6046204c3cd402364dd5629bcaca.svn-base | 252 +- ...855d1859f1b4a54225825619792e6d779.svn-base | 418 +- ...4c1c9798c5c85fcbfec6a6233df1a2a4a.svn-base | 1282 +- ...937d653f4fe305ccf540243e484d00ae6.svn-base | 3410 +- ...b3f144aa11735bde7b6b330601821627a.svn-base | 146 +- ...fe8f27a50245a28052aff7956822da9b4.svn-base | 6676 ++-- ...f842784be101f70173e44c694cc77259d.svn-base | 11310 +++--- ...1e794c1c3327a48f6558f238115107ce5.svn-base | 262 +- ...31b4682f18a93ea56b56a214fc62a6149.svn-base | 104 +- ...b5a2eecc154a0ad46b569c0c3fde4bd74.svn-base | 1378 +- ...c4d28f84ae9afab743b1a08ce4ecb0ffa.svn-base | 104 +- ...778994c1270ff76b5ff89e58a5bc6e07a.svn-base | 3190 ++ ...a276887f6c9d0cfe99e42b72e9b2f8598.svn-base | 2160 ++ ...91b358d610e97a6748e708b125ef4ee4f.svn-base | 7778 ++-- ...b804499c6c0ff87d703ae0e4526439572.svn-base | 10912 +++--- ...600dff374b406773b849e51cecf89ad8f.svn-base | 972 +- ...259a785e24e16f3cbd0767b17f600d24f.svn-base | 84 +- ...a3e747aa86130df603ecddfa2ad60c55a.svn-base | 4762 +-- ...ecaf26f410b709102fb6b71bbd881eb4c.svn-base | 4198 +- ...db18fa13fe4cce1041ec1ba37504094e6.svn-base | 108 +- ...0c17c7aefb110c94335d296b5cc43609e.svn-base | 3228 +- ...4c533f6fcc707cfc8c1266a9b1b9a8b76.svn-base | 2264 +- ...703ab695870c5fd90f37d4e4cd9a5b80b.svn-base | 272 +- ...41b2299a89070fc4fd808e124e3a301f9.svn-base | 2774 +- ...74e0fe957ebc4db8fa10d36494e9285eb.svn-base | 6612 ++-- ...4cd3ea6a05c35daa45579adbda78ae32d.svn-base | 1230 +- ...aef1b461bf361affe8cc048cb65b6eb54.svn-base | 468 +- ...932bbd8d092047830db50888d397658d1.svn-base | 1650 +- ...473947268d732710e94737f6a7b9ae0b9.svn-base | 514 +- ...c69571c32f65d2ed9b109c965f67b81c8.svn-base | 7938 ++-- ...1472f7860cc91190138a10b0ad92e2aef.svn-base | 1516 +- ...05618159730ac44abb07bde73f82ddb2a.svn-base | 3672 +- ...f4ea2f13fc750fea8e1f21147736f7387.svn-base | 2906 +- ...9bafc7411ef908979c809d0bc377ea2d2.svn-base | 746 +- ...d6eb4c9ff0cffc1d6f037f59183afeb3b.svn-base | 394 +- ...7f2df720fcc7499c1d8c553b1920754db.svn-base | 1156 +- ...124e99121f2ac19e703f29da71cad0b1e.svn-base | 250 +- ...74fea7f7b6d1fb3ded6cd613b873cb630.svn-base | 13516 +++---- ...d5f1c46571c49ef3d3b47c0c140f41ec7.svn-base | 3442 +- ...377e2f45c35b8512134075fcab9916e7f.svn-base | 1394 +- ...63dcf675269808788b0d159d91f1510ab.svn-base | 412 +- ...00c64491caa3d0ae12cb377ee421e1370.svn-base | 8654 ++--- ...0a76819ed97ff307fdf71c0cd6e6ee501.svn-base | 9102 ++--- ...9153d8b58f408d51ec8bbda0c8ed2971d.svn-base | 8926 ++--- ...875af83140a4070c652e6ac2d51dbb9f3.svn-base | 2198 ++ ...56306a57e65c71ba7955f40fecfcfdbf2.svn-base | 782 +- ...41e548a2f315b5a05341456f858c6356d.svn-base | 6588 ++-- ...0f31ed9103d87c90535bee3d4e3c8cf45.svn-base | 7720 ++-- ...9e59f881b8dbb3ec1e607349be12f161e.svn-base | 68 +- ...d6ff00ac816011723a8a93563c433acd8.svn-base | 818 +- .svn/wc.db | Bin 266240 -> 274432 bytes BBSHTMLConfig.c | 30 +- BBSUtilities.c | 52 +- BPQMail.aps | Bin 0 -> 92636 bytes BPQMail.c | 11 +- BPQMail.rc | 1090 +- FBBRoutines.c | 191 +- FLDigi64.c | 7720 ++-- HALDriver64.c | 3814 +- HFCommon.c | 3 - LinBPQ.c | 6 + MULTIPSK64.c | 3086 +- MultiConsole.c | 3 + SCSTrackeMulti64.c | 3428 +- Versions.h | 4 +- WebMail.c | 172 +- WinRPR.c | 3 - bpqmail.h | 10 +- templatedefs.c | 3 +- 267 files changed, 494541 insertions(+), 450636 deletions(-) create mode 100644 .svn/pristine/15/1557275e0b72cb1376626ed0c9e0e37f9edb65c4.svn-base create mode 100644 .svn/pristine/21/21952cfff9874914969464dfcddd2892a304118d.svn-base create mode 100644 .svn/pristine/55/55f8243b6492bc4d49d3f6b8f0fbb0f250776fc2.svn-base create mode 100644 .svn/pristine/69/695c3adeaa9b4bb07b019e931a9652ca162c97cf.svn-base create mode 100644 .svn/pristine/93/93324a0ebc973deb75bf1378e2e350a72b369c21.svn-base create mode 100644 .svn/pristine/95/95d0ac20bc0e745d3dca66a9762242c6dac82d4c.svn-base create mode 100644 .svn/pristine/9a/9aab68516605584c36262c7c751e8c3a2a2d2fc5.svn-base create mode 100644 .svn/pristine/9f/9ffff99503dac33e44480e1b206ae77184a240ed.svn-base create mode 100644 .svn/pristine/b5/b54840ee01ca392203f905b243390b31ed14704e.svn-base create mode 100644 .svn/pristine/c6/c6fd23ee09eb6744209f09fd38e64dd3d20f3577.svn-base create mode 100644 .svn/pristine/dd/dd68899778994c1270ff76b5ff89e58a5bc6e07a.svn-base create mode 100644 .svn/pristine/dd/ddbef8ea276887f6c9d0cfe99e42b72e9b2f8598.svn-base create mode 100644 .svn/pristine/fc/fc5b138875af83140a4070c652e6ac2d51dbb9f3.svn-base create mode 100644 BPQMail.aps diff --git a/.svn/pristine/00/0091dc3ec6a47e062a4f7f1c098c31c1e17d2eae.svn-base b/.svn/pristine/00/0091dc3ec6a47e062a4f7f1c098c31c1e17d2eae.svn-base index 6c6b3fc..7b44895 100644 --- a/.svn/pristine/00/0091dc3ec6a47e062a4f7f1c098c31c1e17d2eae.svn-base +++ b/.svn/pristine/00/0091dc3ec6a47e062a4f7f1c098c31c1e17d2eae.svn-base @@ -1,1849 +1,1849 @@ -/* -Copyright 2001-2022 John Wiseman G8BPQ - -This file is part of LinBPQ/BPQ32. - -LinBPQ/BPQ32 is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -LinBPQ/BPQ32 is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses -*/ - -// -// Interface to allow G8BPQ switch to use HSMODEM TNC - - -#define _CRT_SECURE_NO_DEPRECATE - -#include -#include - -#ifndef WIN32 -#ifndef MACBPQ -#include -#endif -#endif - - -#include "cheaders.h" - -#pragma pack(1) - -struct BroadcastMsg -{ - unsigned char Type; - unsigned char initialVolTX; - unsigned char initialVolRX; - unsigned char AudioTimespan; - unsigned char intialVolSpeaker; - unsigned char initalVolMic; - unsigned char Retransmits; - unsigned char SendAudio; - unsigned char RTTYAutoSync; - unsigned char Speed; - char playbackDevice[100]; - char captureDevice[100]; - char Callsign[20]; - char Locator[10]; - char Name[20]; -}; - -struct FileHeader -{ - unsigned char Type; - unsigned char Info; // 0 - First, 1 - Continuation 2 Last 3 - Only - char filename[50]; - unsigned short CRC; // of filename = transfer id - unsigned char Size[3]; // Big endian - unsigned char Data[164]; -}; - -struct FileData -{ - unsigned char Type; - unsigned char Info; // 0 - First, 1 - Continuation 2 Last 3 - Only - unsigned char Data[219]; -}; - -#pragma pack() - -struct HSFILEINFO -{ - struct HSFILEINFO * Next; // May want to chain entries for partial files - - char fileName[50]; - unsigned short CRC; // Used as a transfer ID - int fileSize; - int Sequence; - int State; - int Type; - time_t LastRX; - unsigned char goodBlocks[1024]; - unsigned char * Data; - int dataPointer; - int lastBlock; - int lostBlocks; - unsigned char * txData; - int txSize; - int txLeft; -}; - - -struct HSMODEMINFO -{ - struct HSFILEINFO * File; - - int Mode; - char * Capture; // Capture Device Name - char * Playback; // Playback Device Name - int Seq; // To make CRC more Unique - int txFifo; - int rxFifo; - int Sync; - int DCD; -}; - - -int KillTNC(struct TNCINFO * TNC); -int RestartTNC(struct TNCINFO * TNC); - -extern int (WINAPI FAR *GetModuleFileNameExPtr)(); -extern int (WINAPI FAR *EnumProcessesPtr)(); - -#include "bpq32.h" - -#include "tncinfo.h" - -static int Socket_Data(int sock, int error, int eventcode); - -VOID MoveWindows(struct TNCINFO * TNC); -static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen); -int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error); -BOOL HSMODEMWriteCommBlock(struct TNCINFO * TNC); -void HSMODEMCheckRX(struct TNCINFO * TNC); -int HSMODEMSendData(struct TNCINFO * TNC, UCHAR * data, int txlen); -int HSMODEMSendSingleData(struct TNCINFO * TNC, UCHAR * FN, UCHAR * data, int txlen); -int HSMODEMSendCommand(struct TNCINFO * TNC, UCHAR * data); -int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); -VOID SendInitScript(struct TNCINFO * TNC); -int HSMODEMGetLine(char * buf); -int ProcessEscape(UCHAR * TXMsg); -BOOL KAMStartPort(struct PORTCONTROL * PORT); -BOOL KAMStopPort(struct PORTCONTROL * PORT); -void SendPoll(struct TNCINFO * TNC); -void SendMode(struct TNCINFO * TNC); - -static char ClassName[]="HSMODEMSTATUS"; -static char WindowTitle[] = "HSMODEM"; -static int RigControlRow = 205; - -#ifndef LINBPQ -#include -#endif - -extern int SemHeldByAPI; - -static RECT Rect; - -static int ProcessLine(char * buf, int Port); - -VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); - -static int ProcessLine(char * buf, int Port) -{ - UCHAR * ptr,* p_cmd; - char * p_ipad = 0; - char * p_port = 0; - unsigned short WINMORport = 0; - int BPQport; - int len=510; - struct TNCINFO * TNC = TNCInfo[Port]; - char errbuf[256]; - - strcpy(errbuf, buf); - - ptr = strtok(buf, " \t\n\r"); - - if (ptr == NULL) return (TRUE); - - if (*ptr =='#') return (TRUE); // comment - - if (*ptr ==';') return (TRUE); // comment - - - if (_stricmp(buf, "ADDR")) - return FALSE; // Must start with ADDR - - ptr = strtok(NULL, " \t\n\r"); - - BPQport = Port; - p_ipad = ptr; - - TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); - memset(TNC, 0, sizeof(struct TNCINFO)); - - TNC->HSModemInfo = zalloc(sizeof(struct HSMODEMINFO)); - TNC->HSModemInfo->File = zalloc(sizeof(struct HSFILEINFO)); - - TNC->InitScript = malloc(1000); - TNC->InitScript[0] = 0; - - if (p_ipad == NULL) - p_ipad = strtok(NULL, " \t\n\r"); - - if (p_ipad == NULL) return (FALSE); - - p_port = strtok(NULL, " \t\n\r"); - - if (p_port == NULL) return (FALSE); - - WINMORport = atoi(p_port); - - TNC->TCPPort = WINMORport; - - TNC->destaddr.sin_family = AF_INET; - TNC->destaddr.sin_port = htons(WINMORport + 2); // We only receive on Port + 2 - - TNC->HostName = malloc(strlen(p_ipad)+1); - - if (TNC->HostName == NULL) return TRUE; - - strcpy(TNC->HostName,p_ipad); - - ptr = strtok(NULL, " \t\n\r"); - - if (ptr) - { - if (_stricmp(ptr, "PTT") == 0) - { - ptr = strtok(NULL, " \t\n\r"); - - if (ptr) - { - DecodePTTString(TNC, ptr); - ptr = strtok(NULL, " \t\n\r"); - } - } - } - - if (ptr) - { - if (_memicmp(ptr, "PATH", 4) == 0) - { - p_cmd = strtok(NULL, "\n\r"); - if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); - } - } - - // Read Initialisation lines - - while(TRUE) - { - if (GetLine(buf) == 0) - return TRUE; - - strcpy(errbuf, buf); - - if (memcmp(buf, "****", 4) == 0) - return TRUE; - - ptr = strchr(buf, ';'); - if (ptr) - { - *ptr++ = 13; - *ptr = 0; - } - - if (_memicmp(buf, "CAPTURE", 7) == 0) - { - TNC->HSModemInfo->Capture = _strdup(&buf[8]); - strlop(TNC->HSModemInfo->Capture, 13); - } - else if (_memicmp(buf, "PLAYBACK", 8) == 0) - { - TNC->HSModemInfo->Playback = _strdup(&buf[9]); - strlop(TNC->HSModemInfo->Playback, 13); - } - else if (_memicmp(buf, "MODE ", 5) == 0) - TNC->HSModemInfo->Mode = atoi(&buf[5]); - else if (_memicmp(buf, "LOGDIR ", 7) == 0) - TNC->LogPath = _strdup(&buf[7]); - else - strcat (TNC->InitScript, buf); - } - - return (TRUE); -} - -static char * Config; -static char * ptr1, * ptr2; - -int HSMODEMGetLine(char * buf) -{ -loop: - - if (ptr2 == NULL) - return 0; - - memcpy(buf, ptr1, ptr2 - ptr1 + 2); - buf[ptr2 - ptr1 + 2] = 0; - ptr1 = ptr2 + 2; - ptr2 = strchr(ptr1, 13); - - if (buf[0] < 0x20) goto loop; - if (buf[0] == '#') goto loop; - if (buf[0] == ';') goto loop; - - if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0; - if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0; - buf[strlen(buf)] = 13; - - return 1; -} - -VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); -VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); -VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); - -static time_t ltime; - - - -static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen) -{ - if (TNC->hDevice) - { - // HSMODEM mode. Queue to Hostmode driver - - PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); - - if (buffptr == 0) return; // No buffers, so ignore - - buffptr->Len = EncLen; - memcpy(&buffptr->Data[0], Encoded, EncLen); - - C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); - TNC->Streams[Stream].FramesQueued++; - - return; - } -} - - -VOID HSMODEMChangeMYC(struct TNCINFO * TNC, char * Call) -{ - UCHAR TXMsg[100]; - int datalen; - - if (strcmp(Call, TNC->CurrentMYC) == 0) - return; // No Change - - strcpy(TNC->CurrentMYC, Call); - - datalen = sprintf(TXMsg, "MYCALL %s\r", Call); - HSMODEMSendCommand(TNC, TXMsg); -} - -static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) -{ - int datalen; - PMSGWITHLEN buffptr; -// char txbuff[500]; - unsigned int bytes,txlen = 0; - UCHAR * TXMsg; - - size_t Param; - int Stream = 0; - HKEY hKey=0; - struct TNCINFO * TNC = TNCInfo[port]; - struct STREAMINFO * STREAM = &TNC->Streams[0]; - struct ScanEntry * Scan; - - if (TNC == NULL) - return 0; // Port not defined - - switch (fn) - { - case 7: - - // 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances - - // G7TAJ's code to record activity for stats display - - if ( TNC->BusyFlags && CDBusy ) - TNC->PortRecord->PORTCONTROL.ACTIVE += 2; - - if ( TNC->PTTState ) - TNC->PortRecord->PORTCONTROL.SENDING += 2; - - if (TNC->CONNECTED) - { - TNC->CONNECTED--; - - if (TNC->CONNECTED == 0) - { - sprintf(TNC->WEB_COMMSSTATE, "Connection to HSMODEM lost"); - MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); - } - } - - TNC->PollDelay++; - - if (TNC->PollDelay > 20) - { - TNC->PollDelay = 0; - - SendPoll(TNC); - } - - return 0; - - case 1: // poll - - HSMODEMCheckRX(TNC); - - while (TNC->PortRecord->UI_Q) - { - int datalen; - char * Buffer; - char FECMsg[512]; - char Call[12] = " "; - struct _MESSAGE * buffptr; - int CallLen; - char * ptr = FECMsg; - - buffptr = Q_REM(&TNC->PortRecord->UI_Q); - -/* if (TNC->CONNECTED == 0 || - TNC->Streams[0].Connecting || - TNC->Streams[0].Connected) - { - // discard if TNC not connected or sesison active - - ReleaseBuffer(buffptr); - continue; - } -*/ - datalen = buffptr->LENGTH - MSGHDDRLEN; - Buffer = &buffptr->DEST[0]; // Raw Frame - Buffer[datalen] = 0; - - // Frame has ax.25 format header. Convert to Text - - CallLen = ConvFromAX25(Buffer + 7, Call); // Origin - memcpy(ptr, Call, CallLen); - ptr += CallLen; - - *ptr++ = '!'; - - CallLen = ConvFromAX25(Buffer, Call); // Dest - memcpy(ptr, Call, CallLen); - ptr += CallLen; - - Buffer += 14; // TO Digis - datalen -= 14; - - while ((Buffer[-1] & 1) == 0) - { - *ptr++ = ','; - CallLen = ConvFromAX25(Buffer, Call); - memcpy(ptr, Call, CallLen); - ptr += CallLen; - Buffer += 7; // End of addr - datalen -= 7; - } - - *ptr++ = '_'; - *ptr++ = 'U'; // UI Frame - *ptr++ = 0; // delimit calls - - if (Buffer[0] == 3) // UI - { - Buffer += 2; - datalen -= 2; - } - - HSMODEMSendSingleData(TNC, FECMsg, Buffer, datalen); - - ReleaseBuffer(buffptr); - } - - - if (TNC->DiscPending) - { - TNC->DiscPending--; - - if (TNC->DiscPending == 0) - { - // Too long in Disc Pending - Kill and Restart TNC - } - } - - - for (Stream = 0; Stream <= 2; Stream++) - { - STREAM = &TNC->Streams[Stream]; - - if (STREAM->NeedDisc) - { - STREAM->NeedDisc--; - - if (STREAM->NeedDisc == 0) - { - // Send the DISCONNECT - - HSMODEMSendCommand(TNC, "DISCONNECT\r"); - } - } - - if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0) - { - // New Attach - - int calllen; - char Msg[80]; - - Debugprintf("HSMODEM New Attach Stream %d", Stream); - - STREAM->Attached = TRUE; - - calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, TNC->Streams[Stream].MyCall); - TNC->Streams[Stream].MyCall[calllen] = 0; - - - HSMODEMChangeMYC(TNC, TNC->Streams[0].MyCall); - - // Stop other ports in same group - - SuspendOtherPorts(TNC); - - //sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); - //MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - - // Stop Scanning - - sprintf(Msg, "%d SCANSTOP", TNC->Port); - - Rig_Command( (TRANSPORTENTRY *) -1, Msg); - } - - if (STREAM->Attached) - CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); - - } - - // See if any frames for this port - - for (Stream = 0; Stream <= 2; Stream++) - { - STREAM = &TNC->Streams[Stream]; - - if (STREAM->BPQtoPACTOR_Q) - { - PMSGWITHLEN buffptr = (PMSGWITHLEN)Q_REM(&STREAM->BPQtoPACTOR_Q); - UCHAR * data = &buffptr->Data[0]; - STREAM->FramesQueued--; - txlen = (int)buffptr->Len; - STREAM->bytesTXed += txlen; - - bytes=HSMODEMSendData(TNC, data, txlen); - WritetoTrace(TNC, data, txlen); - } - - if (STREAM->PACTORtoBPQ_Q != 0) - { - buffptr = (PMSGWITHLEN)Q_REM(&STREAM->PACTORtoBPQ_Q); - - datalen = (int)buffptr->Len; - - buff->PORT = Stream; // Compatibility with Kam Driver - buff->PID = 0xf0; - memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte - datalen += sizeof(void *) + 4; - - PutLengthinBuffer(buff, datalen); - - ReleaseBuffer(buffptr); - - return (1); - } - - if (STREAM->ReportDISC) // May need a delay so treat as a counter - { - STREAM->ReportDISC--; - if (STREAM->ReportDISC == 0) - { - buff->PORT = Stream; -// STREAM->Connected = 0; -// STREAM->Attached = 0; - return -1; - } - } - } - return (0); - - case 2: // send - - Stream = buff->PORT; - - if (!TNC->CONNECTED) - { - // Send Error Response - - PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); - - if (buffptr == 0) return (0); // No buffers, so ignore - - buffptr->Len = 21; - memcpy(&buffptr->Data[0], "No Connection to TNC\r", 21); - - C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); - - return 0; // Don't try if not connected - } - - STREAM = &TNC->Streams[Stream]; - - if (TNC->SwallowSignon) - { - TNC->SwallowSignon = FALSE; // Discard *** connected - return 0; - } - - txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID - TXMsg = &buff->L2DATA[0]; - TXMsg[txlen] = 0; - - // for now just send, but allow sending control - // characters with \\ or ^ escape - - if (STREAM->Connected) - { - STREAM->PacketsSent++; - - bytes=HSMODEMSendData(TNC, TXMsg, txlen); - TNC->Streams[Stream].BytesOutstanding += bytes; // So flow control works - will be updated by BUFFER response - STREAM->bytesTXed += bytes; -// WritetoTrace(TNC, &buff->L2DATA[0], txlen); - - return 1; - } - - if (_memicmp(&buff->L2DATA[0], "D\r", 2) == 0 || _memicmp(&buff->L2DATA[0], "BYE\r", 4) == 0) - { - STREAM->ReportDISC = TRUE; // Tell Node - return 0; - } - - - // See if Local command (eg RADIO) - - if (_memicmp(&buff->L2DATA[0], "RADIO ", 6) == 0) - { - char cmd[56]; - - strcpy(cmd, &buff->L2DATA[6]); - sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, cmd); - - - if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &buff->L2DATA[0])) - { - } - else - { - PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); - - if (buffptr == 0) return 1; // No buffers, so ignore - - buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", &buff->L2DATA[0]); - C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); - } - return 1; - } - - if (_memicmp(&buff->L2DATA[0], "OVERRIDEBUSY", 12) == 0) - { - PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); - - TNC->OverrideBusy = TRUE; - - if (buffptr) - { - buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "HSMODEM} OK\r"); - C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); - } - - return 0; - - } - - if (_memicmp(&buff->L2DATA[0], "MODE ", 5) == 0) - { - PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); - - TNC->HSModemInfo->Mode = atoi(&buff->L2DATA[5]); - - if (buffptr) - { - buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "HSMODEM} OK\r"); - C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); - } - - SendMode(TNC); - - return 0; - - } - - - // See if a Connect Command. If so, start codec and set Connecting - - if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect - { - char Connect[80]; - char * ptr = strchr(&buff->L2DATA[2], 13); - - if (ptr) - *ptr = 0; - - _strupr(&buff->L2DATA[2]); - - if (strlen(&buff->L2DATA[2]) > 9) - buff->L2DATA[11] = 0; - - txlen = sprintf(Connect, "C %s\r", &buff->L2DATA[2]); - - HSMODEMChangeMYC(TNC, TNC->Streams[0].MyCall); - - // See if Busy - - if (InterlockedCheckBusy(TNC)) - { - // Channel Busy. Unless override set, wait - - if (TNC->OverrideBusy == 0) - { - // Save Command, and wait up to 10 secs - - sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); - MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - - TNC->ConnectCmd = _strdup(Connect); - TNC->BusyDelay = TNC->BusyWait * 10; // BusyWait secs - return 0; - } - } - - TNC->OverrideBusy = FALSE; - - memset(TNC->Streams[0].RemoteCall, 0, 10); - strcpy(TNC->Streams[0].RemoteCall, &buff->L2DATA[2]); - - sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", STREAM->MyCall, STREAM->RemoteCall); - HSMODEMSendCommand(TNC, Connect); - MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - - STREAM->Connecting = TRUE; - return 0; - - } - - // Normal data. Send to TNC - - - HSMODEMSendData(TNC, TXMsg, txlen); - - return 0; - - case 3: - - // CHECK IF OK TO SEND (And check TNC Status) - - Stream = (int)(size_t)buff; - - // I think we should check buffer space for all comms modes - - { - int Queued; - int Outstanding = TNC->Streams[Stream].BytesOutstanding; - - if (Stream == 0) - Queued = TNC->Streams[13].FramesQueued; // ARDOP Native Mode Send Queue - else - Queued = TNC->Streams[Stream].FramesQueued; - - Outstanding = Queued = 0; - - if (Queued > 4 || Outstanding > 8500) - return (1 | (TNC->HostMode | TNC->CONNECTED) << 8 | STREAM->Disconnecting << 15); - } - - if (TNC->Streams[Stream].Attached == 0) - return (TNC->CONNECTED != 0) << 8 | 1; - - return ((TNC->CONNECTED != 0) << 8 | TNC->Streams[Stream].Disconnecting << 15); // OK - - - case 4: // reinit7 - - return 0; - - case 5: // Close - - return 0; - - case 6: // Scan Stop Interface - - Param = (size_t)buff; - - if (Param == 2) // Check Permission (Shouldn't happen) - { - Debugprintf("Scan Check Permission called on ARDOP"); - return 1; // OK to change - } - - if (Param == 1) // Request Permission - { - if (!TNC->CONNECTED) - return 0; // No connection so no interlock - - if (TNC->ConnectPending == 0 && TNC->PTTState == 0) - { - HSMODEMSendCommand(TNC, "CONOK OFF"); - TNC->GavePermission = TRUE; - return 0; // OK to Change - } - - if (TNC->ConnectPending) - TNC->ConnectPending--; // Time out if set too long - - return TRUE; - } - - if (Param == 3) // Release Permission - { - if (TNC->GavePermission) - { - TNC->GavePermission = FALSE; - if (TNC->ARDOPCurrentMode[0] != 'S') // Skip - HSMODEMSendCommand(TNC, "CONOK ON"); - } - return 0; - } - - // Param is Address of a struct ScanEntry - - Scan = (struct ScanEntry *)buff; - return 0; - } - return 0; -} - -VOID HSMODEMReleaseTNC(struct TNCINFO * TNC) -{ - // Set mycall back to Node or Port Call, and Start Scanner - - UCHAR TXMsg[1000]; - - HSMODEMChangeMYC(TNC, TNC->NodeCall); - - // Start Scanner - - sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); - - Rig_Command( (TRANSPORTENTRY *) -1, TXMsg); - - ReleaseOtherPorts(TNC); - -} - -VOID HSMODEMSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) -{ - HSMODEMSendCommand(TNC, "CONOK OFF\r"); -} - -VOID HSMODEMReleasePort(struct TNCINFO * TNC) -{ - HSMODEMSendCommand(TNC, "CONOK ON\r"); -} - - -static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) -{ - int Len = sprintf(Buff, "" - "" - "VARA Status" - "

HSMODEM Status" - "

", - TNC->Port); - - - Len += sprintf(&Buff[Len], ""); - - Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); - Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); - Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); - Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE); - Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); - Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); -// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); - Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Channel State%s
Proto State%s
Traffic%s
TNC Restarts
"); - - Len += sprintf(&Buff[Len], "", TNC->WebBuffer); - Len = DoScanLine(TNC, Buff, Len); - - return Len; -} - -#ifndef LINBPQ - -#define BGCOLOUR RGB(236,233,216) -HBRUSH RedBrush = NULL; -HBRUSH GreenBrush; -HBRUSH BlueBrush; -static HBRUSH bgBrush = NULL; - -extern HWND ClientWnd, FrameWnd; -extern int OffsetH, OffsetW; - -extern HMENU hMainFrameMenu; -extern HMENU hBaseMenu; -extern HANDLE hInstance; - -extern HKEY REGTREE; - - - -static LRESULT CALLBACK PacWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - int wmId, wmEvent; - MINMAXINFO * mmi; - PAINTSTRUCT ps; - HDC hdc; - - int i; - struct TNCINFO * TNC; - - HKEY hKey; - char Key[80]; - int retCode, disp; - - for (i=0; i<41; i++) - { - TNC = TNCInfo[i]; - if (TNC == NULL) - continue; - - if (TNC->hDlg == hWnd) - break; - } - - if (TNC == NULL) - return DefMDIChildProc(hWnd, message, wParam, lParam); - - switch (message) { - - case WM_CREATE: - - break; - - case WM_PAINT: - - hdc = BeginPaint(hWnd, &ps); - - TextOut(hdc, 10, 162, "RX", 4); - TextOut(hdc, 10, 182, "TX", 4); - - if (TNC->HSModemInfo->Sync) - TextOut(hdc, 305, 162, "Sync", 4); - -// SelectObject(ps.hdc, RedBrush); - SelectObject(ps.hdc, GreenBrush); -// SelectObject(ps.hdc, GetStockObject(GRAY_BRUSH)); - - Rectangle(ps.hdc, 40, 165, TNC->HSModemInfo->rxFifo + 42, 175); - SelectObject(ps.hdc, RedBrush); - Rectangle(ps.hdc, 40, 185, (TNC->HSModemInfo->txFifo * 10) + 42, 195); - - EndPaint(hWnd, &ps); - break; - - case WM_GETMINMAXINFO: - - if (TNC->ClientHeight) - { - mmi = (MINMAXINFO *)lParam; - mmi->ptMaxSize.x = TNC->ClientWidth; - mmi->ptMaxSize.y = TNC->ClientHeight; - mmi->ptMaxTrackSize.x = TNC->ClientWidth; - mmi->ptMaxTrackSize.y = TNC->ClientHeight; - } - - break; - - - case WM_MDIACTIVATE: - { - - // Set the system info menu when getting activated - - if (lParam == (LPARAM) hWnd) - { - // Activate - - RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); - - if (TNC->hMenu) - AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)TNC->hMenu, "Actions"); - - SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); - -// SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) TNC->hMenu, (LPARAM) TNC->hWndMenu); - } - else - { - // Deactivate - - SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); - } - - // call DrawMenuBar after the menu items are set - DrawMenuBar(FrameWnd); - - return DefMDIChildProc(hWnd, message, wParam, lParam); - } - - - - case WM_INITMENUPOPUP: - - if (wParam == (WPARAM)TNC->hMenu) - { - if (TNC->ProgramPath) - { - if (strstr(TNC->ProgramPath, " TNC") || strstr(TNC->ProgramPath, "ARDOP") || strstr(TNC->ProgramPath, "VARA")) - { - EnableMenuItem(TNC->hMenu, WINMOR_RESTART, MF_BYCOMMAND | MF_ENABLED); - EnableMenuItem(TNC->hMenu, WINMOR_KILL, MF_BYCOMMAND | MF_ENABLED); - - break; - } - } - EnableMenuItem(TNC->hMenu, WINMOR_RESTART, MF_BYCOMMAND | MF_GRAYED); - EnableMenuItem(TNC->hMenu, WINMOR_KILL, MF_BYCOMMAND | MF_GRAYED); - } - - break; - - case WM_COMMAND: - - wmId = LOWORD(wParam); - wmEvent = HIWORD(wParam); - - switch (wmId) - { - case WINMOR_KILL: - - KillTNC(TNC); - break; - - case WINMOR_RESTART: - - KillTNC(TNC); - RestartTNC(TNC); - break; - - case WINMOR_RESTARTAFTERFAILURE: - - TNC->RestartAfterFailure = !TNC->RestartAfterFailure; - CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); - - sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\PACTOR\\PORT%d", TNC->Port); - - retCode = RegCreateKeyEx(REGTREE, Key, 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); - - if (retCode == ERROR_SUCCESS) - { - RegSetValueEx(hKey,"TNC->RestartAfterFailure",0,REG_DWORD,(BYTE *)&TNC->RestartAfterFailure, 4); - RegCloseKey(hKey); - } - break; - - case ARDOP_ABORT: - - if (TNC->ForcedCloseProc) - TNC->ForcedCloseProc(TNC, 0); - - break; - } - return DefMDIChildProc(hWnd, message, wParam, lParam); - - case WM_SIZING: - case WM_SIZE: - - MoveWindows(TNC); - - return DefMDIChildProc(hWnd, message, wParam, lParam); - - case WM_SYSCOMMAND: - - wmId = LOWORD(wParam); - wmEvent = HIWORD(wParam); - - switch (wmId) - { - - case SC_RESTORE: - - TNC->Minimized = FALSE; - break; - - case SC_MINIMIZE: - - TNC->Minimized = TRUE; - break; - } - - return DefMDIChildProc(hWnd, message, wParam, lParam); - - case WM_CTLCOLORDLG: - return (LONG)bgBrush; - - - case WM_CTLCOLORSTATIC: - { - HDC hdcStatic = (HDC)wParam; - SetTextColor(hdcStatic, RGB(0, 0, 0)); - SetBkMode(hdcStatic, TRANSPARENT); - return (LONG)bgBrush; - } - - case WM_DESTROY: - - break; - } - return DefMDIChildProc(hWnd, message, wParam, lParam); -} -#endif - - - -VOID * HSMODEMExtInit(EXTPORTDATA * PortEntry) -{ - int port; - char Msg[255]; - char * ptr; - struct TNCINFO * TNC; - char * TempScript; - u_long param = 1; - int ret; - - port=PortEntry->PORTCONTROL.PORTNUMBER; - - ReadConfigFile(port, ProcessLine); - - TNC = TNCInfo[port]; - - if (TNC == NULL) - { - // Not defined in Config file - - sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); - WritetoConsole(Msg); - - return ExtProc; - } - -#ifndef LINBPQ - - if (bgBrush == NULL) - { - bgBrush = CreateSolidBrush(BGCOLOUR); - RedBrush = CreateSolidBrush(RGB(255,0,0)); - GreenBrush = CreateSolidBrush(RGB(0,255,0)); - BlueBrush = CreateSolidBrush(RGB(0,0,255)); - } - -#endif - - Consoleprintf("HSMODEM Host %s %d", TNC->HostName, TNC->TCPPort); - - TNC->Port = port; - TNC->PortRecord = PortEntry; - - TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_HSMODEM; - - if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) - memcpy(TNC->NodeCall, MYNODECALL, 10); - else - ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); - - if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) - TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; - - PortEntry->PORTCONTROL.PROTOCOL = 10; - PortEntry->PORTCONTROL.PORTQUALITY = 0; - - if (TNC->PacketChannels > 1) - TNC->PacketChannels = 1; - - PortEntry->MAXHOSTMODESESSIONS = TNC->PacketChannels + 1; - - PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only - PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream - - PortEntry->PORTCONTROL.UICAPABLE = TRUE; - - if (PortEntry->PORTCONTROL.PORTPACLEN == 0) - PortEntry->PORTCONTROL.PORTPACLEN = 236; - - TNC->SuspendPortProc = HSMODEMSuspendPort; - TNC->ReleasePortProc = HSMODEMReleasePort; - - PortEntry->PORTCONTROL.PORTSTARTCODE = KAMStartPort; - PortEntry->PORTCONTROL.PORTSTOPCODE = KAMStopPort; - - - ptr=strchr(TNC->NodeCall, ' '); - if (ptr) *(ptr) = 0; // Null Terminate - - // Set Essential Params and MYCALL - - // Put overridable ones on front, essential ones on end - - TempScript = zalloc(1000); - - // cant think of any yet - - if (TNC->InitScript) - { - strcat(TempScript, TNC->InitScript); - free(TNC->InitScript); - } - - TNC->InitScript = TempScript; - - // Set MYCALL - - sprintf(Msg, "MYCALL %s\r", TNC->NodeCall); - strcat(TNC->InitScript, Msg); - - strcpy(TNC->CurrentMYC, TNC->NodeCall); - - if (TNC->WL2K == NULL) - if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded - TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; - - PortEntry->PORTCONTROL.TNC = TNC; - - TNC->WebWindowProc = WebProc; - TNC->WebWinX = 520; - TNC->WebWinY = 500; - TNC->WebBuffer = zalloc(5000); - - TNC->WEB_COMMSSTATE = zalloc(100); - TNC->WEB_TNCSTATE = zalloc(100); - TNC->WEB_CHANSTATE = zalloc(100); - TNC->WEB_BUFFERS = zalloc(100); - TNC->WEB_PROTOSTATE = zalloc(100); - TNC->WEB_RESTARTTIME = zalloc(100); - TNC->WEB_RESTARTS = zalloc(100); - - TNC->WEB_MODE = zalloc(20); - TNC->WEB_TRAFFIC = zalloc(100); - - -#ifndef LINBPQ - - CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 450, ForcedClose); - - CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); - TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); - - CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); - TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); - - CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); - TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); - - CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); - TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); - - CreateWindowEx(0, "STATIC", "Proto State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); - TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); - - CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); - TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "0 0 0 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); - - CreateWindowEx(0, "STATIC", "TNC Restarts", WS_CHILD | WS_VISIBLE,10,138,100,20, TNC->hDlg, NULL, hInstance, NULL); - TNC->xIDC_RESTARTS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE,116,138,20,20 , TNC->hDlg, NULL, hInstance, NULL); - CreateWindowEx(0, "STATIC", "Last Restart", WS_CHILD | WS_VISIBLE,140,138,100,20, TNC->hDlg, NULL, hInstance, NULL); - TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,138,200,20, TNC->hDlg, NULL, hInstance, NULL); - - TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | - LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, - 0,170,250,300, TNC->hDlg, NULL, hInstance, NULL); - - TNC->ClientHeight = 450; - TNC->ClientWidth = 500; - - TNC->hMenu = CreatePopupMenu(); - - AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill VARA TNC"); - AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart VARA TNC"); - AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTARTAFTERFAILURE, "Restart TNC after failed Connection"); - CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); - AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session"); - - MoveWindows(TNC); -#endif - - - // Open and bind UDP socket - - TNC->TCPSock = socket(AF_INET,SOCK_DGRAM,0); - - if (TNC->TCPSock == INVALID_SOCKET) - { - WritetoConsole("Failed to create UDP socket for HSMODEM"); - ret = WSAGetLastError(); - } - else - ioctl (TNC->TCPSock, FIONBIO, ¶m); - - ret = bind(TNC->TCPSock, (struct sockaddr *) &TNC->destaddr, sizeof(struct sockaddr_in)); - - if (ret != 0) - { - // Bind Failed - - ret = WSAGetLastError(); - sprintf(Msg, "Bind Failed for UDP port %d - error code = %d", TNC->TCPPort + 2, ret); - WritetoConsole(Msg); - } - - -// SendInitScript(TNC); - - time(&TNC->lasttime); // Get initial time value - - return ExtProc; -} - - -VOID TidyClose(struct TNCINFO * TNC, int Stream) -{ - // If all acked, send disc - - if (TNC->Streams[Stream].BytesOutstanding == 0) - HSMODEMSendCommand(TNC, "DISCONNECT\r"); -} - -VOID ForcedClose(struct TNCINFO * TNC, int Stream) -{ - HSMODEMSendCommand(TNC, "DISCONNECT\r"); -} - - - -VOID CloseComplete(struct TNCINFO * TNC, int Stream) -{ - if (Stream == 0) - { - HSMODEMReleaseTNC(TNC); - } -} - -VOID HSMODEMAbort(struct TNCINFO * TNC) -{ - HSMODEMSendCommand(TNC, "ABORT\r"); -} - -// Host Mode Stuff (we reuse some routines in SCSPactor) - -VOID HSMODEMDoTermModeTimeout(struct TNCINFO * TNC) -{ - UCHAR * Poll = TNC->TXBuffer; - - if (TNC->ReinitState == 0) - { - //Checking if in Terminal Mode - Try to set back to Term Mode - - TNC->ReinitState = 1; - return; - } - - if (TNC->ReinitState == 1) - { - // Forcing back to Term Mode - - TNC->ReinitState = 0; - return; - } - - if (TNC->ReinitState == 3) - { - return; - } -} - -RECT Rect1 = {30, 160, 400, 195}; - - -VOID HSMODEMProcessTNCMessage(struct TNCINFO * TNC, unsigned char * Msg, int Len) -{ - PMSGWITHLEN buffptr; - struct STREAMINFO * STREAM = &TNC->Streams[0]; - struct FileHeader * FH; - struct HSMODEMINFO * Modem = TNC->HSModemInfo; - struct HSFILEINFO * Info = Modem->File; - int fileLen, Seq, Offset; - - // Any message indicates Ok - - Msg[Len] = 0; - - if (TNC->CONNECTED == 0) - { - // Just come up - - sprintf(TNC->WEB_COMMSSTATE, "Connected to HSMODEM"); - MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); - SendMode(TNC); - } - - TNC->CONNECTED = 100; // time out after 10 secs - - /* - 3: responses to broadcast messages (see: GUI Interface: UDP/IP/Initialization) - 1: received payload data - 4: FFT data for a spectrum monitor - 5: IQ data for a constellation display - 6: received RTTY characters - */ - switch (Msg[0]) - { - case 1: - /* - Byte 0 ... 0x01 - Byte 1 ... frame type (which was inserted by the sender) - Byte 2 ... frame counter MSB - Byte 3 ... frame counter LSB (10 bits used) - Byte 4 ... frame information (which was inserted by the sender) - Byte 5 ... unused - Byte 6 ... measured line speed MSB - Byte 7 ... measured line speed LSB - Bytes 8-10 ... unused - Bytes 11-229 ... 219 bytes payload return; - - 1 … BER Test Pattern - 2 … Image - 3 … Ascii File - 4 … HTML File - 5 … Binary File - 6 … Voice Audio (for Codec 2 or Opus) - 7 … UserInfo - */ - - Seq = Msg[2] << 8 | Msg[3]; - - switch (Msg[1]) - { - case 1: - case 6: - case 7: - - Debugprintf("%d %d %02x %s %s %s", Msg[1], Seq, Msg[4], &Msg[11], &Msg[31], &Msg[41]); - return; - - case 2: - case 3: - case 4: - case 5: - - // File transfer types - - switch (Msg[4]) - { - case 0: - case 3: - - // File Header - - FH = (struct FileHeader *) &Msg[9]; - - if (FH->CRC == Info->CRC) - { - Debugprintf("Dup Header %X", Info->CRC); - return; - } - - Info->CRC = FH->CRC; - - fileLen = FH->Size[0] * 65536 + FH->Size[1] * 256 + FH->Size[2]; - - Info->Data = zalloc(fileLen + 512); - - if (Info->Data == NULL) - return; - - Info->fileSize = fileLen; - strcpy(Info->fileName, FH->filename); - - memset(Info->goodBlocks, 0, 1024); - Info->goodBlocks[0] = 1; - - Info->lastBlock = 0; - Info->lostBlocks = 0; - Info->LastRX = time(NULL); - - Debugprintf("%d %d %04X %02x %s %d %s", Msg[1], Seq, FH->CRC, Msg[4], - FH->filename, fileLen, FH->Data); - - memcpy(Info->Data, FH->Data, 164); - Info->dataPointer = 164; - break; - - case 1: - case 2: - - // Data Frame - - if (Seq == Info->lastBlock) - { - Debugprintf("Duplicate data frame %d", Seq); - return; - } - - Info->lastBlock++; - - if (Info->lastBlock != Seq) - Info->lostBlocks += Seq - Info->lastBlock; - - Info->goodBlocks[Seq] = 1; - - Offset = (Seq - 1) * 221 + 164; - - memcpy(&Info->Data[Offset], &Msg[11], 221); - - Debugprintf("%d %d %02x %s %d %s", Msg[1], Seq, Msg[4], &Msg[11]); - break; - - default: - - Debugprintf("%d %d %02x %s %d %s", Msg[1], Seq, Msg[4], &Msg[11]); - return; - - } - - // End of Data Frame Case - - if (Msg[4] == 2 || Msg[4] == 3) - { - // Last Frame - check file - - if (Info->lostBlocks == 0) - { - // filename is encoding of calls and frame type - - struct _MESSAGE * buffptr; - -// FILE * fp1 = fopen(Info->fileName, "wb"); -// int WriteLen; - -// if (fp1) -// { -// WriteLen = (int)fwrite(Info->Data, 1, Info->fileSize, fp1); -// fclose(fp1); -// } - - if (strchr(Info->fileName, '!')) - { - // Callsigns encoded in filename - - - - char * Origin = &Info->fileName[0]; - char * Type = strlop(Origin, '_'); - char * Dest = strlop(Origin, '!'); - unsigned char * Packet; - - - // Convert to ax.25 format - - buffptr = GetBuff(); - - // Convert to ax.25 format - - if (buffptr == 0) - return; // No buffers, so ignore - - Type = strlop(Origin, '_'); - - Packet = &buffptr->ORIGIN[0]; - - buffptr->PORT = TNC->Port; - buffptr->LENGTH = 16 + MSGHDDRLEN + Info->fileSize; - - ConvToAX25(Origin, buffptr->ORIGIN); - ConvToAX25(Dest, buffptr->DEST); - - - while (strchr(Dest, ',')) - { - Dest = strlop(Dest, ','); // Next digi - Packet += 7; - ConvToAX25(Dest, Packet); - buffptr->LENGTH += 7; - } - - Packet[6] |= 1; // Set end of address - - Packet += 7; - - *(Packet++) = 3; - *(Packet++) = 0xF0; - - memcpy(Packet, Info->Data, Info->fileSize); - time(&buffptr->Timestamp); - - BPQTRACE((MESSAGE *)buffptr, TRUE); - } - } - - return; - } - } - - return; - - case 4: // FFT data for a spectrum monitor - - Modem->txFifo = Msg[1]; - Modem->rxFifo = Msg[2]; - Modem->DCD = Msg[3]; - Modem->Sync = Msg[4]; - -#ifndef LINBPQ - InvalidateRect(TNC->hDlg, &Rect1, TRUE); -#endif - -// if (Info->Sync || Info->txFifo) -// Debugprintf("%d %d %d %d", Info->txFifo, Info->rxFifo, Info->DCD, Info->Sync); - /* - Byte 0 ... 0x04 - Byte 1 ... usage of the TX fifo (used by the transmitter to sync its data - output to the modem). This is a value between 0..255. During - an active transmission keep it above 4. - Byte 2 ... usage of RX fifo (not important, but can be displayed to the - user). A very high RX fifo usage indicates the the computer - is too slow for HSmodem. - Byte 3 ... 0 or 1. Indicates that an RF level was detected - Byte 4 ... 0 or 1. Indicates that the HSmodem receiver is synchronized - with a signal - Byte 5 ... maximum audio level (0..100%) of the audio input from the - transceiver. Can be used to detect clipping. - Byte 6 ... maximum audio level (0..100%) of the audio output to the - transceiver. Can be used to detect clipping. - Byte 7 ... in RTTY mode this is the auto-locked RTTY frequency MSB - Byte 8 ... and LSB - Byte 9 ... RTTY: 0=tx off, 1=txon - Byte 10 to the end ... FFT spectrum data, beginning at 0 Hz to 4kHz with - a resolution of 10 Hz -*/ - return; - - - case 5: // IQ data for a constellation display - return; - - case 6: //received RTTY characters - return; - } - - return; - - - buffptr = GetBuff(); - - if (buffptr == 0) - { - return; // No buffers, so ignore - } - - buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", TNC->RXBuffer); - - C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); - -} - -extern char LOC[7]; - -void SendMode(struct TNCINFO * TNC) -{ - unsigned char Msg[221] = ""; - int ret; - - Msg[0] = 16; - Msg[1] = TNC->HSModemInfo->Mode; - - TNC->destaddr.sin_port = htons(TNC->TCPPort + 1); // Data Port - TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); - - ret = sendto(TNC->TCPSock, (char *)&Msg, 221, 0, (struct sockaddr *)&TNC->destaddr, sizeof(struct sockaddr)); - - return; -} - -void SendPoll(struct TNCINFO * TNC) -{ - struct BroadcastMsg PollMsg = {0x3c, 100, 100, 0, 50, 50, 1, 0, 0, 9}; - int ret; - - strcpy(&PollMsg.captureDevice[0], TNC->HSModemInfo->Capture); - strcpy(&PollMsg.playbackDevice[0], TNC->HSModemInfo->Playback); -// strcpy(&PollMsg.playbackDevice[0], "CABLE Input (VB-Audio Virtual Cable)"); - - strcpy(&PollMsg.Callsign[0], TNC->NodeCall); - strcpy(&PollMsg.Locator[0], LOC); - strcpy(&PollMsg.Name[0], "1234567890"); - - TNC->destaddr.sin_port = htons(TNC->TCPPort); // Command Port - TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); - - ret = sendto(TNC->TCPSock, (char *)&PollMsg, 260, 0, (struct sockaddr *)&TNC->destaddr, sizeof(struct sockaddr)); - - return; -} -/* - unsigned char Type; - unsigned char Info; // 0 - First, 1 - Continuation 2 Last 3 - Only - char filename[50]; - unsigned short CRC; // of filename = transfer id - unsigned char Size[3]; // Big endian - unsigned char Data[163]; -*/ - -unsigned short int compute_crc(unsigned char *buf,int len); - -int HSMODEMSendSingleData(struct TNCINFO * TNC, UCHAR * FN, UCHAR * data, int txlen) -{ - struct FileHeader Msg; - unsigned short int crc; - int ret, fragLen = txlen; - struct HSMODEMINFO * Modem = TNC->HSModemInfo; - struct HSFILEINFO * Info = Modem->File; - - char Seq[60] = ""; - - sprintf(Seq, "%04X%s", Modem->Seq++, FN); - - crc = compute_crc(Seq, 60); - - crc ^= 0xffff; - - memset(&Msg, 0, sizeof(struct FileHeader)); - - Msg.Type = 5; // Binary Data - Msg.Info = 3; // Only Fragment - - if (txlen > 163) - { - // Need to send as multiple fragments - - fragLen = 164; - Info->txData = malloc(txlen + 512); - memcpy(Info->txData, data, txlen); - Info->txSize = txlen; - Info->txLeft = txlen - 164; - Msg.Info = 0; // First Fragment - } - - strcpy(Msg.filename, FN); - memcpy(Msg.Data, data, txlen); - memcpy(&Msg.CRC, &crc, 2); - Msg.Size[0] = txlen >> 16; - Msg.Size[1] = txlen >> 8;; - Msg.Size[2] = txlen; - - TNC->destaddr.sin_port = htons(TNC->TCPPort + 1); // Data Port - TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); - - ret = sendto(TNC->TCPSock, (char *)&Msg, 221, 0, (struct sockaddr *)&TNC->destaddr, sizeof(struct sockaddr)); - memset(&Msg, 0, sizeof(struct FileHeader)); - - return ret; -} - -int HSMODEMSendData(struct TNCINFO * TNC, UCHAR * data, int txlen) -{ - struct FileHeader Msg; - - memset(&Msg, 0, sizeof(struct FileHeader)); - - Msg.Type = 5; // Binary Data - Msg.Info = 3; // Only Fragment - - - return 0; -} - -void HSMODEMCheckRX(struct TNCINFO * TNC) -{ - int Len = 0; - unsigned char Buff[2000]; - - struct sockaddr_in rxaddr; - int addrlen = sizeof(struct sockaddr_in); - - Len = recvfrom(TNC->TCPSock, Buff, 2000, 0, (struct sockaddr *)&rxaddr, &addrlen); - - while (1) - { - if (Len == -1) - { -// Debugprintf("%d", GetLastError()); - Len = 0; - return; - } - TNC->RXLen = Len; - HSMODEMProcessTNCMessage(TNC, Buff, Len); - - Len = recvfrom(TNC->TCPSock, Buff, 2000, 0, (struct sockaddr *)&rxaddr, &addrlen); - - } - return; - - return; -} - -int HSMODEMWriteCommBlock(struct TNCINFO * TNC) -{ - if (TNC->hDevice) - return WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); - - return 0; -} - - -int HSMODEMSendCommand(struct TNCINFO * TNC, UCHAR * data) -{ - if (TNC->hDevice) - return WriteCOMBlock(TNC->hDevice, data, (int)strlen(data)); - - return 0; -} - +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// Interface to allow G8BPQ switch to use HSMODEM TNC + + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + +#ifndef WIN32 +#ifndef MACBPQ +#include +#endif +#endif + + +#include "cheaders.h" + +#pragma pack(1) + +struct BroadcastMsg +{ + unsigned char Type; + unsigned char initialVolTX; + unsigned char initialVolRX; + unsigned char AudioTimespan; + unsigned char intialVolSpeaker; + unsigned char initalVolMic; + unsigned char Retransmits; + unsigned char SendAudio; + unsigned char RTTYAutoSync; + unsigned char Speed; + char playbackDevice[100]; + char captureDevice[100]; + char Callsign[20]; + char Locator[10]; + char Name[20]; +}; + +struct FileHeader +{ + unsigned char Type; + unsigned char Info; // 0 - First, 1 - Continuation 2 Last 3 - Only + char filename[50]; + unsigned short CRC; // of filename = transfer id + unsigned char Size[3]; // Big endian + unsigned char Data[164]; +}; + +struct FileData +{ + unsigned char Type; + unsigned char Info; // 0 - First, 1 - Continuation 2 Last 3 - Only + unsigned char Data[219]; +}; + +#pragma pack() + +struct HSFILEINFO +{ + struct HSFILEINFO * Next; // May want to chain entries for partial files + + char fileName[50]; + unsigned short CRC; // Used as a transfer ID + int fileSize; + int Sequence; + int State; + int Type; + time_t LastRX; + unsigned char goodBlocks[1024]; + unsigned char * Data; + int dataPointer; + int lastBlock; + int lostBlocks; + unsigned char * txData; + int txSize; + int txLeft; +}; + + +struct HSMODEMINFO +{ + struct HSFILEINFO * File; + + int Mode; + char * Capture; // Capture Device Name + char * Playback; // Playback Device Name + int Seq; // To make CRC more Unique + int txFifo; + int rxFifo; + int Sync; + int DCD; +}; + + +int KillTNC(struct TNCINFO * TNC); +int RestartTNC(struct TNCINFO * TNC); + +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *EnumProcessesPtr)(); + +#include "bpq32.h" + +#include "tncinfo.h" + +static int Socket_Data(int sock, int error, int eventcode); + +VOID MoveWindows(struct TNCINFO * TNC); +static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen); +int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error); +BOOL HSMODEMWriteCommBlock(struct TNCINFO * TNC); +void HSMODEMCheckRX(struct TNCINFO * TNC); +int HSMODEMSendData(struct TNCINFO * TNC, UCHAR * data, int txlen); +int HSMODEMSendSingleData(struct TNCINFO * TNC, UCHAR * FN, UCHAR * data, int txlen); +int HSMODEMSendCommand(struct TNCINFO * TNC, UCHAR * data); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +VOID SendInitScript(struct TNCINFO * TNC); +int HSMODEMGetLine(char * buf); +int ProcessEscape(UCHAR * TXMsg); +BOOL KAMStartPort(struct PORTCONTROL * PORT); +BOOL KAMStopPort(struct PORTCONTROL * PORT); +void SendPoll(struct TNCINFO * TNC); +void SendMode(struct TNCINFO * TNC); + +static char ClassName[]="HSMODEMSTATUS"; +static char WindowTitle[] = "HSMODEM"; +static int RigControlRow = 205; + +#ifndef LINBPQ +#include +#endif + +extern int SemHeldByAPI; + +static RECT Rect; + +static int ProcessLine(char * buf, int Port); + +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC = TNCInfo[Port]; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if (ptr == NULL) return (TRUE); + + if (*ptr =='#') return (TRUE); // comment + + if (*ptr ==';') return (TRUE); // comment + + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->HSModemInfo = zalloc(sizeof(struct HSMODEMINFO)); + TNC->HSModemInfo->File = zalloc(sizeof(struct HSFILEINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + WINMORport = atoi(p_port); + + TNC->TCPPort = WINMORport; + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(WINMORport + 2); // We only receive on Port + 2 + + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_stricmp(ptr, "PTT") == 0) + { + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + DecodePTTString(TNC, ptr); + ptr = strtok(NULL, " \t\n\r"); + } + } + } + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "CAPTURE", 7) == 0) + { + TNC->HSModemInfo->Capture = _strdup(&buf[8]); + strlop(TNC->HSModemInfo->Capture, 13); + } + else if (_memicmp(buf, "PLAYBACK", 8) == 0) + { + TNC->HSModemInfo->Playback = _strdup(&buf[9]); + strlop(TNC->HSModemInfo->Playback, 13); + } + else if (_memicmp(buf, "MODE ", 5) == 0) + TNC->HSModemInfo->Mode = atoi(&buf[5]); + else if (_memicmp(buf, "LOGDIR ", 7) == 0) + TNC->LogPath = _strdup(&buf[7]); + else + strcat (TNC->InitScript, buf); + } + + return (TRUE); +} + +static char * Config; +static char * ptr1, * ptr2; + +int HSMODEMGetLine(char * buf) +{ +loop: + + if (ptr2 == NULL) + return 0; + + memcpy(buf, ptr1, ptr2 - ptr1 + 2); + buf[ptr2 - ptr1 + 2] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + if (buf[0] < 0x20) goto loop; + if (buf[0] == '#') goto loop; + if (buf[0] == ';') goto loop; + + if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0; + if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0; + buf[strlen(buf)] = 13; + + return 1; +} + +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +static time_t ltime; + + + +static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen) +{ + if (TNC->hDevice) + { + // HSMODEM mode. Queue to Hostmode driver + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = EncLen; + memcpy(&buffptr->Data[0], Encoded, EncLen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + TNC->Streams[Stream].FramesQueued++; + + return; + } +} + + +VOID HSMODEMChangeMYC(struct TNCINFO * TNC, char * Call) +{ + UCHAR TXMsg[100]; + int datalen; + + if (strcmp(Call, TNC->CurrentMYC) == 0) + return; // No Change + + strcpy(TNC->CurrentMYC, Call); + + datalen = sprintf(TXMsg, "MYCALL %s\r", Call); + HSMODEMSendCommand(TNC, TXMsg); +} + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int datalen; + PMSGWITHLEN buffptr; +// char txbuff[500]; + unsigned int bytes,txlen = 0; + UCHAR * TXMsg; + + size_t Param; + int Stream = 0; + HKEY hKey=0; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct ScanEntry * Scan; + + if (TNC == NULL) + return 0; // Port not defined + + switch (fn) + { + case 7: + + // 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + + // G7TAJ's code to record activity for stats display + + if ( TNC->BusyFlags && CDBusy ) + TNC->PortRecord->PORTCONTROL.ACTIVE += 2; + + if ( TNC->PTTState ) + TNC->PortRecord->PORTCONTROL.SENDING += 2; + + if (TNC->CONNECTED) + { + TNC->CONNECTED--; + + if (TNC->CONNECTED == 0) + { + sprintf(TNC->WEB_COMMSSTATE, "Connection to HSMODEM lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + } + + TNC->PollDelay++; + + if (TNC->PollDelay > 20) + { + TNC->PollDelay = 0; + + SendPoll(TNC); + } + + return 0; + + case 1: // poll + + HSMODEMCheckRX(TNC); + + while (TNC->PortRecord->UI_Q) + { + int datalen; + char * Buffer; + char FECMsg[512]; + char Call[12] = " "; + struct _MESSAGE * buffptr; + int CallLen; + char * ptr = FECMsg; + + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + +/* if (TNC->CONNECTED == 0 || + TNC->Streams[0].Connecting || + TNC->Streams[0].Connected) + { + // discard if TNC not connected or sesison active + + ReleaseBuffer(buffptr); + continue; + } +*/ + datalen = buffptr->LENGTH - MSGHDDRLEN; + Buffer = &buffptr->DEST[0]; // Raw Frame + Buffer[datalen] = 0; + + // Frame has ax.25 format header. Convert to Text + + CallLen = ConvFromAX25(Buffer + 7, Call); // Origin + memcpy(ptr, Call, CallLen); + ptr += CallLen; + + *ptr++ = '!'; + + CallLen = ConvFromAX25(Buffer, Call); // Dest + memcpy(ptr, Call, CallLen); + ptr += CallLen; + + Buffer += 14; // TO Digis + datalen -= 14; + + while ((Buffer[-1] & 1) == 0) + { + *ptr++ = ','; + CallLen = ConvFromAX25(Buffer, Call); + memcpy(ptr, Call, CallLen); + ptr += CallLen; + Buffer += 7; // End of addr + datalen -= 7; + } + + *ptr++ = '_'; + *ptr++ = 'U'; // UI Frame + *ptr++ = 0; // delimit calls + + if (Buffer[0] == 3) // UI + { + Buffer += 2; + datalen -= 2; + } + + HSMODEMSendSingleData(TNC, FECMsg, Buffer, datalen); + + ReleaseBuffer(buffptr); + } + + + if (TNC->DiscPending) + { + TNC->DiscPending--; + + if (TNC->DiscPending == 0) + { + // Too long in Disc Pending - Kill and Restart TNC + } + } + + + for (Stream = 0; Stream <= 2; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + HSMODEMSendCommand(TNC, "DISCONNECT\r"); + } + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + Debugprintf("HSMODEM New Attach Stream %d", Stream); + + STREAM->Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, TNC->Streams[Stream].MyCall); + TNC->Streams[Stream].MyCall[calllen] = 0; + + + HSMODEMChangeMYC(TNC, TNC->Streams[0].MyCall); + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + //sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + //MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, Msg); + } + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + } + + // See if any frames for this port + + for (Stream = 0; Stream <= 2; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->BPQtoPACTOR_Q) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)Q_REM(&STREAM->BPQtoPACTOR_Q); + UCHAR * data = &buffptr->Data[0]; + STREAM->FramesQueued--; + txlen = (int)buffptr->Len; + STREAM->bytesTXed += txlen; + + bytes=HSMODEMSendData(TNC, data, txlen); + WritetoTrace(TNC, data, txlen); + } + + if (STREAM->PACTORtoBPQ_Q != 0) + { + buffptr = (PMSGWITHLEN)Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + + if (STREAM->ReportDISC) // May need a delay so treat as a counter + { + STREAM->ReportDISC--; + if (STREAM->ReportDISC == 0) + { + buff->PORT = Stream; +// STREAM->Connected = 0; +// STREAM->Attached = 0; + return -1; + } + } + } + return (0); + + case 2: // send + + Stream = buff->PORT; + + if (!TNC->CONNECTED) + { + // Send Error Response + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = 21; + memcpy(&buffptr->Data[0], "No Connection to TNC\r", 21); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return 0; // Don't try if not connected + } + + STREAM = &TNC->Streams[Stream]; + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; // Discard *** connected + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID + TXMsg = &buff->L2DATA[0]; + TXMsg[txlen] = 0; + + // for now just send, but allow sending control + // characters with \\ or ^ escape + + if (STREAM->Connected) + { + STREAM->PacketsSent++; + + bytes=HSMODEMSendData(TNC, TXMsg, txlen); + TNC->Streams[Stream].BytesOutstanding += bytes; // So flow control works - will be updated by BUFFER response + STREAM->bytesTXed += bytes; +// WritetoTrace(TNC, &buff->L2DATA[0], txlen); + + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "D\r", 2) == 0 || _memicmp(&buff->L2DATA[0], "BYE\r", 4) == 0) + { + STREAM->ReportDISC = TRUE; // Tell Node + return 0; + } + + + // See if Local command (eg RADIO) + + if (_memicmp(&buff->L2DATA[0], "RADIO ", 6) == 0) + { + char cmd[56]; + + strcpy(cmd, &buff->L2DATA[6]); + sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, cmd); + + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &buff->L2DATA[0])) + { + } + else + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", &buff->L2DATA[0]); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "OVERRIDEBUSY", 12) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->OverrideBusy = TRUE; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "HSMODEM} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + + } + + if (_memicmp(&buff->L2DATA[0], "MODE ", 5) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->HSModemInfo->Mode = atoi(&buff->L2DATA[5]); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "HSMODEM} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + SendMode(TNC); + + return 0; + + } + + + // See if a Connect Command. If so, start codec and set Connecting + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char Connect[80]; + char * ptr = strchr(&buff->L2DATA[2], 13); + + if (ptr) + *ptr = 0; + + _strupr(&buff->L2DATA[2]); + + if (strlen(&buff->L2DATA[2]) > 9) + buff->L2DATA[11] = 0; + + txlen = sprintf(Connect, "C %s\r", &buff->L2DATA[2]); + + HSMODEMChangeMYC(TNC, TNC->Streams[0].MyCall); + + // See if Busy + + if (InterlockedCheckBusy(TNC)) + { + // Channel Busy. Unless override set, wait + + if (TNC->OverrideBusy == 0) + { + // Save Command, and wait up to 10 secs + + sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + TNC->ConnectCmd = _strdup(Connect); + TNC->BusyDelay = TNC->BusyWait * 10; // BusyWait secs + return 0; + } + } + + TNC->OverrideBusy = FALSE; + + memset(TNC->Streams[0].RemoteCall, 0, 10); + strcpy(TNC->Streams[0].RemoteCall, &buff->L2DATA[2]); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", STREAM->MyCall, STREAM->RemoteCall); + HSMODEMSendCommand(TNC, Connect); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->Connecting = TRUE; + return 0; + + } + + // Normal data. Send to TNC + + + HSMODEMSendData(TNC, TXMsg, txlen); + + return 0; + + case 3: + + // CHECK IF OK TO SEND (And check TNC Status) + + Stream = (int)(size_t)buff; + + // I think we should check buffer space for all comms modes + + { + int Queued; + int Outstanding = TNC->Streams[Stream].BytesOutstanding; + + if (Stream == 0) + Queued = TNC->Streams[13].FramesQueued; // ARDOP Native Mode Send Queue + else + Queued = TNC->Streams[Stream].FramesQueued; + + Outstanding = Queued = 0; + + if (Queued > 4 || Outstanding > 8500) + return (1 | (TNC->HostMode | TNC->CONNECTED) << 8 | STREAM->Disconnecting << 15); + } + + if (TNC->Streams[Stream].Attached == 0) + return (TNC->CONNECTED != 0) << 8 | 1; + + return ((TNC->CONNECTED != 0) << 8 | TNC->Streams[Stream].Disconnecting << 15); // OK + + + case 4: // reinit7 + + return 0; + + case 5: // Close + + return 0; + + case 6: // Scan Stop Interface + + Param = (size_t)buff; + + if (Param == 2) // Check Permission (Shouldn't happen) + { + Debugprintf("Scan Check Permission called on ARDOP"); + return 1; // OK to change + } + + if (Param == 1) // Request Permission + { + if (!TNC->CONNECTED) + return 0; // No connection so no interlock + + if (TNC->ConnectPending == 0 && TNC->PTTState == 0) + { + HSMODEMSendCommand(TNC, "CONOK OFF"); + TNC->GavePermission = TRUE; + return 0; // OK to Change + } + + if (TNC->ConnectPending) + TNC->ConnectPending--; // Time out if set too long + + return TRUE; + } + + if (Param == 3) // Release Permission + { + if (TNC->GavePermission) + { + TNC->GavePermission = FALSE; + if (TNC->ARDOPCurrentMode[0] != 'S') // Skip + HSMODEMSendCommand(TNC, "CONOK ON"); + } + return 0; + } + + // Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + return 0; + } + return 0; +} + +VOID HSMODEMReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call, and Start Scanner + + UCHAR TXMsg[1000]; + + HSMODEMChangeMYC(TNC, TNC->NodeCall); + + // Start Scanner + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, TXMsg); + + ReleaseOtherPorts(TNC); + +} + +VOID HSMODEMSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) +{ + HSMODEMSendCommand(TNC, "CONOK OFF\r"); +} + +VOID HSMODEMReleasePort(struct TNCINFO * TNC) +{ + HSMODEMSendCommand(TNC, "CONOK ON\r"); +} + + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "" + "VARA Status" + "

HSMODEM Status" + "

", + TNC->Port); + + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); +// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Channel State%s
Proto State%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + +#ifndef LINBPQ + +#define BGCOLOUR RGB(236,233,216) +HBRUSH RedBrush = NULL; +HBRUSH GreenBrush; +HBRUSH BlueBrush; +static HBRUSH bgBrush = NULL; + +extern HWND ClientWnd, FrameWnd; +extern int OffsetH, OffsetW; + +extern HMENU hMainFrameMenu; +extern HMENU hBaseMenu; +extern HANDLE hInstance; + +extern HKEY REGTREE; + + + +static LRESULT CALLBACK PacWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + MINMAXINFO * mmi; + PAINTSTRUCT ps; + HDC hdc; + + int i; + struct TNCINFO * TNC; + + HKEY hKey; + char Key[80]; + int retCode, disp; + + for (i=0; i<41; i++) + { + TNC = TNCInfo[i]; + if (TNC == NULL) + continue; + + if (TNC->hDlg == hWnd) + break; + } + + if (TNC == NULL) + return DefMDIChildProc(hWnd, message, wParam, lParam); + + switch (message) { + + case WM_CREATE: + + break; + + case WM_PAINT: + + hdc = BeginPaint(hWnd, &ps); + + TextOut(hdc, 10, 162, "RX", 4); + TextOut(hdc, 10, 182, "TX", 4); + + if (TNC->HSModemInfo->Sync) + TextOut(hdc, 305, 162, "Sync", 4); + +// SelectObject(ps.hdc, RedBrush); + SelectObject(ps.hdc, GreenBrush); +// SelectObject(ps.hdc, GetStockObject(GRAY_BRUSH)); + + Rectangle(ps.hdc, 40, 165, TNC->HSModemInfo->rxFifo + 42, 175); + SelectObject(ps.hdc, RedBrush); + Rectangle(ps.hdc, 40, 185, (TNC->HSModemInfo->txFifo * 10) + 42, 195); + + EndPaint(hWnd, &ps); + break; + + case WM_GETMINMAXINFO: + + if (TNC->ClientHeight) + { + mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = TNC->ClientWidth; + mmi->ptMaxSize.y = TNC->ClientHeight; + mmi->ptMaxTrackSize.x = TNC->ClientWidth; + mmi->ptMaxTrackSize.y = TNC->ClientHeight; + } + + break; + + + case WM_MDIACTIVATE: + { + + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + + if (TNC->hMenu) + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)TNC->hMenu, "Actions"); + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); + +// SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) TNC->hMenu, (LPARAM) TNC->hWndMenu); + } + else + { + // Deactivate + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + + // call DrawMenuBar after the menu items are set + DrawMenuBar(FrameWnd); + + return DefMDIChildProc(hWnd, message, wParam, lParam); + } + + + + case WM_INITMENUPOPUP: + + if (wParam == (WPARAM)TNC->hMenu) + { + if (TNC->ProgramPath) + { + if (strstr(TNC->ProgramPath, " TNC") || strstr(TNC->ProgramPath, "ARDOP") || strstr(TNC->ProgramPath, "VARA")) + { + EnableMenuItem(TNC->hMenu, WINMOR_RESTART, MF_BYCOMMAND | MF_ENABLED); + EnableMenuItem(TNC->hMenu, WINMOR_KILL, MF_BYCOMMAND | MF_ENABLED); + + break; + } + } + EnableMenuItem(TNC->hMenu, WINMOR_RESTART, MF_BYCOMMAND | MF_GRAYED); + EnableMenuItem(TNC->hMenu, WINMOR_KILL, MF_BYCOMMAND | MF_GRAYED); + } + + break; + + case WM_COMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + case WINMOR_KILL: + + KillTNC(TNC); + break; + + case WINMOR_RESTART: + + KillTNC(TNC); + RestartTNC(TNC); + break; + + case WINMOR_RESTARTAFTERFAILURE: + + TNC->RestartAfterFailure = !TNC->RestartAfterFailure; + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\PACTOR\\PORT%d", TNC->Port); + + retCode = RegCreateKeyEx(REGTREE, Key, 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + RegSetValueEx(hKey,"TNC->RestartAfterFailure",0,REG_DWORD,(BYTE *)&TNC->RestartAfterFailure, 4); + RegCloseKey(hKey); + } + break; + + case ARDOP_ABORT: + + if (TNC->ForcedCloseProc) + TNC->ForcedCloseProc(TNC, 0); + + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_SIZING: + case WM_SIZE: + + MoveWindows(TNC); + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + + case SC_RESTORE: + + TNC->Minimized = FALSE; + break; + + case SC_MINIMIZE: + + TNC->Minimized = TRUE; + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + case WM_DESTROY: + + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); +} +#endif + + + +VOID * HSMODEMExtInit(EXTPORTDATA * PortEntry) +{ + int port; + char Msg[255]; + char * ptr; + struct TNCINFO * TNC; + char * TempScript; + u_long param = 1; + int ret; + + port=PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + +#ifndef LINBPQ + + if (bgBrush == NULL) + { + bgBrush = CreateSolidBrush(BGCOLOUR); + RedBrush = CreateSolidBrush(RGB(255,0,0)); + GreenBrush = CreateSolidBrush(RGB(0,255,0)); + BlueBrush = CreateSolidBrush(RGB(0,0,255)); + } + +#endif + + Consoleprintf("HSMODEM Host %s %d", TNC->HostName, TNC->TCPPort); + + TNC->Port = port; + TNC->PortRecord = PortEntry; + + TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_HSMODEM; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + + if (TNC->PacketChannels > 1) + TNC->PacketChannels = 1; + + PortEntry->MAXHOSTMODESESSIONS = TNC->PacketChannels + 1; + + PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + + PortEntry->PORTCONTROL.UICAPABLE = TRUE; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 236; + + TNC->SuspendPortProc = HSMODEMSuspendPort; + TNC->ReleasePortProc = HSMODEMReleasePort; + + PortEntry->PORTCONTROL.PORTSTARTCODE = KAMStartPort; + PortEntry->PORTCONTROL.PORTSTOPCODE = KAMStopPort; + + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // Set Essential Params and MYCALL + + // Put overridable ones on front, essential ones on end + + TempScript = zalloc(1000); + + // cant think of any yet + + if (TNC->InitScript) + { + strcat(TempScript, TNC->InitScript); + free(TNC->InitScript); + } + + TNC->InitScript = TempScript; + + // Set MYCALL + + sprintf(Msg, "MYCALL %s\r", TNC->NodeCall); + strcat(TNC->InitScript, Msg); + + strcpy(TNC->CurrentMYC, TNC->NodeCall); + + if (TNC->WL2K == NULL) + if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded + TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(20); + TNC->WEB_TRAFFIC = zalloc(100); + + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 450, ForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Proto State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "0 0 0 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC Restarts", WS_CHILD | WS_VISIBLE,10,138,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE,116,138,20,20 , TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(0, "STATIC", "Last Restart", WS_CHILD | WS_VISIBLE,140,138,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,138,200,20, TNC->hDlg, NULL, hInstance, NULL); + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,170,250,300, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill VARA TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart VARA TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTARTAFTERFAILURE, "Restart TNC after failed Connection"); + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session"); + + MoveWindows(TNC); +#endif + + + // Open and bind UDP socket + + TNC->TCPSock = socket(AF_INET,SOCK_DGRAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + WritetoConsole("Failed to create UDP socket for HSMODEM"); + ret = WSAGetLastError(); + } + else + ioctl (TNC->TCPSock, FIONBIO, ¶m); + + ret = bind(TNC->TCPSock, (struct sockaddr *) &TNC->destaddr, sizeof(struct sockaddr_in)); + + if (ret != 0) + { + // Bind Failed + + ret = WSAGetLastError(); + sprintf(Msg, "Bind Failed for UDP port %d - error code = %d", TNC->TCPPort + 2, ret); + WritetoConsole(Msg); + } + + +// SendInitScript(TNC); + + time(&TNC->lasttime); // Get initial time value + + return ExtProc; +} + + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // If all acked, send disc + + if (TNC->Streams[Stream].BytesOutstanding == 0) + HSMODEMSendCommand(TNC, "DISCONNECT\r"); +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + HSMODEMSendCommand(TNC, "DISCONNECT\r"); +} + + + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + if (Stream == 0) + { + HSMODEMReleaseTNC(TNC); + } +} + +VOID HSMODEMAbort(struct TNCINFO * TNC) +{ + HSMODEMSendCommand(TNC, "ABORT\r"); +} + +// Host Mode Stuff (we reuse some routines in SCSPactor) + +VOID HSMODEMDoTermModeTimeout(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 0) + { + //Checking if in Terminal Mode - Try to set back to Term Mode + + TNC->ReinitState = 1; + return; + } + + if (TNC->ReinitState == 1) + { + // Forcing back to Term Mode + + TNC->ReinitState = 0; + return; + } + + if (TNC->ReinitState == 3) + { + return; + } +} + +RECT Rect1 = {30, 160, 400, 195}; + + +VOID HSMODEMProcessTNCMessage(struct TNCINFO * TNC, unsigned char * Msg, int Len) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FileHeader * FH; + struct HSMODEMINFO * Modem = TNC->HSModemInfo; + struct HSFILEINFO * Info = Modem->File; + int fileLen, Seq, Offset; + + // Any message indicates Ok + + Msg[Len] = 0; + + if (TNC->CONNECTED == 0) + { + // Just come up + + sprintf(TNC->WEB_COMMSSTATE, "Connected to HSMODEM"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + SendMode(TNC); + } + + TNC->CONNECTED = 100; // time out after 10 secs + + /* + 3: responses to broadcast messages (see: GUI Interface: UDP/IP/Initialization) + 1: received payload data + 4: FFT data for a spectrum monitor + 5: IQ data for a constellation display + 6: received RTTY characters + */ + switch (Msg[0]) + { + case 1: + /* + Byte 0 ... 0x01 + Byte 1 ... frame type (which was inserted by the sender) + Byte 2 ... frame counter MSB + Byte 3 ... frame counter LSB (10 bits used) + Byte 4 ... frame information (which was inserted by the sender) + Byte 5 ... unused + Byte 6 ... measured line speed MSB + Byte 7 ... measured line speed LSB + Bytes 8-10 ... unused + Bytes 11-229 ... 219 bytes payload return; + + 1 … BER Test Pattern + 2 … Image + 3 … Ascii File + 4 … HTML File + 5 … Binary File + 6 … Voice Audio (for Codec 2 or Opus) + 7 … UserInfo + */ + + Seq = Msg[2] << 8 | Msg[3]; + + switch (Msg[1]) + { + case 1: + case 6: + case 7: + + Debugprintf("%d %d %02x %s %s %s", Msg[1], Seq, Msg[4], &Msg[11], &Msg[31], &Msg[41]); + return; + + case 2: + case 3: + case 4: + case 5: + + // File transfer types + + switch (Msg[4]) + { + case 0: + case 3: + + // File Header + + FH = (struct FileHeader *) &Msg[9]; + + if (FH->CRC == Info->CRC) + { + Debugprintf("Dup Header %X", Info->CRC); + return; + } + + Info->CRC = FH->CRC; + + fileLen = FH->Size[0] * 65536 + FH->Size[1] * 256 + FH->Size[2]; + + Info->Data = zalloc(fileLen + 512); + + if (Info->Data == NULL) + return; + + Info->fileSize = fileLen; + strcpy(Info->fileName, FH->filename); + + memset(Info->goodBlocks, 0, 1024); + Info->goodBlocks[0] = 1; + + Info->lastBlock = 0; + Info->lostBlocks = 0; + Info->LastRX = time(NULL); + + Debugprintf("%d %d %04X %02x %s %d %s", Msg[1], Seq, FH->CRC, Msg[4], + FH->filename, fileLen, FH->Data); + + memcpy(Info->Data, FH->Data, 164); + Info->dataPointer = 164; + break; + + case 1: + case 2: + + // Data Frame + + if (Seq == Info->lastBlock) + { + Debugprintf("Duplicate data frame %d", Seq); + return; + } + + Info->lastBlock++; + + if (Info->lastBlock != Seq) + Info->lostBlocks += Seq - Info->lastBlock; + + Info->goodBlocks[Seq] = 1; + + Offset = (Seq - 1) * 221 + 164; + + memcpy(&Info->Data[Offset], &Msg[11], 221); + + Debugprintf("%d %d %02x %s %d %s", Msg[1], Seq, Msg[4], &Msg[11]); + break; + + default: + + Debugprintf("%d %d %02x %s %d %s", Msg[1], Seq, Msg[4], &Msg[11]); + return; + + } + + // End of Data Frame Case + + if (Msg[4] == 2 || Msg[4] == 3) + { + // Last Frame - check file + + if (Info->lostBlocks == 0) + { + // filename is encoding of calls and frame type + + struct _MESSAGE * buffptr; + +// FILE * fp1 = fopen(Info->fileName, "wb"); +// int WriteLen; + +// if (fp1) +// { +// WriteLen = (int)fwrite(Info->Data, 1, Info->fileSize, fp1); +// fclose(fp1); +// } + + if (strchr(Info->fileName, '!')) + { + // Callsigns encoded in filename + + + + char * Origin = &Info->fileName[0]; + char * Type = strlop(Origin, '_'); + char * Dest = strlop(Origin, '!'); + unsigned char * Packet; + + + // Convert to ax.25 format + + buffptr = GetBuff(); + + // Convert to ax.25 format + + if (buffptr == 0) + return; // No buffers, so ignore + + Type = strlop(Origin, '_'); + + Packet = &buffptr->ORIGIN[0]; + + buffptr->PORT = TNC->Port; + buffptr->LENGTH = 16 + MSGHDDRLEN + Info->fileSize; + + ConvToAX25(Origin, buffptr->ORIGIN); + ConvToAX25(Dest, buffptr->DEST); + + + while (strchr(Dest, ',')) + { + Dest = strlop(Dest, ','); // Next digi + Packet += 7; + ConvToAX25(Dest, Packet); + buffptr->LENGTH += 7; + } + + Packet[6] |= 1; // Set end of address + + Packet += 7; + + *(Packet++) = 3; + *(Packet++) = 0xF0; + + memcpy(Packet, Info->Data, Info->fileSize); + time(&buffptr->Timestamp); + + BPQTRACE((MESSAGE *)buffptr, TRUE); + } + } + + return; + } + } + + return; + + case 4: // FFT data for a spectrum monitor + + Modem->txFifo = Msg[1]; + Modem->rxFifo = Msg[2]; + Modem->DCD = Msg[3]; + Modem->Sync = Msg[4]; + +#ifndef LINBPQ + InvalidateRect(TNC->hDlg, &Rect1, TRUE); +#endif + +// if (Info->Sync || Info->txFifo) +// Debugprintf("%d %d %d %d", Info->txFifo, Info->rxFifo, Info->DCD, Info->Sync); + /* + Byte 0 ... 0x04 + Byte 1 ... usage of the TX fifo (used by the transmitter to sync its data + output to the modem). This is a value between 0..255. During + an active transmission keep it above 4. + Byte 2 ... usage of RX fifo (not important, but can be displayed to the + user). A very high RX fifo usage indicates the the computer + is too slow for HSmodem. + Byte 3 ... 0 or 1. Indicates that an RF level was detected + Byte 4 ... 0 or 1. Indicates that the HSmodem receiver is synchronized + with a signal + Byte 5 ... maximum audio level (0..100%) of the audio input from the + transceiver. Can be used to detect clipping. + Byte 6 ... maximum audio level (0..100%) of the audio output to the + transceiver. Can be used to detect clipping. + Byte 7 ... in RTTY mode this is the auto-locked RTTY frequency MSB + Byte 8 ... and LSB + Byte 9 ... RTTY: 0=tx off, 1=txon + Byte 10 to the end ... FFT spectrum data, beginning at 0 Hz to 4kHz with + a resolution of 10 Hz +*/ + return; + + + case 5: // IQ data for a constellation display + return; + + case 6: //received RTTY characters + return; + } + + return; + + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", TNC->RXBuffer); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + +} + +extern char LOC[7]; + +void SendMode(struct TNCINFO * TNC) +{ + unsigned char Msg[221] = ""; + int ret; + + Msg[0] = 16; + Msg[1] = TNC->HSModemInfo->Mode; + + TNC->destaddr.sin_port = htons(TNC->TCPPort + 1); // Data Port + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + ret = sendto(TNC->TCPSock, (char *)&Msg, 221, 0, (struct sockaddr *)&TNC->destaddr, sizeof(struct sockaddr)); + + return; +} + +void SendPoll(struct TNCINFO * TNC) +{ + struct BroadcastMsg PollMsg = {0x3c, 100, 100, 0, 50, 50, 1, 0, 0, 9}; + int ret; + + strcpy(&PollMsg.captureDevice[0], TNC->HSModemInfo->Capture); + strcpy(&PollMsg.playbackDevice[0], TNC->HSModemInfo->Playback); +// strcpy(&PollMsg.playbackDevice[0], "CABLE Input (VB-Audio Virtual Cable)"); + + strcpy(&PollMsg.Callsign[0], TNC->NodeCall); + strcpy(&PollMsg.Locator[0], LOC); + strcpy(&PollMsg.Name[0], "1234567890"); + + TNC->destaddr.sin_port = htons(TNC->TCPPort); // Command Port + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + ret = sendto(TNC->TCPSock, (char *)&PollMsg, 260, 0, (struct sockaddr *)&TNC->destaddr, sizeof(struct sockaddr)); + + return; +} +/* + unsigned char Type; + unsigned char Info; // 0 - First, 1 - Continuation 2 Last 3 - Only + char filename[50]; + unsigned short CRC; // of filename = transfer id + unsigned char Size[3]; // Big endian + unsigned char Data[163]; +*/ + +unsigned short int compute_crc(unsigned char *buf,int len); + +int HSMODEMSendSingleData(struct TNCINFO * TNC, UCHAR * FN, UCHAR * data, int txlen) +{ + struct FileHeader Msg; + unsigned short int crc; + int ret, fragLen = txlen; + struct HSMODEMINFO * Modem = TNC->HSModemInfo; + struct HSFILEINFO * Info = Modem->File; + + char Seq[60] = ""; + + sprintf(Seq, "%04X%s", Modem->Seq++, FN); + + crc = compute_crc(Seq, 60); + + crc ^= 0xffff; + + memset(&Msg, 0, sizeof(struct FileHeader)); + + Msg.Type = 5; // Binary Data + Msg.Info = 3; // Only Fragment + + if (txlen > 163) + { + // Need to send as multiple fragments + + fragLen = 164; + Info->txData = malloc(txlen + 512); + memcpy(Info->txData, data, txlen); + Info->txSize = txlen; + Info->txLeft = txlen - 164; + Msg.Info = 0; // First Fragment + } + + strcpy(Msg.filename, FN); + memcpy(Msg.Data, data, txlen); + memcpy(&Msg.CRC, &crc, 2); + Msg.Size[0] = txlen >> 16; + Msg.Size[1] = txlen >> 8;; + Msg.Size[2] = txlen; + + TNC->destaddr.sin_port = htons(TNC->TCPPort + 1); // Data Port + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + ret = sendto(TNC->TCPSock, (char *)&Msg, 221, 0, (struct sockaddr *)&TNC->destaddr, sizeof(struct sockaddr)); + memset(&Msg, 0, sizeof(struct FileHeader)); + + return ret; +} + +int HSMODEMSendData(struct TNCINFO * TNC, UCHAR * data, int txlen) +{ + struct FileHeader Msg; + + memset(&Msg, 0, sizeof(struct FileHeader)); + + Msg.Type = 5; // Binary Data + Msg.Info = 3; // Only Fragment + + + return 0; +} + +void HSMODEMCheckRX(struct TNCINFO * TNC) +{ + int Len = 0; + unsigned char Buff[2000]; + + struct sockaddr_in rxaddr; + int addrlen = sizeof(struct sockaddr_in); + + Len = recvfrom(TNC->TCPSock, Buff, 2000, 0, (struct sockaddr *)&rxaddr, &addrlen); + + while (1) + { + if (Len == -1) + { +// Debugprintf("%d", GetLastError()); + Len = 0; + return; + } + TNC->RXLen = Len; + HSMODEMProcessTNCMessage(TNC, Buff, Len); + + Len = recvfrom(TNC->TCPSock, Buff, 2000, 0, (struct sockaddr *)&rxaddr, &addrlen); + + } + return; + + return; +} + +int HSMODEMWriteCommBlock(struct TNCINFO * TNC) +{ + if (TNC->hDevice) + return WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); + + return 0; +} + + +int HSMODEMSendCommand(struct TNCINFO * TNC, UCHAR * data) +{ + if (TNC->hDevice) + return WriteCOMBlock(TNC->hDevice, data, (int)strlen(data)); + + return 0; +} + \ No newline at end of file diff --git a/.svn/pristine/01/0166e0c783c5b8e5a6c55b1369dfe5459e2f71f2.svn-base b/.svn/pristine/01/0166e0c783c5b8e5a6c55b1369dfe5459e2f71f2.svn-base index 5a18429..fdbb96b 100644 --- a/.svn/pristine/01/0166e0c783c5b8e5a6c55b1369dfe5459e2f71f2.svn-base +++ b/.svn/pristine/01/0166e0c783c5b8e5a6c55b1369dfe5459e2f71f2.svn-base @@ -1,243 +1,243 @@ -/* -Copyright 2001-2022 John Wiseman G8BPQ - -This file is part of LinBPQ/BPQ32. - -LinBPQ/BPQ32 is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -LinBPQ/BPQ32 is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses -*/ - -#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers - -#define _CRT_SECURE_NO_DEPRECATE - -#include "compatbits.h" -#include -#include "asmstrucs.h" -#include "tncinfo.h" - -VOID __cdecl Debugprintf(const char * format, ...); - -#ifndef WIN32 - -#define APIENTRY -#define DllExport -#define VOID void - -#else -#include -#endif - -extern BOOL EventsEnabled; -void MQTTReportSession(char * Msg); -extern int MQTT; - - -extern char Modenames[19][10]; - -// Runs use specified routine on certain event -#ifndef WIN32 - -void RunEventProgram(char * Program, char * Param) -{ - char * arg_list[] = {Program, NULL, NULL}; - pid_t child_pid; - - if (EventsEnabled == 0) - return; - - signal(SIGCHLD, SIG_IGN); // Silently (and portably) reap children. - - if (Param && Param[0]) - arg_list[1] = Param; - - // Fork and Exec Specified program - - // Duplicate this process. - - child_pid = fork (); - - if (child_pid == -1) - { - printf ("Event fork() Failed\n"); - return; - } - - if (child_pid == 0) - { - execvp (arg_list[0], arg_list); - - // The execvp function returns only if an error occurs. - - printf ("Failed to run %s\n", arg_list[0]); - exit(0); // Kill the new process - } - -#else - -DllExport void APIENTRY RunEventProgram(char * Program, char * Param) -{ - int n = 0; - char cmdLine[256]; - - STARTUPINFO SInfo; // pointer to STARTUPINFO - PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION - - if (EventsEnabled == 0) - return; - - - SInfo.cb=sizeof(SInfo); - SInfo.lpReserved=NULL; - SInfo.lpDesktop=NULL; - SInfo.lpTitle=NULL; - SInfo.dwFlags=0; - SInfo.cbReserved2=0; - SInfo.lpReserved2=NULL; - - sprintf(cmdLine, "%s %s", Program, Param); - - if (!CreateProcess(NULL, cmdLine, NULL, NULL, FALSE,0 ,NULL ,NULL, &SInfo, &PInfo)) - Debugprintf("Failed to Start %s Error %d ", Program, GetLastError()); - -#endif - - return; -} - -void hookL2SessionAccepted(int Port, char * remotecall, char * ourcall, struct _LINKTABLE * LINK) -{ - // Incoming SABM - - LINK->ConnectTime = time(NULL); - LINK->bytesTXed = LINK->bytesRXed = 0; - - strcpy(LINK->callingCall, remotecall); - strcpy(LINK->receivingCall, ourcall); - strcpy(LINK->Direction, "In"); -} - -void hookL2SessionDeleted(struct _LINKTABLE * LINK) -{ - // calculate session time and av bytes/min in and out - - if (LINK->ConnectTime) - { - if (LINK->bytesTXed == 0 && LINK->bytesRXed == 0) - { - // assume failed connect and ignore for now - maybe log later - - } - else - { - char Msg[256]; - char timestamp[16]; - time_t sessionTime = time(NULL) - LINK->ConnectTime; - double avBytesSent = LINK->bytesTXed / (sessionTime / 60.0); - double avBytesRXed = LINK->bytesRXed / (sessionTime / 60.0); - time_t Now = time(NULL); - struct tm * TM = localtime(&Now); - - sprintf(timestamp, "%02d:%02d:%02d", TM->tm_hour, TM->tm_min, TM->tm_sec); - - if (sessionTime == 0) - sessionTime = 1; // Or will get divide by zero error - - Debugprintf("KISS Session Stats Port %d %s %s %d secs Bytes Sent %d BPM %4.2f Bytes Received %d %4.2f BPM ", - LINK->LINKPORT->PORTNUMBER, LINK->callingCall, LINK->receivingCall, sessionTime, LINK->bytesTXed, avBytesSent, LINK->bytesRXed, avBytesRXed, timestamp); - - - sprintf(Msg, "{\"mode\": \"%s\", \"direction\": \"%s\", \"port\": %d, \"callfrom\": \"%s\", \"callto\": \"%s\", \"time\": %d, \"bytesSent\": %d," - "\"BPMSent\": %4.2f, \"BytesReceived\": %d, \"BPMReceived\": %4.2f, \"timestamp\": \"%s\"}", - "KISS", LINK->Direction, LINK->LINKPORT->PORTNUMBER, LINK->callingCall, LINK->receivingCall, sessionTime, - LINK->bytesTXed, avBytesSent, LINK->bytesRXed, avBytesRXed, timestamp); - - if (MQTT) - MQTTReportSession(Msg); - } - - LINK->ConnectTime = 0; - } - - if (LINK->Sent && LINK->Received && (LINK->SentAfterCompression || LINK->ReceivedAfterExpansion)) - Debugprintf("L2 Compression Stats %s %s TX %d %d %d%% RX %d %d %d%%", LINK->callingCall, LINK->receivingCall, - LINK->Sent, LINK->SentAfterCompression, ((LINK->Sent - LINK->SentAfterCompression) * 100) / LINK->Sent, - LINK->Received, LINK->ReceivedAfterExpansion, ((LINK->ReceivedAfterExpansion - LINK->Received) * 100) / LINK->Received); - -} - -void hookL2SessionAttempt(int Port, char * ourcall, char * remotecall, struct _LINKTABLE * LINK) -{ - LINK->ConnectTime = time(NULL); - LINK->bytesTXed = LINK->bytesRXed = 0; - - strcpy(LINK->callingCall, ourcall); - strcpy(LINK->receivingCall, remotecall); - strcpy(LINK->Direction, "Out"); -} - -void hookL4SessionAttempt(struct STREAMINFO * STREAM, char * remotecall, char * ourcall) -{ - // Outgoing Connect - - STREAM->ConnectTime = time(NULL); - STREAM->bytesTXed = STREAM->bytesRXed = 0; - - strcpy(STREAM->callingCall, ourcall); - strcpy(STREAM->receivingCall, remotecall); - strcpy(STREAM->Direction, "Out"); -} - -void hookL4SessionAccepted(struct STREAMINFO * STREAM, char * remotecall, char * ourcall) -{ - // Incoming Connect - - STREAM->ConnectTime = time(NULL); - STREAM->bytesTXed = STREAM->bytesRXed = 0; - - strcpy(STREAM->callingCall, remotecall); - strcpy(STREAM->receivingCall, ourcall); - strcpy(STREAM->Direction, "In"); -} - -void hookL4SessionDeleted(struct TNCINFO * TNC, struct STREAMINFO * STREAM) -{ - char Msg[256]; - - char timestamp[16]; - - if (STREAM->ConnectTime) - { - time_t sessionTime = time(NULL) - STREAM->ConnectTime; - double avBytesRXed = STREAM->bytesRXed / (sessionTime / 60.0); - double avBytesSent = STREAM->bytesTXed / (sessionTime / 60.0); - time_t Now = time(NULL); - struct tm * TM = localtime(&Now); - sprintf(timestamp, "%02d:%02d:%02d", TM->tm_hour, TM->tm_min, TM->tm_sec); - - if (sessionTime == 0) - sessionTime = 1; // Or will get divide by zero error - - sprintf(Msg, "{\"mode\": \"%s\", \"direction\": \"%s\", \"port\": %d, \"callfrom\": \"%s\", \"callto\": \"%s\", \"time\": %d, \"bytesSent\": %d," - "\"BPMSent\": %4.2f, \"BytesReceived\": %d, \"BPMReceived\": %4.2f, \"timestamp\": \"%s\"}", - Modenames[TNC->Hardware - 1], STREAM->Direction, TNC->Port, STREAM->callingCall, STREAM->receivingCall, sessionTime, - STREAM->bytesTXed, avBytesSent, STREAM->bytesRXed, avBytesRXed, timestamp); - - if (MQTT) - MQTTReportSession(Msg); - - STREAM->ConnectTime = 0; - } -} - - +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +#define _CRT_SECURE_NO_DEPRECATE + +#include "compatbits.h" +#include +#include "asmstrucs.h" +#include "tncinfo.h" + +VOID __cdecl Debugprintf(const char * format, ...); + +#ifndef WIN32 + +#define APIENTRY +#define DllExport +#define VOID void + +#else +#include +#endif + +extern BOOL EventsEnabled; +void MQTTReportSession(char * Msg); +extern int MQTT; + + +extern char Modenames[19][10]; + +// Runs use specified routine on certain event +#ifndef WIN32 + +void RunEventProgram(char * Program, char * Param) +{ + char * arg_list[] = {Program, NULL, NULL}; + pid_t child_pid; + + if (EventsEnabled == 0) + return; + + signal(SIGCHLD, SIG_IGN); // Silently (and portably) reap children. + + if (Param && Param[0]) + arg_list[1] = Param; + + // Fork and Exec Specified program + + // Duplicate this process. + + child_pid = fork (); + + if (child_pid == -1) + { + printf ("Event fork() Failed\n"); + return; + } + + if (child_pid == 0) + { + execvp (arg_list[0], arg_list); + + // The execvp function returns only if an error occurs. + + printf ("Failed to run %s\n", arg_list[0]); + exit(0); // Kill the new process + } + +#else + +DllExport void APIENTRY RunEventProgram(char * Program, char * Param) +{ + int n = 0; + char cmdLine[256]; + + STARTUPINFO SInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION + + if (EventsEnabled == 0) + return; + + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + sprintf(cmdLine, "%s %s", Program, Param); + + if (!CreateProcess(NULL, cmdLine, NULL, NULL, FALSE,0 ,NULL ,NULL, &SInfo, &PInfo)) + Debugprintf("Failed to Start %s Error %d ", Program, GetLastError()); + +#endif + + return; +} + +void hookL2SessionAccepted(int Port, char * remotecall, char * ourcall, struct _LINKTABLE * LINK) +{ + // Incoming SABM + + LINK->ConnectTime = time(NULL); + LINK->bytesTXed = LINK->bytesRXed = 0; + + strcpy(LINK->callingCall, remotecall); + strcpy(LINK->receivingCall, ourcall); + strcpy(LINK->Direction, "In"); +} + +void hookL2SessionDeleted(struct _LINKTABLE * LINK) +{ + // calculate session time and av bytes/min in and out + + if (LINK->ConnectTime) + { + if (LINK->bytesTXed == 0 && LINK->bytesRXed == 0) + { + // assume failed connect and ignore for now - maybe log later + + } + else + { + char Msg[256]; + char timestamp[16]; + time_t sessionTime = time(NULL) - LINK->ConnectTime; + double avBytesSent = LINK->bytesTXed / (sessionTime / 60.0); + double avBytesRXed = LINK->bytesRXed / (sessionTime / 60.0); + time_t Now = time(NULL); + struct tm * TM = localtime(&Now); + + sprintf(timestamp, "%02d:%02d:%02d", TM->tm_hour, TM->tm_min, TM->tm_sec); + + if (sessionTime == 0) + sessionTime = 1; // Or will get divide by zero error + + Debugprintf("KISS Session Stats Port %d %s %s %d secs Bytes Sent %d BPM %4.2f Bytes Received %d %4.2f BPM ", + LINK->LINKPORT->PORTNUMBER, LINK->callingCall, LINK->receivingCall, sessionTime, LINK->bytesTXed, avBytesSent, LINK->bytesRXed, avBytesRXed, timestamp); + + + sprintf(Msg, "{\"mode\": \"%s\", \"direction\": \"%s\", \"port\": %d, \"callfrom\": \"%s\", \"callto\": \"%s\", \"time\": %d, \"bytesSent\": %d," + "\"BPMSent\": %4.2f, \"BytesReceived\": %d, \"BPMReceived\": %4.2f, \"timestamp\": \"%s\"}", + "KISS", LINK->Direction, LINK->LINKPORT->PORTNUMBER, LINK->callingCall, LINK->receivingCall, sessionTime, + LINK->bytesTXed, avBytesSent, LINK->bytesRXed, avBytesRXed, timestamp); + + if (MQTT) + MQTTReportSession(Msg); + } + + LINK->ConnectTime = 0; + } + + if (LINK->Sent && LINK->Received && (LINK->SentAfterCompression || LINK->ReceivedAfterExpansion)) + Debugprintf("L2 Compression Stats %s %s TX %d %d %d%% RX %d %d %d%%", LINK->callingCall, LINK->receivingCall, + LINK->Sent, LINK->SentAfterCompression, ((LINK->Sent - LINK->SentAfterCompression) * 100) / LINK->Sent, + LINK->Received, LINK->ReceivedAfterExpansion, ((LINK->ReceivedAfterExpansion - LINK->Received) * 100) / LINK->Received); + +} + +void hookL2SessionAttempt(int Port, char * ourcall, char * remotecall, struct _LINKTABLE * LINK) +{ + LINK->ConnectTime = time(NULL); + LINK->bytesTXed = LINK->bytesRXed = 0; + + strcpy(LINK->callingCall, ourcall); + strcpy(LINK->receivingCall, remotecall); + strcpy(LINK->Direction, "Out"); +} + +void hookL4SessionAttempt(struct STREAMINFO * STREAM, char * remotecall, char * ourcall) +{ + // Outgoing Connect + + STREAM->ConnectTime = time(NULL); + STREAM->bytesTXed = STREAM->bytesRXed = 0; + + strcpy(STREAM->callingCall, ourcall); + strcpy(STREAM->receivingCall, remotecall); + strcpy(STREAM->Direction, "Out"); +} + +void hookL4SessionAccepted(struct STREAMINFO * STREAM, char * remotecall, char * ourcall) +{ + // Incoming Connect + + STREAM->ConnectTime = time(NULL); + STREAM->bytesTXed = STREAM->bytesRXed = 0; + + strcpy(STREAM->callingCall, remotecall); + strcpy(STREAM->receivingCall, ourcall); + strcpy(STREAM->Direction, "In"); +} + +void hookL4SessionDeleted(struct TNCINFO * TNC, struct STREAMINFO * STREAM) +{ + char Msg[256]; + + char timestamp[16]; + + if (STREAM->ConnectTime) + { + time_t sessionTime = time(NULL) - STREAM->ConnectTime; + double avBytesRXed = STREAM->bytesRXed / (sessionTime / 60.0); + double avBytesSent = STREAM->bytesTXed / (sessionTime / 60.0); + time_t Now = time(NULL); + struct tm * TM = localtime(&Now); + sprintf(timestamp, "%02d:%02d:%02d", TM->tm_hour, TM->tm_min, TM->tm_sec); + + if (sessionTime == 0) + sessionTime = 1; // Or will get divide by zero error + + sprintf(Msg, "{\"mode\": \"%s\", \"direction\": \"%s\", \"port\": %d, \"callfrom\": \"%s\", \"callto\": \"%s\", \"time\": %d, \"bytesSent\": %d," + "\"BPMSent\": %4.2f, \"BytesReceived\": %d, \"BPMReceived\": %4.2f, \"timestamp\": \"%s\"}", + Modenames[TNC->Hardware - 1], STREAM->Direction, TNC->Port, STREAM->callingCall, STREAM->receivingCall, sessionTime, + STREAM->bytesTXed, avBytesSent, STREAM->bytesRXed, avBytesRXed, timestamp); + + if (MQTT) + MQTTReportSession(Msg); + + STREAM->ConnectTime = 0; + } +} + + diff --git a/.svn/pristine/01/01ea0d74258aeb1aaca3b61c3205e84a4ae6371b.svn-base b/.svn/pristine/01/01ea0d74258aeb1aaca3b61c3205e84a4ae6371b.svn-base index 34ec376..48f87d0 100644 --- a/.svn/pristine/01/01ea0d74258aeb1aaca3b61c3205e84a4ae6371b.svn-base +++ b/.svn/pristine/01/01ea0d74258aeb1aaca3b61c3205e84a4ae6371b.svn-base @@ -1,111 +1,111 @@ -#ifndef TELNETSERVER -#define TELNETSERVER - -#ifndef LINBPQ -//#include "resource.h" -#endif -#define WSA_ACCEPT WM_USER + 1 -#define WSA_CONNECT WM_USER + 2 -#define WSA_DATA WM_USER + 3 - -#define InputBufferLen 100000 - -struct ConnectionInfo -{ - int Number; // Number of record - for Connections display - SOCKET socket; - union - { - struct sockaddr_in6 sin6; - struct sockaddr_in sin; - }; - BOOL SocketActive; - int BPQStream; - char Callsign[10]; - BOOL GotHeader; - unsigned char InputBuffer[InputBufferLen]; - int InputLen; - struct UserRec * UserPointer; - int Retries; - int LoginState; // 1 = user ok, 2 = password ok - BOOL DoingCommand; // Processing Telnet Command - BOOL DoEcho; // Telnet Echo option accepted - BOOL CMSSession; // Set if connect to CMS - BOOL FBBMode; // Pure TCP for FBB forwarding - BOOL NeedLF; // FBB mode, but with cr > crlf outbound - BOOL RelayMode; // Pure TCP for RMS Relay Emulation forwarding - BOOL DRATSMode; // HTML Terminal Emulator - BOOL SyncMode; // RMS Relay Sync - BOOL HTTPMode; // HTTP Server - BOOL APIMode; // REST API Server - BOOL TriMode; // Trimode emulation - BOOL TriModeConnected; // Set when remote session is connected - now send data to DataSock - SOCKET TriModeDataSock; // Data Socket - BOOL Auth; // Set if User is flagged as a Secure User - BOOL BPQTermMode; // Set if connected to BPQTermTCP - BOOL ClientSession; // Set if acting as a client (ie Linux HOST Mode) - BOOL MonitorNODES; // Monitor Control Flags - unsigned long long MMASK; - BOOL MCOM; - BOOL MonitorColour; - BOOL MTX; - BOOL MUIOnly; - int CMSIndex; // Pointer to CMS used for this connect - UCHAR * FromHostBuffer; // Somewhere to store msg from CMS - it sends the whole message at once - int FromHostBufferSize; - int FromHostBuffPutptr; - int FromHostBuffGetptr; - - struct TNCINFO * TNC; // Used to pass TCP struct to http code (for passwood list) - - time_t ConnectTime; - BOOL UTF8; // Set if BPQTerminal in UTF8 Mode - BOOL RelaySession; // Set if connection to RMS Relay - BOOL LogonSent; // To ignore second callsign: prompt - char Signon[100]; // User/Pass/Appl for Outgoing Connects - BOOL Keepalive; // For HOST (esp CCC) Keepalives - time_t LastSendTime; - BOOL NoCallsign; // Don't Send Callsign to host if no Signon - UCHAR * ResendBuffer; // Used if send() returns EWOULDBLOCK - int ResendLen; // Len to resend - - struct ADIF * ADIF; // ADIF Logging info - int WebSocks; - char WebURL[32]; // URL for WebSocket Connection - int WebSecure; // Set if secure session -}; - - -#define Disconnect(stream) SessionControl(stream,2,0) -#define Connect(stream) SessionControl(stream,1,0) - -#define SE 240 // End of subnegotiation parameters -#define NOP 241 //No operation -#define xDM 242 //Data mark Indicates the position of a Synch event within the data stream. This should always be accompanied by a TCP urgent notification. -#define BRK 243 //Break Indicates that the "break" or "attention" key was hi. -#define IPx 244 //Suspend Interrupt or abort the process to which the NVT is connected. -#define AO 245 //Abort output Allows the current process to run to completion but does not send its output to the user. -#define AYT 246 //Are you there Send back to the NVT some visible evidence that the AYT was received. -#define EC 247 //Erase character The receiver should delete the last preceding undeleted character from the data stream. -#define EL 248 //Erase line Delete characters from the data stream back to but not including the previous CRLF. -#define GA 249 //Go ahead Under certain circumstances used to tell the other end that it can transmit. -#define SB 250 //Subnegotiation Subnegotiation of the indicated option follows. -#define WILL 251 //will Indicates the desire to begin performing, or confirmation that you are now performing, the indicated option. -#define WONT 252 //wont Indicates the refusal to perform, or continue performing, the indicated option. -#define DOx 253 //do Indicates the request that the other party perform, or confirmation that you are expecting the other party to perform, the indicated option. -#define DONT 254 //dont Indicates the demand that the other party stop performing, or confirmation that you are no longer expecting the other party to perform, the indicated option. -#define IAC 255 - -#define suppressgoahead 3 //858 -#define xStatus 5 //859 -#define echo 1 //857 -#define timingmark 6 //860 -#define terminaltype 24 //1091 -#define windowsize 31 //1073 -#define terminalspeed 32 //1079 -#define remoteflowcontrol 33 //1372 -#define linemode 34 //1184 -#define environmentvariables 36 //1408 - -#endif - +#ifndef TELNETSERVER +#define TELNETSERVER + +#ifndef LINBPQ +//#include "resource.h" +#endif +#define WSA_ACCEPT WM_USER + 1 +#define WSA_CONNECT WM_USER + 2 +#define WSA_DATA WM_USER + 3 + +#define InputBufferLen 100000 + +struct ConnectionInfo +{ + int Number; // Number of record - for Connections display + SOCKET socket; + union + { + struct sockaddr_in6 sin6; + struct sockaddr_in sin; + }; + BOOL SocketActive; + int BPQStream; + char Callsign[10]; + BOOL GotHeader; + unsigned char InputBuffer[InputBufferLen]; + int InputLen; + struct UserRec * UserPointer; + int Retries; + int LoginState; // 1 = user ok, 2 = password ok + BOOL DoingCommand; // Processing Telnet Command + BOOL DoEcho; // Telnet Echo option accepted + BOOL CMSSession; // Set if connect to CMS + BOOL FBBMode; // Pure TCP for FBB forwarding + BOOL NeedLF; // FBB mode, but with cr > crlf outbound + BOOL RelayMode; // Pure TCP for RMS Relay Emulation forwarding + BOOL DRATSMode; // HTML Terminal Emulator + BOOL SyncMode; // RMS Relay Sync + BOOL HTTPMode; // HTTP Server + BOOL APIMode; // REST API Server + BOOL TriMode; // Trimode emulation + BOOL TriModeConnected; // Set when remote session is connected - now send data to DataSock + SOCKET TriModeDataSock; // Data Socket + BOOL Auth; // Set if User is flagged as a Secure User + BOOL BPQTermMode; // Set if connected to BPQTermTCP + BOOL ClientSession; // Set if acting as a client (ie Linux HOST Mode) + BOOL MonitorNODES; // Monitor Control Flags + unsigned long long MMASK; + BOOL MCOM; + BOOL MonitorColour; + BOOL MTX; + BOOL MUIOnly; + int CMSIndex; // Pointer to CMS used for this connect + UCHAR * FromHostBuffer; // Somewhere to store msg from CMS - it sends the whole message at once + int FromHostBufferSize; + int FromHostBuffPutptr; + int FromHostBuffGetptr; + + struct TNCINFO * TNC; // Used to pass TCP struct to http code (for passwood list) + + time_t ConnectTime; + BOOL UTF8; // Set if BPQTerminal in UTF8 Mode + BOOL RelaySession; // Set if connection to RMS Relay + BOOL LogonSent; // To ignore second callsign: prompt + char Signon[100]; // User/Pass/Appl for Outgoing Connects + BOOL Keepalive; // For HOST (esp CCC) Keepalives + time_t LastSendTime; + BOOL NoCallsign; // Don't Send Callsign to host if no Signon + UCHAR * ResendBuffer; // Used if send() returns EWOULDBLOCK + int ResendLen; // Len to resend + + struct ADIF * ADIF; // ADIF Logging info + int WebSocks; + char WebURL[32]; // URL for WebSocket Connection + int WebSecure; // Set if secure session +}; + + +#define Disconnect(stream) SessionControl(stream,2,0) +#define Connect(stream) SessionControl(stream,1,0) + +#define SE 240 // End of subnegotiation parameters +#define NOP 241 //No operation +#define xDM 242 //Data mark Indicates the position of a Synch event within the data stream. This should always be accompanied by a TCP urgent notification. +#define BRK 243 //Break Indicates that the "break" or "attention" key was hi. +#define IPx 244 //Suspend Interrupt or abort the process to which the NVT is connected. +#define AO 245 //Abort output Allows the current process to run to completion but does not send its output to the user. +#define AYT 246 //Are you there Send back to the NVT some visible evidence that the AYT was received. +#define EC 247 //Erase character The receiver should delete the last preceding undeleted character from the data stream. +#define EL 248 //Erase line Delete characters from the data stream back to but not including the previous CRLF. +#define GA 249 //Go ahead Under certain circumstances used to tell the other end that it can transmit. +#define SB 250 //Subnegotiation Subnegotiation of the indicated option follows. +#define WILL 251 //will Indicates the desire to begin performing, or confirmation that you are now performing, the indicated option. +#define WONT 252 //wont Indicates the refusal to perform, or continue performing, the indicated option. +#define DOx 253 //do Indicates the request that the other party perform, or confirmation that you are expecting the other party to perform, the indicated option. +#define DONT 254 //dont Indicates the demand that the other party stop performing, or confirmation that you are no longer expecting the other party to perform, the indicated option. +#define IAC 255 + +#define suppressgoahead 3 //858 +#define xStatus 5 //859 +#define echo 1 //857 +#define timingmark 6 //860 +#define terminaltype 24 //1091 +#define windowsize 31 //1073 +#define terminalspeed 32 //1079 +#define remoteflowcontrol 33 //1372 +#define linemode 34 //1184 +#define environmentvariables 36 //1408 + +#endif + diff --git a/.svn/pristine/03/037b7a71a9a62b41bdee83bd30d4b81006bcecf1.svn-base b/.svn/pristine/03/037b7a71a9a62b41bdee83bd30d4b81006bcecf1.svn-base index 387582e..f703662 100644 --- a/.svn/pristine/03/037b7a71a9a62b41bdee83bd30d4b81006bcecf1.svn-base +++ b/.svn/pristine/03/037b7a71a9a62b41bdee83bd30d4b81006bcecf1.svn-base @@ -1,1560 +1,1560 @@ -/* -Copyright 2001-2018 John Wiseman G8BPQ - -This file is part of LinBPQ/BPQ32. - -LinBPQ/BPQ32 is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -LinBPQ/BPQ32 is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without Fvoideven the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses -*/ - -// Mail and Chat Server for BPQ32 Packet Switch -// -// White Pages Database Support Routines - -#include "bpqmail.h" - -#define GetSemaphore(Semaphore,ID) _GetSemaphore(Semaphore, ID, __FILE__, __LINE__) -void _GetSemaphore(struct SEM * Semaphore, int ID, char * File, int Line); - -int CurrentWPIndex; -char CurrentWPCall[10]; - -time_t LASTWPSendTime; - - -VOID DoWPUpdate(WPRec *WP, char Type, char * Name, char * HA, char * QTH, char * ZIP, time_t WPDate); -VOID Do_Save_WPRec(HWND hDlg); -VOID SaveInt64Value(config_setting_t * group, char * name, long long value); -VOID SaveIntValue(config_setting_t * group, char * name, int value); -VOID SaveStringValue(config_setting_t * group, char * name, char * value); -BOOL GetStringValue(config_setting_t * group, char * name, char * value, int maxlen); -void MQTTMessageEvent(void* message); - -WPRec * AllocateWPRecord() -{ - WPRec * WP = zalloc(sizeof (WPRec)); - - GetSemaphore(&AllocSemaphore, 0); - - WPRecPtr=realloc(WPRecPtr,(++NumberofWPrecs+1) * sizeof(void *)); - WPRecPtr[NumberofWPrecs]= WP; - - FreeSemaphore(&AllocSemaphore); - - return WP; -} - -int BadCall(char * Call) -{ - if (_stricmp(Call, "RMS") == 0) - return 1; - - if (_stricmp(Call, "SYSTEM") == 0) - return 1; - - if (_stricmp(Call, "SWITCH") == 0) - return 1; - - if (_stricmp(Call, "SYSOP") == 0) - return 1; - - if (_memicmp(Call, "SMTP", 4) == 0) - return 1; - - if (_memicmp(Call, "SMTP:", 5) == 0) - return 1; - - if (_stricmp(Call, "AMPR") == 0) - return 1; - - if (_stricmp(Call, "FILE") == 0) - return 1; - - if (_memicmp(Call, "MCAST", 5) == 0) - return 1; - - if (_memicmp(Call, "SYNC", 5) == 0) - return 1; - - return 0; -} - -extern config_t cfg; - -VOID GetWPDatabase() -{ - WPRec WPRec; - FILE * Handle; - int ReadLen; - WPRecP WP; - char CfgName[MAX_PATH]; - long long val; - config_t wpcfg; - config_setting_t * group, * wpgroup; - int i = 1; - struct stat STAT; - - // If WP info is in main config file, use it - - group = config_lookup (&cfg, "WP"); - - if (group) - { - // Set up control record - - WPRecPtr = malloc(sizeof(void *)); - WPRecPtr[0] = zalloc(sizeof(WPRec)); - NumberofWPrecs = 0; - - while (1) - { - char Key[16]; - char Record[1024]; - char * ptr, * ptr2; - unsigned int n; - - sprintf(Key, "R%d", i++); - - GetStringValue(group, Key, Record, 1024); - - if (Record[0] == 0) // End of List - return; - - memset(&WPRec, 0, sizeof(WPRec)); - - WP = &WPRec; - - ptr = Record; - ptr2 = strlop(ptr, '|'); - if (ptr) strcpy(&WP->callsign[0], ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) strcpy(&WP->name[0], ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) WP->Type = atoi(ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) WP->changed = atoi(ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) WP->seen = atoi(ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) strcpy(&WP->first_homebbs[0], ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) strcpy(&WP->secnd_homebbs[0], ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) strcpy(&WP->first_zip[0], ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) strcpy(&WP->secnd_zip[0], ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - - if (ptr == NULL) continue; - - if (strlen(ptr) > 30) - ptr[30] = 0; - - strcpy(&WP->first_qth[0], ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - - if (ptr == NULL) continue; - - if (strlen(ptr) > 30) - ptr[30] = 0; - - strcpy(&WP->secnd_qth[0], ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - - if (ptr) WP->last_modif = atol(ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - - if (ptr) - { - WP->last_seen = atol(ptr); - - // Check Call - - for (n = 1; n < strlen(WP->callsign); n++) // skip first which may also be digit - { - if (isdigit(WP->callsign[n])) - { - // Has a digit. Check Last is not digit - - if (isalpha(WP->callsign[strlen(WP->callsign) - 1])) - { - WP = LookupWP(WPRec.callsign); - if (WP == NULL) - WP = AllocateWPRecord(); - - memcpy(WP, &WPRec, sizeof(WPRec)); - goto WPOK; - } - } - } - Debugprintf("Bad WP Call %s", WP->callsign); - } -WPOK:; - } - return; - } - - // If text format exists use it - - strcpy(CfgName, WPDatabasePath); - strlop(CfgName, '.'); - strcat(CfgName, ".cfg"); - - if (stat(CfgName, &STAT) == -1) - goto tryOld; - - config_init(&wpcfg); - - if (!config_read_file(&wpcfg, CfgName)) - { - char Msg[256]; - sprintf(Msg, "Config File %s Line %d - %s\n", CfgName, - config_error_line(&wpcfg), config_error_text(&wpcfg)); - - printf("%s", Msg); - config_destroy(&wpcfg); - goto tryOld; - } - - // Set up control record - - WPRecPtr = malloc(sizeof(void *)); - WPRecPtr[0] = zalloc(sizeof(WPRec)); - NumberofWPrecs = 0; - - while (1) - { - char Key[16]; - char Temp[128]; - - sprintf(Key, "R%d", i++); - - wpgroup = config_lookup(&wpcfg, Key); - - if (wpgroup == NULL) // End of List - { - config_destroy(&wpcfg); - return; - } - - memset(&WPRec, 0, sizeof(WPRec)); - - GetStringValue(wpgroup, "c", WPRec.callsign, 6); - GetStringValue(wpgroup, "n", WPRec.name, 12); - - WPRec.Type = GetIntValue(wpgroup, "T"); - WPRec.changed = GetIntValue(wpgroup, "ch"); - WPRec.seen = GetIntValue(wpgroup, "s"); - - GetStringValue(wpgroup, "h", WPRec.first_homebbs, 40); - GetStringValue(wpgroup, "sh", WPRec.secnd_homebbs, 40); - GetStringValue(wpgroup, "z", WPRec.first_zip, 8); - GetStringValue(wpgroup, "sz", WPRec.secnd_zip, 8); - - GetStringValue(wpgroup, "q", Temp, 30); - Temp[30] = 0; - strcpy(WPRec.first_qth, Temp); - - GetStringValue(wpgroup, "sq", Temp, 30); - Temp[30] = 0; - strcpy(WPRec.secnd_qth, Temp); - - val = GetIntValue(wpgroup, "m"); - WPRec.last_modif = val; - val = GetIntValue(wpgroup, "ls"); - WPRec.last_seen = val; - - _strupr(WPRec.callsign); - - strlop(WPRec.callsign, ' '); - - if (strlen(WPRec.callsign) > 2) - { - if (strchr(WPRec.callsign, ':')) - continue; - - if (BadCall(WPRec.callsign)) - continue; - - WP = LookupWP(WPRec.callsign); - - if (WP == NULL) - WP = AllocateWPRecord(); - - memcpy(WP, &WPRec, sizeof(WPRec)); - } - } - -tryOld: - - Handle = fopen(WPDatabasePath, "rb"); - - if (Handle == NULL) - { - // Initialise a new File - - WPRecPtr = malloc(sizeof(void *)); - WPRecPtr[0] = malloc(sizeof(WPRec)); - memset(WPRecPtr[0], 0, sizeof(WPRec)); - NumberofWPrecs = 0; - - return; - } - - - // Get First Record - - ReadLen = fread(&WPRec, 1, sizeof(WPRec), Handle); - - if (ReadLen == 0) - { - // Duff file - - memset(&WPRec, 0, sizeof(WPRec)); - } - - // Set up control record - - WPRecPtr = malloc(sizeof(void *)); - WPRecPtr[0] = malloc(sizeof(WPRec)); - memcpy(WPRecPtr[0], &WPRec, sizeof(WPRec)); - - NumberofWPrecs = 0; - -Next: - - ReadLen = fread(&WPRec, 1, sizeof(WPRec), Handle); - - if (ReadLen == sizeof(WPRec)) - { - _strupr(WPRec.callsign); - - strlop(WPRec.callsign, ' '); - - if (strlen(WPRec.callsign) > 2) - { - if (strchr(WPRec.callsign, ':')) - goto Next; - - if (BadCall(WPRec.callsign)) - goto Next; - - WP = LookupWP(WPRec.callsign); - - if (WP == NULL) - WP = AllocateWPRecord(); - - memcpy(WP, &WPRec, sizeof(WPRec)); - } - goto Next; - } - - fclose(Handle); - SaveWPDatabase(); -} - -VOID CopyWPDatabase() -{ - char Backup[MAX_PATH]; - char Orig[MAX_PATH]; - - return; - - strcpy(Backup, WPDatabasePath); - strcat(Backup, ".bak"); - - CopyFile(WPDatabasePath, Backup, FALSE); - - strcpy(Backup, WPDatabasePath); - strlop(Backup, '.'); - strcat(Backup, ".cfg.bak"); - - strcpy(Orig, WPDatabasePath); - strlop(Orig, '.'); - strcat(Orig, ".cfg"); - CopyFile(Orig, Backup, FALSE); -} - -VOID SaveWPDatabase() -{ -// SaveConfig(ConfigName); // WP config is now in main config file - - int i; - config_setting_t *root, *group; - config_t cfg; - char Key[16]; - WPRec * WP; - char CfgName[MAX_PATH]; - long long val; - - memset((void *)&cfg, 0, sizeof(config_t)); - - config_init(&cfg); - - root = config_root_setting(&cfg); - - for (i = 0; i <= NumberofWPrecs; i++) - { - WP = WPRecPtr[i]; - sprintf(Key, "R%d", i); - - group = config_setting_add(root, Key, CONFIG_TYPE_GROUP); - - SaveStringValue(group, "c", &WP->callsign[0]); - SaveStringValue(group, "n", &WP->name[0]); - SaveIntValue(group, "T", WP->Type); - SaveIntValue(group, "ch", WP->changed); - SaveIntValue(group, "s", WP->seen); - SaveStringValue(group, "h", &WP->first_homebbs[0]); - SaveStringValue(group, "sh", &WP->secnd_homebbs[0]); - SaveStringValue(group, "z", &WP->first_zip[0]); - SaveStringValue(group, "sz", &WP->secnd_zip[0]); - SaveStringValue(group, "q", &WP->first_qth[0]); - SaveStringValue(group, "sq", &WP->secnd_qth[0]); - val = WP->last_modif; - SaveInt64Value(group, "m", val); - val = WP->last_seen; - SaveInt64Value(group, "ls", val); - } - - strcpy(CfgName, WPDatabasePath); - strlop(CfgName, '.'); - strcat(CfgName, ".cfg"); - - Debugprintf("Saving WP Database to %s\n", CfgName); - - config_write_file(&cfg, CfgName); - config_destroy(&cfg); - -} - -WPRec * LookupWP(char * Call) -{ - WPRec * ptr = NULL; - int i; - - for (i=1; i <= NumberofWPrecs; i++) - { - ptr = WPRecPtr[i]; - - if (_stricmp(ptr->callsign, Call) == 0) return ptr; - } - - return NULL; -} - -char * FormatWPDate(time_t Datim) -{ - struct tm *tm; - static char Date[]="xx-xxx-xx "; - - tm = gmtime(&Datim); - - if (tm) - { - if (tm->tm_year >= 100) - sprintf_s(Date, sizeof(Date), "%02d-%3s-%02d", - tm->tm_mday, month[tm->tm_mon], tm->tm_year - 100); - else - sprintf_s(Date, sizeof(Date), ""); - } - return Date; -} - -#ifndef LINBPQ - -int Do_WP_Sel_Changed(HWND hDlg) -{ - // Update WP display with newly selected rec - - WPRec * WP; - int Sel = SendDlgItemMessage(hDlg, IDC_WP, CB_GETCURSEL, 0, 0); - char Type[] = " "; - - if (Sel == -1) - SendDlgItemMessage(hDlg, IDC_WP, WM_GETTEXT, Sel, (LPARAM)(LPCTSTR)&CurrentWPCall); - else - SendDlgItemMessage(hDlg, IDC_WP, CB_GETLBTEXT, Sel, (LPARAM)(LPCTSTR)&CurrentWPCall); - - for (CurrentWPIndex = 1; CurrentWPIndex <= NumberofWPrecs; CurrentWPIndex++) - { - WP = WPRecPtr[CurrentWPIndex]; - - if (_stricmp(WP->callsign, CurrentWPCall) == 0) - { - - SetDlgItemText(hDlg, IDC_WPNAME, WP->name); - SetDlgItemText(hDlg, IDC_HOMEBBS1, WP->first_homebbs); - SetDlgItemText(hDlg, IDC_HOMEBBS2, WP->secnd_homebbs); - SetDlgItemText(hDlg, IDC_QTH1, WP->first_qth); - SetDlgItemText(hDlg, IDC_QTH2, WP->secnd_qth); - SetDlgItemText(hDlg, IDC_ZIP1, WP->first_zip); - SetDlgItemText(hDlg, IDC_ZIP2, WP->secnd_zip); - Type[0] = WP->Type; - SetDlgItemText(hDlg, IDC_TYPE, Type); - SetDlgItemInt(hDlg, IDC_CHANGED, WP->changed, FALSE); - SetDlgItemInt(hDlg, IDC_SEEN, WP->seen, FALSE); - - SetDlgItemText(hDlg, IDC_LASTSEEN, FormatWPDate(WP->last_seen)); - SetDlgItemText(hDlg, IDC_LASTMODIFIED, FormatWPDate(WP->last_modif)); - - return 0; - } - } - - CurrentWPIndex = -1; - - return 0; -} - -INT_PTR CALLBACK InfoDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); - - -VOID Do_Save_WPRec(HWND hDlg) -{ - WPRec * WP; - char Type[] = " "; - BOOL OK1; - - if (CurrentWPIndex == -1) - { - sprintf(InfoBoxText, "Please select a WP Record to save"); - DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); - return; - } - - WP = WPRecPtr[CurrentWPIndex]; - - if (strcmp(CurrentWPCall, WP->callsign) != 0) - { - sprintf(InfoBoxText, "Inconsistancy detected - record not saved"); - DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); - return; - } - - GetDlgItemText(hDlg, IDC_WPNAME, WP->name, 13); - GetDlgItemText(hDlg, IDC_HOMEBBS1, WP->first_homebbs, 41); - GetDlgItemText(hDlg, IDC_HOMEBBS2, WP->secnd_homebbs, 41); - GetDlgItemText(hDlg, IDC_QTH1, WP->first_qth, 31); - GetDlgItemText(hDlg, IDC_QTH2, WP->secnd_qth, 31); - GetDlgItemText(hDlg, IDC_ZIP1, WP->first_zip, 31); - GetDlgItemText(hDlg, IDC_ZIP2, WP->secnd_zip, 31); - WP->last_modif = time(NULL); - WP->seen = GetDlgItemInt(hDlg, IDC_SEEN, &OK1, FALSE); - - WP->Type = 'U'; - WP->changed = 1; - - SaveWPDatabase(); - - sprintf(InfoBoxText, "WP information saved"); - DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); -} - -VOID Do_Delete_WPRec(HWND hDlg) -{ - WPRec * WP; - int n; - - if (CurrentWPIndex == -1) - { - sprintf(InfoBoxText, "Please select a WP Record to delete"); - DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); - return; - } - - WP = WPRecPtr[CurrentWPIndex]; - - if (strcmp(CurrentWPCall, WP->callsign) != 0) - { - sprintf(InfoBoxText, "Inconsistancy detected - record not deleted"); - DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); - return; - } - - for (n = CurrentWPIndex; n < NumberofWPrecs; n++) - { - WPRecPtr[n] = WPRecPtr[n+1]; // move down all following entries - } - - NumberofWPrecs--; - - SendDlgItemMessage(hDlg, IDC_WP, CB_RESETCONTENT, 0, 0); - - for (n = 1; n <= NumberofWPrecs; n++) - { - SendDlgItemMessage(hDlg, IDC_WP, CB_ADDSTRING, 0, (LPARAM)WPRecPtr[n]->callsign); - } - - - sprintf(InfoBoxText, "WP record for %s deleted", WP->callsign); - DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); - - free(WP); - - SaveWPDatabase(); - - return; - -} - -INT_PTR CALLBACK WPEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) -{ - int Command, n; - - UNREFERENCED_PARAMETER(lParam); - switch (message) - { - - case WM_INITDIALOG: - - for (n = 1; n <= NumberofWPrecs; n++) - { - SendDlgItemMessage(hDlg, IDC_WP, CB_ADDSTRING, 0, (LPARAM)WPRecPtr[n]->callsign); - } - - return (INT_PTR)TRUE; - - case WM_CTLCOLORDLG: - - return (LONG)bgBrush; - - case WM_CTLCOLORSTATIC: - { - HDC hdcStatic = (HDC)wParam; - SetTextColor(hdcStatic, RGB(0, 0, 0)); - SetBkMode(hdcStatic, TRANSPARENT); - return (LONG)bgBrush; - } - - - case WM_COMMAND: - - Command = LOWORD(wParam); - - switch (Command) - { - - case IDOK: - case IDCANCEL: - - EndDialog(hDlg, LOWORD(wParam)); - return (INT_PTR)TRUE; - - case IDC_WP: - - // Msg Selection Changed - - Do_WP_Sel_Changed(hDlg); - - return TRUE; - - case IDC_SAVEWP: - - Do_Save_WPRec(hDlg); - return TRUE; - - case IDC_DELETEWP: - - Do_Delete_WPRec(hDlg); - return TRUE; - } - break; - } - - return (INT_PTR)FALSE; -} -#endif - -VOID GetWPBBSInfo(char * Rline) -{ - // Update WP with /I records for each R: Line - - // R:111206/1636Z 29130@N9PMO.#SEWI.WI.USA.NOAM [Racine, WI] FBB7.00i - - struct tm rtime; - time_t RLineTime; - int Age; - - WPRec * WP; - char ATBBS[200]; - char Call[200]; - char QTH[200] = ""; - int RLen; - - char * ptr1; - char * ptr2; - - - memset(&rtime, 0, sizeof(struct tm)); - - if (Rline[10] == '/') - { - // Dodgy 4 char year - - sscanf(&Rline[2], "%04d%02d%02d/%02d%02d", - &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min); - rtime.tm_year -= 1900; - rtime.tm_mon--; - } - else if (Rline[8] == '/') - { - sscanf(&Rline[2], "%02d%02d%02d/%02d%02d", - &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min); - - if (rtime.tm_year < 90) - rtime.tm_year += 100; // Range 1990-2089 - rtime.tm_mon--; - } - - // Otherwise leave date as zero, which should be rejected - - if ((RLineTime = mktime(&rtime)) != (time_t)-1 ) - { - Age = (time(NULL) - RLineTime)/86400; - - if ( Age < -1) - return; // in the future - - if (Age > BidLifetime || Age > MaxAge) - return; // Too old - } - - ptr1 = strchr(Rline, '@'); - ptr2 = strchr(Rline, '\r'); - - if (!ptr1) - return; // Duff - - if (*++ptr1 == ':') - ptr1++; // Format 2 - - - if (ptr2 == NULL) - return; // No CR on end - - RLen = ptr2 - ptr1; - - if (RLen > 200) - return; - - memcpy(ATBBS, ptr1, RLen); - ATBBS[RLen] = 0; - - ptr2 = strchr(ATBBS, ' '); - - if (ptr2) - *ptr2 = 0; - - strcpy(Call, ATBBS); - strlop(Call, '.'); - - ptr2 = memchr(ptr1, '[', RLen); - - if (ptr2) - { - ptr1= memchr(ptr2, ']', RLen); - if (ptr1) - memcpy(QTH, ptr2 + 1, ptr1 - ptr2 - 1); - } - - if (BadCall(Call)) - return; - - WP = LookupWP(Call); - - if (!WP) - { - // Not Found - - WP = AllocateWPRecord(); - - strcpy(WP->callsign, Call); - strcpy(WP->first_homebbs, ATBBS); - strcpy(WP->secnd_homebbs, ATBBS); - - if (QTH[0]) - { - strcpy(WP->first_qth, QTH); - strcpy(WP->secnd_qth, QTH); - } - - WP->last_modif = RLineTime; - WP->last_seen = RLineTime; - - WP->Type = 'I'; - WP->changed = TRUE; - - return; - } - - if (WP->last_modif >= RLineTime || WP->Type != 'I') - return; - - // Update 2nd if changed - - if (strcmp(WP->secnd_homebbs , ATBBS) != 0) - { - strcpy(WP->secnd_homebbs, ATBBS); - WP->last_modif = RLineTime; - } - - if (QTH[0] && strcmp(WP->secnd_qth , QTH) != 0) - { - strcpy(WP->secnd_qth, QTH); - WP->last_modif = RLineTime; - } - - return; -} - - - - -VOID GetWPInfoFromRLine(char * From, char * FirstRLine, time_t RLineTime) -{ - /* The /G suffix denotes that the information in this line has been gathered by examining - the header of a message to GUESS at which BBS the sender is registered. The HomeBBS of the User - is assumed to be the BBS shown in the first R: header line. The date associated with this - information is the date shown on this R: header line. - */ - - // R:930101/0000 1530@KA6FUB.#NOCAL.CA.USA.NOAM - - // R:930101/0000 @:KA6FUB.#NOCAL.CA.USA.NOAM #:1530 - - // The FirstRLine pointer points to the message, so shouldnt be changed - - WPRec * WP; - char ATBBS[200]; - int RLen; - - char * ptr1 = strchr(FirstRLine, '@'); - char * ptr2 = strchr(FirstRLine, '\r'); - - if (BadCall(From)) - return; - - if (!ptr1) - return; // Duff - - if (*++ptr1 == ':') - ptr1++; // Format 2 - - RLen = ptr2 - ptr1; - - if (RLen > 200) - return; - - memcpy(ATBBS, ptr1, RLen); - ATBBS[RLen] = 0; - - ptr2 = strchr(ATBBS, ' '); - - if (ptr2) - *ptr2 = 0; - - if (strlen(ATBBS) > 40) - ATBBS[40] = 0; - - WP = LookupWP(From); - - if (!WP) - { - // Not Found - - WP = AllocateWPRecord(); - - strcpy(WP->callsign, From); - strcpy(WP->first_homebbs, ATBBS); - strcpy(WP->secnd_homebbs, ATBBS); - - WP->last_modif = RLineTime; - WP->last_seen = RLineTime; - - WP->Type = 'G'; - WP->changed = TRUE; - - return; - } - - if (WP->last_modif >= RLineTime) - return; - - // Update 2nd if changed - - if (strcmp(WP->secnd_homebbs , ATBBS) != 0) - { - strcpy(WP->secnd_homebbs, ATBBS); - WP->last_modif = RLineTime; - } - - return; -} - -VOID ProcessWPMsg(char * MailBuffer, int Size, char * FirstRLine) -{ - char * ptr1 = MailBuffer; - char * ptr2; - WPRec * WP; - char WPLine[200]; - int WPLen; - - ptr1[Size] = 0; - - ptr1 = FirstRLine; - - if (ptr1 == NULL) - return; - - while (*ptr1) - { - ptr2 = strchr(ptr1, '\r'); - - if (ptr2 == 0) // No CR in buffer - return; - - WPLen = ptr2 - ptr1; - - if (WPLen > 128) - return; - - if ((memcmp(ptr1, "On ", 3) == 0) && (WPLen < 200)) - { - char * Date; - char * Call; - char * AT; - char * HA; - char * zip; - char * ZIP; - char * Name; - char * QTH = NULL; - char * Context; - char seps[] = " \r"; - - // Make copy of string, as strtok messes with it - - memcpy(WPLine, ptr1, WPLen); - WPLine[WPLen] = 0; - - Date = strtok_s(WPLine+3, seps, &Context); - Call = strtok_s(NULL, seps, &Context); - AT = strtok_s(NULL, seps, &Context); - HA = strtok_s(NULL, seps, &Context); - zip = strtok_s(NULL, seps, &Context); - ZIP = strtok_s(NULL, seps, &Context); - Name = strtok_s(NULL, seps, &Context); - QTH = strtok_s(NULL, "\r", &Context); // QTH may have spaces - - if (Date == 0 || Call == 0 || AT == 0 || ZIP == 0 || Name == 0 || QTH == 0) - return; - - if (strlen(HA) > 40) - return; - if (strlen(ZIP) > 8) - return; - if (strlen(Name) > 12) - return; - if (strlen(QTH) > 30) - return; - - if (AT[0] == '@' && (QTH)) - { - struct tm rtime; - time_t WPDate; - char Type; - char * TypeString; - - if (memcmp(Name, "?", 2) == 0) Name = NULL; - if (memcmp(ZIP, "?", 2) == 0) ZIP = NULL; - if (memcmp(QTH, "?", 2) == 0) QTH = NULL; - - memset(&rtime, 0, sizeof(struct tm)); - - sscanf(Date, "%02d%02d%02d", - &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday); - - rtime.tm_year += 100; - rtime.tm_mon--; - -/* -This process freshens the database, following receipt of the new or changed information detailed above. - -The update subroutine will first look for an entry in the database for the callsign which matches the received information. -If it does not exist then a completely new record will be created in the database and the information be used to fill what -fields it can, in both the active and the temporary components. The date will be then changed to the one associated with the -update information. - -If the record does already exist, then the unknown fields of both the temporary and active fields will be filled in, and -those fields already known in the temporary part will be replaced by the new information if the date new information is -younger than that already on file. The date will then be -adjusted such that it is consistent with the updated information. - -If the new information is of the /U category, then the current fields will be replaced by the new information in both -the primary and secondary (Active and Temporary) parts of the record, as this information has been input directly from -the user. If the information was of another category then only the secondary (Temporary) part of the record will be -updated, so the Active or primary record will remain unchanged at this time. - -If a field is changed, a flag giving the update request type is then validated. If the /U flag is already validated, -it will not be replaced. This flag will be used in case the WP update messages are validated. -*/ - if ((WPDate = mktime(&rtime)) != (time_t)-1 ) - { - WPDate -= (time_t)_MYTIMEZONE; - TypeString = strlop(Call, '/'); - - if (strlen(Call) < 3 || strlen(Call) > 6) - return; - - if (TypeString) - Type = TypeString[0]; - else - Type = 'G'; - - if (strchr(Call, ':')) - break; - - if (BadCall(Call)) - break; - - WP = LookupWP(Call); - - if (WP) - { - // Found, so update - - DoWPUpdate(WP, Type, Name, HA, QTH, ZIP, WPDate); - } - else - { - WP = AllocateWPRecord(); - - strcpy(WP->callsign, Call); - if (Name) strcpy(WP->name, Name); - strcpy(WP->first_homebbs, HA); - strcpy(WP->secnd_homebbs, HA); - - if (QTH) - { - strcpy(WP->first_qth, QTH); - strcpy(WP->secnd_qth, QTH);; - } - if (ZIP) - { - strcpy(WP->first_zip, ZIP); - strcpy(WP->secnd_zip, ZIP); - } - - WP->Type = Type; - WP->last_modif = WPDate; - WP->last_seen = WPDate; - WP->changed = TRUE; - WP->seen++; - } - } - } - } - - ptr1 = ++ptr2; - if (*ptr1 == '\n') - ptr1++; - } - - SaveWPDatabase(); - - return; -} - -VOID DoWPUpdate(WPRec * WP, char Type, char * Name, char * HA, char * QTH, char * ZIP, time_t WPDate) -{ - // First Update any unknown field - - if(Name) - if (WP->name == NULL) {strcpy(WP->name, Name); WP->last_modif = WPDate; WP->changed = TRUE;} - - if (QTH) - { - if (WP->first_qth == NULL) {strcpy(WP->first_qth, QTH); WP->last_modif = WPDate; WP->changed = TRUE;} - if (WP->secnd_qth == NULL) {strcpy(WP->secnd_qth, QTH); WP->last_modif = WPDate; WP->changed = TRUE;} - } - if (ZIP) - { - if (WP->first_zip == NULL) {strcpy(WP->first_zip, ZIP); WP->last_modif = WPDate; WP->changed = TRUE;} - if (WP->secnd_zip == NULL) {strcpy(WP->secnd_zip, ZIP); WP->last_modif = WPDate; WP->changed = TRUE;} - } - - WP->last_seen = WPDate; - WP->seen++; - - // Now Update Temp Fields if update is newer than original - - if (WP->last_modif >= WPDate) - return; - - if (Type == 'U') // Definitive Update - { - if (strcmp(WP->first_homebbs , HA) != 0) - { - strcpy(WP->first_homebbs, HA); strcpy(WP->secnd_homebbs, HA); WP->last_modif = WPDate; WP->changed = TRUE; - } - - if (Name) - { - if (strcmp(WP->name , Name) != 0) - { - strcpy(WP->name, Name); - WP->last_modif = WPDate; - WP->changed = TRUE; - } - } - - if (QTH) - { - if (strcmp(WP->first_qth , QTH) != 0) - { - strcpy(WP->first_qth, QTH); - strcpy(WP->secnd_qth, QTH); - WP->last_modif = WPDate; - WP->changed = TRUE; - } - } - - if (ZIP) - { - if (strcmp(WP->first_zip , ZIP) != 0) - { - strcpy(WP->first_zip, ZIP); - strcpy(WP->secnd_zip, ZIP); - WP->last_modif = WPDate; - WP->changed = TRUE; - } - } - - WP->Type = Type; - - return; - } - - // Non-Definitive - only update second copy - - if (strcmp(WP->secnd_homebbs , HA) != 0) {strcpy(WP->secnd_homebbs, HA); WP->last_modif = WPDate; WP->Type = Type;} - - if (Name) - if (strcmp(WP->name , Name) != 0) {strcpy(WP->name, Name); WP->last_modif = WPDate; WP->Type = Type;} - - if (QTH) - if (strcmp(WP->secnd_qth , QTH) != 0) {strcpy(WP->secnd_qth, QTH); WP->last_modif = WPDate; WP->Type = Type;} - - if (ZIP) - if (strcmp(WP->secnd_zip , ZIP) != 0) {strcpy(WP->secnd_zip, ZIP); WP->last_modif = WPDate; WP->Type = Type;} - - return; -} - -VOID UpdateWPWithUserInfo(struct UserInfo * user) -{ - WPRec * WP = LookupWP(user->Call); - - if (strchr(user->Call, ':')) - return; - - if (BadCall(user->Call)) - return; - - if (!WP) - { - WP = AllocateWPRecord(); - strcpy(WP->callsign, user->Call); - } - - // Update Record - - if (user->HomeBBS[0]) - { - strcpy(WP->first_homebbs, user->HomeBBS); - strcpy(WP->secnd_homebbs, user->HomeBBS); - } - - if (user->Address[0]) - { - char Temp[127]; - strcpy(Temp, user->Address); - Temp[30] = 0; - - strcpy(WP->first_qth, Temp); - strcpy(WP->secnd_qth, Temp); - } - - if (user->ZIP[0]) - { - strcpy(WP->first_zip, user->ZIP); - strcpy(WP->secnd_zip, user->ZIP ); - } - - if (user->Name[0]) - strcpy(WP->name, user->Name); - - WP->last_modif = WP->last_seen = time(NULL); - - WP->changed = TRUE; - WP->Type = 'U'; - - SaveWPDatabase(); - -} - -VOID DoWPLookup(ConnectionInfo * conn, struct UserInfo * user, char Type, char *Param) -{ - // Process the I call command - - WPRec * ptr = NULL; - int i; - char ATBBS[100]; - char * HA; - char * Rest; - - _strupr(Param); - Type = toupper(Type); - - switch (Type) - { - case 0: - - for (i=1; i <= NumberofWPrecs; i++) - { - ptr = WPRecPtr[i]; - - if (wildcardcompare(ptr->callsign, Param)) - { - nodeprintf(conn, "%s %s %s %s %s\r", ptr->callsign, ptr->first_homebbs, - ptr->name, ptr->first_zip, ptr->first_qth); - } - } - - return; - - case'@': // AT BBS - - for (i=1; i <= NumberofWPrecs; i++) - { - ptr = WPRecPtr[i]; - - strcpy(ATBBS, ptr->first_homebbs); - strlop(ATBBS,'.'); - - if (wildcardcompare(ATBBS, Param)) - { - nodeprintf(conn, "%s %s %s %s %s\r", ptr->callsign, ptr->first_homebbs, ptr->name, ptr->first_zip, ptr->first_qth); - } - } - - case'H': // Hierarchic Element - - for (i=1; i <= NumberofWPrecs; i++) - { - ptr = WPRecPtr[i]; - - strcpy(ATBBS, ptr->first_homebbs); - - Rest = strlop(ATBBS,'.'); - - if (Rest == 0) - continue; - - HA = strtok_s(Rest, ".", &Rest); - - while (HA) - { - if (wildcardcompare(HA, Param)) - { - nodeprintf(conn, "%s %s %s %s %s\r", ptr->callsign, ptr->first_homebbs, ptr->name, ptr->first_zip, ptr->first_qth); - } - - HA = strtok_s(NULL, ".", &Rest); - } - } - return; - - case'Z': // ZIP - - for (i=1; i <= NumberofWPrecs; i++) - { - ptr = WPRecPtr[i]; - - if (ptr->first_zip[0] == 0) - continue; - - if (wildcardcompare(ptr->first_zip, Param)) - { - nodeprintf(conn, "%s %s %s %s %s\r", ptr->callsign, ptr->first_homebbs, ptr->name, ptr->first_zip, ptr->first_qth); - } - } - return; - } - nodeprintf(conn, "Invalid I command option %c\r", Type); -} -/* -On 111120 N4ZKF/I @ N4ZKF.#NFL.FL.USA.NOAM zip 32118 Dave 32955 -On 111120 F6IQF/I @ F6IQF.FRPA.FRA.EU zip ? ? f6iqf.dyndns.org -On 111121 W9JUN/I @ W9JUN.W9JUN.#SEIN.IN.US.NOAM zip 47250 Don NORTH VERNON, IN -On 111120 KR8ZY/U @ KR8ZY zip ? john ? -On 111120 N0DEC/G @ N0ARY.#NCA.CA.USA.NOAM zip ? ? ? - -From: N0JAL -To: WP -Type/Status: B$ -Date/Time: 22-Nov 10:15Z -Bid: 95F7N0JAL -Title: WP Update - -R:111122/1500Z 35946@KD6PGI.OR.USA.NOAM BPQ1.0.4 -R:111122/1020 16295@K7ZS.OR.USA.NOAM -R:111122/1015 38391@N0JAL.OR.USA.NOAM - -On 111121 N0JAL @ N0JAL.OR.USA.NOAM zip ? Sai Damascus, Oregon CN85sj - -*/ - -VOID UpdateWP() -{ - // If 2nd copy of info has been unchanged for 30 days, copy to active fields - - WPRec * ptr = NULL; - int i; - char * via = NULL; - int MsgLen = 0; - char MailBuffer[100100]; - char * Buffptr = MailBuffer; - int WriteLen=0; - char HDest[61] = "WP"; - char WPMsgType = 'P'; - time_t NOW = time(NULL); - time_t UpdateLimit = NOW - (86400 * 30); // 30 days - LASTWPSendTime = NOW - (86400 * 5); // 5 days max - - for (i=1; i <= NumberofWPrecs; i++) - { - ptr = WPRecPtr[i]; - - // DO we have a new field, and if so is it different? - - if ((ptr->secnd_homebbs[0] && _stricmp(ptr->first_homebbs, ptr->secnd_homebbs)) - || (ptr->secnd_qth[0] && _stricmp(ptr->first_qth, ptr->secnd_qth)) - || (ptr->secnd_zip[0] && _stricmp(ptr->first_zip, ptr->secnd_zip))) - { - // Have new data - - if (ptr->last_modif < UpdateLimit) - { - // Stable for 30 days - - if (ptr->secnd_homebbs[0]) - strcpy(ptr->first_homebbs, ptr->secnd_homebbs); - if (ptr->secnd_qth[0]) - strcpy(ptr->first_qth, ptr->secnd_qth); - if (ptr->secnd_zip[0]) - strcpy(ptr->first_zip, ptr->secnd_zip); - - ptr->last_modif = NOW; - - } - } - } -} - -int CreateWPMessage() -{ - // Include all messages with Type of U whach have changed since LASTWPSendTime - - WPRec * ptr = NULL; - int i; - struct tm *tm; - struct MsgInfo * Msg; - char * via = NULL; - char BID[13]; - BIDRec * BIDRec; - int MsgLen = 0; - char MailBuffer[100100]; - char * Buffptr = MailBuffer; - char MsgFile[MAX_PATH]; - FILE * hFile; - int WriteLen=0; - char ** To = SendWPAddrs; - - LASTWPSendTime = time(NULL) - (86400 * 5); // 5 days max - - for (i=1; i <= NumberofWPrecs; i++) - { - ptr = WPRecPtr[i]; - -// if (ptr->last_modif > LASTWPSendTime && ptr->Type == 'U' && ptr->first_homebbs[0]) - if (ptr->changed && ptr->last_modif > LASTWPSendTime && ptr->first_homebbs[0]) - { - tm = gmtime((time_t *)&ptr->last_modif); - MsgLen += sprintf(Buffptr, "On %02d%02d%02d %s/%c @ %s zip %s %s %s\r\n", - tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, - ptr->callsign, ptr->Type, ptr->first_homebbs, - (ptr->first_zip[0]) ? ptr->first_zip : "?", - (ptr->name[0]) ? ptr->name : "?", - (ptr->first_qth[0]) ? ptr->first_qth : "?"); - - Buffptr = &MailBuffer[MsgLen]; - - ptr->changed = FALSE; - - if (MsgLen > 100000) - break; - } - } - - if (MsgLen == 0) - return TRUE; - - while(To[0]) - { - char TO[256]; - char * VIA; - - Msg = AllocateMsgRecord(); - - // Set number here so they remain in sequence - - Msg->number = ++LatestMsg; - Msg->length = MsgLen; - MsgnotoMsg[Msg->number] = Msg; - - strcpy(Msg->from, BBSName); - - strcpy(TO, To[0]); - - VIA = strlop(TO, '@'); - - if (VIA) - { - if (strlen(VIA) > 40) - VIA[40] = 0; - strcpy(Msg->via, VIA); - } - strcpy(Msg->to, TO); - - strcpy(Msg->title, "WP Update"); - - Msg->type = (SendWPType) ? 'P' : 'B'; - Msg->status = 'N'; - - sprintf_s(BID, sizeof(BID), "%d_%s", LatestMsg, BBSName); - - strcpy(Msg->bid, BID); - - Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); - - BIDRec = AllocateBIDRecord(); - - strcpy(BIDRec->BID, Msg->bid); - BIDRec->mode = Msg->type; - BIDRec->u.msgno = LOWORD(Msg->number); - BIDRec->u.timestamp = LOWORD(time(NULL)/86400); - - sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); - - hFile = fopen(MsgFile, "wb"); - - if (hFile) - { - fwrite(MailBuffer, 1, Msg->length, hFile); - fclose(hFile); - } - - MatchMessagetoBBSList(Msg, 0); - - BuildNNTPList(Msg); // Build NNTP Groups list - -#ifndef NOMQTT - if (MQTT) - MQTTMessageEvent(Msg); -#endif - To++; - } - - SaveMessageDatabase(); - SaveBIDDatabase(); - - return TRUE; -} - -VOID CreateWPReport() -{ - int i; - char Line[200]; - int len; - char File[100]; - FILE * hFile; - WPRec * WP = NULL; - - sprintf_s(File, sizeof(File), "%s/wp.txt", BaseDir); - - hFile = fopen(File, "wb"); - - if (hFile == NULL) - return; - -// len = sprintf(Line, " Call Connects Connects Messages Messages Bytes Bytes Rejected Rejected\r\n"); -// WriteFile(hFile, Line, len, &written, NULL); -// len = sprintf(Line, " In Out Rxed Sent Rxed Sent In Out\r\n\r\n"); -// WriteFile(hFile, Line, len, &written, NULL); - - - for (i=1; i <= NumberofWPrecs; i++) - { - WP = WPRecPtr[i]; - - len = sprintf(Line, "%-7s,%c,%s,%s,%s,%s,%s,%s,%s,%d,%s,%s\r\n", - WP->callsign, WP->Type, WP->first_homebbs, WP->first_qth, WP->first_zip, - WP->secnd_homebbs, WP->secnd_qth, WP->secnd_zip, WP->name, WP->changed, - FormatWPDate((time_t)WP->last_modif), - FormatWPDate((time_t)WP->last_seen)); - - fwrite(Line, 1, len, hFile); - } - fclose(hFile); -} - - - - - - +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without Fvoideven the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// Mail and Chat Server for BPQ32 Packet Switch +// +// White Pages Database Support Routines + +#include "bpqmail.h" + +#define GetSemaphore(Semaphore,ID) _GetSemaphore(Semaphore, ID, __FILE__, __LINE__) +void _GetSemaphore(struct SEM * Semaphore, int ID, char * File, int Line); + +int CurrentWPIndex; +char CurrentWPCall[10]; + +time_t LASTWPSendTime; + + +VOID DoWPUpdate(WPRec *WP, char Type, char * Name, char * HA, char * QTH, char * ZIP, time_t WPDate); +VOID Do_Save_WPRec(HWND hDlg); +VOID SaveInt64Value(config_setting_t * group, char * name, long long value); +VOID SaveIntValue(config_setting_t * group, char * name, int value); +VOID SaveStringValue(config_setting_t * group, char * name, char * value); +BOOL GetStringValue(config_setting_t * group, char * name, char * value, int maxlen); +void MQTTMessageEvent(void* message); + +WPRec * AllocateWPRecord() +{ + WPRec * WP = zalloc(sizeof (WPRec)); + + GetSemaphore(&AllocSemaphore, 0); + + WPRecPtr=realloc(WPRecPtr,(++NumberofWPrecs+1) * sizeof(void *)); + WPRecPtr[NumberofWPrecs]= WP; + + FreeSemaphore(&AllocSemaphore); + + return WP; +} + +int BadCall(char * Call) +{ + if (_stricmp(Call, "RMS") == 0) + return 1; + + if (_stricmp(Call, "SYSTEM") == 0) + return 1; + + if (_stricmp(Call, "SWITCH") == 0) + return 1; + + if (_stricmp(Call, "SYSOP") == 0) + return 1; + + if (_memicmp(Call, "SMTP", 4) == 0) + return 1; + + if (_memicmp(Call, "SMTP:", 5) == 0) + return 1; + + if (_stricmp(Call, "AMPR") == 0) + return 1; + + if (_stricmp(Call, "FILE") == 0) + return 1; + + if (_memicmp(Call, "MCAST", 5) == 0) + return 1; + + if (_memicmp(Call, "SYNC", 5) == 0) + return 1; + + return 0; +} + +extern config_t cfg; + +VOID GetWPDatabase() +{ + WPRec WPRec; + FILE * Handle; + int ReadLen; + WPRecP WP; + char CfgName[MAX_PATH]; + long long val; + config_t wpcfg; + config_setting_t * group, * wpgroup; + int i = 1; + struct stat STAT; + + // If WP info is in main config file, use it + + group = config_lookup (&cfg, "WP"); + + if (group) + { + // Set up control record + + WPRecPtr = malloc(sizeof(void *)); + WPRecPtr[0] = zalloc(sizeof(WPRec)); + NumberofWPrecs = 0; + + while (1) + { + char Key[16]; + char Record[1024]; + char * ptr, * ptr2; + unsigned int n; + + sprintf(Key, "R%d", i++); + + GetStringValue(group, Key, Record, 1024); + + if (Record[0] == 0) // End of List + return; + + memset(&WPRec, 0, sizeof(WPRec)); + + WP = &WPRec; + + ptr = Record; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(&WP->callsign[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(&WP->name[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) WP->Type = atoi(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) WP->changed = atoi(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) WP->seen = atoi(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(&WP->first_homebbs[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(&WP->secnd_homebbs[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(&WP->first_zip[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(&WP->secnd_zip[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + + if (ptr == NULL) continue; + + if (strlen(ptr) > 30) + ptr[30] = 0; + + strcpy(&WP->first_qth[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + + if (ptr == NULL) continue; + + if (strlen(ptr) > 30) + ptr[30] = 0; + + strcpy(&WP->secnd_qth[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + + if (ptr) WP->last_modif = atol(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + + if (ptr) + { + WP->last_seen = atol(ptr); + + // Check Call + + for (n = 1; n < strlen(WP->callsign); n++) // skip first which may also be digit + { + if (isdigit(WP->callsign[n])) + { + // Has a digit. Check Last is not digit + + if (isalpha(WP->callsign[strlen(WP->callsign) - 1])) + { + WP = LookupWP(WPRec.callsign); + if (WP == NULL) + WP = AllocateWPRecord(); + + memcpy(WP, &WPRec, sizeof(WPRec)); + goto WPOK; + } + } + } + Debugprintf("Bad WP Call %s", WP->callsign); + } +WPOK:; + } + return; + } + + // If text format exists use it + + strcpy(CfgName, WPDatabasePath); + strlop(CfgName, '.'); + strcat(CfgName, ".cfg"); + + if (stat(CfgName, &STAT) == -1) + goto tryOld; + + config_init(&wpcfg); + + if (!config_read_file(&wpcfg, CfgName)) + { + char Msg[256]; + sprintf(Msg, "Config File %s Line %d - %s\n", CfgName, + config_error_line(&wpcfg), config_error_text(&wpcfg)); + + printf("%s", Msg); + config_destroy(&wpcfg); + goto tryOld; + } + + // Set up control record + + WPRecPtr = malloc(sizeof(void *)); + WPRecPtr[0] = zalloc(sizeof(WPRec)); + NumberofWPrecs = 0; + + while (1) + { + char Key[16]; + char Temp[128]; + + sprintf(Key, "R%d", i++); + + wpgroup = config_lookup(&wpcfg, Key); + + if (wpgroup == NULL) // End of List + { + config_destroy(&wpcfg); + return; + } + + memset(&WPRec, 0, sizeof(WPRec)); + + GetStringValue(wpgroup, "c", WPRec.callsign, 6); + GetStringValue(wpgroup, "n", WPRec.name, 12); + + WPRec.Type = GetIntValue(wpgroup, "T"); + WPRec.changed = GetIntValue(wpgroup, "ch"); + WPRec.seen = GetIntValue(wpgroup, "s"); + + GetStringValue(wpgroup, "h", WPRec.first_homebbs, 40); + GetStringValue(wpgroup, "sh", WPRec.secnd_homebbs, 40); + GetStringValue(wpgroup, "z", WPRec.first_zip, 8); + GetStringValue(wpgroup, "sz", WPRec.secnd_zip, 8); + + GetStringValue(wpgroup, "q", Temp, 30); + Temp[30] = 0; + strcpy(WPRec.first_qth, Temp); + + GetStringValue(wpgroup, "sq", Temp, 30); + Temp[30] = 0; + strcpy(WPRec.secnd_qth, Temp); + + val = GetIntValue(wpgroup, "m"); + WPRec.last_modif = val; + val = GetIntValue(wpgroup, "ls"); + WPRec.last_seen = val; + + _strupr(WPRec.callsign); + + strlop(WPRec.callsign, ' '); + + if (strlen(WPRec.callsign) > 2) + { + if (strchr(WPRec.callsign, ':')) + continue; + + if (BadCall(WPRec.callsign)) + continue; + + WP = LookupWP(WPRec.callsign); + + if (WP == NULL) + WP = AllocateWPRecord(); + + memcpy(WP, &WPRec, sizeof(WPRec)); + } + } + +tryOld: + + Handle = fopen(WPDatabasePath, "rb"); + + if (Handle == NULL) + { + // Initialise a new File + + WPRecPtr = malloc(sizeof(void *)); + WPRecPtr[0] = malloc(sizeof(WPRec)); + memset(WPRecPtr[0], 0, sizeof(WPRec)); + NumberofWPrecs = 0; + + return; + } + + + // Get First Record + + ReadLen = fread(&WPRec, 1, sizeof(WPRec), Handle); + + if (ReadLen == 0) + { + // Duff file + + memset(&WPRec, 0, sizeof(WPRec)); + } + + // Set up control record + + WPRecPtr = malloc(sizeof(void *)); + WPRecPtr[0] = malloc(sizeof(WPRec)); + memcpy(WPRecPtr[0], &WPRec, sizeof(WPRec)); + + NumberofWPrecs = 0; + +Next: + + ReadLen = fread(&WPRec, 1, sizeof(WPRec), Handle); + + if (ReadLen == sizeof(WPRec)) + { + _strupr(WPRec.callsign); + + strlop(WPRec.callsign, ' '); + + if (strlen(WPRec.callsign) > 2) + { + if (strchr(WPRec.callsign, ':')) + goto Next; + + if (BadCall(WPRec.callsign)) + goto Next; + + WP = LookupWP(WPRec.callsign); + + if (WP == NULL) + WP = AllocateWPRecord(); + + memcpy(WP, &WPRec, sizeof(WPRec)); + } + goto Next; + } + + fclose(Handle); + SaveWPDatabase(); +} + +VOID CopyWPDatabase() +{ + char Backup[MAX_PATH]; + char Orig[MAX_PATH]; + + return; + + strcpy(Backup, WPDatabasePath); + strcat(Backup, ".bak"); + + CopyFile(WPDatabasePath, Backup, FALSE); + + strcpy(Backup, WPDatabasePath); + strlop(Backup, '.'); + strcat(Backup, ".cfg.bak"); + + strcpy(Orig, WPDatabasePath); + strlop(Orig, '.'); + strcat(Orig, ".cfg"); + CopyFile(Orig, Backup, FALSE); +} + +VOID SaveWPDatabase() +{ +// SaveConfig(ConfigName); // WP config is now in main config file + + int i; + config_setting_t *root, *group; + config_t cfg; + char Key[16]; + WPRec * WP; + char CfgName[MAX_PATH]; + long long val; + + memset((void *)&cfg, 0, sizeof(config_t)); + + config_init(&cfg); + + root = config_root_setting(&cfg); + + for (i = 0; i <= NumberofWPrecs; i++) + { + WP = WPRecPtr[i]; + sprintf(Key, "R%d", i); + + group = config_setting_add(root, Key, CONFIG_TYPE_GROUP); + + SaveStringValue(group, "c", &WP->callsign[0]); + SaveStringValue(group, "n", &WP->name[0]); + SaveIntValue(group, "T", WP->Type); + SaveIntValue(group, "ch", WP->changed); + SaveIntValue(group, "s", WP->seen); + SaveStringValue(group, "h", &WP->first_homebbs[0]); + SaveStringValue(group, "sh", &WP->secnd_homebbs[0]); + SaveStringValue(group, "z", &WP->first_zip[0]); + SaveStringValue(group, "sz", &WP->secnd_zip[0]); + SaveStringValue(group, "q", &WP->first_qth[0]); + SaveStringValue(group, "sq", &WP->secnd_qth[0]); + val = WP->last_modif; + SaveInt64Value(group, "m", val); + val = WP->last_seen; + SaveInt64Value(group, "ls", val); + } + + strcpy(CfgName, WPDatabasePath); + strlop(CfgName, '.'); + strcat(CfgName, ".cfg"); + + Debugprintf("Saving WP Database to %s\n", CfgName); + + config_write_file(&cfg, CfgName); + config_destroy(&cfg); + +} + +WPRec * LookupWP(char * Call) +{ + WPRec * ptr = NULL; + int i; + + for (i=1; i <= NumberofWPrecs; i++) + { + ptr = WPRecPtr[i]; + + if (_stricmp(ptr->callsign, Call) == 0) return ptr; + } + + return NULL; +} + +char * FormatWPDate(time_t Datim) +{ + struct tm *tm; + static char Date[]="xx-xxx-xx "; + + tm = gmtime(&Datim); + + if (tm) + { + if (tm->tm_year >= 100) + sprintf_s(Date, sizeof(Date), "%02d-%3s-%02d", + tm->tm_mday, month[tm->tm_mon], tm->tm_year - 100); + else + sprintf_s(Date, sizeof(Date), ""); + } + return Date; +} + +#ifndef LINBPQ + +int Do_WP_Sel_Changed(HWND hDlg) +{ + // Update WP display with newly selected rec + + WPRec * WP; + int Sel = SendDlgItemMessage(hDlg, IDC_WP, CB_GETCURSEL, 0, 0); + char Type[] = " "; + + if (Sel == -1) + SendDlgItemMessage(hDlg, IDC_WP, WM_GETTEXT, Sel, (LPARAM)(LPCTSTR)&CurrentWPCall); + else + SendDlgItemMessage(hDlg, IDC_WP, CB_GETLBTEXT, Sel, (LPARAM)(LPCTSTR)&CurrentWPCall); + + for (CurrentWPIndex = 1; CurrentWPIndex <= NumberofWPrecs; CurrentWPIndex++) + { + WP = WPRecPtr[CurrentWPIndex]; + + if (_stricmp(WP->callsign, CurrentWPCall) == 0) + { + + SetDlgItemText(hDlg, IDC_WPNAME, WP->name); + SetDlgItemText(hDlg, IDC_HOMEBBS1, WP->first_homebbs); + SetDlgItemText(hDlg, IDC_HOMEBBS2, WP->secnd_homebbs); + SetDlgItemText(hDlg, IDC_QTH1, WP->first_qth); + SetDlgItemText(hDlg, IDC_QTH2, WP->secnd_qth); + SetDlgItemText(hDlg, IDC_ZIP1, WP->first_zip); + SetDlgItemText(hDlg, IDC_ZIP2, WP->secnd_zip); + Type[0] = WP->Type; + SetDlgItemText(hDlg, IDC_TYPE, Type); + SetDlgItemInt(hDlg, IDC_CHANGED, WP->changed, FALSE); + SetDlgItemInt(hDlg, IDC_SEEN, WP->seen, FALSE); + + SetDlgItemText(hDlg, IDC_LASTSEEN, FormatWPDate(WP->last_seen)); + SetDlgItemText(hDlg, IDC_LASTMODIFIED, FormatWPDate(WP->last_modif)); + + return 0; + } + } + + CurrentWPIndex = -1; + + return 0; +} + +INT_PTR CALLBACK InfoDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + + +VOID Do_Save_WPRec(HWND hDlg) +{ + WPRec * WP; + char Type[] = " "; + BOOL OK1; + + if (CurrentWPIndex == -1) + { + sprintf(InfoBoxText, "Please select a WP Record to save"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return; + } + + WP = WPRecPtr[CurrentWPIndex]; + + if (strcmp(CurrentWPCall, WP->callsign) != 0) + { + sprintf(InfoBoxText, "Inconsistancy detected - record not saved"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return; + } + + GetDlgItemText(hDlg, IDC_WPNAME, WP->name, 13); + GetDlgItemText(hDlg, IDC_HOMEBBS1, WP->first_homebbs, 41); + GetDlgItemText(hDlg, IDC_HOMEBBS2, WP->secnd_homebbs, 41); + GetDlgItemText(hDlg, IDC_QTH1, WP->first_qth, 31); + GetDlgItemText(hDlg, IDC_QTH2, WP->secnd_qth, 31); + GetDlgItemText(hDlg, IDC_ZIP1, WP->first_zip, 31); + GetDlgItemText(hDlg, IDC_ZIP2, WP->secnd_zip, 31); + WP->last_modif = time(NULL); + WP->seen = GetDlgItemInt(hDlg, IDC_SEEN, &OK1, FALSE); + + WP->Type = 'U'; + WP->changed = 1; + + SaveWPDatabase(); + + sprintf(InfoBoxText, "WP information saved"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); +} + +VOID Do_Delete_WPRec(HWND hDlg) +{ + WPRec * WP; + int n; + + if (CurrentWPIndex == -1) + { + sprintf(InfoBoxText, "Please select a WP Record to delete"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return; + } + + WP = WPRecPtr[CurrentWPIndex]; + + if (strcmp(CurrentWPCall, WP->callsign) != 0) + { + sprintf(InfoBoxText, "Inconsistancy detected - record not deleted"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return; + } + + for (n = CurrentWPIndex; n < NumberofWPrecs; n++) + { + WPRecPtr[n] = WPRecPtr[n+1]; // move down all following entries + } + + NumberofWPrecs--; + + SendDlgItemMessage(hDlg, IDC_WP, CB_RESETCONTENT, 0, 0); + + for (n = 1; n <= NumberofWPrecs; n++) + { + SendDlgItemMessage(hDlg, IDC_WP, CB_ADDSTRING, 0, (LPARAM)WPRecPtr[n]->callsign); + } + + + sprintf(InfoBoxText, "WP record for %s deleted", WP->callsign); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + + free(WP); + + SaveWPDatabase(); + + return; + +} + +INT_PTR CALLBACK WPEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + int Command, n; + + UNREFERENCED_PARAMETER(lParam); + switch (message) + { + + case WM_INITDIALOG: + + for (n = 1; n <= NumberofWPrecs; n++) + { + SendDlgItemMessage(hDlg, IDC_WP, CB_ADDSTRING, 0, (LPARAM)WPRecPtr[n]->callsign); + } + + return (INT_PTR)TRUE; + + case WM_CTLCOLORDLG: + + return (LONG)bgBrush; + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + + case WM_COMMAND: + + Command = LOWORD(wParam); + + switch (Command) + { + + case IDOK: + case IDCANCEL: + + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + + case IDC_WP: + + // Msg Selection Changed + + Do_WP_Sel_Changed(hDlg); + + return TRUE; + + case IDC_SAVEWP: + + Do_Save_WPRec(hDlg); + return TRUE; + + case IDC_DELETEWP: + + Do_Delete_WPRec(hDlg); + return TRUE; + } + break; + } + + return (INT_PTR)FALSE; +} +#endif + +VOID GetWPBBSInfo(char * Rline) +{ + // Update WP with /I records for each R: Line + + // R:111206/1636Z 29130@N9PMO.#SEWI.WI.USA.NOAM [Racine, WI] FBB7.00i + + struct tm rtime; + time_t RLineTime; + int Age; + + WPRec * WP; + char ATBBS[200]; + char Call[200]; + char QTH[200] = ""; + int RLen; + + char * ptr1; + char * ptr2; + + + memset(&rtime, 0, sizeof(struct tm)); + + if (Rline[10] == '/') + { + // Dodgy 4 char year + + sscanf(&Rline[2], "%04d%02d%02d/%02d%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min); + rtime.tm_year -= 1900; + rtime.tm_mon--; + } + else if (Rline[8] == '/') + { + sscanf(&Rline[2], "%02d%02d%02d/%02d%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min); + + if (rtime.tm_year < 90) + rtime.tm_year += 100; // Range 1990-2089 + rtime.tm_mon--; + } + + // Otherwise leave date as zero, which should be rejected + + if ((RLineTime = mktime(&rtime)) != (time_t)-1 ) + { + Age = (time(NULL) - RLineTime)/86400; + + if ( Age < -1) + return; // in the future + + if (Age > BidLifetime || Age > MaxAge) + return; // Too old + } + + ptr1 = strchr(Rline, '@'); + ptr2 = strchr(Rline, '\r'); + + if (!ptr1) + return; // Duff + + if (*++ptr1 == ':') + ptr1++; // Format 2 + + + if (ptr2 == NULL) + return; // No CR on end + + RLen = ptr2 - ptr1; + + if (RLen > 200) + return; + + memcpy(ATBBS, ptr1, RLen); + ATBBS[RLen] = 0; + + ptr2 = strchr(ATBBS, ' '); + + if (ptr2) + *ptr2 = 0; + + strcpy(Call, ATBBS); + strlop(Call, '.'); + + ptr2 = memchr(ptr1, '[', RLen); + + if (ptr2) + { + ptr1= memchr(ptr2, ']', RLen); + if (ptr1) + memcpy(QTH, ptr2 + 1, ptr1 - ptr2 - 1); + } + + if (BadCall(Call)) + return; + + WP = LookupWP(Call); + + if (!WP) + { + // Not Found + + WP = AllocateWPRecord(); + + strcpy(WP->callsign, Call); + strcpy(WP->first_homebbs, ATBBS); + strcpy(WP->secnd_homebbs, ATBBS); + + if (QTH[0]) + { + strcpy(WP->first_qth, QTH); + strcpy(WP->secnd_qth, QTH); + } + + WP->last_modif = RLineTime; + WP->last_seen = RLineTime; + + WP->Type = 'I'; + WP->changed = TRUE; + + return; + } + + if (WP->last_modif >= RLineTime || WP->Type != 'I') + return; + + // Update 2nd if changed + + if (strcmp(WP->secnd_homebbs , ATBBS) != 0) + { + strcpy(WP->secnd_homebbs, ATBBS); + WP->last_modif = RLineTime; + } + + if (QTH[0] && strcmp(WP->secnd_qth , QTH) != 0) + { + strcpy(WP->secnd_qth, QTH); + WP->last_modif = RLineTime; + } + + return; +} + + + + +VOID GetWPInfoFromRLine(char * From, char * FirstRLine, time_t RLineTime) +{ + /* The /G suffix denotes that the information in this line has been gathered by examining + the header of a message to GUESS at which BBS the sender is registered. The HomeBBS of the User + is assumed to be the BBS shown in the first R: header line. The date associated with this + information is the date shown on this R: header line. + */ + + // R:930101/0000 1530@KA6FUB.#NOCAL.CA.USA.NOAM + + // R:930101/0000 @:KA6FUB.#NOCAL.CA.USA.NOAM #:1530 + + // The FirstRLine pointer points to the message, so shouldnt be changed + + WPRec * WP; + char ATBBS[200]; + int RLen; + + char * ptr1 = strchr(FirstRLine, '@'); + char * ptr2 = strchr(FirstRLine, '\r'); + + if (BadCall(From)) + return; + + if (!ptr1) + return; // Duff + + if (*++ptr1 == ':') + ptr1++; // Format 2 + + RLen = ptr2 - ptr1; + + if (RLen > 200) + return; + + memcpy(ATBBS, ptr1, RLen); + ATBBS[RLen] = 0; + + ptr2 = strchr(ATBBS, ' '); + + if (ptr2) + *ptr2 = 0; + + if (strlen(ATBBS) > 40) + ATBBS[40] = 0; + + WP = LookupWP(From); + + if (!WP) + { + // Not Found + + WP = AllocateWPRecord(); + + strcpy(WP->callsign, From); + strcpy(WP->first_homebbs, ATBBS); + strcpy(WP->secnd_homebbs, ATBBS); + + WP->last_modif = RLineTime; + WP->last_seen = RLineTime; + + WP->Type = 'G'; + WP->changed = TRUE; + + return; + } + + if (WP->last_modif >= RLineTime) + return; + + // Update 2nd if changed + + if (strcmp(WP->secnd_homebbs , ATBBS) != 0) + { + strcpy(WP->secnd_homebbs, ATBBS); + WP->last_modif = RLineTime; + } + + return; +} + +VOID ProcessWPMsg(char * MailBuffer, int Size, char * FirstRLine) +{ + char * ptr1 = MailBuffer; + char * ptr2; + WPRec * WP; + char WPLine[200]; + int WPLen; + + ptr1[Size] = 0; + + ptr1 = FirstRLine; + + if (ptr1 == NULL) + return; + + while (*ptr1) + { + ptr2 = strchr(ptr1, '\r'); + + if (ptr2 == 0) // No CR in buffer + return; + + WPLen = ptr2 - ptr1; + + if (WPLen > 128) + return; + + if ((memcmp(ptr1, "On ", 3) == 0) && (WPLen < 200)) + { + char * Date; + char * Call; + char * AT; + char * HA; + char * zip; + char * ZIP; + char * Name; + char * QTH = NULL; + char * Context; + char seps[] = " \r"; + + // Make copy of string, as strtok messes with it + + memcpy(WPLine, ptr1, WPLen); + WPLine[WPLen] = 0; + + Date = strtok_s(WPLine+3, seps, &Context); + Call = strtok_s(NULL, seps, &Context); + AT = strtok_s(NULL, seps, &Context); + HA = strtok_s(NULL, seps, &Context); + zip = strtok_s(NULL, seps, &Context); + ZIP = strtok_s(NULL, seps, &Context); + Name = strtok_s(NULL, seps, &Context); + QTH = strtok_s(NULL, "\r", &Context); // QTH may have spaces + + if (Date == 0 || Call == 0 || AT == 0 || ZIP == 0 || Name == 0 || QTH == 0) + return; + + if (strlen(HA) > 40) + return; + if (strlen(ZIP) > 8) + return; + if (strlen(Name) > 12) + return; + if (strlen(QTH) > 30) + return; + + if (AT[0] == '@' && (QTH)) + { + struct tm rtime; + time_t WPDate; + char Type; + char * TypeString; + + if (memcmp(Name, "?", 2) == 0) Name = NULL; + if (memcmp(ZIP, "?", 2) == 0) ZIP = NULL; + if (memcmp(QTH, "?", 2) == 0) QTH = NULL; + + memset(&rtime, 0, sizeof(struct tm)); + + sscanf(Date, "%02d%02d%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday); + + rtime.tm_year += 100; + rtime.tm_mon--; + +/* +This process freshens the database, following receipt of the new or changed information detailed above. + +The update subroutine will first look for an entry in the database for the callsign which matches the received information. +If it does not exist then a completely new record will be created in the database and the information be used to fill what +fields it can, in both the active and the temporary components. The date will be then changed to the one associated with the +update information. + +If the record does already exist, then the unknown fields of both the temporary and active fields will be filled in, and +those fields already known in the temporary part will be replaced by the new information if the date new information is +younger than that already on file. The date will then be +adjusted such that it is consistent with the updated information. + +If the new information is of the /U category, then the current fields will be replaced by the new information in both +the primary and secondary (Active and Temporary) parts of the record, as this information has been input directly from +the user. If the information was of another category then only the secondary (Temporary) part of the record will be +updated, so the Active or primary record will remain unchanged at this time. + +If a field is changed, a flag giving the update request type is then validated. If the /U flag is already validated, +it will not be replaced. This flag will be used in case the WP update messages are validated. +*/ + if ((WPDate = mktime(&rtime)) != (time_t)-1 ) + { + WPDate -= (time_t)_MYTIMEZONE; + TypeString = strlop(Call, '/'); + + if (strlen(Call) < 3 || strlen(Call) > 6) + return; + + if (TypeString) + Type = TypeString[0]; + else + Type = 'G'; + + if (strchr(Call, ':')) + break; + + if (BadCall(Call)) + break; + + WP = LookupWP(Call); + + if (WP) + { + // Found, so update + + DoWPUpdate(WP, Type, Name, HA, QTH, ZIP, WPDate); + } + else + { + WP = AllocateWPRecord(); + + strcpy(WP->callsign, Call); + if (Name) strcpy(WP->name, Name); + strcpy(WP->first_homebbs, HA); + strcpy(WP->secnd_homebbs, HA); + + if (QTH) + { + strcpy(WP->first_qth, QTH); + strcpy(WP->secnd_qth, QTH);; + } + if (ZIP) + { + strcpy(WP->first_zip, ZIP); + strcpy(WP->secnd_zip, ZIP); + } + + WP->Type = Type; + WP->last_modif = WPDate; + WP->last_seen = WPDate; + WP->changed = TRUE; + WP->seen++; + } + } + } + } + + ptr1 = ++ptr2; + if (*ptr1 == '\n') + ptr1++; + } + + SaveWPDatabase(); + + return; +} + +VOID DoWPUpdate(WPRec * WP, char Type, char * Name, char * HA, char * QTH, char * ZIP, time_t WPDate) +{ + // First Update any unknown field + + if(Name) + if (WP->name == NULL) {strcpy(WP->name, Name); WP->last_modif = WPDate; WP->changed = TRUE;} + + if (QTH) + { + if (WP->first_qth == NULL) {strcpy(WP->first_qth, QTH); WP->last_modif = WPDate; WP->changed = TRUE;} + if (WP->secnd_qth == NULL) {strcpy(WP->secnd_qth, QTH); WP->last_modif = WPDate; WP->changed = TRUE;} + } + if (ZIP) + { + if (WP->first_zip == NULL) {strcpy(WP->first_zip, ZIP); WP->last_modif = WPDate; WP->changed = TRUE;} + if (WP->secnd_zip == NULL) {strcpy(WP->secnd_zip, ZIP); WP->last_modif = WPDate; WP->changed = TRUE;} + } + + WP->last_seen = WPDate; + WP->seen++; + + // Now Update Temp Fields if update is newer than original + + if (WP->last_modif >= WPDate) + return; + + if (Type == 'U') // Definitive Update + { + if (strcmp(WP->first_homebbs , HA) != 0) + { + strcpy(WP->first_homebbs, HA); strcpy(WP->secnd_homebbs, HA); WP->last_modif = WPDate; WP->changed = TRUE; + } + + if (Name) + { + if (strcmp(WP->name , Name) != 0) + { + strcpy(WP->name, Name); + WP->last_modif = WPDate; + WP->changed = TRUE; + } + } + + if (QTH) + { + if (strcmp(WP->first_qth , QTH) != 0) + { + strcpy(WP->first_qth, QTH); + strcpy(WP->secnd_qth, QTH); + WP->last_modif = WPDate; + WP->changed = TRUE; + } + } + + if (ZIP) + { + if (strcmp(WP->first_zip , ZIP) != 0) + { + strcpy(WP->first_zip, ZIP); + strcpy(WP->secnd_zip, ZIP); + WP->last_modif = WPDate; + WP->changed = TRUE; + } + } + + WP->Type = Type; + + return; + } + + // Non-Definitive - only update second copy + + if (strcmp(WP->secnd_homebbs , HA) != 0) {strcpy(WP->secnd_homebbs, HA); WP->last_modif = WPDate; WP->Type = Type;} + + if (Name) + if (strcmp(WP->name , Name) != 0) {strcpy(WP->name, Name); WP->last_modif = WPDate; WP->Type = Type;} + + if (QTH) + if (strcmp(WP->secnd_qth , QTH) != 0) {strcpy(WP->secnd_qth, QTH); WP->last_modif = WPDate; WP->Type = Type;} + + if (ZIP) + if (strcmp(WP->secnd_zip , ZIP) != 0) {strcpy(WP->secnd_zip, ZIP); WP->last_modif = WPDate; WP->Type = Type;} + + return; +} + +VOID UpdateWPWithUserInfo(struct UserInfo * user) +{ + WPRec * WP = LookupWP(user->Call); + + if (strchr(user->Call, ':')) + return; + + if (BadCall(user->Call)) + return; + + if (!WP) + { + WP = AllocateWPRecord(); + strcpy(WP->callsign, user->Call); + } + + // Update Record + + if (user->HomeBBS[0]) + { + strcpy(WP->first_homebbs, user->HomeBBS); + strcpy(WP->secnd_homebbs, user->HomeBBS); + } + + if (user->Address[0]) + { + char Temp[127]; + strcpy(Temp, user->Address); + Temp[30] = 0; + + strcpy(WP->first_qth, Temp); + strcpy(WP->secnd_qth, Temp); + } + + if (user->ZIP[0]) + { + strcpy(WP->first_zip, user->ZIP); + strcpy(WP->secnd_zip, user->ZIP ); + } + + if (user->Name[0]) + strcpy(WP->name, user->Name); + + WP->last_modif = WP->last_seen = time(NULL); + + WP->changed = TRUE; + WP->Type = 'U'; + + SaveWPDatabase(); + +} + +VOID DoWPLookup(ConnectionInfo * conn, struct UserInfo * user, char Type, char *Param) +{ + // Process the I call command + + WPRec * ptr = NULL; + int i; + char ATBBS[100]; + char * HA; + char * Rest; + + _strupr(Param); + Type = toupper(Type); + + switch (Type) + { + case 0: + + for (i=1; i <= NumberofWPrecs; i++) + { + ptr = WPRecPtr[i]; + + if (wildcardcompare(ptr->callsign, Param)) + { + nodeprintf(conn, "%s %s %s %s %s\r", ptr->callsign, ptr->first_homebbs, + ptr->name, ptr->first_zip, ptr->first_qth); + } + } + + return; + + case'@': // AT BBS + + for (i=1; i <= NumberofWPrecs; i++) + { + ptr = WPRecPtr[i]; + + strcpy(ATBBS, ptr->first_homebbs); + strlop(ATBBS,'.'); + + if (wildcardcompare(ATBBS, Param)) + { + nodeprintf(conn, "%s %s %s %s %s\r", ptr->callsign, ptr->first_homebbs, ptr->name, ptr->first_zip, ptr->first_qth); + } + } + + case'H': // Hierarchic Element + + for (i=1; i <= NumberofWPrecs; i++) + { + ptr = WPRecPtr[i]; + + strcpy(ATBBS, ptr->first_homebbs); + + Rest = strlop(ATBBS,'.'); + + if (Rest == 0) + continue; + + HA = strtok_s(Rest, ".", &Rest); + + while (HA) + { + if (wildcardcompare(HA, Param)) + { + nodeprintf(conn, "%s %s %s %s %s\r", ptr->callsign, ptr->first_homebbs, ptr->name, ptr->first_zip, ptr->first_qth); + } + + HA = strtok_s(NULL, ".", &Rest); + } + } + return; + + case'Z': // ZIP + + for (i=1; i <= NumberofWPrecs; i++) + { + ptr = WPRecPtr[i]; + + if (ptr->first_zip[0] == 0) + continue; + + if (wildcardcompare(ptr->first_zip, Param)) + { + nodeprintf(conn, "%s %s %s %s %s\r", ptr->callsign, ptr->first_homebbs, ptr->name, ptr->first_zip, ptr->first_qth); + } + } + return; + } + nodeprintf(conn, "Invalid I command option %c\r", Type); +} +/* +On 111120 N4ZKF/I @ N4ZKF.#NFL.FL.USA.NOAM zip 32118 Dave 32955 +On 111120 F6IQF/I @ F6IQF.FRPA.FRA.EU zip ? ? f6iqf.dyndns.org +On 111121 W9JUN/I @ W9JUN.W9JUN.#SEIN.IN.US.NOAM zip 47250 Don NORTH VERNON, IN +On 111120 KR8ZY/U @ KR8ZY zip ? john ? +On 111120 N0DEC/G @ N0ARY.#NCA.CA.USA.NOAM zip ? ? ? + +From: N0JAL +To: WP +Type/Status: B$ +Date/Time: 22-Nov 10:15Z +Bid: 95F7N0JAL +Title: WP Update + +R:111122/1500Z 35946@KD6PGI.OR.USA.NOAM BPQ1.0.4 +R:111122/1020 16295@K7ZS.OR.USA.NOAM +R:111122/1015 38391@N0JAL.OR.USA.NOAM + +On 111121 N0JAL @ N0JAL.OR.USA.NOAM zip ? Sai Damascus, Oregon CN85sj + +*/ + +VOID UpdateWP() +{ + // If 2nd copy of info has been unchanged for 30 days, copy to active fields + + WPRec * ptr = NULL; + int i; + char * via = NULL; + int MsgLen = 0; + char MailBuffer[100100]; + char * Buffptr = MailBuffer; + int WriteLen=0; + char HDest[61] = "WP"; + char WPMsgType = 'P'; + time_t NOW = time(NULL); + time_t UpdateLimit = NOW - (86400 * 30); // 30 days + LASTWPSendTime = NOW - (86400 * 5); // 5 days max + + for (i=1; i <= NumberofWPrecs; i++) + { + ptr = WPRecPtr[i]; + + // DO we have a new field, and if so is it different? + + if ((ptr->secnd_homebbs[0] && _stricmp(ptr->first_homebbs, ptr->secnd_homebbs)) + || (ptr->secnd_qth[0] && _stricmp(ptr->first_qth, ptr->secnd_qth)) + || (ptr->secnd_zip[0] && _stricmp(ptr->first_zip, ptr->secnd_zip))) + { + // Have new data + + if (ptr->last_modif < UpdateLimit) + { + // Stable for 30 days + + if (ptr->secnd_homebbs[0]) + strcpy(ptr->first_homebbs, ptr->secnd_homebbs); + if (ptr->secnd_qth[0]) + strcpy(ptr->first_qth, ptr->secnd_qth); + if (ptr->secnd_zip[0]) + strcpy(ptr->first_zip, ptr->secnd_zip); + + ptr->last_modif = NOW; + + } + } + } +} + +int CreateWPMessage() +{ + // Include all messages with Type of U whach have changed since LASTWPSendTime + + WPRec * ptr = NULL; + int i; + struct tm *tm; + struct MsgInfo * Msg; + char * via = NULL; + char BID[13]; + BIDRec * BIDRec; + int MsgLen = 0; + char MailBuffer[100100]; + char * Buffptr = MailBuffer; + char MsgFile[MAX_PATH]; + FILE * hFile; + int WriteLen=0; + char ** To = SendWPAddrs; + + LASTWPSendTime = time(NULL) - (86400 * 5); // 5 days max + + for (i=1; i <= NumberofWPrecs; i++) + { + ptr = WPRecPtr[i]; + +// if (ptr->last_modif > LASTWPSendTime && ptr->Type == 'U' && ptr->first_homebbs[0]) + if (ptr->changed && ptr->last_modif > LASTWPSendTime && ptr->first_homebbs[0]) + { + tm = gmtime((time_t *)&ptr->last_modif); + MsgLen += sprintf(Buffptr, "On %02d%02d%02d %s/%c @ %s zip %s %s %s\r\n", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, + ptr->callsign, ptr->Type, ptr->first_homebbs, + (ptr->first_zip[0]) ? ptr->first_zip : "?", + (ptr->name[0]) ? ptr->name : "?", + (ptr->first_qth[0]) ? ptr->first_qth : "?"); + + Buffptr = &MailBuffer[MsgLen]; + + ptr->changed = FALSE; + + if (MsgLen > 100000) + break; + } + } + + if (MsgLen == 0) + return TRUE; + + while(To[0]) + { + char TO[256]; + char * VIA; + + Msg = AllocateMsgRecord(); + + // Set number here so they remain in sequence + + Msg->number = ++LatestMsg; + Msg->length = MsgLen; + MsgnotoMsg[Msg->number] = Msg; + + strcpy(Msg->from, BBSName); + + strcpy(TO, To[0]); + + VIA = strlop(TO, '@'); + + if (VIA) + { + if (strlen(VIA) > 40) + VIA[40] = 0; + strcpy(Msg->via, VIA); + } + strcpy(Msg->to, TO); + + strcpy(Msg->title, "WP Update"); + + Msg->type = (SendWPType) ? 'P' : 'B'; + Msg->status = 'N'; + + sprintf_s(BID, sizeof(BID), "%d_%s", LatestMsg, BBSName); + + strcpy(Msg->bid, BID); + + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + BIDRec = AllocateBIDRecord(); + + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + fwrite(MailBuffer, 1, Msg->length, hFile); + fclose(hFile); + } + + MatchMessagetoBBSList(Msg, 0); + + BuildNNTPList(Msg); // Build NNTP Groups list + +#ifndef NOMQTT + if (MQTT) + MQTTMessageEvent(Msg); +#endif + To++; + } + + SaveMessageDatabase(); + SaveBIDDatabase(); + + return TRUE; +} + +VOID CreateWPReport() +{ + int i; + char Line[200]; + int len; + char File[100]; + FILE * hFile; + WPRec * WP = NULL; + + sprintf_s(File, sizeof(File), "%s/wp.txt", BaseDir); + + hFile = fopen(File, "wb"); + + if (hFile == NULL) + return; + +// len = sprintf(Line, " Call Connects Connects Messages Messages Bytes Bytes Rejected Rejected\r\n"); +// WriteFile(hFile, Line, len, &written, NULL); +// len = sprintf(Line, " In Out Rxed Sent Rxed Sent In Out\r\n\r\n"); +// WriteFile(hFile, Line, len, &written, NULL); + + + for (i=1; i <= NumberofWPrecs; i++) + { + WP = WPRecPtr[i]; + + len = sprintf(Line, "%-7s,%c,%s,%s,%s,%s,%s,%s,%s,%d,%s,%s\r\n", + WP->callsign, WP->Type, WP->first_homebbs, WP->first_qth, WP->first_zip, + WP->secnd_homebbs, WP->secnd_qth, WP->secnd_zip, WP->name, WP->changed, + FormatWPDate((time_t)WP->last_modif), + FormatWPDate((time_t)WP->last_seen)); + + fwrite(Line, 1, len, hFile); + } + fclose(hFile); +} + + + + + + diff --git a/.svn/pristine/03/03b559519d295623ca3bf53396185358ecec2f30.svn-base b/.svn/pristine/03/03b559519d295623ca3bf53396185358ecec2f30.svn-base index 58afaa7..fbdf53d 100644 --- a/.svn/pristine/03/03b559519d295623ca3bf53396185358ecec2f30.svn-base +++ b/.svn/pristine/03/03b559519d295623ca3bf53396185358ecec2f30.svn-base @@ -1,232 +1,232 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {8EFA1E59-8654-4A23-8102-AA77A074D57C} - CBPQ32 - Win32Proj - 10.0.17763.0 - - - - DynamicLibrary - v141 - NotSet - false - - - DynamicLibrary - v141 - MultiByte - - - - - - - - - - - - - - <_ProjectFileVersion>15.0.28127.55 - - - $(SolutionDir)$(Configuration)\ - C:\Dev\Msdev2005\Intermed\$(SolutionName)\$(Configuration)\ - true - - - $(SolutionDir)$(Configuration)\ - C:\Dev\Msdev2005\Intermed\$(SolutionName)\$(Configuration)\ - false - - - - 3 - ..\CInclude - true - - - Disabled - ..\CInclude;..\CommonSource;..\CKernel;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_WINDOWS;_USRDLL;BPQ32_EXPORTS;MDIKERNEL;_USE_32BIT_TIME_T;%(PreprocessorDefinitions) - true - EnableFastChecks - MultiThreadedDebug - - All - c:\devprogs\bpq32\listings\debug\ - true - Level3 - EditAndContinue - - - /section:_BPQDATA,srw %(AdditionalOptions) - WS2_32.Lib;winmm.lib;DbgHelp.lib;comctl32.lib;Iphlpapi.lib;setupapi.lib;..\lib\libconfig.lib;miniupnpc.lib;zlibstat.lib;%(AdditionalDependencies) - c:\DevProgs\BPQ32\bpq32.dll - false - LIBCMTD.lib;%(IgnoreSpecificDefaultLibraries) - ..\CommonSource\bpq32.def - true - true - c:\DevProgs\BPQ32\bpqdev.map - true - Windows - 8000000 - 4000000 - false - - 0x42000000 - ..\lib\bpq32.lib - MachineX86 - false - - - C:\Dev\Msdev2005\Intermed\$(SolutionName)\$(Configuration)\$(ProjectName).bsc - - - "C:\Program Files\7-Zip\7z.exe" a C:\DevProgs\BPQ32\bpq32.zip C:\DevProgs\BPQ32\bpq32.dll && myxcopy /y c:\DevProgs\BPQ32\bpq32.dll c:\windows\SysWOW64\bpq32.dll && del C:\DevProgs\BPQ32\bpq32.dll - - - - - 3 - $(IntDir)$(ProjectName) - ..\CInclude - true - true - true - true - - - /D "MDIKERNEL" %(AdditionalOptions) - Disabled - false - ..\CInclude;..\CommonSource;..\CKernel;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;_USRDLL;BPQ32_EXPORTS;MDIKERNEL;_USE_32BIT_TIME_T;%(PreprocessorDefinitions) - MultiThreaded - - All - c:\devprogs\bpq32\listings\ - Level3 - ProgramDatabase - - - /section:_BPQDATA,srw %(AdditionalOptions) - WS2_32.Lib;winmm.lib;DbgHelp.lib;comctl32.lib;setupapi.lib;..\lib\libconfig.lib;miniupnpc.lib;zlibstat.lib;%(AdditionalDependencies) - C:\DevProgs\BPQ32\bpq32.dll - ..\CommonSource\bpq32.def - true - c:\DevProgs\BPQ32\bpq32.pdb - true - c:\DevProgs\BPQ32\bpqpdn.map - true - Windows - true - true - - 0x42000000 - C:\Dev\Msdev2005\Projects\BPQ32\lib\bpq32.lib - MachineX86 - - - C:\Dev\Msdev2005\Intermed\$(SolutionName)\$(Configuration)\$(ProjectName).bsc - - - "C:\Program Files\7-Zip\7z.exe" a C:\DevProgs\BPQ32\bpq32.zip C:\DevProgs\BPQ32\bpq32.dll && myxcopy /y c:\DevProgs\BPQ32\bpq32.dll c:\windows\SysWOW64\bpq32.dll && del C:\DevProgs\BPQ32\bpq32.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + + {8EFA1E59-8654-4A23-8102-AA77A074D57C} + CBPQ32 + Win32Proj + 10.0.17763.0 + + + + DynamicLibrary + v141 + NotSet + false + + + DynamicLibrary + v141 + MultiByte + + + + + + + + + + + + + + <_ProjectFileVersion>15.0.28127.55 + + + $(SolutionDir)$(Configuration)\ + C:\Dev\Msdev2005\Intermed\$(SolutionName)\$(Configuration)\ + true + + + $(SolutionDir)$(Configuration)\ + C:\Dev\Msdev2005\Intermed\$(SolutionName)\$(Configuration)\ + false + + + + 3 + ..\CInclude + true + + + Disabled + ..\CInclude;..\CommonSource;..\CKernel;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;BPQ32_EXPORTS;MDIKERNEL;_USE_32BIT_TIME_T;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + + All + c:\devprogs\bpq32\listings\debug\ + true + Level3 + EditAndContinue + + + /section:_BPQDATA,srw %(AdditionalOptions) + WS2_32.Lib;winmm.lib;DbgHelp.lib;comctl32.lib;Iphlpapi.lib;setupapi.lib;..\lib\libconfig.lib;miniupnpc.lib;zlibstat.lib;%(AdditionalDependencies) + c:\DevProgs\BPQ32\bpq32.dll + false + LIBCMTD.lib;%(IgnoreSpecificDefaultLibraries) + ..\CommonSource\bpq32.def + true + true + c:\DevProgs\BPQ32\bpqdev.map + true + Windows + 8000000 + 4000000 + false + + 0x42000000 + ..\lib\bpq32.lib + MachineX86 + false + + + C:\Dev\Msdev2005\Intermed\$(SolutionName)\$(Configuration)\$(ProjectName).bsc + + + "C:\Program Files\7-Zip\7z.exe" a C:\DevProgs\BPQ32\bpq32.zip C:\DevProgs\BPQ32\bpq32.dll && myxcopy /y c:\DevProgs\BPQ32\bpq32.dll c:\windows\SysWOW64\bpq32.dll && del C:\DevProgs\BPQ32\bpq32.dll + + + + + 3 + $(IntDir)$(ProjectName) + ..\CInclude + true + true + true + true + + + /D "MDIKERNEL" %(AdditionalOptions) + Disabled + false + ..\CInclude;..\CommonSource;..\CKernel;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;BPQ32_EXPORTS;MDIKERNEL;_USE_32BIT_TIME_T;%(PreprocessorDefinitions) + MultiThreaded + + All + c:\devprogs\bpq32\listings\ + Level3 + ProgramDatabase + + + /section:_BPQDATA,srw %(AdditionalOptions) + WS2_32.Lib;winmm.lib;DbgHelp.lib;comctl32.lib;setupapi.lib;..\lib\libconfig.lib;miniupnpc.lib;zlibstat.lib;%(AdditionalDependencies) + C:\DevProgs\BPQ32\bpq32.dll + ..\CommonSource\bpq32.def + true + c:\DevProgs\BPQ32\bpq32.pdb + true + c:\DevProgs\BPQ32\bpqpdn.map + true + Windows + true + true + + 0x42000000 + C:\Dev\Msdev2005\Projects\BPQ32\lib\bpq32.lib + MachineX86 + + + C:\Dev\Msdev2005\Intermed\$(SolutionName)\$(Configuration)\$(ProjectName).bsc + + + "C:\Program Files\7-Zip\7z.exe" a C:\DevProgs\BPQ32\bpq32.zip C:\DevProgs\BPQ32\bpq32.dll && myxcopy /y c:\DevProgs\BPQ32\bpq32.dll c:\windows\SysWOW64\bpq32.dll && del C:\DevProgs\BPQ32\bpq32.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.svn/pristine/04/048651e525d8fb5c544330e2b48b108f54a00e3d.svn-base b/.svn/pristine/04/048651e525d8fb5c544330e2b48b108f54a00e3d.svn-base index 2682195..53dd522 100644 --- a/.svn/pristine/04/048651e525d8fb5c544330e2b48b108f54a00e3d.svn-base +++ b/.svn/pristine/04/048651e525d8fb5c544330e2b48b108f54a00e3d.svn-base @@ -1,71 +1,71 @@ - -//Microsoft Developer Studio generated resource script. -// -//#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "windows.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (U.K.) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) -#ifdef _WIN32 -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK -#pragma code_page(1252) -#endif //_WIN32 - - -#define BPQICON 400 - -BPQICON ICON DISCARDABLE "..\\bpqicon.ico" - -// -// Version -// -#define TEXTVER "2. 0. 1. 1\0" -#define BINVER 2, 0, 1, 1 - -VS_VERSION_INFO VERSIONINFO - FILEVERSION BINVER - PRODUCTVERSION BINVER - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x1L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "080904b0" - BEGIN - VALUE "Comments", "Program to hold BPQ32.dll in memory\0" - VALUE "CompanyName", " \0" - VALUE "FileDescription", "bpq32\0" - VALUE "FileVersion", TEXTVER - VALUE "InternalName", "bpq32\0" - VALUE "LegalCopyright", "Copyright © 2006-2011 G8BPQ\0" - VALUE "OriginalFilename", "bpq32.exe\0" - VALUE "ProductName", " bpq32\0" - VALUE "ProductVersion", TEXTVER - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x809, 1200 - END -END - - -#endif // not APSTUDIO_INVOKED + +//Microsoft Developer Studio generated resource script. +// +//#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "windows.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.K.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +#pragma code_page(1252) +#endif //_WIN32 + + +#define BPQICON 400 + +BPQICON ICON DISCARDABLE "..\\bpqicon.ico" + +// +// Version +// +#define TEXTVER "2. 0. 1. 1\0" +#define BINVER 2, 0, 1, 1 + +VS_VERSION_INFO VERSIONINFO + FILEVERSION BINVER + PRODUCTVERSION BINVER + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "080904b0" + BEGIN + VALUE "Comments", "Program to hold BPQ32.dll in memory\0" + VALUE "CompanyName", " \0" + VALUE "FileDescription", "bpq32\0" + VALUE "FileVersion", TEXTVER + VALUE "InternalName", "bpq32\0" + VALUE "LegalCopyright", "Copyright © 2006-2011 G8BPQ\0" + VALUE "OriginalFilename", "bpq32.exe\0" + VALUE "ProductName", " bpq32\0" + VALUE "ProductVersion", TEXTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x809, 1200 + END +END + + +#endif // not APSTUDIO_INVOKED diff --git a/.svn/pristine/05/05307223de728f7306ffe553ec00689c462798f9.svn-base b/.svn/pristine/05/05307223de728f7306ffe553ec00689c462798f9.svn-base index 55e015c..f4bc715 100644 --- a/.svn/pristine/05/05307223de728f7306ffe553ec00689c462798f9.svn-base +++ b/.svn/pristine/05/05307223de728f7306ffe553ec00689c462798f9.svn-base @@ -1,1676 +1,1676 @@ -/* -Copyright 2001-2018 John Wiseman G8BPQ - -This file is part of LinBPQ/BPQ32. - -LinBPQ/BPQ32 is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -LinBPQ/BPQ32 is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses -*/ - -// Mail and Chat Server for BPQ32 Packet Switch -// -// Support for FLAMP compatible Mulitcast - -#include "bpqmail.h" - -void decodeblock( unsigned char in[4], unsigned char out[3]); // Base64 Decode - -time_t MulticastMaxAge = 48 * 60 * 60; // 48 Hours in secs - -struct MSESSION * MSessions = NULL; - -#ifndef LINBPQ - -#include "AFXRES.h" - -HWND hMCMonitor = NULL; -HWND MCList; - -static HMENU hMCMenu; // handle of menu - -static char MCClassName[]="BPQMCWINDOW"; - -RECT MCMonitorRect; - -static int Height, Width, LastY; - -#define BGCOLOUR RGB(236,233,216) - -void MCMoveWindows() -{ - RECT rcClient; - int ClientWidth, ClientHeight; - - GetClientRect(hMCMonitor, &rcClient); - - if (rcClient.bottom == 0) // Minimised - return; - - ClientHeight = rcClient.bottom; - ClientWidth = rcClient.right; - - MoveWindow(MCList, 0, 0, rcClient.right, rcClient.bottom, TRUE); -} - -void CopyMCToClipboard(HWND hWnd); - -LRESULT CALLBACK MCWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - int wmId, wmEvent; - LPRECT lprc; - struct MSESSION * Sess = MSessions; - struct MSESSION * Temp; - - switch (message) - { - - case WM_ACTIVATE: - - break; - - case WM_CLOSE: - if (wParam) // Used by Close All Programs. - return 0; - - return (DefWindowProc(hWnd, message, wParam, lParam)); - - case WM_COMMAND: - - wmId = LOWORD(wParam); // Remember, these are... - wmEvent = HIWORD(wParam); // ...different for Win32! - - switch (wmId) - { - - case ID_EDIT_COPY: - - CopyMCToClipboard(hMCMonitor); - return 0;; - - case ID_EDIT_CLEAR: - - while (Sess) - { - ListView_DeleteItem(MCList, Sess->Index); - - if (Sess->FileName) - free(Sess->FileName); - - if (Sess->OrigTimeStamp) - free(Sess->OrigTimeStamp); - - if (Sess->Message) - free(Sess->Message); - - if (Sess->BlockList) - free(Sess->BlockList); - - if (Sess->ID) - free(Sess->ID); - - Temp = Sess; - Sess = Sess->Next; - } - - MSessions = NULL; - return 0; - - default: - return 0; - } - - case WM_SYSCOMMAND: - - wmId = LOWORD(wParam); // Remember, these are... - wmEvent = HIWORD(wParam); // ...different for Win32! - - switch (wmId) { - - case SC_MINIMIZE: - - if (cfgMinToTray) - return ShowWindow(hWnd, SW_HIDE); - - default: - - return (DefWindowProc(hWnd, message, wParam, lParam)); - } - - case WM_SIZING: - - lprc = (LPRECT) lParam; - - Height = lprc->bottom-lprc->top; - Width = lprc->right-lprc->left; - - MCMoveWindows(); - - return TRUE; - - - case WM_DESTROY: - - // Remove the subclass from the edit control. - - GetWindowRect(hWnd, &MonitorRect); // For save soutine - - if (cfgMinToTray) - DeleteTrayMenuItem(hWnd); - - - hMCMonitor = NULL; - - break; - - default: - return (DefWindowProc(hWnd, message, wParam, lParam)); - - } - return (0); -} - - -static void MoveMCWindows() -{ - RECT rcMain, rcClient; - int ClientHeight, ClientWidth; - - GetWindowRect(hMCMonitor, &rcMain); - GetClientRect(hMCMonitor, &rcClient); - - ClientHeight = rcClient.bottom; - ClientWidth = rcClient.right; - -// MoveWindow(hwndMon,2, 0, ClientWidth-4, SplitPos, TRUE); -// MoveWindow(hwndOutput,2, 2, ClientWidth-4, ClientHeight-4, TRUE); -// MoveWindow(hwndSplit,0, SplitPos, ClientWidth, SplitBarHeight, TRUE); -} - - - - -HWND CreateMCListView (HWND hwndParent) -{ - INITCOMMONCONTROLSEX icex; // Structure for control initialization. - HWND hList; - LV_COLUMN Column; - LOGFONT lf; - HFONT hFont; - int n = 0; - - memset(&lf, 0, sizeof(LOGFONT)); - - lf.lfHeight = 12; - lf.lfWidth = 8; - lf.lfPitchAndFamily = FIXED_PITCH; - strcpy (lf.lfFaceName, "FIXEDSYS"); - - hFont = CreateFontIndirect(&lf); - - icex.dwICC = ICC_LISTVIEW_CLASSES; - InitCommonControlsEx(&icex); - - // Create the list-view window in report view with label editing enabled. - - hList = CreateWindow(WC_LISTVIEW, - "Messages", - WS_CHILD | LVS_REPORT | LVS_EDITLABELS, - 0, 0, 100, 100, - hwndParent, - (HMENU)NULL, - hInst, - NULL); - - SendMessage(hList, WM_SETFONT,(WPARAM) hFont, 0); - - - ListView_SetExtendedListViewStyle(hList,LVS_EX_FULLROWSELECT); - - Column.cx=45; - Column.mask=LVCF_WIDTH | LVCF_TEXT; - Column.pszText="ID"; - - SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); - Column.cx=95; - Column.mask=LVCF_WIDTH | LVCF_TEXT; - Column.pszText="From"; - - SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); - - Column.cx=140; - Column.mask=LVCF_WIDTH | LVCF_TEXT; - Column.pszText="FileName"; - - SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); - - Column.cx=50; - Column.mask=LVCF_WIDTH | LVCF_TEXT; - Column.pszText="Size"; - - SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); - - Column.cx=40; - Column.mask=LVCF_WIDTH | LVCF_TEXT; - Column.pszText="%"; - SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); - - Column.cx=55; - Column.mask=LVCF_WIDTH | LVCF_TEXT; - Column.pszText="Time"; - SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); - - Column.cx=55; - Column.mask=LVCF_WIDTH | LVCF_TEXT; - Column.pszText="Age"; - SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); - - Column.cx=20; - Column.mask=LVCF_WIDTH | LVCF_TEXT; - Column.pszText="C"; - - SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); - - Column.cx=430; - Column.mask=LVCF_WIDTH | LVCF_TEXT; - Column.pszText="BlockList"; - SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM) &Column); - - ShowWindow(hList, SW_SHOWNORMAL); - UpdateWindow(hList); - - return (hList); -} - -#define OurSetItemText(hwndLV, i, iSubItem_, pszText_) \ -{ LV_ITEM _ms_lvi;\ - _ms_lvi.iSubItem = iSubItem_;\ - _ms_lvi.pszText = pszText_;\ - SNDMSG((hwndLV), LVM_SETITEMTEXT, (WPARAM)i, (LPARAM)(LV_ITEM FAR *)&_ms_lvi);\ -} - -void RefreshMCLine(struct MSESSION * MSession) -{ - LV_ITEM Item; - LVFINDINFO Finfo; - int ret, n, pcent; - char Time[80]; - char Agestring[80]; - char Size[16] = "??"; - char Percent[16] = "??"; - char Key[16]; - - char BlockList[101] = ""; - struct tm * TM; - time_t Age; - - if (MCList == 0) - return; - - sprintf(Key, "%04X", MSession->Key); - - Age = time(NULL) - MSession->LastUpdated; - -// if (LocalTime) -// TM = localtime(&MSession->LastUpdated); -// else - TM = gmtime(&Age); - - sprintf(Agestring, "%.2d:%.2d", - TM->tm_hour, TM->tm_min); - - TM = gmtime(&MSession->Created); - - sprintf(Time, "%.2d:%.2d", - TM->tm_hour, TM->tm_min); - - - Finfo.flags = LVFI_STRING; - Finfo.psz = Key; - Finfo.vkDirection = VK_DOWN; - ret = SendMessage(MCList, LVM_FINDITEM, (WPARAM)-1, (LPARAM) &Finfo); - - if (ret == -1) - { - n = ListView_GetItemCount(MCList); - MSession->Index = n; - } - else - MSession->Index = ret; - - Item.mask=LVIF_TEXT; - Item.iItem = MSession->Index; - Item.iSubItem = 0; - Item.pszText = Key; - - ret = SendMessage(MCList, LVM_SETITEMTEXT, (WPARAM)MSession->Index, (LPARAM) &Item); - - if (ret == 0) - MSession->Index = ListView_InsertItem(MCList, &Item); - - sprintf(Size, "%d", MSession->MessageLen); - - if (MSession->MessageLen) - { - int i; - - pcent = (MSession->BlocksReceived * 100) / MSession->BlockCount; - sprintf(Percent, "%d", pcent); - - // Flag received blocks. Normalise to 50 wide - - memset(BlockList, '.', 50); - - for (i = 0; i < 50; i++) - { - int posn = (i * MSession->BlockCount) / 50; - if (MSession->BlockList[posn] == 1) - BlockList[i] = 'Y'; - } - } - - n = 0; - - OurSetItemText(MCList, MSession->Index, n++, Key); - if (MSession->ID) - OurSetItemText(MCList, MSession->Index, n++, MSession->ID) - else - OurSetItemText(MCList, MSession->Index, n++, " "); - - OurSetItemText(MCList, MSession->Index, n++, MSession->FileName); - OurSetItemText(MCList, MSession->Index, n++, Size); - OurSetItemText(MCList, MSession->Index, n++, Percent); - OurSetItemText(MCList, MSession->Index, n++, Time); - OurSetItemText(MCList, MSession->Index, n++, Agestring); - - if (MSession->Completed) - OurSetItemText(MCList, MSession->Index, n++, "Y") - else - OurSetItemText(MCList, MSession->Index, n++, " "); - - OurSetItemText(MCList, MSession->Index, n++, BlockList); -} - - -BOOL CreateMulticastConsole() -{ - WNDCLASS wc; - HBRUSH bgBrush; - RECT rcClient; - - if (hMCMonitor) - { - ShowWindow(hMCMonitor, SW_SHOWNORMAL); - SetForegroundWindow(hMCMonitor); - return FALSE; // Already open - } - - bgBrush = CreateSolidBrush(BGCOLOUR); - - wc.style = CS_HREDRAW | CS_VREDRAW; - wc.lpfnWndProc = MCWndProc; - - wc.cbClsExtra = 0; - wc.cbWndExtra = DLGWINDOWEXTRA; - wc.hInstance = hInst; - wc.hIcon = LoadIcon( hInst, MAKEINTRESOURCE(BPQICON) ); - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.hbrBackground = bgBrush; - - wc.lpszMenuName = NULL; - wc.lpszClassName = MCClassName; - - RegisterClass(&wc); - - hMCMonitor=CreateDialog(hInst, MCClassName, 0, NULL); - - if (!hMCMonitor) - return (FALSE); - - hMCMenu=GetMenu(hMCMonitor); - -// CheckMenuItem(hMenu,MONBBS, MonBBS ? MF_CHECKED : MF_UNCHECKED); -// CheckMenuItem(hMenu,MONCHAT, MonCHAT ? MF_CHECKED : MF_UNCHECKED); -// CheckMenuItem(hMenu,MONTCP, MonTCP ? MF_CHECKED : MF_UNCHECKED); - - DrawMenuBar(hMCMonitor); - - // Create List View - - GetClientRect (hMCMonitor, &rcClient); - - MCList = CreateMCListView(hMCMonitor); - - MoveWindow(MCList, 0, 0, rcClient.right, rcClient.bottom, TRUE); - - if (cfgMinToTray) - AddTrayMenuItem(hMCMonitor, "Mail Multicast Monitor"); - - ShowWindow(hMCMonitor, SW_SHOWNORMAL); - - if (MCMonitorRect.right < 100 || MCMonitorRect.bottom < 100) - { - GetWindowRect(hMCMonitor, &MCMonitorRect); - } - - MoveWindow(hMCMonitor, MCMonitorRect.left, MCMonitorRect.top, - MCMonitorRect.right-MCMonitorRect.left, MCMonitorRect.bottom-MCMonitorRect.top, TRUE); - - MoveMCWindows(); - - return TRUE; - -} -void CopyMCToClipboard(HWND hWnd) -{ - int i,n, len=0; - char * Buffer; - HGLOBAL hMem; - char * ptr; - - char Time[80]; - char Agestring[80]; - char From[16]; - char Size[16]; - char Percent[16]; - char FileName[128]; - char Key[16]; - char Complete[2]; - - char BlockList[128]; - - n = ListView_GetItemCount(MCList); - - Buffer = malloc((n + 1) * 200); - - len = sprintf(Buffer, "ID From FileName Size %% Time Age Blocklist\r\n"); - - for (i=0; i> 1) ^ 0xA001; - else - crcval = (crcval >> 1); - } -} - -static unsigned int CalcCRC(UCHAR * ptr, int Len) -{ - int i; - - crcval = 0xFFFF; - for (i = 0; i < Len; i++) - { - update(*ptr++); - } - return crcval; -} - -struct MSESSION * FindMSession(unsigned int Key) -{ - struct MSESSION * Sess = MSessions; - struct MSESSION * LastSess = NULL; - - while (Sess) - { - if (Sess->Key == Key) - return Sess; - - LastSess = Sess; - Sess = Sess->Next; - } - - // Not found - - Sess = zalloc(sizeof(struct MSESSION)); - - if (Sess == NULL) - return NULL; - - Sess->Key = Key; - - Sess->Created = time(NULL); - - if (LastSess) - LastSess->Next = Sess; - else - MSessions = Sess; - - return Sess; -} - -#include "LzmaLib.h" - -#define LZMA_STR "\1LZMA" - -UCHAR * LZUncompress(UCHAR * Decoded, size_t Len, size_t * NewLen) -{ - unsigned char * buf; - unsigned char inprops[LZMA_PROPS_SIZE]; - size_t inlen; - int r; - - size_t rlen = 0; - size_t outlen; - - memcpy(&rlen, &Decoded[5], 4); - - outlen = ntohl(rlen); - *NewLen = outlen; - - buf = malloc(outlen); - - if (outlen > 1 << 25) - { - Debugprintf("Refusing to decompress data (> 32 MiB)"); - return NULL; - } - - - memcpy(inprops, Decoded + strlen(LZMA_STR) + sizeof(int), LZMA_PROPS_SIZE); - - inlen = Len - strlen(LZMA_STR) - sizeof(int) - LZMA_PROPS_SIZE; - - if ((r = LzmaUncompress(buf, &outlen, (const unsigned char*)Decoded + Len - inlen, &inlen, - inprops, LZMA_PROPS_SIZE)) != SZ_OK) - { - Debugprintf("Lzma Uncompress failed: %s", LZMA_ERRORS[r]); - return NULL; - } - else - { - return buf; - } -} - -void decodeblock128(unsigned char in[8], unsigned char out[7] ) -{ - out[0] = (unsigned char) (in[0] << 1 | in[1] >> 6); - out[1] = (unsigned char) (in[1] << 2 | in[2] >> 5); - out[2] = (unsigned char) (in[2] << 3 | in[3] >> 4); - out[3] = (unsigned char) (in[3] << 4 | in[4] >> 3); - out[4] = (unsigned char) in[4] << 5 | in[5] >> 2; - out[5] = (unsigned char) in[5] << 6 | in[6] >> 1; - out[6] = (unsigned char) in[6] << 7 | in[7]; -} - - -void SaveMulticastMessage(struct MSESSION * MSession) -{ - UCHAR * Decoded = NULL; // Output from Basexxx decode - UCHAR * Uncompressed = NULL; - size_t DecodedLen; // Length of decoded message - size_t UncompressedLen; // Length of decompressed message - int ExpectedLen; // From front of Base128 or Base256 message - int HddrLen; // Length of Expected Len Header - - if (MSession->FileName == NULL) - return; // Need Name - - MSession->Completed = TRUE; // So we don't get it again - - // If compresses and encoded, decode and decompress - - if (memcmp(MSession->Message, "[b64:start]", 11) == 0) - { - UCHAR * ptr1 = &MSession->Message[11]; - UCHAR * ptr2 = malloc(MSession->MessageLen); // Must get smaller - - int Len = MSession->MessageLen - 21; // Header and Trailer - - Decoded = ptr2; - - // Decode Base64 encoding - - while (Len > 0) - { - decodeblock(ptr1, ptr2); - ptr1 += 4; - ptr2 += 3; - Len -= 4; - } - - DecodedLen = (int)(ptr2 - Decoded); - Uncompressed = LZUncompress(Decoded, DecodedLen, &UncompressedLen); - } - else if (memcmp(MSession->Message, "[b128:start]", 12) == 0) - { - UCHAR * ptr1 = &MSession->Message[12]; - UCHAR * ptr2 = malloc(MSession->MessageLen); // Must get smaller - UCHAR ch; - UCHAR * Intermed; - - int Len = MSession->MessageLen - 23; // Header and Trailer - - Intermed = ptr2; - - // Decode Base128 encoding - - // First remove transparency (as in base256) - - - // Extract decoded msg len - - ExpectedLen = atoi(ptr1); - - ptr1 = strchr(ptr1, 10); - ptr1++; - - HddrLen = (int)(ptr1 - &MSession->Message[12]); - - if (ExpectedLen == 0 || ExpectedLen > Len || ptr1 == (UCHAR *)1) - { - Debugprintf("MCAST Missing Length Field"); - return; - } - - Len -= HddrLen;; - - while (Len > 0) - { - ch = *(ptr1++); - Len --; - - if (ch == ':') - { - ch = *(ptr1++); - Len--; - - switch (ch) - { - case ':' : *(ptr2++) = ':'; break; - case '0' : *(ptr2++) = 0x00; break; - case '1' : *(ptr2++) = 0x01; break; - case '2' : *(ptr2++) = 0x02; break; - case '3' : *(ptr2++) = 0x03; break; - case '4' : *(ptr2++) = 0x04; break; - case '5' : *(ptr2++) = 0x05; break; - case '6' : *(ptr2++) = 0x06; break; - case '7' : *(ptr2++) = 0x07; break; - case '8' : *(ptr2++) = 0x08; break; - case '9' : *(ptr2++) = 0x09; break; - case 'A' : *(ptr2++) = '\n'; break; - case 'B' : *(ptr2++) = '\r'; break; - case 'C' : *(ptr2++) = '^'; break; - case 'D' : *(ptr2++) = 0x7F; break; - case 'E' : *(ptr2++) = 0xFF; break; - } - } - else - *(ptr2++) = ch; - } - - - Len = ptr2 - Intermed; - - ptr1 = Intermed; - ptr2 = malloc(MSession->MessageLen); // Must get smaller - Decoded = ptr2; - - while (Len > 0) - { - decodeblock128(ptr1, ptr2); - ptr1 += 8; - ptr2 += 7; - Len -= 8; - } - - DecodedLen = ptr2 - Decoded; - Uncompressed = LZUncompress(Decoded, DecodedLen, &UncompressedLen); - } - else if (memcmp(MSession->Message, "[b256:start]", 12) == 0) - { - UCHAR * ptr1 = &MSession->Message[12]; - UCHAR * ptr2 = malloc(MSession->MessageLen); // Must get smaller - UCHAR ch; - - int Len = MSession->MessageLen - 23; // Header and Trailer - - Decoded = ptr2; - - // Decode Base256 encoding - - // Extract decoded msg len - - ExpectedLen = atoi(ptr1); - - ptr1 = strchr(ptr1, 10); - ptr1++; - - HddrLen = ptr1 - &MSession->Message[12]; - - if (ExpectedLen == 0 || ExpectedLen > Len || ptr1 == (UCHAR *)1) - { - Debugprintf("MCAST Missing Length Field"); - return; - } - - Len -= HddrLen;; - - while (Len > 0) - { - ch = *(ptr1++); - Len --; - - if (ch == ':') - { - ch = *(ptr1++); - Len--; - - switch (ch) - { - case ':' : *(ptr2++) = ':'; break; - case '0' : *(ptr2++) = 0x00; break; - case '1' : *(ptr2++) = 0x01; break; - case '2' : *(ptr2++) = 0x02; break; - case '3' : *(ptr2++) = 0x03; break; - case '4' : *(ptr2++) = 0x04; break; - case '5' : *(ptr2++) = 0x05; break; - case '6' : *(ptr2++) = 0x06; break; - case '7' : *(ptr2++) = 0x07; break; - case '8' : *(ptr2++) = 0x08; break; - case '9' : *(ptr2++) = 0x09; break; - case 'A' : *(ptr2++) = '\n'; break; - case 'B' : *(ptr2++) = '\r'; break; - case 'C' : *(ptr2++) = '^'; break; - case 'D' : *(ptr2++) = 0x7F; break; - case 'E' : *(ptr2++) = 0xFF; break; - } - } - else - *(ptr2++) = ch; - } - - DecodedLen = ptr2 - Decoded; - Uncompressed = LZUncompress(Decoded, DecodedLen, &UncompressedLen); - } - else - { - // Plain Text - - UncompressedLen = MSession->MessageLen; - Uncompressed = MSession->Message; - - MSession->Message = NULL; // So we dont try to free again - } - - if (Decoded) - free(Decoded); - - if (Uncompressed) - { - // Write it away and free it - - char MsgFile[MAX_PATH]; - FILE * hFile; - int WriteLen=0; - UCHAR * ptr1 = Uncompressed; - - // Make Sure MCAST directory exists - - sprintf_s(MsgFile, sizeof(MsgFile), "%s/MCAST", MailDir); - -#ifdef WIN32 - CreateDirectory(MsgFile, NULL); // Just in case -#else - mkdir(MsgFile, S_IRWXU | S_IRWXG | S_IRWXO); - chmod(MsgFile, S_IRWXU | S_IRWXG | S_IRWXO); -#endif - - sprintf_s(MsgFile, sizeof(MsgFile), "%s/MCAST/%s", MailDir, MSession->FileName); - - hFile = fopen(MsgFile, "wb"); - - if (hFile) - { - WriteLen = (int)fwrite(Uncompressed, 1, UncompressedLen, hFile); - fclose(hFile); - } - - - // if it looks like an export file (Starts SP SB or ST) and ends /ex - // import and delete it. - - if (*(ptr1) == 'S' && ptr1[2] == ' ') - if (_memicmp(&ptr1[UncompressedLen - 5], "/EX", 3) == 0) - ImportMessages(NULL, MsgFile, TRUE); - - free (Uncompressed); - } -} - -VOID ProcessMCASTLine(ConnectionInfo * conn, struct UserInfo * user, char * Buffer, int MsgLen) -{ - char Opcode[80]; - unsigned int checksum, len = 0; - char * data; - int headerlen = 0; - unsigned int crcval; - int n; - unsigned int Key; - struct MSESSION * MSession; - - if (MsgLen == 1 && Buffer[0] == 13) - return; - - MsgLen --; // Remove the CR we added - - Buffer[MsgLen] = 0; - - if (MsgLen == 1 && Buffer[0] == 13) - return; - -// return; - - n = sscanf(&Buffer[1], "%s %04d %04X", Opcode, &len, &checksum); - - if (n != 3) - return; - - data = strchr(Buffer, '>'); - - if (data) - headerlen = (int)(++data - Buffer); - - if (headerlen + len != MsgLen) - return; - - crcval = CalcCRC(data, len); - - if (checksum != crcval) - return; - - // Extract Session Key - - sscanf(&data[1], "%04X", &Key); - - MSession = FindMSession(Key); - - if (MSession == 0) - return; // ?? couldn't allocate - - MSession->LastUpdated = time(NULL); - - if (MSession->Completed) - return; // We already have it all - - if (strcmp(Opcode, "ID") == 0) - { - strlop(&data[6], ' '); - MSession->ID = _strdup(&data[6]); - - return; - } - - if (strcmp(Opcode, "PROG") == 0) - { - // Ignore for now - return; - } - - if (strcmp(Opcode, "FILE") == 0) - { - // {80BC}20141108142542:debug_log.txt - - char * FN = strchr(&data[6], ':'); - - if (FN) - { - *(FN++) = 0; - - MSession->FileName = _strdup(FN); - MSession->OrigTimeStamp = _strdup(&data[6]); - } - - // We could get whole message without getting the Name, - // so check - - if (MSession->BlockCount && MSession->BlocksReceived == MSession->BlockCount) - { - // We have the whole message. Decode and Save - - if (MSession->MessageLen) // Also need length - SaveMulticastMessage(MSession); - } - - RefreshMCLine(MSession); - return; - } - - if (strcmp(Opcode, "SIZE") == 0) - { - // SIZE 14 2995>{80BC}465 8 64 - - int a, b, c, n = sscanf(&data[6], "%d %d %d", &a, &b, &c); - - if (n == 3) - { - // We may already have some (or even all) the message if we - // missed the SIZE block first time round - - if (MSession->Message) - { - // Already have at least part of it - - if (MSession->BlockSize != c) - { - // We based blocksize on last packet, so need to sort out mess - - // Find where we put the block, and move it - - UCHAR * OldLoc; - - MSession->Message = realloc(MSession->Message, a); - MSession->BlockList = realloc(MSession->BlockList, b); - - OldLoc = &MSession->Message[(MSession->BlockCount - 1) * MSession->BlockSize]; - - memmove(&MSession->Message[(MSession->BlockCount - 1) * c], OldLoc, MSession->BlockSize); - - MSession->BlockSize = c; - } - - if (MSession->BlockCount < b) - { - // Dont have it all, so need to extend ; - - MSession->Message = realloc(MSession->Message, a); - MSession->BlockList = realloc(MSession->BlockList, b); - } - } - - MSession->MessageLen = a; - MSession->BlockCount = b; - MSession->BlockSize = c; - - if (MSession->Message == NULL) - { - MSession->Message = zalloc(b * c); - MSession->BlockList = zalloc(b); - } - - // We might have it all now - - if (MSession->BlocksReceived == MSession->BlockCount) - { - // We have the whole message. Decode and Save - - SaveMulticastMessage(MSession); - } - } - - RefreshMCLine(MSession); - return; - } - - if (strcmp(Opcode, "DATA") == 0) - { - // {80BC:1}[b256:start]401 - - int Blockno = atoi(&data[6]); - char * dataptr = strchr(&data[6], '}'); - - if (dataptr == 0) - return; - - dataptr++; - - // What should we do if we don't have Filename or Size?? - - // If we assume this isn't the last block, then we can get - // the block size from this message. This is pretty save, but - // I guess as we will only get one last block, if we subsequently - // get an earlier one that is bigger, we can recalculate the position - // of this block and move it. - - if (MSession->MessageLen == 0) - { - // Haven't received SIZE Message yet. Guess the blocksize - - int blocksize = MsgLen - (int)(dataptr - Buffer); - - if (MSession->BlockSize == 0) - { - MSession->BlockSize = blocksize; - } - else - { - if (MSession->BlockSize < blocksize) - { - // We based blocksize on last packet, so need to sort out mess - - // Find where we put the block, and move it - - UCHAR * OldLoc = &MSession->Message[(MSession->BlockCount - 1) * MSession->BlockSize]; - memmove(&MSession->Message[(MSession->BlockCount - 1) * blocksize], OldLoc, MSession->BlockSize); - - MSession->BlockSize = blocksize; - } - } - - // We need to realloc Message and Blocklist if this is a later block - - if (MSession->BlockCount < Blockno) - { - MSession->Message = realloc(MSession->Message, Blockno * MSession->BlockSize); - MSession->BlockList = realloc(MSession->BlockList, Blockno); - - memset(&MSession->BlockList[MSession->BlockCount], 0, Blockno - MSession->BlockCount); - MSession->BlockCount = Blockno; - - } - - } - if (Blockno == 0 || Blockno > MSession->BlockCount) - return; - - Blockno--; - - if (MSession->BlockList[Blockno] == 1) - { - // Already have this block - - return; - } - - - memcpy(&MSession->Message[Blockno * MSession->BlockSize], dataptr, MSession->BlockSize); - MSession->BlockList[Blockno] = 1; - - MSession->BlocksReceived++; - - if (MSession->BlocksReceived == MSession->BlockCount && MSession->MessageLen) - { - // We have the whole message. Decode and Save - - SaveMulticastMessage(MSession); - } - - RefreshMCLine(MSession); - - return; - } - - MsgLen++; - -/* - -QST DE GM8BPQ - -{80BC}FLAMP 2.2.03 -{80BC}20141108142542:debug_log.txt -{80BC}GM8BPQ Skigersta -{80BC}465 8 64 -{80BC:1}[b256:start]401 -:1LZMA:0:0:6-]:0:0:0:4:0$--:7m?8-v\-E-Y-[--- -{80BC:2}rS-)N{j--o--ZMPX-,-l-yD------E--;-o:6-|;--f---q----0<---%-- -{80BC:3}*N-?N--*:Cf{:9--z-J:9-HMd:8-------Q--D---_-a----:C$;A-j---(: -{80BC:4}DWb--K---Qq-uj-_--;i------T-\>-{:6---~-ij~-,-(-O--2--+ --:8 -{80BC:5}p---:7Gf:E-5o->x---4--K--:3-\:E---gouuH-3'----:A!.:7 ---N:0S -{80BC:6}.---/-~#.-:D:7zg~--m--:8-'---Y%-?--ze\-ho:5-}-:C:A:1u-1-O- -- -{80BC:7}9p-42-w--G:2G:3--g--O---n----c-#----DF-!~--:D-A--|-e------- -{80BC:8}B{--:0 -[b256:end] -{80BC:EOF} -{80BC:EOT} - -DE GM8BPQ K - -*/ - - return; -/* - if (strcmp(Buffer, "ARQ::ETX\r") == 0) - { - // Decode it. - - UCHAR * ptr1, * ptr2, * ptr3; - int len, linelen; - struct MsgInfo * Msg = conn->TempMsg; - time_t Date; - char FullTo[100]; - char FullFrom[100]; - char ** RecpTo = NULL; // May be several Recipients - char ** HddrTo = NULL; // May be several Recipients - char ** Via = NULL; // May be several Recipients - int LocalMsg[1000] ; // Set if Recipient is a local wl2k address - - int B2To; // Offset to To: fields in B2 header - int Recipients = 0; - int RMSMsgs = 0, BBSMsgs = 0; - -// Msg->B2Flags |= B2Msg; - - - ptr1 = conn->MailBuffer; - len = Msg->length; - ptr1[len] = 0; - - if (strstr(ptr1, "ARQ:ENCODING::")) - { - // a file, not a message. If is called "BBSPOLL" do a reverse forward else Ignore for now - - _strupr(conn->MailBuffer); - if (strstr(conn->MailBuffer, "BBSPOLL")) - { - SendARQMail(conn); - } - - free(conn->MailBuffer); - conn->MailBuffer = NULL; - conn->MailBufferSize = 0; - - return; - } - Loop: - ptr2 = strchr(ptr1, '\r'); - - linelen = ptr2 - ptr1; - - if (_memicmp(ptr1, "From:", 5) == 0 && linelen > 6) // Can have empty From: - { - char SaveFrom[100]; - char * FromHA; - - memcpy(FullFrom, ptr1, linelen); - FullFrom[linelen] = 0; - - // B2 From may now contain an @BBS - - strcpy(SaveFrom, FullFrom); - - FromHA = strlop(SaveFrom, '@'); - - if (strlen(SaveFrom) > 12) SaveFrom[12] = 0; - - strcpy(Msg->from, &SaveFrom[6]); - - if (FromHA) - { - if (strlen(FromHA) > 39) FromHA[39] = 0; - Msg->emailfrom[0] = '@'; - strcpy(&Msg->emailfrom[1], _strupr(FromHA)); - } - - // Remove any SSID - - ptr3 = strchr(Msg->from, '-'); - if (ptr3) *ptr3 = 0; - - } - else if (_memicmp(ptr1, "To:", 3) == 0 || _memicmp(ptr1, "cc:", 3) == 0) - { - HddrTo=realloc(HddrTo, (Recipients+1) * sizeof(void *)); - HddrTo[Recipients] = zalloc(100); - - memset(FullTo, 0, 99); - memcpy(FullTo, &ptr1[4], linelen-4); - memcpy(HddrTo[Recipients], ptr1, linelen+2); - LocalMsg[Recipients] = FALSE; - - _strupr(FullTo); - - B2To = ptr1 - conn->MailBuffer; - - if (_memicmp(FullTo, "RMS:", 4) == 0) - { - // remove RMS and add @winlink.org - - strcpy(FullTo, "RMS"); - strcpy(Msg->via, &FullTo[4]); - } - else - { - ptr3 = strchr(FullTo, '@'); - - if (ptr3) - { - *ptr3++ = 0; - strcpy(Msg->via, ptr3); - } - else - Msg->via[0] = 0; - } - - if (_memicmp(&ptr1[4], "SMTP:", 5) == 0) - { - // Airmail Sends MARS messages as SMTP - - if (CheckifPacket(Msg->via)) - { - // Packet Message - - memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); - _strupr(FullTo); - _strupr(Msg->via); - - // Update the saved to: line (remove the smtp:) - - strcpy(&HddrTo[Recipients][4], &HddrTo[Recipients][9]); - BBSMsgs++; - goto BBSMsg; - } - - // If a winlink.org address we need to convert to call - - if (_stricmp(Msg->via, "winlink.org") == 0) - { - memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); - _strupr(FullTo); - LocalMsg[Recipients] = CheckifLocalRMSUser(FullTo); - } - else - { - memcpy(Msg->via, &ptr1[9], linelen); - Msg->via[linelen - 9] = 0; - strcpy(FullTo,"RMS"); - } -// FullTo[0] = 0; - - BBSMsg: - _strupr(FullTo); - _strupr(Msg->via); - } - - if (memcmp(FullTo, "RMS:", 4) == 0) - { - // remove RMS and add @winlink.org - - memmove(FullTo, &FullTo[4], strlen(FullTo) - 3); - strcpy(Msg->via, "winlink.org"); - sprintf(HddrTo[Recipients], "To: %s\r\n", FullTo); - } - - if (strcmp(Msg->via, "RMS") == 0) - { - // replace RMS with @winlink.org - - strcpy(Msg->via, "winlink.org"); - sprintf(HddrTo[Recipients], "To: %s@winlink.org\r\n", FullTo); - } - - if (strlen(FullTo) > 6) - FullTo[6] = 0; - - strlop(FullTo, '-'); - - strcpy(Msg->to, FullTo); - - if (SendBBStoSYSOPCall) - if (_stricmp(FullTo, BBSName) == 0) - strcpy(Msg->to, SYSOPCall); - - if ((Msg->via[0] == 0 || strcmp(Msg->via, "BPQ") == 0 || strcmp(Msg->via, "BBS") == 0)) - { - // No routing - check @BBS and WP - - struct UserInfo * ToUser = LookupCall(FullTo); - - Msg->via[0] = 0; // In case BPQ and not found - - if (ToUser) - { - // Local User. If Home BBS is specified, use it - - if (ToUser->HomeBBS[0]) - { - strcpy(Msg->via, ToUser->HomeBBS); - } - } - else - { - WPRecP WP = LookupWP(FullTo); - - if (WP) - { - strcpy(Msg->via, WP->first_homebbs); - - } - } - - // Fix To: address in B2 Header - - if (Msg->via[0]) - sprintf(HddrTo[Recipients], "To: %s@%s\r\n", FullTo, Msg->via); - else - sprintf(HddrTo[Recipients], "To: %s\r\n", FullTo); - - } - - RecpTo=realloc(RecpTo, (Recipients+1) * sizeof(void *)); - RecpTo[Recipients] = zalloc(10); - - Via=realloc(Via, (Recipients+1) * sizeof(void *)); - Via[Recipients] = zalloc(50); - - strcpy(Via[Recipients], Msg->via); - strcpy(RecpTo[Recipients++], FullTo); - - // Remove the To: Line from the buffer - - } - else if (_memicmp(ptr1, "Type:", 4) == 0) - { - if (ptr1[6] == 'N') - Msg->type = 'T'; // NTS - else - Msg->type = ptr1[6]; - } - else if (_memicmp(ptr1, "Subject:", 8) == 0) - { - int Subjlen = ptr2 - &ptr1[9]; - if (Subjlen > 60) Subjlen = 60; - memcpy(Msg->title, &ptr1[9], Subjlen); - - goto ProcessBody; - } -// else if (_memicmp(ptr1, "Body:", 4) == 0) -// { -// MsgLen = atoi(&ptr1[5]); -// StartofMsg = ptr1; -// } - else if (_memicmp(ptr1, "File:", 5) == 0) - { - Msg->B2Flags |= Attachments; - } - else if (_memicmp(ptr1, "Date:", 5) == 0) - { - struct tm rtime; - char seps[] = " ,\t\r"; - - memset(&rtime, 0, sizeof(struct tm)); - - // Date: 2009/07/25 10:08 - - sscanf(&ptr1[5], "%04d/%02d/%02d %02d:%02d:%02d", - &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); - - sscanf(&ptr1[5], "%02d/%02d/%04d %02d:%02d:%02d", - &rtime.tm_mday, &rtime.tm_mon, &rtime.tm_year, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); - - rtime.tm_year -= 1900; - - Date = mktime(&rtime) - (time_t)_MYTIMEZONE; - - if (Date == (time_t)-1) - Date = time(NULL); - - Msg->datecreated = Date; - - } - - if (linelen) // Not Null line - { - ptr1 = ptr2 + 2; // Skip cr - goto Loop; - } - - - // Processed all headers -ProcessBody: - - ptr2 +=2; // skip crlf - - Msg->length = &conn->MailBuffer[Msg->length] - ptr2; - - memmove(conn->MailBuffer, ptr2, Msg->length); - - CreateMessageFromBuffer(conn); - - conn->BBSFlags = 0; // Clear ARQ Mode - return; - } - - // File away the data - - Buffer[MsgLen++] = 0x0a; // BBS Msgs stored with crlf - - if ((conn->TempMsg->length + MsgLen) > conn->MailBufferSize) - { - conn->MailBufferSize += 10000; - conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); - - if (conn->MailBuffer == NULL) - { - BBSputs(conn, "*** Failed to extend Message Buffer\r"); - conn->CloseAfterFlush = 20; // 2 Secs - - return; - } - } - - memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, MsgLen); - - conn->TempMsg->length += MsgLen; - - return; - - // Not sure what to do yet with files, but will process emails (using text style forwarding -*/ -/* -ARQ:FILE::flarqmail-1.eml -ARQ:EMAIL:: -ARQ:SIZE::96 -ARQ::STX -//FLARQ COMPOSER -Date: 16/01/2014 22:26:06 -To: g8bpq -From: -Subject: test message - -Hello -Hello - -ARQ::ETX -*/ - - return; -} - -VOID MCastConTimer(ConnectionInfo * conn) -{ - conn->MCastListenTime--; - - if (conn->MCastListenTime == 0) - Disconnect(conn->BPQStream); -} - -VOID MCastTimer() -{ - struct MSESSION * Sess = MSessions; - struct MSESSION * Prev = NULL; - - time_t Now = time(NULL); - - while (Sess) - { - if (Sess->Completed == FALSE) - RefreshMCLine(Sess); - - if (Now - Sess->LastUpdated > MulticastMaxAge) - { - // remove from list - -#ifndef LINBPQ - ListView_DeleteItem(MCList, Sess->Index); -#endif - if (Prev) - Prev->Next = Sess->Next; - else - MSessions = Sess->Next; - - if (Sess->FileName) - free(Sess->FileName); - - if (Sess->OrigTimeStamp) - free(Sess->OrigTimeStamp); - - if (Sess->Message) - free(Sess->Message); - - if (Sess->BlockList) - free(Sess->BlockList); - - if (Sess->ID) - free(Sess->ID); - - free(Sess); - - return; // Saves messing with chain - - } - Prev = Sess; - Sess = Sess->Next; - } -} - -int MulticastStatusHTML(char * Reply) -{ - char StatusPage [] = - "
ID    From      FileName        Size  %%  Time   Age   Blocklist"
-		"                                                   "
-		"\r\n

"; - int Len = 0; - char Unknown[] = "???"; - - struct MSESSION * Sess = MSessions; - - if (Sess ==NULL) - return 0; - - Len = sprintf(Reply, "%s", StatusPage); - - while (Sess) - { - char Percent[16] = "???"; - char BlockList[51] = ""; - int i; - char Time[80]; - char Agestring[80]; - struct tm * TM; - time_t Age; - char * ID = Unknown; - char * FileName = Unknown; - - Age = time(NULL) - Sess->LastUpdated; - - TM = gmtime(&Age); - - sprintf(Agestring, "%.2d:%.2d", TM->tm_hour, TM->tm_min); - - TM = gmtime(&Sess->Created); - - sprintf(Time, "%.2d:%.2d", TM->tm_hour, TM->tm_min); - - if (Sess->MessageLen && Sess->BlockCount) - { - int pcent; - - pcent = (Sess->BlocksReceived * 100) / Sess->BlockCount; - sprintf(Percent, "%d", pcent); - } - - // Flag received blocks. Normalise to 50 wide - - memset(BlockList, '.', 50); - - if (Sess->BlockList) - { - for (i = 0; i < 50; i++) - { - int posn = (i * Sess->BlockCount) / 50; - if (Sess->BlockList[posn] == 1) - BlockList[i] = 'Y'; - } - } - if (Sess->FileName) - FileName = Sess->FileName; - - if (Sess->ID) - ID = Sess->ID; - - Len += sprintf(&Reply[Len], "%04X %-10s%-15s%5d %-3s %s %s %s\r\n", - Sess->Key, ID, FileName, - Sess->MessageLen, Percent, Time, Agestring, BlockList); - - Sess = Sess->Next; - } - - Len += sprintf(&Reply[Len], "%s", StatusTail); - - return Len; -} +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// Mail and Chat Server for BPQ32 Packet Switch +// +// Support for FLAMP compatible Mulitcast + +#include "bpqmail.h" + +void decodeblock( unsigned char in[4], unsigned char out[3]); // Base64 Decode + +time_t MulticastMaxAge = 48 * 60 * 60; // 48 Hours in secs + +struct MSESSION * MSessions = NULL; + +#ifndef LINBPQ + +#include "AFXRES.h" + +HWND hMCMonitor = NULL; +HWND MCList; + +static HMENU hMCMenu; // handle of menu + +static char MCClassName[]="BPQMCWINDOW"; + +RECT MCMonitorRect; + +static int Height, Width, LastY; + +#define BGCOLOUR RGB(236,233,216) + +void MCMoveWindows() +{ + RECT rcClient; + int ClientWidth, ClientHeight; + + GetClientRect(hMCMonitor, &rcClient); + + if (rcClient.bottom == 0) // Minimised + return; + + ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + + MoveWindow(MCList, 0, 0, rcClient.right, rcClient.bottom, TRUE); +} + +void CopyMCToClipboard(HWND hWnd); + +LRESULT CALLBACK MCWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + LPRECT lprc; + struct MSESSION * Sess = MSessions; + struct MSESSION * Temp; + + switch (message) + { + + case WM_ACTIVATE: + + break; + + case WM_CLOSE: + if (wParam) // Used by Close All Programs. + return 0; + + return (DefWindowProc(hWnd, message, wParam, lParam)); + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + + case ID_EDIT_COPY: + + CopyMCToClipboard(hMCMonitor); + return 0;; + + case ID_EDIT_CLEAR: + + while (Sess) + { + ListView_DeleteItem(MCList, Sess->Index); + + if (Sess->FileName) + free(Sess->FileName); + + if (Sess->OrigTimeStamp) + free(Sess->OrigTimeStamp); + + if (Sess->Message) + free(Sess->Message); + + if (Sess->BlockList) + free(Sess->BlockList); + + if (Sess->ID) + free(Sess->ID); + + Temp = Sess; + Sess = Sess->Next; + } + + MSessions = NULL; + return 0; + + default: + return 0; + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) { + + case SC_MINIMIZE: + + if (cfgMinToTray) + return ShowWindow(hWnd, SW_HIDE); + + default: + + return (DefWindowProc(hWnd, message, wParam, lParam)); + } + + case WM_SIZING: + + lprc = (LPRECT) lParam; + + Height = lprc->bottom-lprc->top; + Width = lprc->right-lprc->left; + + MCMoveWindows(); + + return TRUE; + + + case WM_DESTROY: + + // Remove the subclass from the edit control. + + GetWindowRect(hWnd, &MonitorRect); // For save soutine + + if (cfgMinToTray) + DeleteTrayMenuItem(hWnd); + + + hMCMonitor = NULL; + + break; + + default: + return (DefWindowProc(hWnd, message, wParam, lParam)); + + } + return (0); +} + + +static void MoveMCWindows() +{ + RECT rcMain, rcClient; + int ClientHeight, ClientWidth; + + GetWindowRect(hMCMonitor, &rcMain); + GetClientRect(hMCMonitor, &rcClient); + + ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + +// MoveWindow(hwndMon,2, 0, ClientWidth-4, SplitPos, TRUE); +// MoveWindow(hwndOutput,2, 2, ClientWidth-4, ClientHeight-4, TRUE); +// MoveWindow(hwndSplit,0, SplitPos, ClientWidth, SplitBarHeight, TRUE); +} + + + + +HWND CreateMCListView (HWND hwndParent) +{ + INITCOMMONCONTROLSEX icex; // Structure for control initialization. + HWND hList; + LV_COLUMN Column; + LOGFONT lf; + HFONT hFont; + int n = 0; + + memset(&lf, 0, sizeof(LOGFONT)); + + lf.lfHeight = 12; + lf.lfWidth = 8; + lf.lfPitchAndFamily = FIXED_PITCH; + strcpy (lf.lfFaceName, "FIXEDSYS"); + + hFont = CreateFontIndirect(&lf); + + icex.dwICC = ICC_LISTVIEW_CLASSES; + InitCommonControlsEx(&icex); + + // Create the list-view window in report view with label editing enabled. + + hList = CreateWindow(WC_LISTVIEW, + "Messages", + WS_CHILD | LVS_REPORT | LVS_EDITLABELS, + 0, 0, 100, 100, + hwndParent, + (HMENU)NULL, + hInst, + NULL); + + SendMessage(hList, WM_SETFONT,(WPARAM) hFont, 0); + + + ListView_SetExtendedListViewStyle(hList,LVS_EX_FULLROWSELECT); + + Column.cx=45; + Column.mask=LVCF_WIDTH | LVCF_TEXT; + Column.pszText="ID"; + + SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); + Column.cx=95; + Column.mask=LVCF_WIDTH | LVCF_TEXT; + Column.pszText="From"; + + SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); + + Column.cx=140; + Column.mask=LVCF_WIDTH | LVCF_TEXT; + Column.pszText="FileName"; + + SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); + + Column.cx=50; + Column.mask=LVCF_WIDTH | LVCF_TEXT; + Column.pszText="Size"; + + SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); + + Column.cx=40; + Column.mask=LVCF_WIDTH | LVCF_TEXT; + Column.pszText="%"; + SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); + + Column.cx=55; + Column.mask=LVCF_WIDTH | LVCF_TEXT; + Column.pszText="Time"; + SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); + + Column.cx=55; + Column.mask=LVCF_WIDTH | LVCF_TEXT; + Column.pszText="Age"; + SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); + + Column.cx=20; + Column.mask=LVCF_WIDTH | LVCF_TEXT; + Column.pszText="C"; + + SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); + + Column.cx=430; + Column.mask=LVCF_WIDTH | LVCF_TEXT; + Column.pszText="BlockList"; + SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM) &Column); + + ShowWindow(hList, SW_SHOWNORMAL); + UpdateWindow(hList); + + return (hList); +} + +#define OurSetItemText(hwndLV, i, iSubItem_, pszText_) \ +{ LV_ITEM _ms_lvi;\ + _ms_lvi.iSubItem = iSubItem_;\ + _ms_lvi.pszText = pszText_;\ + SNDMSG((hwndLV), LVM_SETITEMTEXT, (WPARAM)i, (LPARAM)(LV_ITEM FAR *)&_ms_lvi);\ +} + +void RefreshMCLine(struct MSESSION * MSession) +{ + LV_ITEM Item; + LVFINDINFO Finfo; + int ret, n, pcent; + char Time[80]; + char Agestring[80]; + char Size[16] = "??"; + char Percent[16] = "??"; + char Key[16]; + + char BlockList[101] = ""; + struct tm * TM; + time_t Age; + + if (MCList == 0) + return; + + sprintf(Key, "%04X", MSession->Key); + + Age = time(NULL) - MSession->LastUpdated; + +// if (LocalTime) +// TM = localtime(&MSession->LastUpdated); +// else + TM = gmtime(&Age); + + sprintf(Agestring, "%.2d:%.2d", + TM->tm_hour, TM->tm_min); + + TM = gmtime(&MSession->Created); + + sprintf(Time, "%.2d:%.2d", + TM->tm_hour, TM->tm_min); + + + Finfo.flags = LVFI_STRING; + Finfo.psz = Key; + Finfo.vkDirection = VK_DOWN; + ret = SendMessage(MCList, LVM_FINDITEM, (WPARAM)-1, (LPARAM) &Finfo); + + if (ret == -1) + { + n = ListView_GetItemCount(MCList); + MSession->Index = n; + } + else + MSession->Index = ret; + + Item.mask=LVIF_TEXT; + Item.iItem = MSession->Index; + Item.iSubItem = 0; + Item.pszText = Key; + + ret = SendMessage(MCList, LVM_SETITEMTEXT, (WPARAM)MSession->Index, (LPARAM) &Item); + + if (ret == 0) + MSession->Index = ListView_InsertItem(MCList, &Item); + + sprintf(Size, "%d", MSession->MessageLen); + + if (MSession->MessageLen) + { + int i; + + pcent = (MSession->BlocksReceived * 100) / MSession->BlockCount; + sprintf(Percent, "%d", pcent); + + // Flag received blocks. Normalise to 50 wide + + memset(BlockList, '.', 50); + + for (i = 0; i < 50; i++) + { + int posn = (i * MSession->BlockCount) / 50; + if (MSession->BlockList[posn] == 1) + BlockList[i] = 'Y'; + } + } + + n = 0; + + OurSetItemText(MCList, MSession->Index, n++, Key); + if (MSession->ID) + OurSetItemText(MCList, MSession->Index, n++, MSession->ID) + else + OurSetItemText(MCList, MSession->Index, n++, " "); + + OurSetItemText(MCList, MSession->Index, n++, MSession->FileName); + OurSetItemText(MCList, MSession->Index, n++, Size); + OurSetItemText(MCList, MSession->Index, n++, Percent); + OurSetItemText(MCList, MSession->Index, n++, Time); + OurSetItemText(MCList, MSession->Index, n++, Agestring); + + if (MSession->Completed) + OurSetItemText(MCList, MSession->Index, n++, "Y") + else + OurSetItemText(MCList, MSession->Index, n++, " "); + + OurSetItemText(MCList, MSession->Index, n++, BlockList); +} + + +BOOL CreateMulticastConsole() +{ + WNDCLASS wc; + HBRUSH bgBrush; + RECT rcClient; + + if (hMCMonitor) + { + ShowWindow(hMCMonitor, SW_SHOWNORMAL); + SetForegroundWindow(hMCMonitor); + return FALSE; // Already open + } + + bgBrush = CreateSolidBrush(BGCOLOUR); + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = MCWndProc; + + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInst; + wc.hIcon = LoadIcon( hInst, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL; + wc.lpszClassName = MCClassName; + + RegisterClass(&wc); + + hMCMonitor=CreateDialog(hInst, MCClassName, 0, NULL); + + if (!hMCMonitor) + return (FALSE); + + hMCMenu=GetMenu(hMCMonitor); + +// CheckMenuItem(hMenu,MONBBS, MonBBS ? MF_CHECKED : MF_UNCHECKED); +// CheckMenuItem(hMenu,MONCHAT, MonCHAT ? MF_CHECKED : MF_UNCHECKED); +// CheckMenuItem(hMenu,MONTCP, MonTCP ? MF_CHECKED : MF_UNCHECKED); + + DrawMenuBar(hMCMonitor); + + // Create List View + + GetClientRect (hMCMonitor, &rcClient); + + MCList = CreateMCListView(hMCMonitor); + + MoveWindow(MCList, 0, 0, rcClient.right, rcClient.bottom, TRUE); + + if (cfgMinToTray) + AddTrayMenuItem(hMCMonitor, "Mail Multicast Monitor"); + + ShowWindow(hMCMonitor, SW_SHOWNORMAL); + + if (MCMonitorRect.right < 100 || MCMonitorRect.bottom < 100) + { + GetWindowRect(hMCMonitor, &MCMonitorRect); + } + + MoveWindow(hMCMonitor, MCMonitorRect.left, MCMonitorRect.top, + MCMonitorRect.right-MCMonitorRect.left, MCMonitorRect.bottom-MCMonitorRect.top, TRUE); + + MoveMCWindows(); + + return TRUE; + +} +void CopyMCToClipboard(HWND hWnd) +{ + int i,n, len=0; + char * Buffer; + HGLOBAL hMem; + char * ptr; + + char Time[80]; + char Agestring[80]; + char From[16]; + char Size[16]; + char Percent[16]; + char FileName[128]; + char Key[16]; + char Complete[2]; + + char BlockList[128]; + + n = ListView_GetItemCount(MCList); + + Buffer = malloc((n + 1) * 200); + + len = sprintf(Buffer, "ID From FileName Size %% Time Age Blocklist\r\n"); + + for (i=0; i> 1) ^ 0xA001; + else + crcval = (crcval >> 1); + } +} + +static unsigned int CalcCRC(UCHAR * ptr, int Len) +{ + int i; + + crcval = 0xFFFF; + for (i = 0; i < Len; i++) + { + update(*ptr++); + } + return crcval; +} + +struct MSESSION * FindMSession(unsigned int Key) +{ + struct MSESSION * Sess = MSessions; + struct MSESSION * LastSess = NULL; + + while (Sess) + { + if (Sess->Key == Key) + return Sess; + + LastSess = Sess; + Sess = Sess->Next; + } + + // Not found + + Sess = zalloc(sizeof(struct MSESSION)); + + if (Sess == NULL) + return NULL; + + Sess->Key = Key; + + Sess->Created = time(NULL); + + if (LastSess) + LastSess->Next = Sess; + else + MSessions = Sess; + + return Sess; +} + +#include "LzmaLib.h" + +#define LZMA_STR "\1LZMA" + +UCHAR * LZUncompress(UCHAR * Decoded, size_t Len, size_t * NewLen) +{ + unsigned char * buf; + unsigned char inprops[LZMA_PROPS_SIZE]; + size_t inlen; + int r; + + size_t rlen = 0; + size_t outlen; + + memcpy(&rlen, &Decoded[5], 4); + + outlen = ntohl(rlen); + *NewLen = outlen; + + buf = malloc(outlen); + + if (outlen > 1 << 25) + { + Debugprintf("Refusing to decompress data (> 32 MiB)"); + return NULL; + } + + + memcpy(inprops, Decoded + strlen(LZMA_STR) + sizeof(int), LZMA_PROPS_SIZE); + + inlen = Len - strlen(LZMA_STR) - sizeof(int) - LZMA_PROPS_SIZE; + + if ((r = LzmaUncompress(buf, &outlen, (const unsigned char*)Decoded + Len - inlen, &inlen, + inprops, LZMA_PROPS_SIZE)) != SZ_OK) + { + Debugprintf("Lzma Uncompress failed: %s", LZMA_ERRORS[r]); + return NULL; + } + else + { + return buf; + } +} + +void decodeblock128(unsigned char in[8], unsigned char out[7] ) +{ + out[0] = (unsigned char) (in[0] << 1 | in[1] >> 6); + out[1] = (unsigned char) (in[1] << 2 | in[2] >> 5); + out[2] = (unsigned char) (in[2] << 3 | in[3] >> 4); + out[3] = (unsigned char) (in[3] << 4 | in[4] >> 3); + out[4] = (unsigned char) in[4] << 5 | in[5] >> 2; + out[5] = (unsigned char) in[5] << 6 | in[6] >> 1; + out[6] = (unsigned char) in[6] << 7 | in[7]; +} + + +void SaveMulticastMessage(struct MSESSION * MSession) +{ + UCHAR * Decoded = NULL; // Output from Basexxx decode + UCHAR * Uncompressed = NULL; + size_t DecodedLen; // Length of decoded message + size_t UncompressedLen; // Length of decompressed message + int ExpectedLen; // From front of Base128 or Base256 message + int HddrLen; // Length of Expected Len Header + + if (MSession->FileName == NULL) + return; // Need Name + + MSession->Completed = TRUE; // So we don't get it again + + // If compresses and encoded, decode and decompress + + if (memcmp(MSession->Message, "[b64:start]", 11) == 0) + { + UCHAR * ptr1 = &MSession->Message[11]; + UCHAR * ptr2 = malloc(MSession->MessageLen); // Must get smaller + + int Len = MSession->MessageLen - 21; // Header and Trailer + + Decoded = ptr2; + + // Decode Base64 encoding + + while (Len > 0) + { + decodeblock(ptr1, ptr2); + ptr1 += 4; + ptr2 += 3; + Len -= 4; + } + + DecodedLen = (int)(ptr2 - Decoded); + Uncompressed = LZUncompress(Decoded, DecodedLen, &UncompressedLen); + } + else if (memcmp(MSession->Message, "[b128:start]", 12) == 0) + { + UCHAR * ptr1 = &MSession->Message[12]; + UCHAR * ptr2 = malloc(MSession->MessageLen); // Must get smaller + UCHAR ch; + UCHAR * Intermed; + + int Len = MSession->MessageLen - 23; // Header and Trailer + + Intermed = ptr2; + + // Decode Base128 encoding + + // First remove transparency (as in base256) + + + // Extract decoded msg len + + ExpectedLen = atoi(ptr1); + + ptr1 = strchr(ptr1, 10); + ptr1++; + + HddrLen = (int)(ptr1 - &MSession->Message[12]); + + if (ExpectedLen == 0 || ExpectedLen > Len || ptr1 == (UCHAR *)1) + { + Debugprintf("MCAST Missing Length Field"); + return; + } + + Len -= HddrLen;; + + while (Len > 0) + { + ch = *(ptr1++); + Len --; + + if (ch == ':') + { + ch = *(ptr1++); + Len--; + + switch (ch) + { + case ':' : *(ptr2++) = ':'; break; + case '0' : *(ptr2++) = 0x00; break; + case '1' : *(ptr2++) = 0x01; break; + case '2' : *(ptr2++) = 0x02; break; + case '3' : *(ptr2++) = 0x03; break; + case '4' : *(ptr2++) = 0x04; break; + case '5' : *(ptr2++) = 0x05; break; + case '6' : *(ptr2++) = 0x06; break; + case '7' : *(ptr2++) = 0x07; break; + case '8' : *(ptr2++) = 0x08; break; + case '9' : *(ptr2++) = 0x09; break; + case 'A' : *(ptr2++) = '\n'; break; + case 'B' : *(ptr2++) = '\r'; break; + case 'C' : *(ptr2++) = '^'; break; + case 'D' : *(ptr2++) = 0x7F; break; + case 'E' : *(ptr2++) = 0xFF; break; + } + } + else + *(ptr2++) = ch; + } + + + Len = ptr2 - Intermed; + + ptr1 = Intermed; + ptr2 = malloc(MSession->MessageLen); // Must get smaller + Decoded = ptr2; + + while (Len > 0) + { + decodeblock128(ptr1, ptr2); + ptr1 += 8; + ptr2 += 7; + Len -= 8; + } + + DecodedLen = ptr2 - Decoded; + Uncompressed = LZUncompress(Decoded, DecodedLen, &UncompressedLen); + } + else if (memcmp(MSession->Message, "[b256:start]", 12) == 0) + { + UCHAR * ptr1 = &MSession->Message[12]; + UCHAR * ptr2 = malloc(MSession->MessageLen); // Must get smaller + UCHAR ch; + + int Len = MSession->MessageLen - 23; // Header and Trailer + + Decoded = ptr2; + + // Decode Base256 encoding + + // Extract decoded msg len + + ExpectedLen = atoi(ptr1); + + ptr1 = strchr(ptr1, 10); + ptr1++; + + HddrLen = ptr1 - &MSession->Message[12]; + + if (ExpectedLen == 0 || ExpectedLen > Len || ptr1 == (UCHAR *)1) + { + Debugprintf("MCAST Missing Length Field"); + return; + } + + Len -= HddrLen;; + + while (Len > 0) + { + ch = *(ptr1++); + Len --; + + if (ch == ':') + { + ch = *(ptr1++); + Len--; + + switch (ch) + { + case ':' : *(ptr2++) = ':'; break; + case '0' : *(ptr2++) = 0x00; break; + case '1' : *(ptr2++) = 0x01; break; + case '2' : *(ptr2++) = 0x02; break; + case '3' : *(ptr2++) = 0x03; break; + case '4' : *(ptr2++) = 0x04; break; + case '5' : *(ptr2++) = 0x05; break; + case '6' : *(ptr2++) = 0x06; break; + case '7' : *(ptr2++) = 0x07; break; + case '8' : *(ptr2++) = 0x08; break; + case '9' : *(ptr2++) = 0x09; break; + case 'A' : *(ptr2++) = '\n'; break; + case 'B' : *(ptr2++) = '\r'; break; + case 'C' : *(ptr2++) = '^'; break; + case 'D' : *(ptr2++) = 0x7F; break; + case 'E' : *(ptr2++) = 0xFF; break; + } + } + else + *(ptr2++) = ch; + } + + DecodedLen = ptr2 - Decoded; + Uncompressed = LZUncompress(Decoded, DecodedLen, &UncompressedLen); + } + else + { + // Plain Text + + UncompressedLen = MSession->MessageLen; + Uncompressed = MSession->Message; + + MSession->Message = NULL; // So we dont try to free again + } + + if (Decoded) + free(Decoded); + + if (Uncompressed) + { + // Write it away and free it + + char MsgFile[MAX_PATH]; + FILE * hFile; + int WriteLen=0; + UCHAR * ptr1 = Uncompressed; + + // Make Sure MCAST directory exists + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/MCAST", MailDir); + +#ifdef WIN32 + CreateDirectory(MsgFile, NULL); // Just in case +#else + mkdir(MsgFile, S_IRWXU | S_IRWXG | S_IRWXO); + chmod(MsgFile, S_IRWXU | S_IRWXG | S_IRWXO); +#endif + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/MCAST/%s", MailDir, MSession->FileName); + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + WriteLen = (int)fwrite(Uncompressed, 1, UncompressedLen, hFile); + fclose(hFile); + } + + + // if it looks like an export file (Starts SP SB or ST) and ends /ex + // import and delete it. + + if (*(ptr1) == 'S' && ptr1[2] == ' ') + if (_memicmp(&ptr1[UncompressedLen - 5], "/EX", 3) == 0) + ImportMessages(NULL, MsgFile, TRUE); + + free (Uncompressed); + } +} + +VOID ProcessMCASTLine(ConnectionInfo * conn, struct UserInfo * user, char * Buffer, int MsgLen) +{ + char Opcode[80]; + unsigned int checksum, len = 0; + char * data; + int headerlen = 0; + unsigned int crcval; + int n; + unsigned int Key; + struct MSESSION * MSession; + + if (MsgLen == 1 && Buffer[0] == 13) + return; + + MsgLen --; // Remove the CR we added + + Buffer[MsgLen] = 0; + + if (MsgLen == 1 && Buffer[0] == 13) + return; + +// return; + + n = sscanf(&Buffer[1], "%s %04d %04X", Opcode, &len, &checksum); + + if (n != 3) + return; + + data = strchr(Buffer, '>'); + + if (data) + headerlen = (int)(++data - Buffer); + + if (headerlen + len != MsgLen) + return; + + crcval = CalcCRC(data, len); + + if (checksum != crcval) + return; + + // Extract Session Key + + sscanf(&data[1], "%04X", &Key); + + MSession = FindMSession(Key); + + if (MSession == 0) + return; // ?? couldn't allocate + + MSession->LastUpdated = time(NULL); + + if (MSession->Completed) + return; // We already have it all + + if (strcmp(Opcode, "ID") == 0) + { + strlop(&data[6], ' '); + MSession->ID = _strdup(&data[6]); + + return; + } + + if (strcmp(Opcode, "PROG") == 0) + { + // Ignore for now + return; + } + + if (strcmp(Opcode, "FILE") == 0) + { + // {80BC}20141108142542:debug_log.txt + + char * FN = strchr(&data[6], ':'); + + if (FN) + { + *(FN++) = 0; + + MSession->FileName = _strdup(FN); + MSession->OrigTimeStamp = _strdup(&data[6]); + } + + // We could get whole message without getting the Name, + // so check + + if (MSession->BlockCount && MSession->BlocksReceived == MSession->BlockCount) + { + // We have the whole message. Decode and Save + + if (MSession->MessageLen) // Also need length + SaveMulticastMessage(MSession); + } + + RefreshMCLine(MSession); + return; + } + + if (strcmp(Opcode, "SIZE") == 0) + { + // SIZE 14 2995>{80BC}465 8 64 + + int a, b, c, n = sscanf(&data[6], "%d %d %d", &a, &b, &c); + + if (n == 3) + { + // We may already have some (or even all) the message if we + // missed the SIZE block first time round + + if (MSession->Message) + { + // Already have at least part of it + + if (MSession->BlockSize != c) + { + // We based blocksize on last packet, so need to sort out mess + + // Find where we put the block, and move it + + UCHAR * OldLoc; + + MSession->Message = realloc(MSession->Message, a); + MSession->BlockList = realloc(MSession->BlockList, b); + + OldLoc = &MSession->Message[(MSession->BlockCount - 1) * MSession->BlockSize]; + + memmove(&MSession->Message[(MSession->BlockCount - 1) * c], OldLoc, MSession->BlockSize); + + MSession->BlockSize = c; + } + + if (MSession->BlockCount < b) + { + // Dont have it all, so need to extend ; + + MSession->Message = realloc(MSession->Message, a); + MSession->BlockList = realloc(MSession->BlockList, b); + } + } + + MSession->MessageLen = a; + MSession->BlockCount = b; + MSession->BlockSize = c; + + if (MSession->Message == NULL) + { + MSession->Message = zalloc(b * c); + MSession->BlockList = zalloc(b); + } + + // We might have it all now + + if (MSession->BlocksReceived == MSession->BlockCount) + { + // We have the whole message. Decode and Save + + SaveMulticastMessage(MSession); + } + } + + RefreshMCLine(MSession); + return; + } + + if (strcmp(Opcode, "DATA") == 0) + { + // {80BC:1}[b256:start]401 + + int Blockno = atoi(&data[6]); + char * dataptr = strchr(&data[6], '}'); + + if (dataptr == 0) + return; + + dataptr++; + + // What should we do if we don't have Filename or Size?? + + // If we assume this isn't the last block, then we can get + // the block size from this message. This is pretty save, but + // I guess as we will only get one last block, if we subsequently + // get an earlier one that is bigger, we can recalculate the position + // of this block and move it. + + if (MSession->MessageLen == 0) + { + // Haven't received SIZE Message yet. Guess the blocksize + + int blocksize = MsgLen - (int)(dataptr - Buffer); + + if (MSession->BlockSize == 0) + { + MSession->BlockSize = blocksize; + } + else + { + if (MSession->BlockSize < blocksize) + { + // We based blocksize on last packet, so need to sort out mess + + // Find where we put the block, and move it + + UCHAR * OldLoc = &MSession->Message[(MSession->BlockCount - 1) * MSession->BlockSize]; + memmove(&MSession->Message[(MSession->BlockCount - 1) * blocksize], OldLoc, MSession->BlockSize); + + MSession->BlockSize = blocksize; + } + } + + // We need to realloc Message and Blocklist if this is a later block + + if (MSession->BlockCount < Blockno) + { + MSession->Message = realloc(MSession->Message, Blockno * MSession->BlockSize); + MSession->BlockList = realloc(MSession->BlockList, Blockno); + + memset(&MSession->BlockList[MSession->BlockCount], 0, Blockno - MSession->BlockCount); + MSession->BlockCount = Blockno; + + } + + } + if (Blockno == 0 || Blockno > MSession->BlockCount) + return; + + Blockno--; + + if (MSession->BlockList[Blockno] == 1) + { + // Already have this block + + return; + } + + + memcpy(&MSession->Message[Blockno * MSession->BlockSize], dataptr, MSession->BlockSize); + MSession->BlockList[Blockno] = 1; + + MSession->BlocksReceived++; + + if (MSession->BlocksReceived == MSession->BlockCount && MSession->MessageLen) + { + // We have the whole message. Decode and Save + + SaveMulticastMessage(MSession); + } + + RefreshMCLine(MSession); + + return; + } + + MsgLen++; + +/* + +QST DE GM8BPQ + +{80BC}FLAMP 2.2.03 +{80BC}20141108142542:debug_log.txt +{80BC}GM8BPQ Skigersta +{80BC}465 8 64 +{80BC:1}[b256:start]401 +:1LZMA:0:0:6-]:0:0:0:4:0$--:7m?8-v\-E-Y-[--- +{80BC:2}rS-)N{j--o--ZMPX-,-l-yD------E--;-o:6-|;--f---q----0<---%-- +{80BC:3}*N-?N--*:Cf{:9--z-J:9-HMd:8-------Q--D---_-a----:C$;A-j---(: +{80BC:4}DWb--K---Qq-uj-_--;i------T-\>-{:6---~-ij~-,-(-O--2--+ +-:8 +{80BC:5}p---:7Gf:E-5o->x---4--K--:3-\:E---gouuH-3'----:A!.:7 +--N:0S +{80BC:6}.---/-~#.-:D:7zg~--m--:8-'---Y%-?--ze\-ho:5-}-:C:A:1u-1-O- +- +{80BC:7}9p-42-w--G:2G:3--g--O---n----c-#----DF-!~--:D-A--|-e------- +{80BC:8}B{--:0 +[b256:end] +{80BC:EOF} +{80BC:EOT} + +DE GM8BPQ K + +*/ + + return; +/* + if (strcmp(Buffer, "ARQ::ETX\r") == 0) + { + // Decode it. + + UCHAR * ptr1, * ptr2, * ptr3; + int len, linelen; + struct MsgInfo * Msg = conn->TempMsg; + time_t Date; + char FullTo[100]; + char FullFrom[100]; + char ** RecpTo = NULL; // May be several Recipients + char ** HddrTo = NULL; // May be several Recipients + char ** Via = NULL; // May be several Recipients + int LocalMsg[1000] ; // Set if Recipient is a local wl2k address + + int B2To; // Offset to To: fields in B2 header + int Recipients = 0; + int RMSMsgs = 0, BBSMsgs = 0; + +// Msg->B2Flags |= B2Msg; + + + ptr1 = conn->MailBuffer; + len = Msg->length; + ptr1[len] = 0; + + if (strstr(ptr1, "ARQ:ENCODING::")) + { + // a file, not a message. If is called "BBSPOLL" do a reverse forward else Ignore for now + + _strupr(conn->MailBuffer); + if (strstr(conn->MailBuffer, "BBSPOLL")) + { + SendARQMail(conn); + } + + free(conn->MailBuffer); + conn->MailBuffer = NULL; + conn->MailBufferSize = 0; + + return; + } + Loop: + ptr2 = strchr(ptr1, '\r'); + + linelen = ptr2 - ptr1; + + if (_memicmp(ptr1, "From:", 5) == 0 && linelen > 6) // Can have empty From: + { + char SaveFrom[100]; + char * FromHA; + + memcpy(FullFrom, ptr1, linelen); + FullFrom[linelen] = 0; + + // B2 From may now contain an @BBS + + strcpy(SaveFrom, FullFrom); + + FromHA = strlop(SaveFrom, '@'); + + if (strlen(SaveFrom) > 12) SaveFrom[12] = 0; + + strcpy(Msg->from, &SaveFrom[6]); + + if (FromHA) + { + if (strlen(FromHA) > 39) FromHA[39] = 0; + Msg->emailfrom[0] = '@'; + strcpy(&Msg->emailfrom[1], _strupr(FromHA)); + } + + // Remove any SSID + + ptr3 = strchr(Msg->from, '-'); + if (ptr3) *ptr3 = 0; + + } + else if (_memicmp(ptr1, "To:", 3) == 0 || _memicmp(ptr1, "cc:", 3) == 0) + { + HddrTo=realloc(HddrTo, (Recipients+1) * sizeof(void *)); + HddrTo[Recipients] = zalloc(100); + + memset(FullTo, 0, 99); + memcpy(FullTo, &ptr1[4], linelen-4); + memcpy(HddrTo[Recipients], ptr1, linelen+2); + LocalMsg[Recipients] = FALSE; + + _strupr(FullTo); + + B2To = ptr1 - conn->MailBuffer; + + if (_memicmp(FullTo, "RMS:", 4) == 0) + { + // remove RMS and add @winlink.org + + strcpy(FullTo, "RMS"); + strcpy(Msg->via, &FullTo[4]); + } + else + { + ptr3 = strchr(FullTo, '@'); + + if (ptr3) + { + *ptr3++ = 0; + strcpy(Msg->via, ptr3); + } + else + Msg->via[0] = 0; + } + + if (_memicmp(&ptr1[4], "SMTP:", 5) == 0) + { + // Airmail Sends MARS messages as SMTP + + if (CheckifPacket(Msg->via)) + { + // Packet Message + + memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); + _strupr(FullTo); + _strupr(Msg->via); + + // Update the saved to: line (remove the smtp:) + + strcpy(&HddrTo[Recipients][4], &HddrTo[Recipients][9]); + BBSMsgs++; + goto BBSMsg; + } + + // If a winlink.org address we need to convert to call + + if (_stricmp(Msg->via, "winlink.org") == 0) + { + memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); + _strupr(FullTo); + LocalMsg[Recipients] = CheckifLocalRMSUser(FullTo); + } + else + { + memcpy(Msg->via, &ptr1[9], linelen); + Msg->via[linelen - 9] = 0; + strcpy(FullTo,"RMS"); + } +// FullTo[0] = 0; + + BBSMsg: + _strupr(FullTo); + _strupr(Msg->via); + } + + if (memcmp(FullTo, "RMS:", 4) == 0) + { + // remove RMS and add @winlink.org + + memmove(FullTo, &FullTo[4], strlen(FullTo) - 3); + strcpy(Msg->via, "winlink.org"); + sprintf(HddrTo[Recipients], "To: %s\r\n", FullTo); + } + + if (strcmp(Msg->via, "RMS") == 0) + { + // replace RMS with @winlink.org + + strcpy(Msg->via, "winlink.org"); + sprintf(HddrTo[Recipients], "To: %s@winlink.org\r\n", FullTo); + } + + if (strlen(FullTo) > 6) + FullTo[6] = 0; + + strlop(FullTo, '-'); + + strcpy(Msg->to, FullTo); + + if (SendBBStoSYSOPCall) + if (_stricmp(FullTo, BBSName) == 0) + strcpy(Msg->to, SYSOPCall); + + if ((Msg->via[0] == 0 || strcmp(Msg->via, "BPQ") == 0 || strcmp(Msg->via, "BBS") == 0)) + { + // No routing - check @BBS and WP + + struct UserInfo * ToUser = LookupCall(FullTo); + + Msg->via[0] = 0; // In case BPQ and not found + + if (ToUser) + { + // Local User. If Home BBS is specified, use it + + if (ToUser->HomeBBS[0]) + { + strcpy(Msg->via, ToUser->HomeBBS); + } + } + else + { + WPRecP WP = LookupWP(FullTo); + + if (WP) + { + strcpy(Msg->via, WP->first_homebbs); + + } + } + + // Fix To: address in B2 Header + + if (Msg->via[0]) + sprintf(HddrTo[Recipients], "To: %s@%s\r\n", FullTo, Msg->via); + else + sprintf(HddrTo[Recipients], "To: %s\r\n", FullTo); + + } + + RecpTo=realloc(RecpTo, (Recipients+1) * sizeof(void *)); + RecpTo[Recipients] = zalloc(10); + + Via=realloc(Via, (Recipients+1) * sizeof(void *)); + Via[Recipients] = zalloc(50); + + strcpy(Via[Recipients], Msg->via); + strcpy(RecpTo[Recipients++], FullTo); + + // Remove the To: Line from the buffer + + } + else if (_memicmp(ptr1, "Type:", 4) == 0) + { + if (ptr1[6] == 'N') + Msg->type = 'T'; // NTS + else + Msg->type = ptr1[6]; + } + else if (_memicmp(ptr1, "Subject:", 8) == 0) + { + int Subjlen = ptr2 - &ptr1[9]; + if (Subjlen > 60) Subjlen = 60; + memcpy(Msg->title, &ptr1[9], Subjlen); + + goto ProcessBody; + } +// else if (_memicmp(ptr1, "Body:", 4) == 0) +// { +// MsgLen = atoi(&ptr1[5]); +// StartofMsg = ptr1; +// } + else if (_memicmp(ptr1, "File:", 5) == 0) + { + Msg->B2Flags |= Attachments; + } + else if (_memicmp(ptr1, "Date:", 5) == 0) + { + struct tm rtime; + char seps[] = " ,\t\r"; + + memset(&rtime, 0, sizeof(struct tm)); + + // Date: 2009/07/25 10:08 + + sscanf(&ptr1[5], "%04d/%02d/%02d %02d:%02d:%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); + + sscanf(&ptr1[5], "%02d/%02d/%04d %02d:%02d:%02d", + &rtime.tm_mday, &rtime.tm_mon, &rtime.tm_year, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); + + rtime.tm_year -= 1900; + + Date = mktime(&rtime) - (time_t)_MYTIMEZONE; + + if (Date == (time_t)-1) + Date = time(NULL); + + Msg->datecreated = Date; + + } + + if (linelen) // Not Null line + { + ptr1 = ptr2 + 2; // Skip cr + goto Loop; + } + + + // Processed all headers +ProcessBody: + + ptr2 +=2; // skip crlf + + Msg->length = &conn->MailBuffer[Msg->length] - ptr2; + + memmove(conn->MailBuffer, ptr2, Msg->length); + + CreateMessageFromBuffer(conn); + + conn->BBSFlags = 0; // Clear ARQ Mode + return; + } + + // File away the data + + Buffer[MsgLen++] = 0x0a; // BBS Msgs stored with crlf + + if ((conn->TempMsg->length + MsgLen) > conn->MailBufferSize) + { + conn->MailBufferSize += 10000; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + BBSputs(conn, "*** Failed to extend Message Buffer\r"); + conn->CloseAfterFlush = 20; // 2 Secs + + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, MsgLen); + + conn->TempMsg->length += MsgLen; + + return; + + // Not sure what to do yet with files, but will process emails (using text style forwarding +*/ +/* +ARQ:FILE::flarqmail-1.eml +ARQ:EMAIL:: +ARQ:SIZE::96 +ARQ::STX +//FLARQ COMPOSER +Date: 16/01/2014 22:26:06 +To: g8bpq +From: +Subject: test message + +Hello +Hello + +ARQ::ETX +*/ + + return; +} + +VOID MCastConTimer(ConnectionInfo * conn) +{ + conn->MCastListenTime--; + + if (conn->MCastListenTime == 0) + Disconnect(conn->BPQStream); +} + +VOID MCastTimer() +{ + struct MSESSION * Sess = MSessions; + struct MSESSION * Prev = NULL; + + time_t Now = time(NULL); + + while (Sess) + { + if (Sess->Completed == FALSE) + RefreshMCLine(Sess); + + if (Now - Sess->LastUpdated > MulticastMaxAge) + { + // remove from list + +#ifndef LINBPQ + ListView_DeleteItem(MCList, Sess->Index); +#endif + if (Prev) + Prev->Next = Sess->Next; + else + MSessions = Sess->Next; + + if (Sess->FileName) + free(Sess->FileName); + + if (Sess->OrigTimeStamp) + free(Sess->OrigTimeStamp); + + if (Sess->Message) + free(Sess->Message); + + if (Sess->BlockList) + free(Sess->BlockList); + + if (Sess->ID) + free(Sess->ID); + + free(Sess); + + return; // Saves messing with chain + + } + Prev = Sess; + Sess = Sess->Next; + } +} + +int MulticastStatusHTML(char * Reply) +{ + char StatusPage [] = + "
ID    From      FileName        Size  %%  Time   Age   Blocklist"
+		"                                                   "
+		"\r\n

"; + int Len = 0; + char Unknown[] = "???"; + + struct MSESSION * Sess = MSessions; + + if (Sess ==NULL) + return 0; + + Len = sprintf(Reply, "%s", StatusPage); + + while (Sess) + { + char Percent[16] = "???"; + char BlockList[51] = ""; + int i; + char Time[80]; + char Agestring[80]; + struct tm * TM; + time_t Age; + char * ID = Unknown; + char * FileName = Unknown; + + Age = time(NULL) - Sess->LastUpdated; + + TM = gmtime(&Age); + + sprintf(Agestring, "%.2d:%.2d", TM->tm_hour, TM->tm_min); + + TM = gmtime(&Sess->Created); + + sprintf(Time, "%.2d:%.2d", TM->tm_hour, TM->tm_min); + + if (Sess->MessageLen && Sess->BlockCount) + { + int pcent; + + pcent = (Sess->BlocksReceived * 100) / Sess->BlockCount; + sprintf(Percent, "%d", pcent); + } + + // Flag received blocks. Normalise to 50 wide + + memset(BlockList, '.', 50); + + if (Sess->BlockList) + { + for (i = 0; i < 50; i++) + { + int posn = (i * Sess->BlockCount) / 50; + if (Sess->BlockList[posn] == 1) + BlockList[i] = 'Y'; + } + } + if (Sess->FileName) + FileName = Sess->FileName; + + if (Sess->ID) + ID = Sess->ID; + + Len += sprintf(&Reply[Len], "%04X %-10s%-15s%5d %-3s %s %s %s\r\n", + Sess->Key, ID, FileName, + Sess->MessageLen, Percent, Time, Agestring, BlockList); + + Sess = Sess->Next; + } + + Len += sprintf(&Reply[Len], "%s", StatusTail); + + return Len; +} diff --git a/.svn/pristine/05/05be6263d55be8cd16e65e2edc8dcf1aab6277a1.svn-base b/.svn/pristine/05/05be6263d55be8cd16e65e2edc8dcf1aab6277a1.svn-base index 2ce8aa6..84df6b3 100644 --- a/.svn/pristine/05/05be6263d55be8cd16e65e2edc8dcf1aab6277a1.svn-base +++ b/.svn/pristine/05/05be6263d55be8cd16e65e2edc8dcf1aab6277a1.svn-base @@ -1,3143 +1,3143 @@ -/* -Copyright 2001-2022 John Wiseman G8BPQ -This file is part of LinBPQ/BPQ32. - -LinBPQ/BPQ32 is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -LinBPQ/BPQ32 is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses -*/ - - -// July 2010 - -// BPQ32 now reads bpqcfg.txt. This module converts it to the original binary format. - -// Based on the standalonw bpqcfg.c - -/************************************************************************/ -/* CONFIG.C Jonathan Naylor G4KLX, 19th November 1988 */ -/* */ -/* Program to produce configuration file for G8BPQ Network Switch */ -/* based on the original written in BASIC by G8BPQ. */ -/* */ -/* Subsequently extended by G8BPQ */ -/* */ -/************************************************************************/ -// -// 22/11/95 - Add second port alias for digipeating (for APRS) -// - Add PORTMAXDIGIS param - -// 1999 - Win32 Version (but should also compile in 16 bit - -// 5/12/99 - Add DLLNAME Param for ext driver - -// 26/11/02 - Added AUTOSAVE - -// Jan 2006 - -// Add params for input and output names -// Wait before exiting if error detected - -// March 2006 - -// Accept # as comment delimiter -// Display input and output filenames -// Wait before exit, even if ok - -// March 2006 - -// Add L4APPL param - -// Jan 2007 - -// Remove UNPROTO -// Add BTEXT -// Add BCALL - -// Nov 2007 - -// Convert calls and APPLICATIONS string to upper case - -// Jan 2008 - -// Remove trailing space from UNPROTO -// Don't warn BBSCALL etc missing if APPL1CALL etc present - -// August 2008 - -// Add IPGATEWAY Parameter -// Add Port DIGIMASK Parameter - -// December 2008 - -// Add C_IS_CHAT Parameter - -// March 2009 - -// Add C style COmments (/* */ at start of line) - -// August 2009 - -// Add INP3 flag to locked routes - -// November 2009 - -// Add PROTOCOL=PACTOR or WINMOR option - -// December 2009 - -// Add INP3 MAXRTT and MAXHOPS Commands - -// March 2010 - -// Add SIMPLE mode - -// March 2010 - -// Change SIMPLE mode default of Full_CTEXT to 1 - -// April 2010 - -// Add NoKeepAlive ROUTE option - -// Converted to intenal bpq32 process - -// Spetember 2010 - -// Add option of embedded port configuration - - - -#define _CRT_SECURE_NO_DEPRECATE - -#include "cheaders.h" - -#include -#include -#include -#include -#include - -#include "configstructs.h" - -// KISS Options Equates - -#define CHECKSUM 1 -#define POLLINGKISS 2 // KISSFLAGS BITS -#define ACKMODE 4 // CAN USE ACK REQURED FRAMES -#define POLLEDKISS 8 // OTHER END IS POLLING US -#define D700 16 // D700 Mode (Escape "C" chars -#define TNCX 32 // TNC-X Mode (Checksum of ACKMODE frames includes ACK bytes -#define PITNC 64 // PITNC Mode - can reset TNC with FEND 15 2 -#define NOPARAMS 128 // Don't send SETPARAMS frame -#define FLDIGI 256 // Support FLDIGI COmmand Frames -#define TRACKER 512 // SCS Tracker. Need to set KISS Mode -#define FASTI2C 1024 // Use BLocked I2C Reads (like ARDOP) -#define DRATS 2048 - - - -struct WL2KInfo * DecodeWL2KReportLine(char * buf); - -// Dummy file routines - write to buffer instead - -char * PortConfig[70]; -char * RadioConfigMsg[70]; -char * WL2KReportLine[70]; - -int nextRadioPort = 0; -int nextDummyInterlock = 233; - -BOOL PortDefined[70]; - -extern BOOL IncludesMail; -extern BOOL IncludesChat; -extern int needAIS; -extern int needADSB; - -extern int AGWPort; -extern int AGWSessions; -extern int AGWMask; - -extern BOOL LoopMonFlag; -extern BOOL Loopflag; - -extern char NodeMapServer[80]; -extern char ChatMapServer[80]; - -double LatFromLOC; -double LonFromLOC; - - - -VOID * zalloc(int len); - -int WritetoConsoleLocal(char * buff); -char * stristr (char *ch1, char *ch2); -int FromLOC(char * Locator, double * pLat, double * pLon); - -VOID Consoleprintf(const char * format, ...) -{ - char Mess[512]; - va_list(arglist); - - va_start(arglist, format); - vsprintf(Mess, format, arglist); - strcat(Mess, "\n"); - WritetoConsoleLocal(Mess); - - return; -} - - -#pragma pack() - -int tnctypes(int i, char value[],char rec[]); -int do_kiss (char value[],char rec[]); - -struct TNCDATA * TNCCONFIGTABLE = NULL; // malloc'ed -int NUMBEROFTNCPORTS = 0; - -struct UPNP * UPNPConfig = NULL; - -struct TNCDATA * TNC2ENTRY; - -extern char PWTEXT[]; -extern char HFCTEXT[]; -extern int HFCTEXTLEN; -extern char LOCATOR[]; -extern char MAPCOMMENT[]; -extern char LOC[]; -extern int RFOnly; - -int decode_rec(char *rec); -int applcallsign(int i,char *value,char *rec); -int appl_qual(int i,char *value,char *rec); -int callsign(char * val, char *value, char *rec); -int int_value(short * val, char *value, char *rec); -int hex_value(int * val, char *value, char *rec); -int bin_switch(char * val, char *value, char *rec); -int dec_switch(char * val, char *value, char *rec); -int applstrings(int i,char *value, char *rec); -int dotext(char * val, char *key_word, int max); -int dolinked(int i, char * value, char * rec); -int routes(int i); -int ports(int i); -int tncports(int i); -int dedports(int i); -int xindex(char *s,char *t); -int verify(char *s,char c); -int GetNextLine(char * rec); -int call_check(char *callsign, char * val); -int call_check_internal(char * callsign); -int callstring(int i,char *value,char *rec); -int decode_port_rec(char *rec); -int doid(int i,char *value,char *rec); -int dodll(int i,char *value,char *rec); -int doDriver(int i,char *value,char *rec); -int hwtypes(int i,char *value,char *rec); -int protocols(int i,char *value,char *rec); -int bbsflag(int i,char *value,char *rec); -int channel(int i,char *value,char *rec); -int validcalls(int i,char *value,char *rec); -int kissoptions(int i,char *value,char *rec); -int decode_tnc_rec(char *rec); -int tnctypes(int i,char *value,char *rec); -int do_kiss(char *value,char *rec); -int decode_ded_rec(char *rec); -int simple(int i); -int64_t int64_value(int64_t * val, char value[], char rec[]); - - -int C_Q_ADD_NP(VOID *PQ, VOID *PBUFF); -int doSerialPortName(int i, char * value, char * rec); -int doPermittedAppls(int i, char * value, char * rec); -int doKissCommand(int i, char * value, char * rec); - -BOOL ProcessAPPLDef(char * rec); -BOOL ToLOC(double Lat, double Lon , char * Locator); - -//int i; -//char value[]; -//char rec[]; - -int FIRSTAPPL=1; -BOOL Comment = FALSE; -int CommentLine = 0; - -#define MAXLINE 512 -#define FILEVERSION 22 - -extern UCHAR BPQDirectory[]; - -struct CONFIGTABLE xxcfg; -struct PORTCONFIG xxp; - -char inputname[250]="bpqcfg.txt"; -char option[250]; - -/************************************************************************/ -/* STATIC VARIABLES */ -/************************************************************************/ - -static char *keywords[] = -{ -"OBSINIT", "OBSMIN", "NODESINTERVAL", "L3TIMETOLIVE", "L4RETRIES", "L4TIMEOUT", -"BUFFERS", "PACLEN", "TRANSDELAY", "T3", "IDLETIME", "BBS", -"NODE", "NODEALIAS", "BBSALIAS", "NODECALL", "BBSCALL", -"TNCPORT", "IDMSG:", "INFOMSG:", "ROUTES:", "PORT", "MAXLINKS", -"MAXNODES", "MAXROUTES", "MAXCIRCUITS", "IDINTERVAL", "MINQUAL", -"HIDENODES", "L4DELAY", "L4WINDOW", "BTINTERVAL", "UNPROTO", "BBSQUAL", -"APPLICATIONS", "EMS", "CTEXT:", "DESQVIEW", "HOSTINTERRUPT", "ENABLE_LINKED", -"XXDEDHOST", "FULL_CTEXT", "SIMPLE", "AUTOSAVE" , "L4APPL", -"APPL1CALL", "APPL2CALL", "APPL3CALL", "APPL4CALL", -"APPL5CALL", "APPL6CALL", "APPL7CALL", "APPL8CALL", -"APPL1ALIAS", "APPL2ALIAS", "APPL3ALIAS", "APPL4ALIAS", -"APPL5ALIAS", "APPL6ALIAS", "APPL7ALIAS", "APPL8ALIAS", -"APPL1QUAL", "APPL2QUAL", "APPL3QUAL", "APPL4QUAL", -"APPL5QUAL", "APPL6QUAL", "APPL7QUAL", "APPL8QUAL", -"BTEXT:", "NETROMCALL", "C_IS_CHAT", "MAXRTT", "MAXHOPS", // IPGATEWAY= no longer allowed -"LogL4Connects", "LogAllConnects", "SAVEMH", "ENABLEADIFLOG", "ENABLEEVENTS", "SAVEAPRSMSGS", -"EnableM0LTEMap", "MQTT", "MQTT_HOST", "MQTT_PORT", "MQTT_USER", "MQTT_PASS", -"L4Compress", "L4CompMaxframe", "L4CompPaclen", "L2Compress", "L2CompMaxframe", "L2CompPaclen" -}; /* parameter keywords */ - -static void * offset[] = -{ -&xxcfg.C_OBSINIT, &xxcfg.C_OBSMIN, &xxcfg.C_NODESINTERVAL, &xxcfg.C_L3TIMETOLIVE, &xxcfg.C_L4RETRIES, &xxcfg.C_L4TIMEOUT, -&xxcfg.C_BUFFERS, &xxcfg.C_PACLEN, &xxcfg.C_TRANSDELAY, &xxcfg.C_T3, &xxcfg.C_IDLETIME, &xxcfg.C_BBS, -&xxcfg.C_NODE, &xxcfg.C_NODEALIAS, &xxcfg.C_BBSALIAS, &xxcfg.C_NODECALL, &xxcfg.C_BBSCALL, -0, &xxcfg.C_IDMSG, &xxcfg.C_INFOMSG, &xxcfg.C_ROUTE, &xxcfg.C_PORT, &xxcfg.C_MAXLINKS, -&xxcfg.C_MAXDESTS, &xxcfg.C_MAXNEIGHBOURS, &xxcfg.C_MAXCIRCUITS, &xxcfg.C_IDINTERVAL, &xxcfg.C_MINQUAL, -&xxcfg.C_HIDENODES, &xxcfg.C_L4DELAY, &xxcfg.C_L4WINDOW, &xxcfg.C_BTINTERVAL, &xxcfg.C_WASUNPROTO, &xxcfg.C_BBSQUAL, -&xxcfg.C_APPL, &xxcfg.C_EMSFLAG, &xxcfg.C_CTEXT , &xxcfg.C_DESQVIEW, &xxcfg.C_HOSTINTERRUPT, &xxcfg.C_LINKEDFLAG, -0, &xxcfg.C_FULLCTEXT, 0, &xxcfg.C_AUTOSAVE, &xxcfg.C_L4APPL, -&xxcfg.C_APPL[0].ApplCall, &xxcfg.C_APPL[1].ApplCall, &xxcfg.C_APPL[2].ApplCall, &xxcfg.C_APPL[3].ApplCall, -&xxcfg.C_APPL[4].ApplCall, &xxcfg.C_APPL[5].ApplCall, &xxcfg.C_APPL[6].ApplCall, &xxcfg.C_APPL[7].ApplCall, -&xxcfg.C_APPL[0].ApplAlias, &xxcfg.C_APPL[1].ApplAlias, &xxcfg.C_APPL[2].ApplAlias, &xxcfg.C_APPL[3].ApplAlias, -&xxcfg.C_APPL[4].ApplAlias, &xxcfg.C_APPL[5].ApplAlias, &xxcfg.C_APPL[6].ApplAlias, &xxcfg.C_APPL[7].ApplAlias, -&xxcfg.C_APPL[0].ApplQual, &xxcfg.C_APPL[1].ApplQual, &xxcfg.C_APPL[2].ApplQual, &xxcfg.C_APPL[3].ApplQual, -&xxcfg.C_APPL[4].ApplQual, &xxcfg.C_APPL[5].ApplQual, &xxcfg.C_APPL[6].ApplQual, &xxcfg.C_APPL[7].ApplQual, -&xxcfg.C_BTEXT, &xxcfg.C_NETROMCALL, &xxcfg.C_C, &xxcfg.C_MAXRTT, &xxcfg.C_MAXHOPS, // IPGATEWAY= no longer allowed -&xxcfg.C_LogL4Connects, &xxcfg.C_LogAllConnects, &xxcfg.C_SaveMH, &xxcfg.C_ADIF, &xxcfg.C_EVENTS, &xxcfg.C_SaveAPRSMsgs, -&xxcfg.C_M0LTEMap, &xxcfg.C_MQTT, &xxcfg.C_MQTT_HOST, &xxcfg.C_MQTT_PORT, &xxcfg.C_MQTT_USER, &xxcfg.C_MQTT_PASS, -&xxcfg.C_L4Compress, &xxcfg.C_L4CompMaxframe, &xxcfg.C_L4CompPaclen, &xxcfg.C_L2Compress, &xxcfg.C_L2CompMaxframe, &xxcfg.C_L2CompPaclen}; /* offset for corresponding data in config file */ - -static int routine[] = -{ -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 2, -2, 0, 0, 0, 0, -3, 4, 20, 5, 6, 1, -1, 1, 1, 1, 1, -2, 1, 1, 1, 7, 1, -8, 2, 4, 2, 9, 10, -11, 1, 12, 2 , 1, -13, 13, 13, 13, -13, 13 ,13, 13, -13, 13, 13, 13, -13, 13 ,13, 13, -14, 14, 14, 14, -14, 14 ,14, 14, -15, 0, 2, 9, 9, -2, 2, 1, 2, 2, 2, -2, 2, 0, 1, 20, 20, -1, 1, 1, 1, 1, 1} ; // Routine to process param - -int PARAMLIM = sizeof(routine)/sizeof(int); -//int NUMBEROFKEYWORDS = sizeof(routine)/sizeof(int); - -//#define PARAMLIM 74 - - -static char eof_message[] = "Unexpected end of file on input\n"; - -static char *pkeywords[] = -{ -"ID", "TYPE", "PROTOCOL", "IOADDR", "INTLEVEL", "SPEED", "CHANNEL", -"BBSFLAG", "QUALITY", "MAXFRAME", "TXDELAY", "SLOTTIME", "PERSIST", -"FULLDUP", "SOFTDCD", "FRACK", "RESPTIME", "RETRIES", -"PACLEN", "CWID", "PORTCALL", "PORTALIAS", "ENDPORT", "VALIDCALLS", -"QUALADJUST", "DIGIFLAG", "DIGIPORT", "USERS" ,"UNPROTO", "PORTNUM", -"TXTAIL", "ALIAS_IS_BBS", "L3ONLY", "KISSOPTIONS", "INTERLOCK", "NODESPACLEN", -"TXPORT", "MHEARD", "CWIDTYPE", "MINQUAL", "MAXDIGIS", "PORTALIAS2", "DLLNAME", -"BCALL", "DIGIMASK", "NOKEEPALIVES", "COMPORT", "DRIVER", "WL2KREPORT", "UIONLY", -"UDPPORT", "IPADDR", "I2CBUS", "I2CDEVICE", "UDPTXPORT", "UDPRXPORT", "NONORMALIZE", -"IGNOREUNLOCKEDROUTES", "INP3ONLY", "TCPPORT", "RIGPORT", "PERMITTEDAPPLS", "HIDE", -"SMARTID", "KISSCOMMAND", "SendtoM0LTEMap", "PortFreq", "M0LTEMapInfo", "QTSMPort"}; /* parameter keywords */ - -static void * poffset[] = -{ -&xxp.ID, &xxp.TYPE, &xxp.PROTOCOL, &xxp.IOADDR, &xxp.INTLEVEL, &xxp.SPEED, &xxp.CHANNEL, -&xxp.BBSFLAG, &xxp.QUALITY, &xxp.MAXFRAME, &xxp.TXDELAY, &xxp.SLOTTIME, &xxp.PERSIST, -&xxp.FULLDUP, &xxp.SOFTDCD, &xxp.FRACK, &xxp.RESPTIME, &xxp.RETRIES, -&xxp.PACLEN, &xxp.CWID, &xxp.PORTCALL, &xxp.PORTALIAS, 0, &xxp.VALIDCALLS, -&xxp.QUALADJUST, &xxp.DIGIFLAG, &xxp.DIGIPORT, &xxp.USERS, &xxp.UNPROTO, &xxp.PORTNUM, -&xxp.TXTAIL, &xxp.ALIAS_IS_BBS, &xxp.L3ONLY, &xxp.KISSOPTIONS, &xxp.INTERLOCK, &xxp.NODESPACLEN, -&xxp.TXPORT, &xxp.MHEARD, &xxp.CWIDTYPE, &xxp.MINQUAL, &xxp.MAXDIGIS, &xxp.PORTALIAS2, &xxp.DLLNAME, -&xxp.BCALL, &xxp.DIGIMASK, &xxp.DefaultNoKeepAlives, &xxp.IOADDR, &xxp.DLLNAME, &xxp.WL2K, &xxp.UIONLY, -&xxp.IOADDR, &xxp.IPADDR, &xxp.INTLEVEL, &xxp.IOADDR, &xxp.IOADDR, &xxp.ListenPort, &xxp.NoNormalize, -&xxp.IGNOREUNLOCKED, &xxp.INP3ONLY, &xxp.TCPPORT, &xxp.RIGPORT, &xxp.PERMITTEDAPPLS, &xxp.Hide, -&xxp.SmartID, &xxp.KissParams, &xxp.SendtoM0LTEMap, &xxp.PortFreq, &xxp.M0LTEMapInfo, &xxp.QtSMPort}; /* offset for corresponding data in config file */ - -static int proutine[] = -{ -4, 5, 8, 3, 1, 1, 7, -6, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 0, 0, 0, 9, 10, -1, 13, 13, 1, 11, 1, -1, 2, 2, 12, 1, 1, -1, 7, 7, 13, 13, 0, 14, -0, 1, 2, 18, 15, 16, 2, -1, 17, 1, 1, 1, 1, 2, -2, 2, 1, 1, 19, 2, -1, 20, 1, 21, 22, 1}; /* routine to process parameter */ - -int PPARAMLIM = sizeof(proutine)/sizeof(int); - -static int endport = 0; -static int portnum = 1; -static int portindex = 0; -static int porterror = 0; -static int tncporterror = 0; -static int dedporterror = 0; -static int kissflags = 0; -static int NextAppl = 0; -static int routeindex = 0; - - -/************************************************************************/ -/* Global variables */ -/************************************************************************/ - -int paramok[100] = {0}; /* PARAMETER OK FLAG */ - -FILE *fp1; /* TEXT INPUT FILE */ - -static char s1[80]; -static char s2[80]; -static char s3[80]; -static char s4[80]; -static char s5[80]; -static char s6[80]; -static char s7[80]; -static char s8[80]; - -char commas[]=",,,,,,,,,,,,,,,,"; - -char bbscall[11]; -char bbsalias[11]; -int bbsqual; - - -extern UCHAR ConfigDirectory[260]; - -BOOL LocSpecified = FALSE; - -/************************************************************************/ -/* MAIN PROGRAM */ -/************************************************************************/ - -VOID WarnThread(); - -int LineNo = 0; - -int heading = 0; - - -BOOL ProcessConfig() -{ - int i; - char rec[MAXLINE]; - int Cfglen = sizeof(xxcfg); - struct APPLCONFIG * App; - - memset(&xxcfg, 0, sizeof(xxcfg)); - memset(&xxp, 0, sizeof(xxp)); - - heading = 0; - portnum = 1; - NextAppl = 0; - LOCATOR[0] = 0; - MAPCOMMENT[0] = 0; - routeindex = 0; - portindex = 0; - - for (i = 0; i < 70; i++) - { - if (PortConfig[i]) - { - free(PortConfig[i]); - PortConfig[i] = NULL; - } - PortDefined[i] = FALSE; - - if (RadioConfigMsg[i]) - { - free(RadioConfigMsg[i]); - RadioConfigMsg[i] = NULL; - } - } - - nextRadioPort = 0; - - TNCCONFIGTABLE = NULL; - NUMBEROFTNCPORTS = 0; - - AGWMask = 0; - - UPNPConfig = NULL; - - Consoleprintf("Configuration file Preprocessor."); - - if (ConfigDirectory[0] == 0) - { - strcpy(inputname, "bpq32.cfg"); - } - else - { - strcpy(inputname,ConfigDirectory); - strcat(inputname,"/"); - strcat(inputname, "bpq32.cfg"); - } - - if ((fp1 = fopen(inputname,"r")) == NULL) - { - Consoleprintf("Could not open file %s Error code %d", inputname, errno); - return FALSE; - } - - Consoleprintf("Using Configuration file %s",inputname); - - memset(&xxcfg, 0, sizeof(xxcfg)); - - App = (struct APPLCONFIG *)&xxcfg.C_APPL[0]; - - for (i=0; i < NumberofAppls; i++) - { - memset(App->Command, ' ', 12); - memset(App->CommandAlias, ' ', 48); - memset(App->ApplCall, ' ', 10); - memset(App->ApplAlias, ' ', 10); - - App++; - } - -// xxcfg.SaveMH = TRUE; // Default to save - - GetNextLine(rec); - - while (rec[0]) - { - decode_rec(rec); - GetNextLine(rec); - } - - if (xxcfg.C_NODECALL[0] == ' ') - { - Consoleprintf("Missing NODECALL"); - heading = 1; - } - - - paramok[6]=1; /* dont need BUFFERS */ - paramok[8]=1; /* dont need TRANSDELAY */ - paramok[13]=1; // NodeAlias - paramok[17]=1; /* dont need TNCPORTS */ - paramok[20]=1; // Or ROUTES - - paramok[32]=1; /* dont need UNPROTO */ - - paramok[35]=1; /* dont need EMS */ - paramok[37]=1; /* dont need DESQVIEW */ - paramok[38]=1; /* dont need HOSTINTERRUPT */ - - paramok[40]=1; /* or DEDHOST */ - - paramok[42]=1; /* or SIMPLE */ - - paramok[43]=1; /* or AUTOSAVE */ - - paramok[44]=1; /* or L4APPL */ - - - paramok[16]=1; // BBSCALL - paramok[14]=1; // BBSALIAS - paramok[33]=1; // BBSQUAL - paramok[34]=1; // APPLICATIONS - - if (paramok[45]==1) - { - paramok[16]=1; // APPL1CALL overrides BBSCALL - memcpy(xxcfg.C_BBSCALL, xxcfg.C_APPL[0].ApplCall, 10); - } - - if (paramok[53]==1) - { - paramok[14]=1; // APPL1ALIAS overrides BBSALIAS - memcpy(xxcfg.C_BBSALIAS, xxcfg.C_APPL[0].ApplAlias, 10); - } - - if (paramok[61]==1) - { - paramok[33]=1; // APPL1QUAL overrides BBSQUAL - xxcfg.C_BBSQUAL = xxcfg.C_APPL[0].ApplQual; - } - - - for (i=0;i<24;i++) - - paramok[45+i]=1; /* or APPLCALLS, APPLALIASS APPLQUAL */ - - paramok[69]=1; // BText optional - paramok[70]=1; // IPGateway optional - paramok[71]=1; // C_IS_CHAT optional - - paramok[72]=1; // MAXRTT optional - paramok[73]=1; // MAXHOPS optional - paramok[74]=1; // LogL4Connects optional - paramok[75]=1; // LogAllConnects optional - paramok[76]=1; // SAVEMH optional - paramok[77]=1; // ENABLEADIFLOG optional - paramok[78]=1; // EnableEvents optional - paramok[79]=1; // SaveAPRSMsgs optional - paramok[79]=1; // SaveAPRSMsgs optional - paramok[80]=1; // EnableM0LTEMap optional - paramok[81]=1; // MQTT Params - paramok[82]=1; // MQTT Params - paramok[83]=1; // MQTT Params - paramok[84]=1; // MQTT Params - paramok[85]=1; // MQTT Params - paramok[86]=1; // L4Compress - paramok[87]=1; // L4Compress Maxframe - paramok[88]=1; // L4Compress Paclen - paramok[89]=1; // L2Compress - paramok[90]=1; // L2Compress Maxframe - paramok[91]=1; // L2Compress Paclen - - - for (i=0; i < PARAMLIM; i++) - { - if (paramok[i] == 0) - { - if (heading == 0) - { - Consoleprintf(" "); - Consoleprintf("The following parameters were not correctly specified"); - heading = 1; - } - Consoleprintf(keywords[i]); - } - } - - if (portnum == 1) - { - Consoleprintf("No valid radio ports defined"); - heading = 1; - } - - if (Comment) - { - Consoleprintf("Unterminated Comment (Missing */) at line %d", CommentLine); - heading = 1; - } - - fclose(fp1); - - if (LOCATOR[0] == 0 && LocSpecified == 0 && RFOnly == 0) - { - Consoleprintf(""); - Consoleprintf("Please enter a LOCATOR statement in your BPQ32.cfg"); - Consoleprintf("If you really don't want to be on the Node Map you can enter LOCATOR=NONE"); - Consoleprintf(""); - -// _beginthread(WarnThread, 0, 0); - } - - if (heading == 0) - { - Consoleprintf("Conversion (probably) successful"); - Consoleprintf(""); - } - else - { - Consoleprintf("Conversion failed"); - return FALSE; - } - -/* - // Dump to file for debugging - - sprintf_s(inputname, sizeof(inputname), "CFG%d", time(NULL)); - - fp1 = fopen(inputname, "wb"); - - if (fp1) - { - fwrite(ConfigBuffer, 1, 100000, fp1); - fclose(fp1); - } -*/ - return TRUE; -} - -/************************************************************************/ -/* Decode PARAM= */ -/************************************************************************/ - -int decode_rec(char * rec) -{ - int i; - int cn = 1; /* RETURN CODE FROM ROUTINES */ - - char key_word[300] = ""; - char value[300] = ""; - - if (_memicmp(rec, "NODEMAPSERVER=", 14) == 0) - { - // Map reporting override - - strcpy(NodeMapServer, &rec[14]); - strlop(NodeMapServer, ' '); - - return 1; - } - - if (_memicmp(rec, "CloseOnError=", 13) == 0) - { - // Close BPQ on trapped program error - - CloseOnError = atoi(&rec[13]); - return 1; - } - - if (_memicmp(rec, "CHATMAPSERVER=", 14) == 0) - { - // Map reporting override - - strcpy(ChatMapServer, &rec[14]); - strlop(ChatMapServer, ' '); - - return 1; - } - - if (_memicmp(rec, "IPGATEWAY", 9) == 0 && rec[9] != '=') // IPGATEWAY, not IPGATEWAY= - { - // Create Embedded IPGateway Config - - // Copy all subsequent lines up to **** to a memory buffer - - char * ptr; - - PortConfig[IPConfigSlot] = ptr = malloc(50000); - - *ptr = 0; - - GetNextLine(rec); - - while (!feof(fp1)) - { - if (_memicmp(rec, "****", 3) == 0) - { - PortConfig[IPConfigSlot] = realloc(PortConfig[IPConfigSlot], (strlen(ptr) + 1)); - xxcfg.C_IP = 1; - return 0; - } - - strcat(ptr, rec); - strcat(ptr, "\r\n"); - GetNextLine(rec); - } - - Consoleprintf("Missing **** for IPGateway Config"); - heading = 1; - - return 0; - } - - if (_memicmp(rec, "PORTMAPPER", 10) == 0) - { - // Create Embedded portmapper Config - - // Copy all subsequent lines up to **** to a memory buffer - - char * ptr; - - PortConfig[PortMapConfigSlot] = ptr = malloc(50000); - - *ptr = 0; - - GetNextLine(rec); - - while (!feof(fp1)) - { - if (_memicmp(rec, "****", 3) == 0) - { - PortConfig[PortMapConfigSlot] = realloc(PortConfig[PortMapConfigSlot], (strlen(ptr) + 1)); - xxcfg.C_PM = 1; - return 0; - } - - strcat(ptr, rec); - strcat(ptr, "\r\n"); - GetNextLine(rec); - } - - Consoleprintf("Missing **** for Portmapper Config"); - heading = 1; - - return 0; - } - - if (_memicmp(rec, "APRSDIGI", 8) == 0) - { - // Create Embedded APRS Config - - // Copy all subsequent lines up to **** to a memory buffer - - char * ptr; - - PortConfig[APRSConfigSlot] = ptr = malloc(50000); - - *ptr = 0; - - // Don't use GetNextLine - we need to keep ; in messages - - fgets(rec,MAXLINE,fp1); - LineNo++; - - while (!feof(fp1)) - { - if (_memicmp(rec, "****", 3) == 0) - { - PortConfig[APRSConfigSlot] = realloc(PortConfig[APRSConfigSlot], (strlen(ptr) + 1)); - return 0; - } - if (strlen(rec) > 1) - { - if (memcmp(rec, "/*", 2) == 0) - { - Comment = TRUE; - CommentLine = LineNo; - goto NextAPRS; - } - else if (memcmp(rec, "*/", 2) == 0) - { - Comment = FALSE; - goto NextAPRS; - } - } - - if (Comment) - goto NextAPRS; - - strcat(ptr, rec); - strcat(ptr, "\r\n"); -NextAPRS: - fgets(rec,MAXLINE,fp1); - LineNo++; - } - - if (_memicmp(rec, "****", 3) == 0) - return 0; // No Newline after *** - - Consoleprintf("Missing **** for APRS Config"); - heading = 1; - - return 0; - } - - if (_memicmp(rec, "PASSWORD", 8) == 0) - { - // SYSOP Password - - if (strlen(rec) > 88) rec[88] = 0; - - _strupr(rec); - - strcpy(PWTEXT, &rec[9]); - return 0; - } - -#ifdef LINBPQ - - if (_memicmp(rec, "LINMAIL", 7) == 0) - { - // Enable Mail on Linux Verdion - - IncludesMail = TRUE; - - return 0; - } - - if (_memicmp(rec, "LINCHAT", 7) == 0) - { - // Enable Chat on Linux Verdion - - IncludesChat = TRUE; - - return 0; - } -#endif - - if (_memicmp(rec, "ENABLEAIS", 9) == 0) - { - needAIS = TRUE; - return 0; - } - - if (_memicmp(rec, "ENABLEADSB", 9) == 0) - { - needADSB = TRUE; - return 0; - } - - if (_memicmp(rec, "HFCTEXT", 7) == 0) - { - // HF only CTEXT (normlly short to reduce traffic) - - if (strlen(rec) > 87) rec[87] = 0; - strcpy(HFCTEXT, &rec[8]); - HFCTEXTLEN = (int)(strlen(HFCTEXT)); - HFCTEXT[HFCTEXTLEN - 1] = '\r'; - return 0; - } - - if (_memicmp(rec, "LOCATOR", 7) == 0) - { - // Station Maidenhead Locator or Lat/Long - - char * Context; - char * ptr1 = strtok_s(&rec[7], " ,=\t\n\r:", &Context); - char * ptr2 = strtok_s(NULL, " ,=\t\n\r:", &Context); - - LocSpecified = TRUE; - - if (_memicmp(&rec[8], "NONE", 4) == 0) - return 0; - - if (_memicmp(&rec[8], "XXnnXX", 6) == 0) - return 0; - - if (ptr1) - { - strcpy(LOCATOR, ptr1); - if (ptr2) - { - strcat(LOCATOR, ":"); - strcat(LOCATOR, ptr2); - ToLOC(atof(ptr1), atof(ptr2), LOC); - LatFromLOC = atof(ptr1); - LonFromLOC = atof(ptr2); - - } - else - { - if (strlen(ptr1) == 6) - { - strcpy(LOC, ptr1); - FromLOC(LOC, &LatFromLOC, &LonFromLOC); - // Randomise in square - LatFromLOC += ((rand() / 24.0) / RAND_MAX); - LonFromLOC += ((rand() / 12.0) / RAND_MAX); - - } - } - } - return 0; - } - - if (_memicmp(rec, "MAPCOMMENT", 10) == 0) - { - // Additional Info for Node Map - - char * ptr1 = &rec[11]; - char * ptr2 = MAPCOMMENT; - - while (*ptr1) - { - if (*ptr1 == ',') - { - *ptr2++ = '&'; - *ptr2++ = '#'; - *ptr2++ = '4'; - *ptr2++ = '4'; - *ptr2++ = ';'; - } - else - *(ptr2++) = *ptr1; - - ptr1++; - - if ((ptr2 - MAPCOMMENT) > 248) - break; - } - - *ptr2 = 0; - - return 0; - } - - // Process BRIDGE statement - - if (_memicmp(rec, "BRIDGE", 6) == 0) - { - int DigiTo; - int Port; - char * Context; - char * p_value; - char * ptr; - - p_value = strtok_s(&rec[7], ",=\t\n\r", &Context); - - Port = atoi(p_value); - - if (Port > MaxBPQPortNo) - return FALSE; - - if (Context == NULL) - return FALSE; - - ptr = strtok_s(NULL, ",\t\n\r", &Context); - - while (ptr) - { - DigiTo = atoi(ptr); - - if (DigiTo > MaxBPQPortNo) - return 0; - - if (Port != DigiTo) // Not to our port! - xxcfg.CfgBridgeMap[Port][DigiTo] = TRUE; - - ptr = strtok_s(NULL, " ,\t\n\r", &Context); - } - - return 0; - } - - - // AGW Emulator Params - - if (_memicmp(rec, "AGWPORT", 7) == 0) - { - AGWPort = atoi(&rec[8]); - return 0; - } - - if (_memicmp(rec, "AGWSESSIONS", 11) == 0) - { - AGWSessions = atoi(&rec[12]); - return 0; - } - - if (_memicmp(rec, "AGWMASK", 7) == 0) - { - AGWMask = strtol(&rec[8], 0, 0); - return 0; - } - - if (_memicmp(rec, "AGWAPPL", 7) == 0) - { - AGWMask |= 1 << (strtol(&rec[8], 0, 0) - 1); - return 0; - } - - if (_memicmp(rec, "AGWLOOPMON", 10) == 0) - { - LoopMonFlag = strtol(&rec[11], 0, 0); - return 0; - } - if (_memicmp(rec, "AGWLOOPTX", 9) == 0) - { - Loopflag = strtol(&rec[10], 0, 0); - return 0; - } - - if (_memicmp(rec, "APPLICATION ", 12) == 0 || _memicmp(rec, "APPLICATION=", 12) == 0) - { - // New Style APPLICATION Definition - - char save[300]; - - strcpy(save, rec); // Save in case error - - if (!ProcessAPPLDef(&rec[12])) - { - Consoleprintf("Invalid Record %s", save); - heading = 1; - } - else - paramok[34]=1; // Got APPLICATIONS - - return 0; - } - - if (_memicmp(rec, "EXCLUDE=", 8) == 0) - { - char * ptr2 = &rec[8]; - UCHAR * ptr3 = xxcfg.C_EXCLUDE; - - _strupr(ptr2); - while (*(ptr2) > 32) - { - ConvToAX25(ptr2, ptr3); - ptr3 += 7; - - if (strchr(ptr2, ',')) - { - ptr2 = strchr(ptr2, ','); - ptr2++; - } - else - break; - - if (ptr3 > &xxcfg.C_EXCLUDE[63]) - { - Consoleprintf("Too Many Excluded Calls"); - heading = 1; - break; - } - } - - return 0; - } - if (_memicmp(rec, "RADIO", 5) == 0) - { - if (strlen(rec) > 11) - { - RadioConfigMsg[nextRadioPort++] = _strdup(rec); - return 0; - } - else - { - // Multiline config, ending in **** - - char * rptr; - - RadioConfigMsg[nextRadioPort] = rptr = zalloc(50000); - - strcpy(rptr, rec); - - GetNextLine(rec); - - while(!feof(fp1)) - { - if (memcmp(rec, "***", 3) == 0) - { - RadioConfigMsg[nextRadioPort] = realloc(RadioConfigMsg[nextRadioPort], (strlen(rptr) + 1)); - nextRadioPort++; - return 0; - } - strcat(rptr, rec); - GetNextLine(rec); - } - } - } - - if (_memicmp(rec, "UPNP ", 5) == 0) - { - struct UPNP * Entry = (struct UPNP *)zalloc(sizeof(struct UPNP)); - char * ptr, * context; - char copy[256]; - - strcpy(copy, rec); - - ptr = strtok_s(&rec[5], ", ", &context); - - if (ptr) - Entry->Protocol = _strdup(ptr); - - ptr = strtok_s(NULL, ", ", &context); - - if (ptr) - Entry->LANport = Entry->WANPort = _strdup(ptr);; - - ptr = strtok_s(NULL, ", ", &context); - - if (ptr) - Entry->WANPort = _strdup(ptr);; - - if (Entry->LANport) - { - Entry->Next = UPNPConfig; - UPNPConfig = Entry; - return 1; - } - - Consoleprintf("Bad UPNP Line %s", copy); - heading = 1; - - return 0; - } - - - if (_memicmp("MQTT_HOST=", rec, 10) == 0) - { - strcpy(xxcfg.C_MQTT_HOST, &rec[10]); - xxcfg.C_MQTT_HOST[strlen(xxcfg.C_MQTT_HOST)-1] = '\0'; - return 0; - } - if (_memicmp("MQTT_USER=", rec, 10) == 0) - { - strcpy(xxcfg.C_MQTT_USER, &rec[10]); - xxcfg.C_MQTT_USER[strlen(xxcfg.C_MQTT_USER)-1] = '\0'; - return 0; - } - if (_memicmp("MQTT_PASS=", rec, 10) == 0) - { - strcpy(xxcfg.C_MQTT_PASS, &rec[10]); - xxcfg.C_MQTT_PASS[strlen(xxcfg.C_MQTT_PASS)-1] = '\0'; - return 0; -} - - - if (xindex(rec,"=") >= 0) - sscanf(rec,"%[^=]=%s",key_word,value); - else - sscanf(rec,"%s",key_word); - -/************************************************************************/ -/* SEARCH FOR KEYWORD IN TABLE */ -/************************************************************************/ - - for (i=0; i < PARAMLIM && _stricmp(keywords[i],key_word) != 0 ; i++) - ; - - if (i == PARAMLIM) - Consoleprintf("bpq32.cfg line no %d not recognised - Ignored: %s" ,LineNo, rec); - else - { - - switch (routine[i]) - { - case 0: - cn = callsign((char *)offset[i], value, rec); /* CALLSIGNS */ - break; - - case 1: - cn = int_value((short *)offset[i], value, rec); /* INTEGER VALUES */ - break; - - case 2: - cn = bin_switch((char *)offset[i], value, rec); /* 0/1 SWITCHES */ - break; - - case 3: - cn = tncports(i); /* VIRTUAL COMBIOS PORTS */ - break; - - case 4: - cn = dotext((char *)offset[i], key_word, 510); /* TEXT PARMS */ - break; - - case 20: - cn = dotext((char *)offset[i], key_word, 2000); /* INFO TEXT PARM */ - break; - - case 5: - cn = routes(i); /* ROUTES TO LOCK IN */ - break; - - case 6: - cn = ports(i); /* PORTS DEFINITION */ - break; - - case 7: - Consoleprintf("Obsolete Record %s ignored",rec); - Consoleprintf("UNPROTO address should now be specified in PORT definition"); - - break; - - case 8: - cn = applstrings(i,value,rec); /* APPLICATIONS LIST */ - break; - - case 9: - cn = dec_switch((char *)offset[i],value,rec); /* 0/9 SWITCHES */ - break; - - case 10: - cn = dolinked(i,value,rec); /* SINGLE CHAR */ - break; - - case 11: - Consoleprintf("Obsolete Record %s ignored", rec); - break; - - case 12: - cn = simple(i); /* Set up basic L2 system*/ - break; - - case 13: - cn = applcallsign(i,value,rec); /* CALLSIGNS */ - break; - - case 14: - cn = appl_qual(i,value,rec); /* INTEGER VALUES */ - break; - - case 15: - cn = dotext((char *)offset[i], key_word, 120); /* BTEXT */ - break; - } - - paramok[i] = cn; - } - - return 0; -} - -/************************************************************************/ -/* CALLSIGNS */ -/************************************************************************/ -int applcallsign(int i, char * value, char * rec) -{ - char * val = (char *)offset[i]; - - if (call_check_internal(value)) - { - // Invalid - - return 0; - } - - memcpy(val, value, 10); - - if (i==45) - strcpy(bbscall,value); - if (i==53) - strcpy(bbsalias,value); - - return 1; -} - -int appl_qual(int i, char * value, char * rec) -{ - int j, k; - int * val = (int *)offset[i]; - - k = sscanf(value," %d",&j); - - if (k != 1) - { - Consoleprintf("Invalid numerical value "); - Consoleprintf("%s\r\n",rec); - return(0); - } - - if (i==61) bbsqual=j; - - *val = j; - return(1); -} - - -int callsign(char * ptr, char * value, char * rec) -{ - if (call_check(value, ptr) == 1) - { - Consoleprintf("%s",rec); - return(0); - } - - return(1); -} - - -/************************************************************************/ -/* VALIDATE INT VALUES */ -/************************************************************************/ - -int int_value(short * val, char value[], char rec[]) -{ - int j,k; - - k = sscanf(value," %d",&j); - - if (k != 1) - { - Consoleprintf("Invalid numerical value "); - Consoleprintf("%s\r\n",rec); - return(0); - } - - val[0] = j; - return(1); -} - -int64_t int64_value(int64_t * val, char value[], char rec[]) -{ - *val = strtoll(value, NULL, 10); - return(1); -} - -/************************************************************************/ -/* VALIDATE HEX INT VALUES */ -/************************************************************************/ - -int hex_value(int * val, char value[], char rec[]) -{ - int j = -1, k = 0; - - k = sscanf(value, " %xH", &j); - - if (j < 0) - { - Consoleprintf("Bad Hex Value"); - Consoleprintf("%s\r\n", rec); - return(0); - } - - val[0] = j; - return(1); -} -; - -/************************************************************************/ -/* VALIDATE BINARY SWITCH DATA AND WRITE TO FILE */ -/************************************************************************/ - -int bin_switch(char * val, char * value, char * rec) -{ - int value_int; - - value_int = atoi(value); - - if (value_int == 0 || value_int == 1) - { - val[0] = value_int; - return 1; - } - else - { - Consoleprintf("Invalid switch value, must be either 0 or 1"); - Consoleprintf("%s\r\n",rec); - return(0); - } -} -/* -; single byte decimal -*/ -int dec_switch (char * val, char * value, char * rec) -{ - int value_int; - - value_int = atoi(value); - - if (value_int < 256) - { - val[0] = value_int; - return 1; - } - else - { - Consoleprintf("Invalid value, must be between 0 and 255"); - Consoleprintf("%s\r\n",rec); - return(0); - } -} - - -int applstrings(int i, char * value, char * rec) -{ - char appl[250]; // In case trailing spaces - char * ptr1; - char * ptr2; - struct APPLCONFIG * App; - int j; - - // strcat(rec,commas); // Ensure 16 commas - - ptr1 = &rec[13]; // skip APPLICATIONS= - - App = &xxcfg.C_APPL[0]; - - while (NextAppl++ < NumberofAppls) - { - memset(appl, ' ', 249); - appl[249] = 0; - - ptr2=appl; - - j = *ptr1++; - - while (j != ',' && j) - { - *(ptr2++) = toupper(j); - j = *ptr1++; - } - - ptr2 = strchr(appl, '/'); - - if (ptr2) - { - // Command has an Alias - - *ptr2++ = 0; - memcpy(App->CommandAlias, ptr2, 48); - strcat(appl, " "); - } - - memcpy(App->Command, appl, 12); - xxcfg.C_BBS = 1; - - if (*(ptr1 - 1) == 0) - return 1; - - App++; - } - return(1); -} - - -/************************************************************************/ -/* USE FOR FREE FORM TEXT IN MESSAGES */ -/************************************************************************/ - -int dotext(char * val, char * key_word, int max) -{ - int len = 0; - char * ptr; - - char rec[MAXLINE]; - - GetNextLine(rec); - - if (xindex(rec,"***") == 0) - *val = '\r'; - - while (xindex(rec,"***") != 0 && !feof(fp1)) - { - ptr = strchr(rec, 10); - if (ptr) *ptr = 0; - ptr = strchr(rec, 13); - if (ptr) *ptr = 0; - - strcat(rec, "\r"); - - len += (int)strlen(rec); - - if (len <= max) - { - strcpy(val, rec); - val += (int)strlen(rec); - } - - fgets(rec,MAXLINE,fp1); - LineNo++; - } - - if (len > max) - { - Consoleprintf("Text too long: %s (max %d\r\n",key_word, max); - return(0); - } - - if (feof(fp1)) - return(0); - else - return(1); -} - - -/************************************************************************/ -/* CONVERT PRE-SET ROUTES PARAMETERS TO BINARY */ -/************************************************************************/ - -int routes(int i) -{ - struct ROUTECONFIG * Route; - - int err_flag = 0; - int main_err = 0; - - char rec[MAXLINE]; - - - GetNextLine(rec); - - while (xindex(rec,"***") != 0 && !feof(fp1)) - { - char Param[8][256]; - char * ptr1, * ptr2; - int n = 0, inp3 = 0; - - Route = &xxcfg.C_ROUTE[routeindex++]; - - // strtok and sscanf can't handle successive commas, so split up usig strchr - - memset(Param, 0, 2048); - strlop(rec, 13); - strlop(rec, ';'); - - ptr1 = rec; - - while (ptr1 && *ptr1 && n < 8) - { - ptr2 = strchr(ptr1, ','); - if (ptr2) *ptr2++ = 0; - - strcpy(&Param[n++][0], ptr1); - ptr1 = ptr2; - while(ptr1 && *ptr1 && *ptr1 == ' ') - ptr1++; - } - - strcpy(Route->call, &Param[0][0]); - - Route->quality = atoi(Param[1]); - Route->port = atoi(Param[2]); - Route->pwind = atoi(Param[3]); - Route->pfrack = atoi(Param[4]); - Route->ppacl = atoi(Param[5]); - inp3 = atoi(Param[6]); - Route->farQual = atoi(Param[7]); - - if (Route->farQual < 0 || Route->farQual > 255) - { - Consoleprintf("Remote Quality must be between 0 and 255"); - Consoleprintf("%s\r\n",rec); - - err_flag = 1; - } - - if (Route->quality < 0 || Route->quality > 255) - { - Consoleprintf("Quality must be between 0 and 255"); - Consoleprintf("%s\r\n",rec); - - err_flag = 1; - } - - if (Route->port < 1 || Route->port > MaxBPQPortNo) - { - Consoleprintf("Port number must be between 1 and 64"); - Consoleprintf("%s\r\n",rec); - err_flag = 1; - } - - // Use top bit of window as INP3 Flag, next as NoKeepAlive - - if (inp3 & 1) - Route->pwind |= 0x80; - - if (inp3 & 2) - Route->pwind |= 0x40; - - if (err_flag == 1) - { - Consoleprintf("%s\r\n",rec); - main_err = 1; - err_flag = 0; - } - GetNextLine(rec); - } - - if (routeindex > MaxLockedRoutes) - { - routeindex--; - Consoleprintf("Route information too long "); - main_err = 1; - } - - if (feof(fp1)) - { - Consoleprintf(eof_message); - return(0); - } - - if (main_err == 1) - return(0); - else - return(1); -} - - -/************************************************************************/ -/* CONVERT PORT DEFINITIONS TO BINARY */ -/************************************************************************/ -int hw; // Hardware type -int LogicalPortNum; // As set by PORTNUM - -int ports(int i) -{ - char rec[MAXLINE]; - endport=0; - porterror=0; - kissflags=0; - - xxp.PORTNUM = portnum; - - LogicalPortNum = portnum; - - if (LogicalPortNum > MaxBPQPortNo) - { - Consoleprintf("Port Number must be between 1 and %d", MaxBPQPortNo); - heading = 1; - } - - xxp.SendtoM0LTEMap = 1; // Default to enabled - - while (endport == 0 && !feof(fp1)) - { - GetNextLine(rec); - decode_port_rec(rec); - } - if (porterror != 0) - { - Consoleprintf("Error in port definition"); - return(0); - } - - if (PortDefined[LogicalPortNum]) // Already defined? - { - Consoleprintf("Port %d already defined", LogicalPortNum); - heading = 1; - } - - PortDefined[LogicalPortNum] = TRUE; - - xxp.KISSOPTIONS = kissflags; - - // copy Port Config to main config - - memcpy(&xxcfg.C_PORT[portindex++], &xxp, sizeof(xxp)); - memset(&xxp, 0, sizeof(xxp)); - - portnum++; - - return(1); - -} - - -int tncports(int i) -{ - char rec[MAXLINE]; - endport=0; - tncporterror=0; - - TNC2ENTRY = zalloc(sizeof(struct TNCDATA)); - - TNC2ENTRY->APPLFLAGS = 6; - TNC2ENTRY->PollDelay = 1; - - while (endport == 0 && !feof(fp1)) - { - GetNextLine(rec); - decode_tnc_rec(rec); - } - if (tncporterror != 0) - { - Consoleprintf("Error in TNC PORT definition"); - free (TNC2ENTRY); - return(0); - } - - C_Q_ADD_NP(&TNCCONFIGTABLE, TNC2ENTRY); // Add to chain - - NUMBEROFTNCPORTS++; - - return(1); - - -} - - -/************************************************************************/ -/* MISC FUNCTIONS */ -/************************************************************************/ - -/************************************************************************/ -/* FIND OCCURENCE OF ONE STRING WITHIN ANOTHER */ -/************************************************************************/ - -int xindex(char s[], char t[]) -{ - int i, j ,k; - - for (i=0; s[i] != '\0'; i++) - { - for (j=i, k=0; t[k] != '\0' && s[i] == t[k]; j++, k++) - ; - if (t[k] == '\0') - return(i); - } - return(-1); -} - - -/************************************************************************/ -/* FIND FIRST OCCURENCE OF A CHARACTER THAT IS NOT c */ -/************************************************************************/ - -int verify(char s[], char c) -{ - int i; - - for (i = 0; s[i] != '\0'; i++) - if (s[i] != c) - return(i); - - return(-1); -} - -/************************************************************************/ -/* GET NEXT LINE THAT ISN'T BLANK OR IS A COMMENT LINE */ -/************************************************************************/ - -// Returns an empty string to indicate end of config - -// Modified Aril 2020 to allow #include of file fragments - -FILE * savefp = NULL; -int saveLineNo; -char includefilename[250]; - -int GetNextLine(char *rec) -{ - int i, j; - char * ret; - char * ptr, *context; - - while (TRUE) - { - ret = fgets(rec,MAXLINE,fp1); - LineNo++; - - if (ret == NULL) - { - if (savefp) - { - // we have reached eof on an include file - switch back - - fclose(fp1); - fp1 = savefp; - savefp = NULL; - LineNo = saveLineNo; - continue; - } - - rec[0] = 0; - return 0; // return end of config - } - - for (i=0; rec[i] != '\0'; i++) - if (rec[i] == '\t' || rec[i] == '\n' || rec[i] == '\r') - rec[i] = ' '; - - - - j = verify(rec,' '); - - if (j > 0) - { - // Remove Leading Spaces - - for (i=0; rec[j] != '\0'; i++, j++) - rec[i] = rec[j]; - - rec[i] = '\0'; - } - - if (stristr(rec,"WebTermCSS") == 0 && stristr(rec,"HybridCoLocatedRMS") == 0 && stristr(rec,"HybridFrequencies") == 0) // Needs ; in string - strlop(rec, ';'); - else - j = j; - - if (strlen(rec) > 1) - if (memcmp(rec, "/*",2) == 0) - { - Comment = TRUE; - CommentLine = LineNo; - } - else - if (memcmp(rec, "*/",2) == 0) - { - rec[0] = 32; - rec[1] = 0; - Comment = FALSE; - } - - if (Comment) - { - rec[0] = 32; - rec[1] = 0; - continue; - } - - // remove trailing spaces - - while(strlen(rec) && rec[strlen(rec) - 1] == ' ') - rec[strlen(rec) - 1] = 0; - - strcat(rec, " "); - - ptr = strtok_s(rec, " ", &context); - - // Put one back - - if (ptr) - { - if (context) - { - ptr[strlen(ptr)] = ' '; - } - rec = ptr; - - // look for #include - - if (_memicmp(rec, "#include ", 9) == 0) - { - savefp = fp1; - - if (BPQDirectory[0] == 0) - { - strcpy(includefilename, &rec[9]); - } - else - { - strcpy(includefilename,BPQDirectory); - strcat(includefilename,"/"); - strcat(includefilename, &rec[9]); - } - - if ((fp1 = fopen(includefilename,"r")) == NULL) - { - Consoleprintf("Could not open #include file %s Error code %d", includefilename, errno); - fp1 = savefp; - savefp = NULL; - } - else - { - saveLineNo = LineNo; - LineNo = 0; - } - continue; // get next line - } - return 0; - } - } - - // Should never reach this - - return 0; -} - - -/************************************************************************/ -/* TEST VALIDITY OF CALLSIGN */ -/************************************************************************/ - -int call_check_internal(char * callsign) -{ - char call[20]; - int ssid; - int err_flag = 0; - int i; - - if (xindex(callsign,"-") > 0) /* There is an SSID field */ - { - sscanf(callsign,"%[^-]-%d",call,&ssid); - if (strlen(call) > 6) - { - Consoleprintf("Callsign too long, 6 characters before SSID"); - Consoleprintf("%s\r\n",callsign); - err_flag = 1; - } - if (ssid < 0 || ssid > 15) - { - Consoleprintf("SSID out of range, must be between 0 and 15"); - Consoleprintf("%s\r\n",callsign); - err_flag = 1; - } - } - else /* No SSID field */ - { - if (strlen(callsign) > 6) - { - Consoleprintf("Callsign too long, 6 characters maximum"); - Consoleprintf("%s\r\n",callsign); - err_flag = 1; - } - } - - strcat(callsign," "); - callsign[10] = '\0'; - for (i=0; i< 10; i++) - callsign[i]=toupper(callsign[i]); - - return(err_flag); -} - -int call_check(char * callsign, char * loc) -{ - int err = call_check_internal(callsign); - memcpy(loc, callsign, 10); - return err; -} - - -/* Process UNPROTO string allowing VIA */ - -char workstring[80]; - -int callstring(int i, char * value, char * rec) -{ - char * val = (char *)poffset[i]; - size_t j = (int)strlen(value); - - memcpy(val, value, j); - return 1; -} - -/* - RADIO PORT PROCESSING -*/ - - -int decode_port_rec(char * rec) -{ - int i; - int cn = 1; /* RETURN CODE FROM ROUTINES */ - uint32_t IPADDR; -#ifdef WIN32 - WSADATA WsaData; // receives data from WSAStartupproblem -#endif - char key_word[30]=""; - char value[300]=""; - - if (_memicmp(rec, "CONFIG", 6) == 0) - { - // Create Embedded PORT Config - - // Copy all subseuent lines up to ENDPORT to a memory buffer - - char * ptr; - int i; - - if (LogicalPortNum > 64) - { - Consoleprintf("Portnum %d is invalid", LogicalPortNum); - LogicalPortNum = 0; - } - - PortConfig[LogicalPortNum] = ptr = malloc(50000); - *ptr = 0; - - GetNextLine(rec); - - while (!feof(fp1)) - { - if (_memicmp(rec, "ENDPORT", 7) == 0) - { - PortConfig[LogicalPortNum] = realloc(PortConfig[LogicalPortNum], (strlen(ptr) + 1)); - endport = 1; - return 0; - } - - i = (int)strlen(rec); - i--; - - while(i > 1) - { - if (rec[i] == ' ') - rec[i] = 0; // Remove trailing spaces - else - break; - - i--; - } - - // Pick out RIGCONFIG Records - - if (_memicmp(rec, "RIGCONTROL", 10) == 0) - { - // RIGCONTROL COM60 19200 ICOM IC706 5e 4 14.103/U1w 14.112/u1 18.1/U1n 10.12/l1 - - // Convert to new format (RADIO Interlockno); - - int Interlock = xxp.INTERLOCK; - char radio[16]; - - if (Interlock == 0) // Replace with dummy - { - Interlock = xxp.INTERLOCK = nextDummyInterlock; - nextDummyInterlock++; - } - - sprintf(radio, "RADIO %d ", Interlock); - memcpy(rec, radio, 10); - - if (strlen(rec) > 15) - { - RadioConfigMsg[nextRadioPort++] = _strdup(rec); - } - else - { - // Multiline config, ending in **** - - char * rptr; - - RadioConfigMsg[nextRadioPort] = rptr = zalloc(50000); - - strcpy(rptr, radio); - - GetNextLine(rec); - - while(!feof(fp1)) - { - if (memcmp(rec, "***", 3) == 0) - { - RadioConfigMsg[nextRadioPort] = realloc(RadioConfigMsg[nextRadioPort], (strlen(rptr) + 1)); - nextRadioPort++; - break; - } - strcat(rptr, rec); - GetNextLine(rec); - } - } - } - else - { - strcat(ptr, rec); - strcat(ptr, "\r\n"); - } - GetNextLine(rec); - } - - Consoleprintf("Missing ENDPORT for Port %d", portnum); - heading = 1; - - return 0; - } - - if (xindex(rec,"=") >= 0) - sscanf(rec,"%[^=]=%s",key_word,value); - else - sscanf(rec,"%s",key_word); - - if (_stricmp(key_word, "portnum") == 0) - { - // Save as LogicalPortNum - - LogicalPortNum = atoi(value); - } - - if (_stricmp(key_word, "XDIGI") == 0) - { - // Cross Port Digi definition - - // XDIGI=CALL,PORT,UI - - struct XDIGI * Digi = zalloc(sizeof(struct XDIGI)); // Chain - char * call, * pport, * Context; - - call = strtok_s(value, ",", &Context); - pport = strtok_s(NULL, ",", &Context); - - if (call && pport && ConvToAX25(call, Digi->Call)) - { - Digi->Port = atoi(pport); - if (Digi->Port) - { - if (Context) - { - _strupr(Context); - if (strstr(Context, "UI")) - Digi->UIOnly = TRUE; - } - - // Add to chain - - if (xxp.XDIGIS) - Digi->Next = xxp.XDIGIS; - - xxp.XDIGIS = Digi; - return 0; - } - } - Consoleprintf("Invalid XDIGI Statement %s", rec); - porterror = 1; - return 0; - } - - -/************************************************************************/ -/* SEARCH FOR KEYWORD IN TABLE */ -/************************************************************************/ - - for (i=0; i < PPARAMLIM && _stricmp(pkeywords[i],key_word) != 0 ; i++) - ; - - if (i == PPARAMLIM) - Consoleprintf("Source record not recognised - Ignored:%s\r\n",rec); - else - { - - switch (proutine[i]) - { - - case 0: - cn = callsign((char *)poffset[i], value, rec); /* CALLSIGNS */ - break; - - case 1: - cn = int_value((short *)poffset[i], value, rec); /* INTEGER VALUES */ - break; - - case 2: - cn = bin_switch((char *)poffset[i], value, rec); /* 0/1 SWITCHES */ - break; - - case 3: - cn = hex_value((int *)poffset[i], value, rec); /* HEX NUMBERS */ - break; - - case 4: - cn = doid(i,value,rec); /* ID PARMS */ - break; - - case 5: - cn = hwtypes(i,value,rec); /* HARDWARE TYPES */ - break; - - case 6: - cn = bbsflag(i,value,rec); - break; - - case 7: - cn = channel(i,value,rec); - break; - - case 8: - cn = protocols(i,value,rec); - break; - - case 10: - cn = validcalls(i,value,rec); - break; - - case 11: - cn = callstring(i,value,rec); - break; - - case 12: - cn = kissoptions(i,value,rec); - break; - - case 13: - cn = dec_switch((char *)poffset[i], value, rec); /* 0/9 SWITCHES */ - break; - - case 14: - cn = dodll(i,value,rec); /* DLL PARMS */ - break; - - case 15: - cn = doDriver(i,value,rec); /* DLL PARMS */ - break; - - case 16: - - xxp.WL2K = DecodeWL2KReportLine(rec); - break; - - case 17: - - // IP Address for KISS over UDP - -#ifdef WIN32 - WSAStartup(MAKEWORD(2, 0), &WsaData); -#endif - IPADDR = inet_addr(&rec[7]); - memcpy(&xxp.IPADDR, &IPADDR, 4); -#ifdef WIN32 - WSACleanup(); -#endif - break; - - case 18: - cn = doSerialPortName(i,value,rec); // COMPORT - break; - - case 19: - cn = doPermittedAppls(i,value,rec); // Permitted Apps - break; - - case 20: - cn = doKissCommand(i, value, rec); // Permitted Apps - break; - - case 21: - cn = int64_value(poffset[i], value, rec); /* INTEGER VALUES */ - break; - - case 22: - xxp.M0LTEMapInfo = _strdup(value); - cn = 1; - break; - - case 9: - - cn = 1; - endport=1; - - break; - } - } - if (cn == 0) porterror=1; - - return 0; -} - - -int doid(int i, char value[], char rec[]) -{ - unsigned int j; - for (j = 3;( j < (unsigned int)strlen(rec)+1); j++) - - workstring[j-3] = rec[j]; - - // Remove trailing spaces before checking length - - i = (int)strlen(workstring); - i--; - - while(i > 1) - { - if (workstring[i] == ' ') - workstring[i] = 0; // Remove trailing spaces - else - break; - - i--; - } - - if (i > 29) - { - Consoleprintf("Port description too long - Truncated"); - Consoleprintf("%s\r\n",rec); - } - strcat(workstring," "); - workstring[30] = '\0'; - - memcpy(xxp.ID, workstring, 30); - return(1); -} - -int dodll(int i, char value[], char rec[]) -{ - unsigned int j; - - strlop(rec, ' '); - for (j = 8;( j < (unsigned int)strlen(rec)+1); j++) - workstring[j-8] = rec[j]; - - if (j > 24) - { - Consoleprintf("DLL name too long - Truncated"); - Consoleprintf("%s\r\n",rec); - - } - - _strupr(workstring); - strcat(workstring," "); - - memcpy(xxp.DLLNAME, workstring, 16); - xxp.TYPE = 16; // External - - if (strstr(xxp.DLLNAME, "TELNET") || strstr(xxp.DLLNAME, "AXIP")) - RFOnly = FALSE; - - return(1); -} - -int doDriver(int i, char * value, char * rec) -{ - unsigned int j; - for (j = 7;( j < (unsigned int)strlen(rec)+1); j++) - workstring[j-7] = rec[j]; - - if (j > 23) - { - Consoleprintf("Driver name too long - Truncated"); - Consoleprintf("%s\r\n",rec); - } - - _strupr(workstring); - strcat(workstring," "); - - memcpy(xxp.DLLNAME, workstring, 16); - xxp.TYPE = 16; // External - - // Set some defaults in case HFKISS - - xxp.CHANNEL = 'A'; - xxp.FRACK = 7000; - xxp.RESPTIME = 1000; - xxp.MAXFRAME = 4; - xxp.RETRIES = 6; - - if (strstr(xxp.DLLNAME, "TELNET") || strstr(xxp.DLLNAME, "AXIP")) - RFOnly = FALSE; - - return 1; -} -int IsNumeric(char *str) -{ - while(*str) - { - if(!isdigit(*str)) - return 0; - str++; - } - - return 1; -} - - -int doSerialPortName(int i, char * value, char * rec) -{ - rec += 8; - - if (strlen(rec) > 250) - { - Consoleprintf("Serial Port Name too long - Truncated"); - Consoleprintf("%s\r\n",rec); - rec[250] = 0; - } - - strlop(rec, ' '); - - if (IsNumeric(rec)) - xxp.IOADDR = atoi(rec); - else - xxp.SerialPortName = _strdup(rec); - - return 1; -} - -int doPermittedAppls(int i, char * value, char * rec) -{ - unsigned int Mask = 0; - char * Context; - char * ptr1 = strtok_s(value, " ,=\t\n\r", &Context); - - // Param is a comma separated list of Appl Numbers allowed to connect on this port - - while (ptr1 && ptr1[0]) - { - Mask |= 1 << (atoi(ptr1) - 1); - ptr1 = strtok_s(NULL, " ,=\t\n\r", &Context); - } - - xxp.HavePermittedAppls = 1; // indicate used - xxp.PERMITTEDAPPLS = Mask; - - return 1; -} -int doKissCommand(int i, char * value, char * rec) -{ - // Param is kiss command and any operands as decimal bytes - - xxp.KissParams = _strdup(strlop(rec, '=')); - return 1; -} - - -int hwtypes(int i, char value[], char rec[]) -{ - hw = 255; - if (_stricmp(value,"ASYNC") == 0) - { - // Set some defaults - - xxp.CHANNEL = 'A'; - xxp.FRACK = 7000; - xxp.RESPTIME = 1000; - xxp.MAXFRAME = 4; - xxp.RETRIES = 6; - hw = 0; - } - - if (_stricmp(value,"PC120") == 0) - hw = 2; - if (_stricmp(value,"DRSI") == 0) - hw = 4; - if (_stricmp(value,"DE56") == 0) - hw = 4; - if (_stricmp(value,"TOSH") == 0) - hw = 6; - if (_stricmp(value,"QUAD") == 0) - hw = 8; - if (_stricmp(value,"RLC100") == 0) - hw = 10; - if (_stricmp(value,"RLC400") == 0) - hw = 12; - if (_stricmp(value,"INTERNAL") == 0 || _stricmp(value,"LOOPBACK") == 0) - { - // Set Sensible defaults - - memset(xxp.ID, ' ', 30); - memcpy(xxp.ID, "Loopback", 8); - xxp.CHANNEL = 'A'; - xxp.FRACK = 5000; - xxp.RESPTIME = 1000; - xxp.MAXFRAME = 4; - xxp.RETRIES = 5; - xxp.DIGIFLAG = 1; - hw = 14; - } - if (_stricmp(value,"EXTERNAL") == 0) - { - hw = 16; - - // Set some defaults in case KISSHF - - xxp.CHANNEL = 'A'; - xxp.FRACK = 7000; - xxp.RESPTIME = 1000; - xxp.MAXFRAME = 4; - xxp.RETRIES = 6; - } - - if (_stricmp(value,"BAYCOM") == 0) - hw = 18; - if (_stricmp(value,"PA0HZP") == 0) - hw = 20; - if (_stricmp(value,"I2C") == 0) - { - // Set some defaults - - xxp.CHANNEL = 'A'; - xxp.FRACK = 7000; - xxp.RESPTIME = 1000; - xxp.MAXFRAME = 4; - xxp.RETRIES = 6; - hw = 22; - } - - if (hw == 255) - { - Consoleprintf("Invalid Hardware Type (not DRSI PC120 INTERNAL EXTERNAL BAYCOM PA0HZP ASYNC QUAD)"); - Consoleprintf("%s\r\n", rec); - return (0); - } - else - xxp.TYPE = hw; - - return(1); -} -int protocols(int i, char value[], char rec[]) -{ - int hw; - - hw = 255; - if (_stricmp(value,"KISS") == 0) - hw = 0; - if (_stricmp(value,"NETROM") == 0) - hw = 2; - if (_stricmp(value,"BPQKISS") == 0) - hw = 4; - if (_stricmp(value,"HDLC") == 0) - hw = 6; - if (_stricmp(value,"L2") == 0) - hw = 8; - if (_stricmp(value,"PACTOR") == 0) - hw = 10; - if (_stricmp(value,"WINMOR") == 0) - hw = 10; - if (_stricmp(value,"ARQ") == 0) - hw = 12; - - if (hw == 255) - { - Consoleprintf("Invalid Protocol (not KISS NETROM PACTOR WINMOR ARQ HDLC )"); - Consoleprintf("%s\r\n", rec); - return (0); - } - else - xxp.PROTOCOL = hw; - return(1); -} - - -int bbsflag(int i, char value[],char rec[]) -{ - int hw=255; - - if (_stricmp(value,"NOBBS") == 0) - hw = 1; - if (_stricmp(value,"BBSOK") == 0) - hw = 0; - if (_stricmp(value,"") == 0) - hw = 0; - - if (hw==255) - { - Consoleprintf("BBS Flag must be NOBBS, BBSOK, or null"); - Consoleprintf("%s\r\n",rec); - return(0); - } - - xxp.BBSFLAG = hw; - - return(1); -} - -int channel(int i, char * value, char * rec) -{ - char * val = (char *)poffset[i]; - val[0] = value[0]; - return 1; -} - -int dolinked(int i, char * value, char * rec) -{ - char * val = (char *)offset[i]; - val[0] = value[0]; - return 1; -} - -int validcalls(int i, char * value, char * rec) -{ - if ((strlen(value) + (int)strlen(xxp.VALIDCALLS)) > 255) - { - Consoleprintf("Too Many VALIDCALLS"); - Consoleprintf("%s\r\n", rec); - return(0); - } - - strcat(xxp.VALIDCALLS, value); - return(1); -} - - -int kissoptions(int i, char value[], char rec[]) -{ - int err=255; - - char opt1[12] = ""; - char opt2[12] = ""; - char opt3[12] = ""; - char opt4[12] = ""; - char opt5[12] = ""; - char opt6[12] = ""; - char opt7[12] = ""; - char opt8[12] = ""; - - - - sscanf(value,"%[^,+],%[^,+],%[^,+],%[^,+],%[^,+],%[^,+],%[^,+],%[^,+]", - opt1,opt2,opt3,opt4,opt5,opt6,opt6,opt8); - - if (opt1[0] != '\0') {do_kiss(opt1,rec);} - if (opt2[0] != '\0') {do_kiss(opt2,rec);} - if (opt3[0] != '\0') {do_kiss(opt3,rec);} - if (opt4[0] != '\0') {do_kiss(opt4,rec);} - if (opt5[0] != '\0') {do_kiss(opt5,rec);} - if (opt6[0] != '\0') {do_kiss(opt6,rec);} - if (opt7[0] != '\0') {do_kiss(opt7,rec);} - if (opt8[0] != '\0') {do_kiss(opt8,rec);} - - return(1); -} - - - -/* - TNC PORT PROCESSING -*/ -static char *tkeywords[] = -{ -"COM", "TYPE", "APPLMASK", "KISSMASK", "APPLFLAGS", "ENDPORT" -}; /* parameter keywords */ - -static int toffset[] = -{ -0, 1, 2, 3, 5, 8 -}; /* offset for corresponding data in config file */ - -static int troutine[] = -{ -1, 5, 1, 3, 1, 9 -}; /* routine to process parameter */ - -#define TPARAMLIM 6 - - -typedef struct _TCMDX -{ - char String[12]; // COMMAND STRING - UCHAR CMDLEN; // SIGNIFICANT LENGTH - VOID (* CMDPROC)(struct TNCDATA * TNC, char * Tail, struct _TCMDX * CMD);// COMMAND PROCESSOR - size_t CMDFLAG; // FLAG/VALUE Offset - -} TCMDX; - - - -extern TCMDX TNCCOMMANDLIST[]; -extern int NUMBEROFTNCCOMMANDS; - -int decode_tnc_rec(char * rec) -{ - char key_word[20]; - char value[300]; - - if (xindex(rec,"=") >= 0) - sscanf(rec,"%[^=]=%s",key_word,value); - else - sscanf(rec,"%s",key_word); - - if (_stricmp(key_word, "ENDPORT") == 0) - { - endport=1; - return 0; - } - else if (_stricmp(key_word, "TYPE") == 0) - { - if (_stricmp(value, "TNC2") == 0) - { - TNC2ENTRY->Mode = TNC2; - - // Set Defaults - - TNC2ENTRY->SENDPAC = 13; - TNC2ENTRY->CRFLAG = 1; - TNC2ENTRY->MTX = 1; - TNC2ENTRY->MCOM = 1; - TNC2ENTRY->MMASK = -1; // MONITOR MASK FOR PORTS - - TNC2ENTRY->COMCHAR = 3; - TNC2ENTRY->CMDTIME = 10; // SYSTEM TIMER = 100MS - TNC2ENTRY->PASSCHAR = 0x16; // CTRL-V - TNC2ENTRY->StreamSW = 0x7C; // | - TNC2ENTRY->LCStream = 1; - } - else if (_stricmp(value, "DED") == 0) - TNC2ENTRY->Mode = DED; - else if (_stricmp(value, "KANT") == 0) - TNC2ENTRY->Mode = KANTRONICS; - else if (_stricmp(value, "SCS") == 0) - TNC2ENTRY->Mode = SCS; - else - { - Consoleprintf("Invalid TNC Type"); - Consoleprintf("%s\r\n",rec); - } - } - else if (_stricmp(key_word, "COMPORT") == 0) - strcpy(TNC2ENTRY->PORTNAME, value); - else if (_stricmp(key_word, "APPLMASK") == 0) - TNC2ENTRY->APPLICATION = strtol(value, 0, 0); - else if (_stricmp(key_word, "APPLNUM") == 0) - TNC2ENTRY->APPLICATION = 1 << (strtol(value, 0, 0) - 1); - else if (_stricmp(key_word, "APPLFLAGS") == 0) - TNC2ENTRY->APPLFLAGS = strtol(value, 0, 0); - else if (_stricmp(key_word, "CHANNELS") == 0) - TNC2ENTRY->HOSTSTREAMS = strtol(value, 0, 0); - else if (_stricmp(key_word, "STREAMS") == 0) - TNC2ENTRY->HOSTSTREAMS = strtol(value, 0, 0); - else if (_stricmp(key_word, "POLLDELAY") == 0) - TNC2ENTRY->PollDelay = strtol(value, 0, 0); - else if (_stricmp(key_word, "CONOK") == 0) - TNC2ENTRY->CONOK = strtol(value, 0, 0); - else if (_stricmp(key_word, "AUTOLF") == 0) - TNC2ENTRY->AUTOLF = strtol(value, 0, 0); - else if (_stricmp(key_word, "ECHO") == 0) - TNC2ENTRY->ECHOFLAG = (char)strtol(value, 0, 0); - else - { - if (TNC2ENTRY->Mode == TNC2) - { - // Try process as TNC2 Command - - int n = 0; - TCMDX * CMD = &TNCCOMMANDLIST[0]; - char * ptr1 = key_word; - UCHAR * valueptr; - - strcat(key_word, " "); - - _strupr(key_word); - - for (n = 0; n < NUMBEROFTNCCOMMANDS; n++) - { - int CL = CMD->CMDLEN; - - // ptr1 is input command - - ptr1 = key_word; - - if (memcmp(CMD->String, ptr1, CL) == 0) - { - // Found match so far - check rest - - char * ptr2 = &CMD->String[CL]; - - ptr1 += CL; - - if (*(ptr1) != ' ') - { - while(*(ptr1) == *ptr2 && *(ptr1) != ' ') - { - ptr1++; - ptr2++; - } - } - - if (*(ptr1) == ' ') - { - valueptr = (UCHAR *)TNC2ENTRY + CMD->CMDFLAG; - *valueptr = (UCHAR)strtol(value, 0, 0); - return 0; - } - } - CMD++; - } - } - - Consoleprintf("Source record not recognised - Ignored:%s\r\n",rec); - } - return 0; -} - - -int do_kiss (char * value,char * rec) -{ - int err=255; - - if (_stricmp(value,"POLLED") == 0) - { - err=0; - kissflags=kissflags | POLLINGKISS; - } - else if (_stricmp(value,"CHECKSUM") == 0) - { - err=0; - kissflags=kissflags | CHECKSUM; - } - else if (_stricmp(value,"D700") == 0) - { - err=0; - kissflags=kissflags | D700; - } - else if (_stricmp(value,"TNCX") == 0) - { - err=0; - kissflags=kissflags | TNCX; - } - else if (_stricmp(value,"PITNC") == 0) - { - err=0; - kissflags=kissflags | PITNC; - } - else if (_stricmp(value,"TRACKER") == 0) - { - err=0; - kissflags |= TRACKER; - } - else if (_stricmp(value,"NOPARAMS") == 0) - { - err=0; - kissflags=kissflags | NOPARAMS; - } - else if (_stricmp(value,"ACKMODE") == 0) - { - err=0; - kissflags=kissflags | ACKMODE; - } - else if (_stricmp(value,"SLAVE") == 0) - { - err=0; - kissflags=kissflags | POLLEDKISS; - } - else if (_stricmp(value,"FLDIGI") == 0) - { - err=0; - kissflags |= FLDIGI; - } - else if (_stricmp(value,"FASTI2C") == 0) - { - err=0; - kissflags |= FASTI2C; - } - - else if (_stricmp(value,"DRATS") == 0) - { - err=0; - kissflags |= DRATS; - } - - if (err == 255) - { - Consoleprintf("Invalid KISS Options (not POLLED ACKMODE CHECKSUM D700 SLAVE TNCX PITNC NOPARAMS FASTI2C DRATS)"); - Consoleprintf("%s\r\n",rec); - } - return (err); -} - - -int simple(int i) -{ - // Set up the basic config header - - xxcfg.C_AUTOSAVE = 1; - xxcfg.C_SaveMH = 1; - xxcfg.C_BBS = 1; - xxcfg.C_BTINTERVAL = 60; - xxcfg.C_BUFFERS = 999; - xxcfg.C_C = 1; - xxcfg.C_DESQVIEW = 0; - xxcfg.C_EMSFLAG = 0; - xxcfg.C_FULLCTEXT = 1; - xxcfg.C_HIDENODES = 0; - xxcfg.C_HOSTINTERRUPT = 127; - xxcfg.C_IDINTERVAL = 10; - xxcfg.C_IDLETIME = 900; - xxcfg.C_IP = 0; - xxcfg.C_PM = 0; - xxcfg.C_L3TIMETOLIVE = 25; - xxcfg.C_L4DELAY = 10; - xxcfg.C_L4RETRIES = 3; - xxcfg.C_L4TIMEOUT = 60; - xxcfg.C_L4WINDOW = 4; - xxcfg.C_LINKEDFLAG = 'A'; - xxcfg.C_MAXCIRCUITS = 128; - xxcfg.C_MAXDESTS = 250; - xxcfg.C_MAXHOPS = 4; - xxcfg.C_MAXLINKS = 64; - xxcfg.C_MAXNEIGHBOURS = 64; - xxcfg.C_MAXRTT = 90; - xxcfg.C_MINQUAL = 150; - xxcfg.C_NODE = 1; - xxcfg.C_NODESINTERVAL = 30; - xxcfg.C_OBSINIT = 6; - xxcfg.C_OBSMIN = 5; - xxcfg.C_PACLEN = 236; - xxcfg.C_T3 = 180; - xxcfg.C_TRANSDELAY = 1; - - /* Set PARAMOK flags on all values that are defaulted */ - - for (i=0; i < PARAMLIM; i++) - paramok[i]=1; - - paramok[15] = 0; // Must have callsign - paramok[45] = 0; // Dont Have Appl1Call - paramok[53] = 0; // or APPL1ALIAS - - return(1); -} - -VOID FreeConfig() -{ -} - -BOOL ProcessAPPLDef(char * buf) -{ - // New Style APPL definition - - // APPL n,COMMAND,CMDALIAS,APPLCALL,APPLALIAS,APPLQUAL,L2ALIAS - - char * ptr1, * ptr2; - int Appl, n = 0; - char Param[8][256]; - struct APPLCONFIG * App; - - memset(Param, 0, 2048); - - ptr1 = buf; - - while (ptr1 && *ptr1 && n < 8) - { - ptr2 = strchr(ptr1, ','); - if (ptr2) *ptr2++ = 0; - - strcpy(&Param[n++][0], ptr1); - ptr1 = ptr2; - } - - if (_stricmp(Param[1], Param[2]) == 0) - { - // Alias = Application - will loop. - - return FALSE; - } - - _strupr(Param[0]); - _strupr(Param[1]); - - // Leave Alias in original case - - _strupr(Param[3]); - _strupr(Param[4]); - _strupr(Param[5]); - _strupr(Param[6]); - _strupr(Param[7]); - - - Appl = atoi(Param[0]); - - if (Appl < 1 || Appl > NumberofAppls) return FALSE; - - App = &xxcfg.C_APPL[Appl - 1]; // Recs from zero - - if (Param[1][0] == 0) // No Application - return FALSE; - - if (strlen(Param[1]) > 12) return FALSE; - - memcpy(App->Command, Param[1], (int)strlen(Param[1])); - - xxcfg.C_BBS = 1; - - if (strlen(Param[2]) > 48) return FALSE; - - memcpy(App->CommandAlias, Param[2], (int)strlen(Param[2])); - - if (strlen(Param[3]) > 10) return FALSE; - - memcpy(App->ApplCall, Param[3], (int)strlen(Param[3])); - - if (strlen(Param[4]) > 10) return FALSE; - - memcpy(App->ApplAlias, Param[4], (int)strlen(Param[4])); - - App->ApplQual = atoi(Param[5]); - - if (strlen(Param[6]) > 10) return FALSE; - - memcpy(App->L2Alias, Param[6], (int)strlen(Param[6])); - - return TRUE; -} - -double xfmod(double p1, double p2) -{ - int temp; - - temp = (int)(p1/p2); - p1 = p1 -(p2 * temp); - return p1; -} - - BOOL ToLOC(double Lat, double Lon , char * Locator) - { - int i; - double S1, S2; - - Lon = Lon + 180; - Lat = Lat + 90; - - S1 = xfmod(Lon, 20); - - #pragma warning(push) - #pragma warning(disable : 4244) - - i = Lon / 20; - Locator[0] = 65 + i; - - S2 = xfmod(S1, 2); - - i = S1 / 2; - Locator[2] = 48 + i; - - i = S2 * 12; - Locator[4] = 65 + i; - - S1 = xfmod(Lat,10); - - i = Lat / 10; - Locator[1] = 65 + i; - - S2 = xfmod(S1,1); - - i = S1; - Locator[3] = 48 + i; - - i = S2 * 24; - Locator[5] = 65 + i; - - #pragma warning(pop) - - return TRUE; -} - -int FromLOC(char * Locator, double * pLat, double * pLon) -{ - double i; - double Lat, Lon; - - _strupr(Locator); - - *pLon = 0; - *pLat = 0; // in case invalid - - - // Basic validation for APRS positions - - // The first pair (a field) encodes with base 18 and the letters "A" to "R". - // The second pair (square) encodes with base 10 and the digits "0" to "9". - // The third pair (subsquare) encodes with base 24 and the letters "a" to "x". - - i = Locator[0]; - - if (i < 'A' || i > 'R') - return 0; - - Lon = (i - 65) * 20; - - i = Locator[2]; - if (i < '0' || i > '9') - return 0; - - Lon = Lon + (i - 48) * 2; - - i = Locator[4]; - if (i < 'A' || i > 'X') - return 0; - - Lon = Lon + (i - 65) / 12; - - i = Locator[1]; - if (i < 'A' || i > 'R') - return 0; - - Lat = (i - 65) * 10; - - i = Locator[3]; - if (i < '0' || i > '9') - return 0; - - Lat = Lat + (i - 48); - - i = Locator[5]; - if (i < 'A' || i > 'X') - return 0; - - Lat = Lat + (i - 65) / 24; - - if (Lon < 0 || Lon > 360) - Lon = 180; - if (Lat < 0 || Lat > 180) - Lat = 90; - - *pLon = Lon - 180; - *pLat = Lat - 90; - - return 1; -} +/* +Copyright 2001-2022 John Wiseman G8BPQ +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + + +// July 2010 + +// BPQ32 now reads bpqcfg.txt. This module converts it to the original binary format. + +// Based on the standalonw bpqcfg.c + +/************************************************************************/ +/* CONFIG.C Jonathan Naylor G4KLX, 19th November 1988 */ +/* */ +/* Program to produce configuration file for G8BPQ Network Switch */ +/* based on the original written in BASIC by G8BPQ. */ +/* */ +/* Subsequently extended by G8BPQ */ +/* */ +/************************************************************************/ +// +// 22/11/95 - Add second port alias for digipeating (for APRS) +// - Add PORTMAXDIGIS param + +// 1999 - Win32 Version (but should also compile in 16 bit + +// 5/12/99 - Add DLLNAME Param for ext driver + +// 26/11/02 - Added AUTOSAVE + +// Jan 2006 + +// Add params for input and output names +// Wait before exiting if error detected + +// March 2006 + +// Accept # as comment delimiter +// Display input and output filenames +// Wait before exit, even if ok + +// March 2006 + +// Add L4APPL param + +// Jan 2007 + +// Remove UNPROTO +// Add BTEXT +// Add BCALL + +// Nov 2007 + +// Convert calls and APPLICATIONS string to upper case + +// Jan 2008 + +// Remove trailing space from UNPROTO +// Don't warn BBSCALL etc missing if APPL1CALL etc present + +// August 2008 + +// Add IPGATEWAY Parameter +// Add Port DIGIMASK Parameter + +// December 2008 + +// Add C_IS_CHAT Parameter + +// March 2009 + +// Add C style COmments (/* */ at start of line) + +// August 2009 + +// Add INP3 flag to locked routes + +// November 2009 + +// Add PROTOCOL=PACTOR or WINMOR option + +// December 2009 + +// Add INP3 MAXRTT and MAXHOPS Commands + +// March 2010 + +// Add SIMPLE mode + +// March 2010 + +// Change SIMPLE mode default of Full_CTEXT to 1 + +// April 2010 + +// Add NoKeepAlive ROUTE option + +// Converted to intenal bpq32 process + +// Spetember 2010 + +// Add option of embedded port configuration + + + +#define _CRT_SECURE_NO_DEPRECATE + +#include "cheaders.h" + +#include +#include +#include +#include +#include + +#include "configstructs.h" + +// KISS Options Equates + +#define CHECKSUM 1 +#define POLLINGKISS 2 // KISSFLAGS BITS +#define ACKMODE 4 // CAN USE ACK REQURED FRAMES +#define POLLEDKISS 8 // OTHER END IS POLLING US +#define D700 16 // D700 Mode (Escape "C" chars +#define TNCX 32 // TNC-X Mode (Checksum of ACKMODE frames includes ACK bytes +#define PITNC 64 // PITNC Mode - can reset TNC with FEND 15 2 +#define NOPARAMS 128 // Don't send SETPARAMS frame +#define FLDIGI 256 // Support FLDIGI COmmand Frames +#define TRACKER 512 // SCS Tracker. Need to set KISS Mode +#define FASTI2C 1024 // Use BLocked I2C Reads (like ARDOP) +#define DRATS 2048 + + + +struct WL2KInfo * DecodeWL2KReportLine(char * buf); + +// Dummy file routines - write to buffer instead + +char * PortConfig[70]; +char * RadioConfigMsg[70]; +char * WL2KReportLine[70]; + +int nextRadioPort = 0; +int nextDummyInterlock = 233; + +BOOL PortDefined[70]; + +extern BOOL IncludesMail; +extern BOOL IncludesChat; +extern int needAIS; +extern int needADSB; + +extern int AGWPort; +extern int AGWSessions; +extern int AGWMask; + +extern BOOL LoopMonFlag; +extern BOOL Loopflag; + +extern char NodeMapServer[80]; +extern char ChatMapServer[80]; + +double LatFromLOC; +double LonFromLOC; + + + +VOID * zalloc(int len); + +int WritetoConsoleLocal(char * buff); +char * stristr (char *ch1, char *ch2); +int FromLOC(char * Locator, double * pLat, double * pLon); + +VOID Consoleprintf(const char * format, ...) +{ + char Mess[512]; + va_list(arglist); + + va_start(arglist, format); + vsprintf(Mess, format, arglist); + strcat(Mess, "\n"); + WritetoConsoleLocal(Mess); + + return; +} + + +#pragma pack() + +int tnctypes(int i, char value[],char rec[]); +int do_kiss (char value[],char rec[]); + +struct TNCDATA * TNCCONFIGTABLE = NULL; // malloc'ed +int NUMBEROFTNCPORTS = 0; + +struct UPNP * UPNPConfig = NULL; + +struct TNCDATA * TNC2ENTRY; + +extern char PWTEXT[]; +extern char HFCTEXT[]; +extern int HFCTEXTLEN; +extern char LOCATOR[]; +extern char MAPCOMMENT[]; +extern char LOC[]; +extern int RFOnly; + +int decode_rec(char *rec); +int applcallsign(int i,char *value,char *rec); +int appl_qual(int i,char *value,char *rec); +int callsign(char * val, char *value, char *rec); +int int_value(short * val, char *value, char *rec); +int hex_value(int * val, char *value, char *rec); +int bin_switch(char * val, char *value, char *rec); +int dec_switch(char * val, char *value, char *rec); +int applstrings(int i,char *value, char *rec); +int dotext(char * val, char *key_word, int max); +int dolinked(int i, char * value, char * rec); +int routes(int i); +int ports(int i); +int tncports(int i); +int dedports(int i); +int xindex(char *s,char *t); +int verify(char *s,char c); +int GetNextLine(char * rec); +int call_check(char *callsign, char * val); +int call_check_internal(char * callsign); +int callstring(int i,char *value,char *rec); +int decode_port_rec(char *rec); +int doid(int i,char *value,char *rec); +int dodll(int i,char *value,char *rec); +int doDriver(int i,char *value,char *rec); +int hwtypes(int i,char *value,char *rec); +int protocols(int i,char *value,char *rec); +int bbsflag(int i,char *value,char *rec); +int channel(int i,char *value,char *rec); +int validcalls(int i,char *value,char *rec); +int kissoptions(int i,char *value,char *rec); +int decode_tnc_rec(char *rec); +int tnctypes(int i,char *value,char *rec); +int do_kiss(char *value,char *rec); +int decode_ded_rec(char *rec); +int simple(int i); +int64_t int64_value(int64_t * val, char value[], char rec[]); + + +int C_Q_ADD_NP(VOID *PQ, VOID *PBUFF); +int doSerialPortName(int i, char * value, char * rec); +int doPermittedAppls(int i, char * value, char * rec); +int doKissCommand(int i, char * value, char * rec); + +BOOL ProcessAPPLDef(char * rec); +BOOL ToLOC(double Lat, double Lon , char * Locator); + +//int i; +//char value[]; +//char rec[]; + +int FIRSTAPPL=1; +BOOL Comment = FALSE; +int CommentLine = 0; + +#define MAXLINE 512 +#define FILEVERSION 22 + +extern UCHAR BPQDirectory[]; + +struct CONFIGTABLE xxcfg; +struct PORTCONFIG xxp; + +char inputname[250]="bpqcfg.txt"; +char option[250]; + +/************************************************************************/ +/* STATIC VARIABLES */ +/************************************************************************/ + +static char *keywords[] = +{ +"OBSINIT", "OBSMIN", "NODESINTERVAL", "L3TIMETOLIVE", "L4RETRIES", "L4TIMEOUT", +"BUFFERS", "PACLEN", "TRANSDELAY", "T3", "IDLETIME", "BBS", +"NODE", "NODEALIAS", "BBSALIAS", "NODECALL", "BBSCALL", +"TNCPORT", "IDMSG:", "INFOMSG:", "ROUTES:", "PORT", "MAXLINKS", +"MAXNODES", "MAXROUTES", "MAXCIRCUITS", "IDINTERVAL", "MINQUAL", +"HIDENODES", "L4DELAY", "L4WINDOW", "BTINTERVAL", "UNPROTO", "BBSQUAL", +"APPLICATIONS", "EMS", "CTEXT:", "DESQVIEW", "HOSTINTERRUPT", "ENABLE_LINKED", +"XXDEDHOST", "FULL_CTEXT", "SIMPLE", "AUTOSAVE" , "L4APPL", +"APPL1CALL", "APPL2CALL", "APPL3CALL", "APPL4CALL", +"APPL5CALL", "APPL6CALL", "APPL7CALL", "APPL8CALL", +"APPL1ALIAS", "APPL2ALIAS", "APPL3ALIAS", "APPL4ALIAS", +"APPL5ALIAS", "APPL6ALIAS", "APPL7ALIAS", "APPL8ALIAS", +"APPL1QUAL", "APPL2QUAL", "APPL3QUAL", "APPL4QUAL", +"APPL5QUAL", "APPL6QUAL", "APPL7QUAL", "APPL8QUAL", +"BTEXT:", "NETROMCALL", "C_IS_CHAT", "MAXRTT", "MAXHOPS", // IPGATEWAY= no longer allowed +"LogL4Connects", "LogAllConnects", "SAVEMH", "ENABLEADIFLOG", "ENABLEEVENTS", "SAVEAPRSMSGS", +"EnableM0LTEMap", "MQTT", "MQTT_HOST", "MQTT_PORT", "MQTT_USER", "MQTT_PASS", +"L4Compress", "L4CompMaxframe", "L4CompPaclen", "L2Compress", "L2CompMaxframe", "L2CompPaclen" +}; /* parameter keywords */ + +static void * offset[] = +{ +&xxcfg.C_OBSINIT, &xxcfg.C_OBSMIN, &xxcfg.C_NODESINTERVAL, &xxcfg.C_L3TIMETOLIVE, &xxcfg.C_L4RETRIES, &xxcfg.C_L4TIMEOUT, +&xxcfg.C_BUFFERS, &xxcfg.C_PACLEN, &xxcfg.C_TRANSDELAY, &xxcfg.C_T3, &xxcfg.C_IDLETIME, &xxcfg.C_BBS, +&xxcfg.C_NODE, &xxcfg.C_NODEALIAS, &xxcfg.C_BBSALIAS, &xxcfg.C_NODECALL, &xxcfg.C_BBSCALL, +0, &xxcfg.C_IDMSG, &xxcfg.C_INFOMSG, &xxcfg.C_ROUTE, &xxcfg.C_PORT, &xxcfg.C_MAXLINKS, +&xxcfg.C_MAXDESTS, &xxcfg.C_MAXNEIGHBOURS, &xxcfg.C_MAXCIRCUITS, &xxcfg.C_IDINTERVAL, &xxcfg.C_MINQUAL, +&xxcfg.C_HIDENODES, &xxcfg.C_L4DELAY, &xxcfg.C_L4WINDOW, &xxcfg.C_BTINTERVAL, &xxcfg.C_WASUNPROTO, &xxcfg.C_BBSQUAL, +&xxcfg.C_APPL, &xxcfg.C_EMSFLAG, &xxcfg.C_CTEXT , &xxcfg.C_DESQVIEW, &xxcfg.C_HOSTINTERRUPT, &xxcfg.C_LINKEDFLAG, +0, &xxcfg.C_FULLCTEXT, 0, &xxcfg.C_AUTOSAVE, &xxcfg.C_L4APPL, +&xxcfg.C_APPL[0].ApplCall, &xxcfg.C_APPL[1].ApplCall, &xxcfg.C_APPL[2].ApplCall, &xxcfg.C_APPL[3].ApplCall, +&xxcfg.C_APPL[4].ApplCall, &xxcfg.C_APPL[5].ApplCall, &xxcfg.C_APPL[6].ApplCall, &xxcfg.C_APPL[7].ApplCall, +&xxcfg.C_APPL[0].ApplAlias, &xxcfg.C_APPL[1].ApplAlias, &xxcfg.C_APPL[2].ApplAlias, &xxcfg.C_APPL[3].ApplAlias, +&xxcfg.C_APPL[4].ApplAlias, &xxcfg.C_APPL[5].ApplAlias, &xxcfg.C_APPL[6].ApplAlias, &xxcfg.C_APPL[7].ApplAlias, +&xxcfg.C_APPL[0].ApplQual, &xxcfg.C_APPL[1].ApplQual, &xxcfg.C_APPL[2].ApplQual, &xxcfg.C_APPL[3].ApplQual, +&xxcfg.C_APPL[4].ApplQual, &xxcfg.C_APPL[5].ApplQual, &xxcfg.C_APPL[6].ApplQual, &xxcfg.C_APPL[7].ApplQual, +&xxcfg.C_BTEXT, &xxcfg.C_NETROMCALL, &xxcfg.C_C, &xxcfg.C_MAXRTT, &xxcfg.C_MAXHOPS, // IPGATEWAY= no longer allowed +&xxcfg.C_LogL4Connects, &xxcfg.C_LogAllConnects, &xxcfg.C_SaveMH, &xxcfg.C_ADIF, &xxcfg.C_EVENTS, &xxcfg.C_SaveAPRSMsgs, +&xxcfg.C_M0LTEMap, &xxcfg.C_MQTT, &xxcfg.C_MQTT_HOST, &xxcfg.C_MQTT_PORT, &xxcfg.C_MQTT_USER, &xxcfg.C_MQTT_PASS, +&xxcfg.C_L4Compress, &xxcfg.C_L4CompMaxframe, &xxcfg.C_L4CompPaclen, &xxcfg.C_L2Compress, &xxcfg.C_L2CompMaxframe, &xxcfg.C_L2CompPaclen}; /* offset for corresponding data in config file */ + +static int routine[] = +{ +1, 1, 1, 1, 1, 1, +1, 1, 1, 1, 1, 2, +2, 0, 0, 0, 0, +3, 4, 20, 5, 6, 1, +1, 1, 1, 1, 1, +2, 1, 1, 1, 7, 1, +8, 2, 4, 2, 9, 10, +11, 1, 12, 2 , 1, +13, 13, 13, 13, +13, 13 ,13, 13, +13, 13, 13, 13, +13, 13 ,13, 13, +14, 14, 14, 14, +14, 14 ,14, 14, +15, 0, 2, 9, 9, +2, 2, 1, 2, 2, 2, +2, 2, 0, 1, 20, 20, +1, 1, 1, 1, 1, 1} ; // Routine to process param + +int PARAMLIM = sizeof(routine)/sizeof(int); +//int NUMBEROFKEYWORDS = sizeof(routine)/sizeof(int); + +//#define PARAMLIM 74 + + +static char eof_message[] = "Unexpected end of file on input\n"; + +static char *pkeywords[] = +{ +"ID", "TYPE", "PROTOCOL", "IOADDR", "INTLEVEL", "SPEED", "CHANNEL", +"BBSFLAG", "QUALITY", "MAXFRAME", "TXDELAY", "SLOTTIME", "PERSIST", +"FULLDUP", "SOFTDCD", "FRACK", "RESPTIME", "RETRIES", +"PACLEN", "CWID", "PORTCALL", "PORTALIAS", "ENDPORT", "VALIDCALLS", +"QUALADJUST", "DIGIFLAG", "DIGIPORT", "USERS" ,"UNPROTO", "PORTNUM", +"TXTAIL", "ALIAS_IS_BBS", "L3ONLY", "KISSOPTIONS", "INTERLOCK", "NODESPACLEN", +"TXPORT", "MHEARD", "CWIDTYPE", "MINQUAL", "MAXDIGIS", "PORTALIAS2", "DLLNAME", +"BCALL", "DIGIMASK", "NOKEEPALIVES", "COMPORT", "DRIVER", "WL2KREPORT", "UIONLY", +"UDPPORT", "IPADDR", "I2CBUS", "I2CDEVICE", "UDPTXPORT", "UDPRXPORT", "NONORMALIZE", +"IGNOREUNLOCKEDROUTES", "INP3ONLY", "TCPPORT", "RIGPORT", "PERMITTEDAPPLS", "HIDE", +"SMARTID", "KISSCOMMAND", "SendtoM0LTEMap", "PortFreq", "M0LTEMapInfo", "QTSMPort"}; /* parameter keywords */ + +static void * poffset[] = +{ +&xxp.ID, &xxp.TYPE, &xxp.PROTOCOL, &xxp.IOADDR, &xxp.INTLEVEL, &xxp.SPEED, &xxp.CHANNEL, +&xxp.BBSFLAG, &xxp.QUALITY, &xxp.MAXFRAME, &xxp.TXDELAY, &xxp.SLOTTIME, &xxp.PERSIST, +&xxp.FULLDUP, &xxp.SOFTDCD, &xxp.FRACK, &xxp.RESPTIME, &xxp.RETRIES, +&xxp.PACLEN, &xxp.CWID, &xxp.PORTCALL, &xxp.PORTALIAS, 0, &xxp.VALIDCALLS, +&xxp.QUALADJUST, &xxp.DIGIFLAG, &xxp.DIGIPORT, &xxp.USERS, &xxp.UNPROTO, &xxp.PORTNUM, +&xxp.TXTAIL, &xxp.ALIAS_IS_BBS, &xxp.L3ONLY, &xxp.KISSOPTIONS, &xxp.INTERLOCK, &xxp.NODESPACLEN, +&xxp.TXPORT, &xxp.MHEARD, &xxp.CWIDTYPE, &xxp.MINQUAL, &xxp.MAXDIGIS, &xxp.PORTALIAS2, &xxp.DLLNAME, +&xxp.BCALL, &xxp.DIGIMASK, &xxp.DefaultNoKeepAlives, &xxp.IOADDR, &xxp.DLLNAME, &xxp.WL2K, &xxp.UIONLY, +&xxp.IOADDR, &xxp.IPADDR, &xxp.INTLEVEL, &xxp.IOADDR, &xxp.IOADDR, &xxp.ListenPort, &xxp.NoNormalize, +&xxp.IGNOREUNLOCKED, &xxp.INP3ONLY, &xxp.TCPPORT, &xxp.RIGPORT, &xxp.PERMITTEDAPPLS, &xxp.Hide, +&xxp.SmartID, &xxp.KissParams, &xxp.SendtoM0LTEMap, &xxp.PortFreq, &xxp.M0LTEMapInfo, &xxp.QtSMPort}; /* offset for corresponding data in config file */ + +static int proutine[] = +{ +4, 5, 8, 3, 1, 1, 7, +6, 1, 1, 1, 1, 1, +1, 1, 1, 1, 1, +1, 0, 0, 0, 9, 10, +1, 13, 13, 1, 11, 1, +1, 2, 2, 12, 1, 1, +1, 7, 7, 13, 13, 0, 14, +0, 1, 2, 18, 15, 16, 2, +1, 17, 1, 1, 1, 1, 2, +2, 2, 1, 1, 19, 2, +1, 20, 1, 21, 22, 1}; /* routine to process parameter */ + +int PPARAMLIM = sizeof(proutine)/sizeof(int); + +static int endport = 0; +static int portnum = 1; +static int portindex = 0; +static int porterror = 0; +static int tncporterror = 0; +static int dedporterror = 0; +static int kissflags = 0; +static int NextAppl = 0; +static int routeindex = 0; + + +/************************************************************************/ +/* Global variables */ +/************************************************************************/ + +int paramok[100] = {0}; /* PARAMETER OK FLAG */ + +FILE *fp1; /* TEXT INPUT FILE */ + +static char s1[80]; +static char s2[80]; +static char s3[80]; +static char s4[80]; +static char s5[80]; +static char s6[80]; +static char s7[80]; +static char s8[80]; + +char commas[]=",,,,,,,,,,,,,,,,"; + +char bbscall[11]; +char bbsalias[11]; +int bbsqual; + + +extern UCHAR ConfigDirectory[260]; + +BOOL LocSpecified = FALSE; + +/************************************************************************/ +/* MAIN PROGRAM */ +/************************************************************************/ + +VOID WarnThread(); + +int LineNo = 0; + +int heading = 0; + + +BOOL ProcessConfig() +{ + int i; + char rec[MAXLINE]; + int Cfglen = sizeof(xxcfg); + struct APPLCONFIG * App; + + memset(&xxcfg, 0, sizeof(xxcfg)); + memset(&xxp, 0, sizeof(xxp)); + + heading = 0; + portnum = 1; + NextAppl = 0; + LOCATOR[0] = 0; + MAPCOMMENT[0] = 0; + routeindex = 0; + portindex = 0; + + for (i = 0; i < 70; i++) + { + if (PortConfig[i]) + { + free(PortConfig[i]); + PortConfig[i] = NULL; + } + PortDefined[i] = FALSE; + + if (RadioConfigMsg[i]) + { + free(RadioConfigMsg[i]); + RadioConfigMsg[i] = NULL; + } + } + + nextRadioPort = 0; + + TNCCONFIGTABLE = NULL; + NUMBEROFTNCPORTS = 0; + + AGWMask = 0; + + UPNPConfig = NULL; + + Consoleprintf("Configuration file Preprocessor."); + + if (ConfigDirectory[0] == 0) + { + strcpy(inputname, "bpq32.cfg"); + } + else + { + strcpy(inputname,ConfigDirectory); + strcat(inputname,"/"); + strcat(inputname, "bpq32.cfg"); + } + + if ((fp1 = fopen(inputname,"r")) == NULL) + { + Consoleprintf("Could not open file %s Error code %d", inputname, errno); + return FALSE; + } + + Consoleprintf("Using Configuration file %s",inputname); + + memset(&xxcfg, 0, sizeof(xxcfg)); + + App = (struct APPLCONFIG *)&xxcfg.C_APPL[0]; + + for (i=0; i < NumberofAppls; i++) + { + memset(App->Command, ' ', 12); + memset(App->CommandAlias, ' ', 48); + memset(App->ApplCall, ' ', 10); + memset(App->ApplAlias, ' ', 10); + + App++; + } + +// xxcfg.SaveMH = TRUE; // Default to save + + GetNextLine(rec); + + while (rec[0]) + { + decode_rec(rec); + GetNextLine(rec); + } + + if (xxcfg.C_NODECALL[0] == ' ') + { + Consoleprintf("Missing NODECALL"); + heading = 1; + } + + + paramok[6]=1; /* dont need BUFFERS */ + paramok[8]=1; /* dont need TRANSDELAY */ + paramok[13]=1; // NodeAlias + paramok[17]=1; /* dont need TNCPORTS */ + paramok[20]=1; // Or ROUTES + + paramok[32]=1; /* dont need UNPROTO */ + + paramok[35]=1; /* dont need EMS */ + paramok[37]=1; /* dont need DESQVIEW */ + paramok[38]=1; /* dont need HOSTINTERRUPT */ + + paramok[40]=1; /* or DEDHOST */ + + paramok[42]=1; /* or SIMPLE */ + + paramok[43]=1; /* or AUTOSAVE */ + + paramok[44]=1; /* or L4APPL */ + + + paramok[16]=1; // BBSCALL + paramok[14]=1; // BBSALIAS + paramok[33]=1; // BBSQUAL + paramok[34]=1; // APPLICATIONS + + if (paramok[45]==1) + { + paramok[16]=1; // APPL1CALL overrides BBSCALL + memcpy(xxcfg.C_BBSCALL, xxcfg.C_APPL[0].ApplCall, 10); + } + + if (paramok[53]==1) + { + paramok[14]=1; // APPL1ALIAS overrides BBSALIAS + memcpy(xxcfg.C_BBSALIAS, xxcfg.C_APPL[0].ApplAlias, 10); + } + + if (paramok[61]==1) + { + paramok[33]=1; // APPL1QUAL overrides BBSQUAL + xxcfg.C_BBSQUAL = xxcfg.C_APPL[0].ApplQual; + } + + + for (i=0;i<24;i++) + + paramok[45+i]=1; /* or APPLCALLS, APPLALIASS APPLQUAL */ + + paramok[69]=1; // BText optional + paramok[70]=1; // IPGateway optional + paramok[71]=1; // C_IS_CHAT optional + + paramok[72]=1; // MAXRTT optional + paramok[73]=1; // MAXHOPS optional + paramok[74]=1; // LogL4Connects optional + paramok[75]=1; // LogAllConnects optional + paramok[76]=1; // SAVEMH optional + paramok[77]=1; // ENABLEADIFLOG optional + paramok[78]=1; // EnableEvents optional + paramok[79]=1; // SaveAPRSMsgs optional + paramok[79]=1; // SaveAPRSMsgs optional + paramok[80]=1; // EnableM0LTEMap optional + paramok[81]=1; // MQTT Params + paramok[82]=1; // MQTT Params + paramok[83]=1; // MQTT Params + paramok[84]=1; // MQTT Params + paramok[85]=1; // MQTT Params + paramok[86]=1; // L4Compress + paramok[87]=1; // L4Compress Maxframe + paramok[88]=1; // L4Compress Paclen + paramok[89]=1; // L2Compress + paramok[90]=1; // L2Compress Maxframe + paramok[91]=1; // L2Compress Paclen + + + for (i=0; i < PARAMLIM; i++) + { + if (paramok[i] == 0) + { + if (heading == 0) + { + Consoleprintf(" "); + Consoleprintf("The following parameters were not correctly specified"); + heading = 1; + } + Consoleprintf(keywords[i]); + } + } + + if (portnum == 1) + { + Consoleprintf("No valid radio ports defined"); + heading = 1; + } + + if (Comment) + { + Consoleprintf("Unterminated Comment (Missing */) at line %d", CommentLine); + heading = 1; + } + + fclose(fp1); + + if (LOCATOR[0] == 0 && LocSpecified == 0 && RFOnly == 0) + { + Consoleprintf(""); + Consoleprintf("Please enter a LOCATOR statement in your BPQ32.cfg"); + Consoleprintf("If you really don't want to be on the Node Map you can enter LOCATOR=NONE"); + Consoleprintf(""); + +// _beginthread(WarnThread, 0, 0); + } + + if (heading == 0) + { + Consoleprintf("Conversion (probably) successful"); + Consoleprintf(""); + } + else + { + Consoleprintf("Conversion failed"); + return FALSE; + } + +/* + // Dump to file for debugging + + sprintf_s(inputname, sizeof(inputname), "CFG%d", time(NULL)); + + fp1 = fopen(inputname, "wb"); + + if (fp1) + { + fwrite(ConfigBuffer, 1, 100000, fp1); + fclose(fp1); + } +*/ + return TRUE; +} + +/************************************************************************/ +/* Decode PARAM= */ +/************************************************************************/ + +int decode_rec(char * rec) +{ + int i; + int cn = 1; /* RETURN CODE FROM ROUTINES */ + + char key_word[300] = ""; + char value[300] = ""; + + if (_memicmp(rec, "NODEMAPSERVER=", 14) == 0) + { + // Map reporting override + + strcpy(NodeMapServer, &rec[14]); + strlop(NodeMapServer, ' '); + + return 1; + } + + if (_memicmp(rec, "CloseOnError=", 13) == 0) + { + // Close BPQ on trapped program error + + CloseOnError = atoi(&rec[13]); + return 1; + } + + if (_memicmp(rec, "CHATMAPSERVER=", 14) == 0) + { + // Map reporting override + + strcpy(ChatMapServer, &rec[14]); + strlop(ChatMapServer, ' '); + + return 1; + } + + if (_memicmp(rec, "IPGATEWAY", 9) == 0 && rec[9] != '=') // IPGATEWAY, not IPGATEWAY= + { + // Create Embedded IPGateway Config + + // Copy all subsequent lines up to **** to a memory buffer + + char * ptr; + + PortConfig[IPConfigSlot] = ptr = malloc(50000); + + *ptr = 0; + + GetNextLine(rec); + + while (!feof(fp1)) + { + if (_memicmp(rec, "****", 3) == 0) + { + PortConfig[IPConfigSlot] = realloc(PortConfig[IPConfigSlot], (strlen(ptr) + 1)); + xxcfg.C_IP = 1; + return 0; + } + + strcat(ptr, rec); + strcat(ptr, "\r\n"); + GetNextLine(rec); + } + + Consoleprintf("Missing **** for IPGateway Config"); + heading = 1; + + return 0; + } + + if (_memicmp(rec, "PORTMAPPER", 10) == 0) + { + // Create Embedded portmapper Config + + // Copy all subsequent lines up to **** to a memory buffer + + char * ptr; + + PortConfig[PortMapConfigSlot] = ptr = malloc(50000); + + *ptr = 0; + + GetNextLine(rec); + + while (!feof(fp1)) + { + if (_memicmp(rec, "****", 3) == 0) + { + PortConfig[PortMapConfigSlot] = realloc(PortConfig[PortMapConfigSlot], (strlen(ptr) + 1)); + xxcfg.C_PM = 1; + return 0; + } + + strcat(ptr, rec); + strcat(ptr, "\r\n"); + GetNextLine(rec); + } + + Consoleprintf("Missing **** for Portmapper Config"); + heading = 1; + + return 0; + } + + if (_memicmp(rec, "APRSDIGI", 8) == 0) + { + // Create Embedded APRS Config + + // Copy all subsequent lines up to **** to a memory buffer + + char * ptr; + + PortConfig[APRSConfigSlot] = ptr = malloc(50000); + + *ptr = 0; + + // Don't use GetNextLine - we need to keep ; in messages + + fgets(rec,MAXLINE,fp1); + LineNo++; + + while (!feof(fp1)) + { + if (_memicmp(rec, "****", 3) == 0) + { + PortConfig[APRSConfigSlot] = realloc(PortConfig[APRSConfigSlot], (strlen(ptr) + 1)); + return 0; + } + if (strlen(rec) > 1) + { + if (memcmp(rec, "/*", 2) == 0) + { + Comment = TRUE; + CommentLine = LineNo; + goto NextAPRS; + } + else if (memcmp(rec, "*/", 2) == 0) + { + Comment = FALSE; + goto NextAPRS; + } + } + + if (Comment) + goto NextAPRS; + + strcat(ptr, rec); + strcat(ptr, "\r\n"); +NextAPRS: + fgets(rec,MAXLINE,fp1); + LineNo++; + } + + if (_memicmp(rec, "****", 3) == 0) + return 0; // No Newline after *** + + Consoleprintf("Missing **** for APRS Config"); + heading = 1; + + return 0; + } + + if (_memicmp(rec, "PASSWORD", 8) == 0) + { + // SYSOP Password + + if (strlen(rec) > 88) rec[88] = 0; + + _strupr(rec); + + strcpy(PWTEXT, &rec[9]); + return 0; + } + +#ifdef LINBPQ + + if (_memicmp(rec, "LINMAIL", 7) == 0) + { + // Enable Mail on Linux Verdion + + IncludesMail = TRUE; + + return 0; + } + + if (_memicmp(rec, "LINCHAT", 7) == 0) + { + // Enable Chat on Linux Verdion + + IncludesChat = TRUE; + + return 0; + } +#endif + + if (_memicmp(rec, "ENABLEAIS", 9) == 0) + { + needAIS = TRUE; + return 0; + } + + if (_memicmp(rec, "ENABLEADSB", 9) == 0) + { + needADSB = TRUE; + return 0; + } + + if (_memicmp(rec, "HFCTEXT", 7) == 0) + { + // HF only CTEXT (normlly short to reduce traffic) + + if (strlen(rec) > 87) rec[87] = 0; + strcpy(HFCTEXT, &rec[8]); + HFCTEXTLEN = (int)(strlen(HFCTEXT)); + HFCTEXT[HFCTEXTLEN - 1] = '\r'; + return 0; + } + + if (_memicmp(rec, "LOCATOR", 7) == 0) + { + // Station Maidenhead Locator or Lat/Long + + char * Context; + char * ptr1 = strtok_s(&rec[7], " ,=\t\n\r:", &Context); + char * ptr2 = strtok_s(NULL, " ,=\t\n\r:", &Context); + + LocSpecified = TRUE; + + if (_memicmp(&rec[8], "NONE", 4) == 0) + return 0; + + if (_memicmp(&rec[8], "XXnnXX", 6) == 0) + return 0; + + if (ptr1) + { + strcpy(LOCATOR, ptr1); + if (ptr2) + { + strcat(LOCATOR, ":"); + strcat(LOCATOR, ptr2); + ToLOC(atof(ptr1), atof(ptr2), LOC); + LatFromLOC = atof(ptr1); + LonFromLOC = atof(ptr2); + + } + else + { + if (strlen(ptr1) == 6) + { + strcpy(LOC, ptr1); + FromLOC(LOC, &LatFromLOC, &LonFromLOC); + // Randomise in square + LatFromLOC += ((rand() / 24.0) / RAND_MAX); + LonFromLOC += ((rand() / 12.0) / RAND_MAX); + + } + } + } + return 0; + } + + if (_memicmp(rec, "MAPCOMMENT", 10) == 0) + { + // Additional Info for Node Map + + char * ptr1 = &rec[11]; + char * ptr2 = MAPCOMMENT; + + while (*ptr1) + { + if (*ptr1 == ',') + { + *ptr2++ = '&'; + *ptr2++ = '#'; + *ptr2++ = '4'; + *ptr2++ = '4'; + *ptr2++ = ';'; + } + else + *(ptr2++) = *ptr1; + + ptr1++; + + if ((ptr2 - MAPCOMMENT) > 248) + break; + } + + *ptr2 = 0; + + return 0; + } + + // Process BRIDGE statement + + if (_memicmp(rec, "BRIDGE", 6) == 0) + { + int DigiTo; + int Port; + char * Context; + char * p_value; + char * ptr; + + p_value = strtok_s(&rec[7], ",=\t\n\r", &Context); + + Port = atoi(p_value); + + if (Port > MaxBPQPortNo) + return FALSE; + + if (Context == NULL) + return FALSE; + + ptr = strtok_s(NULL, ",\t\n\r", &Context); + + while (ptr) + { + DigiTo = atoi(ptr); + + if (DigiTo > MaxBPQPortNo) + return 0; + + if (Port != DigiTo) // Not to our port! + xxcfg.CfgBridgeMap[Port][DigiTo] = TRUE; + + ptr = strtok_s(NULL, " ,\t\n\r", &Context); + } + + return 0; + } + + + // AGW Emulator Params + + if (_memicmp(rec, "AGWPORT", 7) == 0) + { + AGWPort = atoi(&rec[8]); + return 0; + } + + if (_memicmp(rec, "AGWSESSIONS", 11) == 0) + { + AGWSessions = atoi(&rec[12]); + return 0; + } + + if (_memicmp(rec, "AGWMASK", 7) == 0) + { + AGWMask = strtol(&rec[8], 0, 0); + return 0; + } + + if (_memicmp(rec, "AGWAPPL", 7) == 0) + { + AGWMask |= 1 << (strtol(&rec[8], 0, 0) - 1); + return 0; + } + + if (_memicmp(rec, "AGWLOOPMON", 10) == 0) + { + LoopMonFlag = strtol(&rec[11], 0, 0); + return 0; + } + if (_memicmp(rec, "AGWLOOPTX", 9) == 0) + { + Loopflag = strtol(&rec[10], 0, 0); + return 0; + } + + if (_memicmp(rec, "APPLICATION ", 12) == 0 || _memicmp(rec, "APPLICATION=", 12) == 0) + { + // New Style APPLICATION Definition + + char save[300]; + + strcpy(save, rec); // Save in case error + + if (!ProcessAPPLDef(&rec[12])) + { + Consoleprintf("Invalid Record %s", save); + heading = 1; + } + else + paramok[34]=1; // Got APPLICATIONS + + return 0; + } + + if (_memicmp(rec, "EXCLUDE=", 8) == 0) + { + char * ptr2 = &rec[8]; + UCHAR * ptr3 = xxcfg.C_EXCLUDE; + + _strupr(ptr2); + while (*(ptr2) > 32) + { + ConvToAX25(ptr2, ptr3); + ptr3 += 7; + + if (strchr(ptr2, ',')) + { + ptr2 = strchr(ptr2, ','); + ptr2++; + } + else + break; + + if (ptr3 > &xxcfg.C_EXCLUDE[63]) + { + Consoleprintf("Too Many Excluded Calls"); + heading = 1; + break; + } + } + + return 0; + } + if (_memicmp(rec, "RADIO", 5) == 0) + { + if (strlen(rec) > 11) + { + RadioConfigMsg[nextRadioPort++] = _strdup(rec); + return 0; + } + else + { + // Multiline config, ending in **** + + char * rptr; + + RadioConfigMsg[nextRadioPort] = rptr = zalloc(50000); + + strcpy(rptr, rec); + + GetNextLine(rec); + + while(!feof(fp1)) + { + if (memcmp(rec, "***", 3) == 0) + { + RadioConfigMsg[nextRadioPort] = realloc(RadioConfigMsg[nextRadioPort], (strlen(rptr) + 1)); + nextRadioPort++; + return 0; + } + strcat(rptr, rec); + GetNextLine(rec); + } + } + } + + if (_memicmp(rec, "UPNP ", 5) == 0) + { + struct UPNP * Entry = (struct UPNP *)zalloc(sizeof(struct UPNP)); + char * ptr, * context; + char copy[256]; + + strcpy(copy, rec); + + ptr = strtok_s(&rec[5], ", ", &context); + + if (ptr) + Entry->Protocol = _strdup(ptr); + + ptr = strtok_s(NULL, ", ", &context); + + if (ptr) + Entry->LANport = Entry->WANPort = _strdup(ptr);; + + ptr = strtok_s(NULL, ", ", &context); + + if (ptr) + Entry->WANPort = _strdup(ptr);; + + if (Entry->LANport) + { + Entry->Next = UPNPConfig; + UPNPConfig = Entry; + return 1; + } + + Consoleprintf("Bad UPNP Line %s", copy); + heading = 1; + + return 0; + } + + + if (_memicmp("MQTT_HOST=", rec, 10) == 0) + { + strcpy(xxcfg.C_MQTT_HOST, &rec[10]); + xxcfg.C_MQTT_HOST[strlen(xxcfg.C_MQTT_HOST)-1] = '\0'; + return 0; + } + if (_memicmp("MQTT_USER=", rec, 10) == 0) + { + strcpy(xxcfg.C_MQTT_USER, &rec[10]); + xxcfg.C_MQTT_USER[strlen(xxcfg.C_MQTT_USER)-1] = '\0'; + return 0; + } + if (_memicmp("MQTT_PASS=", rec, 10) == 0) + { + strcpy(xxcfg.C_MQTT_PASS, &rec[10]); + xxcfg.C_MQTT_PASS[strlen(xxcfg.C_MQTT_PASS)-1] = '\0'; + return 0; +} + + + if (xindex(rec,"=") >= 0) + sscanf(rec,"%[^=]=%s",key_word,value); + else + sscanf(rec,"%s",key_word); + +/************************************************************************/ +/* SEARCH FOR KEYWORD IN TABLE */ +/************************************************************************/ + + for (i=0; i < PARAMLIM && _stricmp(keywords[i],key_word) != 0 ; i++) + ; + + if (i == PARAMLIM) + Consoleprintf("bpq32.cfg line no %d not recognised - Ignored: %s" ,LineNo, rec); + else + { + + switch (routine[i]) + { + case 0: + cn = callsign((char *)offset[i], value, rec); /* CALLSIGNS */ + break; + + case 1: + cn = int_value((short *)offset[i], value, rec); /* INTEGER VALUES */ + break; + + case 2: + cn = bin_switch((char *)offset[i], value, rec); /* 0/1 SWITCHES */ + break; + + case 3: + cn = tncports(i); /* VIRTUAL COMBIOS PORTS */ + break; + + case 4: + cn = dotext((char *)offset[i], key_word, 510); /* TEXT PARMS */ + break; + + case 20: + cn = dotext((char *)offset[i], key_word, 2000); /* INFO TEXT PARM */ + break; + + case 5: + cn = routes(i); /* ROUTES TO LOCK IN */ + break; + + case 6: + cn = ports(i); /* PORTS DEFINITION */ + break; + + case 7: + Consoleprintf("Obsolete Record %s ignored",rec); + Consoleprintf("UNPROTO address should now be specified in PORT definition"); + + break; + + case 8: + cn = applstrings(i,value,rec); /* APPLICATIONS LIST */ + break; + + case 9: + cn = dec_switch((char *)offset[i],value,rec); /* 0/9 SWITCHES */ + break; + + case 10: + cn = dolinked(i,value,rec); /* SINGLE CHAR */ + break; + + case 11: + Consoleprintf("Obsolete Record %s ignored", rec); + break; + + case 12: + cn = simple(i); /* Set up basic L2 system*/ + break; + + case 13: + cn = applcallsign(i,value,rec); /* CALLSIGNS */ + break; + + case 14: + cn = appl_qual(i,value,rec); /* INTEGER VALUES */ + break; + + case 15: + cn = dotext((char *)offset[i], key_word, 120); /* BTEXT */ + break; + } + + paramok[i] = cn; + } + + return 0; +} + +/************************************************************************/ +/* CALLSIGNS */ +/************************************************************************/ +int applcallsign(int i, char * value, char * rec) +{ + char * val = (char *)offset[i]; + + if (call_check_internal(value)) + { + // Invalid + + return 0; + } + + memcpy(val, value, 10); + + if (i==45) + strcpy(bbscall,value); + if (i==53) + strcpy(bbsalias,value); + + return 1; +} + +int appl_qual(int i, char * value, char * rec) +{ + int j, k; + int * val = (int *)offset[i]; + + k = sscanf(value," %d",&j); + + if (k != 1) + { + Consoleprintf("Invalid numerical value "); + Consoleprintf("%s\r\n",rec); + return(0); + } + + if (i==61) bbsqual=j; + + *val = j; + return(1); +} + + +int callsign(char * ptr, char * value, char * rec) +{ + if (call_check(value, ptr) == 1) + { + Consoleprintf("%s",rec); + return(0); + } + + return(1); +} + + +/************************************************************************/ +/* VALIDATE INT VALUES */ +/************************************************************************/ + +int int_value(short * val, char value[], char rec[]) +{ + int j,k; + + k = sscanf(value," %d",&j); + + if (k != 1) + { + Consoleprintf("Invalid numerical value "); + Consoleprintf("%s\r\n",rec); + return(0); + } + + val[0] = j; + return(1); +} + +int64_t int64_value(int64_t * val, char value[], char rec[]) +{ + *val = strtoll(value, NULL, 10); + return(1); +} + +/************************************************************************/ +/* VALIDATE HEX INT VALUES */ +/************************************************************************/ + +int hex_value(int * val, char value[], char rec[]) +{ + int j = -1, k = 0; + + k = sscanf(value, " %xH", &j); + + if (j < 0) + { + Consoleprintf("Bad Hex Value"); + Consoleprintf("%s\r\n", rec); + return(0); + } + + val[0] = j; + return(1); +} +; + +/************************************************************************/ +/* VALIDATE BINARY SWITCH DATA AND WRITE TO FILE */ +/************************************************************************/ + +int bin_switch(char * val, char * value, char * rec) +{ + int value_int; + + value_int = atoi(value); + + if (value_int == 0 || value_int == 1) + { + val[0] = value_int; + return 1; + } + else + { + Consoleprintf("Invalid switch value, must be either 0 or 1"); + Consoleprintf("%s\r\n",rec); + return(0); + } +} +/* +; single byte decimal +*/ +int dec_switch (char * val, char * value, char * rec) +{ + int value_int; + + value_int = atoi(value); + + if (value_int < 256) + { + val[0] = value_int; + return 1; + } + else + { + Consoleprintf("Invalid value, must be between 0 and 255"); + Consoleprintf("%s\r\n",rec); + return(0); + } +} + + +int applstrings(int i, char * value, char * rec) +{ + char appl[250]; // In case trailing spaces + char * ptr1; + char * ptr2; + struct APPLCONFIG * App; + int j; + + // strcat(rec,commas); // Ensure 16 commas + + ptr1 = &rec[13]; // skip APPLICATIONS= + + App = &xxcfg.C_APPL[0]; + + while (NextAppl++ < NumberofAppls) + { + memset(appl, ' ', 249); + appl[249] = 0; + + ptr2=appl; + + j = *ptr1++; + + while (j != ',' && j) + { + *(ptr2++) = toupper(j); + j = *ptr1++; + } + + ptr2 = strchr(appl, '/'); + + if (ptr2) + { + // Command has an Alias + + *ptr2++ = 0; + memcpy(App->CommandAlias, ptr2, 48); + strcat(appl, " "); + } + + memcpy(App->Command, appl, 12); + xxcfg.C_BBS = 1; + + if (*(ptr1 - 1) == 0) + return 1; + + App++; + } + return(1); +} + + +/************************************************************************/ +/* USE FOR FREE FORM TEXT IN MESSAGES */ +/************************************************************************/ + +int dotext(char * val, char * key_word, int max) +{ + int len = 0; + char * ptr; + + char rec[MAXLINE]; + + GetNextLine(rec); + + if (xindex(rec,"***") == 0) + *val = '\r'; + + while (xindex(rec,"***") != 0 && !feof(fp1)) + { + ptr = strchr(rec, 10); + if (ptr) *ptr = 0; + ptr = strchr(rec, 13); + if (ptr) *ptr = 0; + + strcat(rec, "\r"); + + len += (int)strlen(rec); + + if (len <= max) + { + strcpy(val, rec); + val += (int)strlen(rec); + } + + fgets(rec,MAXLINE,fp1); + LineNo++; + } + + if (len > max) + { + Consoleprintf("Text too long: %s (max %d\r\n",key_word, max); + return(0); + } + + if (feof(fp1)) + return(0); + else + return(1); +} + + +/************************************************************************/ +/* CONVERT PRE-SET ROUTES PARAMETERS TO BINARY */ +/************************************************************************/ + +int routes(int i) +{ + struct ROUTECONFIG * Route; + + int err_flag = 0; + int main_err = 0; + + char rec[MAXLINE]; + + + GetNextLine(rec); + + while (xindex(rec,"***") != 0 && !feof(fp1)) + { + char Param[8][256]; + char * ptr1, * ptr2; + int n = 0, inp3 = 0; + + Route = &xxcfg.C_ROUTE[routeindex++]; + + // strtok and sscanf can't handle successive commas, so split up usig strchr + + memset(Param, 0, 2048); + strlop(rec, 13); + strlop(rec, ';'); + + ptr1 = rec; + + while (ptr1 && *ptr1 && n < 8) + { + ptr2 = strchr(ptr1, ','); + if (ptr2) *ptr2++ = 0; + + strcpy(&Param[n++][0], ptr1); + ptr1 = ptr2; + while(ptr1 && *ptr1 && *ptr1 == ' ') + ptr1++; + } + + strcpy(Route->call, &Param[0][0]); + + Route->quality = atoi(Param[1]); + Route->port = atoi(Param[2]); + Route->pwind = atoi(Param[3]); + Route->pfrack = atoi(Param[4]); + Route->ppacl = atoi(Param[5]); + inp3 = atoi(Param[6]); + Route->farQual = atoi(Param[7]); + + if (Route->farQual < 0 || Route->farQual > 255) + { + Consoleprintf("Remote Quality must be between 0 and 255"); + Consoleprintf("%s\r\n",rec); + + err_flag = 1; + } + + if (Route->quality < 0 || Route->quality > 255) + { + Consoleprintf("Quality must be between 0 and 255"); + Consoleprintf("%s\r\n",rec); + + err_flag = 1; + } + + if (Route->port < 1 || Route->port > MaxBPQPortNo) + { + Consoleprintf("Port number must be between 1 and 64"); + Consoleprintf("%s\r\n",rec); + err_flag = 1; + } + + // Use top bit of window as INP3 Flag, next as NoKeepAlive + + if (inp3 & 1) + Route->pwind |= 0x80; + + if (inp3 & 2) + Route->pwind |= 0x40; + + if (err_flag == 1) + { + Consoleprintf("%s\r\n",rec); + main_err = 1; + err_flag = 0; + } + GetNextLine(rec); + } + + if (routeindex > MaxLockedRoutes) + { + routeindex--; + Consoleprintf("Route information too long "); + main_err = 1; + } + + if (feof(fp1)) + { + Consoleprintf(eof_message); + return(0); + } + + if (main_err == 1) + return(0); + else + return(1); +} + + +/************************************************************************/ +/* CONVERT PORT DEFINITIONS TO BINARY */ +/************************************************************************/ +int hw; // Hardware type +int LogicalPortNum; // As set by PORTNUM + +int ports(int i) +{ + char rec[MAXLINE]; + endport=0; + porterror=0; + kissflags=0; + + xxp.PORTNUM = portnum; + + LogicalPortNum = portnum; + + if (LogicalPortNum > MaxBPQPortNo) + { + Consoleprintf("Port Number must be between 1 and %d", MaxBPQPortNo); + heading = 1; + } + + xxp.SendtoM0LTEMap = 1; // Default to enabled + + while (endport == 0 && !feof(fp1)) + { + GetNextLine(rec); + decode_port_rec(rec); + } + if (porterror != 0) + { + Consoleprintf("Error in port definition"); + return(0); + } + + if (PortDefined[LogicalPortNum]) // Already defined? + { + Consoleprintf("Port %d already defined", LogicalPortNum); + heading = 1; + } + + PortDefined[LogicalPortNum] = TRUE; + + xxp.KISSOPTIONS = kissflags; + + // copy Port Config to main config + + memcpy(&xxcfg.C_PORT[portindex++], &xxp, sizeof(xxp)); + memset(&xxp, 0, sizeof(xxp)); + + portnum++; + + return(1); + +} + + +int tncports(int i) +{ + char rec[MAXLINE]; + endport=0; + tncporterror=0; + + TNC2ENTRY = zalloc(sizeof(struct TNCDATA)); + + TNC2ENTRY->APPLFLAGS = 6; + TNC2ENTRY->PollDelay = 1; + + while (endport == 0 && !feof(fp1)) + { + GetNextLine(rec); + decode_tnc_rec(rec); + } + if (tncporterror != 0) + { + Consoleprintf("Error in TNC PORT definition"); + free (TNC2ENTRY); + return(0); + } + + C_Q_ADD_NP(&TNCCONFIGTABLE, TNC2ENTRY); // Add to chain + + NUMBEROFTNCPORTS++; + + return(1); + + +} + + +/************************************************************************/ +/* MISC FUNCTIONS */ +/************************************************************************/ + +/************************************************************************/ +/* FIND OCCURENCE OF ONE STRING WITHIN ANOTHER */ +/************************************************************************/ + +int xindex(char s[], char t[]) +{ + int i, j ,k; + + for (i=0; s[i] != '\0'; i++) + { + for (j=i, k=0; t[k] != '\0' && s[i] == t[k]; j++, k++) + ; + if (t[k] == '\0') + return(i); + } + return(-1); +} + + +/************************************************************************/ +/* FIND FIRST OCCURENCE OF A CHARACTER THAT IS NOT c */ +/************************************************************************/ + +int verify(char s[], char c) +{ + int i; + + for (i = 0; s[i] != '\0'; i++) + if (s[i] != c) + return(i); + + return(-1); +} + +/************************************************************************/ +/* GET NEXT LINE THAT ISN'T BLANK OR IS A COMMENT LINE */ +/************************************************************************/ + +// Returns an empty string to indicate end of config + +// Modified Aril 2020 to allow #include of file fragments + +FILE * savefp = NULL; +int saveLineNo; +char includefilename[250]; + +int GetNextLine(char *rec) +{ + int i, j; + char * ret; + char * ptr, *context; + + while (TRUE) + { + ret = fgets(rec,MAXLINE,fp1); + LineNo++; + + if (ret == NULL) + { + if (savefp) + { + // we have reached eof on an include file - switch back + + fclose(fp1); + fp1 = savefp; + savefp = NULL; + LineNo = saveLineNo; + continue; + } + + rec[0] = 0; + return 0; // return end of config + } + + for (i=0; rec[i] != '\0'; i++) + if (rec[i] == '\t' || rec[i] == '\n' || rec[i] == '\r') + rec[i] = ' '; + + + + j = verify(rec,' '); + + if (j > 0) + { + // Remove Leading Spaces + + for (i=0; rec[j] != '\0'; i++, j++) + rec[i] = rec[j]; + + rec[i] = '\0'; + } + + if (stristr(rec,"WebTermCSS") == 0 && stristr(rec,"HybridCoLocatedRMS") == 0 && stristr(rec,"HybridFrequencies") == 0) // Needs ; in string + strlop(rec, ';'); + else + j = j; + + if (strlen(rec) > 1) + if (memcmp(rec, "/*",2) == 0) + { + Comment = TRUE; + CommentLine = LineNo; + } + else + if (memcmp(rec, "*/",2) == 0) + { + rec[0] = 32; + rec[1] = 0; + Comment = FALSE; + } + + if (Comment) + { + rec[0] = 32; + rec[1] = 0; + continue; + } + + // remove trailing spaces + + while(strlen(rec) && rec[strlen(rec) - 1] == ' ') + rec[strlen(rec) - 1] = 0; + + strcat(rec, " "); + + ptr = strtok_s(rec, " ", &context); + + // Put one back + + if (ptr) + { + if (context) + { + ptr[strlen(ptr)] = ' '; + } + rec = ptr; + + // look for #include + + if (_memicmp(rec, "#include ", 9) == 0) + { + savefp = fp1; + + if (BPQDirectory[0] == 0) + { + strcpy(includefilename, &rec[9]); + } + else + { + strcpy(includefilename,BPQDirectory); + strcat(includefilename,"/"); + strcat(includefilename, &rec[9]); + } + + if ((fp1 = fopen(includefilename,"r")) == NULL) + { + Consoleprintf("Could not open #include file %s Error code %d", includefilename, errno); + fp1 = savefp; + savefp = NULL; + } + else + { + saveLineNo = LineNo; + LineNo = 0; + } + continue; // get next line + } + return 0; + } + } + + // Should never reach this + + return 0; +} + + +/************************************************************************/ +/* TEST VALIDITY OF CALLSIGN */ +/************************************************************************/ + +int call_check_internal(char * callsign) +{ + char call[20]; + int ssid; + int err_flag = 0; + int i; + + if (xindex(callsign,"-") > 0) /* There is an SSID field */ + { + sscanf(callsign,"%[^-]-%d",call,&ssid); + if (strlen(call) > 6) + { + Consoleprintf("Callsign too long, 6 characters before SSID"); + Consoleprintf("%s\r\n",callsign); + err_flag = 1; + } + if (ssid < 0 || ssid > 15) + { + Consoleprintf("SSID out of range, must be between 0 and 15"); + Consoleprintf("%s\r\n",callsign); + err_flag = 1; + } + } + else /* No SSID field */ + { + if (strlen(callsign) > 6) + { + Consoleprintf("Callsign too long, 6 characters maximum"); + Consoleprintf("%s\r\n",callsign); + err_flag = 1; + } + } + + strcat(callsign," "); + callsign[10] = '\0'; + for (i=0; i< 10; i++) + callsign[i]=toupper(callsign[i]); + + return(err_flag); +} + +int call_check(char * callsign, char * loc) +{ + int err = call_check_internal(callsign); + memcpy(loc, callsign, 10); + return err; +} + + +/* Process UNPROTO string allowing VIA */ + +char workstring[80]; + +int callstring(int i, char * value, char * rec) +{ + char * val = (char *)poffset[i]; + size_t j = (int)strlen(value); + + memcpy(val, value, j); + return 1; +} + +/* + RADIO PORT PROCESSING +*/ + + +int decode_port_rec(char * rec) +{ + int i; + int cn = 1; /* RETURN CODE FROM ROUTINES */ + uint32_t IPADDR; +#ifdef WIN32 + WSADATA WsaData; // receives data from WSAStartupproblem +#endif + char key_word[30]=""; + char value[300]=""; + + if (_memicmp(rec, "CONFIG", 6) == 0) + { + // Create Embedded PORT Config + + // Copy all subseuent lines up to ENDPORT to a memory buffer + + char * ptr; + int i; + + if (LogicalPortNum > 64) + { + Consoleprintf("Portnum %d is invalid", LogicalPortNum); + LogicalPortNum = 0; + } + + PortConfig[LogicalPortNum] = ptr = malloc(50000); + *ptr = 0; + + GetNextLine(rec); + + while (!feof(fp1)) + { + if (_memicmp(rec, "ENDPORT", 7) == 0) + { + PortConfig[LogicalPortNum] = realloc(PortConfig[LogicalPortNum], (strlen(ptr) + 1)); + endport = 1; + return 0; + } + + i = (int)strlen(rec); + i--; + + while(i > 1) + { + if (rec[i] == ' ') + rec[i] = 0; // Remove trailing spaces + else + break; + + i--; + } + + // Pick out RIGCONFIG Records + + if (_memicmp(rec, "RIGCONTROL", 10) == 0) + { + // RIGCONTROL COM60 19200 ICOM IC706 5e 4 14.103/U1w 14.112/u1 18.1/U1n 10.12/l1 + + // Convert to new format (RADIO Interlockno); + + int Interlock = xxp.INTERLOCK; + char radio[16]; + + if (Interlock == 0) // Replace with dummy + { + Interlock = xxp.INTERLOCK = nextDummyInterlock; + nextDummyInterlock++; + } + + sprintf(radio, "RADIO %d ", Interlock); + memcpy(rec, radio, 10); + + if (strlen(rec) > 15) + { + RadioConfigMsg[nextRadioPort++] = _strdup(rec); + } + else + { + // Multiline config, ending in **** + + char * rptr; + + RadioConfigMsg[nextRadioPort] = rptr = zalloc(50000); + + strcpy(rptr, radio); + + GetNextLine(rec); + + while(!feof(fp1)) + { + if (memcmp(rec, "***", 3) == 0) + { + RadioConfigMsg[nextRadioPort] = realloc(RadioConfigMsg[nextRadioPort], (strlen(rptr) + 1)); + nextRadioPort++; + break; + } + strcat(rptr, rec); + GetNextLine(rec); + } + } + } + else + { + strcat(ptr, rec); + strcat(ptr, "\r\n"); + } + GetNextLine(rec); + } + + Consoleprintf("Missing ENDPORT for Port %d", portnum); + heading = 1; + + return 0; + } + + if (xindex(rec,"=") >= 0) + sscanf(rec,"%[^=]=%s",key_word,value); + else + sscanf(rec,"%s",key_word); + + if (_stricmp(key_word, "portnum") == 0) + { + // Save as LogicalPortNum + + LogicalPortNum = atoi(value); + } + + if (_stricmp(key_word, "XDIGI") == 0) + { + // Cross Port Digi definition + + // XDIGI=CALL,PORT,UI + + struct XDIGI * Digi = zalloc(sizeof(struct XDIGI)); // Chain + char * call, * pport, * Context; + + call = strtok_s(value, ",", &Context); + pport = strtok_s(NULL, ",", &Context); + + if (call && pport && ConvToAX25(call, Digi->Call)) + { + Digi->Port = atoi(pport); + if (Digi->Port) + { + if (Context) + { + _strupr(Context); + if (strstr(Context, "UI")) + Digi->UIOnly = TRUE; + } + + // Add to chain + + if (xxp.XDIGIS) + Digi->Next = xxp.XDIGIS; + + xxp.XDIGIS = Digi; + return 0; + } + } + Consoleprintf("Invalid XDIGI Statement %s", rec); + porterror = 1; + return 0; + } + + +/************************************************************************/ +/* SEARCH FOR KEYWORD IN TABLE */ +/************************************************************************/ + + for (i=0; i < PPARAMLIM && _stricmp(pkeywords[i],key_word) != 0 ; i++) + ; + + if (i == PPARAMLIM) + Consoleprintf("Source record not recognised - Ignored:%s\r\n",rec); + else + { + + switch (proutine[i]) + { + + case 0: + cn = callsign((char *)poffset[i], value, rec); /* CALLSIGNS */ + break; + + case 1: + cn = int_value((short *)poffset[i], value, rec); /* INTEGER VALUES */ + break; + + case 2: + cn = bin_switch((char *)poffset[i], value, rec); /* 0/1 SWITCHES */ + break; + + case 3: + cn = hex_value((int *)poffset[i], value, rec); /* HEX NUMBERS */ + break; + + case 4: + cn = doid(i,value,rec); /* ID PARMS */ + break; + + case 5: + cn = hwtypes(i,value,rec); /* HARDWARE TYPES */ + break; + + case 6: + cn = bbsflag(i,value,rec); + break; + + case 7: + cn = channel(i,value,rec); + break; + + case 8: + cn = protocols(i,value,rec); + break; + + case 10: + cn = validcalls(i,value,rec); + break; + + case 11: + cn = callstring(i,value,rec); + break; + + case 12: + cn = kissoptions(i,value,rec); + break; + + case 13: + cn = dec_switch((char *)poffset[i], value, rec); /* 0/9 SWITCHES */ + break; + + case 14: + cn = dodll(i,value,rec); /* DLL PARMS */ + break; + + case 15: + cn = doDriver(i,value,rec); /* DLL PARMS */ + break; + + case 16: + + xxp.WL2K = DecodeWL2KReportLine(rec); + break; + + case 17: + + // IP Address for KISS over UDP + +#ifdef WIN32 + WSAStartup(MAKEWORD(2, 0), &WsaData); +#endif + IPADDR = inet_addr(&rec[7]); + memcpy(&xxp.IPADDR, &IPADDR, 4); +#ifdef WIN32 + WSACleanup(); +#endif + break; + + case 18: + cn = doSerialPortName(i,value,rec); // COMPORT + break; + + case 19: + cn = doPermittedAppls(i,value,rec); // Permitted Apps + break; + + case 20: + cn = doKissCommand(i, value, rec); // Permitted Apps + break; + + case 21: + cn = int64_value(poffset[i], value, rec); /* INTEGER VALUES */ + break; + + case 22: + xxp.M0LTEMapInfo = _strdup(value); + cn = 1; + break; + + case 9: + + cn = 1; + endport=1; + + break; + } + } + if (cn == 0) porterror=1; + + return 0; +} + + +int doid(int i, char value[], char rec[]) +{ + unsigned int j; + for (j = 3;( j < (unsigned int)strlen(rec)+1); j++) + + workstring[j-3] = rec[j]; + + // Remove trailing spaces before checking length + + i = (int)strlen(workstring); + i--; + + while(i > 1) + { + if (workstring[i] == ' ') + workstring[i] = 0; // Remove trailing spaces + else + break; + + i--; + } + + if (i > 29) + { + Consoleprintf("Port description too long - Truncated"); + Consoleprintf("%s\r\n",rec); + } + strcat(workstring," "); + workstring[30] = '\0'; + + memcpy(xxp.ID, workstring, 30); + return(1); +} + +int dodll(int i, char value[], char rec[]) +{ + unsigned int j; + + strlop(rec, ' '); + for (j = 8;( j < (unsigned int)strlen(rec)+1); j++) + workstring[j-8] = rec[j]; + + if (j > 24) + { + Consoleprintf("DLL name too long - Truncated"); + Consoleprintf("%s\r\n",rec); + + } + + _strupr(workstring); + strcat(workstring," "); + + memcpy(xxp.DLLNAME, workstring, 16); + xxp.TYPE = 16; // External + + if (strstr(xxp.DLLNAME, "TELNET") || strstr(xxp.DLLNAME, "AXIP")) + RFOnly = FALSE; + + return(1); +} + +int doDriver(int i, char * value, char * rec) +{ + unsigned int j; + for (j = 7;( j < (unsigned int)strlen(rec)+1); j++) + workstring[j-7] = rec[j]; + + if (j > 23) + { + Consoleprintf("Driver name too long - Truncated"); + Consoleprintf("%s\r\n",rec); + } + + _strupr(workstring); + strcat(workstring," "); + + memcpy(xxp.DLLNAME, workstring, 16); + xxp.TYPE = 16; // External + + // Set some defaults in case HFKISS + + xxp.CHANNEL = 'A'; + xxp.FRACK = 7000; + xxp.RESPTIME = 1000; + xxp.MAXFRAME = 4; + xxp.RETRIES = 6; + + if (strstr(xxp.DLLNAME, "TELNET") || strstr(xxp.DLLNAME, "AXIP")) + RFOnly = FALSE; + + return 1; +} +int IsNumeric(char *str) +{ + while(*str) + { + if(!isdigit(*str)) + return 0; + str++; + } + + return 1; +} + + +int doSerialPortName(int i, char * value, char * rec) +{ + rec += 8; + + if (strlen(rec) > 250) + { + Consoleprintf("Serial Port Name too long - Truncated"); + Consoleprintf("%s\r\n",rec); + rec[250] = 0; + } + + strlop(rec, ' '); + + if (IsNumeric(rec)) + xxp.IOADDR = atoi(rec); + else + xxp.SerialPortName = _strdup(rec); + + return 1; +} + +int doPermittedAppls(int i, char * value, char * rec) +{ + unsigned int Mask = 0; + char * Context; + char * ptr1 = strtok_s(value, " ,=\t\n\r", &Context); + + // Param is a comma separated list of Appl Numbers allowed to connect on this port + + while (ptr1 && ptr1[0]) + { + Mask |= 1 << (atoi(ptr1) - 1); + ptr1 = strtok_s(NULL, " ,=\t\n\r", &Context); + } + + xxp.HavePermittedAppls = 1; // indicate used + xxp.PERMITTEDAPPLS = Mask; + + return 1; +} +int doKissCommand(int i, char * value, char * rec) +{ + // Param is kiss command and any operands as decimal bytes + + xxp.KissParams = _strdup(strlop(rec, '=')); + return 1; +} + + +int hwtypes(int i, char value[], char rec[]) +{ + hw = 255; + if (_stricmp(value,"ASYNC") == 0) + { + // Set some defaults + + xxp.CHANNEL = 'A'; + xxp.FRACK = 7000; + xxp.RESPTIME = 1000; + xxp.MAXFRAME = 4; + xxp.RETRIES = 6; + hw = 0; + } + + if (_stricmp(value,"PC120") == 0) + hw = 2; + if (_stricmp(value,"DRSI") == 0) + hw = 4; + if (_stricmp(value,"DE56") == 0) + hw = 4; + if (_stricmp(value,"TOSH") == 0) + hw = 6; + if (_stricmp(value,"QUAD") == 0) + hw = 8; + if (_stricmp(value,"RLC100") == 0) + hw = 10; + if (_stricmp(value,"RLC400") == 0) + hw = 12; + if (_stricmp(value,"INTERNAL") == 0 || _stricmp(value,"LOOPBACK") == 0) + { + // Set Sensible defaults + + memset(xxp.ID, ' ', 30); + memcpy(xxp.ID, "Loopback", 8); + xxp.CHANNEL = 'A'; + xxp.FRACK = 5000; + xxp.RESPTIME = 1000; + xxp.MAXFRAME = 4; + xxp.RETRIES = 5; + xxp.DIGIFLAG = 1; + hw = 14; + } + if (_stricmp(value,"EXTERNAL") == 0) + { + hw = 16; + + // Set some defaults in case KISSHF + + xxp.CHANNEL = 'A'; + xxp.FRACK = 7000; + xxp.RESPTIME = 1000; + xxp.MAXFRAME = 4; + xxp.RETRIES = 6; + } + + if (_stricmp(value,"BAYCOM") == 0) + hw = 18; + if (_stricmp(value,"PA0HZP") == 0) + hw = 20; + if (_stricmp(value,"I2C") == 0) + { + // Set some defaults + + xxp.CHANNEL = 'A'; + xxp.FRACK = 7000; + xxp.RESPTIME = 1000; + xxp.MAXFRAME = 4; + xxp.RETRIES = 6; + hw = 22; + } + + if (hw == 255) + { + Consoleprintf("Invalid Hardware Type (not DRSI PC120 INTERNAL EXTERNAL BAYCOM PA0HZP ASYNC QUAD)"); + Consoleprintf("%s\r\n", rec); + return (0); + } + else + xxp.TYPE = hw; + + return(1); +} +int protocols(int i, char value[], char rec[]) +{ + int hw; + + hw = 255; + if (_stricmp(value,"KISS") == 0) + hw = 0; + if (_stricmp(value,"NETROM") == 0) + hw = 2; + if (_stricmp(value,"BPQKISS") == 0) + hw = 4; + if (_stricmp(value,"HDLC") == 0) + hw = 6; + if (_stricmp(value,"L2") == 0) + hw = 8; + if (_stricmp(value,"PACTOR") == 0) + hw = 10; + if (_stricmp(value,"WINMOR") == 0) + hw = 10; + if (_stricmp(value,"ARQ") == 0) + hw = 12; + + if (hw == 255) + { + Consoleprintf("Invalid Protocol (not KISS NETROM PACTOR WINMOR ARQ HDLC )"); + Consoleprintf("%s\r\n", rec); + return (0); + } + else + xxp.PROTOCOL = hw; + return(1); +} + + +int bbsflag(int i, char value[],char rec[]) +{ + int hw=255; + + if (_stricmp(value,"NOBBS") == 0) + hw = 1; + if (_stricmp(value,"BBSOK") == 0) + hw = 0; + if (_stricmp(value,"") == 0) + hw = 0; + + if (hw==255) + { + Consoleprintf("BBS Flag must be NOBBS, BBSOK, or null"); + Consoleprintf("%s\r\n",rec); + return(0); + } + + xxp.BBSFLAG = hw; + + return(1); +} + +int channel(int i, char * value, char * rec) +{ + char * val = (char *)poffset[i]; + val[0] = value[0]; + return 1; +} + +int dolinked(int i, char * value, char * rec) +{ + char * val = (char *)offset[i]; + val[0] = value[0]; + return 1; +} + +int validcalls(int i, char * value, char * rec) +{ + if ((strlen(value) + (int)strlen(xxp.VALIDCALLS)) > 255) + { + Consoleprintf("Too Many VALIDCALLS"); + Consoleprintf("%s\r\n", rec); + return(0); + } + + strcat(xxp.VALIDCALLS, value); + return(1); +} + + +int kissoptions(int i, char value[], char rec[]) +{ + int err=255; + + char opt1[12] = ""; + char opt2[12] = ""; + char opt3[12] = ""; + char opt4[12] = ""; + char opt5[12] = ""; + char opt6[12] = ""; + char opt7[12] = ""; + char opt8[12] = ""; + + + + sscanf(value,"%[^,+],%[^,+],%[^,+],%[^,+],%[^,+],%[^,+],%[^,+],%[^,+]", + opt1,opt2,opt3,opt4,opt5,opt6,opt6,opt8); + + if (opt1[0] != '\0') {do_kiss(opt1,rec);} + if (opt2[0] != '\0') {do_kiss(opt2,rec);} + if (opt3[0] != '\0') {do_kiss(opt3,rec);} + if (opt4[0] != '\0') {do_kiss(opt4,rec);} + if (opt5[0] != '\0') {do_kiss(opt5,rec);} + if (opt6[0] != '\0') {do_kiss(opt6,rec);} + if (opt7[0] != '\0') {do_kiss(opt7,rec);} + if (opt8[0] != '\0') {do_kiss(opt8,rec);} + + return(1); +} + + + +/* + TNC PORT PROCESSING +*/ +static char *tkeywords[] = +{ +"COM", "TYPE", "APPLMASK", "KISSMASK", "APPLFLAGS", "ENDPORT" +}; /* parameter keywords */ + +static int toffset[] = +{ +0, 1, 2, 3, 5, 8 +}; /* offset for corresponding data in config file */ + +static int troutine[] = +{ +1, 5, 1, 3, 1, 9 +}; /* routine to process parameter */ + +#define TPARAMLIM 6 + + +typedef struct _TCMDX +{ + char String[12]; // COMMAND STRING + UCHAR CMDLEN; // SIGNIFICANT LENGTH + VOID (* CMDPROC)(struct TNCDATA * TNC, char * Tail, struct _TCMDX * CMD);// COMMAND PROCESSOR + size_t CMDFLAG; // FLAG/VALUE Offset + +} TCMDX; + + + +extern TCMDX TNCCOMMANDLIST[]; +extern int NUMBEROFTNCCOMMANDS; + +int decode_tnc_rec(char * rec) +{ + char key_word[20]; + char value[300]; + + if (xindex(rec,"=") >= 0) + sscanf(rec,"%[^=]=%s",key_word,value); + else + sscanf(rec,"%s",key_word); + + if (_stricmp(key_word, "ENDPORT") == 0) + { + endport=1; + return 0; + } + else if (_stricmp(key_word, "TYPE") == 0) + { + if (_stricmp(value, "TNC2") == 0) + { + TNC2ENTRY->Mode = TNC2; + + // Set Defaults + + TNC2ENTRY->SENDPAC = 13; + TNC2ENTRY->CRFLAG = 1; + TNC2ENTRY->MTX = 1; + TNC2ENTRY->MCOM = 1; + TNC2ENTRY->MMASK = -1; // MONITOR MASK FOR PORTS + + TNC2ENTRY->COMCHAR = 3; + TNC2ENTRY->CMDTIME = 10; // SYSTEM TIMER = 100MS + TNC2ENTRY->PASSCHAR = 0x16; // CTRL-V + TNC2ENTRY->StreamSW = 0x7C; // | + TNC2ENTRY->LCStream = 1; + } + else if (_stricmp(value, "DED") == 0) + TNC2ENTRY->Mode = DED; + else if (_stricmp(value, "KANT") == 0) + TNC2ENTRY->Mode = KANTRONICS; + else if (_stricmp(value, "SCS") == 0) + TNC2ENTRY->Mode = SCS; + else + { + Consoleprintf("Invalid TNC Type"); + Consoleprintf("%s\r\n",rec); + } + } + else if (_stricmp(key_word, "COMPORT") == 0) + strcpy(TNC2ENTRY->PORTNAME, value); + else if (_stricmp(key_word, "APPLMASK") == 0) + TNC2ENTRY->APPLICATION = strtol(value, 0, 0); + else if (_stricmp(key_word, "APPLNUM") == 0) + TNC2ENTRY->APPLICATION = 1 << (strtol(value, 0, 0) - 1); + else if (_stricmp(key_word, "APPLFLAGS") == 0) + TNC2ENTRY->APPLFLAGS = strtol(value, 0, 0); + else if (_stricmp(key_word, "CHANNELS") == 0) + TNC2ENTRY->HOSTSTREAMS = strtol(value, 0, 0); + else if (_stricmp(key_word, "STREAMS") == 0) + TNC2ENTRY->HOSTSTREAMS = strtol(value, 0, 0); + else if (_stricmp(key_word, "POLLDELAY") == 0) + TNC2ENTRY->PollDelay = strtol(value, 0, 0); + else if (_stricmp(key_word, "CONOK") == 0) + TNC2ENTRY->CONOK = strtol(value, 0, 0); + else if (_stricmp(key_word, "AUTOLF") == 0) + TNC2ENTRY->AUTOLF = strtol(value, 0, 0); + else if (_stricmp(key_word, "ECHO") == 0) + TNC2ENTRY->ECHOFLAG = (char)strtol(value, 0, 0); + else + { + if (TNC2ENTRY->Mode == TNC2) + { + // Try process as TNC2 Command + + int n = 0; + TCMDX * CMD = &TNCCOMMANDLIST[0]; + char * ptr1 = key_word; + UCHAR * valueptr; + + strcat(key_word, " "); + + _strupr(key_word); + + for (n = 0; n < NUMBEROFTNCCOMMANDS; n++) + { + int CL = CMD->CMDLEN; + + // ptr1 is input command + + ptr1 = key_word; + + if (memcmp(CMD->String, ptr1, CL) == 0) + { + // Found match so far - check rest + + char * ptr2 = &CMD->String[CL]; + + ptr1 += CL; + + if (*(ptr1) != ' ') + { + while(*(ptr1) == *ptr2 && *(ptr1) != ' ') + { + ptr1++; + ptr2++; + } + } + + if (*(ptr1) == ' ') + { + valueptr = (UCHAR *)TNC2ENTRY + CMD->CMDFLAG; + *valueptr = (UCHAR)strtol(value, 0, 0); + return 0; + } + } + CMD++; + } + } + + Consoleprintf("Source record not recognised - Ignored:%s\r\n",rec); + } + return 0; +} + + +int do_kiss (char * value,char * rec) +{ + int err=255; + + if (_stricmp(value,"POLLED") == 0) + { + err=0; + kissflags=kissflags | POLLINGKISS; + } + else if (_stricmp(value,"CHECKSUM") == 0) + { + err=0; + kissflags=kissflags | CHECKSUM; + } + else if (_stricmp(value,"D700") == 0) + { + err=0; + kissflags=kissflags | D700; + } + else if (_stricmp(value,"TNCX") == 0) + { + err=0; + kissflags=kissflags | TNCX; + } + else if (_stricmp(value,"PITNC") == 0) + { + err=0; + kissflags=kissflags | PITNC; + } + else if (_stricmp(value,"TRACKER") == 0) + { + err=0; + kissflags |= TRACKER; + } + else if (_stricmp(value,"NOPARAMS") == 0) + { + err=0; + kissflags=kissflags | NOPARAMS; + } + else if (_stricmp(value,"ACKMODE") == 0) + { + err=0; + kissflags=kissflags | ACKMODE; + } + else if (_stricmp(value,"SLAVE") == 0) + { + err=0; + kissflags=kissflags | POLLEDKISS; + } + else if (_stricmp(value,"FLDIGI") == 0) + { + err=0; + kissflags |= FLDIGI; + } + else if (_stricmp(value,"FASTI2C") == 0) + { + err=0; + kissflags |= FASTI2C; + } + + else if (_stricmp(value,"DRATS") == 0) + { + err=0; + kissflags |= DRATS; + } + + if (err == 255) + { + Consoleprintf("Invalid KISS Options (not POLLED ACKMODE CHECKSUM D700 SLAVE TNCX PITNC NOPARAMS FASTI2C DRATS)"); + Consoleprintf("%s\r\n",rec); + } + return (err); +} + + +int simple(int i) +{ + // Set up the basic config header + + xxcfg.C_AUTOSAVE = 1; + xxcfg.C_SaveMH = 1; + xxcfg.C_BBS = 1; + xxcfg.C_BTINTERVAL = 60; + xxcfg.C_BUFFERS = 999; + xxcfg.C_C = 1; + xxcfg.C_DESQVIEW = 0; + xxcfg.C_EMSFLAG = 0; + xxcfg.C_FULLCTEXT = 1; + xxcfg.C_HIDENODES = 0; + xxcfg.C_HOSTINTERRUPT = 127; + xxcfg.C_IDINTERVAL = 10; + xxcfg.C_IDLETIME = 900; + xxcfg.C_IP = 0; + xxcfg.C_PM = 0; + xxcfg.C_L3TIMETOLIVE = 25; + xxcfg.C_L4DELAY = 10; + xxcfg.C_L4RETRIES = 3; + xxcfg.C_L4TIMEOUT = 60; + xxcfg.C_L4WINDOW = 4; + xxcfg.C_LINKEDFLAG = 'A'; + xxcfg.C_MAXCIRCUITS = 128; + xxcfg.C_MAXDESTS = 250; + xxcfg.C_MAXHOPS = 4; + xxcfg.C_MAXLINKS = 64; + xxcfg.C_MAXNEIGHBOURS = 64; + xxcfg.C_MAXRTT = 90; + xxcfg.C_MINQUAL = 150; + xxcfg.C_NODE = 1; + xxcfg.C_NODESINTERVAL = 30; + xxcfg.C_OBSINIT = 6; + xxcfg.C_OBSMIN = 5; + xxcfg.C_PACLEN = 236; + xxcfg.C_T3 = 180; + xxcfg.C_TRANSDELAY = 1; + + /* Set PARAMOK flags on all values that are defaulted */ + + for (i=0; i < PARAMLIM; i++) + paramok[i]=1; + + paramok[15] = 0; // Must have callsign + paramok[45] = 0; // Dont Have Appl1Call + paramok[53] = 0; // or APPL1ALIAS + + return(1); +} + +VOID FreeConfig() +{ +} + +BOOL ProcessAPPLDef(char * buf) +{ + // New Style APPL definition + + // APPL n,COMMAND,CMDALIAS,APPLCALL,APPLALIAS,APPLQUAL,L2ALIAS + + char * ptr1, * ptr2; + int Appl, n = 0; + char Param[8][256]; + struct APPLCONFIG * App; + + memset(Param, 0, 2048); + + ptr1 = buf; + + while (ptr1 && *ptr1 && n < 8) + { + ptr2 = strchr(ptr1, ','); + if (ptr2) *ptr2++ = 0; + + strcpy(&Param[n++][0], ptr1); + ptr1 = ptr2; + } + + if (_stricmp(Param[1], Param[2]) == 0) + { + // Alias = Application - will loop. + + return FALSE; + } + + _strupr(Param[0]); + _strupr(Param[1]); + + // Leave Alias in original case + + _strupr(Param[3]); + _strupr(Param[4]); + _strupr(Param[5]); + _strupr(Param[6]); + _strupr(Param[7]); + + + Appl = atoi(Param[0]); + + if (Appl < 1 || Appl > NumberofAppls) return FALSE; + + App = &xxcfg.C_APPL[Appl - 1]; // Recs from zero + + if (Param[1][0] == 0) // No Application + return FALSE; + + if (strlen(Param[1]) > 12) return FALSE; + + memcpy(App->Command, Param[1], (int)strlen(Param[1])); + + xxcfg.C_BBS = 1; + + if (strlen(Param[2]) > 48) return FALSE; + + memcpy(App->CommandAlias, Param[2], (int)strlen(Param[2])); + + if (strlen(Param[3]) > 10) return FALSE; + + memcpy(App->ApplCall, Param[3], (int)strlen(Param[3])); + + if (strlen(Param[4]) > 10) return FALSE; + + memcpy(App->ApplAlias, Param[4], (int)strlen(Param[4])); + + App->ApplQual = atoi(Param[5]); + + if (strlen(Param[6]) > 10) return FALSE; + + memcpy(App->L2Alias, Param[6], (int)strlen(Param[6])); + + return TRUE; +} + +double xfmod(double p1, double p2) +{ + int temp; + + temp = (int)(p1/p2); + p1 = p1 -(p2 * temp); + return p1; +} + + BOOL ToLOC(double Lat, double Lon , char * Locator) + { + int i; + double S1, S2; + + Lon = Lon + 180; + Lat = Lat + 90; + + S1 = xfmod(Lon, 20); + + #pragma warning(push) + #pragma warning(disable : 4244) + + i = Lon / 20; + Locator[0] = 65 + i; + + S2 = xfmod(S1, 2); + + i = S1 / 2; + Locator[2] = 48 + i; + + i = S2 * 12; + Locator[4] = 65 + i; + + S1 = xfmod(Lat,10); + + i = Lat / 10; + Locator[1] = 65 + i; + + S2 = xfmod(S1,1); + + i = S1; + Locator[3] = 48 + i; + + i = S2 * 24; + Locator[5] = 65 + i; + + #pragma warning(pop) + + return TRUE; +} + +int FromLOC(char * Locator, double * pLat, double * pLon) +{ + double i; + double Lat, Lon; + + _strupr(Locator); + + *pLon = 0; + *pLat = 0; // in case invalid + + + // Basic validation for APRS positions + + // The first pair (a field) encodes with base 18 and the letters "A" to "R". + // The second pair (square) encodes with base 10 and the digits "0" to "9". + // The third pair (subsquare) encodes with base 24 and the letters "a" to "x". + + i = Locator[0]; + + if (i < 'A' || i > 'R') + return 0; + + Lon = (i - 65) * 20; + + i = Locator[2]; + if (i < '0' || i > '9') + return 0; + + Lon = Lon + (i - 48) * 2; + + i = Locator[4]; + if (i < 'A' || i > 'X') + return 0; + + Lon = Lon + (i - 65) / 12; + + i = Locator[1]; + if (i < 'A' || i > 'R') + return 0; + + Lat = (i - 65) * 10; + + i = Locator[3]; + if (i < '0' || i > '9') + return 0; + + Lat = Lat + (i - 48); + + i = Locator[5]; + if (i < 'A' || i > 'X') + return 0; + + Lat = Lat + (i - 65) / 24; + + if (Lon < 0 || Lon > 360) + Lon = 180; + if (Lat < 0 || Lat > 180) + Lat = 90; + + *pLon = Lon - 180; + *pLat = Lat - 90; + + return 1; +} diff --git a/.svn/pristine/05/05cb3c09ff4898bc77bda06f9cff0f0a53df0c80.svn-base b/.svn/pristine/05/05cb3c09ff4898bc77bda06f9cff0f0a53df0c80.svn-base index 7859400..20f185d 100644 --- a/.svn/pristine/05/05cb3c09ff4898bc77bda06f9cff0f0a53df0c80.svn-base +++ b/.svn/pristine/05/05cb3c09ff4898bc77bda06f9cff0f0a53df0c80.svn-base @@ -1,199 +1,199 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.svn/pristine/08/0811f3c6b3e6a6eafccc305825e33cd8c8621f75.svn-base b/.svn/pristine/08/0811f3c6b3e6a6eafccc305825e33cd8c8621f75.svn-base index eb68488..8ce98e6 100644 --- a/.svn/pristine/08/0811f3c6b3e6a6eafccc305825e33cd8c8621f75.svn-base +++ b/.svn/pristine/08/0811f3c6b3e6a6eafccc305825e33cd8c8621f75.svn-base @@ -1,4583 +1,4583 @@ -/* -Copyright 2001-2015 John Wiseman G8BPQ - -This file is part of LinBPQ/BPQ32. - -LinBPQ/BPQ32 is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -LinBPQ/BPQ32 is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses -*/ - -// Version 0.0.3.1 July 2016 -// Switch to Thunderforest tile server - -// Version 0.0.4.1 January 2019 -// Add option to set IS filter to map view automatically - -// Version 1.1.14.5 March 2020 -// Add option to run two instances of Linbpq and APRS - -// Version 1.1.14.6 Sept 2021 -// Use my Tile Servers - - -#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. -#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. -#endif - -#define LINBPQ - -#include "compatbits.h" - -#include "BPQAPRS.h" - -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include -#include -#define XK_MISCELLANY -#include - -#include -#include - -#include -#include - -#define LIBCONFIG_STATIC -#include "libconfig.h" - -#include -#include -#include - -#define PNG_SKIP_SETJMP_CHECK - -#include - -#define VOID void -#define UCHAR unsigned char -#define BOOL int -#define BYTE unsigned char -#define UINT unsigned int -#define TRUE 1 -#define FALSE 0 - - -#undef PNG_NO_STDIO - -int multiple = 0; - - -GtkWidget *dialog; -GtkWidget *window; -GtkWidget *dialog; -GtkWidget *window; -GtkWidget *box1; -GtkWidget *box2; -GtkWidget *box3; -GtkWidget *hbox; -GtkWidget *button; -GtkWidget *button2; -GtkWidget *checklabel; -GtkWidget *check1; -GtkWidget *check2; -GtkWidget *check3; -GtkWidget *check4; -GtkWidget *checkhbox; -GtkWidget *separator; -GtkWidget *table; -GtkWidget *vscrollbar; -GtkWidget *vscrollbar2; -GtkTextBuffer *text; -GtkTextBuffer *text2; -GtkWidget *entry; -GtkWidget *vpaned; -GtkWidget *frame1; -GtkWidget *frame2; -GtkWidget *view; -GtkWidget* scrolledwin; -GtkWidget *view2; -GtkWidget* scrolledwin2; -GtkWidget *box10; -GtkWidget *menubar; -GtkWidget *combo; -GtkWidget *label1, *label2; -GtkListStore *receiveditems; -GtkListStore *sentitems; - -GtkTreeModel *model; - -char MyFont[50] = "Monospace 10"; - -gchar *fontname; - -char RX_SOCK_PATH[] = "BPQAPRSrxsock"; -char TX_SOCK_PATH[] = "BPQAPRStxsock"; - -int sfd; -struct sockaddr_un my_addr, peer_addr; -socklen_t peer_addr_size; -int maxfd; - -struct SharedMem * SMEM; - -UCHAR * Shared; // Start of Shared Mememy -UCHAR * StnRecordBase; // Start of Station Records - -int AutoFilterTimer = 0; - -#define AUTOFILTERDELAY 20 // 20 secs - -VOID SecTimer(); -void plotLine(int x0, int y0, int x1, int y1, COLORREF rgb); -void SelectTXMsg (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data); -int LoadImageFile (void * hwnd, char * pstrPathName, - png_byte **ppbImage, int *pxImgSize, int *pyImgSize, - int *piChannels, png_color *pBkgColor); -BOOL PngLoadImage (char * pstrFileName, png_byte **ppbImageData, - png_uint_32 *piWidth, png_uint_32 *piHeight, int *piChannels, png_color *pBkgColor); - -BOOL RGBToJpegFile(char * fileName, BYTE *dataBuf, UINT widthPix, UINT height, BOOL color, int quality); -int XDestroyImage(XImage *ximage); - -int XLookupString(XKeyEvent *event_struct, char *buffer_return, int bytes_buffer, KeySym *keysym_return, void *status_in_out); - -void RefreshTXList(); - -static png_color bkgColor = {127, 127, 127}; - -struct SEM -{ - UINT Flag; - int Clashes; - int Gets; - int Rels; -}; - - -struct SEM Semaphore = {0, 0, 0, 0}; - -void GetSemaphore(struct SEM * Semaphore) -{ - // - // Wait for it to be free - // - - if (Semaphore->Flag != 0) - { - Semaphore->Clashes++; - } - -loop1: - - while (Semaphore->Flag != 0) - { - Sleep(10); - } - - // try to get semaphore - - if (__sync_lock_test_and_set(&Semaphore->Flag, 1) != 0) - - // Failed to get it - goto loop1; // try again; - - //Ok. got it - - Semaphore->Gets++; - - return; -} - -void FreeSemaphore(struct SEM * Semaphore) -{ - if (Semaphore->Flag == 0) - printf("Free Semaphore Called when Sem not held\n"); - - Semaphore->Rels++; - Semaphore->Flag = 0; - - return; -} - -char * strlop(char * buf, char delim) -{ - // Terminate buf at delim, and return rest of string - - char * ptr = strchr(buf, delim); - - if (ptr == NULL) return NULL; - - *(ptr)++=0; - - return ptr; -} - -unsigned long _beginthread(void(*start_address)(), unsigned stack_size, VOID * arglist) -{ - pthread_t thread; - - if (pthread_create(&thread, NULL, (void * (*)(void *))start_address, (void*) arglist) != 0) - perror("New Thread"); - else - pthread_detach(thread); - - return thread; -} - -int Sleep(int ms) -{ - usleep(ms * 1000); - return 0; -} - -struct OSMQUEUE OSMQueue = {NULL,0,0,0}; - -int OSMQueueCount = 0; - -static int cxWinSize = 788, cyWinSize = 788; -static int cxImgSize = 768, cyImgSize = 768; -static int topBorder = 30, bottomBorder = 0; -static int leftBorder = 2, rightBorder = 2; - -static int cImgChannels = 3; -static int ImgChannels; - -int Bytesperpixel = 4; - -int ExpireTime = 120; -int TrackExpireTime = 1440; -BOOL SuppressNullPosn = FALSE; -BOOL DefaultNoTracks = FALSE; -BOOL LocalTime = TRUE; -BOOL KM = FALSE; -BOOL AddViewToFilter = FALSE; - -char ISFilter[1000] = "m/50 u/APBPQ*"; - -int SlowTimer = 0; - -BOOL CreateJPEG = TRUE; -int JPEGInterval = 300; -int JPEGCounter = 0; -char JPEGFileName[MAX_PATH] = "BPQAPRS/HTML/APRSImage.jpg"; - -char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; - - -Display * display; -Window root, win; -GC gc; -XImage * image, * popupimage; - -int SetBaseX = 0; // Lowest Tiles in currently loaded set -int SetBaseY = 0; - -int TileX = 0; -int TileY = 0; - -int Zoom = 2; - -int MaxZoom = 16; - -int MapCentreX = 256; -int MapCentreY = 256; - -int MouseX, MouseY; -int PopupX, PopupY; - -double MouseLat, MouseLon; - -BOOL NeedRefresh = FALSE; -int NeedRedraw = 0; - -int ScrollX = 128; -int ScrollY = 128; - -int WindowX = 100, WindowY = 100; // Position of window on screen -int WindowWidth = 788; -int WindowHeight = 788; - -BOOL popupActive = FALSE; -BOOL selActive = FALSE; - -char OSMDir[256] = "BPQAPRS/OSMTiles"; - -struct STATIONRECORD ** StationRecords = NULL; -struct STATIONRECORD * ControlRecord; - -int StationCount; - - -UCHAR NextSeq = 1; - -char APRSCall[10]; -char LoppedAPRSCall[10]; -char BaseCall[10]; - - -// Image chunks are 256 rows of 3 * 256 bytes - -// Read 8 * 8 files, and copy to a 2048 * 3 * 2048 array. The display scrolls over this area, and -// it is refreshed when window approaches the edge of the array. - -int WIDTH; -int HEIGHT; - -int WIDTHTILES = 4; -int HEIGHTTILES = 4; - -UCHAR * Image = NULL; -UCHAR * iconImage = NULL; -UCHAR * PopupImage = NULL; - -BOOL ImageChanged = 0; - -int RetryCount = 7; -int RetryIntervals[] = {0, 512, 256, 128, 64, 32, 16, 8}; - -// Station Name Font - -const unsigned char ASCII[][5] = { -//const u08 ASCII[][5] = { - {0x00, 0x00, 0x00, 0x00, 0x00} // 20 - ,{0x00, 0x00, 0x5f, 0x00, 0x00} // 21 ! - ,{0x00, 0x07, 0x00, 0x07, 0x00} // 22 " - ,{0x14, 0x7f, 0x14, 0x7f, 0x14} // 23 # - ,{0x24, 0x2a, 0x7f, 0x2a, 0x12} // 24 $ - ,{0x23, 0x13, 0x08, 0x64, 0x62} // 25 % - ,{0x36, 0x49, 0x55, 0x22, 0x50} // 26 & - ,{0x00, 0x05, 0x03, 0x00, 0x00} // 27 ' - ,{0x00, 0x1c, 0x22, 0x41, 0x00} // 28 ( - ,{0x00, 0x41, 0x22, 0x1c, 0x00} // 29 ) - ,{0x14, 0x08, 0x3e, 0x08, 0x14} // 2a * - ,{0x08, 0x08, 0x3e, 0x08, 0x08} // 2b + - ,{0x00, 0x50, 0x30, 0x00, 0x00} // 2c , - ,{0x08, 0x08, 0x08, 0x08, 0x08} // 2d - - ,{0x00, 0x60, 0x60, 0x00, 0x00} // 2e . - ,{0x20, 0x10, 0x08, 0x04, 0x02} // 2f / - ,{0x3e, 0x51, 0x49, 0x45, 0x3e} // 30 0 - ,{0x00, 0x42, 0x7f, 0x40, 0x00} // 31 1 - ,{0x42, 0x61, 0x51, 0x49, 0x46} // 32 2 - ,{0x21, 0x41, 0x45, 0x4b, 0x31} // 33 3 - ,{0x18, 0x14, 0x12, 0x7f, 0x10} // 34 4 - ,{0x27, 0x45, 0x45, 0x45, 0x39} // 35 5 - ,{0x3c, 0x4a, 0x49, 0x49, 0x30} // 36 6 - ,{0x01, 0x71, 0x09, 0x05, 0x03} // 37 7 - ,{0x36, 0x49, 0x49, 0x49, 0x36} // 38 8 - ,{0x06, 0x49, 0x49, 0x29, 0x1e} // 39 9 - ,{0x00, 0x36, 0x36, 0x00, 0x00} // 3a : - ,{0x00, 0x56, 0x36, 0x00, 0x00} // 3b ; - ,{0x08, 0x14, 0x22, 0x41, 0x00} // 3c < - ,{0x14, 0x14, 0x14, 0x14, 0x14} // 3d = - ,{0x00, 0x41, 0x22, 0x14, 0x08} // 3e > - ,{0x02, 0x01, 0x51, 0x09, 0x06} // 3f ? - ,{0x32, 0x49, 0x79, 0x41, 0x3e} // 40 @ - ,{0x7e, 0x11, 0x11, 0x11, 0x7e} // 41 A - ,{0x7f, 0x49, 0x49, 0x49, 0x36} // 42 B - ,{0x3e, 0x41, 0x41, 0x41, 0x22} // 43 C - ,{0x7f, 0x41, 0x41, 0x22, 0x1c} // 44 D - ,{0x7f, 0x49, 0x49, 0x49, 0x41} // 45 E - ,{0x7f, 0x09, 0x09, 0x09, 0x01} // 46 F - ,{0x3e, 0x41, 0x49, 0x49, 0x7a} // 47 G - ,{0x7f, 0x08, 0x08, 0x08, 0x7f} // 48 H - ,{0x00, 0x41, 0x7f, 0x41, 0x00} // 49 I - ,{0x20, 0x40, 0x41, 0x3f, 0x01} // 4a J - ,{0x7f, 0x08, 0x14, 0x22, 0x41} // 4b K - ,{0x7f, 0x40, 0x40, 0x40, 0x40} // 4c L - ,{0x7f, 0x02, 0x0c, 0x02, 0x7f} // 4d M - ,{0x7f, 0x04, 0x08, 0x10, 0x7f} // 4e N - ,{0x3e, 0x41, 0x41, 0x41, 0x3e} // 4f O - ,{0x7f, 0x09, 0x09, 0x09, 0x06} // 50 P - ,{0x3e, 0x41, 0x51, 0x21, 0x5e} // 51 Q - ,{0x7f, 0x09, 0x19, 0x29, 0x46} // 52 R - ,{0x46, 0x49, 0x49, 0x49, 0x31} // 53 S - ,{0x01, 0x01, 0x7f, 0x01, 0x01} // 54 T - ,{0x3f, 0x40, 0x40, 0x40, 0x3f} // 55 U - ,{0x1f, 0x20, 0x40, 0x20, 0x1f} // 56 V - ,{0x3f, 0x40, 0x38, 0x40, 0x3f} // 57 W - ,{0x63, 0x14, 0x08, 0x14, 0x63} // 58 X - ,{0x07, 0x08, 0x70, 0x08, 0x07} // 59 Y - ,{0x61, 0x51, 0x49, 0x45, 0x43} // 5a Z - ,{0x00, 0x7f, 0x41, 0x41, 0x00} // 5b [ - ,{0x02, 0x04, 0x08, 0x10, 0x20} // 5c - ,{0x00, 0x41, 0x41, 0x7f, 0x00} // 5d ] - ,{0x04, 0x02, 0x01, 0x02, 0x04} // 5e ^ - ,{0x40, 0x40, 0x40, 0x40, 0x40} // 5f _ - ,{0x00, 0x01, 0x02, 0x04, 0x00} // 60 ` - ,{0x20, 0x54, 0x54, 0x54, 0x78} // 61 a - ,{0x7f, 0x48, 0x44, 0x44, 0x38} // 62 b - ,{0x38, 0x44, 0x44, 0x44, 0x20} // 63 c - ,{0x38, 0x44, 0x44, 0x48, 0x7f} // 64 d - ,{0x38, 0x54, 0x54, 0x54, 0x18} // 65 e - ,{0x08, 0x7e, 0x09, 0x01, 0x02} // 66 f - ,{0x0c, 0x52, 0x52, 0x52, 0x3e} // 67 g - ,{0x7f, 0x08, 0x04, 0x04, 0x78} // 68 h - ,{0x00, 0x44, 0x7d, 0x40, 0x00} // 69 i - ,{0x20, 0x40, 0x44, 0x3d, 0x00} // 6a j - ,{0x7f, 0x10, 0x28, 0x44, 0x00} // 6b k - ,{0x00, 0x41, 0x7f, 0x40, 0x00} // 6c l - ,{0x7c, 0x04, 0x18, 0x04, 0x78} // 6d m - ,{0x7c, 0x08, 0x04, 0x04, 0x78} // 6e n - ,{0x38, 0x44, 0x44, 0x44, 0x38} // 6f o - ,{0x7c, 0x14, 0x14, 0x14, 0x08} // 70 p - ,{0x08, 0x14, 0x14, 0x18, 0x7c} // 71 q - ,{0x7c, 0x08, 0x04, 0x04, 0x08} // 72 r - ,{0x48, 0x54, 0x54, 0x54, 0x20} // 73 s - ,{0x04, 0x3f, 0x44, 0x40, 0x20} // 74 t - ,{0x3c, 0x40, 0x40, 0x20, 0x7c} // 75 u - ,{0x1c, 0x20, 0x40, 0x20, 0x1c} // 76 v - ,{0x3c, 0x40, 0x30, 0x40, 0x3c} // 77 w - ,{0x44, 0x28, 0x10, 0x28, 0x44} // 78 x - ,{0x0c, 0x50, 0x50, 0x50, 0x3c} // 79 y - ,{0x44, 0x64, 0x54, 0x4c, 0x44} // 7a z - ,{0x00, 0x08, 0x36, 0x41, 0x00} // 7b { - ,{0x00, 0x00, 0x7f, 0x00, 0x00} // 7c | - ,{0x00, 0x41, 0x36, 0x08, 0x00} // 7d } - ,{0x10, 0x08, 0x08, 0x10, 0x08} // 7e ~ - ,{0x78, 0x46, 0x41, 0x46, 0x78} // 7f DEL -}; - -COLORREF Colours[256] = {0, RGB(0,0,255), RGB(0,128,0), RGB(0,128,192), - RGB(0,192,0), RGB(0,192,255), RGB(0,255,0), RGB(128,0,128), - RGB(128,64,0), RGB(128,128,128), RGB(192,0,0), RGB(192,0,255), - RGB(192,64,128), RGB(192,128,255), RGB(255,0,0), RGB(255,0,255), // 81 - RGB(255,64,0), RGB(255,64,128), RGB(255,64,192), RGB(255,128,0)}; - - - - -struct my_error_mgr { - struct jpeg_error_mgr pub; /* "public" fields */ - - jmp_buf setjmp_buffer; /* for return to caller */ -}; - -typedef struct my_error_mgr * my_error_ptr; - -void my_error_exit (j_common_ptr cinfo) -{ - /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */ - my_error_ptr myerr = (my_error_ptr) cinfo->err; - - char buffer[JMSG_LENGTH_MAX]; - /* Create the message */ - (*cinfo->err->format_message) (cinfo, buffer); - - /* Always display the message. */ - printf("JPEG Fatal Error"); - - - /* Return control to the setjmp point */ - longjmp(myerr->setjmp_buffer, 1); -} - -int memicmp(unsigned char *a, unsigned char *b, int n) -{ - if (n) - { - while (n && toupper(*a) == toupper(*b)) - n--, a++, b++; - - if (n) - return toupper(*a) - toupper(*b); - } - return 0; -} -int stricmp(const unsigned char * pStr1, const unsigned char *pStr2) -{ - unsigned char c1, c2; - int v; - - if (pStr1 == NULL) - { - return 1; - } - - - do { - c1 = *pStr1++; - c2 = *pStr2++; - /* The casts are necessary when pStr1 is shorter & char is signed */ - v = tolower(c1) - tolower(c2); - } while ((v == 0) && (c1 != '\0') && (c2 != '\0') ); - - return v; -} - -char * strupr(char* s) -{ - char* p = s; - - if (s == 0) - return 0; - - while (*p = toupper( *p )) p++; - return s; -} - -// Return coorinates in tiles. - -double long2x(double lon, int z) -{ - return (lon + 180.0) / 360.0 * pow(2.0, z); -} - -double lat2y(double lat, int z) -{ - return (1.0 - log( tan(lat * M_PI/180.0) + 1.0 / cos(lat * M_PI/180.0)) / M_PI) / 2.0 * pow(2.0, z); -} - -double tilex2long(double x, int z) -{ - return x / pow(2.0, z) * 360.0 - 180; -} - -double tiley2lat(double y, int z) -{ - double n = M_PI - 2.0 * M_PI * y / pow(2.0, z); - return 180.0 / M_PI * atan(0.5 * (exp(n) - exp(-n))); -} - -void GetCornerLatLon(double * TLLat, double * TLLon, double * BRLat, double * BRLon) -{ - int X = ScrollX; - int Y = ScrollY; - - *TLLat = tiley2lat(SetBaseY + (Y / 256.0), Zoom); - *TLLon = tilex2long(SetBaseX + (X / 256.0), Zoom); - - X = ScrollX + cxWinSize; - Y = ScrollY + cyWinSize; - - *BRLat = tiley2lat(SetBaseY + (Y / 256.0), Zoom); - *BRLon = tilex2long(SetBaseX + (X / 256.0), Zoom); -} - - -void GetMouseLatLon(double * Lat, double * Lon) -{ - int X = ScrollX + MouseX; - int Y = ScrollY + MouseY; - - *Lat = tiley2lat(SetBaseY + (Y / 256.0), Zoom); - *Lon = tilex2long(SetBaseX + (X / 256.0), Zoom); -} - -BOOL GetLocPixels(double Lat, double Lon, int * X, int * Y) -{ - // Get the pixel offet of supplied location in current image. - - // If location is outside current image, return FAlSE - - int TileX; - int TileY; - int OffsetX, OffsetY; - double FX; - double FY; - - // if TileX or TileY are outside the window, return null - - FX = long2x(Lon, Zoom); - TileX = (int)floor(FX); - OffsetX = TileX - SetBaseX; - - if (OffsetX < 0 || OffsetX > 7) - return FALSE; - - FY = lat2y(Lat, Zoom); - TileY = (int)floor(FY); - OffsetY = TileY - SetBaseY; - - if (OffsetY < 0 || OffsetY > 7) - return FALSE; - - FX -= TileX; - FX = FX * 256.0; - - *X = (int)FX + 256 * OffsetX; - - FY -= TileY; - FY = FY * 256.0; - - *Y = (int)FY + 256 * OffsetY; - - return TRUE; -} - -int long2tilex(double lon, int z) -{ - return (int)(floor((lon + 180.0) / 360.0 * pow(2.0, z))); -} - -int lat2tiley(double lat, int z) -{ - return (int)(floor((1.0 - log( tan(lat * M_PI/180.0) + 1.0 / cos(lat * M_PI/180.0)) / M_PI) / 2.0 * pow(2.0, z))); -} - - -BOOL CentrePositionToMouse(double Lat, double Lon) -{ - // Positions specified location at the mouse - - int X, Y; - - SetBaseX = long2tilex(Lon, Zoom) - 2; - SetBaseY = lat2tiley(Lat, Zoom) - 2; // Set Location at middle - - if (GetLocPixels(Lat, Lon, &X, &Y) == FALSE) - return FALSE; // Off map - - ScrollX = X - cxWinSize/2; - ScrollY = Y - cyWinSize/2; - - -// Map is now centered at loc cursor was at - -// Need to move by distance mouse is from centre - - // if ScrollX, Y are zero, the centre of the map corresponds to 1024, 1024 - -// ScrollX -= 1024 - X; // Posn to centre -// ScrollY -= 1024 - Y; - - ScrollX += cxWinSize/2 - MouseX; - ScrollY += cyWinSize/2 - MouseY; - - // May Need to move image - - while(ScrollX < 0) - { - SetBaseX--; - ScrollX += 256; - } - - while(ScrollY < 0) - { - SetBaseY--; - ScrollY += 256; - } - - while(ScrollX > 255) - { - SetBaseX++; - ScrollX -= 256; - } - - while(ScrollY > 255) - { - SetBaseY++; - ScrollY -= 256; - } - - AutoFilterTimer = AUTOFILTERDELAY; // Update filter if no change for 30 secs - - return TRUE; -} - -SOCKADDR_IN destaddr1 = {0}; -SOCKADDR_IN destaddr2 = {0}; - -unsigned int ipaddr = 0; - -//char Host[] = "tile.openstreetmap.org"; - -//char Host[] = "oatile1.mqcdn.com"; //SAT -//char Host[] = "otile1.mqcdn.com"; - -//char Host[] = "tile.thunderforest.com"; - -char Host[] = "server.g8bpq.net"; -char Host1[] = "server1.g8bpq.net"; -char Host2[] = "server2.g8bpq.net"; - -int Host1Down = 0; -int Host2Down = 0; - -char mapStyle[64] = "outdoors"; //"neighbourhood mobile-atlas - - -char HeaderTemplate[] = "Accept: */*\r\nHost: %s\r\nConnection: close\r\nContent-Length: 0\r\nUser-Agent: BPQ32(G8BPQ)\r\n\r\n"; - - -VOID ResolveThread() -{ - struct hostent * HostEnt; - int err; - -// while (TRUE) - { - // Resolve Name if needed - - HostEnt = gethostbyname(Host1); - - if (!HostEnt) - { - err = WSAGetLastError(); - printf("Resolve Failed for %s %d %x\n", Host1, err); - } - else - { - memcpy(&destaddr1.sin_addr.s_addr,HostEnt->h_addr,4); - } - - HostEnt = gethostbyname(Host2); - - if (!HostEnt) - { - err = WSAGetLastError(); - printf("Resolve Failed for %s %d %x\n", Host2, err); - } - else - { - memcpy(&destaddr2.sin_addr.s_addr,HostEnt->h_addr,4); - } -/// Sleep(60 * 15 * 1000); - } -} - - - -VOID OSMGet(int x, int y, int zoom) -{ - struct OSMQUEUE * OSMRec = malloc(sizeof(struct OSMQUEUE)); - - GetSemaphore(&Semaphore); - - OSMQueueCount++; - - OSMRec->Next = OSMQueue.Next; - OSMQueue.Next = OSMRec; - OSMRec->x = x; - OSMRec->y = y; - OSMRec->Zoom = zoom; - - FreeSemaphore(&Semaphore); -} - -VOID RefreshTile(char * FN, int TileZoom, int Tilex, int Tiley); - -VOID OSMThread() -{ - // Request a page from OSM - - char FN[256]; - char Tile[80]; - struct OSMQUEUE * OSMRec; - int Zoom, x, y; - - SOCKET sock; - SOCKADDR_IN sinx; - int addrlen=sizeof(sinx); - int err; - u_long param=1; - BOOL bcopt=TRUE; - char Request[100]; - char Header[256]; - UCHAR Buffer[200000]; - int Len, InputLen = 0; - UCHAR * ptr; - int inptr = 0; - struct stat STAT; - FILE * Handle; - - destaddr1.sin_family = AF_INET; - destaddr1.sin_port = htons(7381); - destaddr2.sin_family = AF_INET; - destaddr2.sin_port = htons(7381); - - while (TRUE) - { - while (OSMQueue.Next) - { - GetSemaphore(&Semaphore); - - OSMRec = OSMQueue.Next; - OSMQueue.Next = OSMRec->Next; - - OSMQueueCount--; - - FreeSemaphore(&Semaphore); - - x = OSMRec->x; - y = OSMRec->y; - Zoom = OSMRec->Zoom; - - free(OSMRec); - -// wsprintf(Tile, "/%02d/%d/%d.png", Zoom, x, y); -// wsprintf(Tile, "/tiles/1.0.0/sat/%02d/%d/%d.jpg", Zoom, x, y); -// sprintf(Tile, "/tiles/1.0.0/osm/%02d/%d/%d.jpg", Zoom, x, y); - -// sprintf(Tile, "/%s/%d/%d/%d.png?apikey=41ab899ed1fd4d09b11da7caf3a48e1f", mapStyle, Zoom, x, y); - - sprintf(Tile, "/styles/klokantech-basic/%d/%d/%d.png", Zoom, x, y); - - sprintf(FN, "%s/%02d/%d/%d.png", OSMDir, Zoom, x, y); - - if (stat(FN, &STAT) == 0) - { - printf(" File %s Exists - skipping\n", FN); - continue; - } - - printf("Getting %s\n", FN); - - Len = sprintf(Request, "GET %s HTTP/1.0\r\n", Tile); - - // Allocate a Socket entry - - sock=socket(AF_INET,SOCK_STREAM,0); - - if (sock == INVALID_SOCKET) - return; - - setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); - - if (Host1Down == 0) - { - if (connect(sock,(LPSOCKADDR) &destaddr1, sizeof(destaddr1)) != 0) - { - printf("OSM GET Connect to %s Failed %d\n", Host1, errno); - Host1Down = 600; // Don't try again for 10 mins - } - else - goto ConnectOK; - } - if (Host2Down == 0) - { - if (connect(sock,(LPSOCKADDR) &destaddr2, sizeof(destaddr2)) != 0) - { - printf("OSM GET Connect to %s Failed %d\n", Host2, errno); - Host1Down = 600; // Don't try again for 10 mins - } - else - goto ConnectOK; - } - - // - // Neither available or connect failed to both - // - - // Reduce retry timers - - if (Host1Down > 60 && Host2Down > 60) - { - Host1Down = 60; - Host2Down = 60; - } - - break; - -ConnectOK: - -//GET /15/15810/9778.png HTTP/1.0 -//Accept: */* -//Host: tile.openstreetmap.org -//Connection: close -//Content-Length: 0 -//User-Agent: APRSIS32(G8BPQ) - - InputLen = 0; - inptr = 0; - - send(sock, Request, Len, 0); - sprintf(Header, HeaderTemplate, Host); - send(sock, Header, strlen(Header), 0); - - while (InputLen != -1) - { - InputLen = recv(sock, &Buffer[inptr], 200000 - inptr, 0); - - if (InputLen > 0) - inptr += InputLen; - else - { - // File Complete?? - - if (strstr(Buffer, " 200 OK")) - { - ptr = strstr(Buffer, "Content-Length:"); - - if (ptr) - { - int FileLen = atoi(ptr + 15); - ptr = strstr(Buffer, "\r\n\r\n"); - - if (ptr) - { - ptr += 4; - char Dir[256]; - - if (FileLen == inptr - (ptr - Buffer)) - { - // File is OK - - int cnt; - - Handle = fopen(FN, "wb"); - - if (Handle) - { - fwrite(ptr, 1, FileLen, Handle); - fclose(Handle); - printf("Tile %s Loaded\n", FN); - RefreshTile(FN, Zoom, x, y); - break; - } - - if (errno != 2) // Bad Path - { - printf("Create %s failed %d\n", FN, errno); - perror("fopen"); - break; - } - - sprintf(Dir, "%s/%02d/%d", OSMDir, Zoom, x); - - if (mkdir(Dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) - { - printf("Error Creating %s\n", FN); - perror("mkdir"); - break; - } - - // Retry Create - - Handle = fopen(FN, "wb"); - - if (Handle) - { - fwrite(ptr, 1, FileLen, Handle); - fclose(Handle); - - printf("Tile %s Loaded\n", FN); - RefreshTile(FN, Zoom, x, y); - break; - } - - printf("Create %s falled\n", FN); - perror("fopen"); - break; - } - } - } - } - printf("OSM GET Bad Response %s ", Buffer); - sprintf(FN, "%s/DummyTile.jpg", OSMDir); - RefreshTile(FN, Zoom, x, y); - - break; - } - } - close(sock); - } - - // Queue is empty - - sleep(1); -} -} - -double radians(double Degrees) -{ - return M_PI * Degrees / 180; -} -double degrees(double Radians) -{ - return Radians * 180 / M_PI; -} - - - -double Distance(double laa, double loa) -{ - double lah = ControlRecord->Lat; - double loh = ControlRecord->Lon; - double dist; -/* - -'Great Circle Calculations. - -'dif = longitute home - longitute away - - -' (this should be within -180 to +180 degrees) -' (Hint: This number should be non-zero, programs should check for -' this and make dif=0.0001 as a minimum) -'lah = latitude of home -'laa = latitude of away - -'dis = ArcCOS(Sin(lah) * Sin(laa) + Cos(lah) * Cos(laa) * Cos(dif)) -'distance = dis / 180 * pi * ERAD -'angle = ArcCOS((Sin(laa) - Sin(lah) * Cos(dis)) / (Cos(lah) * Sin(dis))) - -'p1 = 3.1415926535: P2 = p1 / 180: Rem -- PI, Deg =>= Radians -*/ - - loh = radians(loh); lah = radians(lah); - loa = radians(loa); laa = radians(laa); - - dist = 60*degrees(acos(sin(lah) * sin(laa) + cos(lah) * cos(laa) * cos(loa-loh))) * 1.15077945; - - if (KM) - dist *= 1.60934; - - return dist; -} - -double Bearing(double lat2, double lon2) -{ - double lat1 = ControlRecord->Lat; - double lon1 = ControlRecord->Lon; - double dlat, dlon, TC1; - - lat1 = radians(lat1); - lat2 = radians(lat2); - lon1 = radians(lon1); - lon2 = radians(lon2); - - dlat = lat2 - lat1; - dlon = lon2 - lon1; - - if (dlat == 0 || dlon == 0) return 0; - - TC1 = atan((sin(lon1 - lon2) * cos(lat2)) / (cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon1 - lon2))); - TC1 = degrees(TC1); - - if (fabs(TC1) > 89.5) if (dlon > 0) return 90; else return 270; - - if (dlat > 0) - { - if (dlon > 0) return -TC1; - if (dlon < 0) return 360 - TC1; - return 0; - } - - if (dlat < 0) - { - if (dlon > 0) return TC1 = 180 - TC1; - if (dlon < 0) return TC1 = 180 - TC1; // 'ok? - return 180; - } - - return 0; - -} - - - -VOID DecodeWXReport(struct APRSConnectionInfo * sockptr, char * WX) -{ - UCHAR * ptr = strchr(WX, '_'); - char Type; - int Val; - - if (ptr == 0) - return; - - sockptr->WindDirn = atoi(++ptr); - ptr += 4; - sockptr->WindSpeed = atoi(ptr); - ptr += 3; -WXLoop: - - Type = *(ptr++); - - if (*ptr =='.') // Missing Value - { - while (*ptr == '.') - ptr++; - - goto WXLoop; - } - - Val = atoi(ptr); - - switch (Type) - { - case 'c': // = wind direction (in degrees). - - sockptr->WindDirn = Val; - break; - - case 's': // = sustained one-minute wind speed (in mph). - - sockptr->WindSpeed = Val; - break; - - case 'g': // = gust (peak wind speed in mph in the last 5 minutes). - - sockptr->WindGust = Val; - break; - - case 't': // = temperature (in degrees Fahrenheit). Temperatures below zero are expressed as -01 to -99. - - sockptr->Temp = Val; - break; - - case 'r': // = rainfall (in hundredths of an inch) in the last hour. - - sockptr->RainLastHour = Val; - break; - - case 'p': // = rainfall (in hundredths of an inch) in the last 24 hours. - - sockptr->RainLastDay = Val; - break; - - case 'P': // = rainfall (in hundredths of an inch) since midnight. - - sockptr->RainToday = Val; - break; - - case 'h': // = humidity (in %. 00 = 100%). - - sockptr->Humidity = Val; - break; - - case 'b': // = barometric pressure (in tenths of millibars/tenths of hPascal). - - sockptr->Pressure = Val; - break; - - default: - - return; - } - while(isdigit(*ptr)) - { - ptr++; - } - - if (*ptr != ' ') - goto WXLoop; -} - -struct STATIONRECORD * FindStation(char * Call, BOOL AddIfNotFount) -{ - int i = 0; - struct STATIONRECORD * find; - struct STATIONRECORD * ptr; - struct STATIONRECORD * last = NULL; - int sum = 0; - - if (StationRecords == 0) - return FALSE; - - if (strlen(Call) > 9) - Call[9] = 0; - - find = *StationRecords; - while(find) - { - if (strlen(find->Callsign) > 9) - find->Callsign[9] = 0; - - if (strcmp(find->Callsign, Call) == 0) - return find; - - last = find; - find = find->Next; - i++; - } - - // Not found - add on end - -/* - if (AddIfNotFount) - { - // Get first from station record pool - - ptr = StationRecordPool; - - if (ptr) - { - StationRecordPool = ptr->Next; // Unchain - StationCount++; - } - else - { - // Get First from Stations - - ptr = *StationRecords; - if (ptr) - *StationRecords = ptr->Next; - } - - if (ptr == NULL) return NULL; - - memset(ptr, 0, sizeof(struct STATIONRECORD)); - -// EnterCriticalSection(&Crit); - - if (*StationRecords == NULL) - *StationRecords = ptr; - else - last->Next = ptr; - -// LeaveCriticalSection(&Crit); - - // Debugprintf("APRS Add Stn %s Station Count = %d", Call, StationCount); - - strcpy(ptr->Callsign, Call); - ptr->TimeAdded = time(NULL); - ptr->Index = i; - ptr->NoTracks = DefaultNoTracks; - - for (i = 0; i < 9; i++) - sum += Call[i]; - - sum %= 20; - - ptr->TrackColour = sum; - ptr->Moved = TRUE; - - return ptr; - } - else - */ - return NULL; -} - -int PopupHeight; -int PopupWidth; -int PopupLeft; -int PopupTop; -struct STATIONRECORD * popupStn; -struct STATIONRECORD * List[1000] = {0}; - - -VOID CreateStationPopup(struct STATIONRECORD * ptr, int RelX, int RelY) -{ - char Msg[80]; - int Len = 130; - int Line = 12; - struct tm * TM; - int x, y; - - PopupLeft = RelX - 10; - PopupTop = RelY - 30; - - if (PopupLeft + 400 > cxWinSize) - PopupLeft = cxWinSize - 405; - - if (PopupTop + 150> cyWinSize) - PopupTop= cyWinSize - 165; - - popupActive = TRUE; - popupStn = ptr; - PopupHeight = 200; - PopupWidth = 350; - - XClearArea(display, win, PopupLeft, PopupTop, 350, 200, FALSE); - XDrawRectangle(display, win, gc, PopupLeft, PopupTop, 350, 200); - - x = PopupLeft; - y = PopupTop; - - if (LocalTime) - TM = localtime(&ptr->TimeLastUpdated); - else - TM = gmtime(&ptr->TimeLastUpdated); - - Len = sprintf(Msg, "Last Heard: %.2d:%.2d:%.2d on Port %d", - TM->tm_hour, TM->tm_min, TM->tm_sec, ptr->LastPort); - - XDrawImageString(display, win, gc, x + 2, y + Line, ptr->Callsign, strlen(ptr->Callsign)); - Line += 12; - - XDrawImageString(display, win, gc, x + 2, y + Line, ptr->Path, strlen(ptr->Path)); - Line += 12; - -// XDrawImageString(display, win, gc, x + 2, y + Line, ptr->Status, 40); -// Line += 12; - - XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); - Line += 12; - - -// Item.pszText = ptr->LastPacket; - - Len = sprintf(Msg, "Distance %6.1f Bearing %3.0f Course %1.0f Speed %3.1f", - Distance(ptr->Lat, ptr->Lon), - Bearing(ptr->Lat, ptr->Lon), ptr->Course, ptr->Speed); - - XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); - Line += 12; - - - if (ptr->LastWXPacket[0]) - { - //display wx info - - struct APRSConnectionInfo temp; - - memset(&temp, 0, sizeof(temp)); - - DecodeWXReport(&temp, ptr->LastWXPacket); - - Len = sprintf(Msg, "Wind Speed %d MPH", temp.WindSpeed); - XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); - Line += 12; - - Len = sprintf(Msg, "Wind Gust %d MPH", temp.WindGust); - XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); - Line += 12; - - Len = sprintf(Msg, "Wind Direction %d°", temp.WindDirn); - XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); - Line += 12; - - Len = sprintf(Msg, "Temperature %d°F", temp.Temp); - XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); - Line += 12; - - Len = sprintf(Msg, "Pressure %05.1f", temp.Pressure /10.0); - XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); - Line += 12; - - Len = sprintf(Msg, "Humidity %d%%", temp.Humidity); - XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); - Line += 12; - } - -/* -Rain last hour##RAIN_HOUR_IN##" -Rain today##RAIN_TODAY_IN##" -Rain last 24 hours##RAIN_24_IN##" - -*/ -} - -VOID GetStationFromList(int MouseX, int MouseY) -{ - int RelX = MouseX + leftBorder; - int RelY = MouseY + topBorder; - - int index = (RelY - PopupTop) /12; - - if (List[index]) - { - selActive = FALSE; - CreateStationPopup(List[index], RelX, RelY); - } -} - -VOID FindStationsByPixel(int MouseX, int MouseY) -{ - int j=0; - struct STATIONRECORD * ptr = *StationRecords; - int RelX = MouseX - ScrollX + leftBorder; - int RelY = MouseY - ScrollY + topBorder; - - if (popupActive || selActive) - { - // if mouse within popup, leave alone - - if (RelX > PopupLeft && RelX < (PopupLeft + PopupWidth) && - RelY > PopupTop && RelY < (PopupTop + PopupHeight)) - return; - } - - while(ptr && j < 999) - { - if (abs((ptr->DispX - MouseX)) < 4 && abs((ptr->DispY - MouseY)) < 4) - List[j++] = ptr; - - ptr = ptr->Next; - } - - if (j == 0) - { - if (popupActive) - { - XPutImage (display, win, gc, image, ScrollX, ScrollY, leftBorder, topBorder, cxImgSize, cyImgSize); - popupActive = 0; - } - - if (selActive) - { - XPutImage (display, win, gc, image, ScrollX, ScrollY, leftBorder, topBorder, cxImgSize, cyImgSize); - selActive = 0; - } - return; - } - - // If only one, display info popup, else display selection popup - - if (popupActive || selActive) - return; // Already on display - - if (j == 1) - { - CreateStationPopup(List[0], RelX, RelY); - } - else - { - char Msg[80]; - int Line = 12; - int i; - - PopupLeft = RelX - 10; - PopupTop = RelY - 30; - - if (j > 20) - j = 20; - - PopupHeight = j * 12 + 4; - PopupWidth = 80; - - if (PopupLeft + 80 > cxWinSize) - PopupLeft = cxWinSize - 85; - - if (PopupTop + PopupHeight > cyWinSize) - PopupTop = cyWinSize - PopupHeight; - - selActive = TRUE; - - XClearArea(display, win, PopupLeft, PopupTop, 80, PopupHeight, FALSE); - XDrawRectangle(display, win, gc, PopupLeft, PopupTop, 80, PopupHeight); - - for (i = 0; i < j; i++) - { - memset(Msg, ' ', 12); - memcpy(Msg, List[i]->Callsign, strlen(List[i]->Callsign)); - XDrawImageString(display, win, gc, PopupLeft + 2, PopupTop + Line, Msg, 11); - Line += 12; - } - } -} - -VOID DrawCharacter(int X, int Y, int j, unsigned char chr) -{ - // Font is 5 bits wide x 8 high. Each byte of font contains one column, so 5 bytes per char - - int Pointer, i, c, index, bit, mask; - - Pointer = ((Y - 5) * WIDTH * Bytesperpixel) + ((X + 11) * Bytesperpixel) + (j * 6 * Bytesperpixel); - - mask = 1; - - for (i = 0; i < 2; i++) - { - for (index = 0 ; index < 6 ; index++) - { - Image[Pointer++] = 255; // Blank lines above chars - Image[Pointer++] = 255; - if (Bytesperpixel == 4) - { - Image[Pointer++] = 255; - Pointer++; - } - } - - Pointer += (WIDTH - 6) * Bytesperpixel; - } - - // Pointer = ((Y - 3) * 2048 * 3) + (X * 3) + 36 + (j * 18); - - for (i = 0; i < 7; i++) - { - Image[Pointer++] = 255; // Blank col between chars - Image[Pointer++] = 255; - if (Bytesperpixel == 4) - { - Image[Pointer++] = 255; - Pointer++; - } - for (index = 0 ; index < 5 ; index++) - { - c = ASCII[chr - 0x20][index]; // Font data - bit = c & mask; - - if (bit) - { - Image[Pointer++] = 0; - Image[Pointer++] = 0; - if (Bytesperpixel == 4) - { - Image[Pointer++] = 0; - Pointer++; - } - } - else - { - Image[Pointer++] = 255; - Image[Pointer++] = 255; - if (Bytesperpixel == 4) - { - Image[Pointer++] = 255; - Pointer++; - } - } - } - mask <<= 1; - Pointer += (WIDTH - 6) * Bytesperpixel; - } - - // Pointer = ((Y - 3) * 2048 * 3) + (X * 3) + 36 + (j * 18); - - mask = 1; - - for (i = 0; i < 2; i++) - { - for (index = 0 ; index < 6 ; index++) - { - Image[Pointer++] = 255; // Blank lines below chars between chars - Image[Pointer++] = 255; - if (Bytesperpixel == 4) - { - Image[Pointer++] = 255; - Pointer++; - } - } - Pointer += (WIDTH - 6) * Bytesperpixel; - } -} - -int DrawStation(struct STATIONRECORD * ptr, BOOL AllStations) -{ - int X, Y, Pointer, i, c, index, bit, mask, calllen, calllenpixels; - UINT j; - char Overlay; - char * nptr; - time_t AgeLimit = time(NULL ) - (TrackExpireTime * 60); - int SavePointer; - - if (ptr->Moved == 0 && AllStations == 0) - return 0; // No need to repaint - - if (SuppressNullPosn && ptr->Lat == 0.0) - return 0; - - if (ptr->ObjState == '_') // Killed Object - return 0; - - if (GetLocPixels(ptr->Lat, ptr->Lon, &X, &Y)) - { - if (X < 12 || Y < 12 || X > (WIDTH - 36) || Y > (HEIGHT - 36)) - return 0; // Too close to edges - - if (ptr->LatTrack[0] && ptr->NoTracks == FALSE) - { - // Draw Track - - int Index = ptr->Trackptr; - int i, n; - int X, Y; - int LastX = 0, LastY = 0; - - for (n = 0; n < TRACKPOINTS; n++) - { - if (ptr->LatTrack[Index] && ptr->TrackTime[Index] > AgeLimit) - { - if (GetLocPixels(ptr->LatTrack[Index], ptr->LonTrack[Index], &X, &Y)) - { - if (LastX) - { - if (abs(X - LastX) < 600 && abs(Y - LastY) < 600) - if (X > 0 && Y > 0 && X < (WIDTH - 5) && Y < (HEIGHT - 5)) - plotLine(LastX, LastY, X, Y, Colours[ptr->TrackColour]); - - } - - LastX = X; - LastY = Y; - } - } - Index++; - if (Index == TRACKPOINTS) - Index = 0; - - } - } - - ptr->Moved = 0; - - ptr->DispX = X; - ptr->DispY = Y; // Save for mouse over checks - - // X and Y are offsets into the pixel data in array Image. Actual Bytes are at Y * 2048 * 3 + (X * 3) - - // Draw Icon - - if (Y < 8) Y = 8; - if (X < 8) X = 8; - - nptr = &Image[(((Y - 8) * WIDTH) + X - 8) * Bytesperpixel]; // Center icon on station - - j = (ptr->iconRow * 21 * 337 * Bytesperpixel) - + (ptr->iconCol * 21 * Bytesperpixel) - + 3 * Bytesperpixel + (337 * 3 * Bytesperpixel); - - for (i = 0; i < 16; i++) - { - memcpy(nptr, &iconImage[j], 16 * Bytesperpixel); - nptr += WIDTH * Bytesperpixel; - j += 337 * Bytesperpixel; - } - - // If an overlay is specified, add it - - Overlay = ptr->IconOverlay; - - if (Overlay) - { - Pointer = (((Y - 4) * WIDTH) + (X - 3)) * Bytesperpixel; - mask = 1; - - for (index = 0 ; index < 7 ; index++) - { - Image[Pointer++] = 255; // Blank line above chars - Image[Pointer++] = 255; - if (Bytesperpixel == 4) - { - Image[Pointer++] = 255; - Pointer++; - } - } - Pointer += (WIDTH - 7) * Bytesperpixel; - - for (i = 0; i < 7; i++) - { - Image[Pointer++] = 255; // Blank col - Image[Pointer++] = 255; - if (Bytesperpixel == 4) - { - Image[Pointer++] = 255; - Pointer++; - } - - for (index = 0 ; index < 5 ; index++) - { - c = ASCII[Overlay - 0x20][index]; // Font data - bit = c & mask; - - - if (bit) - { - Image[Pointer++] = 0; - Image[Pointer++] = 0; - if (Bytesperpixel == 4) - { - Image[Pointer++] = 0; - Pointer++; - } - } - else - { - Image[Pointer++] = 255; - Image[Pointer++] = 255; - if (Bytesperpixel == 4) - { - Image[Pointer++] = 255; - Pointer++; - } - } - } - - Image[Pointer++] = 255; // Blank col - Image[Pointer++] = 255; - if (Bytesperpixel == 4) - { - Image[Pointer++] = 255; - Pointer++; - } - - mask <<= 1; - Pointer += (WIDTH - 7) * Bytesperpixel; - } - for (index = 0 ; index < 7 ; index++) - { - Image[Pointer++] = 255; // Blank line below chars - Image[Pointer++] = 255; - if (Bytesperpixel == 4) - { - Image[Pointer++] = 255; - Pointer++; - } - } - Pointer += (WIDTH - 6) * Bytesperpixel; - } - - calllen = strlen(ptr->Callsign); - - while (calllen && ptr->Callsign[calllen - 1] == ' ') // Remove trailing spaces - calllen--; - - calllenpixels = (calllen + 1) * 6; - - // Draw Callsign Box - - Pointer = ((Y - 7) * WIDTH * Bytesperpixel) + ((X + 9) * Bytesperpixel); - - // Draw | at each end - - for (j = 0; j < 13; j++) - { - Image[Pointer] = 0; - Image[Pointer++ + calllenpixels * Bytesperpixel] = 0; - Image[Pointer] = 0; - Image[Pointer++ + calllenpixels * Bytesperpixel] = 0; - if (Bytesperpixel == 4) - { - Image[Pointer] = 0; - Image[Pointer++ + calllenpixels * Bytesperpixel] = 0; - Pointer++; - } - Pointer += (WIDTH - 1) * Bytesperpixel; - } - - // Draw Top Line - - for (i = 0; i < calllenpixels; i++) - { - Image[Pointer++] = 0; - Image[Pointer++] = 0; - if (Bytesperpixel == 4) - { - Image[Pointer++] = 0; - Pointer++; - } - } - - // Draw Bottom Line - - Pointer = ((Y - 7) * WIDTH * Bytesperpixel) + ((X + 9) * Bytesperpixel); - - for (i = 0; i < calllenpixels; i++) - { - Image[Pointer++] = 0; - Image[Pointer++] = 0; - if (Bytesperpixel == 4) - { - Image[Pointer++] = 0; - Pointer++; - } - } - - // Draw Callsign. - - for (j = 0; j < calllen; j++) - { - DrawCharacter(X, Y,j, ptr->Callsign[j]); - } - ImageChanged = TRUE; - return 1; - } - else - { - ptr->DispX = 0; - ptr->DispY = 0; // Off Screen - } - return 0; -} - - -int RefreshStationMap(BOOL AllStations) -{ - struct STATIONRECORD * ptr = *StationRecords; - int blackColor = BlackPixel(display, DefaultScreen(display)); - int whiteColor = WhitePixel(display, DefaultScreen(display)); - int Changed = 0; - char msg[80]; - int i = 0, len; - - while (ptr) - { - Changed += DrawStation(ptr, AllStations); - i++; - ptr = ptr->Next; - } - -// NeedRefresh = FALSE; -// LastRefresh = time(NULL); - -// if (RecsDeleted) -// RefreshStationList();] - - len = sprintf(msg, "%d Stations Zoom = %d", i, Zoom); - XDrawImageString(display, win, gc, 20, 20, msg, len); - - StationCount = i; - return Changed; -} - - - -void j_putRGBScanline(BYTE *jpegline, - int widthPix, - unsigned char *outBuf, - int row, int XOffset, int YOffset) -{ - // Offsets are in tiles, not pixels - - int offset = row * WIDTH * Bytesperpixel; //widthPix - int count; - unsigned int val; - - offset += XOffset * 256 * Bytesperpixel; - offset += YOffset * 256 * WIDTH * Bytesperpixel; - - for (count = 0; count < 256; count++) - { - if (Bytesperpixel == 2) - { - val = (*(jpegline + count * 3 + 2) >> 3); - val |= ((*(jpegline + count * 3 + 1) >> 2) << 5); - val |= ((*(jpegline + count * 3 + 0) >> 3) << 11); - *(outBuf + offset++) = (val & 0xff); - *(outBuf + offset++) = (unsigned char)(val >> 8); - } - else - { - *(outBuf + offset++) = *(jpegline + count * 3 + 2); // Blue - *(outBuf + offset++) = *(jpegline + count * 3 + 1); // Green - *(outBuf + offset++) = *(jpegline + count * 3 + 0); // Red - offset++; - } - } -} - -// -// stash a gray scanline -// - -void j_putGrayScanlineToRGB(BYTE *jpegline, - int widthPix, - BYTE *outBuf, - int row) -{ - int offset = row * widthPix * 3; - int count; - for (count=0;countalloc_sarray) - ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); - - /* Step 6: while (scan lines remain to be read) */ - /* jpeg_read_scanlines(...); */ - - /* Here we use the library's state variable cinfo.output_scanline as the - * loop counter, so that we don't have to keep track ourselves. - */ - while (cinfo.output_scanline < cinfo.output_height) { - /* jpeg_read_scanlines expects an array of pointers to scanlines. - * Here the array is only one element long, but you could ask for - * more than one scanline at a time if that's more convenient. - */ - (void) jpeg_read_scanlines(&cinfo, buffer, 1); - /* Assume put_scanline_someplace wants a pointer and sample count. */ - - // asuumer all 3-components are RGBs - if (cinfo.out_color_components==3) { - - j_putRGBScanline(buffer[0], - *width, - Image, - cinfo.output_scanline-1, XOffset, YOffset); - - } else if (cinfo.out_color_components==1) { - - // assume all single component images are grayscale - j_putGrayScanlineToRGB(buffer[0], - *width, - Image, - cinfo.output_scanline-1); - - } - - } - - /* Step 7: Finish decompression */ - - (void) jpeg_finish_decompress(&cinfo); - /* We can ignore the return value since suspension is not possible - * with the stdio data source. - */ - - /* Step 8: Release JPEG decompression object */ - - /* This is an important step since it will release a good deal of memory. */ - jpeg_destroy_decompress(&cinfo); - - /* After finish_decompress, we can close the input file. - * Here we postpone it until after no more JPEG errors are possible, - * so as to simplify the setjmp error logic above. (Actually, I don't - * think that jpeg_destroy can do an error exit, but why assume anything...) - */ - fclose(infile); - - /* At this point you may want to check to see whether any corrupt-data - * warnings occurred (test whether jerr.pub.num_warnings is nonzero). - */ - - return 0; -} -// store a scanline to our data buffer - -void j_putRGBScanline(BYTE *jpegline, - int widthPix, - BYTE *outBuf, - int row, int X, int Y); - -void j_putGrayScanlineToRGB(BYTE *jpegline, - int widthPix, - BYTE *outBuf, - int row); - -VOID LoadImageTile(int Zoom, int startx, int starty, int x, int y); - -VOID RefreshTile(char * FN, int TileZoom, int Tilex, int Tiley) -{ - // Called when a new tile has been diwnloaded from OSM - - int StartRow, StartCol; - UCHAR * pbImage = NULL; - int x, y, i, j; - int ImgChannels; - - if (TileZoom != Zoom) - return; // Zoom level has changed - - x = Tilex - SetBaseX; - y = Tiley - SetBaseY; - - if (x < 0 || x > WIDTHTILES -1 || y < 0 || y > HEIGHTTILES - 1) - return; // Tile isn't part of current image; - - LoadImageTile (Zoom, Tilex, Tiley, x, y); - NeedRedraw = 1; - -// XPutImage (display, win, gc, image, ScrollX, ScrollY, leftBorder, topBorder, cxImgSize, cyImgSize); -} - - -VOID LoadImageTile(int Zoom, int startx, int starty, int x, int y) -{ - char FN[100]; - int i, j; - int StartRow; - int StartCol; - char Tile[100]; - UCHAR * pbImage = NULL; - int ImgChannels; - BOOL JPG=FALSE; - struct stat STAT; - int cx, cy; - - int Limit = (int)pow(2, Zoom); -/* - printf("LoadImage %d %d %d\n", Limit, startx, startx); - - if (startx < 0) - startx = startx + WIDTHTILES; - else - if (startx > WIDTHTILES - 1) - startx = WIDTHTILES - startx; - - if (starty < 0) - starty = starty + HEIGHTTILES; - else - if (starty > HEIGHTTILES -1 ) - starty = HEIGHTTILES - starty; - - if (startx < 0 || startx > WIDTHTILES) - x = WIDTHTILES / 2; - - if (starty < 0 || y > HEIGHTTILES) - starty = HEIGHTTILES /2; - - printf("LoadImage %d %d %d\n", Limit, startx, starty); -*/ - if ((startx) >= Limit || (starty) >= Limit || startx< 0 || starty < 0) - { -// printf("Not Loading %d %d %d\n",Limit, startx, startx ); - return; //goto NoFile; - } - - // May be PNG or JPG - - sprintf(Tile, "/%02d/%d/%d.png", Zoom, startx, starty); - sprintf(FN, "%s%s", OSMDir, Tile); - - if (stat(FN, &STAT) == 0) - goto gotfile; - - sprintf(Tile, "/%02d/%d/%d.jpg", Zoom, startx, starty); - sprintf(FN, "%s%s", OSMDir, Tile); - - JPG = TRUE; - - if (stat(FN, &STAT) == 0) - goto gotfile; - - - OSMGet(startx, starty, Zoom); - return; - -gotfile: - - if (JPG) - { - JpegFileToRGB(FN, &cx, &cy, x, y); - ImgChannels = 3; - } - else - { - int offset; - int cxImgSize, cyImgSize; - UCHAR * ImageSave; - - LoadImageFile (NULL, FN, &pbImage, &cxImgSize, &cyImgSize, &ImgChannels, &bkgColor); - -// printf("%d %d %d\n", cxImgSize, cyImgSize, ImgChannels); -// ImgChannels = 4; - StartCol = x * Bytesperpixel * 256; - StartRow = y * 256; - -// printf("WIDTH %d Height %d Bytesperpixel = %d x = %d y = %d\n", WIDTH, HEIGHT, Bytesperpixel, x, y); - if (pbImage == NULL) - { - pbImage = malloc(256 * ImgChannels * 256); - memset(pbImage, 0x40, 256 * ImgChannels * 256); - } - - ImageSave = pbImage; - - offset = ((StartRow) * WIDTH * ImgChannels) + StartCol; - -// printf ("x %d y %d offset %d \n", x, y, offset); - - - for (i = 0; i < 256; i++) - { - int count, val; - - offset = ((StartRow + i) * WIDTH * Bytesperpixel) + StartCol; - - // this does one scan line - - for (count = 0; count < 256; count++) - { - if (Bytesperpixel == 2) - { - val = (*(pbImage + count * ImgChannels + 2) >> 3); - val |= ((*(pbImage + count * ImgChannels + 1) >> 2) << 5); - val |= ((*(pbImage + count * ImgChannels + 0) >> 3) << 11); - Image[offset++] = (val & 0xff); - Image[offset++] = (unsigned char)(val >> 8); - } - else - { - Image[offset++] = *(pbImage + count * ImgChannels + 2); // Blue - Image[offset++] = *(pbImage + count * ImgChannels + 1); // Green - Image[offset++] = *(pbImage + count * ImgChannels + 0); // Red - offset++; - } - } - pbImage += ImgChannels * 256; - } - - free(ImageSave); - } -} - - -VOID LoadImageSet(int Zoom, int TileX, int TileY) -{ - int x, y; - - if (SetBaseX != TileX || SetBaseY != TileY) - { - // Only Load if changed - - SetBaseX = TileX; // Lowest Tiles in currently loaded set - SetBaseY = TileY; - - memset(Image, 0, WIDTH * Bytesperpixel * HEIGHT); - XClearWindow(display, win); - - for (y = 0; y < HEIGHTTILES; y++) - { - for (x = 0; x < WIDTHTILES; x++) - { - LoadImageTile(Zoom, TileX + x, TileY + y, x, y); - } - } - RefreshStationMap(TRUE); - } - XPutImage (display, win, gc, image, ScrollX, ScrollY, leftBorder, topBorder, cxImgSize, cyImgSize); -} - -BYTE * ReadIcons(char * fileName, UINT *width, UINT *height) -{ - struct jpeg_decompress_struct cinfo; - struct my_error_mgr jmerr; - struct jpeg_error_mgr jerr; - FILE * infile=NULL; /* source file */ - - JSAMPARRAY buffer; /* Output row buffer */ - int row_stride; /* physical row width in output buffer */ - char buf[250]; - BYTE *dataBuf; - - *width=0; - *height=0; - - if ((infile = fopen(fileName, "rb")) == NULL) { - return NULL; - } - - cinfo.err = jpeg_std_error(&jerr); - - if (setjmp(jmerr.setjmp_buffer)) { - /* If we get here, the JPEG code has signaled an error. - * We need to clean up the JPEG object, close the input file, and return. - */ - - jpeg_destroy_decompress(&cinfo); - - if (infile!=NULL) - fclose(infile); - return NULL; - } - - - jpeg_create_decompress(&cinfo); - - jpeg_stdio_src(&cinfo, infile); - - (void) jpeg_read_header(&cinfo, TRUE); - - (void) jpeg_start_decompress(&cinfo); - - dataBuf = malloc(cinfo.output_width * 4 * cinfo.output_height); - memset(dataBuf, 0, cinfo.output_width * 4 * cinfo.output_height); - - if (dataBuf==NULL) - { - jpeg_destroy_decompress(&cinfo); - fclose(infile); - return NULL; - } - - // how big is this thing gonna be? - *width = cinfo.output_width; - *height = cinfo.output_height; - - /* JSAMPLEs per row in output buffer */ - row_stride = cinfo.output_width * cinfo.output_components; - - /* Make a one-row-high sample array that will go away when done with image */ - buffer = (*cinfo.mem->alloc_sarray) - ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); - - /* Step 6: while (scan lines remain to be read) */ - /* jpeg_read_scanlines(...); */ - - /* Here we use the library's state variable cinfo.output_scanline as the - * loop counter, so that we don't have to keep track ourselves. - */ - while (cinfo.output_scanline < cinfo.output_height) - { - int offset; - int count; - unsigned int val; - - (void) jpeg_read_scanlines(&cinfo, buffer, 1); - - offset = (cinfo.output_scanline-1) * cinfo.output_width * Bytesperpixel; - - for (count = 0; count < cinfo.output_width; count++) - { - if (Bytesperpixel == 2) - { - val = (*(buffer[0] + count * 3 + 2) >> 3); - val |= ((*(buffer[0] + count * 3 + 1) >> 2) << 5); - val |= ((*(buffer[0] + count * 3 + 0) >> 3) << 11); - *(dataBuf + offset++) = (val & 0xff); - *(dataBuf + offset++) = (unsigned char)(val >> 8); - } - else - { - *(dataBuf + offset++) = *(buffer[0] + count * 3 + 2); // Blue - *(dataBuf + offset++) = *(buffer[0] + count * 3 + 1); // Green - *(dataBuf + offset++) = *(buffer[0] + count * 3 + 0); // Red - offset++; - } - } - } - - (void) jpeg_finish_decompress(&cinfo); - jpeg_destroy_decompress(&cinfo); - fclose(infile); - return dataBuf; -} -// store a scanline to our data buffer - -void ZoomIn() -{ - if (Zoom < 16) - { - Zoom ++; - CentrePositionToMouse(MouseLat, MouseLon); - TileX = SetBaseX; - TileY = SetBaseY; - NeedRefresh = TRUE; - } -} -void ZoomOut() -{ - if (Zoom > 1) - { - Zoom --; - CentrePositionToMouse(MouseLat, MouseLon); - TileX = SetBaseX; - TileY = SetBaseY; - if (Zoom == 1) - ScrollX = ScrollY = 0; - - NeedRefresh = TRUE; - } -} - -config_t cfg; -config_setting_t *croot, *group; - -int GetIntValue(config_setting_t * group, char * name, int defaultval) -{ - config_setting_t *setting; - - setting = config_setting_get_member (group, name); - if (setting) - return config_setting_get_int (setting); - - return defaultval; -} - -VOID SaveIntValue(config_setting_t * group, char * name, int value) -{ - config_setting_t *setting; - - setting = config_setting_add(group, name, CONFIG_TYPE_INT); - if(setting) - config_setting_set_int(setting, value); -} - -BOOL GetStringValue(config_setting_t * group, char * name, char * value) -{ - const char * str; - config_setting_t *setting; - - setting = config_setting_get_member (group, name); - - if (setting) - { - str = config_setting_get_string (setting); - strcpy(value, str); - return TRUE; - } - return FALSE; -} - -VOID SaveStringValue(config_setting_t * group, char * name, char * value) -{ - config_setting_t *setting; - - setting = config_setting_add(group, name, CONFIG_TYPE_STRING); - if (setting) - config_setting_set_string(setting, value); - -} - - - -enum -{ - COL_FROM = 0, - COL_TO, - COL_SEQ, - COL_TIME, - COL_RECEIVED, - NUM_COLS -} ; - - -void CancelMessageSend (GtkWidget *menuitem, struct APRSMESSAGE * userdata) -{ -/* - userdata->Retries = 0; - userdata->RetryTimer = 0; - userdata->Cancelled = TRUE; - UpdateTXMessageLine(userdata); -*/ -} - - -void view_popup_menu_onDoNothing (GtkWidget *menuitem, gpointer userdata) -{ - GtkTreeView *treeview = GTK_TREE_VIEW(userdata); -} - -void view_popup_menu (GtkWidget *treeview, GdkEventButton *event, struct APRSMESSAGE * userdata) -{ - GtkWidget *menu, *menuitem1,*menuitem2 ; - char Msg[80]; - - sprintf(Msg,"Cancel Message Seq %s to %s?", userdata->Seq, userdata->ToCall); - - menu = gtk_menu_new(); - - menuitem1 = gtk_menu_item_new_with_label(Msg); - menuitem2 = gtk_menu_item_new_with_label("Return"); - - g_signal_connect(menuitem1, "activate", - (GCallback) CancelMessageSend, (gpointer)userdata); - g_signal_connect(menuitem2, "activate", - (GCallback) view_popup_menu_onDoNothing, treeview); - - if (userdata->Retries) // Not active so cant cancel - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem1); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem2); - - gtk_widget_show_all(menu); - - /* Note: event can be NULL here when called from view_onPopupMenu; - * gdk_event_get_time() accepts a NULL argument */ - gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, - (event != NULL) ? event->button : 0, - gdk_event_get_time((GdkEvent*)event)); - } - - -gboolean view_onButtonPressed (GtkWidget *treeview, GdkEventButton *event, gpointer userdata) -{ - // Right click on TX Message window. If a message is selected, - // Pop up a Cancel Message Window - - if (event->type == GDK_BUTTON_PRESS && event->button == 3) - { - GtkTreeSelection *selection; - GtkTreeModel *model; - GtkTreeIter iter; - GtkTreePath *path; - struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs; - - if (ptr == 0) - return TRUE; - - // Make sure the entry that was clicked is selected - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); - - if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), - (gint) event->x, (gint) event->y, &path, NULL, NULL, NULL)) - { - gtk_tree_selection_unselect_all(selection); - gtk_tree_selection_select_path(selection, path); - gtk_tree_path_free(path); - } - - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); - - if (gtk_tree_selection_get_selected(selection, &model, &iter)) - { - gchar *Seq; - - gtk_tree_model_get (model, &iter, 1, &Seq, -1); - - // Find the message - - while (ptr) - { - if (strcmp(ptr->Seq, Seq) == 0) - { - view_popup_menu(treeview, event, ptr); - g_free(Seq); - return TRUE; - } - ptr = ptr->Next; - } - - g_free(Seq); - g_print ("Msg not found.\n"); - } - g_print ("no row selected.\n"); - } - - return FALSE; /* we did not handle this */ -} - -gboolean view_onPopupMenu (GtkWidget *treeview, gpointer userdata) -{ - view_popup_menu(treeview, NULL, userdata); - - return TRUE; /* we handled this */ -} - - -static GtkWidget *create_sent_window( void ) -{ - GtkCellRenderer *renderer; - GtkTreeModel *model; - GtkTreeIter iter; - - view = gtk_tree_view_new(); - - gtk_signal_connect (GTK_OBJECT (view), "row_activated", - GTK_SIGNAL_FUNC (SelectTXMsg), NULL); - - - g_signal_connect(view, "button-press-event", (GCallback) view_onButtonPressed, NULL); - g_signal_connect(view, "popup-menu", (GCallback) view_onPopupMenu, NULL); - - - - - renderer = gtk_cell_renderer_text_new(); - renderer->ypad = 0; - gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (view), -1, "To", renderer, "text", 0, NULL); - - renderer = gtk_cell_renderer_text_new (); - renderer->ypad = 0; - gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (view), -1, "Seq", renderer, "text", 1, NULL); - - renderer = gtk_cell_renderer_text_new (); - renderer->ypad = 0; - gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (view), -1, "State", renderer, "text", 2, NULL); - - renderer = gtk_cell_renderer_text_new (); - renderer->ypad = 0; - gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (view), -1, "Time", renderer, "text", 3, NULL); - - renderer = gtk_cell_renderer_text_new (); - renderer->ypad = 0; - gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (view), -1, "Sent", renderer, "text", 4, NULL); - - sentitems = gtk_list_store_new(5, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); - - model = GTK_TREE_MODEL(sentitems); - - gtk_tree_view_set_model((GtkTreeView *)view, model); - - /* The tree view has acquired its own reference to the - * model, so we can drop ours. That way the model will - * be freed automatically when the tree view is destroyed */ - - g_object_unref (model); - -// gtk_container_add (GTK_CONTAINER (window), view2); - - scrolledwin = gtk_scrolled_window_new(NULL,NULL); - gtk_container_set_border_width(GTK_CONTAINER(scrolledwin), 1); -// gtk_widget_set_size_request(scrolledwin, 300, 80); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin), GTK_SHADOW_IN); -// gtk_container_add(GTK_CONTAINER(scrolledwin), view); - //tree_view = gtk_tree_view_new(); - gtk_container_add(GTK_CONTAINER (scrolledwin), view); - //gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), GTK_TREE_MODEL (view)); - //gtk_widget_show(tree_view); -/* - gtk_table_attach (GTK_TABLE (table), scrolledwin,0, 1, 0, 1, - GTK_EXPAND | GTK_SHRINK | GTK_FILL, - GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); -*/ - gtk_widget_show(scrolledwin); - return scrolledwin; - -} - -GdkPixbuf *create_pixbuf(const gchar * filename) -{ - GdkPixbuf *pixbuf; - GError *error = NULL; - pixbuf = gdk_pixbuf_new_from_file(filename, &error); - if(!pixbuf) { - fprintf(stderr, "%s\n", error->message); - g_error_free(error); - } - return pixbuf; -} - - -static GtkWidget *create_received_window(void) -{ - GtkCellRenderer *renderer; - - view2 = gtk_tree_view_new(); - -// gtk_tree_view_set_fixed_height_mode(view2, TRUE); - - renderer = gtk_cell_renderer_text_new(); - renderer->ypad = 0; - gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view2), -1, "From", renderer, "text", COL_FROM, NULL); - - renderer = gtk_cell_renderer_text_new (); - renderer->ypad = 0; - gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view2), -1, "To", renderer, "text", COL_TO, NULL); - - renderer = gtk_cell_renderer_text_new (); - renderer->ypad = 0; - gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view2), -1, "Seq", renderer, "text", COL_SEQ, NULL); - - renderer = gtk_cell_renderer_text_new (); - renderer->ypad = 0; - gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view2), -1, "Time", renderer, "text", COL_TIME, NULL); - - renderer = gtk_cell_renderer_text_new (); - renderer->ypad = 0; - gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view2), -1, "Received", renderer, "text", COL_RECEIVED, NULL); - - receiveditems = gtk_list_store_new (NUM_COLS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); - - gtk_tree_view_set_model((GtkTreeView *)view2, (GtkTreeModel *)receiveditems); - - /* The tree view has acquired its own reference to the - * model, so we can drop ours. That way the model will - * be freed automatically when the tree view is destroyed */ - - g_object_unref (receiveditems); - - scrolledwin2 = gtk_scrolled_window_new(NULL,NULL); - gtk_container_set_border_width(GTK_CONTAINER(scrolledwin2), 2); -// gtk_widget_set_size_request(scrolledwin2, 300, 80); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin2),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin2), GTK_SHADOW_IN); - gtk_container_add(GTK_CONTAINER(scrolledwin2), view2); - - gtk_widget_show(scrolledwin2); - - return scrolledwin2; - -} - -char ToCalls[1024] = ""; - -VOID SendAPRSMessage(const char * Text, char * ToCall); - -void enter_callback( GtkWidget *widget, - GtkWidget *entry ) -{ - const gchar *entry_text; - entry_text = gtk_entry_get_text (GTK_ENTRY (entry)); - gchar * tocall = strupr(gtk_combo_box_text_get_active_text((GtkComboBoxText *)combo)); - char Key[32]; - - if (strlen(tocall) > 9) - tocall[9] = 0; - - sprintf(Key, "|%s|", tocall); - - if (tocall) - { - SendAPRSMessage(entry_text, tocall); - - // if new call add to combo box - - if (strstr(ToCalls, Key) == 0) - { - if (strlen(ToCalls) < 1000) - strcat(ToCalls, Key); - - gtk_combo_box_text_prepend_text ((GtkComboBoxText *)combo, tocall); - } - - g_free(tocall); - gtk_entry_set_text (GTK_ENTRY (entry), ""); - } -} - - -void SelectTXMsg (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data) -{ - GtkTreeIter iter; - GtkTreeModel *model; - struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs; - - if (ptr == 0) - return; - - model = gtk_tree_view_get_model(tree_view); - - if (gtk_tree_model_get_iter(model, &iter, path)) - { - gchar *seq; - - gtk_tree_model_get(model, &iter, 1, &seq, -1); - - g_print ("Double-clicked row contains seq %s\n", seq); - g_free(seq); - } - - return; -} - - -VOID GTKThread() -{ - gtk_main(); -} - -int msgWinWidth = 300; -int msgWinHeight = 300; -int msgWinX = 100; -int msgWinY = 100; -int Split = 100; // Rx/Tx Window split - - -void frame_callback(GtkWindow *window, GdkEvent *event, gpointer data) -{ - int x, y; - char buf[10]; - - msgWinX = event->configure.x; - msgWinY = event->configure.y; - msgWinWidth = event->configure.width; - msgWinHeight = event->configure.height; - - // gtk_widget_set_size_request(entry, msgWinWidth - 210 , 20); //gtk_entry_new_with_buffer(text); - - // gtk_window_set_title(window, buf); - // gtk_window_set_title (GTK_WINDOW (window), "BPQAPRS Messaging"); -} - -BOOL OnlyMine = FALSE; -BOOL OnlySeq = FALSE; -BOOL ShowBulls = FALSE; -BOOL AllSSID = FALSE; - - -void check_callback(GtkButton *button, gpointer user_data) -{ - GtkTreeIter iter; - struct APRSMESSAGE * ptr = SMEM->Messages; - int n = 0; - - char BaseFrom[10]; - - OnlyMine = gtk_toggle_button_get_active((GtkToggleButton *)check1); - OnlySeq = gtk_toggle_button_get_active((GtkToggleButton *)check2); - ShowBulls = gtk_toggle_button_get_active((GtkToggleButton *)check3); - AllSSID = gtk_toggle_button_get_active((GtkToggleButton *)check4); - - // rewite the Message display with new filter - - if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(receiveditems), &iter, NULL, 0)) - { - while (gtk_list_store_remove(receiveditems, &iter)) - {} - } - while (ptr) - { - if (memcmp(ptr->ToCall, "BLN", 3)== 0) - if (ShowBulls == TRUE) - goto wantit; - - if (strcmp(ptr->ToCall, APRSCall) == 0) // to me? - goto wantit; - - if (AllSSID) - { - memcpy(BaseFrom, ptr->ToCall, 10); - strlop(BaseFrom, ' '); - strlop(BaseFrom, '-'); - - if (strcmp(BaseFrom, BaseCall) == 0) - goto wantit; - } - - if (OnlyMine == FALSE) // Want All - if (OnlySeq == FALSE || ptr->Seq[0] != 0) - goto wantit; - - // ignore - - ptr = ptr->Next; - continue; - - wantit: - - gtk_list_store_insert_with_values( - receiveditems, &iter, -1, - COL_FROM, ptr->FromCall, - COL_TO, ptr->ToCall, - COL_SEQ, ptr->Seq, - COL_TIME, ptr->Time, - COL_RECEIVED, ptr->Text, -1); - - ptr = ptr->Next; - n++; - } - - - if (n) - gtk_tree_view_scroll_to_cell((GtkTreeView *)view2, - gtk_tree_model_get_path (GTK_TREE_MODEL(receiveditems), &iter), NULL, FALSE, 0, 0); - - -} - - -void button_callback(GtkButton *button, gpointer user_data) -{ - SMEM->ClearRX = 1; -} - - -void button2_callback(GtkButton *button, gpointer user_data) -{ - // Clear Sent Messages - - SMEM->ClearTX = 1; -} - - -static gboolean delete_event (GtkWidget *widget, GdkEvent *event, gpointer data) -{ - // Don't allow window to be closed - - return TRUE; -} - -void SaveConfig() -{ - memset((void *)&cfg, 0, sizeof(config_t)); - - config_init(&cfg); - - croot = config_root_setting(&cfg); - - gtk_window_get_size(GTK_WINDOW(window), &msgWinWidth, &msgWinHeight); - gtk_window_get_position(GTK_WINDOW(window), &msgWinX, &msgWinY); - Split = gtk_paned_get_position((GtkPaned *)vpaned); - - group = config_setting_add(croot, "APRS", CONFIG_TYPE_GROUP); - - SaveIntValue(group, "Zoom", Zoom); - SaveIntValue(group, "SetBaseX", SetBaseX); - SaveIntValue(group, "SetBaseY", SetBaseY); - SaveIntValue(group, "ScrollX", ScrollX); - SaveIntValue(group, "ScrollY", ScrollY); - SaveIntValue(group, "WindowX", WindowX); - SaveIntValue(group, "WindowY", WindowY); - SaveIntValue(group, "WindowWidth", WindowWidth); - SaveIntValue(group, "WindowHeight", WindowHeight); - SaveIntValue(group, "WIDTHTILES", WIDTHTILES); - SaveIntValue(group, "HEIGHTTILES", HEIGHTTILES); - SaveIntValue(group, "msgWinWidth", msgWinWidth); - SaveIntValue(group, "msgWinHeight", msgWinHeight); - SaveIntValue(group, "msgWinX", msgWinX); - SaveIntValue(group, "msgWinY", msgWinY); - SaveIntValue(group, "Split", Split); - - SaveIntValue(group, "OnlyMine", OnlyMine); - SaveIntValue(group, "OnlySeq", OnlySeq); - SaveIntValue(group, "ShowBulls", ShowBulls); - - SaveIntValue(group, "LocalTime", LocalTime); - SaveIntValue(group, "KM", KM); - SaveIntValue(group, "AddViewToFilter", AddViewToFilter); - SaveStringValue(group, "ISFilter", ISFilter); - - - SaveIntValue(group, "CreateJPEG", CreateJPEG); - SaveIntValue(group, "JPEGInterval", JPEGInterval); - SaveStringValue(group, "JPEGFileName", JPEGFileName); - - if(!config_write_file(&cfg, "BPQAPRS.cfg")) - printf("Error while writing config file.\n"); - else - printf("Config Saved\n"); - - config_destroy(&cfg); - - printf("%s\n", ToCalls); -} - -// Linux Signal Handlers - -BOOL Running = TRUE; - -static void sigterm_handler(int sig) -{ - printf("sigterm\n"); - Running = FALSE; -} - -static void sigint_handler(int sig) -{ - SaveConfig(); - Running = FALSE; - exit(0); -} - -int main(int argc, char *argv[]) -{ - UCHAR * pbImage = NULL; - char FN[256]; - int fd; - int x, y; - time_t TimeLoaded = time(NULL); - struct stat STAT; - char * Env; - char BPQDirectory[256]; - char SharedName[256]; - char * ptr1; - - int SharedSize; - - double vals[10]; - int screen_number, depth, bitmap_pad, status; - unsigned long white; - unsigned long black; - Visual * visual; - unsigned int i, j; - Pixmap pixmap, popuppixmap; - - int x11_fd; - fd_set in_fds; - - struct timeval tv; - XEvent event; - XConfigureEvent xce; - XKeyEvent xkeyev; - Time lastupevent; - - char text[256]; - long unsigned int key; - - int LastX, LastY; // Saved mouse position when button down - int MovedX, MovedY; - - double sx, sy; - UCHAR * APRSStationMemory; - Atom wmDeleteMessage; - PangoFontDescription *font_desc; - GtkTreeIter iter; - -#ifndef WIN32 - signal(SIGHUP, SIG_IGN); - signal(SIGINT, sigint_handler); - signal(SIGTERM, sigterm_handler); - signal(SIGPIPE, SIG_IGN); -#endif - - printf("G8BPQ APRS Client for Linux Version 1.1.14.7 \n"); - printf("Copyright(c) 2004-2021 John Wiseman G8BPQ\n"); - printf("APRS is a registered trademark of Bob Bruninga.\n"); - printf("This software is based in part on the work of the Independent JPEG Group.\n"); - printf("Mapping from OpenStreetMap (http://openstreetmap.org)\n"); - printf("Imagery (c) OpenMapTiles (https://openmaptiles.org)\n\n"); - - if (argc > 1 && argv[1] && stricmp(argv[1], "-v") == 0) - return 0; - - config_init(&cfg); - - if (argc > 1 && argv[1] && stricmp(argv[1], "multiple") == 0) - { - multiple = 1; - printf("Running in multiple instance mode\n\n"); - } - - /* Read the file. If there is an error, report it and exit. */ - - if(!config_read_file(&cfg, "BPQAPRS.cfg")) - { - fprintf(stderr, "%d - %s\n", - config_error_line(&cfg), config_error_text(&cfg)); - config_destroy(&cfg); - } - else - { - group = config_lookup (&cfg, "APRS"); - - if (group) - { - Zoom = GetIntValue(group, "Zoom", 2); - TileX = GetIntValue(group, "SetBaseX", 0); - TileY = GetIntValue(group, "SetBaseY", 0); - ScrollX = GetIntValue(group, "ScrollX", 0); - ScrollY = GetIntValue(group, "ScrollY", 0); - WindowX = GetIntValue(group, "WindowX", 100); - WindowY = GetIntValue(group, "WindowY", 100); - WindowWidth = GetIntValue(group, "WindowWidth", 788); - WindowHeight = GetIntValue(group, "WindowHeight", 788); - HEIGHTTILES = GetIntValue(group, "HEIGHTTILES", 4); - WIDTHTILES = GetIntValue(group, "WIDTHTILES", 4); - - msgWinWidth = GetIntValue(group, "msgWinWidth", 300); - msgWinHeight = GetIntValue(group, "msgWinHeight", 300); - msgWinX = GetIntValue(group, "msgWinX", 100); - msgWinY = GetIntValue(group, "msgWinY", 100); - Split = GetIntValue(group, "Split", 100); - - OnlyMine = GetIntValue(group, "OnlyMine", 0); - OnlySeq = GetIntValue(group, "OnlySeq", 1); - ShowBulls = GetIntValue(group, "ShowBulls", 0); - - LocalTime = GetIntValue(group, "LocalTime", 0); - KM = GetIntValue(group, "KM", 0); - - AddViewToFilter = GetIntValue(group, "AddViewToFilter", 0); - - CreateJPEG = GetIntValue(group, "CreateJPEG", 1); - JPEGInterval = GetIntValue(group, "JPEGInterval", 300); - GetStringValue(group, "JPEGFileName", JPEGFileName); - GetStringValue(group, "ISFilter", ISFilter); - } - } - - if (Zoom == 0) - Zoom = 2; - - HEIGHT = HEIGHTTILES * 256; - WIDTH = WIDTHTILES * 256; - - Env = getenv("DISPLAY"); - - if (Env == NULL) - { - printf("DISPLAY is not set - can't run without X\n", Env); - return 0; - } - - printf("DISPLAY is set to %s\n", Env); - - if (strstr(Env, "localhost:1")) - printf("!!! WARNING !!! X session seems to be tunneled over an SSH session\nThis program will run much faster if you set DISPLAY to the Host running your X Server\n"); - - // Get shared memory - - // Append last bit of current directory to shared name - - getcwd(BPQDirectory, 256); - ptr1 = BPQDirectory; - - while (strchr(ptr1, '/')) - { - ptr1 = strchr(ptr1, '/'); - ptr1++; - } - - if (multiple) - sprintf(SharedName, "/BPQAPRSSharedMem%s", ptr1); - else - strcpy(SharedName, "/BPQAPRSSharedMem"); - - printf("Using Shared Memory %s\n", SharedName); - - - fd = shm_open(SharedName, O_RDWR, S_IRUSR | S_IWUSR); - if (fd == -1) - { - printf("Open APRS Shared Memory %s Failed\n", SharedName); - return 0; - } - else - { - // Map shared memory object - - Shared = mmap((void *)APRSSHAREDMEMORYBASE, 8192 * 4096, - PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0); - - if (Shared == MAP_FAILED) - { - printf("Map APRS Shared Memory Failed\n"); - APRSStationMemory = NULL; - return 0; - } - } - - if (Shared != (void *)APRSSHAREDMEMORYBASE) - { - printf("Map APRS Shared Memory Allocated at wrong address %x Should be 0x43000000\n", Shared); - return 0; - } - - printf("Map APRS Shared Memory Allocated at %x\n", Shared); - - - SMEM = (struct SharedMem *)Shared; - SharedSize = SMEM->SharedMemLen; - - printf("Shared Memory Size %d Max %d\n", SharedSize, 8192 * 4096); - - if (SharedSize > 8192 * 4096) - { - printf("MAXSTATIONS too high\n"); - return 0; - } - - StnRecordBase = Shared + 32; - StationRecords = (struct STATIONRECORD**)StnRecordBase; - - ControlRecord = (struct STATIONRECORD*)StnRecordBase; - - memset(APRSCall, 0x20, 9); - memcpy(APRSCall, ControlRecord->Callsign, strlen(ControlRecord->Callsign)); - - printf("LinBPQ Configured with MaxStations %d APRSCall %s\n", - ControlRecord->LastPort, APRSCall); - - memcpy(BaseCall, APRSCall, 10); // Get call less SSID - strlop(BaseCall, ' '); - strlop(BaseCall, '-'); - - // Remap with Server's view of MaxStations - -// munmap(APRSStationMemory, 4096 * 4096); - -// perror("munmap"); - -// Shared = mmap((void *)APRSSHAREDMEMORYBASE, SharedSize, -// PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - -// printf("Map APRS Shared Memory Allocated at %x\n", Shared); - - if (Shared == MAP_FAILED) - { - printf("Extend APRS Shared Memory Failed\n"); - APRSStationMemory = NULL; - return 0; - } - - SMEM->NeedRefresh = 1; // Initial Load of messages - - maxfd = sfd = socket(AF_UNIX, SOCK_DGRAM, 0); - - if (sfd == -1) - { - perror("Socket"); - } - else - { - memset(&my_addr, 0, sizeof(struct sockaddr_un)); - my_addr.sun_family = AF_UNIX; - strncpy(my_addr.sun_path, RX_SOCK_PATH, sizeof(my_addr.sun_path) - 1); - - memset(&peer_addr, 0, sizeof(struct sockaddr_un)); - peer_addr.sun_family = AF_UNIX; - strncpy(peer_addr.sun_path, TX_SOCK_PATH, sizeof(peer_addr.sun_path) - 1); - - unlink(RX_SOCK_PATH); - - if (bind(sfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr_un)) == -1) - perror("bind"); - } - - XInitThreads(); - - ResolveThread(); - _beginthread(OSMThread, 0, NULL); - - - display = XOpenDisplay(NULL); - - if (! display) - { - printf("Couldn't open X display\n"); - return 1; - } - - screen_number = DefaultScreen (display); - depth = DefaultDepth (display, screen_number); - visual = DefaultVisual (display, screen_number); - gc = DefaultGC (display, screen_number); - bitmap_pad = BitmapPad (display); - white = WhitePixel (display, screen_number); - black = BlackPixel (display, screen_number); - root = DefaultRootWindow (display); - - if (depth == 16) - Bytesperpixel = 2; - - Image = malloc(WIDTH * Bytesperpixel * HEIGHT + 100); // Seems past last byte gets corrupt - - if (mkdir(OSMDir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) - { - if (errno != 17) // File exists - { - printf("Error Creating %s\n", OSMDir); - perror("mkdir"); - } - } - - for (i = 0; i < 20; i++) - { - sprintf(FN, "%s/%02d", OSMDir, i); - if (mkdir(FN, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) - { - if (errno != 17) // File exists - { - printf("Error Creating %s\n", FN); - perror("mkdir"); - } - } - } - - // Read Icons - - iconImage = ReadIcons("BPQAPRS/Symbols.jpg", &x, &y); - - if (x == 0) - printf("Couldn't load Icons\n"); - -// win = XCreateSimpleWindow (display, root, 50, 50, 800, 800, 0, black, white); - win = XCreateWindow (display, root, 50, 50, 788, 788, 0, depth, CopyFromParent, CopyFromParent, 0, 0); - XStoreName(display, win, "BPQAPRS Map"); - - wmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", False); - XSetWMProtocols(display, win, &wmDeleteMessage, 1); - -// XSelectInput(display, win, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask); - - XSelectInput(display, win, ExposureMask | KeyPressMask | PointerMotionMask | - ButtonPressMask | ButtonReleaseMask | StructureNotifyMask); - - XSetWindowBackground(display, win, 0xffffff); - XClearWindow(display, win); - XMapWindow (display, win); - - XMoveResizeWindow(display, win, WindowX, WindowY, WindowWidth, WindowHeight); - - image = XCreateImage (display, visual, depth, ZPixmap, 0, NULL, WIDTH, HEIGHT, bitmap_pad, 0); - - printf("depth : %d\nbitmap_pad : %d\nimage bpp : %d\n", depth, bitmap_pad, image->bits_per_pixel); - - image->data = Image; - - pixmap = XCreatePixmap (display, root, WIDTH, HEIGHT, depth); - - XSetLineAttributes(display, gc, 2, LineSolid, CapNotLast, JoinMiter); - - SetBaseX = -1; // force reload - SetBaseY = -1; - - LoadImageSet(Zoom, TileX, TileY); // Loads 1024 * 1024 Block - - x11_fd = ConnectionNumber(display); - - if (x11_fd > maxfd) - maxfd = x11_fd; - - gtk_init (&argc, &argv); - - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - gtk_window_set_default_size(GTK_WINDOW (window), msgWinWidth, msgWinHeight); - gtk_widget_set_uposition(GTK_WIDGET(window),msgWinX, msgWinY); - - gtk_signal_connect (GTK_OBJECT (window), "destroy", - GTK_SIGNAL_FUNC (gtk_main_quit), NULL); - gtk_container_set_border_width (GTK_CONTAINER (window), 10); - - gtk_window_set_resizable(GTK_WINDOW (window), TRUE); -// g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (close_application), NULL); - gtk_window_set_title (GTK_WINDOW (window), "BPQAPRS Messaging"); - gtk_container_set_border_width(GTK_CONTAINER (window), 0); - - // Load bpqicon if present - - if (stat("bpqicon.png", &STAT) == 0) - gtk_window_set_icon(GTK_WINDOW(window), create_pixbuf("bpqicon.png")); - - //gtk_window_get_frame_dimensions(GTK_WINDOW(window),&left,&top,&right,&bottom); - - - gtk_signal_connect(GTK_OBJECT(window), "delete_event", GTK_SIGNAL_FUNC(delete_event), NULL); - -// g_signal_connect(G_OBJECT(window), "configure-event", G_CALLBACK(frame_callback), NULL); - - // Create a box for the menu - - box1 = gtk_vbox_new (FALSE, 0); - gtk_container_add (GTK_CONTAINER (window), box1); - - checklabel = gtk_label_new(" "); - - check1 = gtk_check_button_new_with_label ("Show only My Msgs "); - check4 = gtk_check_button_new_with_label ("All SSIDs "); - check2 = gtk_check_button_new_with_label ("Show only Sequenced Msgs "); - check3 = gtk_check_button_new_with_label ("Show Bulls "); - - button = gtk_button_new_with_label("Clear RX"); - button2 = gtk_button_new_with_label("Clear TX"); - - gtk_toggle_button_set_active((GtkToggleButton *)check1, OnlyMine); - gtk_toggle_button_set_active((GtkToggleButton *)check2, OnlySeq); - gtk_toggle_button_set_active((GtkToggleButton *)check3, ShowBulls); - gtk_toggle_button_set_active((GtkToggleButton *)check4, AllSSID); - - g_signal_connect(G_OBJECT(check1), "clicked", G_CALLBACK(check_callback), "1"); - g_signal_connect(G_OBJECT(check2), "clicked", G_CALLBACK(check_callback), "2"); - g_signal_connect(G_OBJECT(check3), "clicked", G_CALLBACK(check_callback), "3"); - g_signal_connect(G_OBJECT(check4), "clicked", G_CALLBACK(check_callback), "4"); - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(button_callback), "1"); - g_signal_connect(G_OBJECT(button2), "clicked", G_CALLBACK(button2_callback), "1"); - - // hBox for Check boxes - - checkhbox = gtk_hbox_new (FALSE, 0); - gtk_box_pack_start (GTK_BOX (checkhbox), checklabel, FALSE, FALSE, 0); - gtk_container_add (GTK_CONTAINER (checkhbox), check1); - gtk_container_add (GTK_CONTAINER (checkhbox), check4); - gtk_container_add (GTK_CONTAINER (checkhbox), check2); - gtk_container_add (GTK_CONTAINER (checkhbox), check3); - gtk_container_add (GTK_CONTAINER (checkhbox), button); - gtk_container_add (GTK_CONTAINER (checkhbox), button2); - checklabel = gtk_label_new(" "); - gtk_container_add (GTK_CONTAINER (checkhbox), checklabel); - - gtk_box_pack_start (GTK_BOX (box1), checkhbox, FALSE, FALSE, 0); - - box10 = gtk_vbox_new (FALSE, 0); - -// menubar = get_menubar_menu (window); - -// gtk_box_pack_start (GTK_BOX (box1), menubar, FALSE, TRUE, 1); - gtk_container_add (GTK_CONTAINER (box1), box10); - gtk_widget_show (window); - - vpaned = gtk_vpaned_new (); - gtk_container_add (GTK_CONTAINER (box10), vpaned); - gtk_paned_set_position(GTK_PANED(vpaned), Split); - gtk_widget_show (vpaned); - - /* Now create the contents of the two halves of the window */ - - frame1 = create_received_window(); - gtk_paned_add1 (GTK_PANED (vpaned), frame1); - gtk_widget_show (frame1); - - frame2 = create_sent_window(); - gtk_paned_add2 (GTK_PANED (vpaned), frame2); - gtk_widget_show (frame2); - -// separator = gtk_hseparator_new (); -// gtk_box_pack_start(GTK_BOX (box1), separator, FALSE, TRUE, 0); - - box2 = gtk_hbox_new(FALSE, 10); - gtk_container_set_border_width(GTK_CONTAINER (box2), 1); - gtk_box_pack_start(GTK_BOX (box10), box2, FALSE, FALSE, 0); - - // set up the text entry line - - label1 = gtk_label_new(" To"); - label2 = gtk_label_new("Message"); - combo = gtk_combo_box_text_new_with_entry(); - gtk_widget_set_size_request(combo, 100, 10); - - entry = gtk_entry_new(); -// gtk_widget_set_size_request(entry, 100, 20); //gtk_entry_new_with_buffer(text); - gtk_entry_set_max_length(GTK_ENTRY(entry), 100); - gtk_entry_set_activates_default(GTK_ENTRY (entry), TRUE); - g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK(enter_callback), (gpointer)entry); - gtk_box_pack_start(GTK_BOX(box2), label1, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(box2), combo, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(box2), label2, FALSE, FALSE, 0); - gtk_container_add(GTK_CONTAINER(box2), entry); - gtk_widget_grab_focus(entry); - - - - - font_desc=pango_font_description_from_string(MyFont); - gtk_widget_modify_font (entry, font_desc); - gtk_widget_modify_font (combo, font_desc); - gtk_widget_modify_font (view, font_desc); - gtk_widget_modify_font (view2, font_desc); - - gtk_widget_show_all (window); - gtk_widget_show (window); - - // Main loop - - _beginthread(GTKThread, 0, NULL); - _beginthread(SecTimer, 0, NULL); - - AutoFilterTimer = AUTOFILTERDELAY; // Update filter if no change for 30 secs - - while(Running) - { - unsigned char Msg[256]; - int numBytes; - struct STATIONRECORD * Station; - - FD_ZERO(&in_fds); - FD_SET(x11_fd, &in_fds); - FD_SET(sfd, &in_fds); - - tv.tv_usec = 0; - tv.tv_sec = 1; - - // Wait for X Event, Message from LinBPQ or a Timer - - if (select(maxfd+1, &in_fds, 0, 0, &tv)) - { - if (FD_ISSET(sfd, &in_fds)) - { - numBytes = recvfrom(sfd, Msg, 256, 0, NULL, NULL); - } - - // may be X event, but pick up later - } - else - { - // Handle timer here - - if (SMEM->NeedRefresh) - { - SMEM->NeedRefresh = FALSE; - - // Use Checkbox event to rewrite display - - check_callback((GtkButton *)check1, "1"); - RefreshTXList(); - } - - // NeedRedraw += RefreshStationMap(FALSE); // Draw new or moved stations - - // Do a full redraw at least evey 2 mins if anything has changed - -// SlowTimer++; -// if (SlowTimer > 40) // 2 Mins -// if (NeedRedraw) -// NeedRefresh = TRUE; - } - - // Handle XEvents and flush the input - - while(Running && XPending(display)) - { - XNextEvent(display, &event); - - MouseX = event.xbutton.x - leftBorder; - MouseY = event.xbutton.y - topBorder; - - GetMouseLatLon(&MouseLat, &MouseLon); - - switch (event.type) - { - - case ClientMessage: - - if (event.xclient.data.l[0] == wmDeleteMessage) - Running = FALSE; - break; - - case KeyPress: - - xkeyev = event.xkey; - - XLookupString(&event.xkey, text, 255, &key, 0); - -// printf("Key %c Hex %x Code %d %x\n", text[0], text[0], key, key); - - switch(key) - { - case XK_Left: - - WindowX -= 8; - XMoveWindow(display, win, WindowX, WindowY); - break; - - case XK_Up: - - WindowY -= 8; - XMoveWindow(display, win, WindowX, WindowY); - break; - - case XK_Right: - - WindowX += 8; - XMoveWindow(display, win, WindowX, WindowY); - break; - - case XK_Down: - - WindowY += 8; - XMoveWindow(display, win, WindowX, WindowY); - break; - - case '-': - - ZoomOut(); - break; - - case '=': - case '+': - - ZoomIn(); - break; - } - - break; - - case Expose: - - XPutImage (display, win, gc, image, ScrollX, ScrollY, leftBorder, topBorder, cxImgSize, cyImgSize); - break; - - case ConfigureNotify: - - xce = event.xconfigure; - - // This event type is generated for a variety of - // happenings, so check whether the window has been - // resized. - - WindowX = xce.x; - WindowY = xce.y; - WindowWidth = xce.width; - WindowHeight = xce.height; - - if (xce.width != cxWinSize || xce.height != cyWinSize) - { - cxWinSize = xce.width; - cyWinSize = xce.height; - cxImgSize = cxWinSize - (leftBorder + rightBorder); - cyImgSize = cyWinSize - (topBorder + bottomBorder); - - XClearWindow(display, win); - XPutImage (display, win, gc, image, ScrollX, ScrollY, leftBorder, topBorder, cxImgSize, cyImgSize); - } - - break; - - case MotionNotify: - - FindStationsByPixel(MouseX + ScrollX, MouseY + ScrollY); - break; - - - case ButtonPress: - - switch (event.xbutton.button) - { - case 1: // Left Button - - LastX = MouseX; - LastY = MouseY; - break; - - case 4: // Scrollup - - ZoomIn(); - break; - - case 5: // Scrolldown - - ZoomOut(); - break; - } - - break; - - case ButtonRelease: - - if (popupActive && (event.xbutton.time - lastupevent) < 300) - { - // Double Click on Station - - char Key[32]; - char LoppedCall[10]; - int n = 8; - - memcpy(LoppedCall, popupStn->Callsign, 9); - - while (n && LoppedCall[n] == ' ') - { - n--; - } - - if (n) - LoppedCall[n + 1] = 0; - - sprintf(Key, "|%s|", LoppedCall); - - // if new call add to combo box - - if (strstr(ToCalls, Key) == 0) - { - if (strlen(ToCalls) < 1000) - strcat(ToCalls, Key); - - gtk_combo_box_text_prepend_text ((GtkComboBoxText *)combo, LoppedCall); - } - } - - lastupevent = event.xbutton.time; - - switch (event.xbutton.button) - { - case 1: // Left Button - - // if a Popup is on display, then select station, else scroll map - - if (selActive) - { - GetStationFromList(MouseX, MouseY); - break; - } - - MovedX = MouseX - LastX; - MovedY = MouseY - LastY; - - if (MovedX == 0 && MovedY == 0) - break; - - ScrollX -= (MovedX); - ScrollY -= (MovedY); - - while (ScrollX < 0) - { - TileX--; - ScrollX += 256; - } - - while (ScrollX > 255) - { - TileX++; - ScrollX -= 256; - } - - if (TileX < 0) - { - TileX = 0; - ScrollX = 0; - } - - while (ScrollY < 0) - { - TileY--; - ScrollY += 256; - } - - while (ScrollY > 255) - { - TileY++; - ScrollY -= 256; - } - - if (TileY < 0) - { - TileY = 0; - ScrollY = 0; - } - - NeedRefresh = TRUE; - AutoFilterTimer = AUTOFILTERDELAY; // Update filter if no change for 30 secs - - break; - } - break; - } - } // end of while xpending - - if (popupActive || selActive) - { - } - else - { - if (NeedRefresh) - { - SetBaseX = -1; - LoadImageSet(Zoom, TileX, TileY); - NeedRefresh = FALSE; - SlowTimer = 0; - } - if (NeedRedraw) - { - NeedRedraw = 0; - XPutImage (display, win, gc, image, ScrollX, ScrollY, leftBorder, topBorder, cxImgSize, cyImgSize); - } - } - } - - SaveConfig(); - - status = XDestroyImage (image); - return 0; -} - -void PutAPRSMessage(char * Frame, int Len) -{ - if (sendto(sfd, Frame, Len, 0, (struct sockaddr *) &peer_addr, sizeof(struct sockaddr_un)) != Len) - perror("sendto"); -} - -VOID SendFilterCommand(char * Filter) -{ - char Msg[2000]; - int n; - - strcpy(Msg, "SERVER"); - strcpy(&Msg[10], Filter); - - PutAPRSMessage(Msg, strlen(&Msg[10]) + 11); - - strcpy(&Msg[10], "filter?"); - PutAPRSMessage(Msg, strlen(&Msg[10]) + 11); -} - -void RefreshTXList() -{ - struct APRSMESSAGE * Message = SMEM->OutstandingMsgs; - int n = 0; - - GtkTreeIter iter; - gchar *seq; - char status[10]; - - // Clear old list - - if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(sentitems), &iter, NULL, 0)) - { - while (gtk_list_store_remove(sentitems, &iter)) - {} - } - while (Message) - { - if (Message->Acked) - strcpy(status, "A"); - else if (Message->Cancelled) - strcpy(status, "C"); - else if (Message->Retries == 0) - strcpy(status, "F"); - else - sprintf(status, "%d", Message->Retries); - - gtk_list_store_insert_with_values( - sentitems, &iter, -1, - 0, Message->ToCall, - 1, Message->Seq, - 2, status, - 3, Message->Time, - 4, Message->Text, -1); - n++; - - Message = Message->Next; - } - - if (n) - gtk_tree_view_scroll_to_cell ((GtkTreeView *)view, - gtk_tree_model_get_path (GTK_TREE_MODEL(sentitems), &iter), NULL, FALSE, 0, 0); -} - -VOID SendAPRSMessage(const char * Text, char * ToCall) -{ - char Msg[255]; - - strcpy(Msg, ToCall); - strcpy(&Msg[10], Text); - - PutAPRSMessage(Msg, strlen(&Msg[10]) + 11); - - return; -} - -VOID SecTimer() -{ - while (TRUE) - { - struct STATIONRECORD * sptr = *StationRecords; - int n = 0; - char Msg[20]; - - if (Host1Down) - Host1Down --; - - if (Host2Down) - Host2Down --; - - - // See if changed flag set on any stations - - NeedRedraw += RefreshStationMap(FALSE); // Draw new or moved stations - - // Do a full redraw at least evey 2 mins if anything has changed - - SlowTimer++; - if (SlowTimer > 120) // 2 Mins - if (NeedRedraw) - NeedRefresh = TRUE; - -/* - while (sptr) - { - if (sptr->Moved) - DrawStation(sptr, FALSE); - - sptr = sptr->Next; - } -*/ - JPEGCounter++; - - if (CreateJPEG) - { - if (JPEGCounter > JPEGInterval) - { - if (RGBToJpegFile(JPEGFileName, Image, cxImgSize, cyImgSize, TRUE, 50)) - JPEGCounter = 0; - } - } - -/* - if (SendWX) - SendWeatherBeacon(); - - // If any changes to image redraw it - - if (ImageChanged) - { - // We have drawn a new Icon. As we only redraw if it has moved, - // we need to reload image every now and again to get rid of ghost images - - time_t NOW = time(NULL); - - if ((NOW - LastRefresh) > 10) - { - LastRefresh = NOW; - ReloadMaps = TRUE; - } - - ImageChanged = FALSE; - InvalidateRect(hMapWnd, NULL, FALSE); - } - - wsprintf(Msg, "%d", StationCount); - SendMessage(hStatus, SB_SETTEXT, (WPARAM)(INT) 0 | 1, (LPARAM)Msg); - - wsprintf(Msg, "%d", OSMQueueCount); - SendMessage(hStatus, SB_SETTEXT, (WPARAM)(INT) 0 | 2, (LPARAM)Msg); - - wsprintf(Msg, "%d", Zoom); - SendMessage(hStatus, SB_SETTEXT, (WPARAM)(INT) 0 | 3, (LPARAM)Msg); -*/ - - if (AutoFilterTimer) - { - AutoFilterTimer--; - - if (AutoFilterTimer == 0 && AddViewToFilter) - { - // send filter to IS - - double TLLat, TLLon, BRLat, BRLon; - char Filter[256]; - - GetCornerLatLon(&TLLat, &TLLon, &BRLat, &BRLon); - sprintf(Filter, "%s a/%.3f/%.3f/%.3f/%.3f", ISFilter, TLLat, TLLon, BRLat, BRLon); - - SendFilterCommand(Filter); - } - } - - Sleep(1000); - } -} - -BOOL RGBToJpegFile(char * fileName, BYTE *dataBuf, UINT widthPix, UINT height, BOOL color, int quality) -{ - struct jpeg_compress_struct cinfo; - struct my_error_mgr jerr; - FILE * outfile=NULL; - unsigned char * RGBBuff = malloc(widthPix * 3); // no idea why - unsigned char * ptr1, * ptr2; - int n; - unsigned int val, r, g, b; - char Message[32]; - int X, Y, j, Len; - struct tm * TM; - time_t NOW = time(NULL); - - if (dataBuf==NULL) - return FALSE; - if (widthPix==0) - return FALSE; - if (height==0) - return FALSE; - - // Write a Date/Time stamp to top left - - if (LocalTime) - TM = localtime(&NOW); - else - TM = gmtime(&NOW); - - Len = sprintf(Message, "%02d:%02d:%02d %02d %s %04d", - TM->tm_hour, TM->tm_min, TM->tm_sec, - TM->tm_mday, month[TM->tm_mon], TM->tm_year + 1900); - - X = ScrollX; - Y = ScrollY + 8; - - for (j = 0; j < Len; j++) - { - DrawCharacter(X, Y, j, Message[j]); - } - - - - /* More stuff */ - /* Step 1: allocate and initialize JPEG compression object */ - - cinfo.err = jpeg_std_error(&jerr.pub); - jerr.pub.error_exit = my_error_exit; - - /* Establish the setjmp return context for my_error_exit to use. */ - - if (setjmp(jerr.setjmp_buffer)) - { - /* If we get here, the JPEG code has signaled an error. - * We need to clean up the JPEG object, close the input file, and return. - */ - - jpeg_destroy_compress(&cinfo); - - if (outfile!=NULL) - fclose(outfile); - - return FALSE; - } - - /* Now we can initialize the JPEG compression object. */ - - jpeg_create_compress(&cinfo); - - /* Step 2: specify data destination (eg, a file) */ - /* Note: steps 2 and 3 can be done in either order. */ - - if ((outfile = fopen(fileName, "wb")) == NULL) - { -// char buf[250]; -// sprintf(buf, "JpegFile :\nCan't open %s\n", fileName); -// MessageBox(NULL, buf, "", 0); - return FALSE; - } - - jpeg_stdio_dest(&cinfo, outfile); - - /* Step 3: set parameters for compression */ - - /* First we supply a description of the input image. - * Four fields of the cinfo struct must be filled in: - */ - cinfo.image_width = widthPix; /* image widthPix and height, in pixels */ - cinfo.image_height = height; - - cinfo.input_components = 3; /* # of color components per pixel */ - cinfo.in_color_space = JCS_RGB; /* colorspace of input image */ - -/* Now use the library's routine to set default compression parameters. - * (You must set at least cinfo.in_color_space before calling this, - * since the defaults depend on the source color space.) - */ - - jpeg_set_defaults(&cinfo); - /* Now you can set any non-default parameters you wish to. - * Here we just illustrate the use of quality (quantization table) scaling: - */ - jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */); - - /* Step 4: Start compressor */ - - /* TRUE ensures that we will write a complete interchange-JPEG file. - * Pass TRUE unless you are very sure of what you're doing. - */ - jpeg_start_compress(&cinfo, TRUE); - - /* Step 5: while (scan lines remain to be written) */ - /* jpeg_write_scanlines(...); */ - - /* Here we use the library's state variable cinfo.next_scanline as the - * loop counter, so that we don't have to keep track ourselves. - * To keep things simple, we pass one scanline per call; you can pass - * more if you wish, though. - */ - - while (cinfo.next_scanline < cinfo.image_height) - { - /* jpeg_write_scanlines expects an array of pointers to scanlines. - * Here the array is only one element long, but you could pass - * more than one scanline at a time if that's more convenient. - */ - - unsigned char * outRow; - - outRow = RGBBuff; - - // We have to convert from 2 or 4 bytes to pixel to 3 byte rgb - - ptr1 = dataBuf + ((ScrollY * WIDTH) + ScrollX + (cinfo.next_scanline * WIDTH)) * Bytesperpixel; - - ptr2 = RGBBuff; - - for (n = 0; n < widthPix; n++) - { - if (Bytesperpixel == 2) - { - val = (*(ptr1++)); - val |= (*(ptr1++)) << 8; - - b = val << 3; - g = (val >> 5) << 2; - r = (val >> 11) << 3; - - *(ptr2++) = r; - *(ptr2++) = g; - *(ptr2++) = b; - } - else - { - *(ptr2++) = *(ptr1+2); - *(ptr2++) = *(ptr1+1); - *(ptr2++) = *(ptr1); - ptr1 += 4; - } - } - (void) jpeg_write_scanlines(&cinfo, &outRow, 1); - } - - /* Step 6: Finish compression */ - - jpeg_finish_compress(&cinfo); - - /* After finish_compress, we can close the output file. */ - fclose(outfile); - - /* Step 7: release JPEG compression object */ - - /* This is an important step since it will release a good deal of memory. */ - jpeg_destroy_compress(&cinfo); - - /* And we're done! */ - - return TRUE; -} - -VOID plot(int X, int Y, COLORREF rgb) -{ - char * nptr; - int i, j; - unsigned int val; - - if ((X > (WIDTH - 3)) || (Y > (HEIGHT - 3))) - return; - - nptr = &Image[(Y * WIDTH * Bytesperpixel) + (X * Bytesperpixel)]; - - for (j = 0; j < 2; j++) - { - for (i = 0; i < 2; i++) - { - if (Bytesperpixel == 4) - { - *(nptr++) = (rgb >> 16) & 0xff; - *(nptr++) = (rgb >> 8) & 0xff; - *(nptr++) = GetRValue(rgb); - nptr++; - } - else - { - val = ((rgb & 0xff) >> 3) << 11; // Red - val |= (GetGValue(rgb) >> 2) << 5; - val |= (rgb >> 19); // Blue - *(nptr++) = (val & 0xff); - *(nptr++) = (unsigned char)(val >> 8); - } - - } - nptr += (WIDTH - 2) * Bytesperpixel; - } -} - -// Algorithm assumes y increases slower than x. If not, swap x and y in plotline and plotpoint - -void plotLineTB(int x0, int y0, int x1, int y1, COLORREF rgb); -void plotLineLR(int x0, int y0, int x1, int y1, COLORREF rgb); - -void plotLine(int x0, int y0, int x1, int y1, COLORREF rgb) -{ - if (abs(x1 - x0) > abs(y1 - y0)) - plotLineLR(x0, y0, x1, y1, rgb); - else - plotLineTB(y0, x0, y1, x1, rgb); -} - -void plotLineLR(int x0, int y0, int x1, int y1, COLORREF rgb) -{ - int dx; - int dy; - int D, x, y; - - // Must draw with increacing x and y, but can draw either way round, so if x is decreasing, - // just swap ends, so we always draw left to right - - if (x0 > x1) - { - x = x0; - x0 = x1; - x1 = x; - - y = y0; - y0 = y1; - y1 = y; - } - - // if y is now decreasing, we must reverse algorithm - - if (y1 > y0) - { - dx = x1 - x0; - dy = y1 - y0; - D = 2 * dy - dx; - - plot (x0, y0, 0); - - y = y0; - - for (x = x0+1; x < x1; x++) - { - if (D < 0) - { - D = D + (2*dy); - } - else - { - y = y+1; - D = D + (2*dy-2*dx); - - } - plot(x, y, rgb); - } - } - else - { - dx = x1 - x0; - dy = y0 - y1; - D = 2 * dy - dx; - - plot (x0, y0, rgb); - - y = y0; - - for (x = x0+1; x <= x1; x++) - { - if (D > 0) - { - y = y-1; - plot(x, y, rgb); - D = D + (2*dy-2*dx); - } - else - { - plot(x, y, rgb); - D = D + (2*dy); - } - } - } - -} - -void plotLineTB(int x0, int y0, int x1, int y1, COLORREF rgb) -{ - int dx; - int dy; - int D, x, y; - - // Must draw with increacing x and y, but can draw either way round, so if x is decreasing, - // just swap ends, so we always draw left to right - - if (x0 > x1) - { - x = x0; - x0 = x1; - x1 = x; - - y = y0; - y0 = y1; - y1 = y; - } - - // if y is now decreasing, we must reverse algorithm - - if (y1 > y0) - { - dx = x1 - x0; - dy = y1 - y0; - D = 2 * dy - dx; - - plot (y0, x0, 0); - - y = y0; - - for (x = x0+1; x < x1; x++) - { - if (D < 0) - { - D = D + (2*dy); - } - else - { - y = y+1; - D = D + (2*dy-2*dx); - - } - plot(y, x, rgb); - } - } - else - { - dx = x1 - x0; - dy = y0 - y1; - D = 2 * dy - dx; - - plot (y0, x0, 0); - - y = y0; - - for (x = x0+1; x <= x1; x++) - { - if (D > 0) - { - y = y-1; - D = D + (2*dy-2*dx); - } - else - { - D = D + (2*dy); - } - plot(y, x, rgb); - - } - } -} - - -png_const_charp msg; - - -static png_structp png_ptr = NULL; -static png_infop info_ptr = NULL; - - -// cexcept interface - -static void -png_cexcept_error(png_structp png_ptr, png_const_charp msg) -{ - if(png_ptr) - ; -#ifndef PNG_NO_CONSOLE_IO - fprintf(stderr, "libpng error: %s\n", msg); -#endif - { -// Throw msg; - } -} - - -int LoadImageFile (void * hwnd, char * pstrPathName, - png_byte **ppbImage, int *pxImgSize, int *pyImgSize, - int *piChannels, png_color *pBkgColor) -{ - - // if there's an existing PNG, free the memory - - if (*ppbImage) - { - free (*ppbImage); - *ppbImage = NULL; - } - - PngLoadImage (pstrPathName, ppbImage, pxImgSize, pyImgSize, piChannels, - pBkgColor); - - - - if (*ppbImage != NULL) - { - // sprintf (szTmp, "VisualPng - %s", strrchr(pstrPathName, '\\') + 1); - // SetWindowText (hwnd, szTmp); - } - else - { - return FALSE; - } - - return TRUE; -} - -//---------------- -// PNG image handler functions - -BOOL PngLoadImage (char * pstrFileName, png_byte **ppbImageData, - png_uint_32 *piWidth, png_uint_32 *piHeight, int *piChannels, png_color *pBkgColor) -{ - static FILE *pfFile; - png_byte pbSig[8]; - int iBitDepth; - int iColorType; - double dGamma; - png_color_16 *pBackground; - png_uint_32 ulChannels; - png_uint_32 ulRowBytes; - png_byte *pbImageData = *ppbImageData; - static png_byte **ppbRowPointers = NULL; - int i; - - // open the PNG input file - - if (!pstrFileName) - { - *ppbImageData = pbImageData = NULL; - printf("Load PNG Failed 1\n"); - return FALSE; - } - - if (!(pfFile = fopen(pstrFileName, "rb"))) - { - *ppbImageData = pbImageData = NULL; - printf("Load PNG Failed 2\n"); - return FALSE; - } - - // first check the eight byte PNG signature - - fread(pbSig, 1, 8, pfFile); - - if(png_sig_cmp(pbSig, 0, 8) != 0) - //if (!png_check_sig(pbSig, 8)) - { - *ppbImageData = pbImageData = NULL; - - if (pfFile) - fclose (pfFile); - - printf("Bad file %s", pstrFileName); - unlink(pstrFileName); - - return FALSE; - } - - // create the two png(-info) structures - - png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, - (png_error_ptr)png_cexcept_error, (png_error_ptr)NULL); - if (!png_ptr) - { - *ppbImageData = pbImageData = NULL; - printf("Load PNG Failed 4\n"); - return FALSE; - } - - info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) - { - png_destroy_read_struct(&png_ptr, NULL, NULL); - *ppbImageData = pbImageData = NULL; - printf("Load PNG Failed 5\n"); - return FALSE; - } - -// Try - { - - // initialize the png structure - -#if !defined(PNG_NO_STDIO) - png_init_io(png_ptr, pfFile); -#else - png_set_read_fn(png_ptr, (png_voidp)pfFile, png_read_data); -#endif - - png_set_sig_bytes(png_ptr, 8); - - // read all PNG info up to image data - - png_read_info(png_ptr, info_ptr); - - // get width, height, bit-depth and color-type - - png_get_IHDR(png_ptr, info_ptr, piWidth, piHeight, &iBitDepth, - &iColorType, NULL, NULL, NULL); - - // expand images of all color-type and bit-depth to 3x8 bit RGB images - // let the library process things like alpha, transparency, background - - if (iBitDepth == 16) - png_set_strip_16(png_ptr); - if (iColorType == PNG_COLOR_TYPE_PALETTE) - png_set_expand(png_ptr); - if (iBitDepth < 8) - png_set_expand(png_ptr); - if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) - png_set_expand(png_ptr); - if (iColorType == PNG_COLOR_TYPE_GRAY || - iColorType == PNG_COLOR_TYPE_GRAY_ALPHA) - png_set_gray_to_rgb(png_ptr); - - // set the background color to draw transparent and alpha images over. - if (png_get_bKGD(png_ptr, info_ptr, &pBackground)) - { - png_set_background(png_ptr, pBackground, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0); - pBkgColor->red = (byte) pBackground->red; - pBkgColor->green = (byte) pBackground->green; - pBkgColor->blue = (byte) pBackground->blue; - } - else - { - pBkgColor = NULL; - } - - // if required set gamma conversion - if (png_get_gAMA(png_ptr, info_ptr, &dGamma)) - png_set_gamma(png_ptr, (double) 2.2, dGamma); - - // after the transformations have been registered update info_ptr data - - png_read_update_info(png_ptr, info_ptr); - - // get again width, height and the new bit-depth and color-type - - png_get_IHDR(png_ptr, info_ptr, piWidth, piHeight, &iBitDepth, - &iColorType, NULL, NULL, NULL); - - - // row_bytes is the width x number of channels - - ulRowBytes = png_get_rowbytes(png_ptr, info_ptr); - ulChannels = png_get_channels(png_ptr, info_ptr); - - *piChannels = ulChannels; - - // now we can allocate memory to store the image - - if (pbImageData) - { - free (pbImageData); - pbImageData = NULL; - } - if ((pbImageData = (png_byte *) malloc(ulRowBytes * (*piHeight) - * sizeof(png_byte))) == NULL) - { - png_error(png_ptr, "Visual PNG: out of memory"); - } - *ppbImageData = pbImageData; - - // and allocate memory for an array of row-pointers - - if ((ppbRowPointers = (png_bytepp) malloc((*piHeight) - * sizeof(png_bytep))) == NULL) - { - png_error(png_ptr, "Visual PNG: out of memory"); - } - - // set the individual row-pointers to point at the correct offsets - - for (i = 0; i < (*piHeight); i++) - ppbRowPointers[i] = pbImageData + i * ulRowBytes; - - // now we can go ahead and just read the whole image - - png_read_image(png_ptr, ppbRowPointers); - - // read the additional chunks in the PNG file (not really needed) - - png_read_end(png_ptr, NULL); - - // and we're done - - free (ppbRowPointers); - ppbRowPointers = NULL; - - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - - - // yepp, done - } -/* - Catch (msg) - { - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - - *ppbImageData = pbImageData = NULL; - - if(ppbRowPointers) - free (ppbRowPointers); - - fclose(pfFile); - - return FALSE; - } -*/ - if (pfFile) - fclose (pfFile); - - return TRUE; -} - +/* +Copyright 2001-2015 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// Version 0.0.3.1 July 2016 +// Switch to Thunderforest tile server + +// Version 0.0.4.1 January 2019 +// Add option to set IS filter to map view automatically + +// Version 1.1.14.5 March 2020 +// Add option to run two instances of Linbpq and APRS + +// Version 1.1.14.6 Sept 2021 +// Use my Tile Servers + + +#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. +#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#define LINBPQ + +#include "compatbits.h" + +#include "BPQAPRS.h" + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#define XK_MISCELLANY +#include + +#include +#include + +#include +#include + +#define LIBCONFIG_STATIC +#include "libconfig.h" + +#include +#include +#include + +#define PNG_SKIP_SETJMP_CHECK + +#include + +#define VOID void +#define UCHAR unsigned char +#define BOOL int +#define BYTE unsigned char +#define UINT unsigned int +#define TRUE 1 +#define FALSE 0 + + +#undef PNG_NO_STDIO + +int multiple = 0; + + +GtkWidget *dialog; +GtkWidget *window; +GtkWidget *dialog; +GtkWidget *window; +GtkWidget *box1; +GtkWidget *box2; +GtkWidget *box3; +GtkWidget *hbox; +GtkWidget *button; +GtkWidget *button2; +GtkWidget *checklabel; +GtkWidget *check1; +GtkWidget *check2; +GtkWidget *check3; +GtkWidget *check4; +GtkWidget *checkhbox; +GtkWidget *separator; +GtkWidget *table; +GtkWidget *vscrollbar; +GtkWidget *vscrollbar2; +GtkTextBuffer *text; +GtkTextBuffer *text2; +GtkWidget *entry; +GtkWidget *vpaned; +GtkWidget *frame1; +GtkWidget *frame2; +GtkWidget *view; +GtkWidget* scrolledwin; +GtkWidget *view2; +GtkWidget* scrolledwin2; +GtkWidget *box10; +GtkWidget *menubar; +GtkWidget *combo; +GtkWidget *label1, *label2; +GtkListStore *receiveditems; +GtkListStore *sentitems; + +GtkTreeModel *model; + +char MyFont[50] = "Monospace 10"; + +gchar *fontname; + +char RX_SOCK_PATH[] = "BPQAPRSrxsock"; +char TX_SOCK_PATH[] = "BPQAPRStxsock"; + +int sfd; +struct sockaddr_un my_addr, peer_addr; +socklen_t peer_addr_size; +int maxfd; + +struct SharedMem * SMEM; + +UCHAR * Shared; // Start of Shared Mememy +UCHAR * StnRecordBase; // Start of Station Records + +int AutoFilterTimer = 0; + +#define AUTOFILTERDELAY 20 // 20 secs + +VOID SecTimer(); +void plotLine(int x0, int y0, int x1, int y1, COLORREF rgb); +void SelectTXMsg (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data); +int LoadImageFile (void * hwnd, char * pstrPathName, + png_byte **ppbImage, int *pxImgSize, int *pyImgSize, + int *piChannels, png_color *pBkgColor); +BOOL PngLoadImage (char * pstrFileName, png_byte **ppbImageData, + png_uint_32 *piWidth, png_uint_32 *piHeight, int *piChannels, png_color *pBkgColor); + +BOOL RGBToJpegFile(char * fileName, BYTE *dataBuf, UINT widthPix, UINT height, BOOL color, int quality); +int XDestroyImage(XImage *ximage); + +int XLookupString(XKeyEvent *event_struct, char *buffer_return, int bytes_buffer, KeySym *keysym_return, void *status_in_out); + +void RefreshTXList(); + +static png_color bkgColor = {127, 127, 127}; + +struct SEM +{ + UINT Flag; + int Clashes; + int Gets; + int Rels; +}; + + +struct SEM Semaphore = {0, 0, 0, 0}; + +void GetSemaphore(struct SEM * Semaphore) +{ + // + // Wait for it to be free + // + + if (Semaphore->Flag != 0) + { + Semaphore->Clashes++; + } + +loop1: + + while (Semaphore->Flag != 0) + { + Sleep(10); + } + + // try to get semaphore + + if (__sync_lock_test_and_set(&Semaphore->Flag, 1) != 0) + + // Failed to get it + goto loop1; // try again; + + //Ok. got it + + Semaphore->Gets++; + + return; +} + +void FreeSemaphore(struct SEM * Semaphore) +{ + if (Semaphore->Flag == 0) + printf("Free Semaphore Called when Sem not held\n"); + + Semaphore->Rels++; + Semaphore->Flag = 0; + + return; +} + +char * strlop(char * buf, char delim) +{ + // Terminate buf at delim, and return rest of string + + char * ptr = strchr(buf, delim); + + if (ptr == NULL) return NULL; + + *(ptr)++=0; + + return ptr; +} + +unsigned long _beginthread(void(*start_address)(), unsigned stack_size, VOID * arglist) +{ + pthread_t thread; + + if (pthread_create(&thread, NULL, (void * (*)(void *))start_address, (void*) arglist) != 0) + perror("New Thread"); + else + pthread_detach(thread); + + return thread; +} + +int Sleep(int ms) +{ + usleep(ms * 1000); + return 0; +} + +struct OSMQUEUE OSMQueue = {NULL,0,0,0}; + +int OSMQueueCount = 0; + +static int cxWinSize = 788, cyWinSize = 788; +static int cxImgSize = 768, cyImgSize = 768; +static int topBorder = 30, bottomBorder = 0; +static int leftBorder = 2, rightBorder = 2; + +static int cImgChannels = 3; +static int ImgChannels; + +int Bytesperpixel = 4; + +int ExpireTime = 120; +int TrackExpireTime = 1440; +BOOL SuppressNullPosn = FALSE; +BOOL DefaultNoTracks = FALSE; +BOOL LocalTime = TRUE; +BOOL KM = FALSE; +BOOL AddViewToFilter = FALSE; + +char ISFilter[1000] = "m/50 u/APBPQ*"; + +int SlowTimer = 0; + +BOOL CreateJPEG = TRUE; +int JPEGInterval = 300; +int JPEGCounter = 0; +char JPEGFileName[MAX_PATH] = "BPQAPRS/HTML/APRSImage.jpg"; + +char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + + +Display * display; +Window root, win; +GC gc; +XImage * image, * popupimage; + +int SetBaseX = 0; // Lowest Tiles in currently loaded set +int SetBaseY = 0; + +int TileX = 0; +int TileY = 0; + +int Zoom = 2; + +int MaxZoom = 16; + +int MapCentreX = 256; +int MapCentreY = 256; + +int MouseX, MouseY; +int PopupX, PopupY; + +double MouseLat, MouseLon; + +BOOL NeedRefresh = FALSE; +int NeedRedraw = 0; + +int ScrollX = 128; +int ScrollY = 128; + +int WindowX = 100, WindowY = 100; // Position of window on screen +int WindowWidth = 788; +int WindowHeight = 788; + +BOOL popupActive = FALSE; +BOOL selActive = FALSE; + +char OSMDir[256] = "BPQAPRS/OSMTiles"; + +struct STATIONRECORD ** StationRecords = NULL; +struct STATIONRECORD * ControlRecord; + +int StationCount; + + +UCHAR NextSeq = 1; + +char APRSCall[10]; +char LoppedAPRSCall[10]; +char BaseCall[10]; + + +// Image chunks are 256 rows of 3 * 256 bytes + +// Read 8 * 8 files, and copy to a 2048 * 3 * 2048 array. The display scrolls over this area, and +// it is refreshed when window approaches the edge of the array. + +int WIDTH; +int HEIGHT; + +int WIDTHTILES = 4; +int HEIGHTTILES = 4; + +UCHAR * Image = NULL; +UCHAR * iconImage = NULL; +UCHAR * PopupImage = NULL; + +BOOL ImageChanged = 0; + +int RetryCount = 7; +int RetryIntervals[] = {0, 512, 256, 128, 64, 32, 16, 8}; + +// Station Name Font + +const unsigned char ASCII[][5] = { +//const u08 ASCII[][5] = { + {0x00, 0x00, 0x00, 0x00, 0x00} // 20 + ,{0x00, 0x00, 0x5f, 0x00, 0x00} // 21 ! + ,{0x00, 0x07, 0x00, 0x07, 0x00} // 22 " + ,{0x14, 0x7f, 0x14, 0x7f, 0x14} // 23 # + ,{0x24, 0x2a, 0x7f, 0x2a, 0x12} // 24 $ + ,{0x23, 0x13, 0x08, 0x64, 0x62} // 25 % + ,{0x36, 0x49, 0x55, 0x22, 0x50} // 26 & + ,{0x00, 0x05, 0x03, 0x00, 0x00} // 27 ' + ,{0x00, 0x1c, 0x22, 0x41, 0x00} // 28 ( + ,{0x00, 0x41, 0x22, 0x1c, 0x00} // 29 ) + ,{0x14, 0x08, 0x3e, 0x08, 0x14} // 2a * + ,{0x08, 0x08, 0x3e, 0x08, 0x08} // 2b + + ,{0x00, 0x50, 0x30, 0x00, 0x00} // 2c , + ,{0x08, 0x08, 0x08, 0x08, 0x08} // 2d - + ,{0x00, 0x60, 0x60, 0x00, 0x00} // 2e . + ,{0x20, 0x10, 0x08, 0x04, 0x02} // 2f / + ,{0x3e, 0x51, 0x49, 0x45, 0x3e} // 30 0 + ,{0x00, 0x42, 0x7f, 0x40, 0x00} // 31 1 + ,{0x42, 0x61, 0x51, 0x49, 0x46} // 32 2 + ,{0x21, 0x41, 0x45, 0x4b, 0x31} // 33 3 + ,{0x18, 0x14, 0x12, 0x7f, 0x10} // 34 4 + ,{0x27, 0x45, 0x45, 0x45, 0x39} // 35 5 + ,{0x3c, 0x4a, 0x49, 0x49, 0x30} // 36 6 + ,{0x01, 0x71, 0x09, 0x05, 0x03} // 37 7 + ,{0x36, 0x49, 0x49, 0x49, 0x36} // 38 8 + ,{0x06, 0x49, 0x49, 0x29, 0x1e} // 39 9 + ,{0x00, 0x36, 0x36, 0x00, 0x00} // 3a : + ,{0x00, 0x56, 0x36, 0x00, 0x00} // 3b ; + ,{0x08, 0x14, 0x22, 0x41, 0x00} // 3c < + ,{0x14, 0x14, 0x14, 0x14, 0x14} // 3d = + ,{0x00, 0x41, 0x22, 0x14, 0x08} // 3e > + ,{0x02, 0x01, 0x51, 0x09, 0x06} // 3f ? + ,{0x32, 0x49, 0x79, 0x41, 0x3e} // 40 @ + ,{0x7e, 0x11, 0x11, 0x11, 0x7e} // 41 A + ,{0x7f, 0x49, 0x49, 0x49, 0x36} // 42 B + ,{0x3e, 0x41, 0x41, 0x41, 0x22} // 43 C + ,{0x7f, 0x41, 0x41, 0x22, 0x1c} // 44 D + ,{0x7f, 0x49, 0x49, 0x49, 0x41} // 45 E + ,{0x7f, 0x09, 0x09, 0x09, 0x01} // 46 F + ,{0x3e, 0x41, 0x49, 0x49, 0x7a} // 47 G + ,{0x7f, 0x08, 0x08, 0x08, 0x7f} // 48 H + ,{0x00, 0x41, 0x7f, 0x41, 0x00} // 49 I + ,{0x20, 0x40, 0x41, 0x3f, 0x01} // 4a J + ,{0x7f, 0x08, 0x14, 0x22, 0x41} // 4b K + ,{0x7f, 0x40, 0x40, 0x40, 0x40} // 4c L + ,{0x7f, 0x02, 0x0c, 0x02, 0x7f} // 4d M + ,{0x7f, 0x04, 0x08, 0x10, 0x7f} // 4e N + ,{0x3e, 0x41, 0x41, 0x41, 0x3e} // 4f O + ,{0x7f, 0x09, 0x09, 0x09, 0x06} // 50 P + ,{0x3e, 0x41, 0x51, 0x21, 0x5e} // 51 Q + ,{0x7f, 0x09, 0x19, 0x29, 0x46} // 52 R + ,{0x46, 0x49, 0x49, 0x49, 0x31} // 53 S + ,{0x01, 0x01, 0x7f, 0x01, 0x01} // 54 T + ,{0x3f, 0x40, 0x40, 0x40, 0x3f} // 55 U + ,{0x1f, 0x20, 0x40, 0x20, 0x1f} // 56 V + ,{0x3f, 0x40, 0x38, 0x40, 0x3f} // 57 W + ,{0x63, 0x14, 0x08, 0x14, 0x63} // 58 X + ,{0x07, 0x08, 0x70, 0x08, 0x07} // 59 Y + ,{0x61, 0x51, 0x49, 0x45, 0x43} // 5a Z + ,{0x00, 0x7f, 0x41, 0x41, 0x00} // 5b [ + ,{0x02, 0x04, 0x08, 0x10, 0x20} // 5c + ,{0x00, 0x41, 0x41, 0x7f, 0x00} // 5d ] + ,{0x04, 0x02, 0x01, 0x02, 0x04} // 5e ^ + ,{0x40, 0x40, 0x40, 0x40, 0x40} // 5f _ + ,{0x00, 0x01, 0x02, 0x04, 0x00} // 60 ` + ,{0x20, 0x54, 0x54, 0x54, 0x78} // 61 a + ,{0x7f, 0x48, 0x44, 0x44, 0x38} // 62 b + ,{0x38, 0x44, 0x44, 0x44, 0x20} // 63 c + ,{0x38, 0x44, 0x44, 0x48, 0x7f} // 64 d + ,{0x38, 0x54, 0x54, 0x54, 0x18} // 65 e + ,{0x08, 0x7e, 0x09, 0x01, 0x02} // 66 f + ,{0x0c, 0x52, 0x52, 0x52, 0x3e} // 67 g + ,{0x7f, 0x08, 0x04, 0x04, 0x78} // 68 h + ,{0x00, 0x44, 0x7d, 0x40, 0x00} // 69 i + ,{0x20, 0x40, 0x44, 0x3d, 0x00} // 6a j + ,{0x7f, 0x10, 0x28, 0x44, 0x00} // 6b k + ,{0x00, 0x41, 0x7f, 0x40, 0x00} // 6c l + ,{0x7c, 0x04, 0x18, 0x04, 0x78} // 6d m + ,{0x7c, 0x08, 0x04, 0x04, 0x78} // 6e n + ,{0x38, 0x44, 0x44, 0x44, 0x38} // 6f o + ,{0x7c, 0x14, 0x14, 0x14, 0x08} // 70 p + ,{0x08, 0x14, 0x14, 0x18, 0x7c} // 71 q + ,{0x7c, 0x08, 0x04, 0x04, 0x08} // 72 r + ,{0x48, 0x54, 0x54, 0x54, 0x20} // 73 s + ,{0x04, 0x3f, 0x44, 0x40, 0x20} // 74 t + ,{0x3c, 0x40, 0x40, 0x20, 0x7c} // 75 u + ,{0x1c, 0x20, 0x40, 0x20, 0x1c} // 76 v + ,{0x3c, 0x40, 0x30, 0x40, 0x3c} // 77 w + ,{0x44, 0x28, 0x10, 0x28, 0x44} // 78 x + ,{0x0c, 0x50, 0x50, 0x50, 0x3c} // 79 y + ,{0x44, 0x64, 0x54, 0x4c, 0x44} // 7a z + ,{0x00, 0x08, 0x36, 0x41, 0x00} // 7b { + ,{0x00, 0x00, 0x7f, 0x00, 0x00} // 7c | + ,{0x00, 0x41, 0x36, 0x08, 0x00} // 7d } + ,{0x10, 0x08, 0x08, 0x10, 0x08} // 7e ~ + ,{0x78, 0x46, 0x41, 0x46, 0x78} // 7f DEL +}; + +COLORREF Colours[256] = {0, RGB(0,0,255), RGB(0,128,0), RGB(0,128,192), + RGB(0,192,0), RGB(0,192,255), RGB(0,255,0), RGB(128,0,128), + RGB(128,64,0), RGB(128,128,128), RGB(192,0,0), RGB(192,0,255), + RGB(192,64,128), RGB(192,128,255), RGB(255,0,0), RGB(255,0,255), // 81 + RGB(255,64,0), RGB(255,64,128), RGB(255,64,192), RGB(255,128,0)}; + + + + +struct my_error_mgr { + struct jpeg_error_mgr pub; /* "public" fields */ + + jmp_buf setjmp_buffer; /* for return to caller */ +}; + +typedef struct my_error_mgr * my_error_ptr; + +void my_error_exit (j_common_ptr cinfo) +{ + /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */ + my_error_ptr myerr = (my_error_ptr) cinfo->err; + + char buffer[JMSG_LENGTH_MAX]; + /* Create the message */ + (*cinfo->err->format_message) (cinfo, buffer); + + /* Always display the message. */ + printf("JPEG Fatal Error"); + + + /* Return control to the setjmp point */ + longjmp(myerr->setjmp_buffer, 1); +} + +int memicmp(unsigned char *a, unsigned char *b, int n) +{ + if (n) + { + while (n && toupper(*a) == toupper(*b)) + n--, a++, b++; + + if (n) + return toupper(*a) - toupper(*b); + } + return 0; +} +int stricmp(const unsigned char * pStr1, const unsigned char *pStr2) +{ + unsigned char c1, c2; + int v; + + if (pStr1 == NULL) + { + return 1; + } + + + do { + c1 = *pStr1++; + c2 = *pStr2++; + /* The casts are necessary when pStr1 is shorter & char is signed */ + v = tolower(c1) - tolower(c2); + } while ((v == 0) && (c1 != '\0') && (c2 != '\0') ); + + return v; +} + +char * strupr(char* s) +{ + char* p = s; + + if (s == 0) + return 0; + + while (*p = toupper( *p )) p++; + return s; +} + +// Return coorinates in tiles. + +double long2x(double lon, int z) +{ + return (lon + 180.0) / 360.0 * pow(2.0, z); +} + +double lat2y(double lat, int z) +{ + return (1.0 - log( tan(lat * M_PI/180.0) + 1.0 / cos(lat * M_PI/180.0)) / M_PI) / 2.0 * pow(2.0, z); +} + +double tilex2long(double x, int z) +{ + return x / pow(2.0, z) * 360.0 - 180; +} + +double tiley2lat(double y, int z) +{ + double n = M_PI - 2.0 * M_PI * y / pow(2.0, z); + return 180.0 / M_PI * atan(0.5 * (exp(n) - exp(-n))); +} + +void GetCornerLatLon(double * TLLat, double * TLLon, double * BRLat, double * BRLon) +{ + int X = ScrollX; + int Y = ScrollY; + + *TLLat = tiley2lat(SetBaseY + (Y / 256.0), Zoom); + *TLLon = tilex2long(SetBaseX + (X / 256.0), Zoom); + + X = ScrollX + cxWinSize; + Y = ScrollY + cyWinSize; + + *BRLat = tiley2lat(SetBaseY + (Y / 256.0), Zoom); + *BRLon = tilex2long(SetBaseX + (X / 256.0), Zoom); +} + + +void GetMouseLatLon(double * Lat, double * Lon) +{ + int X = ScrollX + MouseX; + int Y = ScrollY + MouseY; + + *Lat = tiley2lat(SetBaseY + (Y / 256.0), Zoom); + *Lon = tilex2long(SetBaseX + (X / 256.0), Zoom); +} + +BOOL GetLocPixels(double Lat, double Lon, int * X, int * Y) +{ + // Get the pixel offet of supplied location in current image. + + // If location is outside current image, return FAlSE + + int TileX; + int TileY; + int OffsetX, OffsetY; + double FX; + double FY; + + // if TileX or TileY are outside the window, return null + + FX = long2x(Lon, Zoom); + TileX = (int)floor(FX); + OffsetX = TileX - SetBaseX; + + if (OffsetX < 0 || OffsetX > 7) + return FALSE; + + FY = lat2y(Lat, Zoom); + TileY = (int)floor(FY); + OffsetY = TileY - SetBaseY; + + if (OffsetY < 0 || OffsetY > 7) + return FALSE; + + FX -= TileX; + FX = FX * 256.0; + + *X = (int)FX + 256 * OffsetX; + + FY -= TileY; + FY = FY * 256.0; + + *Y = (int)FY + 256 * OffsetY; + + return TRUE; +} + +int long2tilex(double lon, int z) +{ + return (int)(floor((lon + 180.0) / 360.0 * pow(2.0, z))); +} + +int lat2tiley(double lat, int z) +{ + return (int)(floor((1.0 - log( tan(lat * M_PI/180.0) + 1.0 / cos(lat * M_PI/180.0)) / M_PI) / 2.0 * pow(2.0, z))); +} + + +BOOL CentrePositionToMouse(double Lat, double Lon) +{ + // Positions specified location at the mouse + + int X, Y; + + SetBaseX = long2tilex(Lon, Zoom) - 2; + SetBaseY = lat2tiley(Lat, Zoom) - 2; // Set Location at middle + + if (GetLocPixels(Lat, Lon, &X, &Y) == FALSE) + return FALSE; // Off map + + ScrollX = X - cxWinSize/2; + ScrollY = Y - cyWinSize/2; + + +// Map is now centered at loc cursor was at + +// Need to move by distance mouse is from centre + + // if ScrollX, Y are zero, the centre of the map corresponds to 1024, 1024 + +// ScrollX -= 1024 - X; // Posn to centre +// ScrollY -= 1024 - Y; + + ScrollX += cxWinSize/2 - MouseX; + ScrollY += cyWinSize/2 - MouseY; + + // May Need to move image + + while(ScrollX < 0) + { + SetBaseX--; + ScrollX += 256; + } + + while(ScrollY < 0) + { + SetBaseY--; + ScrollY += 256; + } + + while(ScrollX > 255) + { + SetBaseX++; + ScrollX -= 256; + } + + while(ScrollY > 255) + { + SetBaseY++; + ScrollY -= 256; + } + + AutoFilterTimer = AUTOFILTERDELAY; // Update filter if no change for 30 secs + + return TRUE; +} + +SOCKADDR_IN destaddr1 = {0}; +SOCKADDR_IN destaddr2 = {0}; + +unsigned int ipaddr = 0; + +//char Host[] = "tile.openstreetmap.org"; + +//char Host[] = "oatile1.mqcdn.com"; //SAT +//char Host[] = "otile1.mqcdn.com"; + +//char Host[] = "tile.thunderforest.com"; + +char Host[] = "server.g8bpq.net"; +char Host1[] = "server1.g8bpq.net"; +char Host2[] = "server2.g8bpq.net"; + +int Host1Down = 0; +int Host2Down = 0; + +char mapStyle[64] = "outdoors"; //"neighbourhood mobile-atlas + + +char HeaderTemplate[] = "Accept: */*\r\nHost: %s\r\nConnection: close\r\nContent-Length: 0\r\nUser-Agent: BPQ32(G8BPQ)\r\n\r\n"; + + +VOID ResolveThread() +{ + struct hostent * HostEnt; + int err; + +// while (TRUE) + { + // Resolve Name if needed + + HostEnt = gethostbyname(Host1); + + if (!HostEnt) + { + err = WSAGetLastError(); + printf("Resolve Failed for %s %d %x\n", Host1, err); + } + else + { + memcpy(&destaddr1.sin_addr.s_addr,HostEnt->h_addr,4); + } + + HostEnt = gethostbyname(Host2); + + if (!HostEnt) + { + err = WSAGetLastError(); + printf("Resolve Failed for %s %d %x\n", Host2, err); + } + else + { + memcpy(&destaddr2.sin_addr.s_addr,HostEnt->h_addr,4); + } +/// Sleep(60 * 15 * 1000); + } +} + + + +VOID OSMGet(int x, int y, int zoom) +{ + struct OSMQUEUE * OSMRec = malloc(sizeof(struct OSMQUEUE)); + + GetSemaphore(&Semaphore); + + OSMQueueCount++; + + OSMRec->Next = OSMQueue.Next; + OSMQueue.Next = OSMRec; + OSMRec->x = x; + OSMRec->y = y; + OSMRec->Zoom = zoom; + + FreeSemaphore(&Semaphore); +} + +VOID RefreshTile(char * FN, int TileZoom, int Tilex, int Tiley); + +VOID OSMThread() +{ + // Request a page from OSM + + char FN[256]; + char Tile[80]; + struct OSMQUEUE * OSMRec; + int Zoom, x, y; + + SOCKET sock; + SOCKADDR_IN sinx; + int addrlen=sizeof(sinx); + int err; + u_long param=1; + BOOL bcopt=TRUE; + char Request[100]; + char Header[256]; + UCHAR Buffer[200000]; + int Len, InputLen = 0; + UCHAR * ptr; + int inptr = 0; + struct stat STAT; + FILE * Handle; + + destaddr1.sin_family = AF_INET; + destaddr1.sin_port = htons(7381); + destaddr2.sin_family = AF_INET; + destaddr2.sin_port = htons(7381); + + while (TRUE) + { + while (OSMQueue.Next) + { + GetSemaphore(&Semaphore); + + OSMRec = OSMQueue.Next; + OSMQueue.Next = OSMRec->Next; + + OSMQueueCount--; + + FreeSemaphore(&Semaphore); + + x = OSMRec->x; + y = OSMRec->y; + Zoom = OSMRec->Zoom; + + free(OSMRec); + +// wsprintf(Tile, "/%02d/%d/%d.png", Zoom, x, y); +// wsprintf(Tile, "/tiles/1.0.0/sat/%02d/%d/%d.jpg", Zoom, x, y); +// sprintf(Tile, "/tiles/1.0.0/osm/%02d/%d/%d.jpg", Zoom, x, y); + +// sprintf(Tile, "/%s/%d/%d/%d.png?apikey=41ab899ed1fd4d09b11da7caf3a48e1f", mapStyle, Zoom, x, y); + + sprintf(Tile, "/styles/klokantech-basic/%d/%d/%d.png", Zoom, x, y); + + sprintf(FN, "%s/%02d/%d/%d.png", OSMDir, Zoom, x, y); + + if (stat(FN, &STAT) == 0) + { + printf(" File %s Exists - skipping\n", FN); + continue; + } + + printf("Getting %s\n", FN); + + Len = sprintf(Request, "GET %s HTTP/1.0\r\n", Tile); + + // Allocate a Socket entry + + sock=socket(AF_INET,SOCK_STREAM,0); + + if (sock == INVALID_SOCKET) + return; + + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); + + if (Host1Down == 0) + { + if (connect(sock,(LPSOCKADDR) &destaddr1, sizeof(destaddr1)) != 0) + { + printf("OSM GET Connect to %s Failed %d\n", Host1, errno); + Host1Down = 600; // Don't try again for 10 mins + } + else + goto ConnectOK; + } + if (Host2Down == 0) + { + if (connect(sock,(LPSOCKADDR) &destaddr2, sizeof(destaddr2)) != 0) + { + printf("OSM GET Connect to %s Failed %d\n", Host2, errno); + Host1Down = 600; // Don't try again for 10 mins + } + else + goto ConnectOK; + } + + // + // Neither available or connect failed to both + // + + // Reduce retry timers + + if (Host1Down > 60 && Host2Down > 60) + { + Host1Down = 60; + Host2Down = 60; + } + + break; + +ConnectOK: + +//GET /15/15810/9778.png HTTP/1.0 +//Accept: */* +//Host: tile.openstreetmap.org +//Connection: close +//Content-Length: 0 +//User-Agent: APRSIS32(G8BPQ) + + InputLen = 0; + inptr = 0; + + send(sock, Request, Len, 0); + sprintf(Header, HeaderTemplate, Host); + send(sock, Header, strlen(Header), 0); + + while (InputLen != -1) + { + InputLen = recv(sock, &Buffer[inptr], 200000 - inptr, 0); + + if (InputLen > 0) + inptr += InputLen; + else + { + // File Complete?? + + if (strstr(Buffer, " 200 OK")) + { + ptr = strstr(Buffer, "Content-Length:"); + + if (ptr) + { + int FileLen = atoi(ptr + 15); + ptr = strstr(Buffer, "\r\n\r\n"); + + if (ptr) + { + ptr += 4; + char Dir[256]; + + if (FileLen == inptr - (ptr - Buffer)) + { + // File is OK + + int cnt; + + Handle = fopen(FN, "wb"); + + if (Handle) + { + fwrite(ptr, 1, FileLen, Handle); + fclose(Handle); + printf("Tile %s Loaded\n", FN); + RefreshTile(FN, Zoom, x, y); + break; + } + + if (errno != 2) // Bad Path + { + printf("Create %s failed %d\n", FN, errno); + perror("fopen"); + break; + } + + sprintf(Dir, "%s/%02d/%d", OSMDir, Zoom, x); + + if (mkdir(Dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) + { + printf("Error Creating %s\n", FN); + perror("mkdir"); + break; + } + + // Retry Create + + Handle = fopen(FN, "wb"); + + if (Handle) + { + fwrite(ptr, 1, FileLen, Handle); + fclose(Handle); + + printf("Tile %s Loaded\n", FN); + RefreshTile(FN, Zoom, x, y); + break; + } + + printf("Create %s falled\n", FN); + perror("fopen"); + break; + } + } + } + } + printf("OSM GET Bad Response %s ", Buffer); + sprintf(FN, "%s/DummyTile.jpg", OSMDir); + RefreshTile(FN, Zoom, x, y); + + break; + } + } + close(sock); + } + + // Queue is empty + + sleep(1); +} +} + +double radians(double Degrees) +{ + return M_PI * Degrees / 180; +} +double degrees(double Radians) +{ + return Radians * 180 / M_PI; +} + + + +double Distance(double laa, double loa) +{ + double lah = ControlRecord->Lat; + double loh = ControlRecord->Lon; + double dist; +/* + +'Great Circle Calculations. + +'dif = longitute home - longitute away + + +' (this should be within -180 to +180 degrees) +' (Hint: This number should be non-zero, programs should check for +' this and make dif=0.0001 as a minimum) +'lah = latitude of home +'laa = latitude of away + +'dis = ArcCOS(Sin(lah) * Sin(laa) + Cos(lah) * Cos(laa) * Cos(dif)) +'distance = dis / 180 * pi * ERAD +'angle = ArcCOS((Sin(laa) - Sin(lah) * Cos(dis)) / (Cos(lah) * Sin(dis))) + +'p1 = 3.1415926535: P2 = p1 / 180: Rem -- PI, Deg =>= Radians +*/ + + loh = radians(loh); lah = radians(lah); + loa = radians(loa); laa = radians(laa); + + dist = 60*degrees(acos(sin(lah) * sin(laa) + cos(lah) * cos(laa) * cos(loa-loh))) * 1.15077945; + + if (KM) + dist *= 1.60934; + + return dist; +} + +double Bearing(double lat2, double lon2) +{ + double lat1 = ControlRecord->Lat; + double lon1 = ControlRecord->Lon; + double dlat, dlon, TC1; + + lat1 = radians(lat1); + lat2 = radians(lat2); + lon1 = radians(lon1); + lon2 = radians(lon2); + + dlat = lat2 - lat1; + dlon = lon2 - lon1; + + if (dlat == 0 || dlon == 0) return 0; + + TC1 = atan((sin(lon1 - lon2) * cos(lat2)) / (cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon1 - lon2))); + TC1 = degrees(TC1); + + if (fabs(TC1) > 89.5) if (dlon > 0) return 90; else return 270; + + if (dlat > 0) + { + if (dlon > 0) return -TC1; + if (dlon < 0) return 360 - TC1; + return 0; + } + + if (dlat < 0) + { + if (dlon > 0) return TC1 = 180 - TC1; + if (dlon < 0) return TC1 = 180 - TC1; // 'ok? + return 180; + } + + return 0; + +} + + + +VOID DecodeWXReport(struct APRSConnectionInfo * sockptr, char * WX) +{ + UCHAR * ptr = strchr(WX, '_'); + char Type; + int Val; + + if (ptr == 0) + return; + + sockptr->WindDirn = atoi(++ptr); + ptr += 4; + sockptr->WindSpeed = atoi(ptr); + ptr += 3; +WXLoop: + + Type = *(ptr++); + + if (*ptr =='.') // Missing Value + { + while (*ptr == '.') + ptr++; + + goto WXLoop; + } + + Val = atoi(ptr); + + switch (Type) + { + case 'c': // = wind direction (in degrees). + + sockptr->WindDirn = Val; + break; + + case 's': // = sustained one-minute wind speed (in mph). + + sockptr->WindSpeed = Val; + break; + + case 'g': // = gust (peak wind speed in mph in the last 5 minutes). + + sockptr->WindGust = Val; + break; + + case 't': // = temperature (in degrees Fahrenheit). Temperatures below zero are expressed as -01 to -99. + + sockptr->Temp = Val; + break; + + case 'r': // = rainfall (in hundredths of an inch) in the last hour. + + sockptr->RainLastHour = Val; + break; + + case 'p': // = rainfall (in hundredths of an inch) in the last 24 hours. + + sockptr->RainLastDay = Val; + break; + + case 'P': // = rainfall (in hundredths of an inch) since midnight. + + sockptr->RainToday = Val; + break; + + case 'h': // = humidity (in %. 00 = 100%). + + sockptr->Humidity = Val; + break; + + case 'b': // = barometric pressure (in tenths of millibars/tenths of hPascal). + + sockptr->Pressure = Val; + break; + + default: + + return; + } + while(isdigit(*ptr)) + { + ptr++; + } + + if (*ptr != ' ') + goto WXLoop; +} + +struct STATIONRECORD * FindStation(char * Call, BOOL AddIfNotFount) +{ + int i = 0; + struct STATIONRECORD * find; + struct STATIONRECORD * ptr; + struct STATIONRECORD * last = NULL; + int sum = 0; + + if (StationRecords == 0) + return FALSE; + + if (strlen(Call) > 9) + Call[9] = 0; + + find = *StationRecords; + while(find) + { + if (strlen(find->Callsign) > 9) + find->Callsign[9] = 0; + + if (strcmp(find->Callsign, Call) == 0) + return find; + + last = find; + find = find->Next; + i++; + } + + // Not found - add on end + +/* + if (AddIfNotFount) + { + // Get first from station record pool + + ptr = StationRecordPool; + + if (ptr) + { + StationRecordPool = ptr->Next; // Unchain + StationCount++; + } + else + { + // Get First from Stations + + ptr = *StationRecords; + if (ptr) + *StationRecords = ptr->Next; + } + + if (ptr == NULL) return NULL; + + memset(ptr, 0, sizeof(struct STATIONRECORD)); + +// EnterCriticalSection(&Crit); + + if (*StationRecords == NULL) + *StationRecords = ptr; + else + last->Next = ptr; + +// LeaveCriticalSection(&Crit); + + // Debugprintf("APRS Add Stn %s Station Count = %d", Call, StationCount); + + strcpy(ptr->Callsign, Call); + ptr->TimeAdded = time(NULL); + ptr->Index = i; + ptr->NoTracks = DefaultNoTracks; + + for (i = 0; i < 9; i++) + sum += Call[i]; + + sum %= 20; + + ptr->TrackColour = sum; + ptr->Moved = TRUE; + + return ptr; + } + else + */ + return NULL; +} + +int PopupHeight; +int PopupWidth; +int PopupLeft; +int PopupTop; +struct STATIONRECORD * popupStn; +struct STATIONRECORD * List[1000] = {0}; + + +VOID CreateStationPopup(struct STATIONRECORD * ptr, int RelX, int RelY) +{ + char Msg[80]; + int Len = 130; + int Line = 12; + struct tm * TM; + int x, y; + + PopupLeft = RelX - 10; + PopupTop = RelY - 30; + + if (PopupLeft + 400 > cxWinSize) + PopupLeft = cxWinSize - 405; + + if (PopupTop + 150> cyWinSize) + PopupTop= cyWinSize - 165; + + popupActive = TRUE; + popupStn = ptr; + PopupHeight = 200; + PopupWidth = 350; + + XClearArea(display, win, PopupLeft, PopupTop, 350, 200, FALSE); + XDrawRectangle(display, win, gc, PopupLeft, PopupTop, 350, 200); + + x = PopupLeft; + y = PopupTop; + + if (LocalTime) + TM = localtime(&ptr->TimeLastUpdated); + else + TM = gmtime(&ptr->TimeLastUpdated); + + Len = sprintf(Msg, "Last Heard: %.2d:%.2d:%.2d on Port %d", + TM->tm_hour, TM->tm_min, TM->tm_sec, ptr->LastPort); + + XDrawImageString(display, win, gc, x + 2, y + Line, ptr->Callsign, strlen(ptr->Callsign)); + Line += 12; + + XDrawImageString(display, win, gc, x + 2, y + Line, ptr->Path, strlen(ptr->Path)); + Line += 12; + +// XDrawImageString(display, win, gc, x + 2, y + Line, ptr->Status, 40); +// Line += 12; + + XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); + Line += 12; + + +// Item.pszText = ptr->LastPacket; + + Len = sprintf(Msg, "Distance %6.1f Bearing %3.0f Course %1.0f Speed %3.1f", + Distance(ptr->Lat, ptr->Lon), + Bearing(ptr->Lat, ptr->Lon), ptr->Course, ptr->Speed); + + XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); + Line += 12; + + + if (ptr->LastWXPacket[0]) + { + //display wx info + + struct APRSConnectionInfo temp; + + memset(&temp, 0, sizeof(temp)); + + DecodeWXReport(&temp, ptr->LastWXPacket); + + Len = sprintf(Msg, "Wind Speed %d MPH", temp.WindSpeed); + XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); + Line += 12; + + Len = sprintf(Msg, "Wind Gust %d MPH", temp.WindGust); + XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); + Line += 12; + + Len = sprintf(Msg, "Wind Direction %d°", temp.WindDirn); + XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); + Line += 12; + + Len = sprintf(Msg, "Temperature %d°F", temp.Temp); + XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); + Line += 12; + + Len = sprintf(Msg, "Pressure %05.1f", temp.Pressure /10.0); + XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); + Line += 12; + + Len = sprintf(Msg, "Humidity %d%%", temp.Humidity); + XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); + Line += 12; + } + +/* +Rain last hour##RAIN_HOUR_IN##" +Rain today##RAIN_TODAY_IN##" +Rain last 24 hours##RAIN_24_IN##" + +*/ +} + +VOID GetStationFromList(int MouseX, int MouseY) +{ + int RelX = MouseX + leftBorder; + int RelY = MouseY + topBorder; + + int index = (RelY - PopupTop) /12; + + if (List[index]) + { + selActive = FALSE; + CreateStationPopup(List[index], RelX, RelY); + } +} + +VOID FindStationsByPixel(int MouseX, int MouseY) +{ + int j=0; + struct STATIONRECORD * ptr = *StationRecords; + int RelX = MouseX - ScrollX + leftBorder; + int RelY = MouseY - ScrollY + topBorder; + + if (popupActive || selActive) + { + // if mouse within popup, leave alone + + if (RelX > PopupLeft && RelX < (PopupLeft + PopupWidth) && + RelY > PopupTop && RelY < (PopupTop + PopupHeight)) + return; + } + + while(ptr && j < 999) + { + if (abs((ptr->DispX - MouseX)) < 4 && abs((ptr->DispY - MouseY)) < 4) + List[j++] = ptr; + + ptr = ptr->Next; + } + + if (j == 0) + { + if (popupActive) + { + XPutImage (display, win, gc, image, ScrollX, ScrollY, leftBorder, topBorder, cxImgSize, cyImgSize); + popupActive = 0; + } + + if (selActive) + { + XPutImage (display, win, gc, image, ScrollX, ScrollY, leftBorder, topBorder, cxImgSize, cyImgSize); + selActive = 0; + } + return; + } + + // If only one, display info popup, else display selection popup + + if (popupActive || selActive) + return; // Already on display + + if (j == 1) + { + CreateStationPopup(List[0], RelX, RelY); + } + else + { + char Msg[80]; + int Line = 12; + int i; + + PopupLeft = RelX - 10; + PopupTop = RelY - 30; + + if (j > 20) + j = 20; + + PopupHeight = j * 12 + 4; + PopupWidth = 80; + + if (PopupLeft + 80 > cxWinSize) + PopupLeft = cxWinSize - 85; + + if (PopupTop + PopupHeight > cyWinSize) + PopupTop = cyWinSize - PopupHeight; + + selActive = TRUE; + + XClearArea(display, win, PopupLeft, PopupTop, 80, PopupHeight, FALSE); + XDrawRectangle(display, win, gc, PopupLeft, PopupTop, 80, PopupHeight); + + for (i = 0; i < j; i++) + { + memset(Msg, ' ', 12); + memcpy(Msg, List[i]->Callsign, strlen(List[i]->Callsign)); + XDrawImageString(display, win, gc, PopupLeft + 2, PopupTop + Line, Msg, 11); + Line += 12; + } + } +} + +VOID DrawCharacter(int X, int Y, int j, unsigned char chr) +{ + // Font is 5 bits wide x 8 high. Each byte of font contains one column, so 5 bytes per char + + int Pointer, i, c, index, bit, mask; + + Pointer = ((Y - 5) * WIDTH * Bytesperpixel) + ((X + 11) * Bytesperpixel) + (j * 6 * Bytesperpixel); + + mask = 1; + + for (i = 0; i < 2; i++) + { + for (index = 0 ; index < 6 ; index++) + { + Image[Pointer++] = 255; // Blank lines above chars + Image[Pointer++] = 255; + if (Bytesperpixel == 4) + { + Image[Pointer++] = 255; + Pointer++; + } + } + + Pointer += (WIDTH - 6) * Bytesperpixel; + } + + // Pointer = ((Y - 3) * 2048 * 3) + (X * 3) + 36 + (j * 18); + + for (i = 0; i < 7; i++) + { + Image[Pointer++] = 255; // Blank col between chars + Image[Pointer++] = 255; + if (Bytesperpixel == 4) + { + Image[Pointer++] = 255; + Pointer++; + } + for (index = 0 ; index < 5 ; index++) + { + c = ASCII[chr - 0x20][index]; // Font data + bit = c & mask; + + if (bit) + { + Image[Pointer++] = 0; + Image[Pointer++] = 0; + if (Bytesperpixel == 4) + { + Image[Pointer++] = 0; + Pointer++; + } + } + else + { + Image[Pointer++] = 255; + Image[Pointer++] = 255; + if (Bytesperpixel == 4) + { + Image[Pointer++] = 255; + Pointer++; + } + } + } + mask <<= 1; + Pointer += (WIDTH - 6) * Bytesperpixel; + } + + // Pointer = ((Y - 3) * 2048 * 3) + (X * 3) + 36 + (j * 18); + + mask = 1; + + for (i = 0; i < 2; i++) + { + for (index = 0 ; index < 6 ; index++) + { + Image[Pointer++] = 255; // Blank lines below chars between chars + Image[Pointer++] = 255; + if (Bytesperpixel == 4) + { + Image[Pointer++] = 255; + Pointer++; + } + } + Pointer += (WIDTH - 6) * Bytesperpixel; + } +} + +int DrawStation(struct STATIONRECORD * ptr, BOOL AllStations) +{ + int X, Y, Pointer, i, c, index, bit, mask, calllen, calllenpixels; + UINT j; + char Overlay; + char * nptr; + time_t AgeLimit = time(NULL ) - (TrackExpireTime * 60); + int SavePointer; + + if (ptr->Moved == 0 && AllStations == 0) + return 0; // No need to repaint + + if (SuppressNullPosn && ptr->Lat == 0.0) + return 0; + + if (ptr->ObjState == '_') // Killed Object + return 0; + + if (GetLocPixels(ptr->Lat, ptr->Lon, &X, &Y)) + { + if (X < 12 || Y < 12 || X > (WIDTH - 36) || Y > (HEIGHT - 36)) + return 0; // Too close to edges + + if (ptr->LatTrack[0] && ptr->NoTracks == FALSE) + { + // Draw Track + + int Index = ptr->Trackptr; + int i, n; + int X, Y; + int LastX = 0, LastY = 0; + + for (n = 0; n < TRACKPOINTS; n++) + { + if (ptr->LatTrack[Index] && ptr->TrackTime[Index] > AgeLimit) + { + if (GetLocPixels(ptr->LatTrack[Index], ptr->LonTrack[Index], &X, &Y)) + { + if (LastX) + { + if (abs(X - LastX) < 600 && abs(Y - LastY) < 600) + if (X > 0 && Y > 0 && X < (WIDTH - 5) && Y < (HEIGHT - 5)) + plotLine(LastX, LastY, X, Y, Colours[ptr->TrackColour]); + + } + + LastX = X; + LastY = Y; + } + } + Index++; + if (Index == TRACKPOINTS) + Index = 0; + + } + } + + ptr->Moved = 0; + + ptr->DispX = X; + ptr->DispY = Y; // Save for mouse over checks + + // X and Y are offsets into the pixel data in array Image. Actual Bytes are at Y * 2048 * 3 + (X * 3) + + // Draw Icon + + if (Y < 8) Y = 8; + if (X < 8) X = 8; + + nptr = &Image[(((Y - 8) * WIDTH) + X - 8) * Bytesperpixel]; // Center icon on station + + j = (ptr->iconRow * 21 * 337 * Bytesperpixel) + + (ptr->iconCol * 21 * Bytesperpixel) + + 3 * Bytesperpixel + (337 * 3 * Bytesperpixel); + + for (i = 0; i < 16; i++) + { + memcpy(nptr, &iconImage[j], 16 * Bytesperpixel); + nptr += WIDTH * Bytesperpixel; + j += 337 * Bytesperpixel; + } + + // If an overlay is specified, add it + + Overlay = ptr->IconOverlay; + + if (Overlay) + { + Pointer = (((Y - 4) * WIDTH) + (X - 3)) * Bytesperpixel; + mask = 1; + + for (index = 0 ; index < 7 ; index++) + { + Image[Pointer++] = 255; // Blank line above chars + Image[Pointer++] = 255; + if (Bytesperpixel == 4) + { + Image[Pointer++] = 255; + Pointer++; + } + } + Pointer += (WIDTH - 7) * Bytesperpixel; + + for (i = 0; i < 7; i++) + { + Image[Pointer++] = 255; // Blank col + Image[Pointer++] = 255; + if (Bytesperpixel == 4) + { + Image[Pointer++] = 255; + Pointer++; + } + + for (index = 0 ; index < 5 ; index++) + { + c = ASCII[Overlay - 0x20][index]; // Font data + bit = c & mask; + + + if (bit) + { + Image[Pointer++] = 0; + Image[Pointer++] = 0; + if (Bytesperpixel == 4) + { + Image[Pointer++] = 0; + Pointer++; + } + } + else + { + Image[Pointer++] = 255; + Image[Pointer++] = 255; + if (Bytesperpixel == 4) + { + Image[Pointer++] = 255; + Pointer++; + } + } + } + + Image[Pointer++] = 255; // Blank col + Image[Pointer++] = 255; + if (Bytesperpixel == 4) + { + Image[Pointer++] = 255; + Pointer++; + } + + mask <<= 1; + Pointer += (WIDTH - 7) * Bytesperpixel; + } + for (index = 0 ; index < 7 ; index++) + { + Image[Pointer++] = 255; // Blank line below chars + Image[Pointer++] = 255; + if (Bytesperpixel == 4) + { + Image[Pointer++] = 255; + Pointer++; + } + } + Pointer += (WIDTH - 6) * Bytesperpixel; + } + + calllen = strlen(ptr->Callsign); + + while (calllen && ptr->Callsign[calllen - 1] == ' ') // Remove trailing spaces + calllen--; + + calllenpixels = (calllen + 1) * 6; + + // Draw Callsign Box + + Pointer = ((Y - 7) * WIDTH * Bytesperpixel) + ((X + 9) * Bytesperpixel); + + // Draw | at each end + + for (j = 0; j < 13; j++) + { + Image[Pointer] = 0; + Image[Pointer++ + calllenpixels * Bytesperpixel] = 0; + Image[Pointer] = 0; + Image[Pointer++ + calllenpixels * Bytesperpixel] = 0; + if (Bytesperpixel == 4) + { + Image[Pointer] = 0; + Image[Pointer++ + calllenpixels * Bytesperpixel] = 0; + Pointer++; + } + Pointer += (WIDTH - 1) * Bytesperpixel; + } + + // Draw Top Line + + for (i = 0; i < calllenpixels; i++) + { + Image[Pointer++] = 0; + Image[Pointer++] = 0; + if (Bytesperpixel == 4) + { + Image[Pointer++] = 0; + Pointer++; + } + } + + // Draw Bottom Line + + Pointer = ((Y - 7) * WIDTH * Bytesperpixel) + ((X + 9) * Bytesperpixel); + + for (i = 0; i < calllenpixels; i++) + { + Image[Pointer++] = 0; + Image[Pointer++] = 0; + if (Bytesperpixel == 4) + { + Image[Pointer++] = 0; + Pointer++; + } + } + + // Draw Callsign. + + for (j = 0; j < calllen; j++) + { + DrawCharacter(X, Y,j, ptr->Callsign[j]); + } + ImageChanged = TRUE; + return 1; + } + else + { + ptr->DispX = 0; + ptr->DispY = 0; // Off Screen + } + return 0; +} + + +int RefreshStationMap(BOOL AllStations) +{ + struct STATIONRECORD * ptr = *StationRecords; + int blackColor = BlackPixel(display, DefaultScreen(display)); + int whiteColor = WhitePixel(display, DefaultScreen(display)); + int Changed = 0; + char msg[80]; + int i = 0, len; + + while (ptr) + { + Changed += DrawStation(ptr, AllStations); + i++; + ptr = ptr->Next; + } + +// NeedRefresh = FALSE; +// LastRefresh = time(NULL); + +// if (RecsDeleted) +// RefreshStationList();] + + len = sprintf(msg, "%d Stations Zoom = %d", i, Zoom); + XDrawImageString(display, win, gc, 20, 20, msg, len); + + StationCount = i; + return Changed; +} + + + +void j_putRGBScanline(BYTE *jpegline, + int widthPix, + unsigned char *outBuf, + int row, int XOffset, int YOffset) +{ + // Offsets are in tiles, not pixels + + int offset = row * WIDTH * Bytesperpixel; //widthPix + int count; + unsigned int val; + + offset += XOffset * 256 * Bytesperpixel; + offset += YOffset * 256 * WIDTH * Bytesperpixel; + + for (count = 0; count < 256; count++) + { + if (Bytesperpixel == 2) + { + val = (*(jpegline + count * 3 + 2) >> 3); + val |= ((*(jpegline + count * 3 + 1) >> 2) << 5); + val |= ((*(jpegline + count * 3 + 0) >> 3) << 11); + *(outBuf + offset++) = (val & 0xff); + *(outBuf + offset++) = (unsigned char)(val >> 8); + } + else + { + *(outBuf + offset++) = *(jpegline + count * 3 + 2); // Blue + *(outBuf + offset++) = *(jpegline + count * 3 + 1); // Green + *(outBuf + offset++) = *(jpegline + count * 3 + 0); // Red + offset++; + } + } +} + +// +// stash a gray scanline +// + +void j_putGrayScanlineToRGB(BYTE *jpegline, + int widthPix, + BYTE *outBuf, + int row) +{ + int offset = row * widthPix * 3; + int count; + for (count=0;countalloc_sarray) + ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); + + /* Step 6: while (scan lines remain to be read) */ + /* jpeg_read_scanlines(...); */ + + /* Here we use the library's state variable cinfo.output_scanline as the + * loop counter, so that we don't have to keep track ourselves. + */ + while (cinfo.output_scanline < cinfo.output_height) { + /* jpeg_read_scanlines expects an array of pointers to scanlines. + * Here the array is only one element long, but you could ask for + * more than one scanline at a time if that's more convenient. + */ + (void) jpeg_read_scanlines(&cinfo, buffer, 1); + /* Assume put_scanline_someplace wants a pointer and sample count. */ + + // asuumer all 3-components are RGBs + if (cinfo.out_color_components==3) { + + j_putRGBScanline(buffer[0], + *width, + Image, + cinfo.output_scanline-1, XOffset, YOffset); + + } else if (cinfo.out_color_components==1) { + + // assume all single component images are grayscale + j_putGrayScanlineToRGB(buffer[0], + *width, + Image, + cinfo.output_scanline-1); + + } + + } + + /* Step 7: Finish decompression */ + + (void) jpeg_finish_decompress(&cinfo); + /* We can ignore the return value since suspension is not possible + * with the stdio data source. + */ + + /* Step 8: Release JPEG decompression object */ + + /* This is an important step since it will release a good deal of memory. */ + jpeg_destroy_decompress(&cinfo); + + /* After finish_decompress, we can close the input file. + * Here we postpone it until after no more JPEG errors are possible, + * so as to simplify the setjmp error logic above. (Actually, I don't + * think that jpeg_destroy can do an error exit, but why assume anything...) + */ + fclose(infile); + + /* At this point you may want to check to see whether any corrupt-data + * warnings occurred (test whether jerr.pub.num_warnings is nonzero). + */ + + return 0; +} +// store a scanline to our data buffer + +void j_putRGBScanline(BYTE *jpegline, + int widthPix, + BYTE *outBuf, + int row, int X, int Y); + +void j_putGrayScanlineToRGB(BYTE *jpegline, + int widthPix, + BYTE *outBuf, + int row); + +VOID LoadImageTile(int Zoom, int startx, int starty, int x, int y); + +VOID RefreshTile(char * FN, int TileZoom, int Tilex, int Tiley) +{ + // Called when a new tile has been diwnloaded from OSM + + int StartRow, StartCol; + UCHAR * pbImage = NULL; + int x, y, i, j; + int ImgChannels; + + if (TileZoom != Zoom) + return; // Zoom level has changed + + x = Tilex - SetBaseX; + y = Tiley - SetBaseY; + + if (x < 0 || x > WIDTHTILES -1 || y < 0 || y > HEIGHTTILES - 1) + return; // Tile isn't part of current image; + + LoadImageTile (Zoom, Tilex, Tiley, x, y); + NeedRedraw = 1; + +// XPutImage (display, win, gc, image, ScrollX, ScrollY, leftBorder, topBorder, cxImgSize, cyImgSize); +} + + +VOID LoadImageTile(int Zoom, int startx, int starty, int x, int y) +{ + char FN[100]; + int i, j; + int StartRow; + int StartCol; + char Tile[100]; + UCHAR * pbImage = NULL; + int ImgChannels; + BOOL JPG=FALSE; + struct stat STAT; + int cx, cy; + + int Limit = (int)pow(2, Zoom); +/* + printf("LoadImage %d %d %d\n", Limit, startx, startx); + + if (startx < 0) + startx = startx + WIDTHTILES; + else + if (startx > WIDTHTILES - 1) + startx = WIDTHTILES - startx; + + if (starty < 0) + starty = starty + HEIGHTTILES; + else + if (starty > HEIGHTTILES -1 ) + starty = HEIGHTTILES - starty; + + if (startx < 0 || startx > WIDTHTILES) + x = WIDTHTILES / 2; + + if (starty < 0 || y > HEIGHTTILES) + starty = HEIGHTTILES /2; + + printf("LoadImage %d %d %d\n", Limit, startx, starty); +*/ + if ((startx) >= Limit || (starty) >= Limit || startx< 0 || starty < 0) + { +// printf("Not Loading %d %d %d\n",Limit, startx, startx ); + return; //goto NoFile; + } + + // May be PNG or JPG + + sprintf(Tile, "/%02d/%d/%d.png", Zoom, startx, starty); + sprintf(FN, "%s%s", OSMDir, Tile); + + if (stat(FN, &STAT) == 0) + goto gotfile; + + sprintf(Tile, "/%02d/%d/%d.jpg", Zoom, startx, starty); + sprintf(FN, "%s%s", OSMDir, Tile); + + JPG = TRUE; + + if (stat(FN, &STAT) == 0) + goto gotfile; + + + OSMGet(startx, starty, Zoom); + return; + +gotfile: + + if (JPG) + { + JpegFileToRGB(FN, &cx, &cy, x, y); + ImgChannels = 3; + } + else + { + int offset; + int cxImgSize, cyImgSize; + UCHAR * ImageSave; + + LoadImageFile (NULL, FN, &pbImage, &cxImgSize, &cyImgSize, &ImgChannels, &bkgColor); + +// printf("%d %d %d\n", cxImgSize, cyImgSize, ImgChannels); +// ImgChannels = 4; + StartCol = x * Bytesperpixel * 256; + StartRow = y * 256; + +// printf("WIDTH %d Height %d Bytesperpixel = %d x = %d y = %d\n", WIDTH, HEIGHT, Bytesperpixel, x, y); + if (pbImage == NULL) + { + pbImage = malloc(256 * ImgChannels * 256); + memset(pbImage, 0x40, 256 * ImgChannels * 256); + } + + ImageSave = pbImage; + + offset = ((StartRow) * WIDTH * ImgChannels) + StartCol; + +// printf ("x %d y %d offset %d \n", x, y, offset); + + + for (i = 0; i < 256; i++) + { + int count, val; + + offset = ((StartRow + i) * WIDTH * Bytesperpixel) + StartCol; + + // this does one scan line + + for (count = 0; count < 256; count++) + { + if (Bytesperpixel == 2) + { + val = (*(pbImage + count * ImgChannels + 2) >> 3); + val |= ((*(pbImage + count * ImgChannels + 1) >> 2) << 5); + val |= ((*(pbImage + count * ImgChannels + 0) >> 3) << 11); + Image[offset++] = (val & 0xff); + Image[offset++] = (unsigned char)(val >> 8); + } + else + { + Image[offset++] = *(pbImage + count * ImgChannels + 2); // Blue + Image[offset++] = *(pbImage + count * ImgChannels + 1); // Green + Image[offset++] = *(pbImage + count * ImgChannels + 0); // Red + offset++; + } + } + pbImage += ImgChannels * 256; + } + + free(ImageSave); + } +} + + +VOID LoadImageSet(int Zoom, int TileX, int TileY) +{ + int x, y; + + if (SetBaseX != TileX || SetBaseY != TileY) + { + // Only Load if changed + + SetBaseX = TileX; // Lowest Tiles in currently loaded set + SetBaseY = TileY; + + memset(Image, 0, WIDTH * Bytesperpixel * HEIGHT); + XClearWindow(display, win); + + for (y = 0; y < HEIGHTTILES; y++) + { + for (x = 0; x < WIDTHTILES; x++) + { + LoadImageTile(Zoom, TileX + x, TileY + y, x, y); + } + } + RefreshStationMap(TRUE); + } + XPutImage (display, win, gc, image, ScrollX, ScrollY, leftBorder, topBorder, cxImgSize, cyImgSize); +} + +BYTE * ReadIcons(char * fileName, UINT *width, UINT *height) +{ + struct jpeg_decompress_struct cinfo; + struct my_error_mgr jmerr; + struct jpeg_error_mgr jerr; + FILE * infile=NULL; /* source file */ + + JSAMPARRAY buffer; /* Output row buffer */ + int row_stride; /* physical row width in output buffer */ + char buf[250]; + BYTE *dataBuf; + + *width=0; + *height=0; + + if ((infile = fopen(fileName, "rb")) == NULL) { + return NULL; + } + + cinfo.err = jpeg_std_error(&jerr); + + if (setjmp(jmerr.setjmp_buffer)) { + /* If we get here, the JPEG code has signaled an error. + * We need to clean up the JPEG object, close the input file, and return. + */ + + jpeg_destroy_decompress(&cinfo); + + if (infile!=NULL) + fclose(infile); + return NULL; + } + + + jpeg_create_decompress(&cinfo); + + jpeg_stdio_src(&cinfo, infile); + + (void) jpeg_read_header(&cinfo, TRUE); + + (void) jpeg_start_decompress(&cinfo); + + dataBuf = malloc(cinfo.output_width * 4 * cinfo.output_height); + memset(dataBuf, 0, cinfo.output_width * 4 * cinfo.output_height); + + if (dataBuf==NULL) + { + jpeg_destroy_decompress(&cinfo); + fclose(infile); + return NULL; + } + + // how big is this thing gonna be? + *width = cinfo.output_width; + *height = cinfo.output_height; + + /* JSAMPLEs per row in output buffer */ + row_stride = cinfo.output_width * cinfo.output_components; + + /* Make a one-row-high sample array that will go away when done with image */ + buffer = (*cinfo.mem->alloc_sarray) + ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); + + /* Step 6: while (scan lines remain to be read) */ + /* jpeg_read_scanlines(...); */ + + /* Here we use the library's state variable cinfo.output_scanline as the + * loop counter, so that we don't have to keep track ourselves. + */ + while (cinfo.output_scanline < cinfo.output_height) + { + int offset; + int count; + unsigned int val; + + (void) jpeg_read_scanlines(&cinfo, buffer, 1); + + offset = (cinfo.output_scanline-1) * cinfo.output_width * Bytesperpixel; + + for (count = 0; count < cinfo.output_width; count++) + { + if (Bytesperpixel == 2) + { + val = (*(buffer[0] + count * 3 + 2) >> 3); + val |= ((*(buffer[0] + count * 3 + 1) >> 2) << 5); + val |= ((*(buffer[0] + count * 3 + 0) >> 3) << 11); + *(dataBuf + offset++) = (val & 0xff); + *(dataBuf + offset++) = (unsigned char)(val >> 8); + } + else + { + *(dataBuf + offset++) = *(buffer[0] + count * 3 + 2); // Blue + *(dataBuf + offset++) = *(buffer[0] + count * 3 + 1); // Green + *(dataBuf + offset++) = *(buffer[0] + count * 3 + 0); // Red + offset++; + } + } + } + + (void) jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + fclose(infile); + return dataBuf; +} +// store a scanline to our data buffer + +void ZoomIn() +{ + if (Zoom < 16) + { + Zoom ++; + CentrePositionToMouse(MouseLat, MouseLon); + TileX = SetBaseX; + TileY = SetBaseY; + NeedRefresh = TRUE; + } +} +void ZoomOut() +{ + if (Zoom > 1) + { + Zoom --; + CentrePositionToMouse(MouseLat, MouseLon); + TileX = SetBaseX; + TileY = SetBaseY; + if (Zoom == 1) + ScrollX = ScrollY = 0; + + NeedRefresh = TRUE; + } +} + +config_t cfg; +config_setting_t *croot, *group; + +int GetIntValue(config_setting_t * group, char * name, int defaultval) +{ + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + return config_setting_get_int (setting); + + return defaultval; +} + +VOID SaveIntValue(config_setting_t * group, char * name, int value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_INT); + if(setting) + config_setting_set_int(setting, value); +} + +BOOL GetStringValue(config_setting_t * group, char * name, char * value) +{ + const char * str; + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + + if (setting) + { + str = config_setting_get_string (setting); + strcpy(value, str); + return TRUE; + } + return FALSE; +} + +VOID SaveStringValue(config_setting_t * group, char * name, char * value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_STRING); + if (setting) + config_setting_set_string(setting, value); + +} + + + +enum +{ + COL_FROM = 0, + COL_TO, + COL_SEQ, + COL_TIME, + COL_RECEIVED, + NUM_COLS +} ; + + +void CancelMessageSend (GtkWidget *menuitem, struct APRSMESSAGE * userdata) +{ +/* + userdata->Retries = 0; + userdata->RetryTimer = 0; + userdata->Cancelled = TRUE; + UpdateTXMessageLine(userdata); +*/ +} + + +void view_popup_menu_onDoNothing (GtkWidget *menuitem, gpointer userdata) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(userdata); +} + +void view_popup_menu (GtkWidget *treeview, GdkEventButton *event, struct APRSMESSAGE * userdata) +{ + GtkWidget *menu, *menuitem1,*menuitem2 ; + char Msg[80]; + + sprintf(Msg,"Cancel Message Seq %s to %s?", userdata->Seq, userdata->ToCall); + + menu = gtk_menu_new(); + + menuitem1 = gtk_menu_item_new_with_label(Msg); + menuitem2 = gtk_menu_item_new_with_label("Return"); + + g_signal_connect(menuitem1, "activate", + (GCallback) CancelMessageSend, (gpointer)userdata); + g_signal_connect(menuitem2, "activate", + (GCallback) view_popup_menu_onDoNothing, treeview); + + if (userdata->Retries) // Not active so cant cancel + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem1); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem2); + + gtk_widget_show_all(menu); + + /* Note: event can be NULL here when called from view_onPopupMenu; + * gdk_event_get_time() accepts a NULL argument */ + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, + (event != NULL) ? event->button : 0, + gdk_event_get_time((GdkEvent*)event)); + } + + +gboolean view_onButtonPressed (GtkWidget *treeview, GdkEventButton *event, gpointer userdata) +{ + // Right click on TX Message window. If a message is selected, + // Pop up a Cancel Message Window + + if (event->type == GDK_BUTTON_PRESS && event->button == 3) + { + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreePath *path; + struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs; + + if (ptr == 0) + return TRUE; + + // Make sure the entry that was clicked is selected + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + + if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), + (gint) event->x, (gint) event->y, &path, NULL, NULL, NULL)) + { + gtk_tree_selection_unselect_all(selection); + gtk_tree_selection_select_path(selection, path); + gtk_tree_path_free(path); + } + + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + + if (gtk_tree_selection_get_selected(selection, &model, &iter)) + { + gchar *Seq; + + gtk_tree_model_get (model, &iter, 1, &Seq, -1); + + // Find the message + + while (ptr) + { + if (strcmp(ptr->Seq, Seq) == 0) + { + view_popup_menu(treeview, event, ptr); + g_free(Seq); + return TRUE; + } + ptr = ptr->Next; + } + + g_free(Seq); + g_print ("Msg not found.\n"); + } + g_print ("no row selected.\n"); + } + + return FALSE; /* we did not handle this */ +} + +gboolean view_onPopupMenu (GtkWidget *treeview, gpointer userdata) +{ + view_popup_menu(treeview, NULL, userdata); + + return TRUE; /* we handled this */ +} + + +static GtkWidget *create_sent_window( void ) +{ + GtkCellRenderer *renderer; + GtkTreeModel *model; + GtkTreeIter iter; + + view = gtk_tree_view_new(); + + gtk_signal_connect (GTK_OBJECT (view), "row_activated", + GTK_SIGNAL_FUNC (SelectTXMsg), NULL); + + + g_signal_connect(view, "button-press-event", (GCallback) view_onButtonPressed, NULL); + g_signal_connect(view, "popup-menu", (GCallback) view_onPopupMenu, NULL); + + + + + renderer = gtk_cell_renderer_text_new(); + renderer->ypad = 0; + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (view), -1, "To", renderer, "text", 0, NULL); + + renderer = gtk_cell_renderer_text_new (); + renderer->ypad = 0; + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (view), -1, "Seq", renderer, "text", 1, NULL); + + renderer = gtk_cell_renderer_text_new (); + renderer->ypad = 0; + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (view), -1, "State", renderer, "text", 2, NULL); + + renderer = gtk_cell_renderer_text_new (); + renderer->ypad = 0; + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (view), -1, "Time", renderer, "text", 3, NULL); + + renderer = gtk_cell_renderer_text_new (); + renderer->ypad = 0; + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (view), -1, "Sent", renderer, "text", 4, NULL); + + sentitems = gtk_list_store_new(5, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + + model = GTK_TREE_MODEL(sentitems); + + gtk_tree_view_set_model((GtkTreeView *)view, model); + + /* The tree view has acquired its own reference to the + * model, so we can drop ours. That way the model will + * be freed automatically when the tree view is destroyed */ + + g_object_unref (model); + +// gtk_container_add (GTK_CONTAINER (window), view2); + + scrolledwin = gtk_scrolled_window_new(NULL,NULL); + gtk_container_set_border_width(GTK_CONTAINER(scrolledwin), 1); +// gtk_widget_set_size_request(scrolledwin, 300, 80); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin), GTK_SHADOW_IN); +// gtk_container_add(GTK_CONTAINER(scrolledwin), view); + //tree_view = gtk_tree_view_new(); + gtk_container_add(GTK_CONTAINER (scrolledwin), view); + //gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), GTK_TREE_MODEL (view)); + //gtk_widget_show(tree_view); +/* + gtk_table_attach (GTK_TABLE (table), scrolledwin,0, 1, 0, 1, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); +*/ + gtk_widget_show(scrolledwin); + return scrolledwin; + +} + +GdkPixbuf *create_pixbuf(const gchar * filename) +{ + GdkPixbuf *pixbuf; + GError *error = NULL; + pixbuf = gdk_pixbuf_new_from_file(filename, &error); + if(!pixbuf) { + fprintf(stderr, "%s\n", error->message); + g_error_free(error); + } + return pixbuf; +} + + +static GtkWidget *create_received_window(void) +{ + GtkCellRenderer *renderer; + + view2 = gtk_tree_view_new(); + +// gtk_tree_view_set_fixed_height_mode(view2, TRUE); + + renderer = gtk_cell_renderer_text_new(); + renderer->ypad = 0; + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view2), -1, "From", renderer, "text", COL_FROM, NULL); + + renderer = gtk_cell_renderer_text_new (); + renderer->ypad = 0; + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view2), -1, "To", renderer, "text", COL_TO, NULL); + + renderer = gtk_cell_renderer_text_new (); + renderer->ypad = 0; + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view2), -1, "Seq", renderer, "text", COL_SEQ, NULL); + + renderer = gtk_cell_renderer_text_new (); + renderer->ypad = 0; + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view2), -1, "Time", renderer, "text", COL_TIME, NULL); + + renderer = gtk_cell_renderer_text_new (); + renderer->ypad = 0; + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view2), -1, "Received", renderer, "text", COL_RECEIVED, NULL); + + receiveditems = gtk_list_store_new (NUM_COLS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + + gtk_tree_view_set_model((GtkTreeView *)view2, (GtkTreeModel *)receiveditems); + + /* The tree view has acquired its own reference to the + * model, so we can drop ours. That way the model will + * be freed automatically when the tree view is destroyed */ + + g_object_unref (receiveditems); + + scrolledwin2 = gtk_scrolled_window_new(NULL,NULL); + gtk_container_set_border_width(GTK_CONTAINER(scrolledwin2), 2); +// gtk_widget_set_size_request(scrolledwin2, 300, 80); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin2),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin2), GTK_SHADOW_IN); + gtk_container_add(GTK_CONTAINER(scrolledwin2), view2); + + gtk_widget_show(scrolledwin2); + + return scrolledwin2; + +} + +char ToCalls[1024] = ""; + +VOID SendAPRSMessage(const char * Text, char * ToCall); + +void enter_callback( GtkWidget *widget, + GtkWidget *entry ) +{ + const gchar *entry_text; + entry_text = gtk_entry_get_text (GTK_ENTRY (entry)); + gchar * tocall = strupr(gtk_combo_box_text_get_active_text((GtkComboBoxText *)combo)); + char Key[32]; + + if (strlen(tocall) > 9) + tocall[9] = 0; + + sprintf(Key, "|%s|", tocall); + + if (tocall) + { + SendAPRSMessage(entry_text, tocall); + + // if new call add to combo box + + if (strstr(ToCalls, Key) == 0) + { + if (strlen(ToCalls) < 1000) + strcat(ToCalls, Key); + + gtk_combo_box_text_prepend_text ((GtkComboBoxText *)combo, tocall); + } + + g_free(tocall); + gtk_entry_set_text (GTK_ENTRY (entry), ""); + } +} + + +void SelectTXMsg (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data) +{ + GtkTreeIter iter; + GtkTreeModel *model; + struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs; + + if (ptr == 0) + return; + + model = gtk_tree_view_get_model(tree_view); + + if (gtk_tree_model_get_iter(model, &iter, path)) + { + gchar *seq; + + gtk_tree_model_get(model, &iter, 1, &seq, -1); + + g_print ("Double-clicked row contains seq %s\n", seq); + g_free(seq); + } + + return; +} + + +VOID GTKThread() +{ + gtk_main(); +} + +int msgWinWidth = 300; +int msgWinHeight = 300; +int msgWinX = 100; +int msgWinY = 100; +int Split = 100; // Rx/Tx Window split + + +void frame_callback(GtkWindow *window, GdkEvent *event, gpointer data) +{ + int x, y; + char buf[10]; + + msgWinX = event->configure.x; + msgWinY = event->configure.y; + msgWinWidth = event->configure.width; + msgWinHeight = event->configure.height; + + // gtk_widget_set_size_request(entry, msgWinWidth - 210 , 20); //gtk_entry_new_with_buffer(text); + + // gtk_window_set_title(window, buf); + // gtk_window_set_title (GTK_WINDOW (window), "BPQAPRS Messaging"); +} + +BOOL OnlyMine = FALSE; +BOOL OnlySeq = FALSE; +BOOL ShowBulls = FALSE; +BOOL AllSSID = FALSE; + + +void check_callback(GtkButton *button, gpointer user_data) +{ + GtkTreeIter iter; + struct APRSMESSAGE * ptr = SMEM->Messages; + int n = 0; + + char BaseFrom[10]; + + OnlyMine = gtk_toggle_button_get_active((GtkToggleButton *)check1); + OnlySeq = gtk_toggle_button_get_active((GtkToggleButton *)check2); + ShowBulls = gtk_toggle_button_get_active((GtkToggleButton *)check3); + AllSSID = gtk_toggle_button_get_active((GtkToggleButton *)check4); + + // rewite the Message display with new filter + + if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(receiveditems), &iter, NULL, 0)) + { + while (gtk_list_store_remove(receiveditems, &iter)) + {} + } + while (ptr) + { + if (memcmp(ptr->ToCall, "BLN", 3)== 0) + if (ShowBulls == TRUE) + goto wantit; + + if (strcmp(ptr->ToCall, APRSCall) == 0) // to me? + goto wantit; + + if (AllSSID) + { + memcpy(BaseFrom, ptr->ToCall, 10); + strlop(BaseFrom, ' '); + strlop(BaseFrom, '-'); + + if (strcmp(BaseFrom, BaseCall) == 0) + goto wantit; + } + + if (OnlyMine == FALSE) // Want All + if (OnlySeq == FALSE || ptr->Seq[0] != 0) + goto wantit; + + // ignore + + ptr = ptr->Next; + continue; + + wantit: + + gtk_list_store_insert_with_values( + receiveditems, &iter, -1, + COL_FROM, ptr->FromCall, + COL_TO, ptr->ToCall, + COL_SEQ, ptr->Seq, + COL_TIME, ptr->Time, + COL_RECEIVED, ptr->Text, -1); + + ptr = ptr->Next; + n++; + } + + + if (n) + gtk_tree_view_scroll_to_cell((GtkTreeView *)view2, + gtk_tree_model_get_path (GTK_TREE_MODEL(receiveditems), &iter), NULL, FALSE, 0, 0); + + +} + + +void button_callback(GtkButton *button, gpointer user_data) +{ + SMEM->ClearRX = 1; +} + + +void button2_callback(GtkButton *button, gpointer user_data) +{ + // Clear Sent Messages + + SMEM->ClearTX = 1; +} + + +static gboolean delete_event (GtkWidget *widget, GdkEvent *event, gpointer data) +{ + // Don't allow window to be closed + + return TRUE; +} + +void SaveConfig() +{ + memset((void *)&cfg, 0, sizeof(config_t)); + + config_init(&cfg); + + croot = config_root_setting(&cfg); + + gtk_window_get_size(GTK_WINDOW(window), &msgWinWidth, &msgWinHeight); + gtk_window_get_position(GTK_WINDOW(window), &msgWinX, &msgWinY); + Split = gtk_paned_get_position((GtkPaned *)vpaned); + + group = config_setting_add(croot, "APRS", CONFIG_TYPE_GROUP); + + SaveIntValue(group, "Zoom", Zoom); + SaveIntValue(group, "SetBaseX", SetBaseX); + SaveIntValue(group, "SetBaseY", SetBaseY); + SaveIntValue(group, "ScrollX", ScrollX); + SaveIntValue(group, "ScrollY", ScrollY); + SaveIntValue(group, "WindowX", WindowX); + SaveIntValue(group, "WindowY", WindowY); + SaveIntValue(group, "WindowWidth", WindowWidth); + SaveIntValue(group, "WindowHeight", WindowHeight); + SaveIntValue(group, "WIDTHTILES", WIDTHTILES); + SaveIntValue(group, "HEIGHTTILES", HEIGHTTILES); + SaveIntValue(group, "msgWinWidth", msgWinWidth); + SaveIntValue(group, "msgWinHeight", msgWinHeight); + SaveIntValue(group, "msgWinX", msgWinX); + SaveIntValue(group, "msgWinY", msgWinY); + SaveIntValue(group, "Split", Split); + + SaveIntValue(group, "OnlyMine", OnlyMine); + SaveIntValue(group, "OnlySeq", OnlySeq); + SaveIntValue(group, "ShowBulls", ShowBulls); + + SaveIntValue(group, "LocalTime", LocalTime); + SaveIntValue(group, "KM", KM); + SaveIntValue(group, "AddViewToFilter", AddViewToFilter); + SaveStringValue(group, "ISFilter", ISFilter); + + + SaveIntValue(group, "CreateJPEG", CreateJPEG); + SaveIntValue(group, "JPEGInterval", JPEGInterval); + SaveStringValue(group, "JPEGFileName", JPEGFileName); + + if(!config_write_file(&cfg, "BPQAPRS.cfg")) + printf("Error while writing config file.\n"); + else + printf("Config Saved\n"); + + config_destroy(&cfg); + + printf("%s\n", ToCalls); +} + +// Linux Signal Handlers + +BOOL Running = TRUE; + +static void sigterm_handler(int sig) +{ + printf("sigterm\n"); + Running = FALSE; +} + +static void sigint_handler(int sig) +{ + SaveConfig(); + Running = FALSE; + exit(0); +} + +int main(int argc, char *argv[]) +{ + UCHAR * pbImage = NULL; + char FN[256]; + int fd; + int x, y; + time_t TimeLoaded = time(NULL); + struct stat STAT; + char * Env; + char BPQDirectory[256]; + char SharedName[256]; + char * ptr1; + + int SharedSize; + + double vals[10]; + int screen_number, depth, bitmap_pad, status; + unsigned long white; + unsigned long black; + Visual * visual; + unsigned int i, j; + Pixmap pixmap, popuppixmap; + + int x11_fd; + fd_set in_fds; + + struct timeval tv; + XEvent event; + XConfigureEvent xce; + XKeyEvent xkeyev; + Time lastupevent; + + char text[256]; + long unsigned int key; + + int LastX, LastY; // Saved mouse position when button down + int MovedX, MovedY; + + double sx, sy; + UCHAR * APRSStationMemory; + Atom wmDeleteMessage; + PangoFontDescription *font_desc; + GtkTreeIter iter; + +#ifndef WIN32 + signal(SIGHUP, SIG_IGN); + signal(SIGINT, sigint_handler); + signal(SIGTERM, sigterm_handler); + signal(SIGPIPE, SIG_IGN); +#endif + + printf("G8BPQ APRS Client for Linux Version 1.1.14.7 \n"); + printf("Copyright(c) 2004-2021 John Wiseman G8BPQ\n"); + printf("APRS is a registered trademark of Bob Bruninga.\n"); + printf("This software is based in part on the work of the Independent JPEG Group.\n"); + printf("Mapping from OpenStreetMap (http://openstreetmap.org)\n"); + printf("Imagery (c) OpenMapTiles (https://openmaptiles.org)\n\n"); + + if (argc > 1 && argv[1] && stricmp(argv[1], "-v") == 0) + return 0; + + config_init(&cfg); + + if (argc > 1 && argv[1] && stricmp(argv[1], "multiple") == 0) + { + multiple = 1; + printf("Running in multiple instance mode\n\n"); + } + + /* Read the file. If there is an error, report it and exit. */ + + if(!config_read_file(&cfg, "BPQAPRS.cfg")) + { + fprintf(stderr, "%d - %s\n", + config_error_line(&cfg), config_error_text(&cfg)); + config_destroy(&cfg); + } + else + { + group = config_lookup (&cfg, "APRS"); + + if (group) + { + Zoom = GetIntValue(group, "Zoom", 2); + TileX = GetIntValue(group, "SetBaseX", 0); + TileY = GetIntValue(group, "SetBaseY", 0); + ScrollX = GetIntValue(group, "ScrollX", 0); + ScrollY = GetIntValue(group, "ScrollY", 0); + WindowX = GetIntValue(group, "WindowX", 100); + WindowY = GetIntValue(group, "WindowY", 100); + WindowWidth = GetIntValue(group, "WindowWidth", 788); + WindowHeight = GetIntValue(group, "WindowHeight", 788); + HEIGHTTILES = GetIntValue(group, "HEIGHTTILES", 4); + WIDTHTILES = GetIntValue(group, "WIDTHTILES", 4); + + msgWinWidth = GetIntValue(group, "msgWinWidth", 300); + msgWinHeight = GetIntValue(group, "msgWinHeight", 300); + msgWinX = GetIntValue(group, "msgWinX", 100); + msgWinY = GetIntValue(group, "msgWinY", 100); + Split = GetIntValue(group, "Split", 100); + + OnlyMine = GetIntValue(group, "OnlyMine", 0); + OnlySeq = GetIntValue(group, "OnlySeq", 1); + ShowBulls = GetIntValue(group, "ShowBulls", 0); + + LocalTime = GetIntValue(group, "LocalTime", 0); + KM = GetIntValue(group, "KM", 0); + + AddViewToFilter = GetIntValue(group, "AddViewToFilter", 0); + + CreateJPEG = GetIntValue(group, "CreateJPEG", 1); + JPEGInterval = GetIntValue(group, "JPEGInterval", 300); + GetStringValue(group, "JPEGFileName", JPEGFileName); + GetStringValue(group, "ISFilter", ISFilter); + } + } + + if (Zoom == 0) + Zoom = 2; + + HEIGHT = HEIGHTTILES * 256; + WIDTH = WIDTHTILES * 256; + + Env = getenv("DISPLAY"); + + if (Env == NULL) + { + printf("DISPLAY is not set - can't run without X\n", Env); + return 0; + } + + printf("DISPLAY is set to %s\n", Env); + + if (strstr(Env, "localhost:1")) + printf("!!! WARNING !!! X session seems to be tunneled over an SSH session\nThis program will run much faster if you set DISPLAY to the Host running your X Server\n"); + + // Get shared memory + + // Append last bit of current directory to shared name + + getcwd(BPQDirectory, 256); + ptr1 = BPQDirectory; + + while (strchr(ptr1, '/')) + { + ptr1 = strchr(ptr1, '/'); + ptr1++; + } + + if (multiple) + sprintf(SharedName, "/BPQAPRSSharedMem%s", ptr1); + else + strcpy(SharedName, "/BPQAPRSSharedMem"); + + printf("Using Shared Memory %s\n", SharedName); + + + fd = shm_open(SharedName, O_RDWR, S_IRUSR | S_IWUSR); + if (fd == -1) + { + printf("Open APRS Shared Memory %s Failed\n", SharedName); + return 0; + } + else + { + // Map shared memory object + + Shared = mmap((void *)APRSSHAREDMEMORYBASE, 8192 * 4096, + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0); + + if (Shared == MAP_FAILED) + { + printf("Map APRS Shared Memory Failed\n"); + APRSStationMemory = NULL; + return 0; + } + } + + if (Shared != (void *)APRSSHAREDMEMORYBASE) + { + printf("Map APRS Shared Memory Allocated at wrong address %x Should be 0x43000000\n", Shared); + return 0; + } + + printf("Map APRS Shared Memory Allocated at %x\n", Shared); + + + SMEM = (struct SharedMem *)Shared; + SharedSize = SMEM->SharedMemLen; + + printf("Shared Memory Size %d Max %d\n", SharedSize, 8192 * 4096); + + if (SharedSize > 8192 * 4096) + { + printf("MAXSTATIONS too high\n"); + return 0; + } + + StnRecordBase = Shared + 32; + StationRecords = (struct STATIONRECORD**)StnRecordBase; + + ControlRecord = (struct STATIONRECORD*)StnRecordBase; + + memset(APRSCall, 0x20, 9); + memcpy(APRSCall, ControlRecord->Callsign, strlen(ControlRecord->Callsign)); + + printf("LinBPQ Configured with MaxStations %d APRSCall %s\n", + ControlRecord->LastPort, APRSCall); + + memcpy(BaseCall, APRSCall, 10); // Get call less SSID + strlop(BaseCall, ' '); + strlop(BaseCall, '-'); + + // Remap with Server's view of MaxStations + +// munmap(APRSStationMemory, 4096 * 4096); + +// perror("munmap"); + +// Shared = mmap((void *)APRSSHAREDMEMORYBASE, SharedSize, +// PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + +// printf("Map APRS Shared Memory Allocated at %x\n", Shared); + + if (Shared == MAP_FAILED) + { + printf("Extend APRS Shared Memory Failed\n"); + APRSStationMemory = NULL; + return 0; + } + + SMEM->NeedRefresh = 1; // Initial Load of messages + + maxfd = sfd = socket(AF_UNIX, SOCK_DGRAM, 0); + + if (sfd == -1) + { + perror("Socket"); + } + else + { + memset(&my_addr, 0, sizeof(struct sockaddr_un)); + my_addr.sun_family = AF_UNIX; + strncpy(my_addr.sun_path, RX_SOCK_PATH, sizeof(my_addr.sun_path) - 1); + + memset(&peer_addr, 0, sizeof(struct sockaddr_un)); + peer_addr.sun_family = AF_UNIX; + strncpy(peer_addr.sun_path, TX_SOCK_PATH, sizeof(peer_addr.sun_path) - 1); + + unlink(RX_SOCK_PATH); + + if (bind(sfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr_un)) == -1) + perror("bind"); + } + + XInitThreads(); + + ResolveThread(); + _beginthread(OSMThread, 0, NULL); + + + display = XOpenDisplay(NULL); + + if (! display) + { + printf("Couldn't open X display\n"); + return 1; + } + + screen_number = DefaultScreen (display); + depth = DefaultDepth (display, screen_number); + visual = DefaultVisual (display, screen_number); + gc = DefaultGC (display, screen_number); + bitmap_pad = BitmapPad (display); + white = WhitePixel (display, screen_number); + black = BlackPixel (display, screen_number); + root = DefaultRootWindow (display); + + if (depth == 16) + Bytesperpixel = 2; + + Image = malloc(WIDTH * Bytesperpixel * HEIGHT + 100); // Seems past last byte gets corrupt + + if (mkdir(OSMDir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) + { + if (errno != 17) // File exists + { + printf("Error Creating %s\n", OSMDir); + perror("mkdir"); + } + } + + for (i = 0; i < 20; i++) + { + sprintf(FN, "%s/%02d", OSMDir, i); + if (mkdir(FN, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) + { + if (errno != 17) // File exists + { + printf("Error Creating %s\n", FN); + perror("mkdir"); + } + } + } + + // Read Icons + + iconImage = ReadIcons("BPQAPRS/Symbols.jpg", &x, &y); + + if (x == 0) + printf("Couldn't load Icons\n"); + +// win = XCreateSimpleWindow (display, root, 50, 50, 800, 800, 0, black, white); + win = XCreateWindow (display, root, 50, 50, 788, 788, 0, depth, CopyFromParent, CopyFromParent, 0, 0); + XStoreName(display, win, "BPQAPRS Map"); + + wmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", False); + XSetWMProtocols(display, win, &wmDeleteMessage, 1); + +// XSelectInput(display, win, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask); + + XSelectInput(display, win, ExposureMask | KeyPressMask | PointerMotionMask | + ButtonPressMask | ButtonReleaseMask | StructureNotifyMask); + + XSetWindowBackground(display, win, 0xffffff); + XClearWindow(display, win); + XMapWindow (display, win); + + XMoveResizeWindow(display, win, WindowX, WindowY, WindowWidth, WindowHeight); + + image = XCreateImage (display, visual, depth, ZPixmap, 0, NULL, WIDTH, HEIGHT, bitmap_pad, 0); + + printf("depth : %d\nbitmap_pad : %d\nimage bpp : %d\n", depth, bitmap_pad, image->bits_per_pixel); + + image->data = Image; + + pixmap = XCreatePixmap (display, root, WIDTH, HEIGHT, depth); + + XSetLineAttributes(display, gc, 2, LineSolid, CapNotLast, JoinMiter); + + SetBaseX = -1; // force reload + SetBaseY = -1; + + LoadImageSet(Zoom, TileX, TileY); // Loads 1024 * 1024 Block + + x11_fd = ConnectionNumber(display); + + if (x11_fd > maxfd) + maxfd = x11_fd; + + gtk_init (&argc, &argv); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_default_size(GTK_WINDOW (window), msgWinWidth, msgWinHeight); + gtk_widget_set_uposition(GTK_WIDGET(window),msgWinX, msgWinY); + + gtk_signal_connect (GTK_OBJECT (window), "destroy", + GTK_SIGNAL_FUNC (gtk_main_quit), NULL); + gtk_container_set_border_width (GTK_CONTAINER (window), 10); + + gtk_window_set_resizable(GTK_WINDOW (window), TRUE); +// g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (close_application), NULL); + gtk_window_set_title (GTK_WINDOW (window), "BPQAPRS Messaging"); + gtk_container_set_border_width(GTK_CONTAINER (window), 0); + + // Load bpqicon if present + + if (stat("bpqicon.png", &STAT) == 0) + gtk_window_set_icon(GTK_WINDOW(window), create_pixbuf("bpqicon.png")); + + //gtk_window_get_frame_dimensions(GTK_WINDOW(window),&left,&top,&right,&bottom); + + + gtk_signal_connect(GTK_OBJECT(window), "delete_event", GTK_SIGNAL_FUNC(delete_event), NULL); + +// g_signal_connect(G_OBJECT(window), "configure-event", G_CALLBACK(frame_callback), NULL); + + // Create a box for the menu + + box1 = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (window), box1); + + checklabel = gtk_label_new(" "); + + check1 = gtk_check_button_new_with_label ("Show only My Msgs "); + check4 = gtk_check_button_new_with_label ("All SSIDs "); + check2 = gtk_check_button_new_with_label ("Show only Sequenced Msgs "); + check3 = gtk_check_button_new_with_label ("Show Bulls "); + + button = gtk_button_new_with_label("Clear RX"); + button2 = gtk_button_new_with_label("Clear TX"); + + gtk_toggle_button_set_active((GtkToggleButton *)check1, OnlyMine); + gtk_toggle_button_set_active((GtkToggleButton *)check2, OnlySeq); + gtk_toggle_button_set_active((GtkToggleButton *)check3, ShowBulls); + gtk_toggle_button_set_active((GtkToggleButton *)check4, AllSSID); + + g_signal_connect(G_OBJECT(check1), "clicked", G_CALLBACK(check_callback), "1"); + g_signal_connect(G_OBJECT(check2), "clicked", G_CALLBACK(check_callback), "2"); + g_signal_connect(G_OBJECT(check3), "clicked", G_CALLBACK(check_callback), "3"); + g_signal_connect(G_OBJECT(check4), "clicked", G_CALLBACK(check_callback), "4"); + g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(button_callback), "1"); + g_signal_connect(G_OBJECT(button2), "clicked", G_CALLBACK(button2_callback), "1"); + + // hBox for Check boxes + + checkhbox = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (checkhbox), checklabel, FALSE, FALSE, 0); + gtk_container_add (GTK_CONTAINER (checkhbox), check1); + gtk_container_add (GTK_CONTAINER (checkhbox), check4); + gtk_container_add (GTK_CONTAINER (checkhbox), check2); + gtk_container_add (GTK_CONTAINER (checkhbox), check3); + gtk_container_add (GTK_CONTAINER (checkhbox), button); + gtk_container_add (GTK_CONTAINER (checkhbox), button2); + checklabel = gtk_label_new(" "); + gtk_container_add (GTK_CONTAINER (checkhbox), checklabel); + + gtk_box_pack_start (GTK_BOX (box1), checkhbox, FALSE, FALSE, 0); + + box10 = gtk_vbox_new (FALSE, 0); + +// menubar = get_menubar_menu (window); + +// gtk_box_pack_start (GTK_BOX (box1), menubar, FALSE, TRUE, 1); + gtk_container_add (GTK_CONTAINER (box1), box10); + gtk_widget_show (window); + + vpaned = gtk_vpaned_new (); + gtk_container_add (GTK_CONTAINER (box10), vpaned); + gtk_paned_set_position(GTK_PANED(vpaned), Split); + gtk_widget_show (vpaned); + + /* Now create the contents of the two halves of the window */ + + frame1 = create_received_window(); + gtk_paned_add1 (GTK_PANED (vpaned), frame1); + gtk_widget_show (frame1); + + frame2 = create_sent_window(); + gtk_paned_add2 (GTK_PANED (vpaned), frame2); + gtk_widget_show (frame2); + +// separator = gtk_hseparator_new (); +// gtk_box_pack_start(GTK_BOX (box1), separator, FALSE, TRUE, 0); + + box2 = gtk_hbox_new(FALSE, 10); + gtk_container_set_border_width(GTK_CONTAINER (box2), 1); + gtk_box_pack_start(GTK_BOX (box10), box2, FALSE, FALSE, 0); + + // set up the text entry line + + label1 = gtk_label_new(" To"); + label2 = gtk_label_new("Message"); + combo = gtk_combo_box_text_new_with_entry(); + gtk_widget_set_size_request(combo, 100, 10); + + entry = gtk_entry_new(); +// gtk_widget_set_size_request(entry, 100, 20); //gtk_entry_new_with_buffer(text); + gtk_entry_set_max_length(GTK_ENTRY(entry), 100); + gtk_entry_set_activates_default(GTK_ENTRY (entry), TRUE); + g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK(enter_callback), (gpointer)entry); + gtk_box_pack_start(GTK_BOX(box2), label1, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(box2), combo, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(box2), label2, FALSE, FALSE, 0); + gtk_container_add(GTK_CONTAINER(box2), entry); + gtk_widget_grab_focus(entry); + + + + + font_desc=pango_font_description_from_string(MyFont); + gtk_widget_modify_font (entry, font_desc); + gtk_widget_modify_font (combo, font_desc); + gtk_widget_modify_font (view, font_desc); + gtk_widget_modify_font (view2, font_desc); + + gtk_widget_show_all (window); + gtk_widget_show (window); + + // Main loop + + _beginthread(GTKThread, 0, NULL); + _beginthread(SecTimer, 0, NULL); + + AutoFilterTimer = AUTOFILTERDELAY; // Update filter if no change for 30 secs + + while(Running) + { + unsigned char Msg[256]; + int numBytes; + struct STATIONRECORD * Station; + + FD_ZERO(&in_fds); + FD_SET(x11_fd, &in_fds); + FD_SET(sfd, &in_fds); + + tv.tv_usec = 0; + tv.tv_sec = 1; + + // Wait for X Event, Message from LinBPQ or a Timer + + if (select(maxfd+1, &in_fds, 0, 0, &tv)) + { + if (FD_ISSET(sfd, &in_fds)) + { + numBytes = recvfrom(sfd, Msg, 256, 0, NULL, NULL); + } + + // may be X event, but pick up later + } + else + { + // Handle timer here + + if (SMEM->NeedRefresh) + { + SMEM->NeedRefresh = FALSE; + + // Use Checkbox event to rewrite display + + check_callback((GtkButton *)check1, "1"); + RefreshTXList(); + } + + // NeedRedraw += RefreshStationMap(FALSE); // Draw new or moved stations + + // Do a full redraw at least evey 2 mins if anything has changed + +// SlowTimer++; +// if (SlowTimer > 40) // 2 Mins +// if (NeedRedraw) +// NeedRefresh = TRUE; + } + + // Handle XEvents and flush the input + + while(Running && XPending(display)) + { + XNextEvent(display, &event); + + MouseX = event.xbutton.x - leftBorder; + MouseY = event.xbutton.y - topBorder; + + GetMouseLatLon(&MouseLat, &MouseLon); + + switch (event.type) + { + + case ClientMessage: + + if (event.xclient.data.l[0] == wmDeleteMessage) + Running = FALSE; + break; + + case KeyPress: + + xkeyev = event.xkey; + + XLookupString(&event.xkey, text, 255, &key, 0); + +// printf("Key %c Hex %x Code %d %x\n", text[0], text[0], key, key); + + switch(key) + { + case XK_Left: + + WindowX -= 8; + XMoveWindow(display, win, WindowX, WindowY); + break; + + case XK_Up: + + WindowY -= 8; + XMoveWindow(display, win, WindowX, WindowY); + break; + + case XK_Right: + + WindowX += 8; + XMoveWindow(display, win, WindowX, WindowY); + break; + + case XK_Down: + + WindowY += 8; + XMoveWindow(display, win, WindowX, WindowY); + break; + + case '-': + + ZoomOut(); + break; + + case '=': + case '+': + + ZoomIn(); + break; + } + + break; + + case Expose: + + XPutImage (display, win, gc, image, ScrollX, ScrollY, leftBorder, topBorder, cxImgSize, cyImgSize); + break; + + case ConfigureNotify: + + xce = event.xconfigure; + + // This event type is generated for a variety of + // happenings, so check whether the window has been + // resized. + + WindowX = xce.x; + WindowY = xce.y; + WindowWidth = xce.width; + WindowHeight = xce.height; + + if (xce.width != cxWinSize || xce.height != cyWinSize) + { + cxWinSize = xce.width; + cyWinSize = xce.height; + cxImgSize = cxWinSize - (leftBorder + rightBorder); + cyImgSize = cyWinSize - (topBorder + bottomBorder); + + XClearWindow(display, win); + XPutImage (display, win, gc, image, ScrollX, ScrollY, leftBorder, topBorder, cxImgSize, cyImgSize); + } + + break; + + case MotionNotify: + + FindStationsByPixel(MouseX + ScrollX, MouseY + ScrollY); + break; + + + case ButtonPress: + + switch (event.xbutton.button) + { + case 1: // Left Button + + LastX = MouseX; + LastY = MouseY; + break; + + case 4: // Scrollup + + ZoomIn(); + break; + + case 5: // Scrolldown + + ZoomOut(); + break; + } + + break; + + case ButtonRelease: + + if (popupActive && (event.xbutton.time - lastupevent) < 300) + { + // Double Click on Station + + char Key[32]; + char LoppedCall[10]; + int n = 8; + + memcpy(LoppedCall, popupStn->Callsign, 9); + + while (n && LoppedCall[n] == ' ') + { + n--; + } + + if (n) + LoppedCall[n + 1] = 0; + + sprintf(Key, "|%s|", LoppedCall); + + // if new call add to combo box + + if (strstr(ToCalls, Key) == 0) + { + if (strlen(ToCalls) < 1000) + strcat(ToCalls, Key); + + gtk_combo_box_text_prepend_text ((GtkComboBoxText *)combo, LoppedCall); + } + } + + lastupevent = event.xbutton.time; + + switch (event.xbutton.button) + { + case 1: // Left Button + + // if a Popup is on display, then select station, else scroll map + + if (selActive) + { + GetStationFromList(MouseX, MouseY); + break; + } + + MovedX = MouseX - LastX; + MovedY = MouseY - LastY; + + if (MovedX == 0 && MovedY == 0) + break; + + ScrollX -= (MovedX); + ScrollY -= (MovedY); + + while (ScrollX < 0) + { + TileX--; + ScrollX += 256; + } + + while (ScrollX > 255) + { + TileX++; + ScrollX -= 256; + } + + if (TileX < 0) + { + TileX = 0; + ScrollX = 0; + } + + while (ScrollY < 0) + { + TileY--; + ScrollY += 256; + } + + while (ScrollY > 255) + { + TileY++; + ScrollY -= 256; + } + + if (TileY < 0) + { + TileY = 0; + ScrollY = 0; + } + + NeedRefresh = TRUE; + AutoFilterTimer = AUTOFILTERDELAY; // Update filter if no change for 30 secs + + break; + } + break; + } + } // end of while xpending + + if (popupActive || selActive) + { + } + else + { + if (NeedRefresh) + { + SetBaseX = -1; + LoadImageSet(Zoom, TileX, TileY); + NeedRefresh = FALSE; + SlowTimer = 0; + } + if (NeedRedraw) + { + NeedRedraw = 0; + XPutImage (display, win, gc, image, ScrollX, ScrollY, leftBorder, topBorder, cxImgSize, cyImgSize); + } + } + } + + SaveConfig(); + + status = XDestroyImage (image); + return 0; +} + +void PutAPRSMessage(char * Frame, int Len) +{ + if (sendto(sfd, Frame, Len, 0, (struct sockaddr *) &peer_addr, sizeof(struct sockaddr_un)) != Len) + perror("sendto"); +} + +VOID SendFilterCommand(char * Filter) +{ + char Msg[2000]; + int n; + + strcpy(Msg, "SERVER"); + strcpy(&Msg[10], Filter); + + PutAPRSMessage(Msg, strlen(&Msg[10]) + 11); + + strcpy(&Msg[10], "filter?"); + PutAPRSMessage(Msg, strlen(&Msg[10]) + 11); +} + +void RefreshTXList() +{ + struct APRSMESSAGE * Message = SMEM->OutstandingMsgs; + int n = 0; + + GtkTreeIter iter; + gchar *seq; + char status[10]; + + // Clear old list + + if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(sentitems), &iter, NULL, 0)) + { + while (gtk_list_store_remove(sentitems, &iter)) + {} + } + while (Message) + { + if (Message->Acked) + strcpy(status, "A"); + else if (Message->Cancelled) + strcpy(status, "C"); + else if (Message->Retries == 0) + strcpy(status, "F"); + else + sprintf(status, "%d", Message->Retries); + + gtk_list_store_insert_with_values( + sentitems, &iter, -1, + 0, Message->ToCall, + 1, Message->Seq, + 2, status, + 3, Message->Time, + 4, Message->Text, -1); + n++; + + Message = Message->Next; + } + + if (n) + gtk_tree_view_scroll_to_cell ((GtkTreeView *)view, + gtk_tree_model_get_path (GTK_TREE_MODEL(sentitems), &iter), NULL, FALSE, 0, 0); +} + +VOID SendAPRSMessage(const char * Text, char * ToCall) +{ + char Msg[255]; + + strcpy(Msg, ToCall); + strcpy(&Msg[10], Text); + + PutAPRSMessage(Msg, strlen(&Msg[10]) + 11); + + return; +} + +VOID SecTimer() +{ + while (TRUE) + { + struct STATIONRECORD * sptr = *StationRecords; + int n = 0; + char Msg[20]; + + if (Host1Down) + Host1Down --; + + if (Host2Down) + Host2Down --; + + + // See if changed flag set on any stations + + NeedRedraw += RefreshStationMap(FALSE); // Draw new or moved stations + + // Do a full redraw at least evey 2 mins if anything has changed + + SlowTimer++; + if (SlowTimer > 120) // 2 Mins + if (NeedRedraw) + NeedRefresh = TRUE; + +/* + while (sptr) + { + if (sptr->Moved) + DrawStation(sptr, FALSE); + + sptr = sptr->Next; + } +*/ + JPEGCounter++; + + if (CreateJPEG) + { + if (JPEGCounter > JPEGInterval) + { + if (RGBToJpegFile(JPEGFileName, Image, cxImgSize, cyImgSize, TRUE, 50)) + JPEGCounter = 0; + } + } + +/* + if (SendWX) + SendWeatherBeacon(); + + // If any changes to image redraw it + + if (ImageChanged) + { + // We have drawn a new Icon. As we only redraw if it has moved, + // we need to reload image every now and again to get rid of ghost images + + time_t NOW = time(NULL); + + if ((NOW - LastRefresh) > 10) + { + LastRefresh = NOW; + ReloadMaps = TRUE; + } + + ImageChanged = FALSE; + InvalidateRect(hMapWnd, NULL, FALSE); + } + + wsprintf(Msg, "%d", StationCount); + SendMessage(hStatus, SB_SETTEXT, (WPARAM)(INT) 0 | 1, (LPARAM)Msg); + + wsprintf(Msg, "%d", OSMQueueCount); + SendMessage(hStatus, SB_SETTEXT, (WPARAM)(INT) 0 | 2, (LPARAM)Msg); + + wsprintf(Msg, "%d", Zoom); + SendMessage(hStatus, SB_SETTEXT, (WPARAM)(INT) 0 | 3, (LPARAM)Msg); +*/ + + if (AutoFilterTimer) + { + AutoFilterTimer--; + + if (AutoFilterTimer == 0 && AddViewToFilter) + { + // send filter to IS + + double TLLat, TLLon, BRLat, BRLon; + char Filter[256]; + + GetCornerLatLon(&TLLat, &TLLon, &BRLat, &BRLon); + sprintf(Filter, "%s a/%.3f/%.3f/%.3f/%.3f", ISFilter, TLLat, TLLon, BRLat, BRLon); + + SendFilterCommand(Filter); + } + } + + Sleep(1000); + } +} + +BOOL RGBToJpegFile(char * fileName, BYTE *dataBuf, UINT widthPix, UINT height, BOOL color, int quality) +{ + struct jpeg_compress_struct cinfo; + struct my_error_mgr jerr; + FILE * outfile=NULL; + unsigned char * RGBBuff = malloc(widthPix * 3); // no idea why + unsigned char * ptr1, * ptr2; + int n; + unsigned int val, r, g, b; + char Message[32]; + int X, Y, j, Len; + struct tm * TM; + time_t NOW = time(NULL); + + if (dataBuf==NULL) + return FALSE; + if (widthPix==0) + return FALSE; + if (height==0) + return FALSE; + + // Write a Date/Time stamp to top left + + if (LocalTime) + TM = localtime(&NOW); + else + TM = gmtime(&NOW); + + Len = sprintf(Message, "%02d:%02d:%02d %02d %s %04d", + TM->tm_hour, TM->tm_min, TM->tm_sec, + TM->tm_mday, month[TM->tm_mon], TM->tm_year + 1900); + + X = ScrollX; + Y = ScrollY + 8; + + for (j = 0; j < Len; j++) + { + DrawCharacter(X, Y, j, Message[j]); + } + + + + /* More stuff */ + /* Step 1: allocate and initialize JPEG compression object */ + + cinfo.err = jpeg_std_error(&jerr.pub); + jerr.pub.error_exit = my_error_exit; + + /* Establish the setjmp return context for my_error_exit to use. */ + + if (setjmp(jerr.setjmp_buffer)) + { + /* If we get here, the JPEG code has signaled an error. + * We need to clean up the JPEG object, close the input file, and return. + */ + + jpeg_destroy_compress(&cinfo); + + if (outfile!=NULL) + fclose(outfile); + + return FALSE; + } + + /* Now we can initialize the JPEG compression object. */ + + jpeg_create_compress(&cinfo); + + /* Step 2: specify data destination (eg, a file) */ + /* Note: steps 2 and 3 can be done in either order. */ + + if ((outfile = fopen(fileName, "wb")) == NULL) + { +// char buf[250]; +// sprintf(buf, "JpegFile :\nCan't open %s\n", fileName); +// MessageBox(NULL, buf, "", 0); + return FALSE; + } + + jpeg_stdio_dest(&cinfo, outfile); + + /* Step 3: set parameters for compression */ + + /* First we supply a description of the input image. + * Four fields of the cinfo struct must be filled in: + */ + cinfo.image_width = widthPix; /* image widthPix and height, in pixels */ + cinfo.image_height = height; + + cinfo.input_components = 3; /* # of color components per pixel */ + cinfo.in_color_space = JCS_RGB; /* colorspace of input image */ + +/* Now use the library's routine to set default compression parameters. + * (You must set at least cinfo.in_color_space before calling this, + * since the defaults depend on the source color space.) + */ + + jpeg_set_defaults(&cinfo); + /* Now you can set any non-default parameters you wish to. + * Here we just illustrate the use of quality (quantization table) scaling: + */ + jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */); + + /* Step 4: Start compressor */ + + /* TRUE ensures that we will write a complete interchange-JPEG file. + * Pass TRUE unless you are very sure of what you're doing. + */ + jpeg_start_compress(&cinfo, TRUE); + + /* Step 5: while (scan lines remain to be written) */ + /* jpeg_write_scanlines(...); */ + + /* Here we use the library's state variable cinfo.next_scanline as the + * loop counter, so that we don't have to keep track ourselves. + * To keep things simple, we pass one scanline per call; you can pass + * more if you wish, though. + */ + + while (cinfo.next_scanline < cinfo.image_height) + { + /* jpeg_write_scanlines expects an array of pointers to scanlines. + * Here the array is only one element long, but you could pass + * more than one scanline at a time if that's more convenient. + */ + + unsigned char * outRow; + + outRow = RGBBuff; + + // We have to convert from 2 or 4 bytes to pixel to 3 byte rgb + + ptr1 = dataBuf + ((ScrollY * WIDTH) + ScrollX + (cinfo.next_scanline * WIDTH)) * Bytesperpixel; + + ptr2 = RGBBuff; + + for (n = 0; n < widthPix; n++) + { + if (Bytesperpixel == 2) + { + val = (*(ptr1++)); + val |= (*(ptr1++)) << 8; + + b = val << 3; + g = (val >> 5) << 2; + r = (val >> 11) << 3; + + *(ptr2++) = r; + *(ptr2++) = g; + *(ptr2++) = b; + } + else + { + *(ptr2++) = *(ptr1+2); + *(ptr2++) = *(ptr1+1); + *(ptr2++) = *(ptr1); + ptr1 += 4; + } + } + (void) jpeg_write_scanlines(&cinfo, &outRow, 1); + } + + /* Step 6: Finish compression */ + + jpeg_finish_compress(&cinfo); + + /* After finish_compress, we can close the output file. */ + fclose(outfile); + + /* Step 7: release JPEG compression object */ + + /* This is an important step since it will release a good deal of memory. */ + jpeg_destroy_compress(&cinfo); + + /* And we're done! */ + + return TRUE; +} + +VOID plot(int X, int Y, COLORREF rgb) +{ + char * nptr; + int i, j; + unsigned int val; + + if ((X > (WIDTH - 3)) || (Y > (HEIGHT - 3))) + return; + + nptr = &Image[(Y * WIDTH * Bytesperpixel) + (X * Bytesperpixel)]; + + for (j = 0; j < 2; j++) + { + for (i = 0; i < 2; i++) + { + if (Bytesperpixel == 4) + { + *(nptr++) = (rgb >> 16) & 0xff; + *(nptr++) = (rgb >> 8) & 0xff; + *(nptr++) = GetRValue(rgb); + nptr++; + } + else + { + val = ((rgb & 0xff) >> 3) << 11; // Red + val |= (GetGValue(rgb) >> 2) << 5; + val |= (rgb >> 19); // Blue + *(nptr++) = (val & 0xff); + *(nptr++) = (unsigned char)(val >> 8); + } + + } + nptr += (WIDTH - 2) * Bytesperpixel; + } +} + +// Algorithm assumes y increases slower than x. If not, swap x and y in plotline and plotpoint + +void plotLineTB(int x0, int y0, int x1, int y1, COLORREF rgb); +void plotLineLR(int x0, int y0, int x1, int y1, COLORREF rgb); + +void plotLine(int x0, int y0, int x1, int y1, COLORREF rgb) +{ + if (abs(x1 - x0) > abs(y1 - y0)) + plotLineLR(x0, y0, x1, y1, rgb); + else + plotLineTB(y0, x0, y1, x1, rgb); +} + +void plotLineLR(int x0, int y0, int x1, int y1, COLORREF rgb) +{ + int dx; + int dy; + int D, x, y; + + // Must draw with increacing x and y, but can draw either way round, so if x is decreasing, + // just swap ends, so we always draw left to right + + if (x0 > x1) + { + x = x0; + x0 = x1; + x1 = x; + + y = y0; + y0 = y1; + y1 = y; + } + + // if y is now decreasing, we must reverse algorithm + + if (y1 > y0) + { + dx = x1 - x0; + dy = y1 - y0; + D = 2 * dy - dx; + + plot (x0, y0, 0); + + y = y0; + + for (x = x0+1; x < x1; x++) + { + if (D < 0) + { + D = D + (2*dy); + } + else + { + y = y+1; + D = D + (2*dy-2*dx); + + } + plot(x, y, rgb); + } + } + else + { + dx = x1 - x0; + dy = y0 - y1; + D = 2 * dy - dx; + + plot (x0, y0, rgb); + + y = y0; + + for (x = x0+1; x <= x1; x++) + { + if (D > 0) + { + y = y-1; + plot(x, y, rgb); + D = D + (2*dy-2*dx); + } + else + { + plot(x, y, rgb); + D = D + (2*dy); + } + } + } + +} + +void plotLineTB(int x0, int y0, int x1, int y1, COLORREF rgb) +{ + int dx; + int dy; + int D, x, y; + + // Must draw with increacing x and y, but can draw either way round, so if x is decreasing, + // just swap ends, so we always draw left to right + + if (x0 > x1) + { + x = x0; + x0 = x1; + x1 = x; + + y = y0; + y0 = y1; + y1 = y; + } + + // if y is now decreasing, we must reverse algorithm + + if (y1 > y0) + { + dx = x1 - x0; + dy = y1 - y0; + D = 2 * dy - dx; + + plot (y0, x0, 0); + + y = y0; + + for (x = x0+1; x < x1; x++) + { + if (D < 0) + { + D = D + (2*dy); + } + else + { + y = y+1; + D = D + (2*dy-2*dx); + + } + plot(y, x, rgb); + } + } + else + { + dx = x1 - x0; + dy = y0 - y1; + D = 2 * dy - dx; + + plot (y0, x0, 0); + + y = y0; + + for (x = x0+1; x <= x1; x++) + { + if (D > 0) + { + y = y-1; + D = D + (2*dy-2*dx); + } + else + { + D = D + (2*dy); + } + plot(y, x, rgb); + + } + } +} + + +png_const_charp msg; + + +static png_structp png_ptr = NULL; +static png_infop info_ptr = NULL; + + +// cexcept interface + +static void +png_cexcept_error(png_structp png_ptr, png_const_charp msg) +{ + if(png_ptr) + ; +#ifndef PNG_NO_CONSOLE_IO + fprintf(stderr, "libpng error: %s\n", msg); +#endif + { +// Throw msg; + } +} + + +int LoadImageFile (void * hwnd, char * pstrPathName, + png_byte **ppbImage, int *pxImgSize, int *pyImgSize, + int *piChannels, png_color *pBkgColor) +{ + + // if there's an existing PNG, free the memory + + if (*ppbImage) + { + free (*ppbImage); + *ppbImage = NULL; + } + + PngLoadImage (pstrPathName, ppbImage, pxImgSize, pyImgSize, piChannels, + pBkgColor); + + + + if (*ppbImage != NULL) + { + // sprintf (szTmp, "VisualPng - %s", strrchr(pstrPathName, '\\') + 1); + // SetWindowText (hwnd, szTmp); + } + else + { + return FALSE; + } + + return TRUE; +} + +//---------------- +// PNG image handler functions + +BOOL PngLoadImage (char * pstrFileName, png_byte **ppbImageData, + png_uint_32 *piWidth, png_uint_32 *piHeight, int *piChannels, png_color *pBkgColor) +{ + static FILE *pfFile; + png_byte pbSig[8]; + int iBitDepth; + int iColorType; + double dGamma; + png_color_16 *pBackground; + png_uint_32 ulChannels; + png_uint_32 ulRowBytes; + png_byte *pbImageData = *ppbImageData; + static png_byte **ppbRowPointers = NULL; + int i; + + // open the PNG input file + + if (!pstrFileName) + { + *ppbImageData = pbImageData = NULL; + printf("Load PNG Failed 1\n"); + return FALSE; + } + + if (!(pfFile = fopen(pstrFileName, "rb"))) + { + *ppbImageData = pbImageData = NULL; + printf("Load PNG Failed 2\n"); + return FALSE; + } + + // first check the eight byte PNG signature + + fread(pbSig, 1, 8, pfFile); + + if(png_sig_cmp(pbSig, 0, 8) != 0) + //if (!png_check_sig(pbSig, 8)) + { + *ppbImageData = pbImageData = NULL; + + if (pfFile) + fclose (pfFile); + + printf("Bad file %s", pstrFileName); + unlink(pstrFileName); + + return FALSE; + } + + // create the two png(-info) structures + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, + (png_error_ptr)png_cexcept_error, (png_error_ptr)NULL); + if (!png_ptr) + { + *ppbImageData = pbImageData = NULL; + printf("Load PNG Failed 4\n"); + return FALSE; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_read_struct(&png_ptr, NULL, NULL); + *ppbImageData = pbImageData = NULL; + printf("Load PNG Failed 5\n"); + return FALSE; + } + +// Try + { + + // initialize the png structure + +#if !defined(PNG_NO_STDIO) + png_init_io(png_ptr, pfFile); +#else + png_set_read_fn(png_ptr, (png_voidp)pfFile, png_read_data); +#endif + + png_set_sig_bytes(png_ptr, 8); + + // read all PNG info up to image data + + png_read_info(png_ptr, info_ptr); + + // get width, height, bit-depth and color-type + + png_get_IHDR(png_ptr, info_ptr, piWidth, piHeight, &iBitDepth, + &iColorType, NULL, NULL, NULL); + + // expand images of all color-type and bit-depth to 3x8 bit RGB images + // let the library process things like alpha, transparency, background + + if (iBitDepth == 16) + png_set_strip_16(png_ptr); + if (iColorType == PNG_COLOR_TYPE_PALETTE) + png_set_expand(png_ptr); + if (iBitDepth < 8) + png_set_expand(png_ptr); + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + png_set_expand(png_ptr); + if (iColorType == PNG_COLOR_TYPE_GRAY || + iColorType == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png_ptr); + + // set the background color to draw transparent and alpha images over. + if (png_get_bKGD(png_ptr, info_ptr, &pBackground)) + { + png_set_background(png_ptr, pBackground, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0); + pBkgColor->red = (byte) pBackground->red; + pBkgColor->green = (byte) pBackground->green; + pBkgColor->blue = (byte) pBackground->blue; + } + else + { + pBkgColor = NULL; + } + + // if required set gamma conversion + if (png_get_gAMA(png_ptr, info_ptr, &dGamma)) + png_set_gamma(png_ptr, (double) 2.2, dGamma); + + // after the transformations have been registered update info_ptr data + + png_read_update_info(png_ptr, info_ptr); + + // get again width, height and the new bit-depth and color-type + + png_get_IHDR(png_ptr, info_ptr, piWidth, piHeight, &iBitDepth, + &iColorType, NULL, NULL, NULL); + + + // row_bytes is the width x number of channels + + ulRowBytes = png_get_rowbytes(png_ptr, info_ptr); + ulChannels = png_get_channels(png_ptr, info_ptr); + + *piChannels = ulChannels; + + // now we can allocate memory to store the image + + if (pbImageData) + { + free (pbImageData); + pbImageData = NULL; + } + if ((pbImageData = (png_byte *) malloc(ulRowBytes * (*piHeight) + * sizeof(png_byte))) == NULL) + { + png_error(png_ptr, "Visual PNG: out of memory"); + } + *ppbImageData = pbImageData; + + // and allocate memory for an array of row-pointers + + if ((ppbRowPointers = (png_bytepp) malloc((*piHeight) + * sizeof(png_bytep))) == NULL) + { + png_error(png_ptr, "Visual PNG: out of memory"); + } + + // set the individual row-pointers to point at the correct offsets + + for (i = 0; i < (*piHeight); i++) + ppbRowPointers[i] = pbImageData + i * ulRowBytes; + + // now we can go ahead and just read the whole image + + png_read_image(png_ptr, ppbRowPointers); + + // read the additional chunks in the PNG file (not really needed) + + png_read_end(png_ptr, NULL); + + // and we're done + + free (ppbRowPointers); + ppbRowPointers = NULL; + + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + + + // yepp, done + } +/* + Catch (msg) + { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + + *ppbImageData = pbImageData = NULL; + + if(ppbRowPointers) + free (ppbRowPointers); + + fclose(pfFile); + + return FALSE; + } +*/ + if (pfFile) + fclose (pfFile); + + return TRUE; +} + diff --git a/.svn/pristine/09/091a2424fc9a479a4b97e349b069851ef8cd3fc2.svn-base b/.svn/pristine/09/091a2424fc9a479a4b97e349b069851ef8cd3fc2.svn-base index 47a463d..4272c12 100644 --- a/.svn/pristine/09/091a2424fc9a479a4b97e349b069851ef8cd3fc2.svn-base +++ b/.svn/pristine/09/091a2424fc9a479a4b97e349b069851ef8cd3fc2.svn-base @@ -1,135 +1,135 @@ -/* LzmaLib.h -- LZMA library interface -2008-08-05 -Igor Pavlov -Public domain */ - -#ifndef __LZMALIB_H -#define __LZMALIB_H - -#include "types.h" - -#ifdef __cplusplus - #define MY_EXTERN_C extern "C" -#else - #define MY_EXTERN_C extern -#endif - -#define MY_STDAPI MY_EXTERN_C int MY_STD_CALL - -#define LZMA_PROPS_SIZE 5 - -/* -RAM requirements for LZMA: - for compression: (dictSize * 11.5 + 6 MB) + state_size - for decompression: dictSize + state_size - state_size = (4 + (1.5 << (lc + lp))) KB - by default (lc=3, lp=0), state_size = 16 KB. - -LZMA properties (5 bytes) format - Offset Size Description - 0 1 lc, lp and pb in encoded form. - 1 4 dictSize (little endian). -*/ - -/* -LzmaCompress ------------- - -outPropsSize - - In: the pointer to the size of outProps buffer; *outPropsSize = LZMA_PROPS_SIZE = 5. - Out: the pointer to the size of written properties in outProps buffer; *outPropsSize = LZMA_PROPS_SIZE = 5. - - LZMA Encoder will use defult values for any parameter, if it is - -1 for any from: level, loc, lp, pb, fb, numThreads - 0 for dictSize - -level - compression level: 0 <= level <= 9; - - level dictSize algo fb - 0: 16 KB 0 32 - 1: 64 KB 0 32 - 2: 256 KB 0 32 - 3: 1 MB 0 32 - 4: 4 MB 0 32 - 5: 16 MB 1 32 - 6: 32 MB 1 32 - 7+: 64 MB 1 64 - - The default value for "level" is 5. - - algo = 0 means fast method - algo = 1 means normal method - -dictSize - The dictionary size in bytes. The maximum value is - 128 MB = (1 << 27) bytes for 32-bit version - 1 GB = (1 << 30) bytes for 64-bit version - The default value is 16 MB = (1 << 24) bytes. - It's recommended to use the dictionary that is larger than 4 KB and - that can be calculated as (1 << N) or (3 << N) sizes. - -lc - The number of literal context bits (high bits of previous literal). - It can be in the range from 0 to 8. The default value is 3. - Sometimes lc=4 gives the gain for big files. - -lp - The number of literal pos bits (low bits of current position for literals). - It can be in the range from 0 to 4. The default value is 0. - The lp switch is intended for periodical data when the period is equal to 2^lp. - For example, for 32-bit (4 bytes) periodical data you can use lp=2. Often it's - better to set lc=0, if you change lp switch. - -pb - The number of pos bits (low bits of current position). - It can be in the range from 0 to 4. The default value is 2. - The pb switch is intended for periodical data when the period is equal 2^pb. - -fb - Word size (the number of fast bytes). - It can be in the range from 5 to 273. The default value is 32. - Usually, a big number gives a little bit better compression ratio and - slower compression process. - -numThreads - The number of thereads. 1 or 2. The default value is 2. - Fast mode (algo = 0) can use only 1 thread. - -Out: - destLen - processed output size -Returns: - SZ_OK - OK - SZ_ERROR_MEM - Memory allocation error - SZ_ERROR_PARAM - Incorrect paramater - SZ_ERROR_OUTPUT_EOF - output buffer overflow - SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) -*/ - -MY_STDAPI LzmaCompress(unsigned char *dest, size_t *destLen, const unsigned char *src, size_t srcLen, - unsigned char *outProps, size_t *outPropsSize, /* *outPropsSize must be = 5 */ - int level, /* 0 <= level <= 9, default = 5 */ - unsigned dictSize, /* default = (1 << 24) */ - int lc, /* 0 <= lc <= 8, default = 3 */ - int lp, /* 0 <= lp <= 4, default = 0 */ - int pb, /* 0 <= pb <= 4, default = 2 */ - int fb, /* 5 <= fb <= 273, default = 32 */ - int numThreads /* 1 or 2, default = 2 */ - ); - -/* -LzmaUncompress --------------- -In: - dest - output data - destLen - output data size - src - input data - srcLen - input data size -Out: - destLen - processed output size - srcLen - processed input size -Returns: - SZ_OK - OK - SZ_ERROR_DATA - Data error - SZ_ERROR_MEM - Memory allocation arror - SZ_ERROR_UNSUPPORTED - Unsupported properties - SZ_ERROR_INPUT_EOF - it needs more bytes in input buffer (src) -*/ - -MY_STDAPI LzmaUncompress(unsigned char *dest, size_t *destLen, const unsigned char *src, SizeT *srcLen, - const unsigned char *props, size_t propsSize); - -#endif +/* LzmaLib.h -- LZMA library interface +2008-08-05 +Igor Pavlov +Public domain */ + +#ifndef __LZMALIB_H +#define __LZMALIB_H + +#include "types.h" + +#ifdef __cplusplus + #define MY_EXTERN_C extern "C" +#else + #define MY_EXTERN_C extern +#endif + +#define MY_STDAPI MY_EXTERN_C int MY_STD_CALL + +#define LZMA_PROPS_SIZE 5 + +/* +RAM requirements for LZMA: + for compression: (dictSize * 11.5 + 6 MB) + state_size + for decompression: dictSize + state_size + state_size = (4 + (1.5 << (lc + lp))) KB + by default (lc=3, lp=0), state_size = 16 KB. + +LZMA properties (5 bytes) format + Offset Size Description + 0 1 lc, lp and pb in encoded form. + 1 4 dictSize (little endian). +*/ + +/* +LzmaCompress +------------ + +outPropsSize - + In: the pointer to the size of outProps buffer; *outPropsSize = LZMA_PROPS_SIZE = 5. + Out: the pointer to the size of written properties in outProps buffer; *outPropsSize = LZMA_PROPS_SIZE = 5. + + LZMA Encoder will use defult values for any parameter, if it is + -1 for any from: level, loc, lp, pb, fb, numThreads + 0 for dictSize + +level - compression level: 0 <= level <= 9; + + level dictSize algo fb + 0: 16 KB 0 32 + 1: 64 KB 0 32 + 2: 256 KB 0 32 + 3: 1 MB 0 32 + 4: 4 MB 0 32 + 5: 16 MB 1 32 + 6: 32 MB 1 32 + 7+: 64 MB 1 64 + + The default value for "level" is 5. + + algo = 0 means fast method + algo = 1 means normal method + +dictSize - The dictionary size in bytes. The maximum value is + 128 MB = (1 << 27) bytes for 32-bit version + 1 GB = (1 << 30) bytes for 64-bit version + The default value is 16 MB = (1 << 24) bytes. + It's recommended to use the dictionary that is larger than 4 KB and + that can be calculated as (1 << N) or (3 << N) sizes. + +lc - The number of literal context bits (high bits of previous literal). + It can be in the range from 0 to 8. The default value is 3. + Sometimes lc=4 gives the gain for big files. + +lp - The number of literal pos bits (low bits of current position for literals). + It can be in the range from 0 to 4. The default value is 0. + The lp switch is intended for periodical data when the period is equal to 2^lp. + For example, for 32-bit (4 bytes) periodical data you can use lp=2. Often it's + better to set lc=0, if you change lp switch. + +pb - The number of pos bits (low bits of current position). + It can be in the range from 0 to 4. The default value is 2. + The pb switch is intended for periodical data when the period is equal 2^pb. + +fb - Word size (the number of fast bytes). + It can be in the range from 5 to 273. The default value is 32. + Usually, a big number gives a little bit better compression ratio and + slower compression process. + +numThreads - The number of thereads. 1 or 2. The default value is 2. + Fast mode (algo = 0) can use only 1 thread. + +Out: + destLen - processed output size +Returns: + SZ_OK - OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_PARAM - Incorrect paramater + SZ_ERROR_OUTPUT_EOF - output buffer overflow + SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) +*/ + +MY_STDAPI LzmaCompress(unsigned char *dest, size_t *destLen, const unsigned char *src, size_t srcLen, + unsigned char *outProps, size_t *outPropsSize, /* *outPropsSize must be = 5 */ + int level, /* 0 <= level <= 9, default = 5 */ + unsigned dictSize, /* default = (1 << 24) */ + int lc, /* 0 <= lc <= 8, default = 3 */ + int lp, /* 0 <= lp <= 4, default = 0 */ + int pb, /* 0 <= pb <= 4, default = 2 */ + int fb, /* 5 <= fb <= 273, default = 32 */ + int numThreads /* 1 or 2, default = 2 */ + ); + +/* +LzmaUncompress +-------------- +In: + dest - output data + destLen - output data size + src - input data + srcLen - input data size +Out: + destLen - processed output size + srcLen - processed input size +Returns: + SZ_OK - OK + SZ_ERROR_DATA - Data error + SZ_ERROR_MEM - Memory allocation arror + SZ_ERROR_UNSUPPORTED - Unsupported properties + SZ_ERROR_INPUT_EOF - it needs more bytes in input buffer (src) +*/ + +MY_STDAPI LzmaUncompress(unsigned char *dest, size_t *destLen, const unsigned char *src, SizeT *srcLen, + const unsigned char *props, size_t propsSize); + +#endif diff --git a/.svn/pristine/0b/0b2525a7593ec9c79302aca324e8bd42b0ed4e56.svn-base b/.svn/pristine/0b/0b2525a7593ec9c79302aca324e8bd42b0ed4e56.svn-base index 18e0979..944010b 100644 --- a/.svn/pristine/0b/0b2525a7593ec9c79302aca324e8bd42b0ed4e56.svn-base +++ b/.svn/pristine/0b/0b2525a7593ec9c79302aca324e8bd42b0ed4e56.svn-base @@ -1,6751 +1,6751 @@ -/* -Copyright 2001-2022 John Wiseman G8BPQ - -This file is part of LinBPQ/BPQ32. - -LinBPQ/BPQ32 is free software: you can redistribute it and/or modifyextern int HTTP -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -LinBPQ/BPQ32 is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses -*/ -// -// 409l Oct 2001 Fix l3timeout for KISS -// -// 409m Oct 2001 Fix Crossband Digi -// -// 409n May 2002 Change error handling on load ext DLL - -// 409p March 2005 Allow Multidigit COM Ports (kiss.c) - -// 409r August 2005 Treat NULL string in Registry as use current directory -// Allow shutdown to close BPQ Applications - -// 409s October 2005 Add DLL:Export entries to API for BPQTNC2 - -// 409t January 2006 -// -// Add API for Perl "GetPerlMsg" -// Add API for BPQ1632 "GETBPQAPI" - returns address of Assembler API routine -// Add Registry Entry "BPQ Directory". If present, overrides "Config File Location" -// Add New API "GetBPQDirectory" - Returns location of config file -// Add New API "ChangeSessionCallsign" - equivalent to "*** linked to" command -// Rename BPQNODES to BPQNODES.dat -// New API "GetAttachedProcesses" - returns number of processes connected. -// Warn if user trys to close Console Window. -// Add Debug entries to record Process Attach/Detach -// Fix recovery following closure of first process - -// 409t Beta 2 February 2006 -// -// Add API Entry "GetPortNumber" -// -// 409u February 2006 -// -// Fix crash if allocate/deallocate called with stream=0 -// Add API to ch -// Display config file path -// Fix saving of Locked Node flag -// Added SAVENODES SYSOP command -// -// 409u 2 March 2006 -// -// Fix SetupBPQDirectory -// Add CopyBPQDirectory (for Basic Programs) -// -// 409u 3 March 2006 -// -// Release streams on DLL unload - -// 409v October 2006 -// -// Support Minimize to Tray for all BPQ progams -// Implement L4 application callsigns - -// 410 November 2006 -// -// Modified to compile with C++ 2005 Express Edition -// Make MCOM MTX MMASK local variables -// -// 410a January 2007 -// -// Add program name to Attach-Detach messages -// Attempt to detect processes which have died -// Fix bug in NETROM and IFrame decode which would cause crash if frame was corrupt -// Add BCALL - origin call for Beacons -// Fix KISS ACKMODE ACK processing -// - -// 410b November 2007 -// -// Allow CTEXT of up to 510, and enforce PACLEN, fragmenting if necessary - -// 410c December 2007 - -// Fix problem with NT introduced in V410a -// Display location of DLL on Console - -// 410d January 2008 - -// Fix crash in DLL Init caused by long path to program -// Invoke Appl2 alias on C command (if enabled) -// Allow C command to be disabled -// Remove debug trap in GETRAWFRAME -// Validate Alias of directly connected node, mainly for KPC3 DISABL Problem -// Move Port statup code out of DLLInit (mainly for perl) -// Changes to allow Load/Unload of bpq32.dll by appl -// CloseBPQ32 API added -// Ext Driver Close routes called -// Changes to release Mutex - -// 410e May 2008 - -// Fix missing SSID on last call of UNPROTO string (CONVTOAX25 in main.asm) -// Fix VCOM Driver (RX Len was 1 byte too long) -// Fix possible crash on L4CODE if L4DACK received out of sequence -// Add basic IP decoding - -// 410f October 2008 - -// Add IP Gateway -// Add Multiport DIGI capability -// Add GetPortDescription API -// Fix potential hangs if RNR lost -// Fix problem if External driver failes to load -// Put pushad/popad round _INITIALISEPORTS (main.asm) -// Add APIs GetApplCallVB and GetPortDescription (mainly for RMS) -// Ensure Route Qual is updated if Port Qual changed -// Add Reload Option, plus menu items for DUMP and SAVENODES - -// 410g December 2008 - -// Restore API Exports BPQHOSTAPIPTR and MONDECODEPTR (accidentally deleted) -// Fix changed init of BPQDirectory (accidentally changed) -// Fix Checks for lost processes (accidentally deleted) -// Support HDLC Cards on W2K and above -// Delete Tray List entries for crashed processes -// Add Option to NODES command to sort by Callsign -// Add options to save or clear BPQNODES before Reconfig. -// Fix Reconfig in Win98 -// Monitor buffering tweaks -// Fix Init for large (>64k) tables -// Fix Nodes count in Stats - -// 410h January 2009 - -// Add Start Minimized Option -// Changes to KISS for WIn98 Virtual COM -// Open \\.\com instead of //./COM -// Extra Dignostics - -// 410i Febuary 2009 - -// Revert KISS Changes -// Save Window positions - -// 410j June 2009 - -// Fix tidying of window List when program crashed -// Add Max Nodes to Stats -// Don't update APPLnALIAS with received NODES info -// Fix MH display in other timezones -// Fix Possible crash when processing NETROM type Zero frames (eg NRR) -// Basic INP3 Stuff -// Add extra diagnostics to Lost Process detection -// Process Netrom Record Route frames. - -// 410k June 2009 - -// Fix calculation of %retries in extended ROUTES display -// Fix corruption of ROUTES table - -// 410l October 2009 - -// Add GetVersionString API call. -// Add GetPortTableEntry API call -// Keep links to neighbouring nodes open - -// Build 2 - -// Fix PE in NOROUTETODEST (missing POP EBX) - -// 410m November 2009 - -// Changes for PACTOR and WINMOR to support the ATTACH command -// Enable INP3 if configured on a route. -// Fix count of nodes in Stats Display -// Overwrite the worst quality unused route if a call is received from a node not in your -// table when the table is full - -// Build 5 - -// Rig Control Interface -// Limit KAM VHF attach and RADIO commands to authorised programs (MailChat and BPQTerminal) - -// Build 6 - -// Fix reading INP3 Flag from BPQNODES - -// Build 7 - -// Add MAXHOPS and MAXRTT config options - -// Build 8 - -// Fix INP3 deletion of Application Nodes. -// Fix GETCALLSIGN for Pactor Sessions -// Add N Call* to display all SSID's of a call -// Fix flow control on Pactor sessions. - -// Build 9 - -// HDLC Support for XP -// Add AUTH routines - -// Build 10 - -// Fix handling commands split over more that one packet. - -// Build 11 - -// Attach cmd changes for winmor disconnecting state -// Option Interlock Winmor/Pactor ports - -// Build 12 - -// Add APPLS export for winmor -// Handle commands ending CR LF - -// Build 13 - -// Incorporate Rig Control in Kernel - -// Build 14 - -// Fix config reload for Rig COntrol - -// 410n March 2010 - -// Implement C P via PACTOR/WINMOR (for Airmail) - -// Build 2 - -// Don't flip SSID bits on Downlink Connect if uplink is Pactor/WINMOR -// Fix resetting IDLE Timer on Pactor/WINMOR sessions -// Send L4 KEEPLI messages based on IDLETIME - -// 410o July 2010 - -// Read bpqcfg.txt instead of .bin -// Support 32 bit MMASK (Allowing 32 Ports) -// Support 32 bit _APPLMASK (Allowing 32 Applications) -// Allow more commands -// Allow longer command aliases -// Fix logic error in RIGControl Port Initialisation (wasn't always raising RTS and DTR -// Clear RIGControl RTS and DTR on close - -// 410o Build 2 August 2010 - -// Fix couple of errors in config (needed APPLICATIONS and BBSCALL/ALIAS/QUAL) -// Fix Kenwood Rig Control when more than one message received at once. -// Save minimzed state of Rigcontrol Window - -// 410o Build 3 August 2010 - -// Fix reporting of set errors in scan to a random session - -// 410o Build 4 August 2010 - -// Change All xxx Ports are in use to no xxxx Ports are available if there are no sessions with _APPLMASK -// Fix validation of TRANSDELAY - -// 410o Build 5 August 2010 - -// Add Repeater Shift and Set Data Mode options to Rigcontrol (for ICOM only) -// Add WINMOR and SCS Pactor mode control option to RigControl -// Extend INFOMSG to 2000 bytes -// Improve Scan freq change lock (check both SCS and WINMOR Ports) - -// 410o Build 6 September 2010 - -// Incorporate IPGateway in main code. -// Fix GetSessionInfo for Pactor/Winmor Ports -// Add Antenna Selection to RigControl -// Allow Bandwidth options on RADIO command line (as well as in Scan definitions) - -// 410o Build 7 September 2010 - -// Move rigconrtol display to driver windows -// Move rigcontrol config to driver config. -// Allow driver and IPGateway config info in bpq32.cfg -// Move IPGateway, AXIP, VKISS, AGW and WINMOR drivers into bpq32.dll -// Add option to reread IP Gateway config. -// Fix Reinit after process with timer closes (error in TellSessions). - -// 410p Build 2 October 2010 - -// Move KAM and SCS drivers to bpq32.dll - -// 410p Build 3 October 2010 - -// Support more than one axip port. - -// 410p Build 4 October 2010 - -// Dynamically load psapi.dll (for 98/ME) - -// 410p Build 5 October 2010 - -// Incorporate TelnetServer -// Fix AXIP ReRead Config -// Report AXIP accept() fails to syslog, not a popup. - -// 410p Build 6 October 2010 - -// Includes HAL support -// Changes to Pactor Drivers disconnect code -// AXIP now sends with source port = dest port, unless overridden by SOURCEPORT param -// Config now checks for duplicate port definitions -// Add Node Map reporting -// Fix WINMOR deferred disconnect. -// Report Pactor PORTCALL to WL2K instead of RMS Applcall - -// 410p Build 7 October 2010 - -// Add In/Out flag to Map reporting, and report centre, not dial -// Write Telnet log to BPQ Directory -// Add Port to AXIP resolver display -// Send Reports to update.g8bpq.net:81 -// Add support for FT100 to Rigcontrol -// Add timeout to Rigcontrol PTT -// Add Save Registry Command - -// 410p Build 8 November 2010 - -// Add NOKEEPALIVES Port Param -// Renumbered for release - -// 410p Build 9 November 2010 - -// Get Bandwith for map report from WL2K Report Command -// Fix freq display for FT100 (was KHz, not MHz) -// Don't try to change SCS mode whilst initialising -// Allow reporting of Lat/Lon as well as Locator -// Fix Telnet Log Name -// Fix starting with Minimized windows when Minimizetotray isn't set -// Extra Program Error trapping in SessionControl -// Fix reporting same freq with different bandwidths at different times. -// Code changes to support SCS Robust Packet Mode. -// Add FT2000 to Rigcontrol -// Only Send CTEXT to connects to Node (not to connects to an Application Call) - -// Released as Build 10 - -// 410p Build 11 January 2011 - -// Fix MH Update for SCS Outgoing Calls -// Add Direct CMS Access to TelnetServer -// Restructure DISCONNECT processing to run in Timer owning process - -// 410p Build 12 January 2011 - -// Add option for Hardware PTT to use a different com port from the scan port -// Add CAT PTT for Yaesu 897 (and maybe others) -// Fix RMS Packet ports busy after restart -// Fix CMS Telnet with MAXSESSIONS > 10 - -// 410p Build 13 January 2011 - -// Fix loss of buffers in TelnetServer -// Add CMS logging. -// Add non - Promiscuous mode option for BPQETHER - -// 410p Build 14 January 2011 - -// Add support for BPQTermTCP -// Allow more that one FBBPORT -// Allow Telnet FBB mode sessions to send CRLF as well as CR on user and pass msgs -// Add session length to CMS Telnet logging. -// Return Secure Session Flag from GetConnectionInfo -// Show Uptime as dd/hh/mm - -// 4.10.16.17 March 2011 - -// Add "Close all programs" command -// Add BPQ Program Directory registry key -// Use HKEY_CURRENT_USER on Vista and above (and move registry if necessary) -// Time out IP Gateway ARP entries, and only reload ax.25 ARP entries -// Add support for SCS Tracker HF Modes -// Fix WL2K Reporting -// Report Version to WL2K -// Add Driver to support Tracker with multiple sessions (but no scanning, wl2k report, etc) - - -// Above released as 5.0.0.1 - -// 5.2.0.1 - -// Add caching of CMS Server IP addresses -// Initialise TNC State on Pactor Dialogs -// Add Shortened (6 digit) AUTH mode. -// Update MH with all frames (not just I/UI) -// Add IPV6 Support for TelnetServer and AXIP -// Fix TNC OK Test for Tracker -// Fix crash in CMS mode if terminal disconnects while tcp commect in progress -// Add WL2K reporting for Robust Packet -// Add option to suppress WL2K reporting for specific frequencies -// Fix Timeband processing for Rig Control -// New Driver for SCS Tracker allowing multiple connects, so Tracker can be used for user access -// New Driver for V4 TNC - -// 5.2.1.3 October 2011 - -// Combine busy detector on Interlocked Ports (SCS PTC, WINMOR or KAM) -// Improved program error logging -// WL2K reporting changed to new format agreed with Lee Inman - -// 5.2.3.1 January 2012 - -// Connects from the console to an APPLCALL or APPLALIAS now invoke any Command Alias that has been defined. -// Fix reporting of Tracker freqs to WL2K. -// Fix Tracker monitoring setup (sending M UISC) -// Fix possible call/application routing error on RP -// Changes for P4Dragon -// Include APRS Digi/IGate -// Tracker monitoring now includes DIGIS -// Support sending UI frames using SCSTRACKER, SCTRKMULTI and UZ7HO drivers -// Include driver for UZ7HO soundcard modem. -// Accept DRIVER as well as DLLNAME, and COMPORT as well as IOADDR in bpq32.cfg. COMPORT is decimal -// No longer supports separate config files, or BPQTELNETSERVER.exe -// Improved flow control for Telnet CMS Sessions -// Fix handling Config file without a newline after last line -// Add non - Promiscuous mode option for BPQETHER -// Change Console Window to a Dialog Box. -// Fix possible corruption and loss of buffers in Tracker drivers -// Add Beacon After Session option to Tracker and UZ7HO Drivers -// Rewrite RigControl and add "Reread Config Command" -// Support User Mode VCOM Driver for VKISS ports - -// 5.2.4.1 January 2012 - -// Remove CR from Telnet User and Password Prompts -// Add Rigcontrol to UZ7HO driver -// Fix corruption of Free Buffer Count by Rigcontol -// Fix WINMOR and V4 PTT -// Add MultiPSK Driver -// Add SendBeacon export for BPQAPRS -// Add SendChatReport function -// Fix check on length of Port Config ID String with trailing spaces -// Fix interlock when Port Number <> Port Slot -// Add NETROMCALL for L3 Activity -// Add support for APRS Application -// Fix Telnet with FBBPORT and no TCPPORT -// Add Reread APRS Config -// Fix switching to Pactor after scanning in normal packet mode (PTC) - -// 5.2.5.1 February 2012 - -// Stop reading Password file. -// Add extra MPSK commands -// Fix MPSK Transparency -// Make LOCATOR command compulsory -// Add MobileBeaconInterval APRS param -// Send Course and Speed when APRS is using GPS -// Fix Robust Packet reporting in PTC driver -// Fix corruption of some MIC-E APRS packets - -// 5.2.6.1 February 2012 - -// Convert to MDI presentation of BPQ32.dll windows -// Send APRS Status packets -// Send QUIT not EXIT in PTC Init -// Implement new WL2K reporting format and include traffic reporting info in CMS signon -// New WL2KREPORT format -// Prevent loops when APPL alias refers to itself -// Add RigControl for Flex radios and ICOM IC-M710 Marine radio - -// 5.2.7.1 - -// Fix opening more thn one console window on Win98 -// Change method of configuring multiple timelots on WL2K reporting -// Add option to update WK2K Sysop Database -// Add Web server -// Add UIONLY port option - -// 5.2.7.2 - -// Fix handling TelnetServer packets over 500 bytes in normal mode - -// 5.2.7.3 - -// Fix Igate handling packets from UIView - -// 5.2.7.4 - -// Prototype Baycom driver. - -// 5.2.7.5 - -// Set WK2K group ref to MARS (3) if using a MARS service code - -// 5.2.7.7 - -// Check for programs calling CloseBPQ32 when holding semaphore -// Try/Except round Status Timer Processing - -// 5.2.7.8 - -// More Try/Except round Timer Processing - -// 5.2.7.9 - -// Enable RX in Baycom, and remove test loopback in tx - -// 5.2.7.10 - -// Try/Except round ProcessHTTPMessage - -// 5.2.7.11 - -// BAYCOM tweaks - -// 5.2.7.13 - -// Release semaphore after program error in Timer Processing -// Check fro valid dest in REFRESHROUTE - - -// Add TNC-X KISSOPTION (includes the ACKMODE bytes in the checksum( - -// Version 5.2.9.1 Sept 2012 - -// Fix using KISS ports with COMn > 16 -// Add "KISS over UDP" driver for PI as a TNC concentrator - -// Version 6.0.1.1 - -// Convert to C for linux portability -// Try to speed up kiss polling - -// Version 6.0.2.1 - -// Fix operation on Win98 -// Fix callsign error with AGWtoBPQ -// Fix PTT problem with WINMOR -// Fix Reread telnet config -// Add Secure CMS signon -// Fix error in cashing addresses of CMS servers -// Fix Port Number when using Send Raw. -// Fix PE in KISS driver if invalid subchannel received -// Fix Orignal address of beacons -// Speed up Telnet port monitoring. -// Add TNC Emulators -// Add CountFramesQueuedOnStream API -// Limit number of frames that can be queued on a session. -// Add XDIGI feature -// Add Winmor Robust Mode switching for compatibility with new Winmor TNC -// Move most APRS code from BPQAPRS to here -// Stop corruption caused by overlong KISS frames - -// Version 6.0.3.1 - -// Add starting/killing WINMOR TNC on remote host -// Fix Program Error when APRS Item or Object name is same as call of reporting station -// Dont digi a frame that we have already digi'ed -// Add ChangeSessionIdleTime API -// Add WK2KSYSOP Command -// Add IDLETIME Command -// Fix Errors in RELAYAPPL processing -// Fix PE cauaed by invalid Rigcontrol Line - -// Version 6.0.4.1 - -// Add frequency dependent autoconnect appls for SCS Pactor -// Fix DED Monitoring of I and UI with no data -// Include AGWPE Emulator (from AGWtoBPQ) -// accept DEL (Hex 7F) as backspace in Telnet -// Fix re-running resolver on re-read AXIP config -// Speed up processing, mainly for Telnet Sessions -// Fix APRS init on restart of bpq32.exe -// Change to 2 stop bits -// Fix scrolling of WINMOR trace window -// Fix Crash when ueing DED TNC Emulator -// Fix Disconnect when using BPQDED2 Driver with Telnet Sessions -// Allow HOST applications even when CMS option is disabled -// Fix processing of APRS DIGIMAP command with no targets (didn't suppress default settings) - -// Version 6.0.5.1 January 2014 - -// Add UTF8 conversion mode to Telnet (converts non-UTF-8 chars to UTF-8) -// Add "Clear" option to MH command -// Add "Connect to RMS Relay" Option -// Revert to one stop bit on serial ports, explictly set two on FT2000 rig control -// Fix routing of first call in Robust Packet -// Add Options to switch input source on rigs with build in soundcards (sor far only IC7100 and Kenwood 590) -// Add RTS>CAT PTT option for Sound Card rigs -// Add Clear Nodes Option (NODE DEL ALL) -// SCS Pactor can set differeant APPLCALLS when scanning. -// Fix possible Scan hangup after a manual requency change with SCS Pactor -// Accept Scan entry of W0 to disable WINMOR on that frequency -// Fix corruption of NETROMCALL by SIMPLE config command -// Enforce Pactor Levels -// Add Telnet outward connect -// Add Relay/Trimode Emulation -// Fix V4 Driver -// Add PTT Mux -// Add Locked ARP Entries (via bpq32.cfg) -// Fix IDLETIME node command -// Fix STAY param on connect -// Add STAY option to Attach and Application Commands -// Fix crash on copying a large AXIP MH Window -// Fix possible crash when bpq32.exe dies -// Fix DIGIPORT for UI frames - -// Version 6.0.6.1 April 2014 - -// FLDigi Interface -// Fix "All CMS Servers are inaccessible" message so Mail Forwarding ELSE works. -// Validate INP3 messages to try to prevent crash -// Fix possible crash if an overlarge KISS frame is received -// Fix error in AXR command -// Add LF to Telnet Outward Connect signin if NEEDLF added to connect line -// Add CBELL to TNC21 emulator -// Add sent objects and third party messages to APRS Dup List -// Incorporate UIUtil -// Use Memory Mapped file to pass APRS info to BPQAPRS, and process APRS HTTP in BPQ32 -// Improvements to FLDIGI interlocking -// Fix TNC State Display for Tracker -// Cache CMS Addresses on LinBPQ -// Fix count error on DED Driver when handling 256 byte packets -// Add basic SNMP interface for MRTG -// Fix memory loss from getaddrinfo -// Process "BUSY" response from Tracker -// Handle serial port writes that don't accept all the data -// Trap Error 10038 and try to reopen socket -// Fix crash if overlong command line received - -// Version 6.0.7.1 Aptil 2014 -// Fix RigContol with no frequencies for Kenwood and Yaesu -// Add busy check to FLDIGI connects - -// Version 6.0.8.1 August 2014 - -// Use HKEY_CURRENT_USER on all OS versions -// Fix crash when APRS symbol is a space. -// Fixes for FT847 CAT -// Fix display of 3rd byte of FRMR -// Add "DEFAULT ROBUST" and "FORCE ROBUST" commands to SCSPactor Driver -// Fix possible memory corruption in WINMOR driver -// Fix FT2000 Modes -// Use new WL2K reporting system (Web API Based) -// APRS Server now cycles through hosts if DNS returns more than one -// BPQ32 can now start and stop FLDIGI -// Fix loss of AXIP Resolver when running more than one AXIP port - -// Version 6.0.9.1 November 2014 - -// Fix setting NOKEEPALIVE flag on route created from incoming L3 message -// Ignore NODES from locked route with quality 0 -// Fix seting source port in AXIP -// Fix Dual Stack (IPV4/V6) on Linux. -// Fix RELAYSOCK if IPv6 is enabled. -// Add support for FT1000 -// Fix hang when APRS Messaging packet received on RF -// Attempt to normalize Node qualies when stations use widely differing Route qualities -// Add NODES VIA command to display nodes reachable via a specified neighbour -// Fix applying "DisconnectOnClose" setting on HOST API connects (Telnet Server) -// Fix buffering large messages in Telnet Host API -// Fix occasional crash in terminal part line processing -// Add "NoFallback" command to Telnet server to disable "fallback to Relay" -// Improved support for APPLCALL scanning with Pactor -// MAXBUFFS config statement is no longer needed. -// Fix USEAPPLCALLS with Tracker when connect to APPLCALL fails -// Implement LISTEN and CQ commands -// FLDIGI driver can now start FLDIGI on a remote system. -// Add IGNOREUNLOCKEDROUTES parameter -// Fix error if too many Telnet server connections - -// Version 6.0.10.1 Feb 2015 - -// Fix crash if corrupt HTML request received. -// Allow SSID's of 'R' and 'T' on non-ax.25 ports for WL2K Radio Only network. -// Make HTTP server HTTP Version 1.1 complient - use persistent conections and close after 2.5 mins -// Add INP3ONLY flag. -// Fix program error if enter UNPROTO without a destination path -// Show client IP address on HTTP sessions in Telnet Server -// Reduce frequency and number of attempts to connect to routes when Keepalives or INP3 is set -// Add FT990 RigControl support, fix FT1000MP support. -// Support ARMV5 processors -// Changes to support LinBPQ APRS Client -// Add IC7410 to supported Soundcard rigs -// Add CAT PTT to NMEA type (for ICOM Marine Radios_ -// Fix ACKMODE -// Add KISS over TCP -// Support ACKMode on VKISS -// Improved reporting of configuration file format errors -// Experimental driver to support ARQ sessions using UI frames - -// Version 6.0.11.1 September 2015 - -// Fixes for IPGateway configuration and Virtual Circuit Mode -// Separate Portmapper from IPGateway -// Add PING Command -// Add ARDOP Driver -// Add basic APPLCALL support for PTC-PRO/Dragon 7800 Packet (using MYALIAS) -// Add "VeryOldMode" for KAM Version 5.02 -// Add KISS over TCP Slave Mode. -// Support Pactor and Packet on P4Dragon on one port -// Add "Remote Staton Quality" to Web ROUTES display -// Add Virtual Host option for IPGateway NET44 Encap -// Add NAT for local hosts to IPGateway -// Fix setting filter from RADIO command for IC7410 -// Add Memory Channel Scanning for ICOM Radios -// Try to reopen Rig Control port if it fails (could be unplugged USB) -// Fix restoring position of Monitor Window -// Stop Codec on Winmor and ARDOP when an interlocked port is attached (instead of listen false) -// Support APRS beacons in RP mode on Dragon// -// Change Virtual MAC address on IPGateway to include last octet of IP Address -// Fix "NOS Fragmentation" in IP over ax.25 Virtual Circuit Mode -// Fix sending I frames before L2 session is up -// Fix Flow control on Telnet outbound sessions. -// Fix reporting of unterminatred comments in config -// Add option for RigControl to not change mode on FT100/FT990/FT1000 -// Add "Attach and Connect" for Telnet ports - -// Version 6.0.12.1 November 2015 - -// Fix logging of IP addresses for connects to FBBPORT -// Allow lower case user and passwords in Telnet "Attach and Connect" -// Fix possible hang in KISS over TCP Slave mode -// Fix duplicating LinBPQ process if running ARDOP fails -// Allow lower case command aliases and increase alias length to 48 -// Fix saving long IP frames pending ARP resolution -// Fix dropping last entry from a RIP44 message. -// Fix displaying Digis in MH list -// Add port name to Monitor config screen port list -// Fix APRS command display filter and add port filter -// Support port names in BPQTermTCP Monitor config -// Add FINDBUFFS command to dump lost buffers to Debugview/Syslog -// Buffer Web Mgmt Edit Config output -// Add WebMail Support -// Fix not closing APRS Send WX file. -// Add RUN option to APRS Config to start APRS Client -// LinBPQ run FindLostBuffers and exit if QCOUNT < 5 -// Close and reopen ARDOP connection if nothing received for 90 secs -// Add facility to bridge traffic between ports (similar to APRS Bridge but for all frame types) -// Add KISSOPTION TRACKER to set SCS Tracker into KISS Mode - -// 6.0.13.1 - -// Allow /ex to exit UNPROTO mode -// Support ARQBW commands. -// Support IC735 -// Fix sending ARDOP beacons after a busy holdoff -// Enable BPQDED driver to beacon via non-ax.25 ports. -// Fix channel number in UZ7HO monitoring -// Add SATGate mode to APRSIS Code. -// Fix crash caused by overlong user name in telnet logon -// Add option to log L4 connects -// Add AUTOADDQuiet mode to AXIP. -// Add EXCLUDE processing -// Support WinmorControl in UZ7HO driver and fix starting TNC on Linux -// Convert calls in MAP entries to upper case. -// Support Linux COM Port names for APRS GPS -// Fix using NETROM serial protocol on ASYNC Port -// Fix setting MYLEVEL by scanner after manual level change. -// Add DEBUGLOG config param to SCS Pactor Driver to log serial port traffic -// Uue #myl to set SCS Pactor MYLEVEL, and add checklevel command -// Add Multicast RX interface to FLDIGI Driver -// Fix processing application aliases to a connect command. -// Fix Buffer loss if radio connected to PTC rig port but BPQ not configured to use it -// Save backups of bpq32.cfg when editing with Web interface and report old and new length -// Add DD command to SCS Pactor, and use it for forced disconnect. -// Add ARDOP mode select to scan config -// ARDOP changes for ARDOP V 0.5+ -// Flip SSID bits on UZ7HO downlink connects - - -// Version 6.0.14.1 - -// Fix Socket leak in ARDOP and FLDIGI drivers. -// Add option to change CMS Server hostname -// ARDOP Changes for 0.8.0+ -// Discard Terminal Keepalive message (two nulls) in ARDOP command hander -// Allow parameters to be passed to ARDOP TNC when starting it -// Fix Web update of Beacon params -// Retry connects to KISS ports after failure -// Add support for ARDOP Serial Interface Native mode. -// Fix gating APRS-IS Messages to RF -// Fix Beacons when PORTNUM used -// Make sure old monitor flag is cleared for TermTCP sessions -// Add CI-V antenna control for IC746 -// Don't allow ARDOP beacons when connected -// Add support for ARDOP Serial over I2C -// Fix possble crash when using manual RADIO messages -// Save out of sequence L2 frames for possible reuse after retry -// Add KISS command to send KISS control frame to TNC -// Stop removing unused digis from packets sent to APRS-IS - -// Processing of ARDOP PING and PINGACK responses -// Handle changed encoding of WL2K update responses. -// Allow anonymous logon to telnet -// Don't use APPL= for RP Calls in Dragon Single mode. -// Add basic messaging page to APRS Web Server -// Add debug log option to SCSTracker and TrkMulti Driver -// Support REBOOT command on LinBPQ -// Allow LISTEN command on all ports that support ax.25 monitoring - -// Version 6.0.15.1 Feb 2018 - -// partial support for ax.25 V2.2 -// Add MHU and MHL commands and MH filter option -// Fix scan interlock with ARDOP -// Add Input source seiect for IC7300 -// Remove % transparency from web terminal signon message -// Fix L4 Connects In count on stats -// Fix crash caused by corrupt CMSInfo.txt -// Add Input peaks display to ARDOP status window -// Add options to show time in local and distances in KM on APRS Web pages -// Add VARA support -// Fix WINMOR Busy left set when port Suspended -// Add ARDOP-Packet Support -// Add Antenna Switching for TS 480 -// Fix possible crash in Web Terminal -// Support different Code Pages on Console sessions -// Use new Winlink API interface (api.winlink.org) -// Support USB/ACC switching on TS590SG -// Fix scanning when ARDOP or WINMOR is used without an Interlocked Pactor port. -// Set NODECALL to first Application Callsign if NODE=0 and BBSCALL not set. -// Add RIGCONTROL TUNE and POWER commands for some ICOM and Kenwwod rigs -// Fix timing out ARDOP PENDING Lock -// Support mixed case WINLINK Passwords -// Add TUNE and POWER Rigcontol Commands for some radios -// ADD LOCALTIME and DISPKM options to APRS Digi/Igate - -// 6.0.16.1 March 2018 - -// Fix Setting data mode and filter for IC7300 radios -// Add VARA to WL2KREPORT -// Add trace to SCS Tracker status window -// Fix possible hang in IPGATEWAY -// Add BeacontoIS parameter to APRSDIGI. Allows you to stop sending beacons to APRS-IS. -// Fix sending CTEXT on WINMOR sessions - -// 6.0.17.1 November 2018 - -// Change WINMOR Restart after connection to Restart after Failure and add same option to ARDOP and VARA -// Add Abort Connection to WINMOR and VARA Interfaces -// Reinstate accidentally removed CMS Access logging -// Fix MH CLEAR -// Fix corruption of NODE table if NODES received from station with null alias -// Fix loss of buffer if session closed with something in PARTCMDBUFFER -// Fix Spurious GUARD ZONE CORRUPT message in IP Code. -// Remove "reread bpq32.cfg and reconfigure" menu options -// Add support for PTT using CM108 based soundcard interfaces -// Datestamp Telnet log files and delete old Telnet and CMSAcces logs - -// 6.0.18.1 January 2019 - -// Fix validation of NODES broadcasts -// Fix HIDENODES -// Check for failure to reread config on axip reconfigure -// Fix crash if STOPPORT or STARTPORT used on KISS over TCP port -// Send Beacons from BCALL or PORTCALL if configured -// Fix possible corruption of last entry in MH display -// Ensure RTS/DTR is down when opening PTT Port -// Remove RECONFIG command -// Preparations for 64 bit version - -// 6.0.19 Sept 2019 -// Fix UZ7HO interlock -// Add commands to set Centre Frequency and Modem with UZ7HO Soundmodem (on Windows only) -// Add option to save and restore MH lists and SAVEMH command -// Add Frequency (if known) to UZ7HO MH lists -// Add Gateway option to Telnet for PAT -// Try to fix SCS Tracker recovery -// Ensure RTS/DTR is down on CAT port if using that line for PTT -// Experimental APRS Messaging in Kernel -// Add Rigcontrol on remote PC's using WinmorControl -// ADD VARAFM and VARAFM96 WL2KREPORT modes -// Fix WL2K sysop update for new Winlink API -// Fix APRS when using PORTNUM higher than the number of ports -// Add Serial Port Type -// Add option to linbpq to log APRS-IS messages. -// Send WL2K Session Reports -// Drop Tunneled Packets from 44.192 - 44.255 -// Log incoming Telnet Connects -// Add IPV4: and IPV6: overrides on AXIP Resolver. -// Add SessionTimeLimit to HF sessions (ARDOP, SCSPactor, WINMOR, VARA) -// Add RADIO FREQ command to display current frequency - -// 6.0.20 April 2020 - -// Trap and reject YAPP file transfer request. -// Fix possible overrun of TCP to Node Buffer -// Fix possible crash if APRS WX file doesn't have a terminating newline -// Change communication with BPQAPRS.exe to restore old message popup behaviour -// Preparation for 64 bit version -// Improve flow control on SCS Dragon -// Fragment messages from network links to L2 links with smaller paclen -// Change WL2K report rate to once every two hours -// Add PASS, CTEXT and CMSG commands and Stream Switch support to TNC2 Emulator -// Add SessionTimeLimit command to HF drivers (ARDOP, SCSPactor, WINMOR, VARA) -// Add links to Ports Web Manangement Page to open individual Driver windows -// Add STOPPORT/STARTPORT support to ARDOP, KAM and SCSPactor drivers -// Add CLOSE and OPEN RADIO command so Rigcontrol port can be freed fpr other use. -// Don't try to send WL2K Traffic report if Internet is down -// Move WL2K Traffic reporting to a separate thread so it doesn't block if it can't connect to server -// ADD AGWAPPL config command to set application number. AGWMASK is still supported -// Register Node Alias with UZ7HO Driver -// Register calls when UZ7HO TNC Restarts and at intervals afterwards -// Fix crash when no IOADDR or COMPORT in async port definition -// Fix Crash with Paclink-Unix when parsing ; VE7SPR-10 DE N7NIX QTC 1 -// Only apply BBSFLAG=NOBBS to APPPLICATION 1 -// Add RIGREONFIG command -// fix APRS RECONFIG on LinBPQ -// Fix Web Terminal scroll to end problem on some browsers -// Add PTT_SETS_INPUT option for IC7600 -// Add TELRECONFIG command to reread users or whole config -// Enforce PACLEN on UZ7HO ports -// Fix PACLEN on Command Output. -// Retry axip resolver if it fails at startup -// Fix AGWAPI connect via digis -// Fix Select() for Linux in MultiPSK, UZ7HO and V4 drivers -// Limit APRS OBJECT length to 80 chars -// UZ7HO disconnect incoming call if no free streams -// Improve response to REJ (no F) followed by RR (F). -// Try to prevent more than MAXFRAME frames outstanding when transmitting -// Allow more than one instance of APRS on Linux -// Stop APRS digi by originating station -// Send driver window trace to main monitor system -// Improve handling of IPOLL messages -// Fix setting end of address bit on dest call on connects to listening sessions -// Set default BBS and CHAT application number and number of streams on LinBPQ -// Support #include in bpq32.cfg processing - -// Version 6.0.21 14 December 2020 - -// Fix occasional missing newlines in some node command reponses -// More 64 bit fixes -// Add option to stop setting PDUPLEX param in SCSPACTOR -// Try to fix buffer loss -// Remove extra space from APRS position reports -// Suppress VARA IAMALIVE messages -// Add display and control of QtSoundModem modems -// Only send "No CMS connection available" message if fallbacktorelay is set. -// Add HAMLIB backend and emulator support to RIGCONTROL -// Ensure all beacons are sent even with very short beacon intervals -// Add VARA500 WL2K Reporting Mode -// Fix problem with prpcessing frame collector -// Temporarily disable L2 and L4 collectors till I can find problem -// Fix possible problem with interactive RADIO commands not giving a response, -// Incease maximum length of NODE command responses to handle maximum length INFO message, -// Allow WL2KREPORT in CONFIG section of UZ7HO port config. -// Fix program error in processing hamlib frame -// Save RestartAfterFailure option for VARA -// Check callsign has a winlink account before sending WL2KREPORT messages -// Add Bandwidth control to VARA scanning -// Renable L2 collector -// Fix TNCPORT reconnect on Linux -// Add SecureTelnet option to limit telnet outward connect to sysop mode sessions or Application Aliases -// Add option to suppress sending call to application in Telnet HOST API -// Add FT991A support to RigControl -// Use background.jpg for Edit Config page -// Send OK response to SCS Pactor commands starting with # -// Resend ICOM PTT OFF command after 30 seconds -// Add WXCall to APRS config -// Fixes for AEAPactor -// Allow PTTMUX to use real or com0com com ports -// Fix monitoring with AGW Emulator -// Derive approx position from packets on APRS ports with a valid 6 char location -// Fix corruption of APRS message lists if the station table fills up. -// Don't accept empty username or password on Relay sessions. -// Fix occasional empty Nodes broadcasts -// Add Digis to UZ7HO Port MH list -// Add PERMITTEDAPPLS port param -// Fix WK2K Session Record Reporting for Airmail and some Pactor Modes. -// Fix handling AX/IP (proto 93) frames -// Fix possible corruption sending APRS messages -// Allow Telnet connections to be made using Connect command as well as Attach then Connect -// Fix Cancel Sysop Signin -// Save axip resolver info and restore on restart -// Add Transparent mode to Telnet Server HOST API -// Fix Tracker driver if WL2KREPRRT is in main config section -// SNMP InOctets count corrected to include all frames and encoding of zero values fixed. -// Change IP Gateway to exclude handling bits of 44 Net sold to Amazon -// Fix crash in Web terminal when processing very long lines - -// Version 6.0.22.1 August 2021 - -// Fix bug in KAM TNCEMULATOR -// Add WinRPR Driver (DED over TCP) -// Fix handling of VARA config commands FM1200 and FM9600 -// Improve Web Termanal Line folding -// Add StartTNC to WinRPR driver -// Add support for VARA2750 Mode -// Add support for VARA connects via a VARA Digipeater -// Add digis to SCSTracker and WinRPR MHeard -// Separate RIGCONTROL config from PORT config and add RigControl window -// Fix crash when a Windows HID device doesn't have a product_string -// Changes to VARA TNC connection and restart process -// Trigger FALLBACKTORELAY if attempt to connect to all CMS servers fail. -// Fix saving part lines in adif log and Winlink Session reporting -// Add port specific CTEXT -// Add FRMR monitoring to UZ7HO driver -// Add audio input switching for IC7610 -// Include Rigcontrol Support for IC-F8101E -// Process any response to KISS command -// Fix NODE ADD command -// Add noUpdate flag to AXIP MAP -// Fix clearing NOFALLBACK flag in Telnet Server -// Allow connects to RMS Relay running on another host -// Allow use of Power setting in Rigcontol scan lines for Kenwood radios -// Prevent problems caused by using "CMS" as a Node Alias -// Include standard APRS Station pages in code -// Fix VALIDCALLS processing in HF drivers -// Send Netrom Link reports to Node Map -// Add REALTELNET mode to Telnet Outward Connect -// Fix using S (Stay) parameter on Telnet connects when using CMDPORT and C HOST -// Add Default frequency to rigcontrol to set a freq/mode to return to after a connection -// Fix long (> 60 seconds) scan intervals -// Improved debugging of stuck semaphores -// Fix potential securiby bug in BPQ Web server -// Send Chat Updates to chatupdate.g8bpq.net port 81 -// Add ReportRelayTraffic to Telnet config to send WL2K traffic reports for connections to RELAY -// Add experimental Mode reporting -// Add SendTandRtoRelay param to SCS Pactor, ARDOP and VARA drivers to divert calls to CMS for -T and -R to RELAY -// Add UPNP Support - -// Version 6.0.23.1 June 2022 - -// Add option to control which applcalls are enabled in VARA -// Add support for rtl_udp to Rig Control -// Fix Telnet Auto Conneect to Application when using TermTCP or Web Terminal -// Allow setting css styles for Web Terminal -// And Kill TNC and Kill and Restart TNC commands to Web Driver Windows -// More flexible RigControl for split frequency operation, eg for QO100 -// Increase stack size for ProcessHTMLMessage (.11) -// Fix HTML Content-Type on images (.12) -// Add AIS and ADSB Support (.13) -// Compress web pages (.14) -// Change minidump routine and close after program error (.15) -// Add RMS Relay SYNC Mode (.17) -// Changes for compatibility with Winlink Hybrid -// Add Rigcontrol CMD feature to Yaesu code (21) -// More diagnostic code -// Trap potential buffer overrun in ax/tcp code -// Fix possible hang in UZ7HO driver if connect takes a long time to succeed or fail -// Add FLRIG as backend for RigControl (.24) -// Fix bug in compressing some management web pages -// Fix bugs in AGW Emulator (.25) -// Add more PTT_Sets_Freq options for split frequency working (.26) -// Allow RIGCONTROL using Radio Number (Rnn) as well as Port (.26) -// Fix Telnet negotiation and backspace processing (.29) -// Fix VARA Mode change when scanning (.30) -// Add Web Mgmt Log Display (.33) -// Fix crash when connecting to RELAY when CMS=0 (.36) -// Send OK to user for manual freq changes with hamlib or flrig -// Fix Rigcontrol leaving port disabled when using an empty timeband -// Fix processing of backspace in Telnet character processing (.40) -// Increase max size of connect script -// Fix HAMLIB Slave Thread control -// Add processing of VARA mode responses and display of VARA Mode (41) -// Fix crash when VARA session aborted on LinBPQ (43) -// Fix handling port selector (2:call or p2 call) on SCS PTC packet ports (44) -// Include APRS Map web page -// Add Enable/Disable to KAMPACTOR scan control (use P0 or P1) (45) -// Add Basic DRATS interface (46) -// Fix MYCALLS on VARA (49) -// Add FreeData driver (51) -// Add additonal Rigcontrol options for QO100 (51) -// Set Content-Type: application/pdf for pdf files downloaded via web interface (51) -// Fix sending large compressed web messages (52) -// Fix freq display when using flrig or hamlib backends to rigcontrol -// Change VARA Driver to send ABORT when Session Time limit expires -// Add Chat Log to Web Logs display -// Fix possible buffer loss in RigControl -// Allow hosts on local lan to be treated as secure -// Improve validation of data sent to Winlink SessionAdd API call -// Add support for FreeDATA modem. -// Add GetLOC API Call -// Change Leaflet link in aprs map. -// Add Connect Log (64) -// Fix crash when Resolve CMS Servers returns ipv6 addresses -// Fix Reporting P4 sessions to Winlink (68) -// Add support for FreeBSD (68) -// Fix Rigcontrol PTCPORT (69) -// Set TNC Emulator sessions as secure (72) -// Fix not always detecting loss of FLRIG (73) -// Add ? and * wildcards to NODES command (74) -// Add Port RADIO config parameter (74) - -// Version 6.0.24.1 August 2023 - -// Apply NODES command wildcard to alias as well a call (2) -// Add STOPPORT/STARTPORT to VARA Driver (2) -// Add bandwidth setting to FLRIG interface. (2) -// Fix N VIA (3) -// Fix NODE ADD and NODE DEL (4) -// Improvements to FLRIG Rigcontrol backend (6, 7) -// Fix UZ7HO Window Title Update -// Reject L2 calls with a blank from call (8) -// Update WinRPR Window header with BPQ Port Description (8) -// Fix error in blank call code (9) -// Change web buttons to white on black when pressed (10) -// Fix Port CTEXT paclen on Tracker and WinRPR drivers (11) -// Add RADIO PTT command for testing PTT (11) -// Fix using APPLCALLs on SCSTracker RP call (12) -// Add Rigcntol Web Page (13) -// Fix scan bandwidth change with ARDOPOFDM (13) -// Fix setting Min Pactor Level in SCSPactor (13) -// Fix length of commands sent via CMD_TO_APPL flag (14) -// Add filter by quality option to N display (15) -// Fix VARA Mode reporting to WL2K (16) -// Add FLRIG POWER and TUNE commands (18) -// Fix crash when processing "C " without a call in UZ7HO, FLDIGI or MULTIPSK drivers (19) -// FLDIGI improvements (19) -// Fix hang at start if Telnet port Number > Number of Telnet Streams (20) -// Fix processing C command if first port driver is SCSPACTROR (20) -// Fix crash in UZ7HO driver if bad raw frame received (21) -// Fix using FLARQ chat mode with FLDIGI ddriover (22) -// Fix to KISSHF driver (23) -// Fix for application buffer loss (24) -// Add Web Sockets auto-refresh option for Webmail index page (25) -// Fix FREEDATA driver for compatibility with FreeData TNC version 0.6.4-alpha.3 (25) -// Add SmartID for bridged frames - Send ID only if packets sent recently (26) -// Add option to save and restore received APRS messages (27) -// Add mechanism to run a user program on certain events (27) -// If BeacontoIS is zero don't Gate any of our messages received locally to APRS-IS (28) -// Add Node Help command (28) -// Add APRS Igate RXOnly option (29) -// Fix RMC message handling with prefixes other than GP (29) -// Add GPSD support for APRS (30) -// Attempt to fix Tracker/WinRPR reconnect code (30) -// Changes to FreeDATA - Don't use deamon and add txlevel and send text commands (31) -// Fix interactive commands in tracker driver (33) -// Fix SESSIONTIMELIMIT processing -// Add STOPPORT/STARTPORT for UZ7HO driver -// Fix processing of extended QtSM 'g' frame (36) -// Allow setting just freq on Yaseu rigs (37) -// Enable KISSHF driver on Linux (40) -// Allow AISHOST and ADSBHOST to be a name as well as an address (41) -// Fix Interlock of incoming UZ7HO connections (41) -// Disable VARA Actions menu if not sysop (41) -// Fix Port CTEXT on UZ7HO B C or D channels (42) -// Fix repeated trigger of SessionTimeLimit (43) -// Fix posible memory corruption in UpateMH (44) -// Add PHG to APRS beacons (45) -// Dont send DM to stations in exclude list(45) -// Improvements to RMS Relay SYNC Mode (46) -// Check L4 connects against EXCLUDE list (47) -// Add vaidation of LOC in WL2K Session Reports (49) -// Change gpsd support for compatibility with Share Gps (50) -// Switch APRS Map to my Tiles (52) -// Fix using ; in UNPROTO Mode messages (52) -// Use sha1 code from https://www.packetizer.com/security/sha1/ instead of openssl (53) -// Fix TNC Emulator Monitoring (53) -// Fix attach and connect on Telnet port bug introduced in .55 (56) -// Fix stopping WinRPR TNC and Start/Stop UZ7HO TNCX on Linux (57) -// Fix stack size in beginthread for MAC (58) -// Add NETROM over VARA (60) -// Add Disconnect Script (64) -// Add node commands to set UZ7HO modem mode and freq (64) -// Trap empty NODECALL or NETROMCALL(65) -// Trap NODES messages with empty From Call (65) -// Add RigControl for SDRConsole (66) -// Fix FLRig crash (66) -// Fix VARA disconnect handling (67) -// Support 64 ports (69) -// Fix Node commands for setting UZ7HO Modem (70) -// Fix processing SABM on an existing session (71) -// Extend KISS Node command to send more than one parameter byte (72) -// Add G7TAJ's code to record activity of HF ports for stats display (72) -// Add option to send KISS command to TNC on startup (73) -// Fix Bug in DED Emulator Monitor code (74) -// Add Filters to DED Monitor code (75) -// Detect loss of DED application (76) -// Fix connects to Application Alias with UZ7HO Driver (76) -// Fix Interlock of ports on same UZ7HO modem. (76) -// Add extended Ports command (77) -// Fix crash in Linbpq when stdout is redirected to /dev/tty? and stdin ia redirected (78) -// Fix Web Terminal (80) -// Trap ENCRYPTION message from VARA (81) -// Fix processing of the Winlink API /account/exists response (82) -// Fix sending CTEXT to L4 connects to Node when FULL_CTEXT is not set - -// Version 6.0.25.? - -// Fix 64 bit compatibility problems in SCSTracker and UZ7HO drivers -// Add Chat PACLEN config (5) -// Fix NC to Application Call (6) -// Fix INP3 L3RTT messages on Linux and correct RTT calculation (9) -// Get Beacon config from config file on Windows (9) -// fix processing DED TNC Emulator M command with space between M and params (10) -// Fix sending UI frames on SCSPACTOR (11) -// Dont allow ports that can't set digi'ed bit in callsigns to digipeat. (11) -// Add SDRAngel rig control (11) -// Add option to specify config and data directories on linbpq (12) -// Allow zero resptime (send RR immediately) (13) -// Make sure CMD bit is set on UI frames -// Add setting Modem Flags in QtSM AGW mode -// If FT847 om PTC Port send a "Cat On" command (17) -// Fix some 63 port bugs in RigCOntrol (17) -// Fix 63 port bug in Bridging (18) -// Add FTDX10 Rigcontrol (19) -// Fix 64 bit bug in displaying INP3 Messages (20) -// Improve restart of WinRPR TNC on remote host (21) -// Fix some Rigcontrol issues with empty timebands (22) -// Fix 64 bit bug in processing INP3 Messages (22) -// First pass at api (24) -// Send OK in response to Rigcontrol CMD (24) -// Disable CTS check in WriteComBlock (26) -// Improvments to reporting to M0LTE Map (26) -// IPGateway fix from github user isavitsky (27) -// Fix possible crash in SCSPactor PTCPORT code (29) -// Add NodeAPI call sendLinks and remove get from other calls (32) -// Improve validation of Web Beacon Config (33) -// Support SNMP via host ip stack as well as IPGateway (34) -// Switch APRS Map to OSM tile servers (36) -// Fix potential buffer overflow in Telnet login (36) -// Allow longer serial device names (37) -// Fix ICF8101 Mode setting (37) -// Kill link if we are getting repeated RR(F) after timeout -// (Indicating other station is seeing our RR(P) but not the resent I frame) (40) -// Change default of SECURETELNET to 1 (41) -// Add optional ATTACH time limit for ARDOP (42) -// Fix buffer overflow risk in HTTP Terminal(42) -// Fix KISSHF Interlock (43) -// Support other than channel A on HFKISS (43) -// Support additional port info reporting for M0LTE Map (44) -// Allow interlocking of KISS and Session mode ports (eg ARDOP and VARA) (45) -// Add ARDOP UI Packets to MH (45) -// Add support for Qtsm Mgmt Interface (45) -// NodeAPI improvements (46) -// Add MQTT Interface (46) -// Fix buffer leak in ARDOP code(46) -// Fix possible crash if MQTT not in use (47) -// Add optional ATTACH time limit for VARA (48) -// API format fixes (48) -// AGWAPI Add protection against accidental connects from a non-agw application (50) -// Save MH and NODES every hour (51) -// Fix handling long unix device names (now max 250 bytes) (52) -// Fix error reporting in api update (53) -// Coding changes to remove some compiler warnings (53, 54) -// Add MQTT reporting of Mail Events (54) -// Fix beaconong on KISSHF ports (55) -// Fix MailAPI msgs endpoint -// Attempt to fix NC going to wrong application. (57) -// Improve ARDOP end of session code (58) -// Run M0LTE Map reporting in a separate thread (59/60) -// Add RHP support for WhatsPac (59) -// Add timestamps to LIS monitor (60) -// Fix problem with L4 frames being delivered out of sequence (60) -// Add Compression of Netrom connections (62) -// Improve handling of Locked Routes (62) -// Add L4 RESET (Paula G8PZT's extension to NETROM) -// Fix problem using SENDRAW from BPQMail (63) -// Fix compatibility with latest ardopcf (64) -// Fix bug in RHP socket timeout code (65) - - -#define CKernel - -#include "Versions.h" - -#define _CRT_SECURE_NO_DEPRECATE - -#pragma data_seg("_BPQDATA") - -#include "time.h" -#include "stdio.h" -#include - -#include "compatbits.h" -#include "AsmStrucs.h" - -#include "SHELLAPI.H" -#include "kernelresource.h" - -#include -#include -#include "BPQTermMDI.h" - -#include "GetVersion.h" - -#define DllImport __declspec( dllimport ) - -#define CheckGuardZone() _CheckGuardZone(__FILE__, __LINE__) -void _CheckGuardZone(char * File, int Line); - -#define CHECKLOADED 0 -#define SETAPPLFLAGS 1 -#define SENDBPQFRAME 2 -#define GETBPQFRAME 3 -#define GETSTREAMSTATUS 4 -#define CLEARSTREAMSTATUS 5 -#define BPQCONDIS 6 -#define GETBUFFERSTATUS 7 -#define GETCONNECTIONINFO 8 -#define BPQRETURN 9 // GETCALLS -//#define RAWTX 10 //IE KISS TYPE DATA -#define GETRAWFRAME 11 -#define UPDATESWITCH 12 -#define BPQALLOC 13 -//#define SENDNETFRAME 14 -#define GETTIME 15 - -extern short NUMBEROFPORTS; -extern long PORTENTRYLEN; -extern long LINKTABLELEN; -extern struct PORTCONTROL * PORTTABLE; -extern void * FREE_Q; -extern UINT APPL_Q; // Queue of frames for APRS Appl - -extern TRANSPORTENTRY * L4TABLE; -extern UCHAR NEXTID; -extern DWORD MAXCIRCUITS; -extern DWORD L4DEFAULTWINDOW; -extern DWORD L4T1; -extern APPLCALLS APPLCALLTABLE[]; -extern char * APPLS; - -extern struct WL2KInfo * WL2KReports; - -extern int NUMBEROFTNCPORTS; - - -void * VCOMExtInit(struct PORTCONTROL * PortEntry); -void * AXIPExtInit(struct PORTCONTROL * PortEntry); -void * SCSExtInit(struct PORTCONTROL * PortEntry); -void * AEAExtInit(struct PORTCONTROL * PortEntry); -void * KAMExtInit(struct PORTCONTROL * PortEntry); -void * HALExtInit(struct PORTCONTROL * PortEntry); -void * ETHERExtInit(struct PORTCONTROL * PortEntry); -void * AGWExtInit(struct PORTCONTROL * PortEntry); -void * WinmorExtInit(EXTPORTDATA * PortEntry); -void * TelnetExtInit(EXTPORTDATA * PortEntry); -//void * SoundModemExtInit(EXTPORTDATA * PortEntry); -void * TrackerExtInit(EXTPORTDATA * PortEntry); -void * TrackerMExtInit(EXTPORTDATA * PortEntry); -void * V4ExtInit(EXTPORTDATA * PortEntry); -void * UZ7HOExtInit(EXTPORTDATA * PortEntry); -void * MPSKExtInit(EXTPORTDATA * PortEntry); -void * FLDigiExtInit(EXTPORTDATA * PortEntry); -void * UIARQExtInit(EXTPORTDATA * PortEntry); -void * SerialExtInit(EXTPORTDATA * PortEntry); -void * ARDOPExtInit(EXTPORTDATA * PortEntry); -void * VARAExtInit(EXTPORTDATA * PortEntry); -void * KISSHFExtInit(EXTPORTDATA * PortEntry); -void * WinRPRExtInit(EXTPORTDATA * PortEntry); -void * HSMODEMExtInit(EXTPORTDATA * PortEntry); -void * FreeDataExtInit(EXTPORTDATA * PortEntry); -void * SIXPACKExtInit(EXTPORTDATA * PortEntry); - -extern char * ConfigBuffer; // Config Area -VOID REMOVENODE(dest_list * DEST); -DllExport int ConvFromAX25(unsigned char * incall,unsigned char * outcall); -DllExport int ConvToAX25(unsigned char * incall,unsigned char * outcall); -VOID GetUIConfig(); -VOID ADIFWriteFreqList(); -void SaveAIS(); -void initAIS(); -void initADSB(); - -extern BOOL ADIFLogEnabled; - -int CloseOnError = 0; - -char UIClassName[]="UIMAINWINDOW"; // the main window class name - -HWND UIhWnd; - -extern char AUTOSAVE; -extern char AUTOSAVEMH; - -extern char MYNODECALL; // 10 chars,not null terminated - -extern QCOUNT; -extern BPQVECSTRUC BPQHOSTVECTOR[]; -#define BPQHOSTSTREAMS 64 -#define IPHOSTVECTOR BPQHOSTVECTOR[BPQHOSTSTREAMS + 3] - -extern char * CONFIGFILENAME; - -DllExport BPQVECSTRUC * BPQHOSTVECPTR; - -extern int DATABASESTART; - -extern struct ROUTE * NEIGHBOURS; -extern int ROUTE_LEN; -extern int MAXNEIGHBOURS; - -extern struct DEST_LIST * DESTS; // NODE LIST -extern int DEST_LIST_LEN; -extern int MAXDESTS; // MAX NODES IN SYSTEM - -extern struct _LINKTABLE * LINKS; -extern int LINK_TABLE_LEN; -extern int MAXLINKS; - -extern double LatFromLOC; -extern double LonFromLOC; - - -extern int BPQHOSTAPI(); -extern int INITIALISEPORTS(); -extern int TIMERINTERRUPT(); -extern int MONDECODE(); -extern int BPQMONOPTIONS(); -extern char PWTEXT[]; -extern char PWLen; - -extern int FINDFREEDESTINATION(); -extern int RAWTX(); -extern int RELBUFF(); -extern int SENDNETFRAME(); -extern char MYCALL[]; // 7 chars, ax.25 format - -extern HWND hIPResWnd; -extern BOOL IPMinimized; - -extern int NODESINPROGRESS; -extern VOID * CURRENTNODE; - - -BOOL Start(); - -VOID SaveWindowPos(int port); -VOID SaveAXIPWindowPos(int port); -VOID SetupRTFHddr(); -DllExport VOID APIENTRY CreateNewTrayIcon(); -int DoReceivedData(int Stream); -int DoStateChange(int Stream); -int DoMonData(int Stream); -struct ConsoleInfo * CreateChildWindow(int Stream, BOOL DuringInit); -CloseHostSessions(); -SaveHostSessions(); -VOID SaveBPQ32Windows(); -VOID CloseDriverWindow(int port); -VOID CheckWL2KReportTimer(); -VOID SetApplPorts(); -VOID WriteMiniDump(); -VOID FindLostBuffers(); -BOOL InitializeTNCEmulator(); -VOID TNCTimer(); -char * strlop(char * buf, char delim); - -DllExport int APIENTRY Get_APPLMASK(int Stream); -DllExport int APIENTRY GetStreamPID(int Stream); -DllExport int APIENTRY GetApplFlags(int Stream); -DllExport int APIENTRY GetApplNum(int Stream); -DllExport BOOL APIENTRY GetAllocationState(int Stream); -DllExport int APIENTRY GetMsg(int stream, char * msg, int * len, int * count ); -DllExport int APIENTRY RXCount(int Stream); -DllExport int APIENTRY TXCount(int Stream); -DllExport int APIENTRY MONCount(int Stream); -DllExport int APIENTRY GetCallsign(int stream, char * callsign); -DllExport VOID APIENTRY RelBuff(VOID * Msg); -void SaveMH(); -void DRATSPoll(); - -#define C_Q_ADD(s, b) _C_Q_ADD(s, b, __FILE__, __LINE__); -int _C_Q_ADD(VOID *PQ, VOID *PBUFF, char * File, int Line); - -VOID SetWindowTextSupport(); -int WritetoConsoleSupport(char * buff); -VOID PMClose(); -VOID MySetWindowText(HWND hWnd, char * Msg); -BOOL CreateMonitorWindow(char * MonSize); -VOID FormatTime3(char * Time, time_t cTime); - -char EXCEPTMSG[80] = ""; - -char SIGNONMSG[128] = ""; -char SESSIONHDDR[80] = ""; -int SESSHDDRLEN = 0; - -BOOL IncludesMail = FALSE; -BOOL IncludesChat = FALSE; // Set if pgram is running - used for Web Page Index - - -char WL2KCall[10]; -char WL2KLoc[7]; - -extern char LOCATOR[]; // Locator for Reporting - may be Maidenhead or LAT:LON -extern char MAPCOMMENT[]; // Locator for Reporting - may be Maidenhead or LAT:LON -extern char LOC[7]; // Maidenhead Locator for Reporting -extern char ReportDest[7]; - -extern UCHAR ConfigDirectory[260]; - -extern uint64_t timeLoadedMS; - -VOID __cdecl Debugprintf(const char * format, ...); -VOID __cdecl Consoleprintf(const char * format, ...); - -DllExport int APIENTRY CloseBPQ32(); -DllExport char * APIENTRY GetLOC(); -DllExport int APIENTRY SessionControl(int stream, int command, int param); - -int DoRefreshWebMailIndex(); - -BOOL APIENTRY Init_IP(); -BOOL APIENTRY Poll_IP(); - -BOOL APIENTRY Init_PM(); -BOOL APIENTRY Poll_PM(); - -BOOL APIENTRY Init_APRS(); -BOOL APIENTRY Poll_APRS(); -VOID HTTPTimer(); - -BOOL APIENTRY Rig_Init(); -BOOL APIENTRY Rig_Close(); -BOOL Rig_Poll(); - -VOID IPClose(); -VOID APRSClose(); -VOID CloseTNCEmulator(); - -VOID Poll_AGW(); -void RHPPoll(); -BOOL AGWAPIInit(); -int AGWAPITerminate(); - -int * Flag = (int *)&Flag; // for Dump Analysis -int MAJORVERSION=4; -int MINORVERSION=9; - -struct SEM Semaphore = {0, 0, 0, 0}; -struct SEM APISemaphore = {0, 0, 0, 0}; -int SemHeldByAPI = 0; -int LastSemGets = 0; -UINT Sem_eax = 0; -UINT Sem_ebx = 0; -UINT Sem_ecx = 0; -UINT Sem_edx = 0; -UINT Sem_esi = 0; -UINT Sem_edi = 0; - - -#define GetSemaphore(Semaphore,ID) _GetSemaphore(Semaphore, ID, __FILE__, __LINE__) -void _GetSemaphore(struct SEM * Semaphore, int ID, char * File, int Line); -void FreeSemaphore(struct SEM * Semaphore); - -DllExport void * BPQHOSTAPIPTR = &BPQHOSTAPI; -//DllExport long MONDECODEPTR = (long)&MONDECODE; - -extern UCHAR BPQDirectory[]; -extern UCHAR LogDirectory[]; -extern UCHAR BPQProgramDirectory[]; - -static char BPQWinMsg[] = "BPQWindowMessage"; - -static char ClassName[] = "BPQMAINWINDOW"; - -HKEY REGTREE = HKEY_CURRENT_USER; -char REGTREETEXT[100] = "HKEY_CURRENT_USER"; - -UINT BPQMsg=0; - -#define MAXLINELEN 120 -#define MAXSCREENLEN 50 - -#define BGCOLOUR RGB(236,233,216) - -HBRUSH bgBrush = NULL; - -//int LINELEN=120; -//int SCREENLEN=50; - -//char Screen[MAXLINELEN*MAXSCREENLEN]={0}; - -//int lineno=0; -//int col=0; - -#define REPORTINTERVAL 15 * 549; // Magic Ticks Per Minute for PC's nominal 100 ms timer -int ReportTimer = 0; - -HANDLE OpenConfigFile(char * file); - -VOID SetupBPQDirectory(); -VOID SendLocation(); - -//uintptr_t _beginthread(void(*start_address)(), unsigned stack_size, int arglist); - -#define TRAY_ICON_ID 1 // ID number for the Notify Icon -#define MY_TRAY_ICON_MESSAGE WM_APP // the message ID sent to our window - -NOTIFYICONDATA niData; - -int SetupConsoleWindow(); - -BOOL StartMinimized=FALSE; -BOOL MinimizetoTray=TRUE; - -BOOL StatusMinimized = FALSE; -BOOL ConsoleMinimized = FALSE; - -HMENU trayMenu=0; - -HWND hConsWnd = NULL, hWndCons = NULL, hWndBG = NULL, ClientWnd = NULL, FrameWnd = NULL, StatusWnd = NULL; - -BOOL FrameMaximized = FALSE; - -BOOL IGateEnabled = TRUE; -extern int ISDelayTimer; // Time before trying to reopen APRS-IS link -extern int ISPort; - -UINT * WINMORTraceQ = NULL; -UINT * SetWindowTextQ = NULL; - -static RECT Rect = {100,100,400,400}; // Console Window Position -RECT FRect = {100,100,800,600}; // Frame -static RECT StatusRect = {100,100,850,500}; // Status Window - -DllExport int APIENTRY DumpSystem(); -DllExport int APIENTRY SaveNodes (); -DllExport int APIENTRY ClearNodes (); -DllExport int APIENTRY SetupTrayIcon(); - -#define Q_REM(s) _Q_REM(s, __FILE__, __LINE__) - -VOID * _Q_REM(VOID *Q, char * File, int Line); - -UINT ReleaseBuffer(UINT *BUFF); - - -VOID CALLBACK TimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime ); - -DllExport int APIENTRY DeallocateStream(int stream); - -int VECTORLENGTH = sizeof (struct _BPQVECSTRUC); - -int FirstEntry = 1; -BOOL CloseLast = TRUE; // If the user started BPQ32.exe, don't close it when other programs close -BOOL Closing = FALSE; // Set if Close All called - prevents respawning bpq32.exe - -BOOL BPQ32_EXE; // Set if Process is running BPQ32.exe. Not initialised. - // Used to Kill surplus BPQ32.exe processes - -DWORD Our_PID; // Our Process ID - local variable - -void * InitDone = 0; -int FirstInitDone = 0; -int PerlReinit = 0; -UINT_PTR TimerHandle = 0; -UINT_PTR SessHandle = 0; - -BOOL EventsEnabled = 0; - -unsigned int TimerInst = 0xffffffff; - -HANDLE hInstance = 0; - -int AttachedProcesses = 0; -int AttachingProcess = 0; -HINSTANCE hIPModule = 0; -HINSTANCE hRigModule = 0; - -BOOL ReconfigFlag = FALSE; -BOOL RigReconfigFlag = FALSE; -BOOL APRSReconfigFlag = FALSE; -BOOL CloseAllNeeded = FALSE; -BOOL NeedWebMailRefresh = FALSE; - -int AttachedPIDList[100] = {0}; - -HWND hWndArray[100] = {0}; -int PIDArray[100] = {0}; -char PopupText[30][100] = {""}; - -// Next 3 should be uninitialised so they are local to each process - -UCHAR MCOM; -UCHAR MTX; // Top bit indicates use local time -uint64_t MMASK; -UCHAR MUIONLY; - -UCHAR AuthorisedProgram; // Local Variable. Set if Program is on secure list - -char pgm[256]; // Uninitialised so per process - -HANDLE Mutex; - -BOOL PartLine = FALSE; -int pindex = 0; -DWORD * WritetoConsoleQ; - - -LARGE_INTEGER lpFrequency = {0}; -LARGE_INTEGER lastRunTime; -LARGE_INTEGER currentTime; - -int ticksPerMillisec; -int interval; - - -VOID CALLBACK SetupTermSessions(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime); - - -TIMERPROC lpTimerFunc = (TIMERPROC) TimerProc; -TIMERPROC lpSetupTermSessions = (TIMERPROC) SetupTermSessions; - - -BOOL ProcessConfig(); -VOID FreeConfig(); - -DllExport int APIENTRY WritetoConsole(char * buff); - -BOOLEAN CheckifBPQ32isLoaded(); -BOOLEAN StartBPQ32(); -DllExport VOID APIENTRY Send_AX(VOID * Block, DWORD len, UCHAR Port); -BOOL LoadIPDriver(); -BOOL Send_IP(VOID * Block, DWORD len); -VOID CheckforLostProcesses(); -BOOL LoadRigDriver(); -VOID SaveConfig(); -VOID CreateRegBackup(); -VOID ResolveUpdateThread(); -VOID OpenReportingSockets(); -DllExport VOID APIENTRY CloseAllPrograms(); -DllExport BOOL APIENTRY SaveReg(char * KeyIn, HANDLE hFile); -int upnpClose(); - -BOOL IPActive = FALSE; -extern BOOL IPRequired; -BOOL PMActive = FALSE; -extern BOOL PMRequired; -BOOL RigRequired = TRUE; -BOOL RigActive = FALSE; -BOOL APRSActive = FALSE; -BOOL AGWActive = FALSE; -BOOL needAIS = FALSE; -int needADSB = 0; - -extern int AGWPort; - -Tell_Sessions(); - - -typedef int (WINAPI FAR *FARPROCX)(); - -FARPROCX CreateToolHelp32SnapShotPtr; -FARPROCX Process32Firstptr; -FARPROCX Process32Nextptr; - -void LoadToolHelperRoutines() -{ - HINSTANCE ExtDriver=0; - int err; - char msg[100]; - - ExtDriver=LoadLibrary("kernel32.dll"); - - if (ExtDriver == NULL) - { - err=GetLastError(); - sprintf(msg,"BPQ32 Error loading kernel32.dll - Error code %d\n", err); - OutputDebugString(msg); - return; - } - - CreateToolHelp32SnapShotPtr = (FARPROCX)GetProcAddress(ExtDriver,"CreateToolhelp32Snapshot"); - Process32Firstptr = (FARPROCX)GetProcAddress(ExtDriver,"Process32First"); - Process32Nextptr = (FARPROCX)GetProcAddress(ExtDriver,"Process32Next"); - - if (CreateToolHelp32SnapShotPtr == 0) - { - err=GetLastError(); - sprintf(msg,"BPQ32 Error getting CreateToolhelp32Snapshot entry point - Error code %d\n", err); - OutputDebugString(msg); - return; - } -} - -BOOL GetProcess(int ProcessID, char * Program) -{ - HANDLE hProcessSnap; - PROCESSENTRY32 pe32; - int p; - - if (CreateToolHelp32SnapShotPtr==0) - { - return (TRUE); // Routine not available - } - // Take a snapshot of all processes in the system. - hProcessSnap = (HANDLE)CreateToolHelp32SnapShotPtr(TH32CS_SNAPPROCESS, 0); - if( hProcessSnap == INVALID_HANDLE_VALUE ) - { - OutputDebugString( "CreateToolhelp32Snapshot (of processes) Failed\n" ); - return( FALSE ); - } - - // Set the size of the structure before using it. - pe32.dwSize = sizeof( PROCESSENTRY32 ); - - // Retrieve information about the first process, - // and exit if unsuccessful - if( !Process32Firstptr( hProcessSnap, &pe32 ) ) - { - OutputDebugString( "Process32First Failed\n" ); // Show cause of failure - CloseHandle( hProcessSnap ); // Must clean up the snapshot object! - return( FALSE ); - } - - // Now walk the snapshot of processes, and - // display information about each process in turn - do - { - if (ProcessID==pe32.th32ProcessID) - { - // if running on 98, program contains the full path - remove it - - for (p = (int)strlen(pe32.szExeFile); p >= 0; p--) - { - if (pe32.szExeFile[p]=='\\') - { - break; - } - } - p++; - - sprintf(Program,"%s", &pe32.szExeFile[p]); - CloseHandle( hProcessSnap ); - return( TRUE ); - } - - } while( Process32Nextptr( hProcessSnap, &pe32 ) ); - - - sprintf(Program,"PID %d Not Found", ProcessID); - CloseHandle( hProcessSnap ); - return(FALSE); -} - -BOOL IsProcess(int ProcessID) -{ - // Check that Process exists - - HANDLE hProcessSnap; - PROCESSENTRY32 pe32; - - if (CreateToolHelp32SnapShotPtr==0) return (TRUE); // Routine not available - - hProcessSnap = (HANDLE)CreateToolHelp32SnapShotPtr(TH32CS_SNAPPROCESS, 0); - - if( hProcessSnap == INVALID_HANDLE_VALUE ) - { - OutputDebugString( "CreateToolhelp32Snapshot (of processes) Failed\n" ); - return(TRUE); // Don't know, so assume ok - } - - pe32.dwSize = sizeof( PROCESSENTRY32 ); - - if( !Process32Firstptr( hProcessSnap, &pe32 ) ) - { - OutputDebugString( "Process32First Failed\n" ); // Show cause of failure - CloseHandle( hProcessSnap ); // Must clean up the snapshot object! - return(TRUE); // Don't know, so assume ok - } - - do - { - if (ProcessID==pe32.th32ProcessID) - { - CloseHandle( hProcessSnap ); - return( TRUE ); - } - - } while( Process32Nextptr( hProcessSnap, &pe32 ) ); - - CloseHandle( hProcessSnap ); - return(FALSE); -} - -#include "DbgHelp.h" - -VOID MonitorThread(int x) -{ - // Thread to detect killed processes. Runs in process owning timer. - - // Obviously can't detect loss of timer owning thread! - - do - { - if (Semaphore.Gets == LastSemGets && Semaphore.Flag) - { - // It is stuck - try to release - - Debugprintf ("Semaphore locked - Process ID = %d, Held By %d from %s Line %d", - Semaphore.SemProcessID, SemHeldByAPI, Semaphore.File, Semaphore.Line); - - // Write a minidump - - WriteMiniDump(); - - Semaphore.Flag = 0; - } - - LastSemGets = Semaphore.Gets; - - Sleep(30000); - CheckforLostProcesses(); - - } while (TRUE); -} - -VOID CheckforLostProcesses() -{ - UCHAR buff[100]; - char Log[80]; - int i, n, ProcessID; - - for (n=0; n < AttachedProcesses; n++) - { - ProcessID=AttachedPIDList[n]; - - if (!IsProcess(ProcessID)) - { - // Process has died - Treat as a detach - - sprintf(Log,"BPQ32 Process %d Died\n", ProcessID); - OutputDebugString(Log); - - // Remove Tray Icon Entry - - for( i = 0; i < 100; ++i ) - { - if (PIDArray[i] == ProcessID) - { - hWndArray[i] = 0; - sprintf(Log,"BPQ32 Removing Tray Item %s\n", PopupText[i]); - OutputDebugString(Log); - DeleteMenu(trayMenu,TRAYBASEID+i,MF_BYCOMMAND); - } - } - - // If process had the semaphore, release it - - if (Semaphore.Flag == 1 && ProcessID == Semaphore.SemProcessID) - { - OutputDebugString("BPQ32 Process was holding Semaphore - attempting recovery\r\n"); - Debugprintf("Last Sem Call %d %x %x %x %x %x %x", SemHeldByAPI, - Sem_eax, Sem_ebx, Sem_ecx, Sem_edx, Sem_esi, Sem_edi); - - Semaphore.Flag = 0; - SemHeldByAPI = 0; - } - - for (i=1;i<65;i++) - { - if (BPQHOSTVECTOR[i-1].STREAMOWNER == AttachedPIDList[n]) - { - DeallocateStream(i); - } - } - - if (TimerInst == ProcessID) - { - KillTimer(NULL,TimerHandle); - TimerHandle=0; - TimerInst=0xffffffff; -// Tell_Sessions(); - OutputDebugString("BPQ32 Process was running timer \n"); - - if (MinimizetoTray) - Shell_NotifyIcon(NIM_DELETE,&niData); - - - } - - // Remove this entry from PID List - - for (i=n; i< AttachedProcesses; i++) - { - AttachedPIDList[i]=AttachedPIDList[i+1]; - } - AttachedProcesses--; - - sprintf(buff,"BPQ32 Lost Process - %d Process(es) Attached\n", AttachedProcesses); - OutputDebugString(buff); - } - } -} -VOID MonitorTimerThread(int x) -{ - // Thread to detect killed timer process. Runs in all other BPQ32 processes. - - do { - - Sleep(60000); - - if (TimerInst != 0xffffffff && !IsProcess(TimerInst)) - { - // Timer owning Process has died - Force a new timer to be created - // New timer thread will detect lost process and tidy up - - Debugprintf("BPQ32 Process %d with Timer died", TimerInst); - - // If process was holding the semaphore, release it - - if (Semaphore.Flag == 1 && TimerInst == Semaphore.SemProcessID) - { - OutputDebugString("BPQ32 Process was holding Semaphore - attempting recovery\r\n"); - Debugprintf("Last Sem Call %d %x %x %x %x %x %x", SemHeldByAPI, - Sem_eax, Sem_ebx, Sem_ecx, Sem_edx, Sem_esi, Sem_edi); - Semaphore.Flag = 0; - SemHeldByAPI = 0; - } - -// KillTimer(NULL,TimerHandle); -// TimerHandle=0; -// TimerInst=0xffffffff; -// Tell_Sessions(); - - CheckforLostProcesses(); // Normally only done in timer thread, which is now dead - - // Timer can only run in BPQ32.exe - - TimerInst=0xffffffff; // So we dont keep doing it - TimerHandle = 0; // So new process attaches - - if (Closing == FALSE && AttachingProcess == FALSE) - { - OutputDebugString("BPQ32 Reloading BPQ32.exe\n"); - StartBPQ32(); - } - -// if (MinimizetoTray) -// Shell_NotifyIcon(NIM_DELETE,&niData); - } - - } while (TRUE); -} - -VOID WritetoTraceSupport(struct TNCINFO * TNC, char * Msg, int Len); - -VOID TimerProcX(); - -VOID CALLBACK TimerProc( - HWND hwnd, // handle of window for timer messages - UINT uMsg, // WM_TIMER message - UINT idEvent, // timer identifier - DWORD dwTime) // current system time -{ - KillTimer(NULL,TimerHandle); - TimerProcX(); - TimerHandle = SetTimer(NULL,0,100,lpTimerFunc); -} -VOID TimerProcX() -{ - struct _EXCEPTION_POINTERS exinfo; - - // - // Get semaphore before proceeeding - // - - GetSemaphore(&Semaphore, 2); - - // Get time since last run - - QueryPerformanceCounter(¤tTime); - - interval = (int)(currentTime.QuadPart - lastRunTime.QuadPart) / ticksPerMillisec; - lastRunTime.QuadPart = currentTime.QuadPart; - - //Debugprintf("%d", interval); - - // Process WINMORTraceQ - - while (WINMORTraceQ) - { - UINT * Buffer = Q_REM(&WINMORTraceQ); - struct TNCINFO * TNC = (struct TNCINFO * )Buffer[1]; - int Len = Buffer[2]; - char * Msg = (char *)&Buffer[3]; - - WritetoTraceSupport(TNC, Msg, Len); - RelBuff(Buffer); - } - - if (SetWindowTextQ) - SetWindowTextSupport(); - - while (WritetoConsoleQ) - { - UINT * Buffer = Q_REM(&WritetoConsoleQ); - WritetoConsoleSupport((char *)&Buffer[2]); - RelBuff(Buffer); - } - - strcpy(EXCEPTMSG, "Timer ReconfigProcessing"); - - __try - { - - if (trayMenu == NULL) - SetupTrayIcon(); - - // See if reconfigure requested - - if (CloseAllNeeded) - { - CloseAllNeeded = FALSE; - CloseAllPrograms(); - } - - if (ReconfigFlag) - { - // Only do it it timer owning process, or we could get in a real mess! - - if(TimerInst == GetCurrentProcessId()) - { - int i; - BPQVECSTRUC * HOSTVEC; - PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; - WSADATA WsaData; // receives data from WSAStartup - RECT cRect; - - ReconfigFlag = FALSE; - - SetupBPQDirectory(); - - WritetoConsole("Reconfiguring ...\n\n"); - OutputDebugString("BPQ32 Reconfiguring ...\n"); - - GetWindowRect(FrameWnd, &FRect); - - SaveWindowPos(70); // Rigcontrol - - for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External - { - if (PORTVEC->PORT_EXT_ADDR) - { - SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); - SaveAXIPWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); - CloseDriverWindow(PORTVEC->PORTCONTROL.PORTNUMBER); - PORTVEC->PORT_EXT_ADDR(5,PORTVEC->PORTCONTROL.PORTNUMBER, NULL); // Close External Ports - } - } - PORTVEC->PORTCONTROL.PORTCLOSECODE(&PORTVEC->PORTCONTROL); - PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; - } - - IPClose(); - PMClose(); - APRSClose(); - Rig_Close(); - CloseTNCEmulator(); - if (AGWActive) - AGWAPITerminate(); - - WSACleanup(); - - WL2KReports = NULL; - - Sleep(2000); - - WSAStartup(MAKEWORD(2, 0), &WsaData); - - Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); - Consoleprintf(VerCopyright); - - Start(); - - INITIALISEPORTS(); // Restart Ports - - SetApplPorts(); - - FreeConfig(); - - for (i=1; i<68; i++) // Include Telnet, APRS and IP Vec - { - HOSTVEC=&BPQHOSTVECTOR[i-1]; - - HOSTVEC->HOSTTRACEQ=0; // Clear header (pool has been reinitialized - - if (HOSTVEC->HOSTSESSION !=0) - { - // Had a connection - - HOSTVEC->HOSTSESSION=0; - HOSTVEC->HOSTFLAGS |=3; // Disconnected - - PostMessage(HOSTVEC->HOSTHANDLE, BPQMsg, i, 4); - } - } - - // Free the APRS Appl Q - - APPL_Q = 0; - - OpenReportingSockets(); - - WritetoConsole("\n\nReconfiguration Complete\n"); - - if (IPRequired) IPActive = Init_IP(); - if (PMRequired) PMActive = Init_PM(); - - APRSActive = Init_APRS(); - - if (ISPort == 0) - IGateEnabled = 0; - - CheckDlgButton(hConsWnd, IDC_ENIGATE, IGateEnabled); - - GetClientRect(hConsWnd, &cRect); - MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); - if (APRSActive) - MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); - else - { - ShowWindow(GetDlgItem(hConsWnd, IDC_GPS), SW_HIDE); - MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); - } - InvalidateRect(hConsWnd, NULL, TRUE); - - RigActive = Rig_Init(); - - if (NUMBEROFTNCPORTS) - { - FreeSemaphore(&Semaphore); - InitializeTNCEmulator(); - GetSemaphore(&Semaphore, 0); - } - - FreeSemaphore(&Semaphore); - AGWActive = AGWAPIInit(); - GetSemaphore(&Semaphore, 0); - - OutputDebugString("BPQ32 Reconfiguration Complete\n"); - } - } - - - if (RigReconfigFlag) - { - // Only do it it timer owning process, or we could get in a real mess! - - if(TimerInst == GetCurrentProcessId()) - { - RigReconfigFlag = FALSE; - CloseDriverWindow(70); - Rig_Close(); - Sleep(6000); // Allow any CATPTT, HAMLIB and FLRIG threads to close - RigActive = Rig_Init(); - - WritetoConsole("Rigcontrol Reconfiguration Complete\n"); - } - } - - if (APRSReconfigFlag) - { - // Only do it it timer owning process, or we could get in a real mess! - - if(TimerInst == GetCurrentProcessId()) - { - APRSReconfigFlag = FALSE; - APRSClose(); - APRSActive = Init_APRS(); - - WritetoConsole("APRS Reconfiguration Complete\n"); - } - } - - } - #include "StdExcept.c" - - if (Semaphore.Flag && Semaphore.SemProcessID == GetCurrentProcessId()) - FreeSemaphore(&Semaphore); - - } - - strcpy(EXCEPTMSG, "Timer Processing"); - - __try - { - if (IPActive) Poll_IP(); - if (PMActive) Poll_PM(); - if (RigActive) Rig_Poll(); - - if (NeedWebMailRefresh) - DoRefreshWebMailIndex(); - - CheckGuardZone(); - - if (APRSActive) - { - Poll_APRS(); - CheckGuardZone(); - } - - CheckWL2KReportTimer(); - - CheckGuardZone(); - - TIMERINTERRUPT(); - - CheckGuardZone(); - - FreeSemaphore(&Semaphore); // SendLocation needs to get the semaphore - - if (NUMBEROFTNCPORTS) - TNCTimer(); - - if (AGWActive) - Poll_AGW(); - - DRATSPoll(); - RHPPoll(); - - CheckGuardZone(); - - strcpy(EXCEPTMSG, "HTTP Timer Processing"); - - HTTPTimer(); - - CheckGuardZone(); - - strcpy(EXCEPTMSG, "WL2K Report Timer Processing"); - - if (ReportTimer) - { - ReportTimer--; - - if (ReportTimer == 0) - { - ReportTimer = REPORTINTERVAL; - SendLocation(); - } - } - } - - #include "StdExcept.c" - - if (Semaphore.Flag && Semaphore.SemProcessID == GetCurrentProcessId()) - FreeSemaphore(&Semaphore); - - } - - CheckGuardZone(); - - return; -} - -HANDLE NPHandle; - -int (WINAPI FAR *GetModuleFileNameExPtr)() = NULL; -int (WINAPI FAR *EnumProcessesPtr)() = NULL; - -FirstInit() -{ - WSADATA WsaData; // receives data from WSAStartup - HINSTANCE ExtDriver=0; - RECT cRect; - - - // First Time Ports and Timer init - - // Moved from DLLINIT to sort out perl problem, and meet MS Guidelines on minimising DLLMain - - // Call wsastartup - most systems need winsock, and duplicate statups could be a problem - - WSAStartup(MAKEWORD(2, 0), &WsaData); - - // Load Psapi.dll if possible - - ExtDriver=LoadLibrary("Psapi.dll"); - - SetupTrayIcon(); - - if (ExtDriver) - { - GetModuleFileNameExPtr = (FARPROCX)GetProcAddress(ExtDriver,"GetModuleFileNameExA"); - EnumProcessesPtr = (FARPROCX)GetProcAddress(ExtDriver,"EnumProcesses"); - } - - timeLoadedMS = GetTickCount(); - - INITIALISEPORTS(); - - OpenReportingSockets(); - - WritetoConsole("\n"); - WritetoConsole("Port Initialisation Complete\n"); - - if (IPRequired) IPActive = Init_IP(); - if (PMRequired) PMActive = Init_PM(); - - APRSActive = Init_APRS(); - - if (APRSActive) - { - hWndBG = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 0,0,40,546, hConsWnd, NULL, hInstance, NULL); - - CreateWindowEx(0, "STATIC", "Enable IGate", WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, - 8,0,90,24, hConsWnd, (HMENU)-1, hInstance, NULL); - - CreateWindowEx(0, "BUTTON", "", WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP, - 95,1,18,24, hConsWnd, (HMENU)IDC_ENIGATE, hInstance, NULL); - - CreateWindowEx(0, "STATIC", "IGate State - Disconnected", - WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 125, 0, 195, 24, hConsWnd, (HMENU)IGATESTATE, hInstance, NULL); - - CreateWindowEx(0, "STATIC", "IGATE Stats - Msgs 0 Local Stns 0", - WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 320, 0, 240, 24, hConsWnd, (HMENU)IGATESTATS, hInstance, NULL); - - CreateWindowEx(0, "STATIC", "GPS Off", - WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 560, 0, 80, 24, hConsWnd, (HMENU)IDC_GPS, hInstance, NULL); - } - - if (ISPort == 0) - IGateEnabled = 0; - - CheckDlgButton(hConsWnd, IDC_ENIGATE, IGateEnabled); - - GetClientRect(hConsWnd, &cRect); - MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); - if (APRSActive) - MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); - else - { - ShowWindow(GetDlgItem(hConsWnd, IDC_GPS), SW_HIDE); - MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); - } - InvalidateRect(hConsWnd, NULL, TRUE); - - RigActive = Rig_Init(); - - _beginthread(MonitorThread,0,0); - - TimerHandle=SetTimer(NULL,0,100,lpTimerFunc); - TimerInst=GetCurrentProcessId(); - SessHandle = SetTimer(NULL, 0, 5000, lpSetupTermSessions); - - // If ARIF reporting is enabled write a Trimode Like ini for RMS Analyser - - if (ADIFLogEnabled) - ADIFWriteFreqList(); - - OutputDebugString("BPQ32 Port Initialisation Complete\n"); - - if (needAIS) - initAIS(); - - if (needADSB) - initADSB(); - - return 0; -} - -Check_Timer() -{ - if (Closing) - return 0; - - if (Semaphore.Flag) - return 0; - - if (InitDone == (void *)-1) - { - GetSemaphore(&Semaphore, 3); - Sleep(15000); - FreeSemaphore(&Semaphore); - exit (0); - } - - if (FirstInitDone == 0) - { - GetSemaphore(&Semaphore, 3); - - if (_stricmp(pgm, "bpq32.exe") == 0) - { - FirstInit(); - FreeSemaphore(&Semaphore); - if (NUMBEROFTNCPORTS) - InitializeTNCEmulator(); - - AGWActive = AGWAPIInit(); - FirstInitDone=1; // Only init in BPQ32.exe - return 0; - } - else - { - FreeSemaphore(&Semaphore); - return 0; - } - } - - if (TimerHandle == 0 && FirstInitDone == 1) - { - WSADATA WsaData; // receives data from WSAStartup - HINSTANCE ExtDriver=0; - RECT cRect; - - // Only attach timer to bpq32.exe - - if (_stricmp(pgm, "bpq32.exe") != 0) - { - return 0; - } - - GetSemaphore(&Semaphore, 3); - OutputDebugString("BPQ32 Reinitialising External Ports and Attaching Timer\n"); - - if (!ProcessConfig()) - { - ShowWindow(hConsWnd, SW_RESTORE); - SendMessage(hConsWnd, WM_PAINT, 0, 0); - SetForegroundWindow(hConsWnd); - - InitDone = (void *)-1; - FreeSemaphore(&Semaphore); - - MessageBox(NULL,"Configuration File Error","BPQ32",MB_ICONSTOP); - - exit (0); - } - - GetVersionInfo("bpq32.dll"); - - SetupConsoleWindow(); - - Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); - Consoleprintf(VerCopyright); - Consoleprintf("Reinitialising..."); - - SetupBPQDirectory(); - - Sleep(1000); // Allow time for sockets to close - - WSAStartup(MAKEWORD(2, 0), &WsaData); - - // Load Psapi.dll if possible - - ExtDriver = LoadLibrary("Psapi.dll"); - - SetupTrayIcon(); - - if (ExtDriver) - { - GetModuleFileNameExPtr = (FARPROCX)GetProcAddress(ExtDriver,"GetModuleFileNameExA"); - EnumProcessesPtr = (FARPROCX)GetProcAddress(ExtDriver,"EnumProcesses"); - } - - Start(); - - INITIALISEPORTS(); - - OpenReportingSockets(); - - NODESINPROGRESS = 0; - CURRENTNODE = 0; - - SetApplPorts(); - - WritetoConsole("\n\nPort Reinitialisation Complete\n"); - - BPQMsg = RegisterWindowMessage(BPQWinMsg); - - CreateMutex(NULL,TRUE,"BPQLOCKMUTEX"); - -// NPHandle=CreateNamedPipe("\\\\.\\pipe\\BPQ32pipe", -// PIPE_ACCESS_DUPLEX,0,64,4096,4096,1000,NULL); - - if (IPRequired) IPActive = Init_IP(); - if (PMRequired) PMActive = Init_PM(); - - RigActive = Rig_Init(); - APRSActive = Init_APRS(); - - if (ISPort == 0) - IGateEnabled = 0; - - CheckDlgButton(hConsWnd, IDC_ENIGATE, IGateEnabled); - - GetClientRect(hConsWnd, &cRect); - MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); - - if (APRSActive) - MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); - else - { - ShowWindow(GetDlgItem(hConsWnd, IDC_GPS), SW_HIDE); - MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); - } - InvalidateRect(hConsWnd, NULL, TRUE); - - FreeConfig(); - - _beginthread(MonitorThread,0,0); - - ReportTimer = 0; - - OpenReportingSockets(); - - FreeSemaphore(&Semaphore); - - if (NUMBEROFTNCPORTS) - InitializeTNCEmulator(); - - AGWActive = AGWAPIInit(); - - if (StartMinimized) - if (MinimizetoTray) - ShowWindow(FrameWnd, SW_HIDE); - else - ShowWindow(FrameWnd, SW_SHOWMINIMIZED); - else - ShowWindow(FrameWnd, SW_RESTORE); - - TimerHandle=SetTimer(NULL,0,100,lpTimerFunc); - TimerInst=GetCurrentProcessId(); - SessHandle = SetTimer(NULL, 0, 5000, lpSetupTermSessions); - - return (1); - } - - return (0); -} - -DllExport INT APIENTRY CheckTimer() -{ - return Check_Timer(); -} - -Tell_Sessions() -{ - // - // Post a message to all listening sessions, so they call the - // API, and cause a new timer to be allocated - // - HWND hWnd; - int i; - - for (i=1;i<65;i++) - { - if (BPQHOSTVECTOR[i-1].HOSTFLAGS & 0x80) - { - hWnd = BPQHOSTVECTOR[i-1].HOSTHANDLE; - PostMessage(hWnd, BPQMsg,i, 1); - PostMessage(hWnd, BPQMsg,i, 2); - } - } - return (0); -} - -BOOL APIENTRY DllMain(HANDLE hInst, DWORD ul_reason_being_called, LPVOID lpReserved) -{ - DWORD n; - char buf[350]; - - int i; - unsigned int ProcessID; - - OSVERSIONINFO osvi; - - memset(&osvi, 0, sizeof(OSVERSIONINFO)); - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - - GetVersionEx(&osvi); - - - switch( ul_reason_being_called ) - { - case DLL_PROCESS_ATTACH: - - if (sizeof(HDLCDATA) > PORTENTRYLEN + 200) // 200 bytes of Hardwaredata - { - // Catastrophic - Refuse to load - - MessageBox(NULL,"BPQ32 Too much HDLC data - Recompile","BPQ32", MB_OK); - return 0; - } - - if (sizeof(struct KISSINFO) > PORTENTRYLEN + 200) // 200 bytes of Hardwaredata - { - // Catastrophic - Refuse to load - - MessageBox(NULL,"BPQ32 Too much KISS data - Recompile","BPQ32", MB_OK); - return 0; - } - - if (sizeof(struct _EXTPORTDATA) > PORTENTRYLEN + 200) // 200 bytes of Hardwaredata - { - // Catastrophic - Refuse to load - - MessageBox(NULL,"BPQ32 Too much _EXTPORTDATA data - Recompile","BPQ32", MB_OK); - return 0; - } - - if (sizeof(LINKTABLE) != LINK_TABLE_LEN) - { - // Catastrophic - Refuse to load - - MessageBox(NULL,"L2 LINK Table .c and .asm mismatch - fix and rebuild","BPQ32", MB_OK); - return 0; - } - if (sizeof(struct ROUTE) != ROUTE_LEN) - { - // Catastrophic - Refuse to load - - MessageBox(NULL,"ROUTE Table .c and .asm mismatch - fix and rebuild", "BPQ32", MB_OK); - return 0; - } - - if (sizeof(struct DEST_LIST) != DEST_LIST_LEN) - { - // Catastrophic - Refuse to load - - MessageBox(NULL,"NODES Table .c and .asm mismatch - fix and rebuild", "BPQ32", MB_OK); - return 0; - } - - GetSemaphore(&Semaphore, 4); - - BPQHOSTVECPTR = &BPQHOSTVECTOR[0]; - - LoadToolHelperRoutines(); - - Our_PID = GetCurrentProcessId(); - - QueryPerformanceFrequency(&lpFrequency); - - ticksPerMillisec = (int)lpFrequency.QuadPart / 1000; - - lastRunTime.QuadPart = lpFrequency.QuadPart; - - GetProcess(Our_PID, pgm); - - if (_stricmp(pgm, "regsvr32.exe") == 0 || _stricmp(pgm, "bpqcontrol.exe") == 0) - { - AttachedProcesses++; // We will get a detach - FreeSemaphore(&Semaphore); - return 1; - } - - if (_stricmp(pgm,"BPQ32.exe") == 0) - BPQ32_EXE = TRUE; - - if (_stricmp(pgm,"BPQMailChat.exe") == 0) - IncludesMail = TRUE; - - if (_stricmp(pgm,"BPQMail.exe") == 0) - IncludesMail = TRUE; - - if (_stricmp(pgm,"BPQChat.exe") == 0) - IncludesChat = TRUE; - - if (FirstEntry) // If loaded by BPQ32.exe, dont close it at end - { - FirstEntry = 0; - if (BPQ32_EXE) - CloseLast = FALSE; - } - else - { - if (BPQ32_EXE && AttachingProcess == 0) - { - AttachedProcesses++; // We will get a detach - FreeSemaphore(&Semaphore); - MessageBox(NULL,"BPQ32.exe is already running\r\n\r\nIt should only be run once", "BPQ32", MB_OK); - return 0; - } - } - - if (_stricmp(pgm,"BPQTelnetServer.exe") == 0) - { - MessageBox(NULL,"BPQTelnetServer is no longer supported\r\n\r\nUse the TelnetServer in BPQ32.dll", "BPQ32", MB_OK); - AttachedProcesses++; // We will get a detach - FreeSemaphore(&Semaphore); - return 0; - } - - if (_stricmp(pgm,"BPQUIUtil.exe") == 0) - { - MessageBox(NULL,"BPQUIUtil is now part of BPQ32.dll\r\nBPQUIUtil.exe cannot be run\r\n", "BPQ32", MB_OK); - AttachedProcesses++; // We will get a detach - FreeSemaphore(&Semaphore); - return 0; - } - - if (_stricmp(pgm,"BPQMailChat.exe") == 0) - { - MessageBox(NULL,"BPQMailChat is obsolete. Run BPQMail.exe and/or BPQChat.exe instead", "BPQ32", MB_OK); - AttachedProcesses++; // We will get a detach - FreeSemaphore(&Semaphore); - return 0; - } - AuthorisedProgram = TRUE; - - if (InitDone == 0) - { -// #pragma warning(push) -// #pragma warning(disable : 4996) - -// if (_winver < 0x0600) -// #pragma warning(pop) -// { -// // Below Vista -// -// REGTREE = HKEY_LOCAL_MACHINE; -// strcpy(REGTREETEXT, "HKEY_LOCAL_MACHINE"); -// } - - hInstance=hInst; - - Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"BPQLOCKMUTEX"); - - if (Mutex != NULL) - { - OutputDebugString("Another BPQ32.dll is loaded\n"); - i=MessageBox(NULL,"BPQ32 DLL already loaded from another directory\nIf you REALLY want this, hit OK, else hit Cancel","BPQ32",MB_OKCANCEL); - FreeSemaphore(&Semaphore); - - if (i != IDOK) return (0); - - CloseHandle(Mutex); - } - - if (!BPQ32_EXE) - { - if (CheckifBPQ32isLoaded() == FALSE) // Start BPQ32.exe if needed - { - // Wasn't Loaded, so we have started it, and should let it init system - - goto SkipInit; - } - } - - GetVersionInfo("bpq32.dll"); - - sprintf (SIGNONMSG, "G8BPQ AX25 Packet Switch System Version %s %s\r\n%s\r\n", - TextVerstring, Datestring, VerCopyright); - - SESSHDDRLEN = sprintf(SESSIONHDDR, "G8BPQ Network System %s for Win32 (", TextVerstring); - - SetupConsoleWindow(); - SetupBPQDirectory(); - - if (!ProcessConfig()) - { - StartMinimized = FALSE; - MinimizetoTray = FALSE; - ShowWindow(FrameWnd, SW_MAXIMIZE); - ShowWindow(hConsWnd, SW_MAXIMIZE); - ShowWindow(StatusWnd, SW_HIDE); - - SendMessage(hConsWnd, WM_PAINT, 0, 0); - SetForegroundWindow(hConsWnd); - - InitDone = (void *)-1; - FreeSemaphore(&Semaphore); - - MessageBox(NULL,"Configuration File Error\r\nProgram will close in 15 seconds","BPQ32",MB_ICONSTOP); - - return (0); - } - - Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); - Consoleprintf(VerCopyright); - - if (Start() !=0) - { - Sleep(3000); - FreeSemaphore(&Semaphore); - return (0); - } - else - { - SetApplPorts(); - - GetUIConfig(); - - InitDone = &InitDone; - BPQMsg = RegisterWindowMessage(BPQWinMsg); -// TimerHandle=SetTimer(NULL,0,100,lpTimerFunc); -// TimerInst=GetCurrentProcessId(); - -/* Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"BPQLOCKMUTEX"); - - if (Mutex != NULL) - { - OutputDebugString("Another BPQ32.dll is loaded\n"); - MessageBox(NULL,"BPQ32 DLL already loaded from another directory","BPQ32",MB_ICONSTOP); - FreeSemaphore(&Semaphore); - return (0); - } - -*/ - Mutex=CreateMutex(NULL,TRUE,"BPQLOCKMUTEX"); - -// CreatePipe(&H1,&H2,NULL,1000); - -// GetLastError(); - -// NPHandle=CreateNamedPipe("\\\\.\\pipe\\BPQ32pipe", -// PIPE_ACCESS_DUPLEX,0,64,4096,4096,1000,NULL); - -// GetLastError(); - -/* - // - // Read SYSOP password - // - - if (PWTEXT[0] == 0) - { - handle = OpenConfigFile("PASSWORD.BPQ"); - - if (handle == INVALID_HANDLE_VALUE) - { - WritetoConsole("Can't open PASSWORD.BPQ\n"); - PWLen=0; - PWTEXT[0]=0; - } - else - { - ReadFile(handle,PWTEXT,78,&n,NULL); - CloseHandle(handle); - } - } -*/ - for (i=0;PWTEXT[i] > 0x20;i++); //Scan for cr or null - PWLen=i; - - } - } - else - { - if (InitDone != &InitDone) - { - MessageBox(NULL,"BPQ32 DLL already loaded at another address","BPQ32",MB_ICONSTOP); - FreeSemaphore(&Semaphore); - return (0); - } - } - - // Run timer monitor thread in all processes - it is possible for the TImer thread not to be the first thread -SkipInit: - - _beginthread(MonitorTimerThread,0,0); - - FreeSemaphore(&Semaphore); - - AttachedPIDList[AttachedProcesses++] = GetCurrentProcessId(); - - if (_stricmp(pgm,"bpq32.exe") == 0 && AttachingProcess == 1) AttachingProcess = 0; - - GetProcess(GetCurrentProcessId(),pgm); - n=sprintf(buf,"BPQ32 DLL Attach complete - Program %s - %d Process(es) Attached\n",pgm,AttachedProcesses); - OutputDebugString(buf); - - // Set up local variables - - MCOM=1; - MTX=1; - MMASK=0xffffffffffffffff; - -// if (StartMinimized) -// if (MinimizetoTray) -// ShowWindow(FrameWnd, SW_HIDE); -// else -// ShowWindow(FrameWnd, SW_SHOWMINIMIZED); -// else -// ShowWindow(FrameWnd, SW_RESTORE); - - return 1; - - case DLL_THREAD_ATTACH: - - return 1; - - case DLL_THREAD_DETACH: - - return 1; - - case DLL_PROCESS_DETACH: - - if (_stricmp(pgm,"BPQMailChat.exe") == 0) - IncludesMail = FALSE; - - if (_stricmp(pgm,"BPQChat.exe") == 0) - IncludesChat = FALSE; - - ProcessID=GetCurrentProcessId(); - - Debugprintf("BPQ32 Process %d Detaching", ProcessID); - - // Release any streams that the app has failed to release - - for (i=1;i<65;i++) - { - if (BPQHOSTVECTOR[i-1].STREAMOWNER == ProcessID) - { - // If connected, disconnect - - SessionControl(i, 2, 0); - DeallocateStream(i); - } - } - - // Remove any Tray Icon Entries - - for( i = 0; i < 100; ++i ) - { - if (PIDArray[i] == ProcessID) - { - char Log[80]; - hWndArray[i] = 0; - sprintf(Log,"BPQ32 Removing Tray Item %s\n", PopupText[i]); - OutputDebugString(Log); - DeleteMenu(trayMenu,TRAYBASEID+i,MF_BYCOMMAND); - } - } - - if (Mutex) CloseHandle(Mutex); - - // Remove our entry from PID List - - for (i=0; i< AttachedProcesses; i++) - if (AttachedPIDList[i] == ProcessID) - break; - - for (; i< AttachedProcesses; i++) - { - AttachedPIDList[i]=AttachedPIDList[i+1]; - } - - AttachedProcesses--; - - if (TimerInst == ProcessID) - { - PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; - - OutputDebugString("BPQ32 Process with Timer closing\n"); - - // Call Port Close Routines - - for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External - { - if (PORTVEC->PORT_EXT_ADDR && PORTVEC->DLLhandle == NULL) // Don't call if real .dll - it's not there! - { - SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); - SaveAXIPWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); - PORTVEC->PORT_EXT_ADDR(5,PORTVEC->PORTCONTROL.PORTNUMBER, NULL); // Close External Ports - } - } - - PORTVEC->PORTCONTROL.PORTCLOSECODE(&PORTVEC->PORTCONTROL); - - PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; - } - - - IPClose(); - PMClose(); - APRSClose(); - Rig_Close(); - CloseTNCEmulator(); - if (AGWActive) - AGWAPITerminate(); - - upnpClose(); - - WSACleanup(); - WSAGetLastError(); - - if (MinimizetoTray) - Shell_NotifyIcon(NIM_DELETE,&niData); - - if (hConsWnd) DestroyWindow(hConsWnd); - - KillTimer(NULL,TimerHandle); - TimerHandle=0; - TimerInst=0xffffffff; - - if (AttachedProcesses && Closing == FALSE && AttachingProcess == 0) // Other processes - { - OutputDebugString("BPQ32 Reloading BPQ32.exe\n"); - StartBPQ32(); - } - } - else - { - // Not Timer Process - - if (AttachedProcesses == 1 && CloseLast) // Only bpq32.exe left - { - Debugprintf("Only BPQ32.exe running - close it"); - CloseAllNeeded = TRUE; - } - } - - if (AttachedProcesses < 2) - { - if (AUTOSAVE) - SaveNodes(); - if (AUTOSAVEMH) - SaveMH(); - - if (needAIS) - SaveAIS(); - } - if (AttachedProcesses == 0) - { - Closing = TRUE; - KillTimer(NULL,TimerHandle); - - if (MinimizetoTray) - Shell_NotifyIcon(NIM_DELETE,&niData); - - // Unload External Drivers - - { - PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; - - for (i=0;iPORTCONTROL.PORTTYPE == 0x10 && PORTVEC->DLLhandle) - FreeLibrary(PORTVEC->DLLhandle); - - PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; - } - } - } - - GetProcess(GetCurrentProcessId(),pgm); - n=sprintf(buf,"BPQ32 DLL Detach complete - Program %s - %d Process(es) Attached\n",pgm,AttachedProcesses); - OutputDebugString(buf); - - return 1; - } - return 1; -} - -DllExport int APIENTRY CloseBPQ32() -{ - // Unload External Drivers - - PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; - int i; - int ProcessID = GetCurrentProcessId(); - - if (Semaphore.Flag == 1 && ProcessID == Semaphore.SemProcessID) - { - OutputDebugString("BPQ32 Process holding Semaphore called CloseBPQ32 - attempting recovery\r\n"); - Debugprintf("Last Sem Call %d %x %x %x %x %x %x", SemHeldByAPI, - Sem_eax, Sem_ebx, Sem_ecx, Sem_edx, Sem_esi, Sem_edi); - - Semaphore.Flag = 0; - SemHeldByAPI = 0; - } - - if (TimerInst == ProcessID) - { - OutputDebugString("BPQ32 Process with Timer called CloseBPQ32\n"); - - if (MinimizetoTray) - Shell_NotifyIcon(NIM_DELETE,&niData); - - for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External - { - if (PORTVEC->PORT_EXT_ADDR) - { - PORTVEC->PORT_EXT_ADDR(5,PORTVEC->PORTCONTROL.PORTNUMBER, NULL); - } - } - PORTVEC->PORTCONTROL.PORTCLOSECODE(&PORTVEC->PORTCONTROL); - - PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; - } - - KillTimer(NULL,TimerHandle); - TimerHandle=0; - TimerInst=0xffffffff; - - IPClose(); - PMClose(); - APRSClose(); - Rig_Close(); - if (AGWActive) - AGWAPITerminate(); - - upnpClose(); - - CloseTNCEmulator(); - WSACleanup(); - - if (hConsWnd) DestroyWindow(hConsWnd); - - Debugprintf("AttachedProcesses %d ", AttachedProcesses); - - if (AttachedProcesses > 1 && Closing == FALSE && AttachingProcess == 0) // Other processes - { - OutputDebugString("BPQ32 Reloading BPQ32.exe\n"); - StartBPQ32(); - } - } - - return 0; -} - -BOOL CopyReg(HKEY hKeyIn, HKEY hKeyOut); - -VOID SetupBPQDirectory() -{ - HKEY hKey = 0; - HKEY hKeyIn = 0; - HKEY hKeyOut = 0; - int disp; - int retCode,Type,Vallen=MAX_PATH,i; - char msg[512]; - char ValfromReg[MAX_PATH] = ""; - char DLLName[256]="Not Known"; - char LogDir[256]; - char Time[64]; - -/* -•NT4 was/is '4' -•Win 95 is 4.00.950 -•Win 98 is 4.10.1998 -•Win 98 SE is 4.10.2222 -•Win ME is 4.90.3000 -•2000 is NT 5.0.2195 -•XP is actually 5.1 -•Vista is 6.0 -•Win7 is 6.1 - - i = _osver; / Build - i = _winmajor; - i = _winminor; -*/ -/* -#pragma warning(push) -#pragma warning(disable : 4996) - -if (_winver < 0x0600) -#pragma warning(pop) - { - // Below Vista - - REGTREE = HKEY_LOCAL_MACHINE; - strcpy(REGTREETEXT, "HKEY_LOCAL_MACHINE"); - ValfromReg[0] = 0; - } - else -*/ - { - if (_stricmp(pgm, "regsvr32.exe") == 0) - { - Debugprintf("BPQ32 loaded by regsvr32.exe - Registry not copied"); - } - else - { - // If necessary, move reg from HKEY_LOCAL_MACHINE to HKEY_CURRENT_USER - - retCode = RegOpenKeyEx (HKEY_LOCAL_MACHINE, - "SOFTWARE\\G8BPQ\\BPQ32", - 0, - KEY_READ, - &hKeyIn); - - retCode = RegCreateKeyEx(HKEY_CURRENT_USER, "SOFTWARE\\G8BPQ\\BPQ32", 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKeyOut, &disp); - - // See if Version Key exists in HKEY_CURRENT_USER - if it does, we have already done the copy - - Vallen = MAX_PATH; - retCode = RegQueryValueEx(hKeyOut, "Version" ,0 , &Type,(UCHAR *)&msg, &Vallen); - - if (retCode != ERROR_SUCCESS) - if (hKeyIn) - CopyReg(hKeyIn, hKeyOut); - - RegCloseKey(hKeyIn); - RegCloseKey(hKeyOut); - } - } - - GetModuleFileName(hInstance,DLLName,256); - - BPQDirectory[0]=0; - - retCode = RegOpenKeyEx (REGTREE, - "SOFTWARE\\G8BPQ\\BPQ32", - 0, - KEY_QUERY_VALUE, - &hKey); - - if (retCode == ERROR_SUCCESS) - { - // Try "BPQ Directory" - - Vallen = MAX_PATH; - retCode = RegQueryValueEx(hKey,"BPQ Directory",0, - &Type,(UCHAR *)&ValfromReg,&Vallen); - - if (retCode == ERROR_SUCCESS) - { - if (strlen(ValfromReg) == 2 && ValfromReg[0] == '"' && ValfromReg[1] == '"') - ValfromReg[0]=0; - } - - if (ValfromReg[0] == 0) - { - // BPQ Directory absent or = "" - try "Config File Location" - - Vallen = MAX_PATH; - - retCode = RegQueryValueEx(hKey,"Config File Location",0, - &Type,(UCHAR *)&ValfromReg,&Vallen); - - if (retCode == ERROR_SUCCESS) - { - if (strlen(ValfromReg) == 2 && ValfromReg[0] == '"' && ValfromReg[1] == '"') - ValfromReg[0]=0; - } - } - - if (ValfromReg[0] == 0) GetCurrentDirectory(MAX_PATH, ValfromReg); - - // Get StartMinimized and MinimizetoTray flags - - Vallen = 4; - retCode = RegQueryValueEx(hKey, "Start Minimized", 0, &Type, (UCHAR *)&StartMinimized, &Vallen); - - Vallen = 4; - retCode = RegQueryValueEx(hKey, "Minimize to Tray", 0, &Type, (UCHAR *)&MinimizetoTray, &Vallen); - - ExpandEnvironmentStrings(ValfromReg, BPQDirectory, MAX_PATH); - - // Also get "BPQ Program Directory" - - ValfromReg[0] = 0; - Vallen = MAX_PATH; - - retCode = RegQueryValueEx(hKey, "BPQ Program Directory",0 , &Type, (UCHAR *)&ValfromReg, &Vallen); - - if (retCode == ERROR_SUCCESS) - ExpandEnvironmentStrings(ValfromReg, BPQProgramDirectory, MAX_PATH); - - // And Log Directory - - ValfromReg[0] = 0; - Vallen = MAX_PATH; - - retCode = RegQueryValueEx(hKey, "Log Directory",0 , &Type, (UCHAR *)&ValfromReg, &Vallen); - - if (retCode == ERROR_SUCCESS) - ExpandEnvironmentStrings(ValfromReg, LogDirectory, MAX_PATH); - - RegCloseKey(hKey); - } - - strcpy(ConfigDirectory, BPQDirectory); - - if (LogDirectory[0] == 0) - strcpy(LogDirectory, BPQDirectory); - - if (BPQProgramDirectory[0] == 0) - strcpy(BPQProgramDirectory, BPQDirectory); - - sprintf(msg,"BPQ32 Ver %s Loaded from: %s by %s\n", VersionString, DLLName, pgm); - WritetoConsole(msg); - OutputDebugString(msg); - FormatTime3(Time, time(NULL)); - sprintf(msg,"Loaded %s\n", Time); - WritetoConsole(msg); - OutputDebugString(msg); - -#pragma warning(push) -#pragma warning(disable : 4996) - -#if _MSC_VER >= 1400 - -#define _winmajor 6 -#define _winminor 0 - -#endif - - i=sprintf(msg,"Windows Ver %d.%d, Using Registry Key %s\n" ,_winmajor, _winminor, REGTREETEXT); - -#pragma warning(pop) - - WritetoConsole(msg); - OutputDebugString(msg); - - i=sprintf(msg,"BPQ32 Using config from: %s\n\n",BPQDirectory); - WritetoConsole(&msg[6]); - msg[i-1]=0; - OutputDebugString(msg); - - // Don't write the Version Key if loaded by regsvr32.exe (Installer is running with Admin rights, - // so will write the wrong tree on ) - - if (_stricmp(pgm, "regsvr32.exe") == 0) - { - Debugprintf("BPQ32 loaded by regsvr32.exe - Version String not written"); - } - else - { - retCode = RegCreateKeyEx(REGTREE, "SOFTWARE\\G8BPQ\\BPQ32", 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); - - sprintf(msg,"%d,%d,%d,%d", Ver[0], Ver[1], Ver[2], Ver[3]); - retCode = RegSetValueEx(hKey, "Version",0, REG_SZ,(BYTE *)msg, strlen(msg) + 1); - - RegCloseKey(hKey); - } - - // Make sure Logs Directory exists - - sprintf(LogDir, "%s/Logs", LogDirectory); - - CreateDirectory(LogDir, NULL); - - return; -} - -HANDLE OpenConfigFile(char *fn) -{ - HANDLE handle; - UCHAR Value[MAX_PATH]; - FILETIME LastWriteTime; - SYSTEMTIME Time; - char Msg[256]; - - - // If no directory, use current - if (BPQDirectory[0] == 0) - { - strcpy(Value,fn); - } - else - { - strcpy(Value,BPQDirectory); - strcat(Value,"\\"); - strcat(Value,fn); - } - - handle = CreateFile(Value, - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL); - - GetFileTime(handle, NULL, NULL, &LastWriteTime); - FileTimeToSystemTime(&LastWriteTime, &Time); - - sprintf(Msg,"BPQ32 Config File %s Created %.2d:%.2d %d/%.2d/%.2d\n", Value, - Time.wHour, Time.wMinute, Time.wYear, Time.wMonth, Time.wDay); - - OutputDebugString(Msg); - - return(handle); -} - -#ifdef _WIN64 -int BPQHOSTAPI() -{ - return 0; -} -#endif - - -DllExport int APIENTRY GETBPQAPI() -{ - return (int)BPQHOSTAPI; -} - -//DllExport UINT APIENTRY GETMONDECODE() -//{ -// return (UINT)MONDECODE; -//} - - -DllExport INT APIENTRY BPQAPI(int Fn, char * params) -{ - -/* -; -; BPQ HOST MODE SUPPORT CODE -; -; 22/11/95 -; -; MOVED FROM TNCODE.ASM COS CONITIONALS WERE GETTING TOO COMPLICATED -; (OS2 VERSION HAD UPSET KANT VERISON -; -; -*/ - - -/* - - BPQHOSTPORT: -; -; SPECIAL INTERFACE, MAINLY FOR EXTERNAL HOST MODE SUPPORT PROGS -; -; COMMANDS SUPPORTED ARE -; -; AH = 0 Get node/switch version number and description. On return -; AH='B',AL='P',BH='Q',BL=' ' -; DH = major version number and DL = minor version number. -; -; -; AH = 1 Set application mask to value in DL (or even DX if 16 -; applications are ever to be supported). -; -; Set application flag(s) to value in CL (or CX). -; whether user gets connected/disconnected messages issued -; by the node etc. -; -; -; AH = 2 Send frame in ES:SI (length CX) -; -; -; AH = 3 Receive frame into buffer at ES:DI, length of frame returned -; in CX. BX returns the number of outstanding frames still to -; be received (ie. after this one) or zero if no more frames -; (ie. this is last one). -; -; -; -; AH = 4 Get stream status. Returns: -; -; CX = 0 if stream disconnected or CX = 1 if stream connected -; DX = 0 if no change of state since last read, or DX = 1 if -; the connected/disconnected state has changed since -; last read (ie. delta-stream status). -; -; -; -; AH = 6 Session control. -; -; CX = 0 Conneect - _APPLMASK in DL -; CX = 1 connect -; CX = 2 disconnect -; CX = 3 return user to node -; -; -; AH = 7 Get buffer counts for stream. Returns: -; -; AX = number of status change messages to be received -; BX = number of frames queued for receive -; CX = number of un-acked frames to be sent -; DX = number of buffers left in node -; SI = number of trace frames queued for receive -; -;AH = 8 Port control/information. Called with a stream number -; in AL returns: -; -; AL = Radio port on which channel is connected (or zero) -; AH = SESSION TYPE BITS -; BX = L2 paclen for the radio port -; CX = L2 maxframe for the radio port -; DX = L4 window size (if L4 circuit, or zero) -; ES:DI = CALLSIGN - -;AH = 9 Fetch node/application callsign & alias. AL = application -; number: -; -; 0 = node -; 1 = BBS -; 2 = HOST -; 3 = SYSOP etc. etc. -; -; Returns string with alias & callsign or application name in -; user's buffer pointed to by ES:SI length CX. For example: -; -; "WORCS:G8TIC" or "TICPMS:G8TIC-10". -; -; -; AH = 10 Unproto transmit frame. Data pointed to by ES:SI, of -; length CX, is transmitted as a HDLC frame on the radio -; port (not stream) in AL. -; -; -; AH = 11 Get Trace (RAW Data) Frame into ES:DI, -; Length to CX, Timestamp to AX -; -; -; AH = 12 Update Switch. At the moment only Beacon Text may be updated -; DX = Function -; 1=update BT. ES:SI, Len CX = Text -; 2=kick off nodes broadcast -; -; AH = 13 Allocate/deallocate stream -; If AL=0, return first free stream -; If AL>0, CL=1, Allocate stream. If aleady allocated, -; return CX nonzero, else allocate, and return CX=0 -; If AL>0, CL=2, Release stream -; -; -; AH = 14 Internal Interface for IP Router -; -; Send frame - to NETROM L3 if DL=0 -; to L2 Session if DL<>0 -; -; -; AH = 15 Get interval timer - - -*/ - - - switch(Fn) - { - - case CHECKLOADED: - - params[0]=MAJORVERSION; - params[1]=MINORVERSION; - params[2]=QCOUNT; - - return (1); - } - return 0; -} - -DllExport int APIENTRY InitSwitch() -{ - return (0); -} - -/*DllExport int APIENTRY SwitchTimer() -{ - GetSemaphore((&Semaphore); - - TIMERINTERRUPT(); - - FreeSemaphore(&Semaphore); - - return (0); -} -*/ -DllExport int APIENTRY GetFreeBuffs() -{ -// Returns number of free buffers -// (BPQHOST function 7 (part)). - return (QCOUNT); -} - -DllExport UCHAR * APIENTRY GetNodeCall() -{ - return (&MYNODECALL); -} - - -DllExport UCHAR * APIENTRY GetNodeAlias() -{ - return (&MYALIASTEXT[0]); -} - -DllExport UCHAR * APIENTRY GetBBSCall() -{ - return (UCHAR *)(&APPLCALLTABLE[0].APPLCALL_TEXT); -} - - -DllExport UCHAR * APIENTRY GetBBSAlias() -{ - return (UCHAR *)(&APPLCALLTABLE[0].APPLALIAS_TEXT); -} - -DllExport VOID APIENTRY GetApplCallVB(int Appl, char * ApplCall) -{ - if (Appl < 1 || Appl > NumberofAppls ) return; - - strncpy(ApplCall,(char *)&APPLCALLTABLE[Appl-1].APPLCALL_TEXT, 10); -} - -BOOL UpdateNodesForApp(int Appl); - -DllExport BOOL APIENTRY SetApplCall(int Appl, char * NewCall) -{ - char Call[10]=" "; - int i; - - if (Appl < 1 || Appl > NumberofAppls ) return FALSE; - - i=strlen(NewCall); - - if (i > 10) i=10; - - strncpy(Call,NewCall,i); - - strncpy((char *)&APPLCALLTABLE[Appl-1].APPLCALL_TEXT,Call,10); - - if (!ConvToAX25(Call,APPLCALLTABLE[Appl-1].APPLCALL)) return FALSE; - - UpdateNodesForApp(Appl); - - return TRUE; - -} - -DllExport BOOL APIENTRY SetApplAlias(int Appl, char * NewCall) -{ - char Call[10]=" "; - int i; - - if (Appl < 1 || Appl > NumberofAppls ) return FALSE; - - i=strlen(NewCall); - - if (i > 10) i=10; - - strncpy(Call,NewCall,i); - - strncpy((char *)&APPLCALLTABLE[Appl-1].APPLALIAS_TEXT,Call,10); - - if (!ConvToAX25(Call,APPLCALLTABLE[Appl-1].APPLALIAS)) return FALSE; - - UpdateNodesForApp(Appl); - - return TRUE; - -} - - - -DllExport BOOL APIENTRY SetApplQual(int Appl, int NewQual) -{ - if (Appl < 1 || Appl > NumberofAppls ) return FALSE; - - APPLCALLTABLE[Appl-1].APPLQUAL=NewQual; - - UpdateNodesForApp(Appl); - - return TRUE; - -} - - -BOOL UpdateNodesForApp(int Appl) -{ - int App=Appl-1; - int DestLen = sizeof (struct DEST_LIST); - int n = MAXDESTS; - - struct DEST_LIST * DEST = APPLCALLTABLE[App].NODEPOINTER; - APPLCALLS * APPL=&APPLCALLTABLE[App]; - - if (DEST == NULL) - { - // No dest at the moment. If we have valid call and Qual, create an entry - - if (APPLCALLTABLE[App].APPLQUAL == 0) return FALSE; - - if (APPLCALLTABLE[App].APPLCALL[0] < 41) return FALSE; - - - GetSemaphore(&Semaphore, 5); - - DEST = DESTS; - - while (n--) - { - if (DEST->DEST_CALL[0] == 0) // Spare - break; - } - - if (n == 0) - { - // no dests - - FreeSemaphore(&Semaphore); - return FALSE; - } - - NUMBEROFNODES++; - APPL->NODEPOINTER = DEST; - - memmove (DEST->DEST_CALL,APPL->APPLCALL,13); - - DEST->DEST_STATE=0x80; // SPECIAL ENTRY - - DEST->NRROUTE[0].ROUT_QUALITY = (BYTE)APPL->APPLQUAL; - DEST->NRROUTE[0].ROUT_OBSCOUNT = 255; - - FreeSemaphore(&Semaphore); - - return TRUE; - } - - // We have a destination. If Quality is zero, remove it, else update it - - if (APPLCALLTABLE[App].APPLQUAL == 0) - { - GetSemaphore(&Semaphore, 6); - - REMOVENODE(DEST); // Clear buffers, Remove from Sorted Nodes chain, and zap entry - - APPL->NODEPOINTER=NULL; - - FreeSemaphore(&Semaphore); - return FALSE; - - } - - if (APPLCALLTABLE[App].APPLCALL[0] < 41) return FALSE; - - GetSemaphore(&Semaphore, 7); - - memmove (DEST->DEST_CALL,APPL->APPLCALL,13); - - DEST->DEST_STATE=0x80; // SPECIAL ENTRY - - DEST->NRROUTE[0].ROUT_QUALITY = (BYTE)APPL->APPLQUAL; - DEST->NRROUTE[0].ROUT_OBSCOUNT = 255; - - FreeSemaphore(&Semaphore); - return TRUE; - -} - - -DllExport UCHAR * APIENTRY GetSignOnMsg() -{ - return (&SIGNONMSG[0]); -} - - -DllExport HKEY APIENTRY GetRegistryKey() -{ - return REGTREE; -} - -DllExport char * APIENTRY GetRegistryKeyText() -{ - return REGTREETEXT;; -} - -DllExport UCHAR * APIENTRY GetBPQDirectory() -{ - while (BPQDirectory[0] == 0) - { - Debugprintf("BPQ Directory not set up - waiting"); - Sleep(1000); - } - return (&BPQDirectory[0]); -} - -DllExport UCHAR * APIENTRY GetProgramDirectory() -{ - return (&BPQProgramDirectory[0]); -} - -DllExport UCHAR * APIENTRY GetLogDirectory() -{ - return (&LogDirectory[0]); -} - -// Version for Visual Basic - -DllExport char * APIENTRY CopyBPQDirectory(char * dir) -{ - return (strcpy(dir,BPQDirectory)); -} - -DllExport int APIENTRY GetMsgPerl(int stream, char * msg) -{ - int len,count; - - GetMsg(stream, msg, &len, &count ); - - return len; -} - -int Rig_Command(int Session, char * Command); - -BOOL Rig_CommandInt(int Session, char * Command) -{ - return Rig_Command(Session, Command); -} - -DllExport int APIENTRY BPQSetHandle(int Stream, HWND hWnd) -{ - BPQHOSTVECTOR[Stream-1].HOSTHANDLE=hWnd; - return (0); -} - -#define L4USER 0 - -BPQVECSTRUC * PORTVEC ; - -VOID * InitializeExtDriver(PEXTPORTDATA PORTVEC) -{ - HINSTANCE ExtDriver=0; - char msg[128]; - int err=0; - HKEY hKey=0; - UCHAR Value[MAX_PATH]; - - // If no directory, use current - - if (BPQDirectory[0] == 0) - { - strcpy(Value,PORTVEC->PORT_DLL_NAME); - } - else - { - strcpy(Value,BPQDirectory); - strcat(Value,"\\"); - strcat(Value,PORTVEC->PORT_DLL_NAME); - } - - // Several Drivers are now built into bpq32.dll - - _strupr(Value); - - if (strstr(Value, "BPQVKISS")) - return VCOMExtInit; - - if (strstr(Value, "BPQAXIP")) - return AXIPExtInit; - - if (strstr(Value, "BPQETHER")) - return ETHERExtInit; - - if (strstr(Value, "BPQTOAGW")) - return AGWExtInit; - - if (strstr(Value, "AEAPACTOR")) - return AEAExtInit; - - if (strstr(Value, "HALDRIVER")) - return HALExtInit; - - if (strstr(Value, "KAMPACTOR")) - return KAMExtInit; - - if (strstr(Value, "SCSPACTOR")) - return SCSExtInit; - - if (strstr(Value, "WINMOR")) - return WinmorExtInit; - - if (strstr(Value, "V4")) - return V4ExtInit; - - if (strstr(Value, "TELNET")) - return TelnetExtInit; - -// if (strstr(Value, "SOUNDMODEM")) -// return SoundModemExtInit; - - if (strstr(Value, "SCSTRACKER")) - return TrackerExtInit; - - if (strstr(Value, "TRKMULTI")) - return TrackerMExtInit; - - if (strstr(Value, "UZ7HO")) - return UZ7HOExtInit; - - if (strstr(Value, "MULTIPSK")) - return MPSKExtInit; - - if (strstr(Value, "FLDIGI")) - return FLDigiExtInit; - - if (strstr(Value, "UIARQ")) - return UIARQExtInit; - -// if (strstr(Value, "BAYCOM")) -// return (UINT) BaycomExtInit; - - if (strstr(Value, "VARA")) - return VARAExtInit; - - if (strstr(Value, "ARDOP")) - return ARDOPExtInit; - - if (strstr(Value, "SERIAL")) - return SerialExtInit; - - if (strstr(Value, "KISSHF")) - return KISSHFExtInit; - - if (strstr(Value, "WINRPR")) - return WinRPRExtInit; - - if (strstr(Value, "HSMODEM")) - return HSMODEMExtInit; - - if (strstr(Value, "FREEDATA")) - return FreeDataExtInit; - - if (strstr(Value, "6PACK")) - return SIXPACKExtInit; - - ExtDriver = LoadLibrary(Value); - - if (ExtDriver == NULL) - { - err=GetLastError(); - - sprintf(msg,"Error loading Driver %s - Error code %d", - PORTVEC->PORT_DLL_NAME,err); - - MessageBox(NULL,msg,"BPQ32",MB_ICONSTOP); - - return(0); - } - - PORTVEC->DLLhandle=ExtDriver; - - return (GetProcAddress(ExtDriver,"_ExtInit@4")); - -} - -/* -_DATABASE LABEL BYTE - -FILLER DB 14 DUP (0) ; PROTECTION AGENST BUFFER PROBLEMS! - DB MAJORVERSION,MINORVERSION -_NEIGHBOURS DD 0 - DW TYPE ROUTE -_MAXNEIGHBOURS DW 20 ; MAX ADJACENT NODES - -_DESTS DD 0 ; NODE LIST - DW TYPE DEST_LIST -MAXDESTS DW 100 ; MAX NODES IN SYSTEM -*/ - - -DllExport int APIENTRY GetAttachedProcesses() -{ - return (AttachedProcesses); -} - -DllExport int * APIENTRY GetAttachedProcessList() -{ - return (&AttachedPIDList[0]); -} - -DllExport int * APIENTRY SaveNodesSupport() -{ - return (&DATABASESTART); -} - -// -// Internal BPQNODES support -// - -#define UCHAR unsigned char - -/* -ROUTE ADD G1HTL-1 2 200 0 0 0 -ROUTE ADD G4IRX-3 2 200 0 0 0 -NODE ADD MAPPLY:G1HTL-1 G1HTL-1 2 200 G4IRX-3 2 98 -NODE ADD NOT:GB7NOT G1HTL-1 2 199 G4IRX-3 2 98 - -*/ - -struct DEST_LIST * Dests; -struct ROUTE * Routes; - -int MaxNodes; -int MaxRoutes; -int NodeLen; -int RouteLen; - -int count; -int cursor; - -int len,i; - -ULONG cnt; -char Normcall[10]; -char Portcall[10]; -char Alias[7]; - -char line[100]; - -HANDLE handle; - -int APIENTRY Restart() -{ - int i, Count = AttachedProcesses; - HANDLE hProc; - DWORD PID; - - for (i = 0; i < Count; i++) - { - PID = AttachedPIDList[i]; - - // Kill Timer Owner last - - if (TimerInst != PID) - { - hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, PID); - - if (hProc) - { - TerminateProcess(hProc, 0); - CloseHandle(hProc); - } - } - } - - hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, TimerInst); - - if (hProc) - { - TerminateProcess(hProc, 0); - CloseHandle(hProc); - } - - - return 0; -} - -int APIENTRY Reboot() -{ - // Run shutdown -r -f - - STARTUPINFO SInfo; - PROCESS_INFORMATION PInfo; - char Cmd[] = "shutdown -r -f"; - - SInfo.cb=sizeof(SInfo); - SInfo.lpReserved=NULL; - SInfo.lpDesktop=NULL; - SInfo.lpTitle=NULL; - SInfo.dwFlags=0; - SInfo.cbReserved2=0; - SInfo.lpReserved2=NULL; - - return CreateProcess(NULL, Cmd, NULL, NULL, FALSE,0 ,NULL ,NULL, &SInfo, &PInfo); -} -/* -int APIENTRY Reconfig() -{ - if (!ProcessConfig()) - { - return (0); - } - SaveNodes(); - WritetoConsole("Nodes Saved\n"); - ReconfigFlag=TRUE; - WritetoConsole("Reconfig requested ... Waiting for Timer Poll\n"); - return 1; -} -*/ -// Code to support minimizing all BPQ Apps to a single Tray ICON - -// As we can't minimize the console window to the tray, I'll use an ordinary -// window instead. This also gives me somewhere to post the messages to - - -char AppName[] = "BPQ32"; -char Title[80] = "BPQ32.dll Console"; - -int NewLine(); - -char FrameClassName[] = TEXT("MdiFrame"); - -HWND ClientWnd; //This stores the MDI client area window handle - -LOGFONT LFTTYFONT ; - -HFONT hFont ; - -HMENU hPopMenu, hWndMenu; -HMENU hMainFrameMenu = NULL; -HMENU hBaseMenu = NULL; -HMENU hConsMenu = NULL; -HMENU hTermMenu = NULL; -HMENU hMonMenu = NULL; -HMENU hTermActMenu, hTermCfgMenu, hTermEdtMenu, hTermHlpMenu; -HMENU hMonActMenu, hMonCfgMenu, hMonEdtMenu, hMonHlpMenu; - - -LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); -LRESULT CALLBACK StatusWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); - -DllExport int APIENTRY DeleteTrayMenuItem(HWND hWnd); - -#define BPQMonitorAvail 1 -#define BPQDataAvail 2 -#define BPQStateChange 4 - -VOID GetJSONValue(char * _REPLYBUFFER, char * Name, char * Value); -SOCKET OpenWL2KHTTPSock(); -SendHTTPRequest(SOCKET sock, char * Request, char * Params, int Len, char * Return); - -BOOL GetWL2KSYSOPInfo(char * Call, char * _REPLYBUFFER); -BOOL UpdateWL2KSYSOPInfo(char * Call, char * SQL); - - -static INT_PTR CALLBACK ConfigWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) -{ - switch (message) - { - case WM_INITDIALOG: - { - char _REPLYBUFFER[1000] = ""; - char Value[1000]; - - if (GetWL2KSYSOPInfo(WL2KCall, _REPLYBUFFER)) - { -// if (strstr(_REPLYBUFFER, "\"ErrorMessage\":") == 0) - - GetJSONValue(_REPLYBUFFER, "\"SysopName\":", Value); - SetDlgItemText(hDlg, NAME, Value); - - GetJSONValue(_REPLYBUFFER, "\"GridSquare\":", Value); - SetDlgItemText(hDlg, IDC_Locator, Value); - - GetJSONValue(_REPLYBUFFER, "\"StreetAddress1\":", Value); - SetDlgItemText(hDlg, ADDR1, Value); - - GetJSONValue(_REPLYBUFFER, "\"StreetAddress2\":", Value); - SetDlgItemText(hDlg, ADDR2, Value); - - GetJSONValue(_REPLYBUFFER, "\"City\":", Value); - SetDlgItemText(hDlg, CITY, Value); - - GetJSONValue(_REPLYBUFFER, "\"State\":", Value); - SetDlgItemText(hDlg, STATE, Value); - - GetJSONValue(_REPLYBUFFER, "\"Country\":", Value); - SetDlgItemText(hDlg, COUNTRY, Value); - - GetJSONValue(_REPLYBUFFER, "\"PostalCode\":", Value); - SetDlgItemText(hDlg, POSTCODE, Value); - - GetJSONValue(_REPLYBUFFER, "\"Email\":", Value); - SetDlgItemText(hDlg, EMAIL, Value); - - GetJSONValue(_REPLYBUFFER, "\"Website\":", Value); - SetDlgItemText(hDlg, WEBSITE, Value); - - GetJSONValue(_REPLYBUFFER, "\"Phones\":", Value); - SetDlgItemText(hDlg, PHONE, Value); - - GetJSONValue(_REPLYBUFFER, "\"Comments\":", Value); - SetDlgItemText(hDlg, ADDITIONALDATA, Value); - - } - - return (INT_PTR)TRUE; - } - case WM_COMMAND: - - switch(LOWORD(wParam)) - { - - case ID_SAVE: - { - char Name[100]; - char PasswordText[100]; - char LocatorText[100]; - char Addr1[100]; - char Addr2[100]; - char City[100]; - char State[100]; - char Country[100]; - char PostCode[100]; - char Email[100]; - char Website[100]; - char Phone[100]; - char Data[100]; - - SOCKET sock; - - int Len; - char Message[2048]; - char Reply[2048] = ""; - - - GetDlgItemText(hDlg, NAME, Name, 99); - GetDlgItemText(hDlg, IDC_Password, PasswordText, 99); - GetDlgItemText(hDlg, IDC_Locator, LocatorText, 99); - GetDlgItemText(hDlg, ADDR1, Addr1, 99); - GetDlgItemText(hDlg, ADDR2, Addr2, 99); - GetDlgItemText(hDlg, CITY, City, 99); - GetDlgItemText(hDlg, STATE, State, 99); - GetDlgItemText(hDlg, COUNTRY, Country, 99); - GetDlgItemText(hDlg, POSTCODE, PostCode, 99); - GetDlgItemText(hDlg, EMAIL, Email, 99); - GetDlgItemText(hDlg, WEBSITE, Website, 99); - GetDlgItemText(hDlg, PHONE, Phone, 99); - GetDlgItemText(hDlg, ADDITIONALDATA, Data, 99); - - -//{"Callsign":"String","GridSquare":"String","SysopName":"String", -//"StreetAddress1":"String","StreetAddress2":"String","City":"String", -//"State":"String","Country":"String","PostalCode":"String","Email":"String", -//"Phones":"String","Website":"String","Comments":"String"} - - Len = sprintf(Message, - "\"Callsign\":\"%s\"," - "\"Password\":\"%s\"," - "\"GridSquare\":\"%s\"," - "\"SysopName\":\"%s\"," - "\"StreetAddress1\":\"%s\"," - "\"StreetAddress2\":\"%s\"," - "\"City\":\"%s\"," - "\"State\":\"%s\"," - "\"Country\":\"%s\"," - "\"PostalCode\":\"%s\"," - "\"Email\":\"%s\"," - "\"Phones\":\"%s\"," - "\"Website\":\"%s\"," - "\"Comments\":\"%s\"", - - WL2KCall, PasswordText, LocatorText, Name, Addr1, Addr2, City, State, Country, PostCode, Email, Phone, Website, Data); - - Debugprintf("Sending %s", Message); - - sock = OpenWL2KHTTPSock(); - - if (sock) - { - char * ptr; - - SendHTTPRequest(sock, - "/sysop/add", Message, Len, Reply); - - ptr = strstr(Reply, "\"ErrorCode\":"); - - if (ptr) - { - ptr = strstr(ptr, "Message"); - if (ptr) - { - ptr += 10; - strlop(ptr, '"'); - MessageBox(NULL ,ptr, "Error", MB_OK); - } - } - else - MessageBox(NULL, "Sysop Record Updated", "BPQ32", MB_OK); - - } - closesocket(sock); - } - - case ID_CANCEL: - { - EndDialog(hDlg, LOWORD(wParam)); - return (INT_PTR)TRUE; - } - break; - } - } - return (INT_PTR)FALSE; -} - - - -LRESULT CALLBACK UIWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); -VOID WINAPI OnTabbedDialogInit(HWND hDlg); - -LRESULT CALLBACK FrameWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - int wmId, wmEvent; - POINT pos; - BOOL ret; - - CLIENTCREATESTRUCT MDIClientCreateStruct; // Structure to be used for MDI client area - //HWND m_hwndSystemInformation = 0; - - if (message == BPQMsg) - { - if (lParam & BPQDataAvail) - DoReceivedData(wParam); - - if (lParam & BPQMonitorAvail) - DoMonData(wParam); - - if (lParam & BPQStateChange) - DoStateChange(wParam); - - return (0); - } - - switch (message) - { - case MY_TRAY_ICON_MESSAGE: - - switch(lParam) - { - case WM_RBUTTONUP: - case WM_LBUTTONUP: - - GetCursorPos(&pos); - - // SetForegroundWindow(FrameWnd); - - TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, FrameWnd, 0); - return 0; - } - - break; - - case WM_CTLCOLORDLG: - return (LONG)bgBrush; - - case WM_SIZING: - case WM_SIZE: - - SendMessage(ClientWnd, WM_MDIICONARRANGE, 0 ,0); - break; - - case WM_NCCREATE: - - ret = DefFrameProc(hWnd, ClientWnd, message, wParam, lParam); - return TRUE; - - case WM_CREATE: - - // On creation of main frame, create the MDI client area - - MDIClientCreateStruct.hWindowMenu = NULL; - MDIClientCreateStruct.idFirstChild = IDM_FIRSTCHILD; - - ClientWnd = CreateWindow(TEXT("MDICLIENT"), // predefined value for MDI client area - NULL, // no caption required - WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE, - 0, // No need to give any x/y or height/width since this client - // will just be used to get client windows created, effectively - // in the main window we will be seeing the mainframe window client area itself. - 0, - 0, - 0, - hWnd, - NULL, - hInstance, - (void *) &MDIClientCreateStruct); - - - return 0; - - case WM_COMMAND: - - wmId = LOWORD(wParam); // Remember, these are... - wmEvent = HIWORD(wParam); // ...different for Win32! - - if (wmId >= TRAYBASEID && wmId < (TRAYBASEID + 100)) - { - handle=hWndArray[wmId-TRAYBASEID]; - - if (handle == FrameWnd) - ShowWindow(handle, SW_NORMAL); - - if (handle == FrameWnd && FrameMaximized == TRUE) - PostMessage(handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0); - else - PostMessage(handle, WM_SYSCOMMAND, SC_RESTORE, 0); - - SetForegroundWindow(handle); - return 0; - } - - switch(wmId) - { - struct ConsoleInfo * Cinfo = NULL; - - case ID_NEWWINDOW: - Cinfo = CreateChildWindow(0, FALSE); - if (Cinfo) - SendMessage(ClientWnd, WM_MDIACTIVATE, (WPARAM)Cinfo->hConsole, 0); - break; - - case ID_WINDOWS_CASCADE: - SendMessage(ClientWnd, WM_MDICASCADE, 0, 0); - return 0; - - case ID_WINDOWS_TILE: - SendMessage(ClientWnd, WM_MDITILE , MDITILE_HORIZONTAL, 0); - return 0; - - case BPQCLOSEALL: - CloseAllPrograms(); - // SendMessage(ClientWnd, WM_MDIICONARRANGE, 0 ,0); - - return 0; - - case BPQUICONFIG: - { - int err, i=0; - char Title[80]; - WNDCLASS wc; - - wc.style = CS_HREDRAW | CS_VREDRAW; - wc.lpfnWndProc = UIWndProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = DLGWINDOWEXTRA; - wc.hInstance = hInstance; - wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON) ); - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.hbrBackground = bgBrush; - - wc.lpszMenuName = NULL; - wc.lpszClassName = UIClassName; - - RegisterClass(&wc); - - UIhWnd = CreateDialog(hInstance, UIClassName, 0, NULL); - - if (!UIhWnd) - { - err=GetLastError(); - return FALSE; - } - - wsprintf(Title,"BPQ32 Beacon Configuration"); - MySetWindowText(UIhWnd, Title); - ShowWindow(UIhWnd, SW_NORMAL); - - OnTabbedDialogInit(UIhWnd); // Set up pages - - // UpdateWindow(UIhWnd); - return 0; - } - - - case IDD_WL2KSYSOP: - - if (WL2KCall[0] == 0) - { - MessageBox(NULL,"WL2K Reporting is not configured","BPQ32", MB_OK); - break; - } - - DialogBox(hInstance, MAKEINTRESOURCE(IDD_WL2KSYSOP), hWnd, ConfigWndProc); - break; - - - // Handle MDI Window commands - - default: - { - if(wmId >= IDM_FIRSTCHILD) - { - DefFrameProc(hWnd, ClientWnd, message, wParam, lParam); - } - else - { - HWND hChild = (HWND)SendMessage(ClientWnd, WM_MDIGETACTIVE,0,0); - - if(hChild) - SendMessage(hChild, WM_COMMAND, wParam, lParam); - } - } - } - - break; - - case WM_INITMENUPOPUP: - { - HWND hChild = (HWND)SendMessage(ClientWnd, WM_MDIGETACTIVE,0,0); - - if(hChild) - SendMessage(hChild, WM_INITMENUPOPUP, wParam, lParam); - } - - case WM_SYSCOMMAND: - - wmId = LOWORD(wParam); // Remember, these are... - wmEvent = HIWORD(wParam); // ...different for Win32! - - switch (wmId) - { - case SC_MAXIMIZE: - - FrameMaximized = TRUE; - break; - - case SC_RESTORE: - - FrameMaximized = FALSE; - break; - - case SC_MINIMIZE: - - if (MinimizetoTray) - { - ShowWindow(hWnd, SW_HIDE); - return TRUE; - } - } - - return (DefFrameProc(hWnd, ClientWnd, message, wParam, lParam)); - - case WM_CLOSE: - - PostQuitMessage(0); - - if (MinimizetoTray) - DeleteTrayMenuItem(hWnd); - - break; - - default: - return (DefFrameProc(hWnd, ClientWnd, message, wParam, lParam)); - - } - return (DefFrameProc(hWnd, ClientWnd, message, wParam, lParam)); -} - -int OffsetH, OffsetW; - -int SetupConsoleWindow() -{ - WNDCLASS wc; - int i; - int retCode, Type, Vallen; - HKEY hKey=0; - char Size[80]; - WNDCLASSEX wndclassMainFrame; - RECT CRect; - - retCode = RegOpenKeyEx (REGTREE, - "SOFTWARE\\G8BPQ\\BPQ32", - 0, - KEY_QUERY_VALUE, - &hKey); - - if (retCode == ERROR_SUCCESS) - { - Vallen=80; - - retCode = RegQueryValueEx(hKey,"FrameWindowSize",0, - (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); - - if (retCode == ERROR_SUCCESS) - sscanf(Size,"%d,%d,%d,%d",&FRect.left,&FRect.right,&FRect.top,&FRect.bottom); - - if (FRect.top < - 500 || FRect.left < - 500) - { - FRect.left = 0; - FRect.top = 0; - FRect.right = 600; - FRect.bottom = 400; - } - - - Vallen=80; - retCode = RegQueryValueEx(hKey,"WindowSize",0, - (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); - - if (retCode == ERROR_SUCCESS) - sscanf(Size,"%d,%d,%d,%d,%d",&Rect.left,&Rect.right,&Rect.top,&Rect.bottom, &ConsoleMinimized); - - if (Rect.top < - 500 || Rect.left < - 500) - { - Rect.left = 0; - Rect.top = 0; - Rect.right = 600; - Rect.bottom = 400; - } - - Vallen=80; - - retCode = RegQueryValueEx(hKey,"StatusWindowSize",0, - (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); - - if (retCode == ERROR_SUCCESS) - sscanf(Size, "%d,%d,%d,%d,%d", &StatusRect.left, &StatusRect.right, - &StatusRect.top, &StatusRect.bottom, &StatusMinimized); - - if (StatusRect.top < - 500 || StatusRect.left < - 500) - { - StatusRect.left = 0; - StatusRect.top = 0; - StatusRect.right = 850; - StatusRect.bottom = 500; - } - - - // Get StartMinimized and MinimizetoTray flags - - Vallen = 4; - retCode = RegQueryValueEx(hKey, "Start Minimized", 0, &Type, (UCHAR *)&StartMinimized, &Vallen); - - Vallen = 4; - retCode = RegQueryValueEx(hKey, "Minimize to Tray", 0, &Type, (UCHAR *)&MinimizetoTray, &Vallen); - } - - wndclassMainFrame.cbSize = sizeof(WNDCLASSEX); - wndclassMainFrame.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; - wndclassMainFrame.lpfnWndProc = FrameWndProc; - wndclassMainFrame.cbClsExtra = 0; - wndclassMainFrame.cbWndExtra = 0; - wndclassMainFrame.hInstance = hInstance; - wndclassMainFrame.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON)); - wndclassMainFrame.hCursor = LoadCursor(NULL, IDC_ARROW); - wndclassMainFrame.hbrBackground = (HBRUSH) GetStockObject(GRAY_BRUSH); - wndclassMainFrame.lpszMenuName = NULL; - wndclassMainFrame.lpszClassName = FrameClassName; - wndclassMainFrame.hIconSm = NULL; - - if(!RegisterClassEx(&wndclassMainFrame)) - { - return 0; - } - - pindex = 0; - PartLine = FALSE; - - bgBrush = CreateSolidBrush(BGCOLOUR); - -// hMainFrameMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MAINFRAME_MENU)); - - hBaseMenu = LoadMenu(hInstance, MAKEINTRESOURCE(CONS_MENU)); - hConsMenu = GetSubMenu(hBaseMenu, 1); - hWndMenu = GetSubMenu(hBaseMenu, 0); - - hTermMenu = LoadMenu(hInstance, MAKEINTRESOURCE(TERM_MENU)); - hTermActMenu = GetSubMenu(hTermMenu, 1); - hTermCfgMenu = GetSubMenu(hTermMenu, 2); - hTermEdtMenu = GetSubMenu(hTermMenu, 3); - hTermHlpMenu = GetSubMenu(hTermMenu, 4); - - hMonMenu = LoadMenu(hInstance, MAKEINTRESOURCE(MON_MENU)); - hMonCfgMenu = GetSubMenu(hMonMenu, 1); - hMonEdtMenu = GetSubMenu(hMonMenu, 2); - hMonHlpMenu = GetSubMenu(hMonMenu, 3); - - hMainFrameMenu = CreateMenu(); - AppendMenu(hMainFrameMenu, MF_STRING + MF_POPUP, (UINT)hWndMenu, "Window"); - - //Create the main MDI frame window - - ClientWnd = NULL; - - FrameWnd = CreateWindow(FrameClassName, - "BPQ32 Console", - WS_OVERLAPPEDWINDOW |WS_CLIPCHILDREN, - FRect.left, - FRect.top, - FRect.right - FRect.left, - FRect.bottom - FRect.top, - NULL, // handle to parent window - hMainFrameMenu, // handle to menu - hInstance, // handle to the instance of module - NULL); // Long pointer to a value to be passed to the window through the - // CREATESTRUCT structure passed in the lParam parameter the WM_CREATE message - - - // Get Client Params - - if (FrameWnd == 0) - { - Debugprintf("SetupConsoleWindow Create Frame failed %d", GetLastError()); - return 0; - } - - ShowWindow(FrameWnd, SW_RESTORE); - - - GetWindowRect(FrameWnd, &FRect); - OffsetH = FRect.bottom - FRect.top; - OffsetW = FRect.right - FRect.left; - GetClientRect(FrameWnd, &CRect); - OffsetH -= CRect.bottom; - OffsetW -= CRect.right; - OffsetH -= 4; - - // Create Console Window - - wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; - wc.lpfnWndProc = (WNDPROC)WndProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = DLGWINDOWEXTRA; - wc.hInstance = hInstance; - wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(BPQICON)); - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); - wc.lpszMenuName = 0; - wc.lpszClassName = ClassName; - - i=RegisterClass(&wc); - - sprintf (Title, "BPQ32.dll Console Version %s", VersionString); - - hConsWnd = CreateMDIWindow(ClassName, "Console", 0, - 0,0,0,0, ClientWnd, hInstance, 1234); - - i = GetLastError(); - - if (!hConsWnd) { - return (FALSE); - } - - wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; - wc.lpfnWndProc = (WNDPROC)StatusWndProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = DLGWINDOWEXTRA; - wc.hInstance = hInstance; - wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(BPQICON)); - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); - wc.lpszMenuName = 0; - wc.lpszClassName = "Status"; - - i=RegisterClass(&wc); - - if (StatusRect.top < OffsetH) // Make sure not off top of MDI frame - { - int Error = OffsetH - StatusRect.top; - StatusRect.top += Error; - StatusRect.bottom += Error; - } - - StatusWnd = CreateMDIWindow("Status", "Stream Status", 0, - StatusRect.left, StatusRect.top, StatusRect.right - StatusRect.left, - StatusRect.bottom - StatusRect.top, ClientWnd, hInstance, 1234); - - SetTimer(StatusWnd, 1, 1000, NULL); - - hPopMenu = GetSubMenu(hBaseMenu, 1) ; - - if (MinimizetoTray) - CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_CHECKED); - else - CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_UNCHECKED); - - if (StartMinimized) - CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_CHECKED); - else - CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_UNCHECKED); - - DrawMenuBar(hConsWnd); - - // setup default font information - - LFTTYFONT.lfHeight = 12; - LFTTYFONT.lfWidth = 8 ; - LFTTYFONT.lfEscapement = 0 ; - LFTTYFONT.lfOrientation = 0 ; - LFTTYFONT.lfWeight = 0 ; - LFTTYFONT.lfItalic = 0 ; - LFTTYFONT.lfUnderline = 0 ; - LFTTYFONT.lfStrikeOut = 0 ; - LFTTYFONT.lfCharSet = 0; - LFTTYFONT.lfOutPrecision = OUT_DEFAULT_PRECIS ; - LFTTYFONT.lfClipPrecision = CLIP_DEFAULT_PRECIS ; - LFTTYFONT.lfQuality = DEFAULT_QUALITY ; - LFTTYFONT.lfPitchAndFamily = FIXED_PITCH; - lstrcpy(LFTTYFONT.lfFaceName, "FIXEDSYS" ) ; - - hFont = CreateFontIndirect(&LFTTYFONT) ; - - SetWindowText(hConsWnd,Title); - - if (Rect.right < 100 || Rect.bottom < 100) - { - GetWindowRect(hConsWnd, &Rect); - } - - if (Rect.top < OffsetH) // Make sure not off top of MDI frame - { - int Error = OffsetH - Rect.top; - Rect.top += Error; - Rect.bottom += Error; - } - - - MoveWindow(hConsWnd, Rect.left - (OffsetW /2), Rect.top - OffsetH, Rect.right-Rect.left, Rect.bottom-Rect.top, TRUE); - - MoveWindow(StatusWnd, StatusRect.left - (OffsetW /2), StatusRect.top - OffsetH, - StatusRect.right-StatusRect.left, StatusRect.bottom-StatusRect.top, TRUE); - - hWndCons = CreateWindowEx(WS_EX_CLIENTEDGE, "LISTBOX", "", - WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | - LBS_DISABLENOSCROLL | LBS_NOSEL | WS_VSCROLL | WS_HSCROLL, - Rect.left, Rect.top, Rect.right - Rect.left, Rect.bottom - Rect.top, - hConsWnd, NULL, hInstance, NULL); - -// SendMessage(hWndCons, WM_SETFONT, hFont, 0); - - SendMessage(hWndCons, LB_SETHORIZONTALEXTENT , 1000, 0); - - if (ConsoleMinimized) - ShowWindow(hConsWnd, SW_SHOWMINIMIZED); - else - ShowWindow(hConsWnd, SW_RESTORE); - - if (StatusMinimized) - ShowWindow(StatusWnd, SW_SHOWMINIMIZED); - else - ShowWindow(StatusWnd, SW_RESTORE); - - ShowWindow(FrameWnd, SW_RESTORE); - - - LoadLibrary("riched20.dll"); - - if (StartMinimized) - if (MinimizetoTray) - ShowWindow(FrameWnd, SW_HIDE); - else - ShowWindow(FrameWnd, SW_SHOWMINIMIZED); - else - ShowWindow(FrameWnd, SW_RESTORE); - - CreateMonitorWindow(Size); - - return 0; -} - -DllExport int APIENTRY SetupTrayIcon() -{ - if (MinimizetoTray == 0) - return 0; - - trayMenu = CreatePopupMenu(); - - for( i = 0; i < 100; ++i ) - { - if (strcmp(PopupText[i],"BPQ32 Console") == 0) - { - hWndArray[i] = FrameWnd; - goto doneit; - } - } - - for( i = 0; i < 100; ++i ) - { - if (hWndArray[i] == 0) - { - hWndArray[i] = FrameWnd; - strcpy(PopupText[i],"BPQ32 Console"); - break; - } - } -doneit: - - for( i = 0; i < 100; ++i ) - { - if (hWndArray[i] != 0) - AppendMenu(trayMenu,MF_STRING,TRAYBASEID+i,PopupText[i]); - } - - // Set up Tray ICON - - ZeroMemory(&niData,sizeof(NOTIFYICONDATA)); - - niData.cbSize = sizeof(NOTIFYICONDATA); - - // the ID number can be any UINT you choose and will - // be used to identify your icon in later calls to - // Shell_NotifyIcon - - niData.uID = TRAY_ICON_ID; - - // state which structure members are valid - // here you can also choose the style of tooltip - // window if any - specifying a balloon window: - // NIF_INFO is a little more complicated - - strcpy(niData.szTip,"BPQ32 Windows"); - - niData.uFlags = NIF_ICON|NIF_MESSAGE|NIF_TIP; - - // load the icon note: you should destroy the icon - // after the call to Shell_NotifyIcon - - niData.hIcon = - - //LoadIcon(NULL, IDI_APPLICATION); - - (HICON)LoadImage( hInstance, - MAKEINTRESOURCE(BPQICON), - IMAGE_ICON, - GetSystemMetrics(SM_CXSMICON), - GetSystemMetrics(SM_CYSMICON), - LR_DEFAULTCOLOR); - - - // set the window you want to receive event messages - - niData.hWnd = FrameWnd; - - // set the message to send - // note: the message value should be in the - // range of WM_APP through 0xBFFF - - niData.uCallbackMessage = MY_TRAY_ICON_MESSAGE; - - // Call Shell_NotifyIcon. NIM_ADD adds a new tray icon - - if (Shell_NotifyIcon(NIM_ADD,&niData)) - Debugprintf("BPQ32 Create Tray Icon Ok"); -// else -// Debugprintf("BPQ32 Create Tray Icon failed %d", GetLastError()); - - return 0; -} - -VOID SaveConfig() -{ - HKEY hKey=0; - int retCode, disp; - - retCode = RegCreateKeyEx(REGTREE, - "SOFTWARE\\G8BPQ\\BPQ32", - 0, // Reserved - 0, // Class - 0, // Options - KEY_ALL_ACCESS, - NULL, // Security Attrs - &hKey, - &disp); - - if (retCode == ERROR_SUCCESS) - { - retCode = RegSetValueEx(hKey, "Start Minimized", 0, REG_DWORD, (UCHAR *)&StartMinimized, 4); - retCode = RegSetValueEx(hKey, "Minimize to Tray", 0, REG_DWORD, (UCHAR *)&MinimizetoTray, 4); - } -} - -LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - int wmId, wmEvent; - POINT pos; - HWND handle; - RECT cRect; - - switch (message) - { - case WM_MDIACTIVATE: - - // Set the system info menu when getting activated - - if (lParam == (LPARAM) hWnd) - { - // Activate - - // GetSubMenu function should retrieve a handle to the drop-down menu or submenu. - - RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); - AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)hConsMenu, "Actions"); - SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); - } - else - { - // Deactivate - - SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); - } - - DrawMenuBar(FrameWnd); - - return TRUE; //DefMDIChildProc(hWnd, message, wParam, lParam); - - case MY_TRAY_ICON_MESSAGE: - - switch(lParam) - { - case WM_RBUTTONUP: - case WM_LBUTTONUP: - - GetCursorPos(&pos); - - SetForegroundWindow(hWnd); - - TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, hWnd, 0); - return 0; - } - - break; - - case WM_CTLCOLORDLG: - return (LONG)bgBrush; - - case WM_COMMAND: - - wmId = LOWORD(wParam); // Remember, these are... - wmEvent = HIWORD(wParam); // ...different for Win32! - - if (wmId == IDC_ENIGATE) - { - int retCode, disp; - HKEY hKey=0; - - IGateEnabled = IsDlgButtonChecked(hWnd, IDC_ENIGATE); - - if (IGateEnabled) - ISDelayTimer = 60; - - retCode = RegCreateKeyEx(REGTREE, - "SOFTWARE\\G8BPQ\\BPQ32", - 0, // Reserved - 0, // Class - 0, // Options - KEY_ALL_ACCESS, - NULL, // Security Attrs - &hKey, - &disp); - - if (retCode == ERROR_SUCCESS) - { - retCode = RegSetValueEx(hKey,"IGateEnabled", 0 , REG_DWORD,(BYTE *)&IGateEnabled, 4); - RegCloseKey(hKey); - } - - return 0; - } - - if (wmId == BPQSAVENODES) - { - SaveNodes(); - WritetoConsole("Nodes Saved\n"); - return 0; - } - if (wmId == BPQCLEARRECONFIG) - { - if (!ProcessConfig()) - { - MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); - return (0); - } - - ClearNodes(); - WritetoConsole("Nodes file Cleared\n"); - ReconfigFlag=TRUE; - WritetoConsole("Reconfig requested ... Waiting for Timer Poll\n"); - return 0; - } - if (wmId == BPQRECONFIG) - { - if (!ProcessConfig()) - { - MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); - return (0); - } - SaveNodes(); - WritetoConsole("Nodes Saved\n"); - ReconfigFlag=TRUE; - WritetoConsole("Reconfig requested ... Waiting for Timer Poll\n"); - return 0; - } - - if (wmId == SCANRECONFIG) - { - if (!ProcessConfig()) - { - MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); - return (0); - } - - RigReconfigFlag = TRUE; - WritetoConsole("Rigcontrol Reconfig requested ... Waiting for Timer Poll\n"); - return 0; - } - - if (wmId == APRSRECONFIG) - { - if (!ProcessConfig()) - { - MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); - return (0); - } - - APRSReconfigFlag=TRUE; - WritetoConsole("APRS Reconfig requested ... Waiting for Timer Poll\n"); - return 0; - } - if (wmId == BPQDUMP) - { - DumpSystem(); - return 0; - } - - if (wmId == BPQCLOSEALL) - { - CloseAllPrograms(); - return 0; - } - - if (wmId == BPQUICONFIG) - { - int err, i=0; - char Title[80]; - WNDCLASS wc; - - wc.style = CS_HREDRAW | CS_VREDRAW; - wc.lpfnWndProc = UIWndProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = DLGWINDOWEXTRA; - wc.hInstance = hInstance; - wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON) ); - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.hbrBackground = bgBrush; - - wc.lpszMenuName = NULL; - wc.lpszClassName = UIClassName; - - RegisterClass(&wc); - - UIhWnd = CreateDialog(hInstance, UIClassName,0,NULL); - - if (!UIhWnd) - { - err=GetLastError(); - return FALSE; - } - - wsprintf(Title,"BPQ32 Beacon Utility Version"); - MySetWindowText(UIhWnd, Title); - return 0; - } - - if (wmId == BPQSAVEREG) - { - CreateRegBackup(); - return 0; - } - - if (wmId == BPQMINTOTRAY) - { - MinimizetoTray = !MinimizetoTray; - - if (MinimizetoTray) - CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_CHECKED); - else - CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_UNCHECKED); - - SaveConfig(); - return 0; - } - - if (wmId == BPQSTARTMIN) - { - StartMinimized = !StartMinimized; - - if (StartMinimized) - CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_CHECKED); - else - CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_UNCHECKED); - - SaveConfig(); - return 0; - } - - if (wmId >= TRAYBASEID && wmId < (TRAYBASEID + 100)) - { - handle=hWndArray[wmId-TRAYBASEID]; - - if (handle == FrameWnd && FrameMaximized == TRUE) - PostMessage(handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0); - else - PostMessage(handle, WM_SYSCOMMAND, SC_RESTORE, 0); - - SetForegroundWindow(handle); - return 0; - } - - case WM_SYSCOMMAND: - - wmId = LOWORD(wParam); // Remember, these are... - wmEvent = HIWORD(wParam); // ...different for Win32! - - switch (wmId) - { - case SC_MINIMIZE: - - ConsoleMinimized = TRUE; - break; - - case SC_RESTORE: - - ConsoleMinimized = FALSE; - SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); - - break; - } - - return DefMDIChildProc(hWnd, message, wParam, lParam); - - - case WM_SIZE: - - GetClientRect(hWnd, &cRect); - - MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); - - if (APRSActive) - MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); - else - MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); - -// InvalidateRect(hWnd, NULL, TRUE); - break; - -/* - case WM_PAINT: - - hdc = BeginPaint (hWnd, &ps); - - hOldFont = SelectObject( hdc, hFont) ; - - for (i=0; i 300) - len = 300; - - memcpy(&buffptr[2], buff, len + 1); - - C_Q_ADD(&WritetoConsoleQ, buffptr); - - return 0; -} - -int WritetoConsoleSupport(char * buff) -{ - - int len=strlen(buff); - char Temp[2000]= ""; - char * ptr; - - if (PartLine) - { - SendMessage(hWndCons, LB_GETTEXT, pindex, (LPARAM)(LPCTSTR) Temp); - SendMessage(hWndCons, LB_DELETESTRING, pindex, 0); - PartLine = FALSE; - } - - if ((strlen(Temp) + strlen(buff)) > 1990) - Temp[0] = 0; // Should never have anything this long - - strcat(Temp, buff); - - ptr = strchr(Temp, '\n'); - - if (ptr) - *ptr = 0; - else - PartLine = TRUE; - - pindex=SendMessage(hWndCons, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR) Temp); - return 0; - } - -DllExport VOID APIENTRY BPQOutputDebugString(char * String) -{ - OutputDebugString(String); - return; - } - -HANDLE handle; -char fn[]="BPQDUMP"; -ULONG cnt; -char * stack; -//char screen[1920]; -//COORD ReadCoord; - -#define DATABYTES 400000 - -extern UCHAR DATAAREA[]; - -DllExport int APIENTRY DumpSystem() -{ - char fn[200]; - char Msg[250]; - - sprintf(fn,"%s\\BPQDUMP",BPQDirectory); - - handle = CreateFile(fn, - GENERIC_WRITE, - FILE_SHARE_READ, - NULL, - CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - NULL); - -#ifndef _WIN64 - - _asm { - - mov stack,esp - } - - WriteFile(handle,stack,128,&cnt,NULL); -#endif - -// WriteFile(handle,Screen,MAXLINELEN*MAXSCREENLEN,&cnt,NULL); - - WriteFile(handle,DATAAREA, DATABYTES,&cnt,NULL); - - CloseHandle(handle); - - sprintf(Msg, "Dump to %s Completed\n", fn); - WritetoConsole(Msg); - - FindLostBuffers(); - - return (0); -} - -BOOLEAN CheckifBPQ32isLoaded() -{ - HANDLE Mutex; - - // See if BPQ32 is running - if we create it in the NTVDM address space by - // loading bpq32.dll it will not work. - - Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"BPQLOCKMUTEX"); - - if (Mutex == NULL) - { - if (AttachingProcess == 0) // Already starting BPQ32 - { - OutputDebugString("BPQ32 No other bpq32 programs running - Loading BPQ32.exe\n"); - StartBPQ32(); - } - return FALSE; - } - - CloseHandle(Mutex); - - return TRUE; -} - -BOOLEAN StartBPQ32() -{ - UCHAR Value[100]; - - char bpq[]="BPQ32.exe"; - char *fn=(char *)&bpq; - HKEY hKey=0; - int ret,Type,Vallen=99; - - char Errbuff[100]; - char buff[20]; - - STARTUPINFO StartupInfo; // pointer to STARTUPINFO - PROCESS_INFORMATION ProcessInformation; // pointer to PROCESS_INFORMATION - - AttachingProcess = 1; - -// Get address of BPQ Directory - - Value[0]=0; - - ret = RegOpenKeyEx (REGTREE, - "SOFTWARE\\G8BPQ\\BPQ32", - 0, - KEY_QUERY_VALUE, - &hKey); - - if (ret == ERROR_SUCCESS) - { - ret = RegQueryValueEx(hKey, "BPQ Program Directory", 0, &Type,(UCHAR *)&Value, &Vallen); - - if (ret == ERROR_SUCCESS) - { - if (strlen(Value) == 2 && Value[0] == '"' && Value[1] == '"') - Value[0]=0; - } - - - if (Value[0] == 0) - { - - // BPQ Directory absent or = "" - "try Config File Location" - - ret = RegQueryValueEx(hKey,"BPQ Directory",0, - &Type,(UCHAR *)&Value,&Vallen); - - if (ret == ERROR_SUCCESS) - { - if (strlen(Value) == 2 && Value[0] == '"' && Value[1] == '"') - Value[0]=0; - } - - } - RegCloseKey(hKey); - } - - if (Value[0] == 0) - { - strcpy(Value,fn); - } - else - { - strcat(Value,"\\"); - strcat(Value,fn); - } - - StartupInfo.cb=sizeof(StartupInfo); - StartupInfo.lpReserved=NULL; - StartupInfo.lpDesktop=NULL; - StartupInfo.lpTitle=NULL; - StartupInfo.dwFlags=0; - StartupInfo.cbReserved2=0; - StartupInfo.lpReserved2=NULL; - - if (!CreateProcess(Value,NULL,NULL,NULL,FALSE, - CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP, - NULL,NULL,&StartupInfo,&ProcessInformation)) - { - ret=GetLastError(); - - _itoa(ret,buff,10); - - strcpy(Errbuff, "BPQ32 Load "); - strcat(Errbuff,Value); - strcat(Errbuff," failed "); - strcat(Errbuff,buff); - OutputDebugString(Errbuff); - AttachingProcess = 0; - return FALSE; - } - - return TRUE; -} - - -DllExport BPQVECSTRUC * APIENTRY GetIPVectorAddr() -{ - return &IPHOSTVECTOR; -} - -DllExport UINT APIENTRY GETSENDNETFRAMEADDR() -{ - return (UINT)&SENDNETFRAME; -} - -DllExport VOID APIENTRY RelBuff(VOID * Msg) -{ - UINT * pointer, * BUFF = Msg; - - if (Semaphore.Flag == 0) - Debugprintf("ReleaseBuffer called without semaphore"); - - pointer = FREE_Q; - - *BUFF =(UINT)pointer; - - FREE_Q = BUFF; - - QCOUNT++; - - return; -} - -extern int MINBUFFCOUNT; - -DllExport VOID * APIENTRY GetBuff() -{ - UINT * Temp = Q_REM(&FREE_Q); - - if (Semaphore.Flag == 0) - Debugprintf("GetBuff called without semaphore"); - - if (Temp) - { - QCOUNT--; - - if (QCOUNT < MINBUFFCOUNT) - MINBUFFCOUNT = QCOUNT; - } - - return Temp; -} - - -VOID __cdecl Debugprintf(const char * format, ...) -{ - char Mess[10000]; - va_list(arglist); - - va_start(arglist, format); - vsprintf(Mess, format, arglist); - strcat(Mess, "\r\n"); - OutputDebugString(Mess); - - return; -} - -unsigned short int compute_crc(unsigned char *buf, int txlen); - -extern SOCKADDR_IN reportdest; - -extern SOCKET ReportSocket; - -extern SOCKADDR_IN Chatreportdest; - -DllExport VOID APIENTRY SendChatReport(SOCKET ChatReportSocket, char * buff, int txlen) -{ - unsigned short int crc = compute_crc(buff, txlen); - - crc ^= 0xffff; - - buff[txlen++] = (crc&0xff); - buff[txlen++] = (crc>>8); - - sendto(ChatReportSocket, buff, txlen, 0, (LPSOCKADDR)&Chatreportdest, sizeof(Chatreportdest)); -} - -VOID CreateRegBackup() -{ - char Backup1[MAX_PATH]; - char Backup2[MAX_PATH]; - char RegFileName[MAX_PATH]; - char Msg[80]; - HANDLE handle; - int len, written; - char RegLine[300]; - -// SHELLEXECUTEINFO sei; -// STARTUPINFO SInfo; -// PROCESS_INFORMATION PInfo; - - sprintf(RegFileName, "%s\\BPQ32.reg", BPQDirectory); - - // Keep 4 Generations - - strcpy(Backup2, RegFileName); - strcat(Backup2, ".bak.3"); - - strcpy(Backup1, RegFileName); - strcat(Backup1, ".bak.2"); - - DeleteFile(Backup2); // Remove old .bak.3 - MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 - - strcpy(Backup2, RegFileName); - strcat(Backup2, ".bak.1"); - - MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 - - strcpy(Backup1, RegFileName); - strcat(Backup1, ".bak"); - - MoveFile(Backup1, Backup2); //Move .bak to .bak.1 - - strcpy(Backup2, RegFileName); - strcat(Backup2, ".bak"); - - CopyFile(RegFileName, Backup2, FALSE); // Copy to .bak - - handle = CreateFile(RegFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - - if (handle == INVALID_HANDLE_VALUE) - { - sprintf(Msg, "Failed to open Registry Save File\n"); - WritetoConsole(Msg); - return; - } - - len = sprintf(RegLine, "Windows Registry Editor Version 5.00\r\n\r\n"); - WriteFile(handle, RegLine, len, &written, NULL); - - if (SaveReg("Software\\G8BPQ\\BPQ32", handle)) - WritetoConsole("Registry Save complete\n"); - else - WritetoConsole("Registry Save failed\n"); - - CloseHandle(handle); - return ; -/* - - if (REGTREE == HKEY_LOCAL_MACHINE) // < Vista - { - sprintf(cmd, - "regedit /E \"%s\\BPQ32.reg\" %s\\Software\\G8BPQ\\BPQ32", BPQDirectory, REGTREETEXT); - - ZeroMemory(&SInfo, sizeof(SInfo)); - - SInfo.cb=sizeof(SInfo); - SInfo.lpReserved=NULL; - SInfo.lpDesktop=NULL; - SInfo.lpTitle=NULL; - SInfo.dwFlags=0; - SInfo.cbReserved2=0; - SInfo.lpReserved2=NULL; - - if (CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0 ,NULL, NULL, &SInfo, &PInfo) == 0) - { - sprintf(Msg, "Error: CreateProcess for regedit failed 0%d\n", GetLastError() ); - WritetoConsole(Msg); - return; - } - } - else - { - - sprintf(cmd, - "/E \"%s\\BPQ32.reg\" %s\\Software\\G8BPQ\\BPQ32", BPQDirectory, REGTREETEXT); - - ZeroMemory(&sei, sizeof(sei)); - - sei.cbSize = sizeof(SHELLEXECUTEINFOW); - sei.hwnd = hWnd; - sei.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI; - sei.lpVerb = "runas"; - sei.lpFile = "regedit.exe"; - sei.lpParameters = cmd; - sei.nShow = SW_SHOWNORMAL; - - if (!ShellExecuteEx(&sei)) - { - sprintf(Msg, "Error: ShellExecuteEx for regedit failed %d\n", GetLastError() ); - WritetoConsole(Msg); - return; - } - } - - sprintf(Msg, "Registry Save Initiated\n", fn); - WritetoConsole(Msg); - - return ; -*/ -} - -BOOL CALLBACK EnumForCloseProc(HWND hwnd, LPARAM lParam) -{ - struct TNCINFO * TNC = (struct TNCINFO *)lParam; - UINT ProcessId; - - GetWindowThreadProcessId(hwnd, &ProcessId); - - for (i=0; i< AttachedProcesses; i++) - { - if (AttachedPIDList[i] == ProcessId) - { - Debugprintf("BPQ32 Close All Closing PID %d", ProcessId); - PostMessage(hwnd, WM_CLOSE, 1, 1); - // AttachedPIDList[i] = 0; // So we don't do it again - break; - } - } - - return (TRUE); -} -DllExport BOOL APIENTRY RestoreFrameWindow() -{ - return ShowWindow(FrameWnd, SW_RESTORE); -} - -DllExport VOID APIENTRY CreateNewTrayIcon() -{ - Shell_NotifyIcon(NIM_DELETE,&niData); - trayMenu = NULL; -} - -DllExport VOID APIENTRY CloseAllPrograms() -{ -// HANDLE hProc; - - // Close all attached BPQ32 programs - - Closing = TRUE; - - ShowWindow(FrameWnd, SW_RESTORE); - - GetWindowRect(FrameWnd, &FRect); - - SaveBPQ32Windows(); - CloseHostSessions(); - - if (AttachedProcesses == 1) - CloseBPQ32(); - - Debugprintf("BPQ32 Close All Processes %d PIDS %d %d %d %d", AttachedProcesses, AttachedPIDList[0], - AttachedPIDList[1], AttachedPIDList[2], AttachedPIDList[3]); - - if (MinimizetoTray) - Shell_NotifyIcon(NIM_DELETE,&niData); - - EnumWindows(EnumForCloseProc, (LPARAM)NULL); -} - -#define MAX_KEY_LENGTH 255 -#define MAX_VALUE_NAME 16383 -#define MAX_VALUE_DATA 65536 - -BOOL CopyReg(HKEY hKeyIn, HKEY hKeyOut) -{ - TCHAR achKey[MAX_KEY_LENGTH]; // buffer for subkey name - DWORD cbName; // size of name string - TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name - DWORD cchClassName = MAX_PATH; // size of class string - DWORD cSubKeys=0; // number of subkeys - DWORD cbMaxSubKey; // longest subkey size - DWORD cchMaxClass; // longest class string - DWORD cValues; // number of values for key - DWORD cchMaxValue; // longest value name - DWORD cbMaxValueData; // longest value data - DWORD cbSecurityDescriptor; // size of security descriptor - FILETIME ftLastWriteTime; // last write time - - DWORD i, retCode; - - TCHAR achValue[MAX_VALUE_NAME]; - DWORD cchValue = MAX_VALUE_NAME; - - // Get the class name and the value count. - retCode = RegQueryInfoKey( - hKeyIn, // key handle - achClass, // buffer for class name - &cchClassName, // size of class string - NULL, // reserved - &cSubKeys, // number of subkeys - &cbMaxSubKey, // longest subkey size - &cchMaxClass, // longest class string - &cValues, // number of values for this key - &cchMaxValue, // longest value name - &cbMaxValueData, // longest value data - &cbSecurityDescriptor, // security descriptor - &ftLastWriteTime); // last write time - - // Enumerate the subkeys, until RegEnumKeyEx fails. - - if (cSubKeys) - { - Debugprintf( "\nNumber of subkeys: %d\n", cSubKeys); - - for (i=0; i 76) - { - len = sprintf(RegLine, "%s\\\r\n", RegLine); - WriteFile(hFile, RegLine, len, &written, NULL); - strcpy(RegLine, " "); - len = 2; - } - - len = sprintf(RegLine, "%s%02x,", RegLine, Value[k]); - } - RegLine[--len] = 0x0d; - RegLine[++len] = 0x0a; - len++; - - break; - - case REG_DWORD: //( 4 ) // 32-bit number -// case REG_DWORD_LITTLE_ENDIAN: //( 4 ) // 32-bit number (same as REG_DWORD) - - memcpy(&Intval, Value, 4); - len = sprintf(RegLine, "\"%s\"=dword:%08x\r\n", achValue, Intval); - break; - - case REG_DWORD_BIG_ENDIAN: //( 5 ) // 32-bit number - break; - case REG_LINK: //( 6 ) // Symbolic Link (unicode) - break; - case REG_MULTI_SZ: //( 7 ) // Multiple Unicode strings - - len = sprintf(RegLine, "\"%s\"=hex(7):%02x,00,", achValue, Value[0]); - for (k = 1; k < ValLen; k++) - { - if (len > 76) - { - len = sprintf(RegLine, "%s\\\r\n", RegLine); - WriteFile(hFile, RegLine, len, &written, NULL); - strcpy(RegLine, " "); - len = 2; - } - len = sprintf(RegLine, "%s%02x,", RegLine, Value[k]); - if (len > 76) - { - len = sprintf(RegLine, "%s\\\r\n", RegLine); - WriteFile(hFile, RegLine, len, &written, NULL); - strcpy(RegLine, " "); - } - len = sprintf(RegLine, "%s00,", RegLine); - } - - RegLine[--len] = 0x0d; - RegLine[++len] = 0x0a; - len++; - break; - - case REG_RESOURCE_LIST: //( 8 ) // Resource list in the resource map - break; - case REG_FULL_RESOURCE_DESCRIPTOR: //( 9 ) // Resource list in the hardware description - break; - case REG_RESOURCE_REQUIREMENTS_LIST://( 10 ) - break; - case REG_QWORD: //( 11 ) // 64-bit number -// case REG_QWORD_LITTLE_ENDIAN: //( 11 ) // 64-bit number (same as REG_QWORD) - break; - - } - - WriteFile(hFile, RegLine, len, &written, NULL); - } - } - } - - WriteFile(hFile, "\r\n", 2, &written, NULL); - - // Enumerate the subkeys, until RegEnumKeyEx fails. - - if (cSubKeys) - { - for (i=0; i> 1; - } - - Flags=GetApplFlags(i); - - if (OneBits > 1) - sprintf(&NewScreen[(i+1)*54],"%2d%s%3d %3d %3d %03x %3x %10s%-20s", - i, flag, RXCount(i), TXCount(i), MONCount(i), Mask, Flags, callsign, - BPQHOSTVECTOR[i-1].PgmName); - else - sprintf(&NewScreen[(i+1)*54],"%2d%s%3d %3d %3d %3d %3x %10s%-20s", - i, flag, RXCount(i), TXCount(i), MONCount(i), AppNumber, Flags, callsign, - BPQHOSTVECTOR[i-1].PgmName); - - } - } - - #include "StdExcept.c" - - if (Semaphore.Flag && Semaphore.SemProcessID == GetCurrentProcessId()) - FreeSemaphore(&Semaphore); - - } - - if (memcmp(Screen, NewScreen, 33 * 108) == 0) // No Change - return 0; - - memcpy(Screen, NewScreen, 33 * 108); - InvalidateRect(StatusWnd,NULL,FALSE); - - return(0); -} - -LRESULT CALLBACK StatusWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - int wmId, wmEvent; - PAINTSTRUCT ps; - HDC hdc; - HFONT hOldFont ; - HGLOBAL hMem; - MINMAXINFO * mmi; - int i; - - switch (message) - { - case WM_TIMER: - - if (Semaphore.Flag == 0) - DoStatus(); - break; - - case WM_MDIACTIVATE: - - // Set the system info menu when getting activated - - if (lParam == (LPARAM) hWnd) - { - // Activate - - RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); - AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)hConsMenu, "Actions"); - SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); - } - else - { - SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); - } - - DrawMenuBar(FrameWnd); - - return TRUE; //DefMDIChildProc(hWnd, message, wParam, lParam); - - case WM_GETMINMAXINFO: - - mmi = (MINMAXINFO *)lParam; - mmi->ptMaxSize.x = 850; - mmi->ptMaxSize.y = 500; - mmi->ptMaxTrackSize.x = 850; - mmi->ptMaxTrackSize.y = 500; - - - case WM_COMMAND: - - wmId = LOWORD(wParam); // Remember, these are... - wmEvent = HIWORD(wParam); // ...different for Win32! - - //Parse the menu selections: - - switch (wmId) - { - -/* - case BPQSTREAMS: - - CheckMenuItem(hMenu,BPQSTREAMS,MF_CHECKED); - CheckMenuItem(hMenu,BPQIPSTATUS,MF_UNCHECKED); - - StreamDisplay = TRUE; - - break; - - case BPQIPSTATUS: - - CheckMenuItem(hMenu,BPQSTREAMS,MF_UNCHECKED); - CheckMenuItem(hMenu,BPQIPSTATUS,MF_CHECKED); - - StreamDisplay = FALSE; - memset(Screen, ' ', 4000); - - - break; - -*/ - - case BPQCOPY: - - // - // Copy buffer to clipboard - // - hMem=GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, 33*110); - - if (hMem != 0) - { - if (OpenClipboard(hWnd)) - { -// CopyScreentoBuffer(GlobalLock(hMem)); - GlobalUnlock(hMem); - EmptyClipboard(); - SetClipboardData(CF_TEXT,hMem); - CloseClipboard(); - } - else - { - GlobalFree(hMem); - } - - } - - break; - - } - - return DefMDIChildProc(hWnd, message, wParam, lParam); - - - case WM_SYSCOMMAND: - - wmId = LOWORD(wParam); // Remember, these are... - wmEvent = HIWORD(wParam); // ...different for Win32! - - switch (wmId) - { - case SC_MAXIMIZE: - - break; - - case SC_MINIMIZE: - - StatusMinimized = TRUE; - break; - - case SC_RESTORE: - - StatusMinimized = FALSE; - SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); - break; - } - - return DefMDIChildProc(hWnd, message, wParam, lParam); - - case WM_PAINT: - - hdc = BeginPaint (hWnd, &ps); - - hOldFont = SelectObject( hdc, hFont) ; - - for (i=0; i<33; i++) - { - TextOut(hdc,0,i*14,&Screen[i*108],108); - } - - SelectObject( hdc, hOldFont ) ; - EndPaint (hWnd, &ps); - - break; - - case WM_DESTROY: - -// PostQuitMessage(0); - - break; - - - default: - - return DefMDIChildProc(hWnd, message, wParam, lParam); - - } - return (0); -} - -VOID SaveMDIWindowPos(HWND hWnd, char * RegKey, char * Value, BOOL Minimized) -{ - HKEY hKey=0; - char Size[80]; - char Key[80]; - int retCode, disp; - RECT Rect; - - if (IsWindow(hWnd) == FALSE) - return; - - ShowWindow(hWnd, SW_RESTORE); - - if (GetWindowRect(hWnd, &Rect) == FALSE) - return; - - // Make relative to Frame - - Rect.top -= FRect.top ; - Rect.left -= FRect.left; - Rect.bottom -= FRect.top; - Rect.right -= FRect.left; - - sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\%s", RegKey); - - retCode = RegCreateKeyEx(REGTREE, Key, 0, 0, 0, - KEY_ALL_ACCESS, NULL, &hKey, &disp); - - if (retCode == ERROR_SUCCESS) - { - sprintf(Size,"%d,%d,%d,%d,%d", Rect.left, Rect.right, Rect.top ,Rect.bottom, Minimized); - retCode = RegSetValueEx(hKey, Value, 0, REG_SZ,(BYTE *)&Size, strlen(Size)); - RegCloseKey(hKey); - } -} - -extern int GPSPort; -extern char LAT[]; // in standard APRS Format -extern char LON[]; // in standard APRS Format - -VOID SaveBPQ32Windows() -{ - HKEY hKey=0; - char Size[80]; - int retCode, disp; - PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; - int i; - - retCode = RegCreateKeyEx(REGTREE, "SOFTWARE\\G8BPQ\\BPQ32", 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); - - if (retCode == ERROR_SUCCESS) - { - sprintf(Size,"%d,%d,%d,%d", FRect.left, FRect.right, FRect.top, FRect.bottom); - retCode = RegSetValueEx(hKey, "FrameWindowSize", 0, REG_SZ, (BYTE *)&Size, strlen(Size)); - - // Save GPS Position - - if (GPSPort) - { - sprintf(Size, "%s, %s", LAT, LON); - retCode = RegSetValueEx(hKey, "GPS", 0, REG_SZ,(BYTE *)&Size, strlen(Size)); - } - - RegCloseKey(hKey); - } - - SaveMDIWindowPos(StatusWnd, "", "StatusWindowSize", StatusMinimized); - SaveMDIWindowPos(hConsWnd, "", "WindowSize", ConsoleMinimized); - - for (i=0; iPORTCONTROL.PORTTYPE == 0x10) // External - { - if (PORTVEC->PORT_EXT_ADDR) - { - SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); - SaveAXIPWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); - } - } - PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; - } - - SaveWindowPos(70); // Rigcontrol - - - if (hIPResWnd) - SaveMDIWindowPos(hIPResWnd, "", "IPResSize", IPMinimized); - - SaveHostSessions(); -} - -DllExport BOOL APIENTRY CheckIfOwner() -{ - // - // Returns TRUE if current process is root process - // that loaded the DLL - // - - if (TimerInst == GetCurrentProcessId()) - - return (TRUE); - else - return (FALSE); -} - -VOID GetParam(char * input, char * key, char * value) -{ - char * ptr = strstr(input, key); - char Param[2048]; - char * ptr1, * ptr2; - char c; - - - if (ptr) - { - ptr2 = strchr(ptr, '&'); - if (ptr2) *ptr2 = 0; - strcpy(Param, ptr + strlen(key)); - if (ptr2) *ptr2 = '&'; // Restore string - - // Undo any % transparency - - ptr1 = Param; - ptr2 = Param; - - c = *(ptr1++); - - while (c) - { - if (c == '%') - { - int n; - int m = *(ptr1++) - '0'; - if (m > 9) m = m - 7; - n = *(ptr1++) - '0'; - if (n > 9) n = n - 7; - - *(ptr2++) = m * 16 + n; - } - else if (c == '+') - *(ptr2++) = ' '; - else - *(ptr2++) = c; - - c = *(ptr1++); - } - - *(ptr2++) = 0; - - strcpy(value, Param); - } -} - -int GetListeningPortsPID(int Port) -{ - MIB_TCPTABLE_OWNER_PID * TcpTable = NULL; - PMIB_TCPROW_OWNER_PID Row; - int dwSize = 0; - DWORD n; - - // Get PID of process for this TCP Port - - // Get Length of table - - GetExtendedTcpTable(TcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_LISTENER, 0); - - TcpTable = malloc(dwSize); - - if (TcpTable == NULL) - return 0; - - GetExtendedTcpTable(TcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_LISTENER, 0); - - for (n = 0; n < TcpTable->dwNumEntries; n++) - { - Row = &TcpTable->table[n]; - - if (Row->dwLocalPort == Port && Row->dwState == MIB_TCP_STATE_LISTEN) - { - return Row->dwOwningPid; - break; - } - } - return 0; // Not found -} - -DllExport char * APIENTRY GetLOC() -{ - return LOC; -} - -DllExport void APIENTRY GetLatLon(double * lat, double * lon) -{ - *lat = LatFromLOC; - *lon = LonFromLOC; - return; -} - - -// UZ7HO Dll PTT interface - -// 1 ext_PTT_info -// 2 ext_PTT_settings -// 3 ext_PTT_OFF -// 4 ext_PTT_ON -// 5 ext_PTT_close -// 6 ext_PTT_open - -extern struct RIGINFO * DLLRIG; // Rig record for dll PTT interface (currently only for UZ7HO); - -VOID Rig_PTT(struct TNCINFO * TNC, BOOL PTTState); -VOID Rig_PTTEx(struct RIGINFO * RIG, BOOL PTTState, struct TNCINFO * TNC); - -int WINAPI ext_PTT_info() -{ - return 0; -} - -int WINAPI ext_PTT_settings() -{ - return 0; -} - -int WINAPI ext_PTT_OFF(int Port) -{ - if (DLLRIG) - Rig_PTTEx(DLLRIG, 0, 0); - - return 0; -} - -int WINAPI ext_PTT_ON(int Port) -{ - if (DLLRIG) - Rig_PTTEx(DLLRIG, 1, 0); - - return 0; -} -int WINAPI ext_PTT_close() -{ - if (DLLRIG) - Rig_PTTEx(DLLRIG, 0, 0); - - return 0; -} - -DllExport INT WINAPI ext_PTT_open() -{ - return 1; -} - -char * stristr (char *ch1, char *ch2) -{ - char *chN1, *chN2; - char *chNdx; - char *chRet = NULL; - - chN1 = _strdup(ch1); - chN2 = _strdup(ch2); - - if (chN1 && chN2) - { - chNdx = chN1; - while (*chNdx) - { - *chNdx = (char) tolower(*chNdx); - chNdx ++; - } - chNdx = chN2; - - while (*chNdx) - { - *chNdx = (char) tolower(*chNdx); - chNdx ++; - } - - chNdx = strstr(chN1, chN2); - - if (chNdx) - chRet = ch1 + (chNdx - chN1); - } - - free (chN1); - free (chN2); - return chRet; -} - +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modifyextern int HTTP +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ +// +// 409l Oct 2001 Fix l3timeout for KISS +// +// 409m Oct 2001 Fix Crossband Digi +// +// 409n May 2002 Change error handling on load ext DLL + +// 409p March 2005 Allow Multidigit COM Ports (kiss.c) + +// 409r August 2005 Treat NULL string in Registry as use current directory +// Allow shutdown to close BPQ Applications + +// 409s October 2005 Add DLL:Export entries to API for BPQTNC2 + +// 409t January 2006 +// +// Add API for Perl "GetPerlMsg" +// Add API for BPQ1632 "GETBPQAPI" - returns address of Assembler API routine +// Add Registry Entry "BPQ Directory". If present, overrides "Config File Location" +// Add New API "GetBPQDirectory" - Returns location of config file +// Add New API "ChangeSessionCallsign" - equivalent to "*** linked to" command +// Rename BPQNODES to BPQNODES.dat +// New API "GetAttachedProcesses" - returns number of processes connected. +// Warn if user trys to close Console Window. +// Add Debug entries to record Process Attach/Detach +// Fix recovery following closure of first process + +// 409t Beta 2 February 2006 +// +// Add API Entry "GetPortNumber" +// +// 409u February 2006 +// +// Fix crash if allocate/deallocate called with stream=0 +// Add API to ch +// Display config file path +// Fix saving of Locked Node flag +// Added SAVENODES SYSOP command +// +// 409u 2 March 2006 +// +// Fix SetupBPQDirectory +// Add CopyBPQDirectory (for Basic Programs) +// +// 409u 3 March 2006 +// +// Release streams on DLL unload + +// 409v October 2006 +// +// Support Minimize to Tray for all BPQ progams +// Implement L4 application callsigns + +// 410 November 2006 +// +// Modified to compile with C++ 2005 Express Edition +// Make MCOM MTX MMASK local variables +// +// 410a January 2007 +// +// Add program name to Attach-Detach messages +// Attempt to detect processes which have died +// Fix bug in NETROM and IFrame decode which would cause crash if frame was corrupt +// Add BCALL - origin call for Beacons +// Fix KISS ACKMODE ACK processing +// + +// 410b November 2007 +// +// Allow CTEXT of up to 510, and enforce PACLEN, fragmenting if necessary + +// 410c December 2007 + +// Fix problem with NT introduced in V410a +// Display location of DLL on Console + +// 410d January 2008 + +// Fix crash in DLL Init caused by long path to program +// Invoke Appl2 alias on C command (if enabled) +// Allow C command to be disabled +// Remove debug trap in GETRAWFRAME +// Validate Alias of directly connected node, mainly for KPC3 DISABL Problem +// Move Port statup code out of DLLInit (mainly for perl) +// Changes to allow Load/Unload of bpq32.dll by appl +// CloseBPQ32 API added +// Ext Driver Close routes called +// Changes to release Mutex + +// 410e May 2008 + +// Fix missing SSID on last call of UNPROTO string (CONVTOAX25 in main.asm) +// Fix VCOM Driver (RX Len was 1 byte too long) +// Fix possible crash on L4CODE if L4DACK received out of sequence +// Add basic IP decoding + +// 410f October 2008 + +// Add IP Gateway +// Add Multiport DIGI capability +// Add GetPortDescription API +// Fix potential hangs if RNR lost +// Fix problem if External driver failes to load +// Put pushad/popad round _INITIALISEPORTS (main.asm) +// Add APIs GetApplCallVB and GetPortDescription (mainly for RMS) +// Ensure Route Qual is updated if Port Qual changed +// Add Reload Option, plus menu items for DUMP and SAVENODES + +// 410g December 2008 + +// Restore API Exports BPQHOSTAPIPTR and MONDECODEPTR (accidentally deleted) +// Fix changed init of BPQDirectory (accidentally changed) +// Fix Checks for lost processes (accidentally deleted) +// Support HDLC Cards on W2K and above +// Delete Tray List entries for crashed processes +// Add Option to NODES command to sort by Callsign +// Add options to save or clear BPQNODES before Reconfig. +// Fix Reconfig in Win98 +// Monitor buffering tweaks +// Fix Init for large (>64k) tables +// Fix Nodes count in Stats + +// 410h January 2009 + +// Add Start Minimized Option +// Changes to KISS for WIn98 Virtual COM +// Open \\.\com instead of //./COM +// Extra Dignostics + +// 410i Febuary 2009 + +// Revert KISS Changes +// Save Window positions + +// 410j June 2009 + +// Fix tidying of window List when program crashed +// Add Max Nodes to Stats +// Don't update APPLnALIAS with received NODES info +// Fix MH display in other timezones +// Fix Possible crash when processing NETROM type Zero frames (eg NRR) +// Basic INP3 Stuff +// Add extra diagnostics to Lost Process detection +// Process Netrom Record Route frames. + +// 410k June 2009 + +// Fix calculation of %retries in extended ROUTES display +// Fix corruption of ROUTES table + +// 410l October 2009 + +// Add GetVersionString API call. +// Add GetPortTableEntry API call +// Keep links to neighbouring nodes open + +// Build 2 + +// Fix PE in NOROUTETODEST (missing POP EBX) + +// 410m November 2009 + +// Changes for PACTOR and WINMOR to support the ATTACH command +// Enable INP3 if configured on a route. +// Fix count of nodes in Stats Display +// Overwrite the worst quality unused route if a call is received from a node not in your +// table when the table is full + +// Build 5 + +// Rig Control Interface +// Limit KAM VHF attach and RADIO commands to authorised programs (MailChat and BPQTerminal) + +// Build 6 + +// Fix reading INP3 Flag from BPQNODES + +// Build 7 + +// Add MAXHOPS and MAXRTT config options + +// Build 8 + +// Fix INP3 deletion of Application Nodes. +// Fix GETCALLSIGN for Pactor Sessions +// Add N Call* to display all SSID's of a call +// Fix flow control on Pactor sessions. + +// Build 9 + +// HDLC Support for XP +// Add AUTH routines + +// Build 10 + +// Fix handling commands split over more that one packet. + +// Build 11 + +// Attach cmd changes for winmor disconnecting state +// Option Interlock Winmor/Pactor ports + +// Build 12 + +// Add APPLS export for winmor +// Handle commands ending CR LF + +// Build 13 + +// Incorporate Rig Control in Kernel + +// Build 14 + +// Fix config reload for Rig COntrol + +// 410n March 2010 + +// Implement C P via PACTOR/WINMOR (for Airmail) + +// Build 2 + +// Don't flip SSID bits on Downlink Connect if uplink is Pactor/WINMOR +// Fix resetting IDLE Timer on Pactor/WINMOR sessions +// Send L4 KEEPLI messages based on IDLETIME + +// 410o July 2010 + +// Read bpqcfg.txt instead of .bin +// Support 32 bit MMASK (Allowing 32 Ports) +// Support 32 bit _APPLMASK (Allowing 32 Applications) +// Allow more commands +// Allow longer command aliases +// Fix logic error in RIGControl Port Initialisation (wasn't always raising RTS and DTR +// Clear RIGControl RTS and DTR on close + +// 410o Build 2 August 2010 + +// Fix couple of errors in config (needed APPLICATIONS and BBSCALL/ALIAS/QUAL) +// Fix Kenwood Rig Control when more than one message received at once. +// Save minimzed state of Rigcontrol Window + +// 410o Build 3 August 2010 + +// Fix reporting of set errors in scan to a random session + +// 410o Build 4 August 2010 + +// Change All xxx Ports are in use to no xxxx Ports are available if there are no sessions with _APPLMASK +// Fix validation of TRANSDELAY + +// 410o Build 5 August 2010 + +// Add Repeater Shift and Set Data Mode options to Rigcontrol (for ICOM only) +// Add WINMOR and SCS Pactor mode control option to RigControl +// Extend INFOMSG to 2000 bytes +// Improve Scan freq change lock (check both SCS and WINMOR Ports) + +// 410o Build 6 September 2010 + +// Incorporate IPGateway in main code. +// Fix GetSessionInfo for Pactor/Winmor Ports +// Add Antenna Selection to RigControl +// Allow Bandwidth options on RADIO command line (as well as in Scan definitions) + +// 410o Build 7 September 2010 + +// Move rigconrtol display to driver windows +// Move rigcontrol config to driver config. +// Allow driver and IPGateway config info in bpq32.cfg +// Move IPGateway, AXIP, VKISS, AGW and WINMOR drivers into bpq32.dll +// Add option to reread IP Gateway config. +// Fix Reinit after process with timer closes (error in TellSessions). + +// 410p Build 2 October 2010 + +// Move KAM and SCS drivers to bpq32.dll + +// 410p Build 3 October 2010 + +// Support more than one axip port. + +// 410p Build 4 October 2010 + +// Dynamically load psapi.dll (for 98/ME) + +// 410p Build 5 October 2010 + +// Incorporate TelnetServer +// Fix AXIP ReRead Config +// Report AXIP accept() fails to syslog, not a popup. + +// 410p Build 6 October 2010 + +// Includes HAL support +// Changes to Pactor Drivers disconnect code +// AXIP now sends with source port = dest port, unless overridden by SOURCEPORT param +// Config now checks for duplicate port definitions +// Add Node Map reporting +// Fix WINMOR deferred disconnect. +// Report Pactor PORTCALL to WL2K instead of RMS Applcall + +// 410p Build 7 October 2010 + +// Add In/Out flag to Map reporting, and report centre, not dial +// Write Telnet log to BPQ Directory +// Add Port to AXIP resolver display +// Send Reports to update.g8bpq.net:81 +// Add support for FT100 to Rigcontrol +// Add timeout to Rigcontrol PTT +// Add Save Registry Command + +// 410p Build 8 November 2010 + +// Add NOKEEPALIVES Port Param +// Renumbered for release + +// 410p Build 9 November 2010 + +// Get Bandwith for map report from WL2K Report Command +// Fix freq display for FT100 (was KHz, not MHz) +// Don't try to change SCS mode whilst initialising +// Allow reporting of Lat/Lon as well as Locator +// Fix Telnet Log Name +// Fix starting with Minimized windows when Minimizetotray isn't set +// Extra Program Error trapping in SessionControl +// Fix reporting same freq with different bandwidths at different times. +// Code changes to support SCS Robust Packet Mode. +// Add FT2000 to Rigcontrol +// Only Send CTEXT to connects to Node (not to connects to an Application Call) + +// Released as Build 10 + +// 410p Build 11 January 2011 + +// Fix MH Update for SCS Outgoing Calls +// Add Direct CMS Access to TelnetServer +// Restructure DISCONNECT processing to run in Timer owning process + +// 410p Build 12 January 2011 + +// Add option for Hardware PTT to use a different com port from the scan port +// Add CAT PTT for Yaesu 897 (and maybe others) +// Fix RMS Packet ports busy after restart +// Fix CMS Telnet with MAXSESSIONS > 10 + +// 410p Build 13 January 2011 + +// Fix loss of buffers in TelnetServer +// Add CMS logging. +// Add non - Promiscuous mode option for BPQETHER + +// 410p Build 14 January 2011 + +// Add support for BPQTermTCP +// Allow more that one FBBPORT +// Allow Telnet FBB mode sessions to send CRLF as well as CR on user and pass msgs +// Add session length to CMS Telnet logging. +// Return Secure Session Flag from GetConnectionInfo +// Show Uptime as dd/hh/mm + +// 4.10.16.17 March 2011 + +// Add "Close all programs" command +// Add BPQ Program Directory registry key +// Use HKEY_CURRENT_USER on Vista and above (and move registry if necessary) +// Time out IP Gateway ARP entries, and only reload ax.25 ARP entries +// Add support for SCS Tracker HF Modes +// Fix WL2K Reporting +// Report Version to WL2K +// Add Driver to support Tracker with multiple sessions (but no scanning, wl2k report, etc) + + +// Above released as 5.0.0.1 + +// 5.2.0.1 + +// Add caching of CMS Server IP addresses +// Initialise TNC State on Pactor Dialogs +// Add Shortened (6 digit) AUTH mode. +// Update MH with all frames (not just I/UI) +// Add IPV6 Support for TelnetServer and AXIP +// Fix TNC OK Test for Tracker +// Fix crash in CMS mode if terminal disconnects while tcp commect in progress +// Add WL2K reporting for Robust Packet +// Add option to suppress WL2K reporting for specific frequencies +// Fix Timeband processing for Rig Control +// New Driver for SCS Tracker allowing multiple connects, so Tracker can be used for user access +// New Driver for V4 TNC + +// 5.2.1.3 October 2011 + +// Combine busy detector on Interlocked Ports (SCS PTC, WINMOR or KAM) +// Improved program error logging +// WL2K reporting changed to new format agreed with Lee Inman + +// 5.2.3.1 January 2012 + +// Connects from the console to an APPLCALL or APPLALIAS now invoke any Command Alias that has been defined. +// Fix reporting of Tracker freqs to WL2K. +// Fix Tracker monitoring setup (sending M UISC) +// Fix possible call/application routing error on RP +// Changes for P4Dragon +// Include APRS Digi/IGate +// Tracker monitoring now includes DIGIS +// Support sending UI frames using SCSTRACKER, SCTRKMULTI and UZ7HO drivers +// Include driver for UZ7HO soundcard modem. +// Accept DRIVER as well as DLLNAME, and COMPORT as well as IOADDR in bpq32.cfg. COMPORT is decimal +// No longer supports separate config files, or BPQTELNETSERVER.exe +// Improved flow control for Telnet CMS Sessions +// Fix handling Config file without a newline after last line +// Add non - Promiscuous mode option for BPQETHER +// Change Console Window to a Dialog Box. +// Fix possible corruption and loss of buffers in Tracker drivers +// Add Beacon After Session option to Tracker and UZ7HO Drivers +// Rewrite RigControl and add "Reread Config Command" +// Support User Mode VCOM Driver for VKISS ports + +// 5.2.4.1 January 2012 + +// Remove CR from Telnet User and Password Prompts +// Add Rigcontrol to UZ7HO driver +// Fix corruption of Free Buffer Count by Rigcontol +// Fix WINMOR and V4 PTT +// Add MultiPSK Driver +// Add SendBeacon export for BPQAPRS +// Add SendChatReport function +// Fix check on length of Port Config ID String with trailing spaces +// Fix interlock when Port Number <> Port Slot +// Add NETROMCALL for L3 Activity +// Add support for APRS Application +// Fix Telnet with FBBPORT and no TCPPORT +// Add Reread APRS Config +// Fix switching to Pactor after scanning in normal packet mode (PTC) + +// 5.2.5.1 February 2012 + +// Stop reading Password file. +// Add extra MPSK commands +// Fix MPSK Transparency +// Make LOCATOR command compulsory +// Add MobileBeaconInterval APRS param +// Send Course and Speed when APRS is using GPS +// Fix Robust Packet reporting in PTC driver +// Fix corruption of some MIC-E APRS packets + +// 5.2.6.1 February 2012 + +// Convert to MDI presentation of BPQ32.dll windows +// Send APRS Status packets +// Send QUIT not EXIT in PTC Init +// Implement new WL2K reporting format and include traffic reporting info in CMS signon +// New WL2KREPORT format +// Prevent loops when APPL alias refers to itself +// Add RigControl for Flex radios and ICOM IC-M710 Marine radio + +// 5.2.7.1 + +// Fix opening more thn one console window on Win98 +// Change method of configuring multiple timelots on WL2K reporting +// Add option to update WK2K Sysop Database +// Add Web server +// Add UIONLY port option + +// 5.2.7.2 + +// Fix handling TelnetServer packets over 500 bytes in normal mode + +// 5.2.7.3 + +// Fix Igate handling packets from UIView + +// 5.2.7.4 + +// Prototype Baycom driver. + +// 5.2.7.5 + +// Set WK2K group ref to MARS (3) if using a MARS service code + +// 5.2.7.7 + +// Check for programs calling CloseBPQ32 when holding semaphore +// Try/Except round Status Timer Processing + +// 5.2.7.8 + +// More Try/Except round Timer Processing + +// 5.2.7.9 + +// Enable RX in Baycom, and remove test loopback in tx + +// 5.2.7.10 + +// Try/Except round ProcessHTTPMessage + +// 5.2.7.11 + +// BAYCOM tweaks + +// 5.2.7.13 + +// Release semaphore after program error in Timer Processing +// Check fro valid dest in REFRESHROUTE + + +// Add TNC-X KISSOPTION (includes the ACKMODE bytes in the checksum( + +// Version 5.2.9.1 Sept 2012 + +// Fix using KISS ports with COMn > 16 +// Add "KISS over UDP" driver for PI as a TNC concentrator + +// Version 6.0.1.1 + +// Convert to C for linux portability +// Try to speed up kiss polling + +// Version 6.0.2.1 + +// Fix operation on Win98 +// Fix callsign error with AGWtoBPQ +// Fix PTT problem with WINMOR +// Fix Reread telnet config +// Add Secure CMS signon +// Fix error in cashing addresses of CMS servers +// Fix Port Number when using Send Raw. +// Fix PE in KISS driver if invalid subchannel received +// Fix Orignal address of beacons +// Speed up Telnet port monitoring. +// Add TNC Emulators +// Add CountFramesQueuedOnStream API +// Limit number of frames that can be queued on a session. +// Add XDIGI feature +// Add Winmor Robust Mode switching for compatibility with new Winmor TNC +// Move most APRS code from BPQAPRS to here +// Stop corruption caused by overlong KISS frames + +// Version 6.0.3.1 + +// Add starting/killing WINMOR TNC on remote host +// Fix Program Error when APRS Item or Object name is same as call of reporting station +// Dont digi a frame that we have already digi'ed +// Add ChangeSessionIdleTime API +// Add WK2KSYSOP Command +// Add IDLETIME Command +// Fix Errors in RELAYAPPL processing +// Fix PE cauaed by invalid Rigcontrol Line + +// Version 6.0.4.1 + +// Add frequency dependent autoconnect appls for SCS Pactor +// Fix DED Monitoring of I and UI with no data +// Include AGWPE Emulator (from AGWtoBPQ) +// accept DEL (Hex 7F) as backspace in Telnet +// Fix re-running resolver on re-read AXIP config +// Speed up processing, mainly for Telnet Sessions +// Fix APRS init on restart of bpq32.exe +// Change to 2 stop bits +// Fix scrolling of WINMOR trace window +// Fix Crash when ueing DED TNC Emulator +// Fix Disconnect when using BPQDED2 Driver with Telnet Sessions +// Allow HOST applications even when CMS option is disabled +// Fix processing of APRS DIGIMAP command with no targets (didn't suppress default settings) + +// Version 6.0.5.1 January 2014 + +// Add UTF8 conversion mode to Telnet (converts non-UTF-8 chars to UTF-8) +// Add "Clear" option to MH command +// Add "Connect to RMS Relay" Option +// Revert to one stop bit on serial ports, explictly set two on FT2000 rig control +// Fix routing of first call in Robust Packet +// Add Options to switch input source on rigs with build in soundcards (sor far only IC7100 and Kenwood 590) +// Add RTS>CAT PTT option for Sound Card rigs +// Add Clear Nodes Option (NODE DEL ALL) +// SCS Pactor can set differeant APPLCALLS when scanning. +// Fix possible Scan hangup after a manual requency change with SCS Pactor +// Accept Scan entry of W0 to disable WINMOR on that frequency +// Fix corruption of NETROMCALL by SIMPLE config command +// Enforce Pactor Levels +// Add Telnet outward connect +// Add Relay/Trimode Emulation +// Fix V4 Driver +// Add PTT Mux +// Add Locked ARP Entries (via bpq32.cfg) +// Fix IDLETIME node command +// Fix STAY param on connect +// Add STAY option to Attach and Application Commands +// Fix crash on copying a large AXIP MH Window +// Fix possible crash when bpq32.exe dies +// Fix DIGIPORT for UI frames + +// Version 6.0.6.1 April 2014 + +// FLDigi Interface +// Fix "All CMS Servers are inaccessible" message so Mail Forwarding ELSE works. +// Validate INP3 messages to try to prevent crash +// Fix possible crash if an overlarge KISS frame is received +// Fix error in AXR command +// Add LF to Telnet Outward Connect signin if NEEDLF added to connect line +// Add CBELL to TNC21 emulator +// Add sent objects and third party messages to APRS Dup List +// Incorporate UIUtil +// Use Memory Mapped file to pass APRS info to BPQAPRS, and process APRS HTTP in BPQ32 +// Improvements to FLDIGI interlocking +// Fix TNC State Display for Tracker +// Cache CMS Addresses on LinBPQ +// Fix count error on DED Driver when handling 256 byte packets +// Add basic SNMP interface for MRTG +// Fix memory loss from getaddrinfo +// Process "BUSY" response from Tracker +// Handle serial port writes that don't accept all the data +// Trap Error 10038 and try to reopen socket +// Fix crash if overlong command line received + +// Version 6.0.7.1 Aptil 2014 +// Fix RigContol with no frequencies for Kenwood and Yaesu +// Add busy check to FLDIGI connects + +// Version 6.0.8.1 August 2014 + +// Use HKEY_CURRENT_USER on all OS versions +// Fix crash when APRS symbol is a space. +// Fixes for FT847 CAT +// Fix display of 3rd byte of FRMR +// Add "DEFAULT ROBUST" and "FORCE ROBUST" commands to SCSPactor Driver +// Fix possible memory corruption in WINMOR driver +// Fix FT2000 Modes +// Use new WL2K reporting system (Web API Based) +// APRS Server now cycles through hosts if DNS returns more than one +// BPQ32 can now start and stop FLDIGI +// Fix loss of AXIP Resolver when running more than one AXIP port + +// Version 6.0.9.1 November 2014 + +// Fix setting NOKEEPALIVE flag on route created from incoming L3 message +// Ignore NODES from locked route with quality 0 +// Fix seting source port in AXIP +// Fix Dual Stack (IPV4/V6) on Linux. +// Fix RELAYSOCK if IPv6 is enabled. +// Add support for FT1000 +// Fix hang when APRS Messaging packet received on RF +// Attempt to normalize Node qualies when stations use widely differing Route qualities +// Add NODES VIA command to display nodes reachable via a specified neighbour +// Fix applying "DisconnectOnClose" setting on HOST API connects (Telnet Server) +// Fix buffering large messages in Telnet Host API +// Fix occasional crash in terminal part line processing +// Add "NoFallback" command to Telnet server to disable "fallback to Relay" +// Improved support for APPLCALL scanning with Pactor +// MAXBUFFS config statement is no longer needed. +// Fix USEAPPLCALLS with Tracker when connect to APPLCALL fails +// Implement LISTEN and CQ commands +// FLDIGI driver can now start FLDIGI on a remote system. +// Add IGNOREUNLOCKEDROUTES parameter +// Fix error if too many Telnet server connections + +// Version 6.0.10.1 Feb 2015 + +// Fix crash if corrupt HTML request received. +// Allow SSID's of 'R' and 'T' on non-ax.25 ports for WL2K Radio Only network. +// Make HTTP server HTTP Version 1.1 complient - use persistent conections and close after 2.5 mins +// Add INP3ONLY flag. +// Fix program error if enter UNPROTO without a destination path +// Show client IP address on HTTP sessions in Telnet Server +// Reduce frequency and number of attempts to connect to routes when Keepalives or INP3 is set +// Add FT990 RigControl support, fix FT1000MP support. +// Support ARMV5 processors +// Changes to support LinBPQ APRS Client +// Add IC7410 to supported Soundcard rigs +// Add CAT PTT to NMEA type (for ICOM Marine Radios_ +// Fix ACKMODE +// Add KISS over TCP +// Support ACKMode on VKISS +// Improved reporting of configuration file format errors +// Experimental driver to support ARQ sessions using UI frames + +// Version 6.0.11.1 September 2015 + +// Fixes for IPGateway configuration and Virtual Circuit Mode +// Separate Portmapper from IPGateway +// Add PING Command +// Add ARDOP Driver +// Add basic APPLCALL support for PTC-PRO/Dragon 7800 Packet (using MYALIAS) +// Add "VeryOldMode" for KAM Version 5.02 +// Add KISS over TCP Slave Mode. +// Support Pactor and Packet on P4Dragon on one port +// Add "Remote Staton Quality" to Web ROUTES display +// Add Virtual Host option for IPGateway NET44 Encap +// Add NAT for local hosts to IPGateway +// Fix setting filter from RADIO command for IC7410 +// Add Memory Channel Scanning for ICOM Radios +// Try to reopen Rig Control port if it fails (could be unplugged USB) +// Fix restoring position of Monitor Window +// Stop Codec on Winmor and ARDOP when an interlocked port is attached (instead of listen false) +// Support APRS beacons in RP mode on Dragon// +// Change Virtual MAC address on IPGateway to include last octet of IP Address +// Fix "NOS Fragmentation" in IP over ax.25 Virtual Circuit Mode +// Fix sending I frames before L2 session is up +// Fix Flow control on Telnet outbound sessions. +// Fix reporting of unterminatred comments in config +// Add option for RigControl to not change mode on FT100/FT990/FT1000 +// Add "Attach and Connect" for Telnet ports + +// Version 6.0.12.1 November 2015 + +// Fix logging of IP addresses for connects to FBBPORT +// Allow lower case user and passwords in Telnet "Attach and Connect" +// Fix possible hang in KISS over TCP Slave mode +// Fix duplicating LinBPQ process if running ARDOP fails +// Allow lower case command aliases and increase alias length to 48 +// Fix saving long IP frames pending ARP resolution +// Fix dropping last entry from a RIP44 message. +// Fix displaying Digis in MH list +// Add port name to Monitor config screen port list +// Fix APRS command display filter and add port filter +// Support port names in BPQTermTCP Monitor config +// Add FINDBUFFS command to dump lost buffers to Debugview/Syslog +// Buffer Web Mgmt Edit Config output +// Add WebMail Support +// Fix not closing APRS Send WX file. +// Add RUN option to APRS Config to start APRS Client +// LinBPQ run FindLostBuffers and exit if QCOUNT < 5 +// Close and reopen ARDOP connection if nothing received for 90 secs +// Add facility to bridge traffic between ports (similar to APRS Bridge but for all frame types) +// Add KISSOPTION TRACKER to set SCS Tracker into KISS Mode + +// 6.0.13.1 + +// Allow /ex to exit UNPROTO mode +// Support ARQBW commands. +// Support IC735 +// Fix sending ARDOP beacons after a busy holdoff +// Enable BPQDED driver to beacon via non-ax.25 ports. +// Fix channel number in UZ7HO monitoring +// Add SATGate mode to APRSIS Code. +// Fix crash caused by overlong user name in telnet logon +// Add option to log L4 connects +// Add AUTOADDQuiet mode to AXIP. +// Add EXCLUDE processing +// Support WinmorControl in UZ7HO driver and fix starting TNC on Linux +// Convert calls in MAP entries to upper case. +// Support Linux COM Port names for APRS GPS +// Fix using NETROM serial protocol on ASYNC Port +// Fix setting MYLEVEL by scanner after manual level change. +// Add DEBUGLOG config param to SCS Pactor Driver to log serial port traffic +// Uue #myl to set SCS Pactor MYLEVEL, and add checklevel command +// Add Multicast RX interface to FLDIGI Driver +// Fix processing application aliases to a connect command. +// Fix Buffer loss if radio connected to PTC rig port but BPQ not configured to use it +// Save backups of bpq32.cfg when editing with Web interface and report old and new length +// Add DD command to SCS Pactor, and use it for forced disconnect. +// Add ARDOP mode select to scan config +// ARDOP changes for ARDOP V 0.5+ +// Flip SSID bits on UZ7HO downlink connects + + +// Version 6.0.14.1 + +// Fix Socket leak in ARDOP and FLDIGI drivers. +// Add option to change CMS Server hostname +// ARDOP Changes for 0.8.0+ +// Discard Terminal Keepalive message (two nulls) in ARDOP command hander +// Allow parameters to be passed to ARDOP TNC when starting it +// Fix Web update of Beacon params +// Retry connects to KISS ports after failure +// Add support for ARDOP Serial Interface Native mode. +// Fix gating APRS-IS Messages to RF +// Fix Beacons when PORTNUM used +// Make sure old monitor flag is cleared for TermTCP sessions +// Add CI-V antenna control for IC746 +// Don't allow ARDOP beacons when connected +// Add support for ARDOP Serial over I2C +// Fix possble crash when using manual RADIO messages +// Save out of sequence L2 frames for possible reuse after retry +// Add KISS command to send KISS control frame to TNC +// Stop removing unused digis from packets sent to APRS-IS + +// Processing of ARDOP PING and PINGACK responses +// Handle changed encoding of WL2K update responses. +// Allow anonymous logon to telnet +// Don't use APPL= for RP Calls in Dragon Single mode. +// Add basic messaging page to APRS Web Server +// Add debug log option to SCSTracker and TrkMulti Driver +// Support REBOOT command on LinBPQ +// Allow LISTEN command on all ports that support ax.25 monitoring + +// Version 6.0.15.1 Feb 2018 + +// partial support for ax.25 V2.2 +// Add MHU and MHL commands and MH filter option +// Fix scan interlock with ARDOP +// Add Input source seiect for IC7300 +// Remove % transparency from web terminal signon message +// Fix L4 Connects In count on stats +// Fix crash caused by corrupt CMSInfo.txt +// Add Input peaks display to ARDOP status window +// Add options to show time in local and distances in KM on APRS Web pages +// Add VARA support +// Fix WINMOR Busy left set when port Suspended +// Add ARDOP-Packet Support +// Add Antenna Switching for TS 480 +// Fix possible crash in Web Terminal +// Support different Code Pages on Console sessions +// Use new Winlink API interface (api.winlink.org) +// Support USB/ACC switching on TS590SG +// Fix scanning when ARDOP or WINMOR is used without an Interlocked Pactor port. +// Set NODECALL to first Application Callsign if NODE=0 and BBSCALL not set. +// Add RIGCONTROL TUNE and POWER commands for some ICOM and Kenwwod rigs +// Fix timing out ARDOP PENDING Lock +// Support mixed case WINLINK Passwords +// Add TUNE and POWER Rigcontol Commands for some radios +// ADD LOCALTIME and DISPKM options to APRS Digi/Igate + +// 6.0.16.1 March 2018 + +// Fix Setting data mode and filter for IC7300 radios +// Add VARA to WL2KREPORT +// Add trace to SCS Tracker status window +// Fix possible hang in IPGATEWAY +// Add BeacontoIS parameter to APRSDIGI. Allows you to stop sending beacons to APRS-IS. +// Fix sending CTEXT on WINMOR sessions + +// 6.0.17.1 November 2018 + +// Change WINMOR Restart after connection to Restart after Failure and add same option to ARDOP and VARA +// Add Abort Connection to WINMOR and VARA Interfaces +// Reinstate accidentally removed CMS Access logging +// Fix MH CLEAR +// Fix corruption of NODE table if NODES received from station with null alias +// Fix loss of buffer if session closed with something in PARTCMDBUFFER +// Fix Spurious GUARD ZONE CORRUPT message in IP Code. +// Remove "reread bpq32.cfg and reconfigure" menu options +// Add support for PTT using CM108 based soundcard interfaces +// Datestamp Telnet log files and delete old Telnet and CMSAcces logs + +// 6.0.18.1 January 2019 + +// Fix validation of NODES broadcasts +// Fix HIDENODES +// Check for failure to reread config on axip reconfigure +// Fix crash if STOPPORT or STARTPORT used on KISS over TCP port +// Send Beacons from BCALL or PORTCALL if configured +// Fix possible corruption of last entry in MH display +// Ensure RTS/DTR is down when opening PTT Port +// Remove RECONFIG command +// Preparations for 64 bit version + +// 6.0.19 Sept 2019 +// Fix UZ7HO interlock +// Add commands to set Centre Frequency and Modem with UZ7HO Soundmodem (on Windows only) +// Add option to save and restore MH lists and SAVEMH command +// Add Frequency (if known) to UZ7HO MH lists +// Add Gateway option to Telnet for PAT +// Try to fix SCS Tracker recovery +// Ensure RTS/DTR is down on CAT port if using that line for PTT +// Experimental APRS Messaging in Kernel +// Add Rigcontrol on remote PC's using WinmorControl +// ADD VARAFM and VARAFM96 WL2KREPORT modes +// Fix WL2K sysop update for new Winlink API +// Fix APRS when using PORTNUM higher than the number of ports +// Add Serial Port Type +// Add option to linbpq to log APRS-IS messages. +// Send WL2K Session Reports +// Drop Tunneled Packets from 44.192 - 44.255 +// Log incoming Telnet Connects +// Add IPV4: and IPV6: overrides on AXIP Resolver. +// Add SessionTimeLimit to HF sessions (ARDOP, SCSPactor, WINMOR, VARA) +// Add RADIO FREQ command to display current frequency + +// 6.0.20 April 2020 + +// Trap and reject YAPP file transfer request. +// Fix possible overrun of TCP to Node Buffer +// Fix possible crash if APRS WX file doesn't have a terminating newline +// Change communication with BPQAPRS.exe to restore old message popup behaviour +// Preparation for 64 bit version +// Improve flow control on SCS Dragon +// Fragment messages from network links to L2 links with smaller paclen +// Change WL2K report rate to once every two hours +// Add PASS, CTEXT and CMSG commands and Stream Switch support to TNC2 Emulator +// Add SessionTimeLimit command to HF drivers (ARDOP, SCSPactor, WINMOR, VARA) +// Add links to Ports Web Manangement Page to open individual Driver windows +// Add STOPPORT/STARTPORT support to ARDOP, KAM and SCSPactor drivers +// Add CLOSE and OPEN RADIO command so Rigcontrol port can be freed fpr other use. +// Don't try to send WL2K Traffic report if Internet is down +// Move WL2K Traffic reporting to a separate thread so it doesn't block if it can't connect to server +// ADD AGWAPPL config command to set application number. AGWMASK is still supported +// Register Node Alias with UZ7HO Driver +// Register calls when UZ7HO TNC Restarts and at intervals afterwards +// Fix crash when no IOADDR or COMPORT in async port definition +// Fix Crash with Paclink-Unix when parsing ; VE7SPR-10 DE N7NIX QTC 1 +// Only apply BBSFLAG=NOBBS to APPPLICATION 1 +// Add RIGREONFIG command +// fix APRS RECONFIG on LinBPQ +// Fix Web Terminal scroll to end problem on some browsers +// Add PTT_SETS_INPUT option for IC7600 +// Add TELRECONFIG command to reread users or whole config +// Enforce PACLEN on UZ7HO ports +// Fix PACLEN on Command Output. +// Retry axip resolver if it fails at startup +// Fix AGWAPI connect via digis +// Fix Select() for Linux in MultiPSK, UZ7HO and V4 drivers +// Limit APRS OBJECT length to 80 chars +// UZ7HO disconnect incoming call if no free streams +// Improve response to REJ (no F) followed by RR (F). +// Try to prevent more than MAXFRAME frames outstanding when transmitting +// Allow more than one instance of APRS on Linux +// Stop APRS digi by originating station +// Send driver window trace to main monitor system +// Improve handling of IPOLL messages +// Fix setting end of address bit on dest call on connects to listening sessions +// Set default BBS and CHAT application number and number of streams on LinBPQ +// Support #include in bpq32.cfg processing + +// Version 6.0.21 14 December 2020 + +// Fix occasional missing newlines in some node command reponses +// More 64 bit fixes +// Add option to stop setting PDUPLEX param in SCSPACTOR +// Try to fix buffer loss +// Remove extra space from APRS position reports +// Suppress VARA IAMALIVE messages +// Add display and control of QtSoundModem modems +// Only send "No CMS connection available" message if fallbacktorelay is set. +// Add HAMLIB backend and emulator support to RIGCONTROL +// Ensure all beacons are sent even with very short beacon intervals +// Add VARA500 WL2K Reporting Mode +// Fix problem with prpcessing frame collector +// Temporarily disable L2 and L4 collectors till I can find problem +// Fix possible problem with interactive RADIO commands not giving a response, +// Incease maximum length of NODE command responses to handle maximum length INFO message, +// Allow WL2KREPORT in CONFIG section of UZ7HO port config. +// Fix program error in processing hamlib frame +// Save RestartAfterFailure option for VARA +// Check callsign has a winlink account before sending WL2KREPORT messages +// Add Bandwidth control to VARA scanning +// Renable L2 collector +// Fix TNCPORT reconnect on Linux +// Add SecureTelnet option to limit telnet outward connect to sysop mode sessions or Application Aliases +// Add option to suppress sending call to application in Telnet HOST API +// Add FT991A support to RigControl +// Use background.jpg for Edit Config page +// Send OK response to SCS Pactor commands starting with # +// Resend ICOM PTT OFF command after 30 seconds +// Add WXCall to APRS config +// Fixes for AEAPactor +// Allow PTTMUX to use real or com0com com ports +// Fix monitoring with AGW Emulator +// Derive approx position from packets on APRS ports with a valid 6 char location +// Fix corruption of APRS message lists if the station table fills up. +// Don't accept empty username or password on Relay sessions. +// Fix occasional empty Nodes broadcasts +// Add Digis to UZ7HO Port MH list +// Add PERMITTEDAPPLS port param +// Fix WK2K Session Record Reporting for Airmail and some Pactor Modes. +// Fix handling AX/IP (proto 93) frames +// Fix possible corruption sending APRS messages +// Allow Telnet connections to be made using Connect command as well as Attach then Connect +// Fix Cancel Sysop Signin +// Save axip resolver info and restore on restart +// Add Transparent mode to Telnet Server HOST API +// Fix Tracker driver if WL2KREPRRT is in main config section +// SNMP InOctets count corrected to include all frames and encoding of zero values fixed. +// Change IP Gateway to exclude handling bits of 44 Net sold to Amazon +// Fix crash in Web terminal when processing very long lines + +// Version 6.0.22.1 August 2021 + +// Fix bug in KAM TNCEMULATOR +// Add WinRPR Driver (DED over TCP) +// Fix handling of VARA config commands FM1200 and FM9600 +// Improve Web Termanal Line folding +// Add StartTNC to WinRPR driver +// Add support for VARA2750 Mode +// Add support for VARA connects via a VARA Digipeater +// Add digis to SCSTracker and WinRPR MHeard +// Separate RIGCONTROL config from PORT config and add RigControl window +// Fix crash when a Windows HID device doesn't have a product_string +// Changes to VARA TNC connection and restart process +// Trigger FALLBACKTORELAY if attempt to connect to all CMS servers fail. +// Fix saving part lines in adif log and Winlink Session reporting +// Add port specific CTEXT +// Add FRMR monitoring to UZ7HO driver +// Add audio input switching for IC7610 +// Include Rigcontrol Support for IC-F8101E +// Process any response to KISS command +// Fix NODE ADD command +// Add noUpdate flag to AXIP MAP +// Fix clearing NOFALLBACK flag in Telnet Server +// Allow connects to RMS Relay running on another host +// Allow use of Power setting in Rigcontol scan lines for Kenwood radios +// Prevent problems caused by using "CMS" as a Node Alias +// Include standard APRS Station pages in code +// Fix VALIDCALLS processing in HF drivers +// Send Netrom Link reports to Node Map +// Add REALTELNET mode to Telnet Outward Connect +// Fix using S (Stay) parameter on Telnet connects when using CMDPORT and C HOST +// Add Default frequency to rigcontrol to set a freq/mode to return to after a connection +// Fix long (> 60 seconds) scan intervals +// Improved debugging of stuck semaphores +// Fix potential securiby bug in BPQ Web server +// Send Chat Updates to chatupdate.g8bpq.net port 81 +// Add ReportRelayTraffic to Telnet config to send WL2K traffic reports for connections to RELAY +// Add experimental Mode reporting +// Add SendTandRtoRelay param to SCS Pactor, ARDOP and VARA drivers to divert calls to CMS for -T and -R to RELAY +// Add UPNP Support + +// Version 6.0.23.1 June 2022 + +// Add option to control which applcalls are enabled in VARA +// Add support for rtl_udp to Rig Control +// Fix Telnet Auto Conneect to Application when using TermTCP or Web Terminal +// Allow setting css styles for Web Terminal +// And Kill TNC and Kill and Restart TNC commands to Web Driver Windows +// More flexible RigControl for split frequency operation, eg for QO100 +// Increase stack size for ProcessHTMLMessage (.11) +// Fix HTML Content-Type on images (.12) +// Add AIS and ADSB Support (.13) +// Compress web pages (.14) +// Change minidump routine and close after program error (.15) +// Add RMS Relay SYNC Mode (.17) +// Changes for compatibility with Winlink Hybrid +// Add Rigcontrol CMD feature to Yaesu code (21) +// More diagnostic code +// Trap potential buffer overrun in ax/tcp code +// Fix possible hang in UZ7HO driver if connect takes a long time to succeed or fail +// Add FLRIG as backend for RigControl (.24) +// Fix bug in compressing some management web pages +// Fix bugs in AGW Emulator (.25) +// Add more PTT_Sets_Freq options for split frequency working (.26) +// Allow RIGCONTROL using Radio Number (Rnn) as well as Port (.26) +// Fix Telnet negotiation and backspace processing (.29) +// Fix VARA Mode change when scanning (.30) +// Add Web Mgmt Log Display (.33) +// Fix crash when connecting to RELAY when CMS=0 (.36) +// Send OK to user for manual freq changes with hamlib or flrig +// Fix Rigcontrol leaving port disabled when using an empty timeband +// Fix processing of backspace in Telnet character processing (.40) +// Increase max size of connect script +// Fix HAMLIB Slave Thread control +// Add processing of VARA mode responses and display of VARA Mode (41) +// Fix crash when VARA session aborted on LinBPQ (43) +// Fix handling port selector (2:call or p2 call) on SCS PTC packet ports (44) +// Include APRS Map web page +// Add Enable/Disable to KAMPACTOR scan control (use P0 or P1) (45) +// Add Basic DRATS interface (46) +// Fix MYCALLS on VARA (49) +// Add FreeData driver (51) +// Add additonal Rigcontrol options for QO100 (51) +// Set Content-Type: application/pdf for pdf files downloaded via web interface (51) +// Fix sending large compressed web messages (52) +// Fix freq display when using flrig or hamlib backends to rigcontrol +// Change VARA Driver to send ABORT when Session Time limit expires +// Add Chat Log to Web Logs display +// Fix possible buffer loss in RigControl +// Allow hosts on local lan to be treated as secure +// Improve validation of data sent to Winlink SessionAdd API call +// Add support for FreeDATA modem. +// Add GetLOC API Call +// Change Leaflet link in aprs map. +// Add Connect Log (64) +// Fix crash when Resolve CMS Servers returns ipv6 addresses +// Fix Reporting P4 sessions to Winlink (68) +// Add support for FreeBSD (68) +// Fix Rigcontrol PTCPORT (69) +// Set TNC Emulator sessions as secure (72) +// Fix not always detecting loss of FLRIG (73) +// Add ? and * wildcards to NODES command (74) +// Add Port RADIO config parameter (74) + +// Version 6.0.24.1 August 2023 + +// Apply NODES command wildcard to alias as well a call (2) +// Add STOPPORT/STARTPORT to VARA Driver (2) +// Add bandwidth setting to FLRIG interface. (2) +// Fix N VIA (3) +// Fix NODE ADD and NODE DEL (4) +// Improvements to FLRIG Rigcontrol backend (6, 7) +// Fix UZ7HO Window Title Update +// Reject L2 calls with a blank from call (8) +// Update WinRPR Window header with BPQ Port Description (8) +// Fix error in blank call code (9) +// Change web buttons to white on black when pressed (10) +// Fix Port CTEXT paclen on Tracker and WinRPR drivers (11) +// Add RADIO PTT command for testing PTT (11) +// Fix using APPLCALLs on SCSTracker RP call (12) +// Add Rigcntol Web Page (13) +// Fix scan bandwidth change with ARDOPOFDM (13) +// Fix setting Min Pactor Level in SCSPactor (13) +// Fix length of commands sent via CMD_TO_APPL flag (14) +// Add filter by quality option to N display (15) +// Fix VARA Mode reporting to WL2K (16) +// Add FLRIG POWER and TUNE commands (18) +// Fix crash when processing "C " without a call in UZ7HO, FLDIGI or MULTIPSK drivers (19) +// FLDIGI improvements (19) +// Fix hang at start if Telnet port Number > Number of Telnet Streams (20) +// Fix processing C command if first port driver is SCSPACTROR (20) +// Fix crash in UZ7HO driver if bad raw frame received (21) +// Fix using FLARQ chat mode with FLDIGI ddriover (22) +// Fix to KISSHF driver (23) +// Fix for application buffer loss (24) +// Add Web Sockets auto-refresh option for Webmail index page (25) +// Fix FREEDATA driver for compatibility with FreeData TNC version 0.6.4-alpha.3 (25) +// Add SmartID for bridged frames - Send ID only if packets sent recently (26) +// Add option to save and restore received APRS messages (27) +// Add mechanism to run a user program on certain events (27) +// If BeacontoIS is zero don't Gate any of our messages received locally to APRS-IS (28) +// Add Node Help command (28) +// Add APRS Igate RXOnly option (29) +// Fix RMC message handling with prefixes other than GP (29) +// Add GPSD support for APRS (30) +// Attempt to fix Tracker/WinRPR reconnect code (30) +// Changes to FreeDATA - Don't use deamon and add txlevel and send text commands (31) +// Fix interactive commands in tracker driver (33) +// Fix SESSIONTIMELIMIT processing +// Add STOPPORT/STARTPORT for UZ7HO driver +// Fix processing of extended QtSM 'g' frame (36) +// Allow setting just freq on Yaseu rigs (37) +// Enable KISSHF driver on Linux (40) +// Allow AISHOST and ADSBHOST to be a name as well as an address (41) +// Fix Interlock of incoming UZ7HO connections (41) +// Disable VARA Actions menu if not sysop (41) +// Fix Port CTEXT on UZ7HO B C or D channels (42) +// Fix repeated trigger of SessionTimeLimit (43) +// Fix posible memory corruption in UpateMH (44) +// Add PHG to APRS beacons (45) +// Dont send DM to stations in exclude list(45) +// Improvements to RMS Relay SYNC Mode (46) +// Check L4 connects against EXCLUDE list (47) +// Add vaidation of LOC in WL2K Session Reports (49) +// Change gpsd support for compatibility with Share Gps (50) +// Switch APRS Map to my Tiles (52) +// Fix using ; in UNPROTO Mode messages (52) +// Use sha1 code from https://www.packetizer.com/security/sha1/ instead of openssl (53) +// Fix TNC Emulator Monitoring (53) +// Fix attach and connect on Telnet port bug introduced in .55 (56) +// Fix stopping WinRPR TNC and Start/Stop UZ7HO TNCX on Linux (57) +// Fix stack size in beginthread for MAC (58) +// Add NETROM over VARA (60) +// Add Disconnect Script (64) +// Add node commands to set UZ7HO modem mode and freq (64) +// Trap empty NODECALL or NETROMCALL(65) +// Trap NODES messages with empty From Call (65) +// Add RigControl for SDRConsole (66) +// Fix FLRig crash (66) +// Fix VARA disconnect handling (67) +// Support 64 ports (69) +// Fix Node commands for setting UZ7HO Modem (70) +// Fix processing SABM on an existing session (71) +// Extend KISS Node command to send more than one parameter byte (72) +// Add G7TAJ's code to record activity of HF ports for stats display (72) +// Add option to send KISS command to TNC on startup (73) +// Fix Bug in DED Emulator Monitor code (74) +// Add Filters to DED Monitor code (75) +// Detect loss of DED application (76) +// Fix connects to Application Alias with UZ7HO Driver (76) +// Fix Interlock of ports on same UZ7HO modem. (76) +// Add extended Ports command (77) +// Fix crash in Linbpq when stdout is redirected to /dev/tty? and stdin ia redirected (78) +// Fix Web Terminal (80) +// Trap ENCRYPTION message from VARA (81) +// Fix processing of the Winlink API /account/exists response (82) +// Fix sending CTEXT to L4 connects to Node when FULL_CTEXT is not set + +// Version 6.0.25.? + +// Fix 64 bit compatibility problems in SCSTracker and UZ7HO drivers +// Add Chat PACLEN config (5) +// Fix NC to Application Call (6) +// Fix INP3 L3RTT messages on Linux and correct RTT calculation (9) +// Get Beacon config from config file on Windows (9) +// fix processing DED TNC Emulator M command with space between M and params (10) +// Fix sending UI frames on SCSPACTOR (11) +// Dont allow ports that can't set digi'ed bit in callsigns to digipeat. (11) +// Add SDRAngel rig control (11) +// Add option to specify config and data directories on linbpq (12) +// Allow zero resptime (send RR immediately) (13) +// Make sure CMD bit is set on UI frames +// Add setting Modem Flags in QtSM AGW mode +// If FT847 om PTC Port send a "Cat On" command (17) +// Fix some 63 port bugs in RigCOntrol (17) +// Fix 63 port bug in Bridging (18) +// Add FTDX10 Rigcontrol (19) +// Fix 64 bit bug in displaying INP3 Messages (20) +// Improve restart of WinRPR TNC on remote host (21) +// Fix some Rigcontrol issues with empty timebands (22) +// Fix 64 bit bug in processing INP3 Messages (22) +// First pass at api (24) +// Send OK in response to Rigcontrol CMD (24) +// Disable CTS check in WriteComBlock (26) +// Improvments to reporting to M0LTE Map (26) +// IPGateway fix from github user isavitsky (27) +// Fix possible crash in SCSPactor PTCPORT code (29) +// Add NodeAPI call sendLinks and remove get from other calls (32) +// Improve validation of Web Beacon Config (33) +// Support SNMP via host ip stack as well as IPGateway (34) +// Switch APRS Map to OSM tile servers (36) +// Fix potential buffer overflow in Telnet login (36) +// Allow longer serial device names (37) +// Fix ICF8101 Mode setting (37) +// Kill link if we are getting repeated RR(F) after timeout +// (Indicating other station is seeing our RR(P) but not the resent I frame) (40) +// Change default of SECURETELNET to 1 (41) +// Add optional ATTACH time limit for ARDOP (42) +// Fix buffer overflow risk in HTTP Terminal(42) +// Fix KISSHF Interlock (43) +// Support other than channel A on HFKISS (43) +// Support additional port info reporting for M0LTE Map (44) +// Allow interlocking of KISS and Session mode ports (eg ARDOP and VARA) (45) +// Add ARDOP UI Packets to MH (45) +// Add support for Qtsm Mgmt Interface (45) +// NodeAPI improvements (46) +// Add MQTT Interface (46) +// Fix buffer leak in ARDOP code(46) +// Fix possible crash if MQTT not in use (47) +// Add optional ATTACH time limit for VARA (48) +// API format fixes (48) +// AGWAPI Add protection against accidental connects from a non-agw application (50) +// Save MH and NODES every hour (51) +// Fix handling long unix device names (now max 250 bytes) (52) +// Fix error reporting in api update (53) +// Coding changes to remove some compiler warnings (53, 54) +// Add MQTT reporting of Mail Events (54) +// Fix beaconong on KISSHF ports (55) +// Fix MailAPI msgs endpoint +// Attempt to fix NC going to wrong application. (57) +// Improve ARDOP end of session code (58) +// Run M0LTE Map reporting in a separate thread (59/60) +// Add RHP support for WhatsPac (59) +// Add timestamps to LIS monitor (60) +// Fix problem with L4 frames being delivered out of sequence (60) +// Add Compression of Netrom connections (62) +// Improve handling of Locked Routes (62) +// Add L4 RESET (Paula G8PZT's extension to NETROM) +// Fix problem using SENDRAW from BPQMail (63) +// Fix compatibility with latest ardopcf (64) +// Fix bug in RHP socket timeout code (65) + + +#define CKernel + +#include "Versions.h" + +#define _CRT_SECURE_NO_DEPRECATE + +#pragma data_seg("_BPQDATA") + +#include "time.h" +#include "stdio.h" +#include + +#include "compatbits.h" +#include "AsmStrucs.h" + +#include "SHELLAPI.H" +#include "kernelresource.h" + +#include +#include +#include "BPQTermMDI.h" + +#include "GetVersion.h" + +#define DllImport __declspec( dllimport ) + +#define CheckGuardZone() _CheckGuardZone(__FILE__, __LINE__) +void _CheckGuardZone(char * File, int Line); + +#define CHECKLOADED 0 +#define SETAPPLFLAGS 1 +#define SENDBPQFRAME 2 +#define GETBPQFRAME 3 +#define GETSTREAMSTATUS 4 +#define CLEARSTREAMSTATUS 5 +#define BPQCONDIS 6 +#define GETBUFFERSTATUS 7 +#define GETCONNECTIONINFO 8 +#define BPQRETURN 9 // GETCALLS +//#define RAWTX 10 //IE KISS TYPE DATA +#define GETRAWFRAME 11 +#define UPDATESWITCH 12 +#define BPQALLOC 13 +//#define SENDNETFRAME 14 +#define GETTIME 15 + +extern short NUMBEROFPORTS; +extern long PORTENTRYLEN; +extern long LINKTABLELEN; +extern struct PORTCONTROL * PORTTABLE; +extern void * FREE_Q; +extern UINT APPL_Q; // Queue of frames for APRS Appl + +extern TRANSPORTENTRY * L4TABLE; +extern UCHAR NEXTID; +extern DWORD MAXCIRCUITS; +extern DWORD L4DEFAULTWINDOW; +extern DWORD L4T1; +extern APPLCALLS APPLCALLTABLE[]; +extern char * APPLS; + +extern struct WL2KInfo * WL2KReports; + +extern int NUMBEROFTNCPORTS; + + +void * VCOMExtInit(struct PORTCONTROL * PortEntry); +void * AXIPExtInit(struct PORTCONTROL * PortEntry); +void * SCSExtInit(struct PORTCONTROL * PortEntry); +void * AEAExtInit(struct PORTCONTROL * PortEntry); +void * KAMExtInit(struct PORTCONTROL * PortEntry); +void * HALExtInit(struct PORTCONTROL * PortEntry); +void * ETHERExtInit(struct PORTCONTROL * PortEntry); +void * AGWExtInit(struct PORTCONTROL * PortEntry); +void * WinmorExtInit(EXTPORTDATA * PortEntry); +void * TelnetExtInit(EXTPORTDATA * PortEntry); +//void * SoundModemExtInit(EXTPORTDATA * PortEntry); +void * TrackerExtInit(EXTPORTDATA * PortEntry); +void * TrackerMExtInit(EXTPORTDATA * PortEntry); +void * V4ExtInit(EXTPORTDATA * PortEntry); +void * UZ7HOExtInit(EXTPORTDATA * PortEntry); +void * MPSKExtInit(EXTPORTDATA * PortEntry); +void * FLDigiExtInit(EXTPORTDATA * PortEntry); +void * UIARQExtInit(EXTPORTDATA * PortEntry); +void * SerialExtInit(EXTPORTDATA * PortEntry); +void * ARDOPExtInit(EXTPORTDATA * PortEntry); +void * VARAExtInit(EXTPORTDATA * PortEntry); +void * KISSHFExtInit(EXTPORTDATA * PortEntry); +void * WinRPRExtInit(EXTPORTDATA * PortEntry); +void * HSMODEMExtInit(EXTPORTDATA * PortEntry); +void * FreeDataExtInit(EXTPORTDATA * PortEntry); +void * SIXPACKExtInit(EXTPORTDATA * PortEntry); + +extern char * ConfigBuffer; // Config Area +VOID REMOVENODE(dest_list * DEST); +DllExport int ConvFromAX25(unsigned char * incall,unsigned char * outcall); +DllExport int ConvToAX25(unsigned char * incall,unsigned char * outcall); +VOID GetUIConfig(); +VOID ADIFWriteFreqList(); +void SaveAIS(); +void initAIS(); +void initADSB(); + +extern BOOL ADIFLogEnabled; + +int CloseOnError = 0; + +char UIClassName[]="UIMAINWINDOW"; // the main window class name + +HWND UIhWnd; + +extern char AUTOSAVE; +extern char AUTOSAVEMH; + +extern char MYNODECALL; // 10 chars,not null terminated + +extern QCOUNT; +extern BPQVECSTRUC BPQHOSTVECTOR[]; +#define BPQHOSTSTREAMS 64 +#define IPHOSTVECTOR BPQHOSTVECTOR[BPQHOSTSTREAMS + 3] + +extern char * CONFIGFILENAME; + +DllExport BPQVECSTRUC * BPQHOSTVECPTR; + +extern int DATABASESTART; + +extern struct ROUTE * NEIGHBOURS; +extern int ROUTE_LEN; +extern int MAXNEIGHBOURS; + +extern struct DEST_LIST * DESTS; // NODE LIST +extern int DEST_LIST_LEN; +extern int MAXDESTS; // MAX NODES IN SYSTEM + +extern struct _LINKTABLE * LINKS; +extern int LINK_TABLE_LEN; +extern int MAXLINKS; + +extern double LatFromLOC; +extern double LonFromLOC; + + +extern int BPQHOSTAPI(); +extern int INITIALISEPORTS(); +extern int TIMERINTERRUPT(); +extern int MONDECODE(); +extern int BPQMONOPTIONS(); +extern char PWTEXT[]; +extern char PWLen; + +extern int FINDFREEDESTINATION(); +extern int RAWTX(); +extern int RELBUFF(); +extern int SENDNETFRAME(); +extern char MYCALL[]; // 7 chars, ax.25 format + +extern HWND hIPResWnd; +extern BOOL IPMinimized; + +extern int NODESINPROGRESS; +extern VOID * CURRENTNODE; + + +BOOL Start(); + +VOID SaveWindowPos(int port); +VOID SaveAXIPWindowPos(int port); +VOID SetupRTFHddr(); +DllExport VOID APIENTRY CreateNewTrayIcon(); +int DoReceivedData(int Stream); +int DoStateChange(int Stream); +int DoMonData(int Stream); +struct ConsoleInfo * CreateChildWindow(int Stream, BOOL DuringInit); +CloseHostSessions(); +SaveHostSessions(); +VOID SaveBPQ32Windows(); +VOID CloseDriverWindow(int port); +VOID CheckWL2KReportTimer(); +VOID SetApplPorts(); +VOID WriteMiniDump(); +VOID FindLostBuffers(); +BOOL InitializeTNCEmulator(); +VOID TNCTimer(); +char * strlop(char * buf, char delim); + +DllExport int APIENTRY Get_APPLMASK(int Stream); +DllExport int APIENTRY GetStreamPID(int Stream); +DllExport int APIENTRY GetApplFlags(int Stream); +DllExport int APIENTRY GetApplNum(int Stream); +DllExport BOOL APIENTRY GetAllocationState(int Stream); +DllExport int APIENTRY GetMsg(int stream, char * msg, int * len, int * count ); +DllExport int APIENTRY RXCount(int Stream); +DllExport int APIENTRY TXCount(int Stream); +DllExport int APIENTRY MONCount(int Stream); +DllExport int APIENTRY GetCallsign(int stream, char * callsign); +DllExport VOID APIENTRY RelBuff(VOID * Msg); +void SaveMH(); +void DRATSPoll(); + +#define C_Q_ADD(s, b) _C_Q_ADD(s, b, __FILE__, __LINE__); +int _C_Q_ADD(VOID *PQ, VOID *PBUFF, char * File, int Line); + +VOID SetWindowTextSupport(); +int WritetoConsoleSupport(char * buff); +VOID PMClose(); +VOID MySetWindowText(HWND hWnd, char * Msg); +BOOL CreateMonitorWindow(char * MonSize); +VOID FormatTime3(char * Time, time_t cTime); + +char EXCEPTMSG[80] = ""; + +char SIGNONMSG[128] = ""; +char SESSIONHDDR[80] = ""; +int SESSHDDRLEN = 0; + +BOOL IncludesMail = FALSE; +BOOL IncludesChat = FALSE; // Set if pgram is running - used for Web Page Index + + +char WL2KCall[10]; +char WL2KLoc[7]; + +extern char LOCATOR[]; // Locator for Reporting - may be Maidenhead or LAT:LON +extern char MAPCOMMENT[]; // Locator for Reporting - may be Maidenhead or LAT:LON +extern char LOC[7]; // Maidenhead Locator for Reporting +extern char ReportDest[7]; + +extern UCHAR ConfigDirectory[260]; + +extern uint64_t timeLoadedMS; + +VOID __cdecl Debugprintf(const char * format, ...); +VOID __cdecl Consoleprintf(const char * format, ...); + +DllExport int APIENTRY CloseBPQ32(); +DllExport char * APIENTRY GetLOC(); +DllExport int APIENTRY SessionControl(int stream, int command, int param); + +int DoRefreshWebMailIndex(); + +BOOL APIENTRY Init_IP(); +BOOL APIENTRY Poll_IP(); + +BOOL APIENTRY Init_PM(); +BOOL APIENTRY Poll_PM(); + +BOOL APIENTRY Init_APRS(); +BOOL APIENTRY Poll_APRS(); +VOID HTTPTimer(); + +BOOL APIENTRY Rig_Init(); +BOOL APIENTRY Rig_Close(); +BOOL Rig_Poll(); + +VOID IPClose(); +VOID APRSClose(); +VOID CloseTNCEmulator(); + +VOID Poll_AGW(); +void RHPPoll(); +BOOL AGWAPIInit(); +int AGWAPITerminate(); + +int * Flag = (int *)&Flag; // for Dump Analysis +int MAJORVERSION=4; +int MINORVERSION=9; + +struct SEM Semaphore = {0, 0, 0, 0}; +struct SEM APISemaphore = {0, 0, 0, 0}; +int SemHeldByAPI = 0; +int LastSemGets = 0; +UINT Sem_eax = 0; +UINT Sem_ebx = 0; +UINT Sem_ecx = 0; +UINT Sem_edx = 0; +UINT Sem_esi = 0; +UINT Sem_edi = 0; + + +#define GetSemaphore(Semaphore,ID) _GetSemaphore(Semaphore, ID, __FILE__, __LINE__) +void _GetSemaphore(struct SEM * Semaphore, int ID, char * File, int Line); +void FreeSemaphore(struct SEM * Semaphore); + +DllExport void * BPQHOSTAPIPTR = &BPQHOSTAPI; +//DllExport long MONDECODEPTR = (long)&MONDECODE; + +extern UCHAR BPQDirectory[]; +extern UCHAR LogDirectory[]; +extern UCHAR BPQProgramDirectory[]; + +static char BPQWinMsg[] = "BPQWindowMessage"; + +static char ClassName[] = "BPQMAINWINDOW"; + +HKEY REGTREE = HKEY_CURRENT_USER; +char REGTREETEXT[100] = "HKEY_CURRENT_USER"; + +UINT BPQMsg=0; + +#define MAXLINELEN 120 +#define MAXSCREENLEN 50 + +#define BGCOLOUR RGB(236,233,216) + +HBRUSH bgBrush = NULL; + +//int LINELEN=120; +//int SCREENLEN=50; + +//char Screen[MAXLINELEN*MAXSCREENLEN]={0}; + +//int lineno=0; +//int col=0; + +#define REPORTINTERVAL 15 * 549; // Magic Ticks Per Minute for PC's nominal 100 ms timer +int ReportTimer = 0; + +HANDLE OpenConfigFile(char * file); + +VOID SetupBPQDirectory(); +VOID SendLocation(); + +//uintptr_t _beginthread(void(*start_address)(), unsigned stack_size, int arglist); + +#define TRAY_ICON_ID 1 // ID number for the Notify Icon +#define MY_TRAY_ICON_MESSAGE WM_APP // the message ID sent to our window + +NOTIFYICONDATA niData; + +int SetupConsoleWindow(); + +BOOL StartMinimized=FALSE; +BOOL MinimizetoTray=TRUE; + +BOOL StatusMinimized = FALSE; +BOOL ConsoleMinimized = FALSE; + +HMENU trayMenu=0; + +HWND hConsWnd = NULL, hWndCons = NULL, hWndBG = NULL, ClientWnd = NULL, FrameWnd = NULL, StatusWnd = NULL; + +BOOL FrameMaximized = FALSE; + +BOOL IGateEnabled = TRUE; +extern int ISDelayTimer; // Time before trying to reopen APRS-IS link +extern int ISPort; + +UINT * WINMORTraceQ = NULL; +UINT * SetWindowTextQ = NULL; + +static RECT Rect = {100,100,400,400}; // Console Window Position +RECT FRect = {100,100,800,600}; // Frame +static RECT StatusRect = {100,100,850,500}; // Status Window + +DllExport int APIENTRY DumpSystem(); +DllExport int APIENTRY SaveNodes (); +DllExport int APIENTRY ClearNodes (); +DllExport int APIENTRY SetupTrayIcon(); + +#define Q_REM(s) _Q_REM(s, __FILE__, __LINE__) + +VOID * _Q_REM(VOID *Q, char * File, int Line); + +UINT ReleaseBuffer(UINT *BUFF); + + +VOID CALLBACK TimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime ); + +DllExport int APIENTRY DeallocateStream(int stream); + +int VECTORLENGTH = sizeof (struct _BPQVECSTRUC); + +int FirstEntry = 1; +BOOL CloseLast = TRUE; // If the user started BPQ32.exe, don't close it when other programs close +BOOL Closing = FALSE; // Set if Close All called - prevents respawning bpq32.exe + +BOOL BPQ32_EXE; // Set if Process is running BPQ32.exe. Not initialised. + // Used to Kill surplus BPQ32.exe processes + +DWORD Our_PID; // Our Process ID - local variable + +void * InitDone = 0; +int FirstInitDone = 0; +int PerlReinit = 0; +UINT_PTR TimerHandle = 0; +UINT_PTR SessHandle = 0; + +BOOL EventsEnabled = 0; + +unsigned int TimerInst = 0xffffffff; + +HANDLE hInstance = 0; + +int AttachedProcesses = 0; +int AttachingProcess = 0; +HINSTANCE hIPModule = 0; +HINSTANCE hRigModule = 0; + +BOOL ReconfigFlag = FALSE; +BOOL RigReconfigFlag = FALSE; +BOOL APRSReconfigFlag = FALSE; +BOOL CloseAllNeeded = FALSE; +BOOL NeedWebMailRefresh = FALSE; + +int AttachedPIDList[100] = {0}; + +HWND hWndArray[100] = {0}; +int PIDArray[100] = {0}; +char PopupText[30][100] = {""}; + +// Next 3 should be uninitialised so they are local to each process + +UCHAR MCOM; +UCHAR MTX; // Top bit indicates use local time +uint64_t MMASK; +UCHAR MUIONLY; + +UCHAR AuthorisedProgram; // Local Variable. Set if Program is on secure list + +char pgm[256]; // Uninitialised so per process + +HANDLE Mutex; + +BOOL PartLine = FALSE; +int pindex = 0; +DWORD * WritetoConsoleQ; + + +LARGE_INTEGER lpFrequency = {0}; +LARGE_INTEGER lastRunTime; +LARGE_INTEGER currentTime; + +int ticksPerMillisec; +int interval; + + +VOID CALLBACK SetupTermSessions(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime); + + +TIMERPROC lpTimerFunc = (TIMERPROC) TimerProc; +TIMERPROC lpSetupTermSessions = (TIMERPROC) SetupTermSessions; + + +BOOL ProcessConfig(); +VOID FreeConfig(); + +DllExport int APIENTRY WritetoConsole(char * buff); + +BOOLEAN CheckifBPQ32isLoaded(); +BOOLEAN StartBPQ32(); +DllExport VOID APIENTRY Send_AX(VOID * Block, DWORD len, UCHAR Port); +BOOL LoadIPDriver(); +BOOL Send_IP(VOID * Block, DWORD len); +VOID CheckforLostProcesses(); +BOOL LoadRigDriver(); +VOID SaveConfig(); +VOID CreateRegBackup(); +VOID ResolveUpdateThread(); +VOID OpenReportingSockets(); +DllExport VOID APIENTRY CloseAllPrograms(); +DllExport BOOL APIENTRY SaveReg(char * KeyIn, HANDLE hFile); +int upnpClose(); + +BOOL IPActive = FALSE; +extern BOOL IPRequired; +BOOL PMActive = FALSE; +extern BOOL PMRequired; +BOOL RigRequired = TRUE; +BOOL RigActive = FALSE; +BOOL APRSActive = FALSE; +BOOL AGWActive = FALSE; +BOOL needAIS = FALSE; +int needADSB = 0; + +extern int AGWPort; + +Tell_Sessions(); + + +typedef int (WINAPI FAR *FARPROCX)(); + +FARPROCX CreateToolHelp32SnapShotPtr; +FARPROCX Process32Firstptr; +FARPROCX Process32Nextptr; + +void LoadToolHelperRoutines() +{ + HINSTANCE ExtDriver=0; + int err; + char msg[100]; + + ExtDriver=LoadLibrary("kernel32.dll"); + + if (ExtDriver == NULL) + { + err=GetLastError(); + sprintf(msg,"BPQ32 Error loading kernel32.dll - Error code %d\n", err); + OutputDebugString(msg); + return; + } + + CreateToolHelp32SnapShotPtr = (FARPROCX)GetProcAddress(ExtDriver,"CreateToolhelp32Snapshot"); + Process32Firstptr = (FARPROCX)GetProcAddress(ExtDriver,"Process32First"); + Process32Nextptr = (FARPROCX)GetProcAddress(ExtDriver,"Process32Next"); + + if (CreateToolHelp32SnapShotPtr == 0) + { + err=GetLastError(); + sprintf(msg,"BPQ32 Error getting CreateToolhelp32Snapshot entry point - Error code %d\n", err); + OutputDebugString(msg); + return; + } +} + +BOOL GetProcess(int ProcessID, char * Program) +{ + HANDLE hProcessSnap; + PROCESSENTRY32 pe32; + int p; + + if (CreateToolHelp32SnapShotPtr==0) + { + return (TRUE); // Routine not available + } + // Take a snapshot of all processes in the system. + hProcessSnap = (HANDLE)CreateToolHelp32SnapShotPtr(TH32CS_SNAPPROCESS, 0); + if( hProcessSnap == INVALID_HANDLE_VALUE ) + { + OutputDebugString( "CreateToolhelp32Snapshot (of processes) Failed\n" ); + return( FALSE ); + } + + // Set the size of the structure before using it. + pe32.dwSize = sizeof( PROCESSENTRY32 ); + + // Retrieve information about the first process, + // and exit if unsuccessful + if( !Process32Firstptr( hProcessSnap, &pe32 ) ) + { + OutputDebugString( "Process32First Failed\n" ); // Show cause of failure + CloseHandle( hProcessSnap ); // Must clean up the snapshot object! + return( FALSE ); + } + + // Now walk the snapshot of processes, and + // display information about each process in turn + do + { + if (ProcessID==pe32.th32ProcessID) + { + // if running on 98, program contains the full path - remove it + + for (p = (int)strlen(pe32.szExeFile); p >= 0; p--) + { + if (pe32.szExeFile[p]=='\\') + { + break; + } + } + p++; + + sprintf(Program,"%s", &pe32.szExeFile[p]); + CloseHandle( hProcessSnap ); + return( TRUE ); + } + + } while( Process32Nextptr( hProcessSnap, &pe32 ) ); + + + sprintf(Program,"PID %d Not Found", ProcessID); + CloseHandle( hProcessSnap ); + return(FALSE); +} + +BOOL IsProcess(int ProcessID) +{ + // Check that Process exists + + HANDLE hProcessSnap; + PROCESSENTRY32 pe32; + + if (CreateToolHelp32SnapShotPtr==0) return (TRUE); // Routine not available + + hProcessSnap = (HANDLE)CreateToolHelp32SnapShotPtr(TH32CS_SNAPPROCESS, 0); + + if( hProcessSnap == INVALID_HANDLE_VALUE ) + { + OutputDebugString( "CreateToolhelp32Snapshot (of processes) Failed\n" ); + return(TRUE); // Don't know, so assume ok + } + + pe32.dwSize = sizeof( PROCESSENTRY32 ); + + if( !Process32Firstptr( hProcessSnap, &pe32 ) ) + { + OutputDebugString( "Process32First Failed\n" ); // Show cause of failure + CloseHandle( hProcessSnap ); // Must clean up the snapshot object! + return(TRUE); // Don't know, so assume ok + } + + do + { + if (ProcessID==pe32.th32ProcessID) + { + CloseHandle( hProcessSnap ); + return( TRUE ); + } + + } while( Process32Nextptr( hProcessSnap, &pe32 ) ); + + CloseHandle( hProcessSnap ); + return(FALSE); +} + +#include "DbgHelp.h" + +VOID MonitorThread(int x) +{ + // Thread to detect killed processes. Runs in process owning timer. + + // Obviously can't detect loss of timer owning thread! + + do + { + if (Semaphore.Gets == LastSemGets && Semaphore.Flag) + { + // It is stuck - try to release + + Debugprintf ("Semaphore locked - Process ID = %d, Held By %d from %s Line %d", + Semaphore.SemProcessID, SemHeldByAPI, Semaphore.File, Semaphore.Line); + + // Write a minidump + + WriteMiniDump(); + + Semaphore.Flag = 0; + } + + LastSemGets = Semaphore.Gets; + + Sleep(30000); + CheckforLostProcesses(); + + } while (TRUE); +} + +VOID CheckforLostProcesses() +{ + UCHAR buff[100]; + char Log[80]; + int i, n, ProcessID; + + for (n=0; n < AttachedProcesses; n++) + { + ProcessID=AttachedPIDList[n]; + + if (!IsProcess(ProcessID)) + { + // Process has died - Treat as a detach + + sprintf(Log,"BPQ32 Process %d Died\n", ProcessID); + OutputDebugString(Log); + + // Remove Tray Icon Entry + + for( i = 0; i < 100; ++i ) + { + if (PIDArray[i] == ProcessID) + { + hWndArray[i] = 0; + sprintf(Log,"BPQ32 Removing Tray Item %s\n", PopupText[i]); + OutputDebugString(Log); + DeleteMenu(trayMenu,TRAYBASEID+i,MF_BYCOMMAND); + } + } + + // If process had the semaphore, release it + + if (Semaphore.Flag == 1 && ProcessID == Semaphore.SemProcessID) + { + OutputDebugString("BPQ32 Process was holding Semaphore - attempting recovery\r\n"); + Debugprintf("Last Sem Call %d %x %x %x %x %x %x", SemHeldByAPI, + Sem_eax, Sem_ebx, Sem_ecx, Sem_edx, Sem_esi, Sem_edi); + + Semaphore.Flag = 0; + SemHeldByAPI = 0; + } + + for (i=1;i<65;i++) + { + if (BPQHOSTVECTOR[i-1].STREAMOWNER == AttachedPIDList[n]) + { + DeallocateStream(i); + } + } + + if (TimerInst == ProcessID) + { + KillTimer(NULL,TimerHandle); + TimerHandle=0; + TimerInst=0xffffffff; +// Tell_Sessions(); + OutputDebugString("BPQ32 Process was running timer \n"); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + + } + + // Remove this entry from PID List + + for (i=n; i< AttachedProcesses; i++) + { + AttachedPIDList[i]=AttachedPIDList[i+1]; + } + AttachedProcesses--; + + sprintf(buff,"BPQ32 Lost Process - %d Process(es) Attached\n", AttachedProcesses); + OutputDebugString(buff); + } + } +} +VOID MonitorTimerThread(int x) +{ + // Thread to detect killed timer process. Runs in all other BPQ32 processes. + + do { + + Sleep(60000); + + if (TimerInst != 0xffffffff && !IsProcess(TimerInst)) + { + // Timer owning Process has died - Force a new timer to be created + // New timer thread will detect lost process and tidy up + + Debugprintf("BPQ32 Process %d with Timer died", TimerInst); + + // If process was holding the semaphore, release it + + if (Semaphore.Flag == 1 && TimerInst == Semaphore.SemProcessID) + { + OutputDebugString("BPQ32 Process was holding Semaphore - attempting recovery\r\n"); + Debugprintf("Last Sem Call %d %x %x %x %x %x %x", SemHeldByAPI, + Sem_eax, Sem_ebx, Sem_ecx, Sem_edx, Sem_esi, Sem_edi); + Semaphore.Flag = 0; + SemHeldByAPI = 0; + } + +// KillTimer(NULL,TimerHandle); +// TimerHandle=0; +// TimerInst=0xffffffff; +// Tell_Sessions(); + + CheckforLostProcesses(); // Normally only done in timer thread, which is now dead + + // Timer can only run in BPQ32.exe + + TimerInst=0xffffffff; // So we dont keep doing it + TimerHandle = 0; // So new process attaches + + if (Closing == FALSE && AttachingProcess == FALSE) + { + OutputDebugString("BPQ32 Reloading BPQ32.exe\n"); + StartBPQ32(); + } + +// if (MinimizetoTray) +// Shell_NotifyIcon(NIM_DELETE,&niData); + } + + } while (TRUE); +} + +VOID WritetoTraceSupport(struct TNCINFO * TNC, char * Msg, int Len); + +VOID TimerProcX(); + +VOID CALLBACK TimerProc( + HWND hwnd, // handle of window for timer messages + UINT uMsg, // WM_TIMER message + UINT idEvent, // timer identifier + DWORD dwTime) // current system time +{ + KillTimer(NULL,TimerHandle); + TimerProcX(); + TimerHandle = SetTimer(NULL,0,100,lpTimerFunc); +} +VOID TimerProcX() +{ + struct _EXCEPTION_POINTERS exinfo; + + // + // Get semaphore before proceeeding + // + + GetSemaphore(&Semaphore, 2); + + // Get time since last run + + QueryPerformanceCounter(¤tTime); + + interval = (int)(currentTime.QuadPart - lastRunTime.QuadPart) / ticksPerMillisec; + lastRunTime.QuadPart = currentTime.QuadPart; + + //Debugprintf("%d", interval); + + // Process WINMORTraceQ + + while (WINMORTraceQ) + { + UINT * Buffer = Q_REM(&WINMORTraceQ); + struct TNCINFO * TNC = (struct TNCINFO * )Buffer[1]; + int Len = Buffer[2]; + char * Msg = (char *)&Buffer[3]; + + WritetoTraceSupport(TNC, Msg, Len); + RelBuff(Buffer); + } + + if (SetWindowTextQ) + SetWindowTextSupport(); + + while (WritetoConsoleQ) + { + UINT * Buffer = Q_REM(&WritetoConsoleQ); + WritetoConsoleSupport((char *)&Buffer[2]); + RelBuff(Buffer); + } + + strcpy(EXCEPTMSG, "Timer ReconfigProcessing"); + + __try + { + + if (trayMenu == NULL) + SetupTrayIcon(); + + // See if reconfigure requested + + if (CloseAllNeeded) + { + CloseAllNeeded = FALSE; + CloseAllPrograms(); + } + + if (ReconfigFlag) + { + // Only do it it timer owning process, or we could get in a real mess! + + if(TimerInst == GetCurrentProcessId()) + { + int i; + BPQVECSTRUC * HOSTVEC; + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + WSADATA WsaData; // receives data from WSAStartup + RECT cRect; + + ReconfigFlag = FALSE; + + SetupBPQDirectory(); + + WritetoConsole("Reconfiguring ...\n\n"); + OutputDebugString("BPQ32 Reconfiguring ...\n"); + + GetWindowRect(FrameWnd, &FRect); + + SaveWindowPos(70); // Rigcontrol + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR) + { + SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + SaveAXIPWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + CloseDriverWindow(PORTVEC->PORTCONTROL.PORTNUMBER); + PORTVEC->PORT_EXT_ADDR(5,PORTVEC->PORTCONTROL.PORTNUMBER, NULL); // Close External Ports + } + } + PORTVEC->PORTCONTROL.PORTCLOSECODE(&PORTVEC->PORTCONTROL); + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + IPClose(); + PMClose(); + APRSClose(); + Rig_Close(); + CloseTNCEmulator(); + if (AGWActive) + AGWAPITerminate(); + + WSACleanup(); + + WL2KReports = NULL; + + Sleep(2000); + + WSAStartup(MAKEWORD(2, 0), &WsaData); + + Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); + Consoleprintf(VerCopyright); + + Start(); + + INITIALISEPORTS(); // Restart Ports + + SetApplPorts(); + + FreeConfig(); + + for (i=1; i<68; i++) // Include Telnet, APRS and IP Vec + { + HOSTVEC=&BPQHOSTVECTOR[i-1]; + + HOSTVEC->HOSTTRACEQ=0; // Clear header (pool has been reinitialized + + if (HOSTVEC->HOSTSESSION !=0) + { + // Had a connection + + HOSTVEC->HOSTSESSION=0; + HOSTVEC->HOSTFLAGS |=3; // Disconnected + + PostMessage(HOSTVEC->HOSTHANDLE, BPQMsg, i, 4); + } + } + + // Free the APRS Appl Q + + APPL_Q = 0; + + OpenReportingSockets(); + + WritetoConsole("\n\nReconfiguration Complete\n"); + + if (IPRequired) IPActive = Init_IP(); + if (PMRequired) PMActive = Init_PM(); + + APRSActive = Init_APRS(); + + if (ISPort == 0) + IGateEnabled = 0; + + CheckDlgButton(hConsWnd, IDC_ENIGATE, IGateEnabled); + + GetClientRect(hConsWnd, &cRect); + MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); + if (APRSActive) + MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); + else + { + ShowWindow(GetDlgItem(hConsWnd, IDC_GPS), SW_HIDE); + MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); + } + InvalidateRect(hConsWnd, NULL, TRUE); + + RigActive = Rig_Init(); + + if (NUMBEROFTNCPORTS) + { + FreeSemaphore(&Semaphore); + InitializeTNCEmulator(); + GetSemaphore(&Semaphore, 0); + } + + FreeSemaphore(&Semaphore); + AGWActive = AGWAPIInit(); + GetSemaphore(&Semaphore, 0); + + OutputDebugString("BPQ32 Reconfiguration Complete\n"); + } + } + + + if (RigReconfigFlag) + { + // Only do it it timer owning process, or we could get in a real mess! + + if(TimerInst == GetCurrentProcessId()) + { + RigReconfigFlag = FALSE; + CloseDriverWindow(70); + Rig_Close(); + Sleep(6000); // Allow any CATPTT, HAMLIB and FLRIG threads to close + RigActive = Rig_Init(); + + WritetoConsole("Rigcontrol Reconfiguration Complete\n"); + } + } + + if (APRSReconfigFlag) + { + // Only do it it timer owning process, or we could get in a real mess! + + if(TimerInst == GetCurrentProcessId()) + { + APRSReconfigFlag = FALSE; + APRSClose(); + APRSActive = Init_APRS(); + + WritetoConsole("APRS Reconfiguration Complete\n"); + } + } + + } + #include "StdExcept.c" + + if (Semaphore.Flag && Semaphore.SemProcessID == GetCurrentProcessId()) + FreeSemaphore(&Semaphore); + + } + + strcpy(EXCEPTMSG, "Timer Processing"); + + __try + { + if (IPActive) Poll_IP(); + if (PMActive) Poll_PM(); + if (RigActive) Rig_Poll(); + + if (NeedWebMailRefresh) + DoRefreshWebMailIndex(); + + CheckGuardZone(); + + if (APRSActive) + { + Poll_APRS(); + CheckGuardZone(); + } + + CheckWL2KReportTimer(); + + CheckGuardZone(); + + TIMERINTERRUPT(); + + CheckGuardZone(); + + FreeSemaphore(&Semaphore); // SendLocation needs to get the semaphore + + if (NUMBEROFTNCPORTS) + TNCTimer(); + + if (AGWActive) + Poll_AGW(); + + DRATSPoll(); + RHPPoll(); + + CheckGuardZone(); + + strcpy(EXCEPTMSG, "HTTP Timer Processing"); + + HTTPTimer(); + + CheckGuardZone(); + + strcpy(EXCEPTMSG, "WL2K Report Timer Processing"); + + if (ReportTimer) + { + ReportTimer--; + + if (ReportTimer == 0) + { + ReportTimer = REPORTINTERVAL; + SendLocation(); + } + } + } + + #include "StdExcept.c" + + if (Semaphore.Flag && Semaphore.SemProcessID == GetCurrentProcessId()) + FreeSemaphore(&Semaphore); + + } + + CheckGuardZone(); + + return; +} + +HANDLE NPHandle; + +int (WINAPI FAR *GetModuleFileNameExPtr)() = NULL; +int (WINAPI FAR *EnumProcessesPtr)() = NULL; + +FirstInit() +{ + WSADATA WsaData; // receives data from WSAStartup + HINSTANCE ExtDriver=0; + RECT cRect; + + + // First Time Ports and Timer init + + // Moved from DLLINIT to sort out perl problem, and meet MS Guidelines on minimising DLLMain + + // Call wsastartup - most systems need winsock, and duplicate statups could be a problem + + WSAStartup(MAKEWORD(2, 0), &WsaData); + + // Load Psapi.dll if possible + + ExtDriver=LoadLibrary("Psapi.dll"); + + SetupTrayIcon(); + + if (ExtDriver) + { + GetModuleFileNameExPtr = (FARPROCX)GetProcAddress(ExtDriver,"GetModuleFileNameExA"); + EnumProcessesPtr = (FARPROCX)GetProcAddress(ExtDriver,"EnumProcesses"); + } + + timeLoadedMS = GetTickCount(); + + INITIALISEPORTS(); + + OpenReportingSockets(); + + WritetoConsole("\n"); + WritetoConsole("Port Initialisation Complete\n"); + + if (IPRequired) IPActive = Init_IP(); + if (PMRequired) PMActive = Init_PM(); + + APRSActive = Init_APRS(); + + if (APRSActive) + { + hWndBG = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 0,0,40,546, hConsWnd, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Enable IGate", WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, + 8,0,90,24, hConsWnd, (HMENU)-1, hInstance, NULL); + + CreateWindowEx(0, "BUTTON", "", WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP, + 95,1,18,24, hConsWnd, (HMENU)IDC_ENIGATE, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "IGate State - Disconnected", + WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 125, 0, 195, 24, hConsWnd, (HMENU)IGATESTATE, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "IGATE Stats - Msgs 0 Local Stns 0", + WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 320, 0, 240, 24, hConsWnd, (HMENU)IGATESTATS, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "GPS Off", + WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 560, 0, 80, 24, hConsWnd, (HMENU)IDC_GPS, hInstance, NULL); + } + + if (ISPort == 0) + IGateEnabled = 0; + + CheckDlgButton(hConsWnd, IDC_ENIGATE, IGateEnabled); + + GetClientRect(hConsWnd, &cRect); + MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); + if (APRSActive) + MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); + else + { + ShowWindow(GetDlgItem(hConsWnd, IDC_GPS), SW_HIDE); + MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); + } + InvalidateRect(hConsWnd, NULL, TRUE); + + RigActive = Rig_Init(); + + _beginthread(MonitorThread,0,0); + + TimerHandle=SetTimer(NULL,0,100,lpTimerFunc); + TimerInst=GetCurrentProcessId(); + SessHandle = SetTimer(NULL, 0, 5000, lpSetupTermSessions); + + // If ARIF reporting is enabled write a Trimode Like ini for RMS Analyser + + if (ADIFLogEnabled) + ADIFWriteFreqList(); + + OutputDebugString("BPQ32 Port Initialisation Complete\n"); + + if (needAIS) + initAIS(); + + if (needADSB) + initADSB(); + + return 0; +} + +Check_Timer() +{ + if (Closing) + return 0; + + if (Semaphore.Flag) + return 0; + + if (InitDone == (void *)-1) + { + GetSemaphore(&Semaphore, 3); + Sleep(15000); + FreeSemaphore(&Semaphore); + exit (0); + } + + if (FirstInitDone == 0) + { + GetSemaphore(&Semaphore, 3); + + if (_stricmp(pgm, "bpq32.exe") == 0) + { + FirstInit(); + FreeSemaphore(&Semaphore); + if (NUMBEROFTNCPORTS) + InitializeTNCEmulator(); + + AGWActive = AGWAPIInit(); + FirstInitDone=1; // Only init in BPQ32.exe + return 0; + } + else + { + FreeSemaphore(&Semaphore); + return 0; + } + } + + if (TimerHandle == 0 && FirstInitDone == 1) + { + WSADATA WsaData; // receives data from WSAStartup + HINSTANCE ExtDriver=0; + RECT cRect; + + // Only attach timer to bpq32.exe + + if (_stricmp(pgm, "bpq32.exe") != 0) + { + return 0; + } + + GetSemaphore(&Semaphore, 3); + OutputDebugString("BPQ32 Reinitialising External Ports and Attaching Timer\n"); + + if (!ProcessConfig()) + { + ShowWindow(hConsWnd, SW_RESTORE); + SendMessage(hConsWnd, WM_PAINT, 0, 0); + SetForegroundWindow(hConsWnd); + + InitDone = (void *)-1; + FreeSemaphore(&Semaphore); + + MessageBox(NULL,"Configuration File Error","BPQ32",MB_ICONSTOP); + + exit (0); + } + + GetVersionInfo("bpq32.dll"); + + SetupConsoleWindow(); + + Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); + Consoleprintf(VerCopyright); + Consoleprintf("Reinitialising..."); + + SetupBPQDirectory(); + + Sleep(1000); // Allow time for sockets to close + + WSAStartup(MAKEWORD(2, 0), &WsaData); + + // Load Psapi.dll if possible + + ExtDriver = LoadLibrary("Psapi.dll"); + + SetupTrayIcon(); + + if (ExtDriver) + { + GetModuleFileNameExPtr = (FARPROCX)GetProcAddress(ExtDriver,"GetModuleFileNameExA"); + EnumProcessesPtr = (FARPROCX)GetProcAddress(ExtDriver,"EnumProcesses"); + } + + Start(); + + INITIALISEPORTS(); + + OpenReportingSockets(); + + NODESINPROGRESS = 0; + CURRENTNODE = 0; + + SetApplPorts(); + + WritetoConsole("\n\nPort Reinitialisation Complete\n"); + + BPQMsg = RegisterWindowMessage(BPQWinMsg); + + CreateMutex(NULL,TRUE,"BPQLOCKMUTEX"); + +// NPHandle=CreateNamedPipe("\\\\.\\pipe\\BPQ32pipe", +// PIPE_ACCESS_DUPLEX,0,64,4096,4096,1000,NULL); + + if (IPRequired) IPActive = Init_IP(); + if (PMRequired) PMActive = Init_PM(); + + RigActive = Rig_Init(); + APRSActive = Init_APRS(); + + if (ISPort == 0) + IGateEnabled = 0; + + CheckDlgButton(hConsWnd, IDC_ENIGATE, IGateEnabled); + + GetClientRect(hConsWnd, &cRect); + MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); + + if (APRSActive) + MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); + else + { + ShowWindow(GetDlgItem(hConsWnd, IDC_GPS), SW_HIDE); + MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); + } + InvalidateRect(hConsWnd, NULL, TRUE); + + FreeConfig(); + + _beginthread(MonitorThread,0,0); + + ReportTimer = 0; + + OpenReportingSockets(); + + FreeSemaphore(&Semaphore); + + if (NUMBEROFTNCPORTS) + InitializeTNCEmulator(); + + AGWActive = AGWAPIInit(); + + if (StartMinimized) + if (MinimizetoTray) + ShowWindow(FrameWnd, SW_HIDE); + else + ShowWindow(FrameWnd, SW_SHOWMINIMIZED); + else + ShowWindow(FrameWnd, SW_RESTORE); + + TimerHandle=SetTimer(NULL,0,100,lpTimerFunc); + TimerInst=GetCurrentProcessId(); + SessHandle = SetTimer(NULL, 0, 5000, lpSetupTermSessions); + + return (1); + } + + return (0); +} + +DllExport INT APIENTRY CheckTimer() +{ + return Check_Timer(); +} + +Tell_Sessions() +{ + // + // Post a message to all listening sessions, so they call the + // API, and cause a new timer to be allocated + // + HWND hWnd; + int i; + + for (i=1;i<65;i++) + { + if (BPQHOSTVECTOR[i-1].HOSTFLAGS & 0x80) + { + hWnd = BPQHOSTVECTOR[i-1].HOSTHANDLE; + PostMessage(hWnd, BPQMsg,i, 1); + PostMessage(hWnd, BPQMsg,i, 2); + } + } + return (0); +} + +BOOL APIENTRY DllMain(HANDLE hInst, DWORD ul_reason_being_called, LPVOID lpReserved) +{ + DWORD n; + char buf[350]; + + int i; + unsigned int ProcessID; + + OSVERSIONINFO osvi; + + memset(&osvi, 0, sizeof(OSVERSIONINFO)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + + GetVersionEx(&osvi); + + + switch( ul_reason_being_called ) + { + case DLL_PROCESS_ATTACH: + + if (sizeof(HDLCDATA) > PORTENTRYLEN + 200) // 200 bytes of Hardwaredata + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"BPQ32 Too much HDLC data - Recompile","BPQ32", MB_OK); + return 0; + } + + if (sizeof(struct KISSINFO) > PORTENTRYLEN + 200) // 200 bytes of Hardwaredata + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"BPQ32 Too much KISS data - Recompile","BPQ32", MB_OK); + return 0; + } + + if (sizeof(struct _EXTPORTDATA) > PORTENTRYLEN + 200) // 200 bytes of Hardwaredata + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"BPQ32 Too much _EXTPORTDATA data - Recompile","BPQ32", MB_OK); + return 0; + } + + if (sizeof(LINKTABLE) != LINK_TABLE_LEN) + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"L2 LINK Table .c and .asm mismatch - fix and rebuild","BPQ32", MB_OK); + return 0; + } + if (sizeof(struct ROUTE) != ROUTE_LEN) + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"ROUTE Table .c and .asm mismatch - fix and rebuild", "BPQ32", MB_OK); + return 0; + } + + if (sizeof(struct DEST_LIST) != DEST_LIST_LEN) + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"NODES Table .c and .asm mismatch - fix and rebuild", "BPQ32", MB_OK); + return 0; + } + + GetSemaphore(&Semaphore, 4); + + BPQHOSTVECPTR = &BPQHOSTVECTOR[0]; + + LoadToolHelperRoutines(); + + Our_PID = GetCurrentProcessId(); + + QueryPerformanceFrequency(&lpFrequency); + + ticksPerMillisec = (int)lpFrequency.QuadPart / 1000; + + lastRunTime.QuadPart = lpFrequency.QuadPart; + + GetProcess(Our_PID, pgm); + + if (_stricmp(pgm, "regsvr32.exe") == 0 || _stricmp(pgm, "bpqcontrol.exe") == 0) + { + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + return 1; + } + + if (_stricmp(pgm,"BPQ32.exe") == 0) + BPQ32_EXE = TRUE; + + if (_stricmp(pgm,"BPQMailChat.exe") == 0) + IncludesMail = TRUE; + + if (_stricmp(pgm,"BPQMail.exe") == 0) + IncludesMail = TRUE; + + if (_stricmp(pgm,"BPQChat.exe") == 0) + IncludesChat = TRUE; + + if (FirstEntry) // If loaded by BPQ32.exe, dont close it at end + { + FirstEntry = 0; + if (BPQ32_EXE) + CloseLast = FALSE; + } + else + { + if (BPQ32_EXE && AttachingProcess == 0) + { + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + MessageBox(NULL,"BPQ32.exe is already running\r\n\r\nIt should only be run once", "BPQ32", MB_OK); + return 0; + } + } + + if (_stricmp(pgm,"BPQTelnetServer.exe") == 0) + { + MessageBox(NULL,"BPQTelnetServer is no longer supported\r\n\r\nUse the TelnetServer in BPQ32.dll", "BPQ32", MB_OK); + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + return 0; + } + + if (_stricmp(pgm,"BPQUIUtil.exe") == 0) + { + MessageBox(NULL,"BPQUIUtil is now part of BPQ32.dll\r\nBPQUIUtil.exe cannot be run\r\n", "BPQ32", MB_OK); + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + return 0; + } + + if (_stricmp(pgm,"BPQMailChat.exe") == 0) + { + MessageBox(NULL,"BPQMailChat is obsolete. Run BPQMail.exe and/or BPQChat.exe instead", "BPQ32", MB_OK); + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + return 0; + } + AuthorisedProgram = TRUE; + + if (InitDone == 0) + { +// #pragma warning(push) +// #pragma warning(disable : 4996) + +// if (_winver < 0x0600) +// #pragma warning(pop) +// { +// // Below Vista +// +// REGTREE = HKEY_LOCAL_MACHINE; +// strcpy(REGTREETEXT, "HKEY_LOCAL_MACHINE"); +// } + + hInstance=hInst; + + Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"BPQLOCKMUTEX"); + + if (Mutex != NULL) + { + OutputDebugString("Another BPQ32.dll is loaded\n"); + i=MessageBox(NULL,"BPQ32 DLL already loaded from another directory\nIf you REALLY want this, hit OK, else hit Cancel","BPQ32",MB_OKCANCEL); + FreeSemaphore(&Semaphore); + + if (i != IDOK) return (0); + + CloseHandle(Mutex); + } + + if (!BPQ32_EXE) + { + if (CheckifBPQ32isLoaded() == FALSE) // Start BPQ32.exe if needed + { + // Wasn't Loaded, so we have started it, and should let it init system + + goto SkipInit; + } + } + + GetVersionInfo("bpq32.dll"); + + sprintf (SIGNONMSG, "G8BPQ AX25 Packet Switch System Version %s %s\r\n%s\r\n", + TextVerstring, Datestring, VerCopyright); + + SESSHDDRLEN = sprintf(SESSIONHDDR, "G8BPQ Network System %s for Win32 (", TextVerstring); + + SetupConsoleWindow(); + SetupBPQDirectory(); + + if (!ProcessConfig()) + { + StartMinimized = FALSE; + MinimizetoTray = FALSE; + ShowWindow(FrameWnd, SW_MAXIMIZE); + ShowWindow(hConsWnd, SW_MAXIMIZE); + ShowWindow(StatusWnd, SW_HIDE); + + SendMessage(hConsWnd, WM_PAINT, 0, 0); + SetForegroundWindow(hConsWnd); + + InitDone = (void *)-1; + FreeSemaphore(&Semaphore); + + MessageBox(NULL,"Configuration File Error\r\nProgram will close in 15 seconds","BPQ32",MB_ICONSTOP); + + return (0); + } + + Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); + Consoleprintf(VerCopyright); + + if (Start() !=0) + { + Sleep(3000); + FreeSemaphore(&Semaphore); + return (0); + } + else + { + SetApplPorts(); + + GetUIConfig(); + + InitDone = &InitDone; + BPQMsg = RegisterWindowMessage(BPQWinMsg); +// TimerHandle=SetTimer(NULL,0,100,lpTimerFunc); +// TimerInst=GetCurrentProcessId(); + +/* Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"BPQLOCKMUTEX"); + + if (Mutex != NULL) + { + OutputDebugString("Another BPQ32.dll is loaded\n"); + MessageBox(NULL,"BPQ32 DLL already loaded from another directory","BPQ32",MB_ICONSTOP); + FreeSemaphore(&Semaphore); + return (0); + } + +*/ + Mutex=CreateMutex(NULL,TRUE,"BPQLOCKMUTEX"); + +// CreatePipe(&H1,&H2,NULL,1000); + +// GetLastError(); + +// NPHandle=CreateNamedPipe("\\\\.\\pipe\\BPQ32pipe", +// PIPE_ACCESS_DUPLEX,0,64,4096,4096,1000,NULL); + +// GetLastError(); + +/* + // + // Read SYSOP password + // + + if (PWTEXT[0] == 0) + { + handle = OpenConfigFile("PASSWORD.BPQ"); + + if (handle == INVALID_HANDLE_VALUE) + { + WritetoConsole("Can't open PASSWORD.BPQ\n"); + PWLen=0; + PWTEXT[0]=0; + } + else + { + ReadFile(handle,PWTEXT,78,&n,NULL); + CloseHandle(handle); + } + } +*/ + for (i=0;PWTEXT[i] > 0x20;i++); //Scan for cr or null + PWLen=i; + + } + } + else + { + if (InitDone != &InitDone) + { + MessageBox(NULL,"BPQ32 DLL already loaded at another address","BPQ32",MB_ICONSTOP); + FreeSemaphore(&Semaphore); + return (0); + } + } + + // Run timer monitor thread in all processes - it is possible for the TImer thread not to be the first thread +SkipInit: + + _beginthread(MonitorTimerThread,0,0); + + FreeSemaphore(&Semaphore); + + AttachedPIDList[AttachedProcesses++] = GetCurrentProcessId(); + + if (_stricmp(pgm,"bpq32.exe") == 0 && AttachingProcess == 1) AttachingProcess = 0; + + GetProcess(GetCurrentProcessId(),pgm); + n=sprintf(buf,"BPQ32 DLL Attach complete - Program %s - %d Process(es) Attached\n",pgm,AttachedProcesses); + OutputDebugString(buf); + + // Set up local variables + + MCOM=1; + MTX=1; + MMASK=0xffffffffffffffff; + +// if (StartMinimized) +// if (MinimizetoTray) +// ShowWindow(FrameWnd, SW_HIDE); +// else +// ShowWindow(FrameWnd, SW_SHOWMINIMIZED); +// else +// ShowWindow(FrameWnd, SW_RESTORE); + + return 1; + + case DLL_THREAD_ATTACH: + + return 1; + + case DLL_THREAD_DETACH: + + return 1; + + case DLL_PROCESS_DETACH: + + if (_stricmp(pgm,"BPQMailChat.exe") == 0) + IncludesMail = FALSE; + + if (_stricmp(pgm,"BPQChat.exe") == 0) + IncludesChat = FALSE; + + ProcessID=GetCurrentProcessId(); + + Debugprintf("BPQ32 Process %d Detaching", ProcessID); + + // Release any streams that the app has failed to release + + for (i=1;i<65;i++) + { + if (BPQHOSTVECTOR[i-1].STREAMOWNER == ProcessID) + { + // If connected, disconnect + + SessionControl(i, 2, 0); + DeallocateStream(i); + } + } + + // Remove any Tray Icon Entries + + for( i = 0; i < 100; ++i ) + { + if (PIDArray[i] == ProcessID) + { + char Log[80]; + hWndArray[i] = 0; + sprintf(Log,"BPQ32 Removing Tray Item %s\n", PopupText[i]); + OutputDebugString(Log); + DeleteMenu(trayMenu,TRAYBASEID+i,MF_BYCOMMAND); + } + } + + if (Mutex) CloseHandle(Mutex); + + // Remove our entry from PID List + + for (i=0; i< AttachedProcesses; i++) + if (AttachedPIDList[i] == ProcessID) + break; + + for (; i< AttachedProcesses; i++) + { + AttachedPIDList[i]=AttachedPIDList[i+1]; + } + + AttachedProcesses--; + + if (TimerInst == ProcessID) + { + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + + OutputDebugString("BPQ32 Process with Timer closing\n"); + + // Call Port Close Routines + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR && PORTVEC->DLLhandle == NULL) // Don't call if real .dll - it's not there! + { + SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + SaveAXIPWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + PORTVEC->PORT_EXT_ADDR(5,PORTVEC->PORTCONTROL.PORTNUMBER, NULL); // Close External Ports + } + } + + PORTVEC->PORTCONTROL.PORTCLOSECODE(&PORTVEC->PORTCONTROL); + + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + + IPClose(); + PMClose(); + APRSClose(); + Rig_Close(); + CloseTNCEmulator(); + if (AGWActive) + AGWAPITerminate(); + + upnpClose(); + + WSACleanup(); + WSAGetLastError(); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + if (hConsWnd) DestroyWindow(hConsWnd); + + KillTimer(NULL,TimerHandle); + TimerHandle=0; + TimerInst=0xffffffff; + + if (AttachedProcesses && Closing == FALSE && AttachingProcess == 0) // Other processes + { + OutputDebugString("BPQ32 Reloading BPQ32.exe\n"); + StartBPQ32(); + } + } + else + { + // Not Timer Process + + if (AttachedProcesses == 1 && CloseLast) // Only bpq32.exe left + { + Debugprintf("Only BPQ32.exe running - close it"); + CloseAllNeeded = TRUE; + } + } + + if (AttachedProcesses < 2) + { + if (AUTOSAVE) + SaveNodes(); + if (AUTOSAVEMH) + SaveMH(); + + if (needAIS) + SaveAIS(); + } + if (AttachedProcesses == 0) + { + Closing = TRUE; + KillTimer(NULL,TimerHandle); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + // Unload External Drivers + + { + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10 && PORTVEC->DLLhandle) + FreeLibrary(PORTVEC->DLLhandle); + + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + } + } + + GetProcess(GetCurrentProcessId(),pgm); + n=sprintf(buf,"BPQ32 DLL Detach complete - Program %s - %d Process(es) Attached\n",pgm,AttachedProcesses); + OutputDebugString(buf); + + return 1; + } + return 1; +} + +DllExport int APIENTRY CloseBPQ32() +{ + // Unload External Drivers + + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + int i; + int ProcessID = GetCurrentProcessId(); + + if (Semaphore.Flag == 1 && ProcessID == Semaphore.SemProcessID) + { + OutputDebugString("BPQ32 Process holding Semaphore called CloseBPQ32 - attempting recovery\r\n"); + Debugprintf("Last Sem Call %d %x %x %x %x %x %x", SemHeldByAPI, + Sem_eax, Sem_ebx, Sem_ecx, Sem_edx, Sem_esi, Sem_edi); + + Semaphore.Flag = 0; + SemHeldByAPI = 0; + } + + if (TimerInst == ProcessID) + { + OutputDebugString("BPQ32 Process with Timer called CloseBPQ32\n"); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR) + { + PORTVEC->PORT_EXT_ADDR(5,PORTVEC->PORTCONTROL.PORTNUMBER, NULL); + } + } + PORTVEC->PORTCONTROL.PORTCLOSECODE(&PORTVEC->PORTCONTROL); + + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + KillTimer(NULL,TimerHandle); + TimerHandle=0; + TimerInst=0xffffffff; + + IPClose(); + PMClose(); + APRSClose(); + Rig_Close(); + if (AGWActive) + AGWAPITerminate(); + + upnpClose(); + + CloseTNCEmulator(); + WSACleanup(); + + if (hConsWnd) DestroyWindow(hConsWnd); + + Debugprintf("AttachedProcesses %d ", AttachedProcesses); + + if (AttachedProcesses > 1 && Closing == FALSE && AttachingProcess == 0) // Other processes + { + OutputDebugString("BPQ32 Reloading BPQ32.exe\n"); + StartBPQ32(); + } + } + + return 0; +} + +BOOL CopyReg(HKEY hKeyIn, HKEY hKeyOut); + +VOID SetupBPQDirectory() +{ + HKEY hKey = 0; + HKEY hKeyIn = 0; + HKEY hKeyOut = 0; + int disp; + int retCode,Type,Vallen=MAX_PATH,i; + char msg[512]; + char ValfromReg[MAX_PATH] = ""; + char DLLName[256]="Not Known"; + char LogDir[256]; + char Time[64]; + +/* +•NT4 was/is '4' +•Win 95 is 4.00.950 +•Win 98 is 4.10.1998 +•Win 98 SE is 4.10.2222 +•Win ME is 4.90.3000 +•2000 is NT 5.0.2195 +•XP is actually 5.1 +•Vista is 6.0 +•Win7 is 6.1 + + i = _osver; / Build + i = _winmajor; + i = _winminor; +*/ +/* +#pragma warning(push) +#pragma warning(disable : 4996) + +if (_winver < 0x0600) +#pragma warning(pop) + { + // Below Vista + + REGTREE = HKEY_LOCAL_MACHINE; + strcpy(REGTREETEXT, "HKEY_LOCAL_MACHINE"); + ValfromReg[0] = 0; + } + else +*/ + { + if (_stricmp(pgm, "regsvr32.exe") == 0) + { + Debugprintf("BPQ32 loaded by regsvr32.exe - Registry not copied"); + } + else + { + // If necessary, move reg from HKEY_LOCAL_MACHINE to HKEY_CURRENT_USER + + retCode = RegOpenKeyEx (HKEY_LOCAL_MACHINE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_READ, + &hKeyIn); + + retCode = RegCreateKeyEx(HKEY_CURRENT_USER, "SOFTWARE\\G8BPQ\\BPQ32", 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKeyOut, &disp); + + // See if Version Key exists in HKEY_CURRENT_USER - if it does, we have already done the copy + + Vallen = MAX_PATH; + retCode = RegQueryValueEx(hKeyOut, "Version" ,0 , &Type,(UCHAR *)&msg, &Vallen); + + if (retCode != ERROR_SUCCESS) + if (hKeyIn) + CopyReg(hKeyIn, hKeyOut); + + RegCloseKey(hKeyIn); + RegCloseKey(hKeyOut); + } + } + + GetModuleFileName(hInstance,DLLName,256); + + BPQDirectory[0]=0; + + retCode = RegOpenKeyEx (REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_QUERY_VALUE, + &hKey); + + if (retCode == ERROR_SUCCESS) + { + // Try "BPQ Directory" + + Vallen = MAX_PATH; + retCode = RegQueryValueEx(hKey,"BPQ Directory",0, + &Type,(UCHAR *)&ValfromReg,&Vallen); + + if (retCode == ERROR_SUCCESS) + { + if (strlen(ValfromReg) == 2 && ValfromReg[0] == '"' && ValfromReg[1] == '"') + ValfromReg[0]=0; + } + + if (ValfromReg[0] == 0) + { + // BPQ Directory absent or = "" - try "Config File Location" + + Vallen = MAX_PATH; + + retCode = RegQueryValueEx(hKey,"Config File Location",0, + &Type,(UCHAR *)&ValfromReg,&Vallen); + + if (retCode == ERROR_SUCCESS) + { + if (strlen(ValfromReg) == 2 && ValfromReg[0] == '"' && ValfromReg[1] == '"') + ValfromReg[0]=0; + } + } + + if (ValfromReg[0] == 0) GetCurrentDirectory(MAX_PATH, ValfromReg); + + // Get StartMinimized and MinimizetoTray flags + + Vallen = 4; + retCode = RegQueryValueEx(hKey, "Start Minimized", 0, &Type, (UCHAR *)&StartMinimized, &Vallen); + + Vallen = 4; + retCode = RegQueryValueEx(hKey, "Minimize to Tray", 0, &Type, (UCHAR *)&MinimizetoTray, &Vallen); + + ExpandEnvironmentStrings(ValfromReg, BPQDirectory, MAX_PATH); + + // Also get "BPQ Program Directory" + + ValfromReg[0] = 0; + Vallen = MAX_PATH; + + retCode = RegQueryValueEx(hKey, "BPQ Program Directory",0 , &Type, (UCHAR *)&ValfromReg, &Vallen); + + if (retCode == ERROR_SUCCESS) + ExpandEnvironmentStrings(ValfromReg, BPQProgramDirectory, MAX_PATH); + + // And Log Directory + + ValfromReg[0] = 0; + Vallen = MAX_PATH; + + retCode = RegQueryValueEx(hKey, "Log Directory",0 , &Type, (UCHAR *)&ValfromReg, &Vallen); + + if (retCode == ERROR_SUCCESS) + ExpandEnvironmentStrings(ValfromReg, LogDirectory, MAX_PATH); + + RegCloseKey(hKey); + } + + strcpy(ConfigDirectory, BPQDirectory); + + if (LogDirectory[0] == 0) + strcpy(LogDirectory, BPQDirectory); + + if (BPQProgramDirectory[0] == 0) + strcpy(BPQProgramDirectory, BPQDirectory); + + sprintf(msg,"BPQ32 Ver %s Loaded from: %s by %s\n", VersionString, DLLName, pgm); + WritetoConsole(msg); + OutputDebugString(msg); + FormatTime3(Time, time(NULL)); + sprintf(msg,"Loaded %s\n", Time); + WritetoConsole(msg); + OutputDebugString(msg); + +#pragma warning(push) +#pragma warning(disable : 4996) + +#if _MSC_VER >= 1400 + +#define _winmajor 6 +#define _winminor 0 + +#endif + + i=sprintf(msg,"Windows Ver %d.%d, Using Registry Key %s\n" ,_winmajor, _winminor, REGTREETEXT); + +#pragma warning(pop) + + WritetoConsole(msg); + OutputDebugString(msg); + + i=sprintf(msg,"BPQ32 Using config from: %s\n\n",BPQDirectory); + WritetoConsole(&msg[6]); + msg[i-1]=0; + OutputDebugString(msg); + + // Don't write the Version Key if loaded by regsvr32.exe (Installer is running with Admin rights, + // so will write the wrong tree on ) + + if (_stricmp(pgm, "regsvr32.exe") == 0) + { + Debugprintf("BPQ32 loaded by regsvr32.exe - Version String not written"); + } + else + { + retCode = RegCreateKeyEx(REGTREE, "SOFTWARE\\G8BPQ\\BPQ32", 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + sprintf(msg,"%d,%d,%d,%d", Ver[0], Ver[1], Ver[2], Ver[3]); + retCode = RegSetValueEx(hKey, "Version",0, REG_SZ,(BYTE *)msg, strlen(msg) + 1); + + RegCloseKey(hKey); + } + + // Make sure Logs Directory exists + + sprintf(LogDir, "%s/Logs", LogDirectory); + + CreateDirectory(LogDir, NULL); + + return; +} + +HANDLE OpenConfigFile(char *fn) +{ + HANDLE handle; + UCHAR Value[MAX_PATH]; + FILETIME LastWriteTime; + SYSTEMTIME Time; + char Msg[256]; + + + // If no directory, use current + if (BPQDirectory[0] == 0) + { + strcpy(Value,fn); + } + else + { + strcpy(Value,BPQDirectory); + strcat(Value,"\\"); + strcat(Value,fn); + } + + handle = CreateFile(Value, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + GetFileTime(handle, NULL, NULL, &LastWriteTime); + FileTimeToSystemTime(&LastWriteTime, &Time); + + sprintf(Msg,"BPQ32 Config File %s Created %.2d:%.2d %d/%.2d/%.2d\n", Value, + Time.wHour, Time.wMinute, Time.wYear, Time.wMonth, Time.wDay); + + OutputDebugString(Msg); + + return(handle); +} + +#ifdef _WIN64 +int BPQHOSTAPI() +{ + return 0; +} +#endif + + +DllExport int APIENTRY GETBPQAPI() +{ + return (int)BPQHOSTAPI; +} + +//DllExport UINT APIENTRY GETMONDECODE() +//{ +// return (UINT)MONDECODE; +//} + + +DllExport INT APIENTRY BPQAPI(int Fn, char * params) +{ + +/* +; +; BPQ HOST MODE SUPPORT CODE +; +; 22/11/95 +; +; MOVED FROM TNCODE.ASM COS CONITIONALS WERE GETTING TOO COMPLICATED +; (OS2 VERSION HAD UPSET KANT VERISON +; +; +*/ + + +/* + + BPQHOSTPORT: +; +; SPECIAL INTERFACE, MAINLY FOR EXTERNAL HOST MODE SUPPORT PROGS +; +; COMMANDS SUPPORTED ARE +; +; AH = 0 Get node/switch version number and description. On return +; AH='B',AL='P',BH='Q',BL=' ' +; DH = major version number and DL = minor version number. +; +; +; AH = 1 Set application mask to value in DL (or even DX if 16 +; applications are ever to be supported). +; +; Set application flag(s) to value in CL (or CX). +; whether user gets connected/disconnected messages issued +; by the node etc. +; +; +; AH = 2 Send frame in ES:SI (length CX) +; +; +; AH = 3 Receive frame into buffer at ES:DI, length of frame returned +; in CX. BX returns the number of outstanding frames still to +; be received (ie. after this one) or zero if no more frames +; (ie. this is last one). +; +; +; +; AH = 4 Get stream status. Returns: +; +; CX = 0 if stream disconnected or CX = 1 if stream connected +; DX = 0 if no change of state since last read, or DX = 1 if +; the connected/disconnected state has changed since +; last read (ie. delta-stream status). +; +; +; +; AH = 6 Session control. +; +; CX = 0 Conneect - _APPLMASK in DL +; CX = 1 connect +; CX = 2 disconnect +; CX = 3 return user to node +; +; +; AH = 7 Get buffer counts for stream. Returns: +; +; AX = number of status change messages to be received +; BX = number of frames queued for receive +; CX = number of un-acked frames to be sent +; DX = number of buffers left in node +; SI = number of trace frames queued for receive +; +;AH = 8 Port control/information. Called with a stream number +; in AL returns: +; +; AL = Radio port on which channel is connected (or zero) +; AH = SESSION TYPE BITS +; BX = L2 paclen for the radio port +; CX = L2 maxframe for the radio port +; DX = L4 window size (if L4 circuit, or zero) +; ES:DI = CALLSIGN + +;AH = 9 Fetch node/application callsign & alias. AL = application +; number: +; +; 0 = node +; 1 = BBS +; 2 = HOST +; 3 = SYSOP etc. etc. +; +; Returns string with alias & callsign or application name in +; user's buffer pointed to by ES:SI length CX. For example: +; +; "WORCS:G8TIC" or "TICPMS:G8TIC-10". +; +; +; AH = 10 Unproto transmit frame. Data pointed to by ES:SI, of +; length CX, is transmitted as a HDLC frame on the radio +; port (not stream) in AL. +; +; +; AH = 11 Get Trace (RAW Data) Frame into ES:DI, +; Length to CX, Timestamp to AX +; +; +; AH = 12 Update Switch. At the moment only Beacon Text may be updated +; DX = Function +; 1=update BT. ES:SI, Len CX = Text +; 2=kick off nodes broadcast +; +; AH = 13 Allocate/deallocate stream +; If AL=0, return first free stream +; If AL>0, CL=1, Allocate stream. If aleady allocated, +; return CX nonzero, else allocate, and return CX=0 +; If AL>0, CL=2, Release stream +; +; +; AH = 14 Internal Interface for IP Router +; +; Send frame - to NETROM L3 if DL=0 +; to L2 Session if DL<>0 +; +; +; AH = 15 Get interval timer + + +*/ + + + switch(Fn) + { + + case CHECKLOADED: + + params[0]=MAJORVERSION; + params[1]=MINORVERSION; + params[2]=QCOUNT; + + return (1); + } + return 0; +} + +DllExport int APIENTRY InitSwitch() +{ + return (0); +} + +/*DllExport int APIENTRY SwitchTimer() +{ + GetSemaphore((&Semaphore); + + TIMERINTERRUPT(); + + FreeSemaphore(&Semaphore); + + return (0); +} +*/ +DllExport int APIENTRY GetFreeBuffs() +{ +// Returns number of free buffers +// (BPQHOST function 7 (part)). + return (QCOUNT); +} + +DllExport UCHAR * APIENTRY GetNodeCall() +{ + return (&MYNODECALL); +} + + +DllExport UCHAR * APIENTRY GetNodeAlias() +{ + return (&MYALIASTEXT[0]); +} + +DllExport UCHAR * APIENTRY GetBBSCall() +{ + return (UCHAR *)(&APPLCALLTABLE[0].APPLCALL_TEXT); +} + + +DllExport UCHAR * APIENTRY GetBBSAlias() +{ + return (UCHAR *)(&APPLCALLTABLE[0].APPLALIAS_TEXT); +} + +DllExport VOID APIENTRY GetApplCallVB(int Appl, char * ApplCall) +{ + if (Appl < 1 || Appl > NumberofAppls ) return; + + strncpy(ApplCall,(char *)&APPLCALLTABLE[Appl-1].APPLCALL_TEXT, 10); +} + +BOOL UpdateNodesForApp(int Appl); + +DllExport BOOL APIENTRY SetApplCall(int Appl, char * NewCall) +{ + char Call[10]=" "; + int i; + + if (Appl < 1 || Appl > NumberofAppls ) return FALSE; + + i=strlen(NewCall); + + if (i > 10) i=10; + + strncpy(Call,NewCall,i); + + strncpy((char *)&APPLCALLTABLE[Appl-1].APPLCALL_TEXT,Call,10); + + if (!ConvToAX25(Call,APPLCALLTABLE[Appl-1].APPLCALL)) return FALSE; + + UpdateNodesForApp(Appl); + + return TRUE; + +} + +DllExport BOOL APIENTRY SetApplAlias(int Appl, char * NewCall) +{ + char Call[10]=" "; + int i; + + if (Appl < 1 || Appl > NumberofAppls ) return FALSE; + + i=strlen(NewCall); + + if (i > 10) i=10; + + strncpy(Call,NewCall,i); + + strncpy((char *)&APPLCALLTABLE[Appl-1].APPLALIAS_TEXT,Call,10); + + if (!ConvToAX25(Call,APPLCALLTABLE[Appl-1].APPLALIAS)) return FALSE; + + UpdateNodesForApp(Appl); + + return TRUE; + +} + + + +DllExport BOOL APIENTRY SetApplQual(int Appl, int NewQual) +{ + if (Appl < 1 || Appl > NumberofAppls ) return FALSE; + + APPLCALLTABLE[Appl-1].APPLQUAL=NewQual; + + UpdateNodesForApp(Appl); + + return TRUE; + +} + + +BOOL UpdateNodesForApp(int Appl) +{ + int App=Appl-1; + int DestLen = sizeof (struct DEST_LIST); + int n = MAXDESTS; + + struct DEST_LIST * DEST = APPLCALLTABLE[App].NODEPOINTER; + APPLCALLS * APPL=&APPLCALLTABLE[App]; + + if (DEST == NULL) + { + // No dest at the moment. If we have valid call and Qual, create an entry + + if (APPLCALLTABLE[App].APPLQUAL == 0) return FALSE; + + if (APPLCALLTABLE[App].APPLCALL[0] < 41) return FALSE; + + + GetSemaphore(&Semaphore, 5); + + DEST = DESTS; + + while (n--) + { + if (DEST->DEST_CALL[0] == 0) // Spare + break; + } + + if (n == 0) + { + // no dests + + FreeSemaphore(&Semaphore); + return FALSE; + } + + NUMBEROFNODES++; + APPL->NODEPOINTER = DEST; + + memmove (DEST->DEST_CALL,APPL->APPLCALL,13); + + DEST->DEST_STATE=0x80; // SPECIAL ENTRY + + DEST->NRROUTE[0].ROUT_QUALITY = (BYTE)APPL->APPLQUAL; + DEST->NRROUTE[0].ROUT_OBSCOUNT = 255; + + FreeSemaphore(&Semaphore); + + return TRUE; + } + + // We have a destination. If Quality is zero, remove it, else update it + + if (APPLCALLTABLE[App].APPLQUAL == 0) + { + GetSemaphore(&Semaphore, 6); + + REMOVENODE(DEST); // Clear buffers, Remove from Sorted Nodes chain, and zap entry + + APPL->NODEPOINTER=NULL; + + FreeSemaphore(&Semaphore); + return FALSE; + + } + + if (APPLCALLTABLE[App].APPLCALL[0] < 41) return FALSE; + + GetSemaphore(&Semaphore, 7); + + memmove (DEST->DEST_CALL,APPL->APPLCALL,13); + + DEST->DEST_STATE=0x80; // SPECIAL ENTRY + + DEST->NRROUTE[0].ROUT_QUALITY = (BYTE)APPL->APPLQUAL; + DEST->NRROUTE[0].ROUT_OBSCOUNT = 255; + + FreeSemaphore(&Semaphore); + return TRUE; + +} + + +DllExport UCHAR * APIENTRY GetSignOnMsg() +{ + return (&SIGNONMSG[0]); +} + + +DllExport HKEY APIENTRY GetRegistryKey() +{ + return REGTREE; +} + +DllExport char * APIENTRY GetRegistryKeyText() +{ + return REGTREETEXT;; +} + +DllExport UCHAR * APIENTRY GetBPQDirectory() +{ + while (BPQDirectory[0] == 0) + { + Debugprintf("BPQ Directory not set up - waiting"); + Sleep(1000); + } + return (&BPQDirectory[0]); +} + +DllExport UCHAR * APIENTRY GetProgramDirectory() +{ + return (&BPQProgramDirectory[0]); +} + +DllExport UCHAR * APIENTRY GetLogDirectory() +{ + return (&LogDirectory[0]); +} + +// Version for Visual Basic + +DllExport char * APIENTRY CopyBPQDirectory(char * dir) +{ + return (strcpy(dir,BPQDirectory)); +} + +DllExport int APIENTRY GetMsgPerl(int stream, char * msg) +{ + int len,count; + + GetMsg(stream, msg, &len, &count ); + + return len; +} + +int Rig_Command(int Session, char * Command); + +BOOL Rig_CommandInt(int Session, char * Command) +{ + return Rig_Command(Session, Command); +} + +DllExport int APIENTRY BPQSetHandle(int Stream, HWND hWnd) +{ + BPQHOSTVECTOR[Stream-1].HOSTHANDLE=hWnd; + return (0); +} + +#define L4USER 0 + +BPQVECSTRUC * PORTVEC ; + +VOID * InitializeExtDriver(PEXTPORTDATA PORTVEC) +{ + HINSTANCE ExtDriver=0; + char msg[128]; + int err=0; + HKEY hKey=0; + UCHAR Value[MAX_PATH]; + + // If no directory, use current + + if (BPQDirectory[0] == 0) + { + strcpy(Value,PORTVEC->PORT_DLL_NAME); + } + else + { + strcpy(Value,BPQDirectory); + strcat(Value,"\\"); + strcat(Value,PORTVEC->PORT_DLL_NAME); + } + + // Several Drivers are now built into bpq32.dll + + _strupr(Value); + + if (strstr(Value, "BPQVKISS")) + return VCOMExtInit; + + if (strstr(Value, "BPQAXIP")) + return AXIPExtInit; + + if (strstr(Value, "BPQETHER")) + return ETHERExtInit; + + if (strstr(Value, "BPQTOAGW")) + return AGWExtInit; + + if (strstr(Value, "AEAPACTOR")) + return AEAExtInit; + + if (strstr(Value, "HALDRIVER")) + return HALExtInit; + + if (strstr(Value, "KAMPACTOR")) + return KAMExtInit; + + if (strstr(Value, "SCSPACTOR")) + return SCSExtInit; + + if (strstr(Value, "WINMOR")) + return WinmorExtInit; + + if (strstr(Value, "V4")) + return V4ExtInit; + + if (strstr(Value, "TELNET")) + return TelnetExtInit; + +// if (strstr(Value, "SOUNDMODEM")) +// return SoundModemExtInit; + + if (strstr(Value, "SCSTRACKER")) + return TrackerExtInit; + + if (strstr(Value, "TRKMULTI")) + return TrackerMExtInit; + + if (strstr(Value, "UZ7HO")) + return UZ7HOExtInit; + + if (strstr(Value, "MULTIPSK")) + return MPSKExtInit; + + if (strstr(Value, "FLDIGI")) + return FLDigiExtInit; + + if (strstr(Value, "UIARQ")) + return UIARQExtInit; + +// if (strstr(Value, "BAYCOM")) +// return (UINT) BaycomExtInit; + + if (strstr(Value, "VARA")) + return VARAExtInit; + + if (strstr(Value, "ARDOP")) + return ARDOPExtInit; + + if (strstr(Value, "SERIAL")) + return SerialExtInit; + + if (strstr(Value, "KISSHF")) + return KISSHFExtInit; + + if (strstr(Value, "WINRPR")) + return WinRPRExtInit; + + if (strstr(Value, "HSMODEM")) + return HSMODEMExtInit; + + if (strstr(Value, "FREEDATA")) + return FreeDataExtInit; + + if (strstr(Value, "6PACK")) + return SIXPACKExtInit; + + ExtDriver = LoadLibrary(Value); + + if (ExtDriver == NULL) + { + err=GetLastError(); + + sprintf(msg,"Error loading Driver %s - Error code %d", + PORTVEC->PORT_DLL_NAME,err); + + MessageBox(NULL,msg,"BPQ32",MB_ICONSTOP); + + return(0); + } + + PORTVEC->DLLhandle=ExtDriver; + + return (GetProcAddress(ExtDriver,"_ExtInit@4")); + +} + +/* +_DATABASE LABEL BYTE + +FILLER DB 14 DUP (0) ; PROTECTION AGENST BUFFER PROBLEMS! + DB MAJORVERSION,MINORVERSION +_NEIGHBOURS DD 0 + DW TYPE ROUTE +_MAXNEIGHBOURS DW 20 ; MAX ADJACENT NODES + +_DESTS DD 0 ; NODE LIST + DW TYPE DEST_LIST +MAXDESTS DW 100 ; MAX NODES IN SYSTEM +*/ + + +DllExport int APIENTRY GetAttachedProcesses() +{ + return (AttachedProcesses); +} + +DllExport int * APIENTRY GetAttachedProcessList() +{ + return (&AttachedPIDList[0]); +} + +DllExport int * APIENTRY SaveNodesSupport() +{ + return (&DATABASESTART); +} + +// +// Internal BPQNODES support +// + +#define UCHAR unsigned char + +/* +ROUTE ADD G1HTL-1 2 200 0 0 0 +ROUTE ADD G4IRX-3 2 200 0 0 0 +NODE ADD MAPPLY:G1HTL-1 G1HTL-1 2 200 G4IRX-3 2 98 +NODE ADD NOT:GB7NOT G1HTL-1 2 199 G4IRX-3 2 98 + +*/ + +struct DEST_LIST * Dests; +struct ROUTE * Routes; + +int MaxNodes; +int MaxRoutes; +int NodeLen; +int RouteLen; + +int count; +int cursor; + +int len,i; + +ULONG cnt; +char Normcall[10]; +char Portcall[10]; +char Alias[7]; + +char line[100]; + +HANDLE handle; + +int APIENTRY Restart() +{ + int i, Count = AttachedProcesses; + HANDLE hProc; + DWORD PID; + + for (i = 0; i < Count; i++) + { + PID = AttachedPIDList[i]; + + // Kill Timer Owner last + + if (TimerInst != PID) + { + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, PID); + + if (hProc) + { + TerminateProcess(hProc, 0); + CloseHandle(hProc); + } + } + } + + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, TimerInst); + + if (hProc) + { + TerminateProcess(hProc, 0); + CloseHandle(hProc); + } + + + return 0; +} + +int APIENTRY Reboot() +{ + // Run shutdown -r -f + + STARTUPINFO SInfo; + PROCESS_INFORMATION PInfo; + char Cmd[] = "shutdown -r -f"; + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + return CreateProcess(NULL, Cmd, NULL, NULL, FALSE,0 ,NULL ,NULL, &SInfo, &PInfo); +} +/* +int APIENTRY Reconfig() +{ + if (!ProcessConfig()) + { + return (0); + } + SaveNodes(); + WritetoConsole("Nodes Saved\n"); + ReconfigFlag=TRUE; + WritetoConsole("Reconfig requested ... Waiting for Timer Poll\n"); + return 1; +} +*/ +// Code to support minimizing all BPQ Apps to a single Tray ICON + +// As we can't minimize the console window to the tray, I'll use an ordinary +// window instead. This also gives me somewhere to post the messages to + + +char AppName[] = "BPQ32"; +char Title[80] = "BPQ32.dll Console"; + +int NewLine(); + +char FrameClassName[] = TEXT("MdiFrame"); + +HWND ClientWnd; //This stores the MDI client area window handle + +LOGFONT LFTTYFONT ; + +HFONT hFont ; + +HMENU hPopMenu, hWndMenu; +HMENU hMainFrameMenu = NULL; +HMENU hBaseMenu = NULL; +HMENU hConsMenu = NULL; +HMENU hTermMenu = NULL; +HMENU hMonMenu = NULL; +HMENU hTermActMenu, hTermCfgMenu, hTermEdtMenu, hTermHlpMenu; +HMENU hMonActMenu, hMonCfgMenu, hMonEdtMenu, hMonHlpMenu; + + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +LRESULT CALLBACK StatusWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + +DllExport int APIENTRY DeleteTrayMenuItem(HWND hWnd); + +#define BPQMonitorAvail 1 +#define BPQDataAvail 2 +#define BPQStateChange 4 + +VOID GetJSONValue(char * _REPLYBUFFER, char * Name, char * Value); +SOCKET OpenWL2KHTTPSock(); +SendHTTPRequest(SOCKET sock, char * Request, char * Params, int Len, char * Return); + +BOOL GetWL2KSYSOPInfo(char * Call, char * _REPLYBUFFER); +BOOL UpdateWL2KSYSOPInfo(char * Call, char * SQL); + + +static INT_PTR CALLBACK ConfigWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + { + char _REPLYBUFFER[1000] = ""; + char Value[1000]; + + if (GetWL2KSYSOPInfo(WL2KCall, _REPLYBUFFER)) + { +// if (strstr(_REPLYBUFFER, "\"ErrorMessage\":") == 0) + + GetJSONValue(_REPLYBUFFER, "\"SysopName\":", Value); + SetDlgItemText(hDlg, NAME, Value); + + GetJSONValue(_REPLYBUFFER, "\"GridSquare\":", Value); + SetDlgItemText(hDlg, IDC_Locator, Value); + + GetJSONValue(_REPLYBUFFER, "\"StreetAddress1\":", Value); + SetDlgItemText(hDlg, ADDR1, Value); + + GetJSONValue(_REPLYBUFFER, "\"StreetAddress2\":", Value); + SetDlgItemText(hDlg, ADDR2, Value); + + GetJSONValue(_REPLYBUFFER, "\"City\":", Value); + SetDlgItemText(hDlg, CITY, Value); + + GetJSONValue(_REPLYBUFFER, "\"State\":", Value); + SetDlgItemText(hDlg, STATE, Value); + + GetJSONValue(_REPLYBUFFER, "\"Country\":", Value); + SetDlgItemText(hDlg, COUNTRY, Value); + + GetJSONValue(_REPLYBUFFER, "\"PostalCode\":", Value); + SetDlgItemText(hDlg, POSTCODE, Value); + + GetJSONValue(_REPLYBUFFER, "\"Email\":", Value); + SetDlgItemText(hDlg, EMAIL, Value); + + GetJSONValue(_REPLYBUFFER, "\"Website\":", Value); + SetDlgItemText(hDlg, WEBSITE, Value); + + GetJSONValue(_REPLYBUFFER, "\"Phones\":", Value); + SetDlgItemText(hDlg, PHONE, Value); + + GetJSONValue(_REPLYBUFFER, "\"Comments\":", Value); + SetDlgItemText(hDlg, ADDITIONALDATA, Value); + + } + + return (INT_PTR)TRUE; + } + case WM_COMMAND: + + switch(LOWORD(wParam)) + { + + case ID_SAVE: + { + char Name[100]; + char PasswordText[100]; + char LocatorText[100]; + char Addr1[100]; + char Addr2[100]; + char City[100]; + char State[100]; + char Country[100]; + char PostCode[100]; + char Email[100]; + char Website[100]; + char Phone[100]; + char Data[100]; + + SOCKET sock; + + int Len; + char Message[2048]; + char Reply[2048] = ""; + + + GetDlgItemText(hDlg, NAME, Name, 99); + GetDlgItemText(hDlg, IDC_Password, PasswordText, 99); + GetDlgItemText(hDlg, IDC_Locator, LocatorText, 99); + GetDlgItemText(hDlg, ADDR1, Addr1, 99); + GetDlgItemText(hDlg, ADDR2, Addr2, 99); + GetDlgItemText(hDlg, CITY, City, 99); + GetDlgItemText(hDlg, STATE, State, 99); + GetDlgItemText(hDlg, COUNTRY, Country, 99); + GetDlgItemText(hDlg, POSTCODE, PostCode, 99); + GetDlgItemText(hDlg, EMAIL, Email, 99); + GetDlgItemText(hDlg, WEBSITE, Website, 99); + GetDlgItemText(hDlg, PHONE, Phone, 99); + GetDlgItemText(hDlg, ADDITIONALDATA, Data, 99); + + +//{"Callsign":"String","GridSquare":"String","SysopName":"String", +//"StreetAddress1":"String","StreetAddress2":"String","City":"String", +//"State":"String","Country":"String","PostalCode":"String","Email":"String", +//"Phones":"String","Website":"String","Comments":"String"} + + Len = sprintf(Message, + "\"Callsign\":\"%s\"," + "\"Password\":\"%s\"," + "\"GridSquare\":\"%s\"," + "\"SysopName\":\"%s\"," + "\"StreetAddress1\":\"%s\"," + "\"StreetAddress2\":\"%s\"," + "\"City\":\"%s\"," + "\"State\":\"%s\"," + "\"Country\":\"%s\"," + "\"PostalCode\":\"%s\"," + "\"Email\":\"%s\"," + "\"Phones\":\"%s\"," + "\"Website\":\"%s\"," + "\"Comments\":\"%s\"", + + WL2KCall, PasswordText, LocatorText, Name, Addr1, Addr2, City, State, Country, PostCode, Email, Phone, Website, Data); + + Debugprintf("Sending %s", Message); + + sock = OpenWL2KHTTPSock(); + + if (sock) + { + char * ptr; + + SendHTTPRequest(sock, + "/sysop/add", Message, Len, Reply); + + ptr = strstr(Reply, "\"ErrorCode\":"); + + if (ptr) + { + ptr = strstr(ptr, "Message"); + if (ptr) + { + ptr += 10; + strlop(ptr, '"'); + MessageBox(NULL ,ptr, "Error", MB_OK); + } + } + else + MessageBox(NULL, "Sysop Record Updated", "BPQ32", MB_OK); + + } + closesocket(sock); + } + + case ID_CANCEL: + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + break; + } + } + return (INT_PTR)FALSE; +} + + + +LRESULT CALLBACK UIWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +VOID WINAPI OnTabbedDialogInit(HWND hDlg); + +LRESULT CALLBACK FrameWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + POINT pos; + BOOL ret; + + CLIENTCREATESTRUCT MDIClientCreateStruct; // Structure to be used for MDI client area + //HWND m_hwndSystemInformation = 0; + + if (message == BPQMsg) + { + if (lParam & BPQDataAvail) + DoReceivedData(wParam); + + if (lParam & BPQMonitorAvail) + DoMonData(wParam); + + if (lParam & BPQStateChange) + DoStateChange(wParam); + + return (0); + } + + switch (message) + { + case MY_TRAY_ICON_MESSAGE: + + switch(lParam) + { + case WM_RBUTTONUP: + case WM_LBUTTONUP: + + GetCursorPos(&pos); + + // SetForegroundWindow(FrameWnd); + + TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, FrameWnd, 0); + return 0; + } + + break; + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + case WM_SIZING: + case WM_SIZE: + + SendMessage(ClientWnd, WM_MDIICONARRANGE, 0 ,0); + break; + + case WM_NCCREATE: + + ret = DefFrameProc(hWnd, ClientWnd, message, wParam, lParam); + return TRUE; + + case WM_CREATE: + + // On creation of main frame, create the MDI client area + + MDIClientCreateStruct.hWindowMenu = NULL; + MDIClientCreateStruct.idFirstChild = IDM_FIRSTCHILD; + + ClientWnd = CreateWindow(TEXT("MDICLIENT"), // predefined value for MDI client area + NULL, // no caption required + WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE, + 0, // No need to give any x/y or height/width since this client + // will just be used to get client windows created, effectively + // in the main window we will be seeing the mainframe window client area itself. + 0, + 0, + 0, + hWnd, + NULL, + hInstance, + (void *) &MDIClientCreateStruct); + + + return 0; + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + if (wmId >= TRAYBASEID && wmId < (TRAYBASEID + 100)) + { + handle=hWndArray[wmId-TRAYBASEID]; + + if (handle == FrameWnd) + ShowWindow(handle, SW_NORMAL); + + if (handle == FrameWnd && FrameMaximized == TRUE) + PostMessage(handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0); + else + PostMessage(handle, WM_SYSCOMMAND, SC_RESTORE, 0); + + SetForegroundWindow(handle); + return 0; + } + + switch(wmId) + { + struct ConsoleInfo * Cinfo = NULL; + + case ID_NEWWINDOW: + Cinfo = CreateChildWindow(0, FALSE); + if (Cinfo) + SendMessage(ClientWnd, WM_MDIACTIVATE, (WPARAM)Cinfo->hConsole, 0); + break; + + case ID_WINDOWS_CASCADE: + SendMessage(ClientWnd, WM_MDICASCADE, 0, 0); + return 0; + + case ID_WINDOWS_TILE: + SendMessage(ClientWnd, WM_MDITILE , MDITILE_HORIZONTAL, 0); + return 0; + + case BPQCLOSEALL: + CloseAllPrograms(); + // SendMessage(ClientWnd, WM_MDIICONARRANGE, 0 ,0); + + return 0; + + case BPQUICONFIG: + { + int err, i=0; + char Title[80]; + WNDCLASS wc; + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = UIWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL; + wc.lpszClassName = UIClassName; + + RegisterClass(&wc); + + UIhWnd = CreateDialog(hInstance, UIClassName, 0, NULL); + + if (!UIhWnd) + { + err=GetLastError(); + return FALSE; + } + + wsprintf(Title,"BPQ32 Beacon Configuration"); + MySetWindowText(UIhWnd, Title); + ShowWindow(UIhWnd, SW_NORMAL); + + OnTabbedDialogInit(UIhWnd); // Set up pages + + // UpdateWindow(UIhWnd); + return 0; + } + + + case IDD_WL2KSYSOP: + + if (WL2KCall[0] == 0) + { + MessageBox(NULL,"WL2K Reporting is not configured","BPQ32", MB_OK); + break; + } + + DialogBox(hInstance, MAKEINTRESOURCE(IDD_WL2KSYSOP), hWnd, ConfigWndProc); + break; + + + // Handle MDI Window commands + + default: + { + if(wmId >= IDM_FIRSTCHILD) + { + DefFrameProc(hWnd, ClientWnd, message, wParam, lParam); + } + else + { + HWND hChild = (HWND)SendMessage(ClientWnd, WM_MDIGETACTIVE,0,0); + + if(hChild) + SendMessage(hChild, WM_COMMAND, wParam, lParam); + } + } + } + + break; + + case WM_INITMENUPOPUP: + { + HWND hChild = (HWND)SendMessage(ClientWnd, WM_MDIGETACTIVE,0,0); + + if(hChild) + SendMessage(hChild, WM_INITMENUPOPUP, wParam, lParam); + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_MAXIMIZE: + + FrameMaximized = TRUE; + break; + + case SC_RESTORE: + + FrameMaximized = FALSE; + break; + + case SC_MINIMIZE: + + if (MinimizetoTray) + { + ShowWindow(hWnd, SW_HIDE); + return TRUE; + } + } + + return (DefFrameProc(hWnd, ClientWnd, message, wParam, lParam)); + + case WM_CLOSE: + + PostQuitMessage(0); + + if (MinimizetoTray) + DeleteTrayMenuItem(hWnd); + + break; + + default: + return (DefFrameProc(hWnd, ClientWnd, message, wParam, lParam)); + + } + return (DefFrameProc(hWnd, ClientWnd, message, wParam, lParam)); +} + +int OffsetH, OffsetW; + +int SetupConsoleWindow() +{ + WNDCLASS wc; + int i; + int retCode, Type, Vallen; + HKEY hKey=0; + char Size[80]; + WNDCLASSEX wndclassMainFrame; + RECT CRect; + + retCode = RegOpenKeyEx (REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_QUERY_VALUE, + &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen=80; + + retCode = RegQueryValueEx(hKey,"FrameWindowSize",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size,"%d,%d,%d,%d",&FRect.left,&FRect.right,&FRect.top,&FRect.bottom); + + if (FRect.top < - 500 || FRect.left < - 500) + { + FRect.left = 0; + FRect.top = 0; + FRect.right = 600; + FRect.bottom = 400; + } + + + Vallen=80; + retCode = RegQueryValueEx(hKey,"WindowSize",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size,"%d,%d,%d,%d,%d",&Rect.left,&Rect.right,&Rect.top,&Rect.bottom, &ConsoleMinimized); + + if (Rect.top < - 500 || Rect.left < - 500) + { + Rect.left = 0; + Rect.top = 0; + Rect.right = 600; + Rect.bottom = 400; + } + + Vallen=80; + + retCode = RegQueryValueEx(hKey,"StatusWindowSize",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size, "%d,%d,%d,%d,%d", &StatusRect.left, &StatusRect.right, + &StatusRect.top, &StatusRect.bottom, &StatusMinimized); + + if (StatusRect.top < - 500 || StatusRect.left < - 500) + { + StatusRect.left = 0; + StatusRect.top = 0; + StatusRect.right = 850; + StatusRect.bottom = 500; + } + + + // Get StartMinimized and MinimizetoTray flags + + Vallen = 4; + retCode = RegQueryValueEx(hKey, "Start Minimized", 0, &Type, (UCHAR *)&StartMinimized, &Vallen); + + Vallen = 4; + retCode = RegQueryValueEx(hKey, "Minimize to Tray", 0, &Type, (UCHAR *)&MinimizetoTray, &Vallen); + } + + wndclassMainFrame.cbSize = sizeof(WNDCLASSEX); + wndclassMainFrame.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wndclassMainFrame.lpfnWndProc = FrameWndProc; + wndclassMainFrame.cbClsExtra = 0; + wndclassMainFrame.cbWndExtra = 0; + wndclassMainFrame.hInstance = hInstance; + wndclassMainFrame.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON)); + wndclassMainFrame.hCursor = LoadCursor(NULL, IDC_ARROW); + wndclassMainFrame.hbrBackground = (HBRUSH) GetStockObject(GRAY_BRUSH); + wndclassMainFrame.lpszMenuName = NULL; + wndclassMainFrame.lpszClassName = FrameClassName; + wndclassMainFrame.hIconSm = NULL; + + if(!RegisterClassEx(&wndclassMainFrame)) + { + return 0; + } + + pindex = 0; + PartLine = FALSE; + + bgBrush = CreateSolidBrush(BGCOLOUR); + +// hMainFrameMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MAINFRAME_MENU)); + + hBaseMenu = LoadMenu(hInstance, MAKEINTRESOURCE(CONS_MENU)); + hConsMenu = GetSubMenu(hBaseMenu, 1); + hWndMenu = GetSubMenu(hBaseMenu, 0); + + hTermMenu = LoadMenu(hInstance, MAKEINTRESOURCE(TERM_MENU)); + hTermActMenu = GetSubMenu(hTermMenu, 1); + hTermCfgMenu = GetSubMenu(hTermMenu, 2); + hTermEdtMenu = GetSubMenu(hTermMenu, 3); + hTermHlpMenu = GetSubMenu(hTermMenu, 4); + + hMonMenu = LoadMenu(hInstance, MAKEINTRESOURCE(MON_MENU)); + hMonCfgMenu = GetSubMenu(hMonMenu, 1); + hMonEdtMenu = GetSubMenu(hMonMenu, 2); + hMonHlpMenu = GetSubMenu(hMonMenu, 3); + + hMainFrameMenu = CreateMenu(); + AppendMenu(hMainFrameMenu, MF_STRING + MF_POPUP, (UINT)hWndMenu, "Window"); + + //Create the main MDI frame window + + ClientWnd = NULL; + + FrameWnd = CreateWindow(FrameClassName, + "BPQ32 Console", + WS_OVERLAPPEDWINDOW |WS_CLIPCHILDREN, + FRect.left, + FRect.top, + FRect.right - FRect.left, + FRect.bottom - FRect.top, + NULL, // handle to parent window + hMainFrameMenu, // handle to menu + hInstance, // handle to the instance of module + NULL); // Long pointer to a value to be passed to the window through the + // CREATESTRUCT structure passed in the lParam parameter the WM_CREATE message + + + // Get Client Params + + if (FrameWnd == 0) + { + Debugprintf("SetupConsoleWindow Create Frame failed %d", GetLastError()); + return 0; + } + + ShowWindow(FrameWnd, SW_RESTORE); + + + GetWindowRect(FrameWnd, &FRect); + OffsetH = FRect.bottom - FRect.top; + OffsetW = FRect.right - FRect.left; + GetClientRect(FrameWnd, &CRect); + OffsetH -= CRect.bottom; + OffsetW -= CRect.right; + OffsetH -= 4; + + // Create Console Window + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wc.lpfnWndProc = (WNDPROC)WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(BPQICON)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wc.lpszMenuName = 0; + wc.lpszClassName = ClassName; + + i=RegisterClass(&wc); + + sprintf (Title, "BPQ32.dll Console Version %s", VersionString); + + hConsWnd = CreateMDIWindow(ClassName, "Console", 0, + 0,0,0,0, ClientWnd, hInstance, 1234); + + i = GetLastError(); + + if (!hConsWnd) { + return (FALSE); + } + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wc.lpfnWndProc = (WNDPROC)StatusWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(BPQICON)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wc.lpszMenuName = 0; + wc.lpszClassName = "Status"; + + i=RegisterClass(&wc); + + if (StatusRect.top < OffsetH) // Make sure not off top of MDI frame + { + int Error = OffsetH - StatusRect.top; + StatusRect.top += Error; + StatusRect.bottom += Error; + } + + StatusWnd = CreateMDIWindow("Status", "Stream Status", 0, + StatusRect.left, StatusRect.top, StatusRect.right - StatusRect.left, + StatusRect.bottom - StatusRect.top, ClientWnd, hInstance, 1234); + + SetTimer(StatusWnd, 1, 1000, NULL); + + hPopMenu = GetSubMenu(hBaseMenu, 1) ; + + if (MinimizetoTray) + CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_CHECKED); + else + CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_UNCHECKED); + + if (StartMinimized) + CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_CHECKED); + else + CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_UNCHECKED); + + DrawMenuBar(hConsWnd); + + // setup default font information + + LFTTYFONT.lfHeight = 12; + LFTTYFONT.lfWidth = 8 ; + LFTTYFONT.lfEscapement = 0 ; + LFTTYFONT.lfOrientation = 0 ; + LFTTYFONT.lfWeight = 0 ; + LFTTYFONT.lfItalic = 0 ; + LFTTYFONT.lfUnderline = 0 ; + LFTTYFONT.lfStrikeOut = 0 ; + LFTTYFONT.lfCharSet = 0; + LFTTYFONT.lfOutPrecision = OUT_DEFAULT_PRECIS ; + LFTTYFONT.lfClipPrecision = CLIP_DEFAULT_PRECIS ; + LFTTYFONT.lfQuality = DEFAULT_QUALITY ; + LFTTYFONT.lfPitchAndFamily = FIXED_PITCH; + lstrcpy(LFTTYFONT.lfFaceName, "FIXEDSYS" ) ; + + hFont = CreateFontIndirect(&LFTTYFONT) ; + + SetWindowText(hConsWnd,Title); + + if (Rect.right < 100 || Rect.bottom < 100) + { + GetWindowRect(hConsWnd, &Rect); + } + + if (Rect.top < OffsetH) // Make sure not off top of MDI frame + { + int Error = OffsetH - Rect.top; + Rect.top += Error; + Rect.bottom += Error; + } + + + MoveWindow(hConsWnd, Rect.left - (OffsetW /2), Rect.top - OffsetH, Rect.right-Rect.left, Rect.bottom-Rect.top, TRUE); + + MoveWindow(StatusWnd, StatusRect.left - (OffsetW /2), StatusRect.top - OffsetH, + StatusRect.right-StatusRect.left, StatusRect.bottom-StatusRect.top, TRUE); + + hWndCons = CreateWindowEx(WS_EX_CLIENTEDGE, "LISTBOX", "", + WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | LBS_NOSEL | WS_VSCROLL | WS_HSCROLL, + Rect.left, Rect.top, Rect.right - Rect.left, Rect.bottom - Rect.top, + hConsWnd, NULL, hInstance, NULL); + +// SendMessage(hWndCons, WM_SETFONT, hFont, 0); + + SendMessage(hWndCons, LB_SETHORIZONTALEXTENT , 1000, 0); + + if (ConsoleMinimized) + ShowWindow(hConsWnd, SW_SHOWMINIMIZED); + else + ShowWindow(hConsWnd, SW_RESTORE); + + if (StatusMinimized) + ShowWindow(StatusWnd, SW_SHOWMINIMIZED); + else + ShowWindow(StatusWnd, SW_RESTORE); + + ShowWindow(FrameWnd, SW_RESTORE); + + + LoadLibrary("riched20.dll"); + + if (StartMinimized) + if (MinimizetoTray) + ShowWindow(FrameWnd, SW_HIDE); + else + ShowWindow(FrameWnd, SW_SHOWMINIMIZED); + else + ShowWindow(FrameWnd, SW_RESTORE); + + CreateMonitorWindow(Size); + + return 0; +} + +DllExport int APIENTRY SetupTrayIcon() +{ + if (MinimizetoTray == 0) + return 0; + + trayMenu = CreatePopupMenu(); + + for( i = 0; i < 100; ++i ) + { + if (strcmp(PopupText[i],"BPQ32 Console") == 0) + { + hWndArray[i] = FrameWnd; + goto doneit; + } + } + + for( i = 0; i < 100; ++i ) + { + if (hWndArray[i] == 0) + { + hWndArray[i] = FrameWnd; + strcpy(PopupText[i],"BPQ32 Console"); + break; + } + } +doneit: + + for( i = 0; i < 100; ++i ) + { + if (hWndArray[i] != 0) + AppendMenu(trayMenu,MF_STRING,TRAYBASEID+i,PopupText[i]); + } + + // Set up Tray ICON + + ZeroMemory(&niData,sizeof(NOTIFYICONDATA)); + + niData.cbSize = sizeof(NOTIFYICONDATA); + + // the ID number can be any UINT you choose and will + // be used to identify your icon in later calls to + // Shell_NotifyIcon + + niData.uID = TRAY_ICON_ID; + + // state which structure members are valid + // here you can also choose the style of tooltip + // window if any - specifying a balloon window: + // NIF_INFO is a little more complicated + + strcpy(niData.szTip,"BPQ32 Windows"); + + niData.uFlags = NIF_ICON|NIF_MESSAGE|NIF_TIP; + + // load the icon note: you should destroy the icon + // after the call to Shell_NotifyIcon + + niData.hIcon = + + //LoadIcon(NULL, IDI_APPLICATION); + + (HICON)LoadImage( hInstance, + MAKEINTRESOURCE(BPQICON), + IMAGE_ICON, + GetSystemMetrics(SM_CXSMICON), + GetSystemMetrics(SM_CYSMICON), + LR_DEFAULTCOLOR); + + + // set the window you want to receive event messages + + niData.hWnd = FrameWnd; + + // set the message to send + // note: the message value should be in the + // range of WM_APP through 0xBFFF + + niData.uCallbackMessage = MY_TRAY_ICON_MESSAGE; + + // Call Shell_NotifyIcon. NIM_ADD adds a new tray icon + + if (Shell_NotifyIcon(NIM_ADD,&niData)) + Debugprintf("BPQ32 Create Tray Icon Ok"); +// else +// Debugprintf("BPQ32 Create Tray Icon failed %d", GetLastError()); + + return 0; +} + +VOID SaveConfig() +{ + HKEY hKey=0; + int retCode, disp; + + retCode = RegCreateKeyEx(REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, // Reserved + 0, // Class + 0, // Options + KEY_ALL_ACCESS, + NULL, // Security Attrs + &hKey, + &disp); + + if (retCode == ERROR_SUCCESS) + { + retCode = RegSetValueEx(hKey, "Start Minimized", 0, REG_DWORD, (UCHAR *)&StartMinimized, 4); + retCode = RegSetValueEx(hKey, "Minimize to Tray", 0, REG_DWORD, (UCHAR *)&MinimizetoTray, 4); + } +} + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + POINT pos; + HWND handle; + RECT cRect; + + switch (message) + { + case WM_MDIACTIVATE: + + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + // GetSubMenu function should retrieve a handle to the drop-down menu or submenu. + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)hConsMenu, "Actions"); + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); + } + else + { + // Deactivate + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + + DrawMenuBar(FrameWnd); + + return TRUE; //DefMDIChildProc(hWnd, message, wParam, lParam); + + case MY_TRAY_ICON_MESSAGE: + + switch(lParam) + { + case WM_RBUTTONUP: + case WM_LBUTTONUP: + + GetCursorPos(&pos); + + SetForegroundWindow(hWnd); + + TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, hWnd, 0); + return 0; + } + + break; + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + if (wmId == IDC_ENIGATE) + { + int retCode, disp; + HKEY hKey=0; + + IGateEnabled = IsDlgButtonChecked(hWnd, IDC_ENIGATE); + + if (IGateEnabled) + ISDelayTimer = 60; + + retCode = RegCreateKeyEx(REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, // Reserved + 0, // Class + 0, // Options + KEY_ALL_ACCESS, + NULL, // Security Attrs + &hKey, + &disp); + + if (retCode == ERROR_SUCCESS) + { + retCode = RegSetValueEx(hKey,"IGateEnabled", 0 , REG_DWORD,(BYTE *)&IGateEnabled, 4); + RegCloseKey(hKey); + } + + return 0; + } + + if (wmId == BPQSAVENODES) + { + SaveNodes(); + WritetoConsole("Nodes Saved\n"); + return 0; + } + if (wmId == BPQCLEARRECONFIG) + { + if (!ProcessConfig()) + { + MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); + return (0); + } + + ClearNodes(); + WritetoConsole("Nodes file Cleared\n"); + ReconfigFlag=TRUE; + WritetoConsole("Reconfig requested ... Waiting for Timer Poll\n"); + return 0; + } + if (wmId == BPQRECONFIG) + { + if (!ProcessConfig()) + { + MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); + return (0); + } + SaveNodes(); + WritetoConsole("Nodes Saved\n"); + ReconfigFlag=TRUE; + WritetoConsole("Reconfig requested ... Waiting for Timer Poll\n"); + return 0; + } + + if (wmId == SCANRECONFIG) + { + if (!ProcessConfig()) + { + MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); + return (0); + } + + RigReconfigFlag = TRUE; + WritetoConsole("Rigcontrol Reconfig requested ... Waiting for Timer Poll\n"); + return 0; + } + + if (wmId == APRSRECONFIG) + { + if (!ProcessConfig()) + { + MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); + return (0); + } + + APRSReconfigFlag=TRUE; + WritetoConsole("APRS Reconfig requested ... Waiting for Timer Poll\n"); + return 0; + } + if (wmId == BPQDUMP) + { + DumpSystem(); + return 0; + } + + if (wmId == BPQCLOSEALL) + { + CloseAllPrograms(); + return 0; + } + + if (wmId == BPQUICONFIG) + { + int err, i=0; + char Title[80]; + WNDCLASS wc; + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = UIWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL; + wc.lpszClassName = UIClassName; + + RegisterClass(&wc); + + UIhWnd = CreateDialog(hInstance, UIClassName,0,NULL); + + if (!UIhWnd) + { + err=GetLastError(); + return FALSE; + } + + wsprintf(Title,"BPQ32 Beacon Utility Version"); + MySetWindowText(UIhWnd, Title); + return 0; + } + + if (wmId == BPQSAVEREG) + { + CreateRegBackup(); + return 0; + } + + if (wmId == BPQMINTOTRAY) + { + MinimizetoTray = !MinimizetoTray; + + if (MinimizetoTray) + CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_CHECKED); + else + CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_UNCHECKED); + + SaveConfig(); + return 0; + } + + if (wmId == BPQSTARTMIN) + { + StartMinimized = !StartMinimized; + + if (StartMinimized) + CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_CHECKED); + else + CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_UNCHECKED); + + SaveConfig(); + return 0; + } + + if (wmId >= TRAYBASEID && wmId < (TRAYBASEID + 100)) + { + handle=hWndArray[wmId-TRAYBASEID]; + + if (handle == FrameWnd && FrameMaximized == TRUE) + PostMessage(handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0); + else + PostMessage(handle, WM_SYSCOMMAND, SC_RESTORE, 0); + + SetForegroundWindow(handle); + return 0; + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_MINIMIZE: + + ConsoleMinimized = TRUE; + break; + + case SC_RESTORE: + + ConsoleMinimized = FALSE; + SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); + + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + + case WM_SIZE: + + GetClientRect(hWnd, &cRect); + + MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); + + if (APRSActive) + MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); + else + MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); + +// InvalidateRect(hWnd, NULL, TRUE); + break; + +/* + case WM_PAINT: + + hdc = BeginPaint (hWnd, &ps); + + hOldFont = SelectObject( hdc, hFont) ; + + for (i=0; i 300) + len = 300; + + memcpy(&buffptr[2], buff, len + 1); + + C_Q_ADD(&WritetoConsoleQ, buffptr); + + return 0; +} + +int WritetoConsoleSupport(char * buff) +{ + + int len=strlen(buff); + char Temp[2000]= ""; + char * ptr; + + if (PartLine) + { + SendMessage(hWndCons, LB_GETTEXT, pindex, (LPARAM)(LPCTSTR) Temp); + SendMessage(hWndCons, LB_DELETESTRING, pindex, 0); + PartLine = FALSE; + } + + if ((strlen(Temp) + strlen(buff)) > 1990) + Temp[0] = 0; // Should never have anything this long + + strcat(Temp, buff); + + ptr = strchr(Temp, '\n'); + + if (ptr) + *ptr = 0; + else + PartLine = TRUE; + + pindex=SendMessage(hWndCons, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR) Temp); + return 0; + } + +DllExport VOID APIENTRY BPQOutputDebugString(char * String) +{ + OutputDebugString(String); + return; + } + +HANDLE handle; +char fn[]="BPQDUMP"; +ULONG cnt; +char * stack; +//char screen[1920]; +//COORD ReadCoord; + +#define DATABYTES 400000 + +extern UCHAR DATAAREA[]; + +DllExport int APIENTRY DumpSystem() +{ + char fn[200]; + char Msg[250]; + + sprintf(fn,"%s\\BPQDUMP",BPQDirectory); + + handle = CreateFile(fn, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + +#ifndef _WIN64 + + _asm { + + mov stack,esp + } + + WriteFile(handle,stack,128,&cnt,NULL); +#endif + +// WriteFile(handle,Screen,MAXLINELEN*MAXSCREENLEN,&cnt,NULL); + + WriteFile(handle,DATAAREA, DATABYTES,&cnt,NULL); + + CloseHandle(handle); + + sprintf(Msg, "Dump to %s Completed\n", fn); + WritetoConsole(Msg); + + FindLostBuffers(); + + return (0); +} + +BOOLEAN CheckifBPQ32isLoaded() +{ + HANDLE Mutex; + + // See if BPQ32 is running - if we create it in the NTVDM address space by + // loading bpq32.dll it will not work. + + Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"BPQLOCKMUTEX"); + + if (Mutex == NULL) + { + if (AttachingProcess == 0) // Already starting BPQ32 + { + OutputDebugString("BPQ32 No other bpq32 programs running - Loading BPQ32.exe\n"); + StartBPQ32(); + } + return FALSE; + } + + CloseHandle(Mutex); + + return TRUE; +} + +BOOLEAN StartBPQ32() +{ + UCHAR Value[100]; + + char bpq[]="BPQ32.exe"; + char *fn=(char *)&bpq; + HKEY hKey=0; + int ret,Type,Vallen=99; + + char Errbuff[100]; + char buff[20]; + + STARTUPINFO StartupInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION ProcessInformation; // pointer to PROCESS_INFORMATION + + AttachingProcess = 1; + +// Get address of BPQ Directory + + Value[0]=0; + + ret = RegOpenKeyEx (REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_QUERY_VALUE, + &hKey); + + if (ret == ERROR_SUCCESS) + { + ret = RegQueryValueEx(hKey, "BPQ Program Directory", 0, &Type,(UCHAR *)&Value, &Vallen); + + if (ret == ERROR_SUCCESS) + { + if (strlen(Value) == 2 && Value[0] == '"' && Value[1] == '"') + Value[0]=0; + } + + + if (Value[0] == 0) + { + + // BPQ Directory absent or = "" - "try Config File Location" + + ret = RegQueryValueEx(hKey,"BPQ Directory",0, + &Type,(UCHAR *)&Value,&Vallen); + + if (ret == ERROR_SUCCESS) + { + if (strlen(Value) == 2 && Value[0] == '"' && Value[1] == '"') + Value[0]=0; + } + + } + RegCloseKey(hKey); + } + + if (Value[0] == 0) + { + strcpy(Value,fn); + } + else + { + strcat(Value,"\\"); + strcat(Value,fn); + } + + StartupInfo.cb=sizeof(StartupInfo); + StartupInfo.lpReserved=NULL; + StartupInfo.lpDesktop=NULL; + StartupInfo.lpTitle=NULL; + StartupInfo.dwFlags=0; + StartupInfo.cbReserved2=0; + StartupInfo.lpReserved2=NULL; + + if (!CreateProcess(Value,NULL,NULL,NULL,FALSE, + CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP, + NULL,NULL,&StartupInfo,&ProcessInformation)) + { + ret=GetLastError(); + + _itoa(ret,buff,10); + + strcpy(Errbuff, "BPQ32 Load "); + strcat(Errbuff,Value); + strcat(Errbuff," failed "); + strcat(Errbuff,buff); + OutputDebugString(Errbuff); + AttachingProcess = 0; + return FALSE; + } + + return TRUE; +} + + +DllExport BPQVECSTRUC * APIENTRY GetIPVectorAddr() +{ + return &IPHOSTVECTOR; +} + +DllExport UINT APIENTRY GETSENDNETFRAMEADDR() +{ + return (UINT)&SENDNETFRAME; +} + +DllExport VOID APIENTRY RelBuff(VOID * Msg) +{ + UINT * pointer, * BUFF = Msg; + + if (Semaphore.Flag == 0) + Debugprintf("ReleaseBuffer called without semaphore"); + + pointer = FREE_Q; + + *BUFF =(UINT)pointer; + + FREE_Q = BUFF; + + QCOUNT++; + + return; +} + +extern int MINBUFFCOUNT; + +DllExport VOID * APIENTRY GetBuff() +{ + UINT * Temp = Q_REM(&FREE_Q); + + if (Semaphore.Flag == 0) + Debugprintf("GetBuff called without semaphore"); + + if (Temp) + { + QCOUNT--; + + if (QCOUNT < MINBUFFCOUNT) + MINBUFFCOUNT = QCOUNT; + } + + return Temp; +} + + +VOID __cdecl Debugprintf(const char * format, ...) +{ + char Mess[10000]; + va_list(arglist); + + va_start(arglist, format); + vsprintf(Mess, format, arglist); + strcat(Mess, "\r\n"); + OutputDebugString(Mess); + + return; +} + +unsigned short int compute_crc(unsigned char *buf, int txlen); + +extern SOCKADDR_IN reportdest; + +extern SOCKET ReportSocket; + +extern SOCKADDR_IN Chatreportdest; + +DllExport VOID APIENTRY SendChatReport(SOCKET ChatReportSocket, char * buff, int txlen) +{ + unsigned short int crc = compute_crc(buff, txlen); + + crc ^= 0xffff; + + buff[txlen++] = (crc&0xff); + buff[txlen++] = (crc>>8); + + sendto(ChatReportSocket, buff, txlen, 0, (LPSOCKADDR)&Chatreportdest, sizeof(Chatreportdest)); +} + +VOID CreateRegBackup() +{ + char Backup1[MAX_PATH]; + char Backup2[MAX_PATH]; + char RegFileName[MAX_PATH]; + char Msg[80]; + HANDLE handle; + int len, written; + char RegLine[300]; + +// SHELLEXECUTEINFO sei; +// STARTUPINFO SInfo; +// PROCESS_INFORMATION PInfo; + + sprintf(RegFileName, "%s\\BPQ32.reg", BPQDirectory); + + // Keep 4 Generations + + strcpy(Backup2, RegFileName); + strcat(Backup2, ".bak.3"); + + strcpy(Backup1, RegFileName); + strcat(Backup1, ".bak.2"); + + DeleteFile(Backup2); // Remove old .bak.3 + MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 + + strcpy(Backup2, RegFileName); + strcat(Backup2, ".bak.1"); + + MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 + + strcpy(Backup1, RegFileName); + strcat(Backup1, ".bak"); + + MoveFile(Backup1, Backup2); //Move .bak to .bak.1 + + strcpy(Backup2, RegFileName); + strcat(Backup2, ".bak"); + + CopyFile(RegFileName, Backup2, FALSE); // Copy to .bak + + handle = CreateFile(RegFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (handle == INVALID_HANDLE_VALUE) + { + sprintf(Msg, "Failed to open Registry Save File\n"); + WritetoConsole(Msg); + return; + } + + len = sprintf(RegLine, "Windows Registry Editor Version 5.00\r\n\r\n"); + WriteFile(handle, RegLine, len, &written, NULL); + + if (SaveReg("Software\\G8BPQ\\BPQ32", handle)) + WritetoConsole("Registry Save complete\n"); + else + WritetoConsole("Registry Save failed\n"); + + CloseHandle(handle); + return ; +/* + + if (REGTREE == HKEY_LOCAL_MACHINE) // < Vista + { + sprintf(cmd, + "regedit /E \"%s\\BPQ32.reg\" %s\\Software\\G8BPQ\\BPQ32", BPQDirectory, REGTREETEXT); + + ZeroMemory(&SInfo, sizeof(SInfo)); + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + if (CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0 ,NULL, NULL, &SInfo, &PInfo) == 0) + { + sprintf(Msg, "Error: CreateProcess for regedit failed 0%d\n", GetLastError() ); + WritetoConsole(Msg); + return; + } + } + else + { + + sprintf(cmd, + "/E \"%s\\BPQ32.reg\" %s\\Software\\G8BPQ\\BPQ32", BPQDirectory, REGTREETEXT); + + ZeroMemory(&sei, sizeof(sei)); + + sei.cbSize = sizeof(SHELLEXECUTEINFOW); + sei.hwnd = hWnd; + sei.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI; + sei.lpVerb = "runas"; + sei.lpFile = "regedit.exe"; + sei.lpParameters = cmd; + sei.nShow = SW_SHOWNORMAL; + + if (!ShellExecuteEx(&sei)) + { + sprintf(Msg, "Error: ShellExecuteEx for regedit failed %d\n", GetLastError() ); + WritetoConsole(Msg); + return; + } + } + + sprintf(Msg, "Registry Save Initiated\n", fn); + WritetoConsole(Msg); + + return ; +*/ +} + +BOOL CALLBACK EnumForCloseProc(HWND hwnd, LPARAM lParam) +{ + struct TNCINFO * TNC = (struct TNCINFO *)lParam; + UINT ProcessId; + + GetWindowThreadProcessId(hwnd, &ProcessId); + + for (i=0; i< AttachedProcesses; i++) + { + if (AttachedPIDList[i] == ProcessId) + { + Debugprintf("BPQ32 Close All Closing PID %d", ProcessId); + PostMessage(hwnd, WM_CLOSE, 1, 1); + // AttachedPIDList[i] = 0; // So we don't do it again + break; + } + } + + return (TRUE); +} +DllExport BOOL APIENTRY RestoreFrameWindow() +{ + return ShowWindow(FrameWnd, SW_RESTORE); +} + +DllExport VOID APIENTRY CreateNewTrayIcon() +{ + Shell_NotifyIcon(NIM_DELETE,&niData); + trayMenu = NULL; +} + +DllExport VOID APIENTRY CloseAllPrograms() +{ +// HANDLE hProc; + + // Close all attached BPQ32 programs + + Closing = TRUE; + + ShowWindow(FrameWnd, SW_RESTORE); + + GetWindowRect(FrameWnd, &FRect); + + SaveBPQ32Windows(); + CloseHostSessions(); + + if (AttachedProcesses == 1) + CloseBPQ32(); + + Debugprintf("BPQ32 Close All Processes %d PIDS %d %d %d %d", AttachedProcesses, AttachedPIDList[0], + AttachedPIDList[1], AttachedPIDList[2], AttachedPIDList[3]); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + EnumWindows(EnumForCloseProc, (LPARAM)NULL); +} + +#define MAX_KEY_LENGTH 255 +#define MAX_VALUE_NAME 16383 +#define MAX_VALUE_DATA 65536 + +BOOL CopyReg(HKEY hKeyIn, HKEY hKeyOut) +{ + TCHAR achKey[MAX_KEY_LENGTH]; // buffer for subkey name + DWORD cbName; // size of name string + TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name + DWORD cchClassName = MAX_PATH; // size of class string + DWORD cSubKeys=0; // number of subkeys + DWORD cbMaxSubKey; // longest subkey size + DWORD cchMaxClass; // longest class string + DWORD cValues; // number of values for key + DWORD cchMaxValue; // longest value name + DWORD cbMaxValueData; // longest value data + DWORD cbSecurityDescriptor; // size of security descriptor + FILETIME ftLastWriteTime; // last write time + + DWORD i, retCode; + + TCHAR achValue[MAX_VALUE_NAME]; + DWORD cchValue = MAX_VALUE_NAME; + + // Get the class name and the value count. + retCode = RegQueryInfoKey( + hKeyIn, // key handle + achClass, // buffer for class name + &cchClassName, // size of class string + NULL, // reserved + &cSubKeys, // number of subkeys + &cbMaxSubKey, // longest subkey size + &cchMaxClass, // longest class string + &cValues, // number of values for this key + &cchMaxValue, // longest value name + &cbMaxValueData, // longest value data + &cbSecurityDescriptor, // security descriptor + &ftLastWriteTime); // last write time + + // Enumerate the subkeys, until RegEnumKeyEx fails. + + if (cSubKeys) + { + Debugprintf( "\nNumber of subkeys: %d\n", cSubKeys); + + for (i=0; i 76) + { + len = sprintf(RegLine, "%s\\\r\n", RegLine); + WriteFile(hFile, RegLine, len, &written, NULL); + strcpy(RegLine, " "); + len = 2; + } + + len = sprintf(RegLine, "%s%02x,", RegLine, Value[k]); + } + RegLine[--len] = 0x0d; + RegLine[++len] = 0x0a; + len++; + + break; + + case REG_DWORD: //( 4 ) // 32-bit number +// case REG_DWORD_LITTLE_ENDIAN: //( 4 ) // 32-bit number (same as REG_DWORD) + + memcpy(&Intval, Value, 4); + len = sprintf(RegLine, "\"%s\"=dword:%08x\r\n", achValue, Intval); + break; + + case REG_DWORD_BIG_ENDIAN: //( 5 ) // 32-bit number + break; + case REG_LINK: //( 6 ) // Symbolic Link (unicode) + break; + case REG_MULTI_SZ: //( 7 ) // Multiple Unicode strings + + len = sprintf(RegLine, "\"%s\"=hex(7):%02x,00,", achValue, Value[0]); + for (k = 1; k < ValLen; k++) + { + if (len > 76) + { + len = sprintf(RegLine, "%s\\\r\n", RegLine); + WriteFile(hFile, RegLine, len, &written, NULL); + strcpy(RegLine, " "); + len = 2; + } + len = sprintf(RegLine, "%s%02x,", RegLine, Value[k]); + if (len > 76) + { + len = sprintf(RegLine, "%s\\\r\n", RegLine); + WriteFile(hFile, RegLine, len, &written, NULL); + strcpy(RegLine, " "); + } + len = sprintf(RegLine, "%s00,", RegLine); + } + + RegLine[--len] = 0x0d; + RegLine[++len] = 0x0a; + len++; + break; + + case REG_RESOURCE_LIST: //( 8 ) // Resource list in the resource map + break; + case REG_FULL_RESOURCE_DESCRIPTOR: //( 9 ) // Resource list in the hardware description + break; + case REG_RESOURCE_REQUIREMENTS_LIST://( 10 ) + break; + case REG_QWORD: //( 11 ) // 64-bit number +// case REG_QWORD_LITTLE_ENDIAN: //( 11 ) // 64-bit number (same as REG_QWORD) + break; + + } + + WriteFile(hFile, RegLine, len, &written, NULL); + } + } + } + + WriteFile(hFile, "\r\n", 2, &written, NULL); + + // Enumerate the subkeys, until RegEnumKeyEx fails. + + if (cSubKeys) + { + for (i=0; i> 1; + } + + Flags=GetApplFlags(i); + + if (OneBits > 1) + sprintf(&NewScreen[(i+1)*54],"%2d%s%3d %3d %3d %03x %3x %10s%-20s", + i, flag, RXCount(i), TXCount(i), MONCount(i), Mask, Flags, callsign, + BPQHOSTVECTOR[i-1].PgmName); + else + sprintf(&NewScreen[(i+1)*54],"%2d%s%3d %3d %3d %3d %3x %10s%-20s", + i, flag, RXCount(i), TXCount(i), MONCount(i), AppNumber, Flags, callsign, + BPQHOSTVECTOR[i-1].PgmName); + + } + } + + #include "StdExcept.c" + + if (Semaphore.Flag && Semaphore.SemProcessID == GetCurrentProcessId()) + FreeSemaphore(&Semaphore); + + } + + if (memcmp(Screen, NewScreen, 33 * 108) == 0) // No Change + return 0; + + memcpy(Screen, NewScreen, 33 * 108); + InvalidateRect(StatusWnd,NULL,FALSE); + + return(0); +} + +LRESULT CALLBACK StatusWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + PAINTSTRUCT ps; + HDC hdc; + HFONT hOldFont ; + HGLOBAL hMem; + MINMAXINFO * mmi; + int i; + + switch (message) + { + case WM_TIMER: + + if (Semaphore.Flag == 0) + DoStatus(); + break; + + case WM_MDIACTIVATE: + + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)hConsMenu, "Actions"); + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); + } + else + { + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + + DrawMenuBar(FrameWnd); + + return TRUE; //DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_GETMINMAXINFO: + + mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = 850; + mmi->ptMaxSize.y = 500; + mmi->ptMaxTrackSize.x = 850; + mmi->ptMaxTrackSize.y = 500; + + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + //Parse the menu selections: + + switch (wmId) + { + +/* + case BPQSTREAMS: + + CheckMenuItem(hMenu,BPQSTREAMS,MF_CHECKED); + CheckMenuItem(hMenu,BPQIPSTATUS,MF_UNCHECKED); + + StreamDisplay = TRUE; + + break; + + case BPQIPSTATUS: + + CheckMenuItem(hMenu,BPQSTREAMS,MF_UNCHECKED); + CheckMenuItem(hMenu,BPQIPSTATUS,MF_CHECKED); + + StreamDisplay = FALSE; + memset(Screen, ' ', 4000); + + + break; + +*/ + + case BPQCOPY: + + // + // Copy buffer to clipboard + // + hMem=GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, 33*110); + + if (hMem != 0) + { + if (OpenClipboard(hWnd)) + { +// CopyScreentoBuffer(GlobalLock(hMem)); + GlobalUnlock(hMem); + EmptyClipboard(); + SetClipboardData(CF_TEXT,hMem); + CloseClipboard(); + } + else + { + GlobalFree(hMem); + } + + } + + break; + + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_MAXIMIZE: + + break; + + case SC_MINIMIZE: + + StatusMinimized = TRUE; + break; + + case SC_RESTORE: + + StatusMinimized = FALSE; + SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_PAINT: + + hdc = BeginPaint (hWnd, &ps); + + hOldFont = SelectObject( hdc, hFont) ; + + for (i=0; i<33; i++) + { + TextOut(hdc,0,i*14,&Screen[i*108],108); + } + + SelectObject( hdc, hOldFont ) ; + EndPaint (hWnd, &ps); + + break; + + case WM_DESTROY: + +// PostQuitMessage(0); + + break; + + + default: + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + } + return (0); +} + +VOID SaveMDIWindowPos(HWND hWnd, char * RegKey, char * Value, BOOL Minimized) +{ + HKEY hKey=0; + char Size[80]; + char Key[80]; + int retCode, disp; + RECT Rect; + + if (IsWindow(hWnd) == FALSE) + return; + + ShowWindow(hWnd, SW_RESTORE); + + if (GetWindowRect(hWnd, &Rect) == FALSE) + return; + + // Make relative to Frame + + Rect.top -= FRect.top ; + Rect.left -= FRect.left; + Rect.bottom -= FRect.top; + Rect.right -= FRect.left; + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\%s", RegKey); + + retCode = RegCreateKeyEx(REGTREE, Key, 0, 0, 0, + KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + sprintf(Size,"%d,%d,%d,%d,%d", Rect.left, Rect.right, Rect.top ,Rect.bottom, Minimized); + retCode = RegSetValueEx(hKey, Value, 0, REG_SZ,(BYTE *)&Size, strlen(Size)); + RegCloseKey(hKey); + } +} + +extern int GPSPort; +extern char LAT[]; // in standard APRS Format +extern char LON[]; // in standard APRS Format + +VOID SaveBPQ32Windows() +{ + HKEY hKey=0; + char Size[80]; + int retCode, disp; + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + int i; + + retCode = RegCreateKeyEx(REGTREE, "SOFTWARE\\G8BPQ\\BPQ32", 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + sprintf(Size,"%d,%d,%d,%d", FRect.left, FRect.right, FRect.top, FRect.bottom); + retCode = RegSetValueEx(hKey, "FrameWindowSize", 0, REG_SZ, (BYTE *)&Size, strlen(Size)); + + // Save GPS Position + + if (GPSPort) + { + sprintf(Size, "%s, %s", LAT, LON); + retCode = RegSetValueEx(hKey, "GPS", 0, REG_SZ,(BYTE *)&Size, strlen(Size)); + } + + RegCloseKey(hKey); + } + + SaveMDIWindowPos(StatusWnd, "", "StatusWindowSize", StatusMinimized); + SaveMDIWindowPos(hConsWnd, "", "WindowSize", ConsoleMinimized); + + for (i=0; iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR) + { + SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + SaveAXIPWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + } + } + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + SaveWindowPos(70); // Rigcontrol + + + if (hIPResWnd) + SaveMDIWindowPos(hIPResWnd, "", "IPResSize", IPMinimized); + + SaveHostSessions(); +} + +DllExport BOOL APIENTRY CheckIfOwner() +{ + // + // Returns TRUE if current process is root process + // that loaded the DLL + // + + if (TimerInst == GetCurrentProcessId()) + + return (TRUE); + else + return (FALSE); +} + +VOID GetParam(char * input, char * key, char * value) +{ + char * ptr = strstr(input, key); + char Param[2048]; + char * ptr1, * ptr2; + char c; + + + if (ptr) + { + ptr2 = strchr(ptr, '&'); + if (ptr2) *ptr2 = 0; + strcpy(Param, ptr + strlen(key)); + if (ptr2) *ptr2 = '&'; // Restore string + + // Undo any % transparency + + ptr1 = Param; + ptr2 = Param; + + c = *(ptr1++); + + while (c) + { + if (c == '%') + { + int n; + int m = *(ptr1++) - '0'; + if (m > 9) m = m - 7; + n = *(ptr1++) - '0'; + if (n > 9) n = n - 7; + + *(ptr2++) = m * 16 + n; + } + else if (c == '+') + *(ptr2++) = ' '; + else + *(ptr2++) = c; + + c = *(ptr1++); + } + + *(ptr2++) = 0; + + strcpy(value, Param); + } +} + +int GetListeningPortsPID(int Port) +{ + MIB_TCPTABLE_OWNER_PID * TcpTable = NULL; + PMIB_TCPROW_OWNER_PID Row; + int dwSize = 0; + DWORD n; + + // Get PID of process for this TCP Port + + // Get Length of table + + GetExtendedTcpTable(TcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_LISTENER, 0); + + TcpTable = malloc(dwSize); + + if (TcpTable == NULL) + return 0; + + GetExtendedTcpTable(TcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_LISTENER, 0); + + for (n = 0; n < TcpTable->dwNumEntries; n++) + { + Row = &TcpTable->table[n]; + + if (Row->dwLocalPort == Port && Row->dwState == MIB_TCP_STATE_LISTEN) + { + return Row->dwOwningPid; + break; + } + } + return 0; // Not found +} + +DllExport char * APIENTRY GetLOC() +{ + return LOC; +} + +DllExport void APIENTRY GetLatLon(double * lat, double * lon) +{ + *lat = LatFromLOC; + *lon = LonFromLOC; + return; +} + + +// UZ7HO Dll PTT interface + +// 1 ext_PTT_info +// 2 ext_PTT_settings +// 3 ext_PTT_OFF +// 4 ext_PTT_ON +// 5 ext_PTT_close +// 6 ext_PTT_open + +extern struct RIGINFO * DLLRIG; // Rig record for dll PTT interface (currently only for UZ7HO); + +VOID Rig_PTT(struct TNCINFO * TNC, BOOL PTTState); +VOID Rig_PTTEx(struct RIGINFO * RIG, BOOL PTTState, struct TNCINFO * TNC); + +int WINAPI ext_PTT_info() +{ + return 0; +} + +int WINAPI ext_PTT_settings() +{ + return 0; +} + +int WINAPI ext_PTT_OFF(int Port) +{ + if (DLLRIG) + Rig_PTTEx(DLLRIG, 0, 0); + + return 0; +} + +int WINAPI ext_PTT_ON(int Port) +{ + if (DLLRIG) + Rig_PTTEx(DLLRIG, 1, 0); + + return 0; +} +int WINAPI ext_PTT_close() +{ + if (DLLRIG) + Rig_PTTEx(DLLRIG, 0, 0); + + return 0; +} + +DllExport INT WINAPI ext_PTT_open() +{ + return 1; +} + +char * stristr (char *ch1, char *ch2) +{ + char *chN1, *chN2; + char *chNdx; + char *chRet = NULL; + + chN1 = _strdup(ch1); + chN2 = _strdup(ch2); + + if (chN1 && chN2) + { + chNdx = chN1; + while (*chNdx) + { + *chNdx = (char) tolower(*chNdx); + chNdx ++; + } + chNdx = chN2; + + while (*chNdx) + { + *chNdx = (char) tolower(*chNdx); + chNdx ++; + } + + chNdx = strstr(chN1, chN2); + + if (chNdx) + chRet = ch1 + (chNdx - chN1); + } + + free (chN1); + free (chN2); + return chRet; +} + diff --git a/.svn/pristine/0c/0c8d0dde1866e8f5b2214eab03930e5891967d4f.svn-base b/.svn/pristine/0c/0c8d0dde1866e8f5b2214eab03930e5891967d4f.svn-base index 07cfa13..a06d3b6 100644 --- a/.svn/pristine/0c/0c8d0dde1866e8f5b2214eab03930e5891967d4f.svn-base +++ b/.svn/pristine/0c/0c8d0dde1866e8f5b2214eab03930e5891967d4f.svn-base @@ -1,424 +1,424 @@ -// Mail and Chat Server for BPQ32 Packet Switch -// -// Monitor Window(s) Module - -#include "bpqmail.h" - -static char ClassName[]="BPQMONWINDOW"; - -char SYSOPCall[50]; - -static WNDPROC wpOrigInputProc; -static WNDPROC wpOrigOutputProc; - -HWND hMonitor; - -static HWND hwndInput; -static HWND hwndOutput; - -static HMENU hMenu; // handle of menu - - -#define InputBoxHeight 25 -RECT MonitorRect; -RECT OutputRect; - -int Height, Width, LastY; - -static char kbbuf[160]; -static int kbptr=0; - -static char * readbuff; -static int readbufflen; - -static BOOL StripLF = TRUE; -BOOL MonBBS = TRUE; -BOOL MonCHAT = TRUE; -BOOL MonTCP = TRUE; - -BOOL LogBBS = TRUE; -BOOL LogCHAT = TRUE; -BOOL LogTCP = TRUE; - - -static int PartLinePtr=0; -static int PartLineIndex=0; // Listbox index of (last) incomplete line - - -static LRESULT CALLBACK MonWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); -static LRESULT APIENTRY InputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; -static LRESULT APIENTRY OutputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; -static LRESULT APIENTRY MonProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; -static void MoveWindows(); -static void MoveMCWindows(); - -#define BGCOLOUR RGB(236,233,216) - -BOOL CreateMonitor() -{ - WNDCLASS wc; - HBRUSH bgBrush; - - if (hMonitor) - { - ShowWindow(hMonitor, SW_SHOWNORMAL); - SetForegroundWindow(hMonitor); - return FALSE; // Alreaqy open - } - - bgBrush = CreateSolidBrush(BGCOLOUR); - - wc.style = CS_HREDRAW | CS_VREDRAW; - wc.lpfnWndProc = MonWndProc; - - wc.cbClsExtra = 0; - wc.cbWndExtra = DLGWINDOWEXTRA; - wc.hInstance = hInst; - wc.hIcon = LoadIcon( hInst, MAKEINTRESOURCE(BPQICON) ); - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.hbrBackground = bgBrush; - - wc.lpszMenuName = NULL; - wc.lpszClassName = ClassName; - - RegisterClass(&wc); - - hMonitor=CreateDialog(hInst,ClassName,0,NULL); - - if (!hMonitor) - return (FALSE); - - readbuff = zalloc(1000); - readbufflen = 1000; - - hMenu=GetMenu(hMonitor); - - CheckMenuItem(hMenu,MONBBS, MonBBS ? MF_CHECKED : MF_UNCHECKED); - CheckMenuItem(hMenu,MONCHAT, MonCHAT ? MF_CHECKED : MF_UNCHECKED); - CheckMenuItem(hMenu,MONTCP, MonTCP ? MF_CHECKED : MF_UNCHECKED); - - DrawMenuBar(hWnd); - - // Retrieve the handlse to the edit controls. - - hwndOutput = GetDlgItem(hMonitor, 121); - - // Set our own WndProcs for the controls. - - wpOrigOutputProc = (WNDPROC)SetWindowLong(hwndOutput, GWL_WNDPROC, (LONG)OutputProc); - - if (cfgMinToTray) - AddTrayMenuItem(hMonitor, "Mail Monitor"); - - ShowWindow(hMonitor, SW_SHOWNORMAL); - - if (MonitorRect.right < 100 || MonitorRect.bottom < 100) - { - GetWindowRect(hMonitor, &MonitorRect); - } - - MoveWindow(hMonitor,MonitorRect.left,MonitorRect.top, MonitorRect.right-MonitorRect.left, MonitorRect.bottom-MonitorRect.top, TRUE); - - MoveWindows(); - - return TRUE; - -} - -static void MoveWindows() -{ - RECT rcMain, rcClient; - int ClientHeight, ClientWidth; - - GetWindowRect(hMonitor, &rcMain); - GetClientRect(hMonitor, &rcClient); - - ClientHeight = rcClient.bottom; - ClientWidth = rcClient.right; - -// MoveWindow(hwndMon,2, 0, ClientWidth-4, SplitPos, TRUE); - MoveWindow(hwndOutput,2, 2, ClientWidth-4, ClientHeight-4, TRUE); -// MoveWindow(hwndSplit,0, SplitPos, ClientWidth, SplitBarHeight, TRUE); -} - -static LRESULT CALLBACK MonWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - int wmId, wmEvent; - LPRECT lprc; - - switch (message) - { - - case WM_ACTIVATE: - - SetFocus(hwndInput); - break; - - case WM_CLOSE: - if (wParam) // Used by Close All Programs. - return 0; - - return (DefWindowProc(hWnd, message, wParam, lParam)); - - case WM_COMMAND: - - wmId = LOWORD(wParam); // Remember, these are... - wmEvent = HIWORD(wParam); // ...different for Win32! - - switch (wmId) { - - case MONBBS: - - ToggleParam(hMenu, hWnd, &MonBBS, MONBBS); - break; - - case MONCHAT: - - ToggleParam(hMenu, hWnd, &MonCHAT, MONCHAT); - break; - - case MONTCP: - - ToggleParam(hMenu, hWnd, &MonTCP, MONTCP); - break; - - - case BPQCLEAROUT: - - SendMessage(hwndOutput,LB_RESETCONTENT, 0, 0); - break; - - case BPQCOPYOUT: - - CopyToClipboard(hwndOutput); - break; - - - - //case BPQHELP: - - // HtmlHelp(hWnd,"BPQTerminal.chm",HH_HELP_FINDER,0); - // break; - - default: - - return 0; - - } - - case WM_SYSCOMMAND: - - wmId = LOWORD(wParam); // Remember, these are... - wmEvent = HIWORD(wParam); // ...different for Win32! - - switch (wmId) { - - case SC_MINIMIZE: - - if (cfgMinToTray) - return ShowWindow(hWnd, SW_HIDE); - - default: - - return (DefWindowProc(hWnd, message, wParam, lParam)); - } - - case WM_SIZING: - - lprc = (LPRECT) lParam; - - Height = lprc->bottom-lprc->top; - Width = lprc->right-lprc->left; - - MoveWindows(); - - return TRUE; - - - case WM_DESTROY: - - // Remove the subclass from the edit control. - - GetWindowRect(hWnd, &MonitorRect); // For save soutine - - SetWindowLong(hwndInput, GWL_WNDPROC, - (LONG) wpOrigInputProc); - - - if (cfgMinToTray) - DeleteTrayMenuItem(hWnd); - - - hMonitor = NULL; - - free(readbuff); - readbufflen = 0; - - break; - - default: - return (DefWindowProc(hWnd, message, wParam, lParam)); - - } - return (0); -} - - - -LRESULT APIENTRY OutputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - - // Trap mouse messages, so we cant select stuff in output and mon windows, - // otherwise scrolling doesnt work. - - if (uMsg >= WM_MOUSEFIRST && uMsg <= WM_LBUTTONDBLCLK) - return TRUE; - - return CallWindowProc(wpOrigOutputProc, hwnd, uMsg, wParam, lParam); -} - -int WritetoMonitorWindow(char * Msg, int len) -{ - char * ptr1, * ptr2; - int index; - - if (len+PartLinePtr > readbufflen) - { - readbufflen += len+PartLinePtr; - readbuff = realloc(readbuff, readbufflen); - } - - if (PartLinePtr != 0) - SendMessage(hwndOutput,LB_DELETESTRING,PartLineIndex,(LPARAM)(LPCTSTR) 0 ); - - memcpy(&readbuff[PartLinePtr], Msg, len); - - len=len+PartLinePtr; - - ptr1=&readbuff[0]; - readbuff[len]=0; - - do { - ptr2=memchr(ptr1,7,len); - - if (ptr2) - *(ptr2)=32; - - } while (ptr2); - -lineloop: - -// if (PartLinePtr > 300) -// PartLinePtr = 0; - - if (len > 0) - { - // copy text to control a line at a time - - ptr2=memchr(ptr1,13,len); - - if (ptr2 == 0) - { - // no newline. Move data to start of buffer and Save pointer - - PartLinePtr=len; - memmove(readbuff,ptr1,len); - PartLineIndex=SendMessage(hwndOutput,LB_ADDSTRING,0,(LPARAM)(LPCTSTR) ptr1 ); - SendMessage(hwndOutput,LB_SETCARETINDEX,(WPARAM) PartLineIndex, MAKELPARAM(FALSE, 0)); - - return (0); - - } - - *(ptr2++)=0; - - index=SendMessage(hwndOutput,LB_ADDSTRING,0,(LPARAM)(LPCTSTR) ptr1 ); - - // if (LogOutput) WriteMonitorLine(ptr1, ptr2 - ptr1); - - PartLinePtr=0; - - len-=(ptr2-ptr1); - - ptr1=ptr2; - - if ((len > 0) && StripLF) - { - if (*ptr1 == 0x0a) // Line Feed - { - ptr1++; - len--; - } - } - - if (index > 1200) - - do{ - - index=SendMessage(hwndOutput,LB_DELETESTRING, 0, 0); - - } while (index > 1000); - - SendMessage(hwndOutput,LB_SETCARETINDEX,(WPARAM) index, MAKELPARAM(FALSE, 0)); - - goto lineloop; - } - - - return (0); -} - -static int ToggleParam(HMENU hMenu, HWND hWnd, BOOL * Param, int Item) -{ - *Param = !(*Param); - - CheckMenuItem(hMenu,Item, (*Param) ? MF_CHECKED : MF_UNCHECKED); - - return (0); -} - -static void CopyToClipboard(HWND hWnd) -{ - int i,n, len=0; - HGLOBAL hMem; - char * ptr; - // - // Copy List Box to clipboard - // - - n = SendMessage(hWnd, LB_GETCOUNT, 0, 0); - - for (i=0; ibottom-lprc->top; + Width = lprc->right-lprc->left; + + MoveWindows(); + + return TRUE; + + + case WM_DESTROY: + + // Remove the subclass from the edit control. + + GetWindowRect(hWnd, &MonitorRect); // For save soutine + + SetWindowLong(hwndInput, GWL_WNDPROC, + (LONG) wpOrigInputProc); + + + if (cfgMinToTray) + DeleteTrayMenuItem(hWnd); + + + hMonitor = NULL; + + free(readbuff); + readbufflen = 0; + + break; + + default: + return (DefWindowProc(hWnd, message, wParam, lParam)); + + } + return (0); +} + + + +LRESULT APIENTRY OutputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + + // Trap mouse messages, so we cant select stuff in output and mon windows, + // otherwise scrolling doesnt work. + + if (uMsg >= WM_MOUSEFIRST && uMsg <= WM_LBUTTONDBLCLK) + return TRUE; + + return CallWindowProc(wpOrigOutputProc, hwnd, uMsg, wParam, lParam); +} + +int WritetoMonitorWindow(char * Msg, int len) +{ + char * ptr1, * ptr2; + int index; + + if (len+PartLinePtr > readbufflen) + { + readbufflen += len+PartLinePtr; + readbuff = realloc(readbuff, readbufflen); + } + + if (PartLinePtr != 0) + SendMessage(hwndOutput,LB_DELETESTRING,PartLineIndex,(LPARAM)(LPCTSTR) 0 ); + + memcpy(&readbuff[PartLinePtr], Msg, len); + + len=len+PartLinePtr; + + ptr1=&readbuff[0]; + readbuff[len]=0; + + do { + ptr2=memchr(ptr1,7,len); + + if (ptr2) + *(ptr2)=32; + + } while (ptr2); + +lineloop: + +// if (PartLinePtr > 300) +// PartLinePtr = 0; + + if (len > 0) + { + // copy text to control a line at a time + + ptr2=memchr(ptr1,13,len); + + if (ptr2 == 0) + { + // no newline. Move data to start of buffer and Save pointer + + PartLinePtr=len; + memmove(readbuff,ptr1,len); + PartLineIndex=SendMessage(hwndOutput,LB_ADDSTRING,0,(LPARAM)(LPCTSTR) ptr1 ); + SendMessage(hwndOutput,LB_SETCARETINDEX,(WPARAM) PartLineIndex, MAKELPARAM(FALSE, 0)); + + return (0); + + } + + *(ptr2++)=0; + + index=SendMessage(hwndOutput,LB_ADDSTRING,0,(LPARAM)(LPCTSTR) ptr1 ); + + // if (LogOutput) WriteMonitorLine(ptr1, ptr2 - ptr1); + + PartLinePtr=0; + + len-=(ptr2-ptr1); + + ptr1=ptr2; + + if ((len > 0) && StripLF) + { + if (*ptr1 == 0x0a) // Line Feed + { + ptr1++; + len--; + } + } + + if (index > 1200) + + do{ + + index=SendMessage(hwndOutput,LB_DELETESTRING, 0, 0); + + } while (index > 1000); + + SendMessage(hwndOutput,LB_SETCARETINDEX,(WPARAM) index, MAKELPARAM(FALSE, 0)); + + goto lineloop; + } + + + return (0); +} + +static int ToggleParam(HMENU hMenu, HWND hWnd, BOOL * Param, int Item) +{ + *Param = !(*Param); + + CheckMenuItem(hMenu,Item, (*Param) ? MF_CHECKED : MF_UNCHECKED); + + return (0); +} + +static void CopyToClipboard(HWND hWnd) +{ + int i,n, len=0; + HGLOBAL hMem; + char * ptr; + // + // Copy List Box to clipboard + // + + n = SendMessage(hWnd, LB_GETCOUNT, 0, 0); + + for (i=0; iTime < Now - 86400) - { - Rec = History; - History = Rec->next; // Remove from front of chain - } - else - Rec = malloc(sizeof (struct HistoryRec)); - - memset(Rec, 0, sizeof (struct HistoryRec)); - - tm = gmtime(&Now); - - if (strlen(text) + strlen(user->name) + strlen(user->call) > 2000) - return 0; // Shouldn't be that long, but protect buffer - - sprintf(Stamp,"%02d:%02d ", tm->tm_hour, tm->tm_min); - sprintf(buf, "%s%-6.6s %s %c %s\r", Stamp, user->call, user->name, ':', text); - - Rec->Time = Now; - - Rec->Topic = _strdup(user->topic->name); - Rec->Message = _strdup(buf); - - if (History == NULL) - History = Rec; - - else - { - ptr = History; - - while (ptr && ptr->next) - { - n++; - ptr = ptr->next; - } - - n++; - ptr->next = Rec; - } - - return n; -} - - - -int ChatIsUTF8(unsigned char *ptr, int len) -{ - int n; - unsigned char * cpt = ptr; - - // This is simpler than the Term version, as it only handles complete lines of text, so cant get split sequences - - cpt--; - - for (n = 0; n < len; n++) - { - cpt++; - - if (*cpt < 128) - continue; - - if ((*cpt & 0xF8) == 0xF0) - { // start of 4-byte sequence - if (((*(cpt + 1) & 0xC0) == 0x80) - && ((*(cpt + 2) & 0xC0) == 0x80) - && ((*(cpt + 3) & 0xC0) == 0x80)) - { - cpt += 3; - n += 3; - continue; - } - return FALSE; - } - else if ((*cpt & 0xF0) == 0xE0) - { // start of 3-byte sequence - if (((*(cpt + 1) & 0xC0) == 0x80) - && ((*(cpt + 2) & 0xC0) == 0x80)) - { - cpt += 2; - n += 2; - continue; - } - return FALSE; - } - else if ((*cpt & 0xE0) == 0xC0) - { // start of 2-byte sequence - if ((*(cpt + 1) & 0xC0) == 0x80) - { - cpt++; - n++; - continue; - } - return FALSE; - } - return FALSE; - } - - return TRUE; -} - -#ifndef LINBPQ - -char * strlop(char * buf, char delim) -{ - // Terminate buf at delim, and return rest of string - - char * ptr = strchr(buf, delim); - - if (ptr == NULL) return NULL; - - *(ptr)++=0; - - return ptr; -} - - -VOID * _zalloc(size_t len) -{ - // ?? malloc and clear - - void * ptr; - - ptr=malloc(len); - memset(ptr, 0, len); - - return ptr; -} - - -VOID * _zalloc_dbg(int len, int type, char * file, int line) -{ - // ?? malloc and clear - - void * ptr; - - ptr=_malloc_dbg(len, type, file, line); - - if (ptr == NULL) - CriticalErrorHandler("malloc failed"); - - memset(ptr, 0, len); - - return ptr; -} - -#endif - -VOID __cdecl nprintf(ChatCIRCUIT * conn, const char * format, ...) - -{ - // seems to be printf to a socket - - char buff[65536]; - va_list(arglist); - - va_start(arglist, format); - vsnprintf(buff, sizeof(buff), format, arglist); - - nputs(conn, buff); -} - - -VOID nputc(ChatCIRCUIT * conn, char chr) -{ - // Seems to send chr to socket - - ChatWriteLogLine(conn, '>',&chr, 1, LOG_CHAT); - ChatQueueMsg(conn, &chr, 1); -} - -VOID nputs(ChatCIRCUIT * conn, char * buf) -{ - // Seems to send buf to socket - - ChatQueueMsg(conn, buf, (int)strlen(buf)); - - if (*buf == 0x1b) - buf += 2; // Colour Escape - - ChatWriteLogLine(conn, '>',buf, (int)strlen(buf), LOG_CHAT); -} - -int ChatQueueMsg(ChatCIRCUIT * conn, char * msg, int len) -{ - // Add Message to queue for this connection - - if (conn->rtcflags & p_linked) - conn->u.link->lastMsgReceived = time(NULL); - - // UCHAR * OutputQueue; // Messages to user - // int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message - // int OutputGetPointer; // Next byte to send. When Getpointer = Queue Length all is sent - free the buffer and start again. - - // Create or extend buffer - - GetSemaphore(&OutputSEM, 0); - - while (conn->OutputQueueLength + len > conn->OutputQueueSize) - { - // Extend Queue - - conn->OutputQueueSize += 4096; - conn->OutputQueue = realloc(conn->OutputQueue, conn->OutputQueueSize); - } - - memcpy(&conn->OutputQueue[conn->OutputQueueLength], msg, len); - conn->OutputQueueLength += len; - - FreeSemaphore(&OutputSEM); - - return len; -} - -VOID ChatSendWelcomeMsg(int Stream, ChatCIRCUIT * conn, struct UserInfo * user) -{ - if (!rtloginu (conn, TRUE)) - { - // Already connected - close - - ChatFlush(conn); - Sleep(1000); - Disconnect(conn->BPQStream); - } - return; - -} - -VOID ChatExpandAndSendMessage(ChatCIRCUIT * conn, char * Msg, int LOG) -{ - char NewMessage[10000]; - char * OldP = Msg; - char * NewP = NewMessage; - char * ptr, * pptr; - int len; - char Dollar[] = "$"; - char CR[] = "\r"; - int Msgs = 0, Unread = 0; - - - ptr = strchr(OldP, '$'); - - while (ptr) - { - len = (int)(ptr - OldP); // Chars before $ - memcpy(NewP, OldP, len); - NewP += len; - - switch (*++ptr) - { - case 'I': // First name of the connected user. - - pptr = conn->UserPointer->Name; - break; - - - case 'U': // Callsign of the connected user. - - pptr = conn->UserPointer->Call; - break; - - case 'W': // Inserts a carriage return. - - pptr = CR; - break; - - break; - - default: - - pptr = Dollar; // Just Copy $ - } - - len = (int)strlen(pptr); - memcpy(NewP, pptr, len); - NewP += len; - - OldP = ++ptr; - ptr = strchr(OldP, '$'); - } - - strcpy(NewP, OldP); - - len = RemoveLF(NewMessage, (int)strlen(NewMessage)); - - ChatWriteLogLine(conn, '>', NewMessage, len, LOG); - ChatQueueMsg(conn, NewMessage, len); -} - - - -void chat_link_out (LINK *link) -{ - int n, p; - ChatCIRCUIT * conn; - char Msg[80]; - - for (n = NumberofChatStreams-1; n >= 0 ; n--) - { - conn = &ChatConnections[n]; - - if (conn->Active == FALSE) - { - p = conn->BPQStream; - memset(conn, 0, sizeof(ChatCIRCUIT)); // Clear everything - conn->BPQStream = p; - - conn->Active = TRUE; - circuit_new(conn, p_linkini); - conn->u.link = link; - conn->Flags = CHATMODE | CHATLINK; - - n=sprintf_s(Msg, sizeof(Msg), "Connecting to Chat Node %s", conn->u.link->alias); - - strcpy(conn->Callsign, conn->u.link->alias); - - ChatWriteLogLine(conn, '|',Msg, n, LOG_CHAT); - - link->ScriptIndex = -1; - RunningConnectScript = time(NULL); - link->MoreLines = TRUE; - link->scriptRunning = TRUE; - link->RTLSent = 0; - - ConnectUsingAppl(conn->BPQStream, ChatApplMask); - - // Connected Event will trigger connect to remote system - - return; - } - } - return; -} - - -VOID saywhat(ChatCIRCUIT *circuit) -{ - nputs(circuit, "Invalid Command\r"); -} - -VOID saydone(ChatCIRCUIT *circuit) -{ - nputs(circuit, "Ok\r"); -} - -VOID strnew(char ** new, char *f1) -{ - // seems to allocate a new string, and copy the old one to it - // how is this different to strdup?? - - *new = _strdup(f1); -} - -#define sl_ins_hd(link, hd) \ - if (hd == NULL)\ - hd=link;\ - else\ - {\ - link->next=hd->next;\ - hd->next=link;\ - } - -BOOL matchi(char * p1, char * p2) -{ - // Return TRUE is strings match - - if (_stricmp(p1, p2)) - return FALSE; - else - return TRUE; -} - - -VOID ProcessChatLine(ChatCIRCUIT * conn, struct UserInfo * user, char* OrigBuffer, int len) -{ - ChatCIRCUIT *c; - char * Buffer = OrigBuffer; - WCHAR BufferW[65536]; - UCHAR BufferB[65536]; - - // Sanity Check - - if (len > 32768) - return; - - // Convert to UTF8 if not already in UTF-8 - - if (len == 73 && memcmp(&OrigBuffer[40], " ", 20) == 0) - { - // Chat Signon Message. If Topic is present, switch to it - - char * Context; - char * Appl; - char * topic; - - Appl = strtok_s(OrigBuffer, " ,\r", &Context); - topic = strtok_s(NULL, " ,\r", &Context); - - if (topic == NULL) - return; // Just Chat - - // Have a Topic - - if (conn->Flags & GETTINGUSER) - { - // Need to log in before switching topic, so Give a dummy name here - - conn->Flags &= ~GETTINGUSER; - strcpy(user->Name, "?_name"); - ChatSendWelcomeMsg(conn->BPQStream, conn, user); - } - - OrigBuffer[40] = 0; - sprintf(&OrigBuffer[40],"/t %s\r", topic); - strcpy(OrigBuffer, &OrigBuffer[40]); - len = (int)strlen(OrigBuffer); - } - else - { - // Normal input - - if (conn->Flags & GETTINGUSER) - { - // Check not getting *RTL in response to Name prompt - - if (memcmp(Buffer, "*RTL", 4) == 0) - { - // Other end thinks this is a node-node link - - Logprintf(LOG_CHAT, conn, '!', "Station %s trying to start Node Protocol, but not defined as a Node", - conn->Callsign); - - knownnode_add(conn->Callsign); // So it won't happen again - - Disconnect(conn->BPQStream); - return; - } - - conn->Flags &= ~GETTINGUSER; - memcpy(user->Name, Buffer, len-1); - ChatSendWelcomeMsg(conn->BPQStream, conn, user); - - return; - } - } - - if (ChatIsUTF8(OrigBuffer, len) == FALSE) - { - // With Windows it is simple - convert using current codepage - // I think the only reliable way is to convert to unicode and back - -#ifdef WIN32 - - int wlen; - - wlen = MultiByteToWideChar(CP_ACP, 0, Buffer, len, BufferW, 65536); - len = WideCharToMultiByte(CP_UTF8, 0, BufferW, wlen, BufferB, 63336, NULL, NULL); - Buffer = BufferB; - -#else - size_t left = 65536; - size_t clen = len; - - UCHAR * BufferBP = BufferB; - struct user_t * icu = conn->u.user; - - if (conn->rtcflags & p_user) - { - if (icu->iconv_toUTF8 == NULL) - { - icu->iconv_toUTF8 = iconv_open("UTF-8//IGNORE", icu->Codepage); - - if (icu->iconv_toUTF8 == (iconv_t)-1) - icu->iconv_toUTF8 = iconv_open("UTF-8//IGNORE", "CP1252"); - } - - iconv(icu->iconv_toUTF8, NULL, NULL, NULL, NULL); // Reset State Machine - iconv(icu->iconv_toUTF8, &Buffer, &clen, (char ** __restrict__)&BufferBP, &left); - } - else - { - if (link_toUTF8 == NULL) - link_toUTF8 = iconv_open("UTF-8//IGNORE", "CP1252"); - - iconv(link_toUTF8, NULL, NULL, NULL, NULL); // Reset State Machine - iconv(link_toUTF8, &Buffer, &clen, (char ** __restrict__)&BufferBP, &left); - } - len = 65536 - left; - Buffer = BufferB; - -#endif - - } - ChatWriteLogLine(conn, '<',Buffer, len, LOG_CHAT); - - - Buffer[len] = 0; - - strlop(Buffer, '\r'); - - if (conn->rtcflags == p_linkwait) - { - //waiting for *RTL - - if (memcmp(Buffer, "*RTL", 4) == 0) - { - // Node - Node Connect - - if (rtloginl (conn, conn->Callsign)) - { - // Accepted - - conn->Flags |= CHATLINK; - return; - } - else - { - // Connection refused. rtlogin1 has sent error message and closed link - - return; - } - } - - if (Buffer[0] == '[' && Buffer[len-2] == ']') // SID - return; - - nprintf(conn, "Unexpected Message on Chat Node-Node Link - Disconnecting\r"); - ChatFlush(conn); - Sleep(500); - conn->rtcflags = p_nil; - - Disconnect(conn->BPQStream); - return; - } - - if (conn->Flags & CHATLINK) - { -#ifndef LINBPQ - - struct _EXCEPTION_POINTERS exinfo; - - __try - { - chkctl(conn, Buffer, len); - } - - #define EXCEPTMSG "Process Chat Line" - #include "StdExcept.c" - - Debugprintf("CHAT *** Was procesing Chat Node Message %s", Buffer); - Disconnect(conn->BPQStream); - CheckProgramErrors(); - } -#else - chkctl(conn, Buffer, len); -#endif - return; - } - - if(conn->u.user == NULL) - { - // A node link, but not activated yet, or a chat console which has dosconnected - - if (conn->BPQStream != -2) - return; - - // Log console user in - - if (rtloginu (conn, TRUE)) - conn->Flags |= CHATMODE; - - return; - - } - - if ((len <6) && (memcmp(Buffer, "*RTL", 4) == 0)) - { - // Other end thinks this is a node-node link - - Logprintf(LOG_CHAT, conn, '!', "Station %s trying to start Node Protocol, but not defined as a Node", - conn->Callsign); - - knownnode_add(conn->Callsign); // So it won't happen again - - Disconnect(conn->BPQStream); - return; - } - - if (Buffer[0] == '/') - { - // Process Command - - int cmdLen = 0; - char * param = strchr(&Buffer[1], ' '); - - if (param) - cmdLen = param - &Buffer[1]; - else - cmdLen = strlen(&Buffer[1]); - - if (_memicmp(&Buffer[1], "Bye", 1) == 0) - { - SendUnbuffered(conn->BPQStream, ChatSignoffMsg, (int)strlen(ChatSignoffMsg)); - - if (conn->BPQStream < 0) - { - logout(conn); - conn->Flags = 0; - if (conn->BPQStream == -2) - CloseConsole(conn->BPQStream); - } - else - ReturntoNode(conn->BPQStream); - - return; - } - - if (_memicmp(&Buffer[1], "Quit", 4) == 0) - { - SendUnbuffered(conn->BPQStream, ChatSignoffMsg, (int)strlen(ChatSignoffMsg)); - - if (conn->BPQStream < 0) - { - logout(conn); - conn->Flags = 0; - if (conn->BPQStream == -2) - CloseConsole(conn->BPQStream); - } - - else - { - Sleep(1000); - Disconnect(conn->BPQStream); - } - return; - } - - if (cmdLen > 1 && _memicmp(&Buffer[1], "History", cmdLen) == 0) // Accept Hi but not H - { - // Param is number of minutes to go back (max 24 hours) - - struct HistoryRec * ptr = History; - int interval = 0; - time_t start; - int n = HistoryCount; - - if (param) - interval = atoi(param); - - if (interval < 1) - { - nprintf(conn, "Format is /history n, where n is history time in minutes\r"); - conn->u.user->lastsendtime = time(NULL); - return; - } - - if (interval > 1440) - { - nprintf(conn, "History is only held for 24 Hours (1440 Minutes)\r"); - interval = 1440; // Limit to 1 day - } - - start = time(NULL) - (interval * 60); - - // Find first record to send - - while (ptr) - { - if (ptr->Time > start) - break; - n--; - ptr = ptr->next; - } - - // n is records found - - while (ptr) - { - nprintf(conn, ptr->Message); - ptr = ptr->next; - } - - conn->u.user->lastsendtime = time(NULL); - return; - } - - - - if (_memicmp(&Buffer[1], "Keepalive", 4) == 0) - { - conn->u.user->rtflags ^= u_keepalive; - upduser(conn->u.user); - nprintf(conn, "Keepalive is %s\r", (conn->u.user->rtflags & u_keepalive) ? "Enabled" : "Disabled"); - conn->u.user->lastsendtime = time(NULL); - return; - } - if (_memicmp(&Buffer[1], "AUTOCHARSET", 4) == 0) - { - conn->u.user->rtflags ^= u_auto; - upduser(conn->u.user); - nprintf(conn, "Automatic Character set selection is %s\r", (conn->u.user->rtflags & u_auto) ? "Enabled" : "Disabled"); - conn->u.user->lastsendtime = time(NULL); - return; - } - if (_memicmp(&Buffer[1], "UTF-8", 3) == 0) - { - conn->u.user->rtflags ^= u_noUTF8; - upduser(conn->u.user); - nprintf(conn, "Character set is %s\r", (conn->u.user->rtflags & u_noUTF8) ? "8 Bit" : "UTF-8"); - conn->u.user->lastsendtime = time(NULL); - return; - } - - if ((_memicmp(&Buffer[1], "CodePage", 3) == 0) || (_memicmp(&Buffer[1], "CP", 2) == 0)) - { - char * Context; - char * CP = strtok_s(&Buffer[1], " ,\r", &Context); -#ifndef WIN32 - iconv_t temp = NULL; -#else - int temp = 0; - WCHAR TempW[10]; -#endif - CP = strtok_s(NULL, " ,\r", &Context); - - if (CP == NULL || CP[0] == 0) - { -#ifndef WIN32 - if (conn->u.user->Codepage[0]) - nprintf(conn, "Codepage is %s\r", conn->u.user->Codepage); -#else - if (conn->u.user->Codepage) - nprintf(conn, "Codepage is %d\r", conn->u.user->Codepage); -#endif - else - nprintf(conn, "Codepage is not set\r"); - - return; - } - _strupr(CP); - -#ifndef WIN32 - - // Validate Code Page by trying to open an iconv descriptor - - temp = iconv_open("UTF-8", CP); - - if (temp == (iconv_t)-1) - { - nprintf(conn, "Invalid Codepage %s\r", CP); - return; - } - - iconv_close(conn->u.user->iconv_toUTF8); - iconv_close(conn->u.user->iconv_fromUTF8); - - conn->u.user->iconv_toUTF8 = temp; - conn->u.user->iconv_fromUTF8 = iconv_open(CP, "UTF-8"); - - strcpy(conn->u.user->Codepage, CP); - nprintf(conn, "Codepage set to %s\r", conn->u.user->Codepage); -#else - if (CP[0] == 'C') - CP +=2; - - // Validate by trying ot use it - - temp = atoi(CP); - - if (MultiByteToWideChar(temp, 0, "\r", 2, TempW, 10) == 0) - { - int err = GetLastError(); - - if (err == ERROR_INVALID_PARAMETER) - { - nprintf(conn, "Invalid Codepage %d\r", temp); - return; - } - } - - conn->u.user->Codepage = temp; - nprintf(conn, "Codepage set to %d\r", conn->u.user->Codepage); -#endif - upduser(conn->u.user); - - return; - } - - if (_memicmp(&Buffer[1], "Shownames", 4) == 0) - { - conn->u.user->rtflags ^= u_shownames; - upduser(conn->u.user); - nprintf(conn, "Shownames is %s\r", (conn->u.user->rtflags & u_shownames) ? "Enabled" : "Disabled"); - conn->u.user->lastsendtime = time(NULL); - return; - } - - if (_memicmp(&Buffer[1], "Time", 4) == 0) - { - conn->u.user->rtflags ^= u_showtime; - upduser(conn->u.user); - nprintf(conn, "Show Time is %s\r", (conn->u.user->rtflags & u_showtime) ? "Enabled" : "Disabled"); - conn->u.user->lastsendtime = time(NULL); - return; - } - - if (_memicmp(&Buffer[1], "colours", 4) == 0) - { - int i =0; - - while (i < 100) - { - nprintf(conn, "\x1b%c%02d XXXXX\r", i + 10, i); - i++; - if (i == 3) - i++; - } - return; - } - - rt_cmd(conn, Buffer); - - return; - } - - // Send message to all other connected users on same channel - - text_tellu(conn->u.user, Buffer, NULL, o_topic); // To local users. - - HistoryCount = AddtoHistory(conn->u.user, Buffer); - - conn->u.user->lastrealmsgtime = conn->u.user->lastmsgtime = time(NULL); - - // Send to Linked nodes - - for (c = circuit_hd; c; c = c->next) - { - if ((c->rtcflags & p_linked) && c->refcnt && ct_find(c, conn->u.user->topic)) - nprintf(c, "%c%c%s %s %s\r", FORMAT, id_data, OurNode, conn->u.user->call, Buffer); - } -} - -void upduser(USER *user) -{ - FILE *in, *out; - char *c; - char Buffer[2048]; - char *buf = Buffer; - - in = fopen(RtUsr, "r"); - - if (!(in)) - { - in = fopen(RtUsr, "w"); - if (in) - fclose(in); - in = fopen(RtUsr, "r"); - } - - if (!(in)) return; - - out = fopen(RtUsrTemp, "w"); - - if (!(out)) return; - - while(fgets(buf, 128, in)) - { - if (strstr(buf, "*RTL")) // Tidy user database - continue; - - c = strchr(buf, ' '); - if (c) *c = '\0'; - if (!matchi(buf, user->call)) - { - if (c) *c = ' '; - fputs(buf, out); - } - } - -#ifndef WIN32 - fprintf(out, "%s %d %s %s¬%d¬%s\n", user->call, user->rtflags, user->name, user->qth, user->Colour, user->Codepage); -#else - fprintf(out, "%s %d %s %s¬%d¬%d\n", user->call, user->rtflags, user->name, user->qth, user->Colour, user->Codepage); -#endif - fclose(in); - fclose(out); - - remove(RtUsr); - rename(RtUsrTemp, RtUsr); -} - -char * lookupuser(char * call) -{ - FILE *in; - char *flags; - char Buffer[2048]; - char *buf = Buffer; - char * name; - - in = fopen(RtUsr, "r"); - - if (in) - { - while(fgets(buf, 128, in)) - { - strlop(buf, '\n'); - - flags = strlop(buf, ' '); - if (!matchi(buf, call)) continue; - if (!flags) break; - - fclose(in); - name = strlop(flags, ' '); - strlop(name, ' '); - return _strdup(name); - } - fclose(in); - } - - return NULL; -} - - - -void rduser(USER *user) -{ - FILE *in; - char *name, *flags, *qth; - char Buffer[2048]; - char *buf = Buffer; - char * ptr; - - user->name = _strdup("?_name"); - user->qth = _strdup("?_qth"); - - in = fopen(RtUsr, "r"); - - if (in) - { - while(fgets(buf, 128, in)) - { - strlop(buf, '\n'); - - flags = strlop(buf, ' '); - if (!matchi(buf, user->call)) continue; - if (!flags) break; - - name = strlop(flags, ' '); - user->rtflags = atoi(flags); - - qth = strlop(name, ' '); - strnew(&user->name, name); - - if (!qth) break; - - // Colour Code may follow QTH, and Code Page may follow Colour - - ptr = strchr(qth, '¬'); - if (ptr) - { - *ptr++ = 0; - user->Colour = atoi(ptr); - - ptr = strchr(ptr, '¬'); - - if (ptr) - { - *ptr++ = 0; -#ifndef WIN32 - strcpy(user->Codepage, ptr); -#else - user->Codepage = atoi(ptr); -#endif - } - } - - strnew(&user->qth, qth); - break; - } - fclose(in); - -#ifndef WIN32 - - // Open an iconv decriptor for each conversion - - if (user->Codepage[0]) - user->iconv_toUTF8 = iconv_open("UTF-8//IGNORE", user->Codepage); - else - user->iconv_toUTF8 = (iconv_t)-1; - - if (user->iconv_toUTF8 == (iconv_t)-1) - user->iconv_toUTF8 = iconv_open("UTF-8//IGNORE", "CP1252"); - - - if (user->Codepage[0]) - user->iconv_fromUTF8 = iconv_open(user->Codepage, "UTF-8"); - else - user->iconv_fromUTF8 = (iconv_t)-1; - - if (user->iconv_fromUTF8 == (iconv_t)-1) - user->iconv_fromUTF8 = iconv_open("CP1252//IGNORE", "UTF-8"); -#endif - } -} - - -void ReportBadJoin(char * ncall, char *ucall) -{ - Logprintf(LOG_CHAT, NULL, '!', "User %s Join from Node %s but already connected", ucall, ncall); -} - -void ReportBadLeave(char * ncall, char * ucall) -{ - Logprintf(LOG_CHAT, NULL, '!', "Node %s reporting Node %s as a leaving user", ncall, ucall); -} - - -struct DUPINFO DupInfo[MAXDUPS]; - -static BOOL CheckforDups(ChatCIRCUIT * circuit, char * Call, char * Msg) -{ - // Primitive duplicate suppression - see if same call and text reeived in last few secons - - time_t Now = time(NULL); - time_t DupCheck = Now - DUPSECONDS; - int i, saveindex = -1; - - for (i = 0; i < MAXDUPS; i++) - { - if (DupInfo[i].DupTime < DupCheck) - { - // too old - use first if we need to save it - - if (saveindex == -1) - { - saveindex = i; - } - continue; - } - - if ((strcmp(Call, DupInfo[i].DupUser) == 0) && (memcmp(Msg, DupInfo[i].DupText, strlen(DupInfo[i].DupText)) == 0)) - { - // Duplicate, so discard, but save time - - DupInfo[i].DupTime = Now; - Logprintf(LOG_CHAT, circuit, '?', "Duplicate Message From %s %s suppressed", Call, Msg); - - return TRUE; // Duplicate - } - - } - - // Not in list - - if (saveindex == -1) // List is full - saveindex = MAXDUPS - 1; // Stick on end - - DupInfo[saveindex].DupTime = Now; - strcpy(DupInfo[saveindex].DupUser, Call); - - if (strlen(Msg) > 99) - { - memcpy(DupInfo[saveindex].DupText, Msg, 99); - DupInfo[saveindex].DupText[99] = 0; - } - else - strcpy(DupInfo[saveindex].DupText, Msg); - - return FALSE; -} - -void chkctl(ChatCIRCUIT *ckt_from, char * Buffer, int Len) -{ - CHATNODE * node, *ln; - ChatCIRCUIT * ckt_to; - USER * user, * su; - time_t Now = time(NULL); - LINK * Link = ckt_from->u.link; - - char * ncall, * ucall, * f1, * f2, * buf; - int i; - - if (Buffer[FORMAT_O] != FORMAT) return; // Not a control message. - - // Check for corruption - - for (i = 1; i < (Len - 1); i++) - { - if (Buffer[i] < 32) - { - if (Buffer[i] == 9) - { - Buffer[i] = 32; - continue; - } - Debugprintf("Corrupt Chat Link Messages %s", Buffer); - return; - } - } - - buf = _strdup(Buffer + DATA_O); - -// FORMAT and TYPE bytes are followed by node and user callsigns. - - ncall = buf; - ucall = strlop(buf, ' '); - if (!ucall) { free(buf); return; } // Not a control message. - -// There may be at least one field after the node and user callsigns. -// Node leave (id_unlink) has no F1. - - f1 = strlop(ucall, ' '); - strlop(ucall, 9); // some have tabs ?? - -// If the frame came from an unknown node ignore it. -// If the frame came from us ignore it (loop breaking). - - node = node_find(ncall); - if (!node || matchi(ncall, OurNode)) { free(buf); return; } - - if (ckt_from->rtcflags & p_linked) - ckt_from->u.link->lastMsgReceived = Now; - - switch(Buffer[TYPE_O]) - { - // Data from user ucall at node ncall. - - case id_data : - - // Check for dups - - if (CheckforDups(ckt_from, ucall, f1)) - break; - - user = user_find(ucall, ncall); - - if (!user) - break; - - user->lastrealmsgtime = user->lastmsgtime = time(NULL); - - text_tellu(user, f1, NULL, o_topic); - HistoryCount = AddtoHistory(user, f1); - - for (ckt_to = circuit_hd; ckt_to; ckt_to = ckt_to->next) - { - if ((ckt_to->rtcflags & p_linked) && ckt_to->refcnt && - !cn_find(ckt_to, node) && ct_find(ckt_to, user->topic)) - nprintf(ckt_to, "%s\r", Buffer); - } - break; - - // User ucall at node ncall changed their Name/QTH info. - - case id_user : - - user = user_find(ucall, ncall); - if (!user) break; - f2 = strlop(f1, ' '); - if (!f2) break; - - if ((strcmp(user->name, f1) == 0) && (strcmp(user->qth, f2) == 0)) // No Change? - break; - - echo(ckt_from, node, Buffer); // Relay to other nodes. - strnew(&user->name, f1); - strnew(&user->qth, f2); - upduser(user); - break; - - // User ucall logged into node ncall. - - case id_join : - - user = user_find(ucall, ncall); - - if (user) - { - // Already Here - - // If last join was less the 5 secs ago don't report - probably a "Join/Leave Storm" - - if (time(NULL) - user->timeconnected > 5) - ReportBadJoin(ncall, ucall); - - //if (strcmp(user->node->call, OurNode) == 0) - //{ - // Locally connected, and at another node - //} - - user->timeconnected = time(NULL); - break; // We have this user as an active Node - } - - // update join time - - echo(ckt_from, node, Buffer); // Relay to other nodes. - f2 = strlop(f1, ' '); - if (!f2) break; - user = user_join(ckt_from, ucall, ncall, NULL, FALSE); - if (!user) break; - ckt_from->refcnt++; - text_tellu_Joined(user); - strnew(&user->name, f1); - strnew(&user->qth, f2); - upduser(user); -// makelinks(); // Bring up our links if not already up - - break; - - // User ucall logged out of node ncall. - - case id_leave : - - user = user_find(ucall, ncall); - if (!user) - { - Debugprintf("CHAT: Leave for %s from %s when not on list", ucall, ncall); - break; - } - - // if connected for for less than 3 seconds ignore. May give stuck nodes but should stop "Join/Leave Storm" - // we can't just silently leave as next join will propagate - - if (time(NULL) - user->timeconnected < 3) - break; - - echo(ckt_from, node, Buffer); // Relay to other nodes. - - f2 = strlop(f1, ' '); - if (!f2) break; - - text_tellu(user, rtleave, NULL, o_all); - ckt_from->refcnt--; - strnew(&user->name, f1); - strnew(&user->qth, f2); - upduser(user); - user_leave(user); - - cn_dec(ckt_from, node); - node_dec(node); - - break; - - // Node ncall lost its link to node ucall, alias f1. - - case id_unlink : - - // Only relay to other nodes if we had node. Could get loop otherwise. - // ?? This could possibly cause stuck nodes - - ln = node_find(ucall); - - // if connected for for less than 3 seconds ignore. May give stuck nodes but should stop "Join/Leave Storm" - // we can't just silently leave as next join will propagate - - if (ln) - { - if (time(NULL) - ln->timeconnected < 3) - break; - - // is it on this circuit? - - if (cn_find(ckt_from, ln)) - { - cn_dec(ckt_from, ln); - node_dec(ln); - echo(ckt_from, node, Buffer); // Relay to other nodes if we had node. COuld get loop if - } - else - { - Debugprintf("CHAT: node %s unlink for %s when not on this link", ncall, ucall); - } - } - else - { - Debugprintf("CHAT: node %s unlink for %s when not on list", ncall, ucall); - } - - break; - - // Node ncall acquired a link to node ucall, alias f1. - // If we are not linked, is no problem, don't link. - // If we are linked, is a loop, do what? (Try ignore!) - - case id_link : - - ln = node_find(ucall); - - if (!ln && !matchi(ncall, OurNode)) - { - f2 = strlop(f1, ' '); - cn_inc(ckt_from, ucall, f1, f2); - echo(ckt_from, node, Buffer); // Relay to other nodes. - } - else - { - // If last join was less the 5 secs ago don't report - probably a "Join/Leave Storm" - - if (time(NULL) - ln->timeconnected > 5) - Debugprintf("CHAT: node %s link for %s when already on list", ncall, ucall); - - // update join time - - ln->timeconnected = time(NULL); - break; - } - - break; - - // User ucall at node ncall sent f2 to user f1. - - case id_send : - user = user_find(ucall, ncall); - if (!user) break; - f2 = strlop(f1, ' '); - if (!f2) break; - su = user_find(f1, NULL); - if (!su) break; - - if (su->circuit->rtcflags & p_user) - text_tellu(user, f2, f1, o_one); - else - echo(ckt_from, node, Buffer); // Relay to other nodes. - break; - - // User ucall at node ncall changed topic. - - case id_topic : - user = user_find(ucall, ncall); - if (user) - { - if (_stricmp(user->topic->name, f1) != 0) - { - echo(ckt_from, node, Buffer); // Relay to other nodes. - topic_chg(user, f1); - } - } - break; - - - case id_keepalive : - - ln = node_find(ncall); - if (ln) - { - if (ln->Version == NULL) - if (f1) - ln->Version = _strdup(f1); - } - - nprintf(ckt_from, "%c%c%s %s\r", FORMAT, id_pollresp, OurNode, Link->call); - break; - - case id_poll: - - // Send Poll Response - - Link->supportsPolls = Now; - nprintf(ckt_from, "%c%c%s %s\r", FORMAT, id_pollresp, OurNode, Link->call); - break; - - case id_pollresp: - - Link->supportsPolls = Now; - Link->RTT = Now - Link->timePollSent; - Link->timePollSent = 0; // Cancel Timeout - break; - - default: - break; - } - - free(buf); -} - -// Tell another node about nodes known by this node. -// Do not tell it about this node, the other node knows who it -// linked to (or who linked to it). -// Tell another node about users known by this node. -// Done at incoming or outgoing link establishment. - -void state_tell(ChatCIRCUIT *circuit, char * Version) -{ - CHATNODE *node; - USER *user; - - node = cn_inc(circuit, circuit->u.link->call, circuit->u.link->alias, Version); - node_tell(node, id_link); // Tell other nodes about this new link - - // Tell the node that just linked here about nodes known on other links. - - for (node = node_hd; node; node = node->next) - { - if (!matchi(node->call, OurNode)) - node_xmit(node, id_link, circuit); - } - - // Tell the node that just linked here about known users, and their topics. - - for (user = user_hd; user; user = user->next) - { - user_xmit(user, id_join, circuit); - topic_xmit(user, circuit); - } -} - -static void circuit_free(ChatCIRCUIT *circuit) -{ - ChatCIRCUIT *c, *cp; - CN *ncn; - CHATNODE *nn; - TOPIC *tn; - - cp = NULL; - - for (c = circuit_hd; c; cp = c, c = c->next) - { - if (c == circuit) - { - if (cp) cp->next = c->next; else circuit_hd = c->next; - - while (c->hnode) - { - ncn = c->hnode->next; - free(c->hnode); - c->hnode = ncn; - } - - break; - } - } - - if (circuit_hd) return; - -// RT has gone inactive. Clean up. - - while (node_hd) - { - nn = node_hd->next; - free(node_hd->alias); - free(node_hd->call); - free(node_hd); - node_hd = nn; - } - - while (topic_hd) - { - tn = topic_hd->next; - free(topic_hd->name); - free(topic_hd); - topic_hd = tn; - } -} - - -// Find a node in the node list. - -CHATNODE *node_find(char *call) -{ - CHATNODE *node; - - for (node = node_hd; node; node = node->next) - { - //if (node->refcnt && matchi(node->call, call)) I don't think this is right!!! - if (matchi(node->call, call)) - break; - } - - return node; -} - -// Add a reference to a node. - -static CHATNODE *node_inc(char *call, char *alias, char * Version) -{ - CHATNODE *node; - - node = node_find(call); - - if (!node) - { - knownnode_add(call); - - node = zalloc(sizeof(CHATNODE)); - sl_ins_hd(node, node_hd); - node->call = _strdup(call); - node->alias = _strdup(alias); - if (Version) - node->Version = _strdup(Version); - - node->timeconnected = time(NULL); - -// Debugprintf("New Node Rec Created at %x for %s %s", node, node->call, node->alias); - } - - node->refcnt++; - return node; -} - -// Remove a reference to a node. - -static void node_dec(CHATNODE *node) -{ - CHATNODE *t, *tp; - USER *user; - - ChatCIRCUIT *circuit; - CN *cn; - - if (--node->refcnt) return; // Other references. - - // Remove the node from the node list. - - tp = NULL; - - // Make sure there aren't any user or circuit records pointing to it - - for (user = user_hd; user; user = user->next) - { - if (user->node == node) - { - Debugprintf("Trying to remove node %s that is linked from user %s", node->call, user->call); - node->refcnt++; - } - } - - for (circuit = circuit_hd; circuit; circuit = circuit->next) - { - if (circuit->rtcflags & p_linked) - { - for (cn = circuit->hnode; cn; cn = cn->next) - { - if (cn->node == node) - { - Debugprintf("Trying to remove node %s that is linked from circuit %s", node->call, circuit->Callsign); - node->refcnt++; - } - } - } - } - - if (node->refcnt) return; // Now have other references. - - for (t = node_hd; t; tp = t, t = t->next) - { - if (t == node) - { - if (tp) tp->next = t->next; else node_hd = t->next; - free(t->alias); - t->alias = NULL; - free(t->call); - t->call = NULL; - free(t); - break; - } - } -} - -// User joins a topic. - -static TOPIC *topic_join(ChatCIRCUIT *circuit, char *s) -{ - CT *ct; - TOPIC *topic; - -// Look for an existing topic. - - for (topic = topic_hd; topic; topic = topic->next) - { - if (matchi(topic->name, s)) - break; - } - -// Create a new topic, if needed. - - if (!topic) - { - topic = zalloc(sizeof(TOPIC)); - sl_ins_hd(topic, topic_hd); - topic->name = _strdup(s); - } - - topic->refcnt++; // One more user in this topic. - - Logprintf(LOG_CHAT, circuit, '?', "topic_join complete user %s topic %s addr %x ref %d", - circuit->u.user->call, topic->name, topic, topic->refcnt); - - -// Add the circuit / topic association. - - for (ct = circuit->topic; ct; ct = ct->next) - { - if (ct->topic == topic) - { - ct->refcnt++; - return topic; - } - } - - ct = zalloc(sizeof(CT)); - sl_ins_hd(ct, circuit->topic); - ct->topic = topic; - ct->refcnt = 1; - return topic; -} - -// User leaves a topic. - -static void topic_leave(ChatCIRCUIT *circuit, TOPIC *topic) -{ - CT *ct, *ctp; - TOPIC *t, *tp; - - Logprintf(LOG_CHAT, circuit, '?', "topic_leave user %s topic %s addr %x ref %d", - circuit->u.user->call, topic->name, topic, topic->refcnt); - - topic->refcnt--; - - ctp = NULL; - - for (ct = circuit->topic; ct; ctp = ct, ct = ct->next) - { - if (ct->topic == topic) - { - if (!--ct->refcnt) - { - if (ctp) ctp->next = ct->next; else circuit->topic = ct->next; - free(ct); - break; - } - } - } - - tp = NULL; - - for (t = topic_hd; t; tp = t, t = t->next) - { - if (!t->refcnt && (t == topic)) - { - if (tp) tp->next = t->next; else topic_hd = t->next; - free(t->name); - free(t); - break; - } - } -} - -// Find a circuit/topic association. - -int ct_find(ChatCIRCUIT *circuit, TOPIC *topic) -{ - CT *ct; - - for (ct = circuit->topic; ct; ct = ct->next) - { - if (ct->topic == topic) - return ct->refcnt; - } - return 0; -} - -// Nodes reached from each circuit. Used only if the circuit is a link. - -// Remove a circuit/node association. - -static void cn_dec(ChatCIRCUIT *circuit, CHATNODE *node) -{ - CN *c, *cp; - -// Debugprintf("CHAT: Remove c/n %s ", node->call); - - cp = NULL; - - for (c = circuit->hnode; c; cp = c, c = c->next) - { - if (c->node == node) - { -// CN * cn; -// int len; -// char line[1000]=""; - - if (--c->refcnt) - { -// Debugprintf("CHAT: Remove c/n Node %s still in use refcount %d", node->call, c->refcnt); - return; // Still in use - } - - if (cp) - cp->next = c->next; - else - circuit->hnode = c->next; - - free(c); - - break; - } - } - - if (c == NULL) - { - CN * cn; - int len; - char line[1000]=""; - - // not found?? - - Debugprintf("CHAT: !! Remove c/n Node %s addr %x not found cn chain follows", node->call, node); - - line[0] = 0; - - for (cn = circuit->hnode; cn; cn = cn->next) - { - if (cn->node && cn->node->call) - { -#ifndef LINBPQ - __try - { -#endif - len += sprintf(&line[len], " %p %s", cn->node, cn->node->alias); - if (len > 80) - { - Debugprintf("%s", line); - len = sprintf(line, " "); - } -#ifndef LINBPQ - } - __except(EXCEPTION_EXECUTE_HANDLER) - {len = sprintf("%s *PE* Corrupt Rec %x %x ", line, cn, cn->node);} -#endif - } - else - { - len = sprintf("%s Corrupt Rec %x %x ", line, cn, cn->node); - } - } - Debugprintf("%s", line); - - } - - -} - -// Add a circuit/node association. - -static CHATNODE *cn_inc(ChatCIRCUIT *circuit, char *call, char *alias, char * Version) -{ - CHATNODE *node; - CN *cn; - - node = node_inc(call, alias, Version); - - for (cn = circuit->hnode; cn; cn = cn->next) - { - if (cn->node == node) - { - cn->refcnt++; -// Debugprintf("cn_inc cn Refcount for %s->%s incremented to %d - adding Call %s", -// circuit->Callsign, node->call, cn->refcnt, call); - - return node; - } - } - - cn = zalloc(sizeof(CN)); - sl_ins_hd(cn, circuit->hnode); - cn->node = node; - cn->refcnt = 1; - -// Debugprintf("cn_inc New cn for %s->%s - adding Call %s", -// circuit->Callsign, node->call, call); - - return node; -} - -// Find a circuit/node association. - -static int cn_find(ChatCIRCUIT *circuit, CHATNODE *node) -{ - CN *cn; - - for (cn = circuit->hnode; cn; cn = cn->next) - { - if (cn->node == node) - return cn->refcnt; - } - return 0; -} - -// From a local user to a specific user at another node. - -static void text_xmit(USER *user, USER *to, char *text) -{ - nprintf(to->circuit, "%c%c%s %s %s %s\r", - FORMAT, id_send, OurNode, user->call, to->call, text); -} - -void put_text(ChatCIRCUIT * circuit, USER * user, UCHAR * buf) -{ - UCHAR BufferB[4096]; - - // Text is UTF-8 internally. If user doen't want UTF-8. convert to Node's locale - - if (circuit->u.user->rtflags & u_noUTF8) - { -#ifdef WIN32 - char * Buffer = buf; - WCHAR BufferW[4096]; - int wlen, blen; - BOOL DefaultUsed = FALSE; - char Subst = '?'; - - wlen = MultiByteToWideChar(CP_UTF8, 0, buf, (int)strlen(buf) + 1, BufferW, 4096); - blen = WideCharToMultiByte(circuit->u.user->Codepage, 0, BufferW, wlen, BufferB + 2, 4096, &Subst, &DefaultUsed); - - if (blen == 0) // Probably means invalid code page - blen = WideCharToMultiByte(CP_ACP, 0, BufferW, wlen, BufferB + 2, 4096, &Subst, &DefaultUsed); - - buf = BufferB + 2; - BufferB[blen + 2] = 0; -#else - - size_t left = 4096; - UCHAR * BufferBP = BufferB; - size_t len = strlen(buf) + 1; - struct user_t * icu = circuit->u.user; - - if (icu->iconv_fromUTF8 == NULL) - { - icu->iconv_fromUTF8 = iconv_open(icu->Codepage, "UTF-8"); - - if (icu->iconv_fromUTF8 == (iconv_t)-1) - icu->iconv_fromUTF8 = iconv_open("CP1252//IGNORE", "UTF-8"); - } - - iconv(icu->iconv_fromUTF8, NULL, NULL, NULL, NULL); // Reset State Machine - iconv(icu->iconv_fromUTF8, (char ** __restrict__)&buf, &len, (char ** __restrict__)&BufferBP, &left); - - len = 4096 - left; - buf = BufferB; - -#endif - - } - - - if (circuit->u.user->rtflags & u_colour) // Use Colour - { - // Put a colour header on message - - *(--buf) = user->Colour; - *(--buf) = 0x1b; - nputs(circuit, buf); - buf +=2; - } - else - nputs(circuit, buf); - - - - circuit->u.user->lastsendtime = time(NULL); -} - -void text_tellu(USER *user, char *text, char *to, int who) -{ - ChatCIRCUIT *circuit; - UCHAR Buffer[2048]; - UCHAR *buf = &Buffer[4]; - char * Time; - struct tm * tm; - char Stamp[20]; - time_t T; - - T = time(NULL); - tm = gmtime(&T); - - sprintf(Stamp,"%02d:%02d ", tm->tm_hour, tm->tm_min); - -// Send it to all connected users in the same topic. -// Echo to originator if requested. - - for (circuit = circuit_hd; circuit; circuit = circuit->next) - { - if (!(circuit->rtcflags & p_user)) continue; // Circuit is a link. - - if ((circuit->u.user == user) && !(user->rtflags & u_echo)) continue; - - if (circuit->u.user->rtflags & u_showtime) - Time = Stamp; - else - Time = ""; - - if (circuit->u.user->rtflags & u_shownames) - sprintf(buf, "%s%-6.6s %s %c %s\r", Time, user->call, user->name, (who == o_one) ? '>' : ':', text); - else - sprintf(buf, "%s%-6.6s %c %s\r", Time, user->call, (who == o_one) ? '>' : ':', text); - - - switch(who) - { - case o_topic : - if (circuit->u.user->topic == user->topic) - put_text(circuit, user, buf); // Send adding Colour if wanted - - break; - - case o_all: - - put_text(circuit, user, buf); // Send adding Colour if wanted - - break; - - case o_one : - if (matchi(circuit->u.user->call, to)) - put_text(circuit, user, buf); // Send adding Colour if wanted - break; - } - } -} - -extern int FlashOnConnect; - -void text_tellu_Joined(USER * user) -{ - ChatCIRCUIT *circuit; - UCHAR Buffer[200]; - UCHAR *buf = &Buffer[4]; - char * Time; - struct tm * tm; - char Stamp[20]; - time_t T; - char prog[256] = ""; - - T = time(NULL); - tm = gmtime(&T); - - sprintf(Stamp,"%02d:%02d ", tm->tm_hour, tm->tm_min); - - sprintf(buf, "%s%-6.6s : %s *** Joined Chat, Topic %s", Stamp, user->call, user->name, user->topic->name); - - if (reportChatEvents) - { - -#ifdef WIN32 - if (pRunEventProgram) - pRunEventProgram("ChatNewUser.exe", user->call); -#else - sprintf(prog, "%s/%s", BPQDirectory, "ChatNewUser"); - RunEventProgram(prog, user->call); -#endif - } - -// Send it to all connected users in the same topic. -// Echo to originator if requested. - - for (circuit = circuit_hd; circuit; circuit = circuit->next) - { - if (!(circuit->rtcflags & p_user)) continue; // Circuit is a link. - if ((circuit->u.user == user) && !(user->rtflags & u_echo)) continue; - - if (circuit->u.user->rtflags & u_showtime) - Time = Stamp; - else - Time = ""; - - sprintf(buf, "%s%-6.6s : %s *** Joined Chat, Topic %s", Time, user->call, user->name, user->topic->name); - - put_text(circuit, user, buf); // Send adding Colour if wanted - - if (circuit->u.user->rtflags & u_bells) - if (circuit->BPQStream < 0) // Console - { -#ifndef LINBPQ - if (FlashOnConnect) FlashWindow(ConsHeader[1]->hConsole, TRUE); -#endif - nputc(circuit, 7); -// PlaySound ("BPQCHAT_USER_LOGIN", NULL, SND_ALIAS | SND_APPLICATION | SND_ASYNC); - } - else - nputc(circuit, 7); - - nputc(circuit, 13); - } -} -// Tell one link circuit about a local user change of topic. - -static void topic_xmit(USER *user, ChatCIRCUIT *circuit) -{ - nprintf(circuit, "%c%c%s %s %s\r", - FORMAT, id_topic, OurNode, user->call, user->topic->name); -} - -// Tell another node about one known node on a link add or drop -// if that node is from some other link. - -static void node_xmit(CHATNODE *node, char kind, ChatCIRCUIT *circuit) -{ -#ifndef LINBPQ - struct _EXCEPTION_POINTERS exinfo; - - __try - { -#endif - if (!cn_find(circuit, node)) - if (node->Version && (kind == id_link)) - nprintf(circuit, "%c%c%s %s %s %s\r", FORMAT, kind, OurNode, node->call, node->alias, node->Version); - else - nprintf(circuit, "%c%c%s %s %s\r", FORMAT, kind, OurNode, node->call, node->alias); - -#ifndef LINBPQ - } - - #define EXCEPTMSG "node_xmit" - #include "StdExcept.c" - - Debugprintf("Corrupt Rec %x %x %x", node, node->call, node->alias); - } -#endif -} - -// Tell all other nodes about one node known by this node. - -static void node_tell(CHATNODE *node, char kind) -{ - ChatCIRCUIT *circuit; - - for (circuit = circuit_hd; circuit; circuit = circuit->next) - { - if (circuit->rtcflags & p_linked) - node_xmit(node, kind, circuit); - } -} - -// Tell another node about a user login/logout at this node. - -static void user_xmit(USER *user, char kind, ChatCIRCUIT *circuit) -{ - CHATNODE *node; - - node = user->node; - - if (!cn_find(circuit, node)) - nprintf(circuit, "%c%c%s %s %s %s\r", FORMAT, kind, node->call, user->call, user->name, user->qth); -} - -// Tell all other nodes about a user login/logout at this node. - -static void user_tell(USER *user, char kind) -{ - ChatCIRCUIT *circuit; - - for (circuit = circuit_hd; circuit; circuit = circuit->next) - { - if (circuit->rtcflags & p_linked) - user_xmit(user, kind, circuit); - } -} - -// Find the user record for call@node. Node can be NULL, meaning any node - -USER *user_find(char *call, char * node) -{ - USER *user; - - for (user = user_hd; user; user = user->next) - { - if (node) - { - if (matchi(user->call, call) && matchi(user->node->call, node)) - break; - } - else - { - if (matchi(user->call, call)) - break; - } - } - - return user; -} - -static void user_leave(USER *user) -{ - USER *t, *tp; - - topic_leave(user->circuit, user->topic); - - tp = NULL; - - for (t = user_hd; t; tp = t, t = t->next) - { - if (t == user) - { - if (tp) tp->next = t->next; else user_hd = t->next; - - free(t->name); - free(t->call); - free(t->qth); -#ifndef WIN32 - if (t->iconv_fromUTF8) - iconv_close(t->iconv_fromUTF8); - if (t->iconv_toUTF8) - iconv_close(t->iconv_toUTF8); -#endif - free(t); - break; - } - } - - if (user_hd == NULL) - ChatTmr = 59; // If no users, disconnect links after 10-20 secs -} - -// User changed to a different topic. - -static BOOL topic_chg(USER *user, char *s) -{ - char buf[128]; - - if (_stricmp(user->topic->name, s) == 0) return FALSE; // Not Changed - - sprintf(buf, "*** Left Topic: %s", user->topic->name); - text_tellu(user, buf, NULL, o_topic); // Tell everyone in the old topic. - topic_leave(user->circuit, user->topic); - user->topic = topic_join(user->circuit, s); - sprintf(buf, "*** Joined Topic: %s", user->topic->name); - text_tellu(user, buf, NULL, o_topic); // Tell everyone in the new topic. - - return TRUE; -} - -// Create a user record for this user. - -static USER *user_join(ChatCIRCUIT *circuit, char *ucall, char *ncall, char *nalias, BOOL Local) -{ - CHATNODE *node; - USER *user; - - if (Local) - { - node = cn_inc(circuit, ncall, nalias, Verstring); - } - else - node = cn_inc(circuit, ncall, nalias, NULL); - -// Is this user already logged in at this node? - - for (user = user_hd; user; user = user->next) - { - if (matchi(user->call, ucall) && (user->node == node)) - return user; - } - -// User is not logged in, create a user record for them. - - user = zalloc(sizeof(USER)); - sl_ins_hd(user, user_hd); - user->circuit = circuit; - user->call = _strdup(ucall); - _strupr(user->call); - user->node = node; - rduser(user); - - if (user->Colour == 0 || user->Colour == 11) // None or default - { - // Allocate Random - int sum = 0, i; - - for (i = 0; i < 9; i++) - sum += user->call[i]; - sum %= 20; - - user->Colour = AutoColours[sum] + 10; // Best 20 colours - } - - if (circuit->rtcflags & p_user) - circuit->u.user = user; - - user->timeconnected = user->lastrealmsgtime = user->lastmsgtime = time(NULL); - - user->topic = topic_join(circuit, deftopic); - return user; -} - -// Link went away. We dropped it, or the other node dropped it. -// Drop nodes and users connected from this link. -// Tell other (still connected) links what was dropped. - -void link_drop(ChatCIRCUIT *circuit) -{ - USER *user, *usernext; - CN *cn; - -// So we don't try and send anything on this circuit. - - if (circuit->u.link) - if (circuit->rtcflags == p_linkini) - Debugprintf("Chat link %s Link Setup Failed", circuit->u.link->call); - - if (circuit->rtcflags == p_linkini) - circuit->u.link->flags = p_linkfailed; - else - circuit->u.link->flags = 0; - - circuit->rtcflags = p_nil; - -// Users connected on the dropped link are no longer connected. - - for (user = user_hd; user; user = usernext) - { - usernext = user->next; // Save next pointer in case entry is free'd - - if (user->circuit == circuit) - { - CHATNODE *node; - - node = user->node; - - text_tellu(user, rtleave, NULL, o_all); - user_tell(user, id_leave); - user_leave(user); - - circuit->refcnt--; - if (node) - node_dec(node); - } - } - -// Any node known from the dropped link is no longer known. - - for (cn = circuit->hnode; cn; cn = cn->next) - { - node_tell(cn->node, id_unlink); - node_dec(cn->node); - } - -// The circuit is no longer used. - - circuit_free(circuit); - NeedStatus = TRUE; -} - -// Handle an incoming control frame from a linked RT system. - -static void echo(ChatCIRCUIT *fc, CHATNODE *node, char * Buffer) -{ - ChatCIRCUIT *tc; - - for (tc = circuit_hd; tc; tc = tc->next) - { - if ((tc != fc) && (tc->rtcflags & p_linked) && !cn_find(tc, node)) - nprintf(tc, "%s\r", Buffer); - } -} - -char ** SeparateConnectScript(char * MultiString) -{ - char * ptr1 = MultiString; - char ** Value; - int Count = 0; - char * ptr; - - // Convert to string array - - Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values - Value[0] = NULL; - - ptr = MultiString; - - while (ptr && strlen(ptr)) - { - ptr1 = strchr(ptr, '|'); - - if (ptr1) - *(ptr1++) = 0; - - if (strlen(ptr)) - { - Value = realloc(Value, (Count + 2) * sizeof(void *)); - Value[Count++] = _strdup(ptr); - } - ptr = ptr1; - } - - Value[Count] = NULL; - return Value; -} - -// Add an entry to list of link partners - -int rtlink (char * Call) -{ - LINK *link, *temp; - char *c; - char * script; - - _strupr(Call); - script = strlop(Call, '|'); - - c = strlop(Call, ':'); - if (!c) return FALSE; - - link = zalloc(sizeof(LINK)); - - link->alias = _strdup(Call); - link->call = _strdup(c); - - if (script) - { - link->ConnectScript = SeparateConnectScript(script); - link->Lines = 0; - while (link->ConnectScript[++link->Lines]); - } - else - { - // Create Script with one entry to call partner direct; - - link->ConnectScript = zalloc(sizeof(void *) * 2); // always NULL entry on end - link->ConnectScript[0] = malloc(32); - sprintf(link->ConnectScript[0], "C %s", c); - link->Lines = 1; - } - - if (link_hd == NULL) - link_hd = link; - else - { - temp = link_hd; - while(temp->next) - temp = temp->next; - - temp->next = link; - } - - return TRUE; -} - -VOID removelinks() -{ - LINK *link, *nextlink; - - for (link = link_hd; link; link = nextlink) - { - nextlink = link->next; - - if (link->ConnectScript) - { - int n = 0; - while(link->ConnectScript[n]) - free(link->ConnectScript[n++]); - - free(link->ConnectScript); - } - - free(link->alias); - link->alias = 0; - free(link->call); - link->call = 0; - free(link); - link = 0; - } - link_hd = NULL; -} -VOID removeknown() -{ - // Save Known Nodes list and free struct - - KNOWNNODE *node, *nextnode; - FILE *out; - - out = fopen(RtKnown, "w"); - - if (!out) - return; - - for (node = known_hd; node; node = nextnode) - { - fprintf(out, "%s %u\n", node->call, (unsigned int)node->LastHeard); - - nextnode = node->next; - free(node->call); - free(node); - } - known_hd = NULL; - - fclose(out); -} - -VOID LoadKnown() -{ - // Reload Known Nodes list - - FILE *in; - char buf[128]; - char * ptr; - - in = fopen(RtKnown, "r"); - - if (in == NULL) - return; - - while(fgets(buf, 128, in)) - { - ptr = strchr(buf, ' '); - if (ptr) - { - *(ptr) = 0; - knownnode_add(buf); - } - } - - fclose(in); -} - -// We don't allocate memory for circuit, but we do chain it - -ChatCIRCUIT *circuit_new(ChatCIRCUIT *circuit, int flags) -{ - // Make sure circuit isn't already on list - - ChatCIRCUIT *c; - - circuit->rtcflags = flags; - circuit->next = NULL; - - for (c = circuit_hd; c; c = c->next) - { - if (c == circuit) - { - Debugprintf("CHAT: Attempting to add Circuit when already on list"); - return circuit; - } - } - - sl_ins_hd(circuit, circuit_hd); - - return circuit; -} - -// Handle an incoming link. We should only get here if we think the station is a node. - -int rtloginl (ChatCIRCUIT *conn, char * call) -{ - LINK * link; - - if (node_find(call)) - { - Logprintf(LOG_CHAT, conn, '|', "Refusing link from %s to %s to prevent a loop", conn->Callsign, OurNode); - - nprintf(conn, "Refusing link from %s to %s to prevent a loop.\n", conn->Callsign, OurNode); - ChatFlush(conn); - Sleep(500); - conn->rtcflags = p_nil; - Disconnect(conn->BPQStream); - return FALSE; // Already linked. - } - - for (link = link_hd; link; link = link->next) - { - if (matchi(call, link->call)) - break; - } - - if (!link) - { - // We don't link with this system. Shouldn't happen, as we checked earlier - - nprintf(conn, "Node %s does not have %s defined as a node to link to - closing.\r", - OurNode, conn->Callsign); - ChatFlush(conn); - Sleep(500); - conn->rtcflags = p_nil; - Disconnect(conn->BPQStream); - return FALSE; - } - - if (link->flags & (p_linked | p_linkini)) - { - // Already Linked. Used to Disconnect, but that can cause sync errors - // Try closing old link and keeping new - - ChatCIRCUIT *c; - int len; - char Msg[80]; - - for (c = circuit_hd; c; c = c->next) - { - if (c->u.link == link) - { - len=sprintf_s(Msg, sizeof(Msg), "Chat Node %s Connect when Connected - Old Connection Closed", call); - ChatWriteLogLine(conn, '|',Msg, len, LOG_CHAT); - - c->Active = FALSE; // So we don't try to clear circuit again - Disconnect(c->BPQStream); - link_drop(c); - RefreshMainWindow(); - break; - } - } - } - -// Accept the link request. - - circuit_new(conn, p_linked); - conn->u.link = link; - nputs(conn, "OK\r"); - link->flags = p_linked; - link->delay = 0; // Dont delay first restart - state_tell(conn, NULL); - conn->u.link->timePollSent = time(NULL); // Keepalive is a poll - nprintf(conn, "%c%c%s %s %s\r", FORMAT, id_keepalive, OurNode, conn->u.link->call, Verstring); - - NeedStatus = TRUE; - - return TRUE; -} - -// User connected to chat, or did chat command from BBS - -int rtloginu (ChatCIRCUIT *circuit, BOOL Local) -{ - USER *user; - -// Is this user already logged in to RT somewhere else? - - user = user_find(circuit->UserPointer->Call, NULL); - - if (user) - { - // if connected at this node, kill old connection and allow new login - - if (user->node == node_find(OurNode)) - { - nputs(circuit, "*** Already connected at this node - old session will be closed.\r"); - - if (user->circuit->BPQStream < 0) - { - CloseConsole(user->circuit->BPQStream); - } - else - { - Disconnect(user->circuit->BPQStream); - } - } - else - nputs(circuit, "*** Already connected at another node.\r"); - - return FALSE; - } - -// Create the user entry. - - circuit_new(circuit, p_user); - - user = user_join(circuit, circuit->UserPointer->Call, OurNode, OurAlias, Local); - circuit->u.user = user; - - if (strcmp(user->name, "?_name") == 0) - { - user->name = _strdup(circuit->UserPointer->Name); - } - upduser(user); - - ChatExpandAndSendMessage(circuit, ChatWelcomeMsg, LOG_CHAT); - text_tellu_Joined(user); - user_tell(user, id_join); - show_users(circuit); - user->lastsendtime = time(NULL); -// makelinks(); - - return TRUE; -} - -void logout(ChatCIRCUIT *circuit) -{ - USER *user; - CHATNODE *node; - - circuit->rtcflags = p_nil; - user = circuit->u.user; - - if (user) // May not have logged in if already conencted - { - node = user->node; - - user_tell(user, id_leave); - text_tellu(user, rtleave, NULL, o_all); - user_leave(user); - - // order changed so node_dec can check if a node that is about the be deleted has eny users - - if (node) - { - cn_dec(circuit, node); - node_dec(node); - } - - circuit->u.user = NULL; - } - - circuit_free(circuit); -} - -void show_users(ChatCIRCUIT *circuit) -{ - USER *user; - char * Alias; - char * Topic; - - int i = 0; - - // First count them - - for (user = user_hd; user; user = user->next) - { - i++; - } - - nprintf(circuit, "%d Station(s) connected:\r", i); - - for (user = user_hd; user; user = user->next) - { - if ((user->node == 0) || (user->node->alias == 0)) - Alias = "(Corrupt Alias)"; - else - Alias = user->node->alias; - - if ((user->topic == 0) || (user->topic->name == 0)) - Topic = "(Corrupt Topic)"; - else - Topic = user->topic->name; - -#ifndef LINBPQ - __try - { -#endif - if (circuit->u.user->rtflags & u_colour) // Use Colour - nprintf(circuit, "\x1b%c%-6.6s at %-9.9s %s, %s [%s] Idle for %d seconds\r", - user->Colour, user->call, Alias, user->name, user->qth, Topic, time(NULL) - user->lastrealmsgtime); - else - nprintf(circuit, "%-6.6s at %-9.9s %s, %s [%s] Idle for %d seconds\r", - user->call, Alias, user->name, user->qth, Topic, time(NULL) - user->lastrealmsgtime); -#ifndef LINBPQ - } - __except(EXCEPTION_EXECUTE_HANDLER) - { - Debugprintf("MAILCHAT *** Program Error in show_users"); - CheckProgramErrors(); - } -#endif - } -} - - -static void show_nodes(ChatCIRCUIT *circuit) -{ - CHATNODE *node; - - nputs(circuit, "Known Nodes:\r"); - - for (node = node_hd; node; node = node->next) - { - if (node->refcnt) - if (node->Version) - nprintf(circuit, "%s:%s %s %u\r", node->alias, node->call, node->Version, node->refcnt); - else - nprintf(circuit, "%s:%s %s %u\r", node->alias, node->call, "Not Known", node->refcnt); - } -} - -// /P Command: List circuits and remote RT on them. - -#define xxx "\r " - -static void show_circuits(ChatCIRCUIT *conn, char Flag) -{ - ChatCIRCUIT *circuit; - CHATNODE *node; - LINK *link; - char line[1000]; - int len = 0; - CN *cn; - - int i = 0; - - // First count them - - for (node = node_hd; node; node = node->next) - { - i++; - } - - nprintf(conn, "%d Node(s)\r", i); - - if (Flag == 'c') - len = sprintf(line, "Here %-6.6s <-", OurNode); - else - len = sprintf(line, "Here %-6.6s <-", OurAlias); - - for (node = node_hd; node; node = node->next) if (node->refcnt) - { - if (Flag == 'c') - len += sprintf(&line[len], " %s", node->call); - else - len += sprintf(&line[len], " %s", node->alias); - if (len > 80) - { - nprintf(conn, "%s\r", line); - len = sprintf(line, " "); - } - } - - nprintf(conn, "%s\r", line); - - for (circuit = circuit_hd; circuit; circuit = circuit->next) - { - if (circuit->rtcflags & p_linked) - { - if (Flag == 'c') - len = sprintf(line, "Nodes via %-6.6s(%d) -", circuit->u.link->call, circuit->refcnt); - else - len = sprintf(line, "Nodes via %-6.6s(%d) -", circuit->u.link->alias, circuit->refcnt); - -#ifndef LINBPQ - __try{ - for (cn = circuit->hnode; cn; cn = cn->next) - { - if (cn->node && cn->node->alias) - { - __try - { - if (Flag == 'c') - len += sprintf(&line[len], " %s", cn->node->call); - else - len += sprintf(&line[len], " %s", cn->node->alias); - if (len > 80) - { - nprintf(conn, "%s\r", line); - len = sprintf(line, " "); - } - } - __except(EXCEPTION_EXECUTE_HANDLER) - {len += sprintf(&line[len], " *PE* Corrupt Rec %x %x", cn, cn->node);} - } - else - len = sprintf(&line[len], " Corrupt Rec %x %x ", cn, cn->node); - } - } - __except(EXCEPTION_EXECUTE_HANDLER) - {len += sprintf(&line[len], " *PE* Corrupt Rec %x %x ", cn, cn->node);} -#else - for (cn = circuit->hnode; cn; cn = cn->next) - { - if (cn->node && cn->node->alias) - { - if (Flag == 'c') - len += sprintf(&line[len], " %s", cn->node->call); - else - len += sprintf(&line[len], " %s", cn->node->alias); - if (len > 80) - { - nprintf(conn, "%s\r", line); - len = sprintf(line, " "); - } - } - else - len += sprintf(&line[len], " Corrupt Rec %p %p ", cn, cn->node); - } -#endif - nprintf(conn, "%s\r", line); - - } - else if (circuit->rtcflags & p_user) - nprintf(conn, "User %-6.6s\r", circuit->u.user->call); - else if (circuit->rtcflags & p_linkini) - { - if (circuit->u.link) - { if (Flag == 'c') - nprintf(conn, "Link %-6.6s (setup)\r", circuit->u.link->call); - else - nprintf(conn, "Link %-6.6s (setup)\r", circuit->u.link->alias); - - } - else - nprintf(conn, "Link ?? (setup)\r"); - } - } - - nprintf(conn, "Links Defined:\r"); - - for (link = link_hd; link; link = link->next) - { - if (link->flags & p_linked ) - if (link->supportsPolls) - nprintf(conn, " %-10.10s Open RTT %d\r", link->call, link->RTT); - else - nprintf(conn, " %-10.10s Open\r", link->call); - else if (link->flags & (p_linked | p_linkini)) - nprintf(conn, " %-10.10s Connecting\r", link->call); - else if (link->flags & p_linkfailed) - nprintf(conn, " %-10.10s Connect failed\r", link->call); - else - nprintf(conn, " %-10.10s Idle\r", link->call); - } -} - -// /T Command: List topics and users in them. - -static void show_topics(ChatCIRCUIT *conn) -{ - TOPIC *topic; - USER *user; - - nputs(conn, "Active Topics are:\r"); - - for (topic = topic_hd; topic; topic = topic->next) - { - nprintf(conn, "%s\r", topic->name); - - if (topic->refcnt) - { - nputs(conn, " "); - for (user = user_hd; user; user = user->next) - { - if (user->topic == topic) - nprintf(conn, " %s", user->call); - } - nputc(conn, '\r'); - } - } -} - -static void show_users_in_topic(ChatCIRCUIT *conn) -{ - TOPIC *topic; - USER *user; - - nputs(conn, "Users in Topic:\r"); - - topic = conn->u.user->topic; - { - if (topic->refcnt) - { - for (user = user_hd; user; user = user->next) - { - if (user->topic == topic) - nprintf(conn, "%s ", user->call); - } - nputc(conn, '\r'); - } - } -} - -// Do a user command. - -int rt_cmd(ChatCIRCUIT *circuit, char * Buffer) -{ - ChatCIRCUIT *c; - USER *user, *su; - char *f1, *f2; - - user = circuit->u.user; - -// user->lastsendtime = time(NULL); - - switch(tolower(Buffer[1])) - { - case 'a' : - user->rtflags ^= u_bells; - upduser(user); - nprintf(circuit, "Alert %s\r", (user->rtflags & u_bells) ? "Enabled" : "Disabled"); - return TRUE; - - case 'b' : return FALSE; - - case 'c' : - user->rtflags ^= u_colour; - upduser(user); - nprintf(circuit, "Colour Mode %s\r", (user->rtflags & u_colour) ? "Enabled" : "Disabled"); - return TRUE; - - case 'e' : - user->rtflags ^= u_echo; - upduser(user); - nprintf(circuit, "Echo %s\r", (user->rtflags & u_echo) ? "Enabled" : "Disabled"); - return TRUE; - - case 'f' : makelinks(); return TRUE; - - case 'h' : - case '?' : - { - char * Save; - char * MsgBytes = Save = ReadInfoFile("chathelp.txt"); - - if (MsgBytes) - { - int Length; - - // Remove lf chars - - Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); - - ChatQueueMsg(circuit, MsgBytes, Length); - free(Save); - } - else - { - nputs(circuit, "Commands can be in upper or lower case.\r"); - nputs(circuit, "/U - Show Users.\r/N - Enter your Name.\r/Q - Enter your QTH.\r/T - Show Topics.\r"); - nputs(circuit, "/T Name - Join Topic or Create new Topic. Topic Names are not case sensitive\r/P - Show Ports and Links.\r"); - nprintf(circuit, "/A - Toggle Alert on user join - %s.\r", - (user->rtflags & u_bells) ? "Enabled" : "Disabled"); - nprintf(circuit, "/C - Toggle Colour Mode on or off (only works on Console or BPQTerm/TermTCP/QtTermTCP - %s.\r", - (user->rtflags & u_colour) ? "Enabled" : "Disabled"); - nputs(circuit, "/Codepage CPnnnn - Set Codepage to use if UTF-8 is disabled.\r"); - nprintf(circuit, "/E - Toggle Echo - %s .\r", - (user->rtflags & u_echo) ? "Enabled" : "Disabled"); - nprintf(circuit, "/Keepalive - Toggle sending Keepalive messages every 10 minutes - %s.\r", - (user->rtflags & u_keepalive) ? "Enabled" : "Disabled"); - nprintf(circuit, "/ShowNames - Toggle displaying name as well as call on each message - %s\r", - (user->rtflags & u_shownames) ? "Enabled" : "Disabled"); - nprintf(circuit, "/Auto - Toggle Automatic character set selection - %s.\r", - (user->rtflags & u_auto) ? "Enabled" : "Disabled"); - nprintf(circuit, "/UTF-8 - Character set Selection - %s.\r", - (user->rtflags & u_noUTF8) ? "8 Bit" : "UTF-8"); - nprintf(circuit, "/Time - Toggle displaying timestamp on each message - %s.\r", - (user->rtflags & u_showtime) ? "Enabled" : "Disabled"); - nputs(circuit, "/S CALL Text - Send Text to that station only.\r"); - nputs(circuit, "/F - Force all links to be made.\r/K - Show Known nodes.\r"); - nputs(circuit, "/B - Leave Chat and return to node.\r/QUIT - Leave Chat and disconnect from node.\r"); - nputs(circuit, "/History nn - Display chat messages received in last nn minutes.\r"); - } - } - return TRUE; - - case 'k' : show_nodes(circuit); return TRUE; - - case 'n' : - - f1 = &Buffer[2]; - - while ((*f1 != 0) && (*f1 == ' ')) - f1++; - - if (*f1 == 0) - { - nprintf(circuit, "Name is %s\r", user->name); - return TRUE; - } - - strnew(&user->name, f1); - nprintf(circuit, "Name set to %s\r", user->name); - upduser(user); - user_tell(user, id_user); - return TRUE; - - case 'p' : show_circuits(circuit, Buffer[3]); return TRUE; - - case 'q' : - - f1 = &Buffer[2]; - - while ((*f1 != 0) && (*f1 == ' ')) - f1++; - - if (*f1 == 0) - { - nprintf(circuit, "QTH is %s\r", user->qth); - return TRUE; - } - - strnew(&user->qth, f1); - - nprintf(circuit, "QTH set to %s\r", user->qth); - upduser(user); - user_tell(user, id_user); - return TRUE; - - case 's' : - strcat(Buffer, "\r"); - f1 = strlop(Buffer, ' '); // To. - if (!f1) break; - f2 = strlop(f1, ' '); // Text to send. - if (!f2) break; - _strupr(f1); - su = user_find(f1, NULL); - - if (!su) - { - nputs(circuit, "*** That user is not logged in.\r"); - return TRUE; - } - - // Send to the desired user only. - - if (su->circuit->rtcflags & p_user) - text_tellu(user, f2, f1, o_one); - else - text_xmit(user, su, f2); - - return TRUE; - - case 't' : - f1 = strlop(Buffer, ' '); - if (f1) - { - if (topic_chg(user, f1)) - { - nprintf(circuit, "Switched to Topic %s\r", user->topic->name); - show_users_in_topic(circuit); - - // Tell all link circuits about the change of topic. - - for (c = circuit_hd; c; c = c->next) - { - if (c->rtcflags & p_linked) - topic_xmit(user, c); - } - } - else - { - // Already in topic - - nprintf(circuit, "You were already in Topic %s\r", user->topic->name); - } - } - else - show_topics(circuit); - return TRUE; - - case 'u' : show_users(circuit); return TRUE; - - default : break; - } - - saywhat(circuit); - return TRUE; -} - -void makelinks(void) -{ - LINK *link; - ChatCIRCUIT *circuit; - - // Make the links. Called every 10 seconds - - // Make sure previous link has completed or failed - - if (RunningConnectScript) - { - // Make sure Connect Script isn't taking too long - - if (time(NULL) - RunningConnectScript < 30) - return; - - // Running too long - close it - - for (circuit = circuit_hd; circuit; circuit = circuit->next) - { - // Find the link - - if (circuit->rtcflags & (p_linkini)) - { - link = circuit->u.link; - link->flags = p_linkfailed; - RunningConnectScript = 0; - link->scriptRunning = 0; // so it doesn't get reentered - Logprintf(LOG_CHAT, circuit, '|', "Connect to %s timed out", circuit->Callsign); - - Disconnect(circuit->BPQStream); - } - } - RunningConnectScript = 0; - } - - for (link = link_hd; link; link = link->next) - { - // Is this link already established? - - if (link->flags & (p_linked | p_linkini)) - continue; - - // Already linked through some other node? - // If so, making this link would create a loop. - - if (node_find(link->call)) - continue; - - // Fire up the process to handle this link. - - if (link->delay == 0) - { - link->flags = p_linkini; - link->delay = 12; // 2 mins - chat_link_out(link); - return; // One at a time - } - else - link->delay--; - } -} - -VOID node_close() -{ - // Close all Node-Node Links - - ChatCIRCUIT *circuit; - - for (circuit = circuit_hd; circuit; circuit = circuit->next) - { - if (circuit->rtcflags & (p_linked | p_linkini | p_linkwait)) - Disconnect(circuit->BPQStream); - } -} - -// Send Keepalives to all connected nodes - -static void node_keepalive() -{ - ChatCIRCUIT *circuit; - - NeedStatus = TRUE; // Send Report to Monitor - - if (user_hd) // Any Users? - { - for (circuit = circuit_hd; circuit; circuit = circuit->next) - { - if (circuit->rtcflags & p_linked && circuit->u.link) - { - nprintf(circuit, "%c%c%s %s %s\r", FORMAT, id_keepalive, OurNode, circuit->u.link->call, Verstring); - circuit->u.link->timePollSent = time(NULL); // Also acts as poll - } - } - } - else - { - // No users. Close links - - node_close(); - } -} - -VOID ChatTimer() -{ - // Entered every 10 seconds - - int i = 0; - ChatCIRCUIT *c; - -#ifndef LINBPQ - int len; - CHATNODE *node; - TOPIC *topic; - char Msg[256]; -#endif - USER *user; - time_t NOW = time(NULL); - - GetSemaphore(&ChatSemaphore, 0); - - if (NeedStatus) - { - NeedStatus = FALSE; - SendChatLinkStatus(); - } - -#ifndef LINBPQ - - ClearDebugWindow(); - - WritetoDebugWindow("Chat Nodes\r\n", 12); - - for (node = node_hd; node; node = node->next) - { - len = sprintf_s(Msg, sizeof(Msg), "%s Version %s Count %d\r\n", - node->call, node->Version, node->refcnt); - WritetoDebugWindow(Msg, len); - - i++; - } - - SetDlgItemInt(hWnd, IDC_NODES, i, FALSE); - - WritetoDebugWindow("Chat Links\r\n", 12); - - i = 0; - for (c = circuit_hd; c; c = c->next) - { - if (c->rtcflags & p_linked) - { - char buff[1000]; - int ptr; - CT * ct; - ptr = sprintf_s(buff, sizeof(buff), "%s Topics: ", c->u.user->call); - - if (c->topic) - { - for (ct = c->topic; ct; ct = ct->next) - { - ptr+= sprintf_s(&buff[ptr], sizeof(buff) - ptr, "%s ", ct->topic->name); - } - } - WritetoDebugWindow(buff, ptr); - WritetoDebugWindow("\r\n", 2); - - i++; - } - } - - SetDlgItemInt(hWnd, IDC_LINKS, i, FALSE); - - WritetoDebugWindow("Chat Topics\r\n", 12); - - i = 0; - for (topic = topic_hd; topic; topic = topic->next) - { - len = sprintf_s(Msg, sizeof(Msg), "%s %d\r\n", topic->name, topic->refcnt); - WritetoDebugWindow(Msg, len); - i++; - } - - WritetoDebugWindow("Chat Users\r\n", 12); - - i = 0; - for (user = user_hd; user; user = user->next) - { - len = sprintf_s(Msg, sizeof(Msg), "%s Topic %s\r\n", user->call, - (user->topic) ? user->topic->name : "** Missing Topic **"); - WritetoDebugWindow(Msg, len); - i++; - - if (user->circuit && user->circuit->rtcflags & p_user) // Local User - { - time_t Idle = NOW - user->lastmsgtime; - - if (Idle > 7200) - { - nprintf(user->circuit, "*** Disconnected - Idle time exceeded\r"); - Sleep(1000); - - if (user->circuit->BPQStream < 0) - { - CloseConsole(user->circuit->BPQStream); - break; - } - else - { - Disconnect(user->circuit->BPQStream); - break; - } - } - - if ((user->rtflags & u_keepalive) && (NOW - user->lastsendtime) > 600) - { - nprintf(user->circuit, "Chat Keepalive\r"); - user->lastsendtime = NOW; - } - } - } - - SetDlgItemInt(hWnd, IDC_USERS, i, FALSE); - -#else - - for (user = user_hd; user; user = user->next) - { - if (user->circuit && user->circuit->rtcflags & p_user) // Local User - { - if ((NOW - user->lastmsgtime) > 7200) - { - nprintf(user->circuit, "*** Disconnected - Idle time exceeded\r"); - Sleep(1000); - - if (user->circuit->BPQStream < 0) - { - CloseConsole(user->circuit->BPQStream); - break; - } - else - { - Disconnect(user->circuit->BPQStream); - break; - } - } - - if ((user->rtflags & u_keepalive) && (NOW - user->lastsendtime) > 600) - { - nprintf(user->circuit, "Chat Keepalive\r"); - user->lastsendtime = NOW; - } - } - } - -#endif - - // if no message on a Node-Node link, send poll - - for (c = circuit_hd; c; c = c->next) - { - if (c->rtcflags & p_linked && c->u.link) - { - time_t Now = time(NULL); - LINK * Link = c->u.link; - - if (Now - Link->lastMsgReceived > 60) - { - // if we have a poll outstanding for ? 30 secs close link - // but check other end can handle polls - - if (Link->supportsPolls && Link->timePollSent && Now - Link->timePollSent > 30) - { - Logprintf(LOG_CHAT, c, '|', "%s No Poll Response for %d Secs - Dropping Link", - c->Callsign, Now - Link->timePollSent); - - Disconnect(c->BPQStream); - continue; - } - - Link->timePollSent = Now; - nprintf(c, "%c%c%s %s\r", FORMAT, id_poll, OurNode, Link->call); - } - } - } - - ChatTmr++; - - if (user_hd) // Any Users? - makelinks(); - - if (ChatTmr > 60) // 10 Mins - { - ChatTmr = 1; - node_keepalive(); - } - - FreeSemaphore(&ChatSemaphore); - - if (NeedINFO) - { - NeedINFO--; - - if (NeedINFO == 0) - { - // Send INFO to Chatmap - - char Msg[500]; - int len; - - NeedINFO = 360; // Send Every Hour - - if (Position[0]) - { - len = sprintf(Msg, "INFO %s|%s|%d|\r", Position, PopupText, PopupMode); - - if (len < 256) - Send_MON_Datagram(Msg, len); - } - } - } -} - -VOID FreeChatMemory() -{ - removelinks(); - removeknown(); -} - -// Find a call in the known node list. - -KNOWNNODE *knownnode_find(char *call) -{ - KNOWNNODE *node; - - for (node = known_hd; node; node = node->next) - { - if (matchi(node->call, call)) - break; - } - - return node; -} - -// Add a known node. - -static KNOWNNODE *knownnode_add(char *call) -{ - KNOWNNODE *node; - - node = knownnode_find(call); - - if (!node) - { - node = zalloc(sizeof(KNOWNNODE)); - sl_ins_hd(node, known_hd); - node->call = _strdup(call); - } - - node->LastHeard = time(NULL); - return node; -} - -static char UIDEST[10] = "DUMMY"; -static char AXDEST[7]; -static char ChatMYCALL[7]; - -#pragma pack(1) - - -typedef struct _MESSAGEX -{ -// BASIC LINK LEVEL MESSAGE BUFFER LAYOUT - - struct _MESSAGE * CHAIN; - - UCHAR PORT; - USHORT LENGTH; - - UCHAR DEST[7]; - UCHAR ORIGIN[7]; - -// MAY BE UP TO 56 BYTES OF DIGIS - - UCHAR CTL; - UCHAR PID; - UCHAR DATA[256]; - -}MESSAGEX, *PMESSAGEX; - -#pragma pack() - -SOCKET ChatReportSocket = 0; - - -VOID SetupChat() -{ - u_long param=1; - BOOL bcopt=TRUE; - - ConvToAX25(OurNode, ChatMYCALL); - ConvToAX25(UIDEST, AXDEST); - - sprintf(Verstring, "%d.%d.%d.%d", Ver[0], Ver[1], Ver[2], Ver[3]); - - LoadKnown(); - - ChatReportSocket = socket(AF_INET,SOCK_DGRAM,0); - - if (ChatReportSocket == INVALID_SOCKET) - { - Debugprintf("Failed to create Chat Reporting socket"); - ChatReportSocket = 0; - return; - } - - ioctlsocket (ChatReportSocket, FIONBIO, ¶m); - setsockopt (ChatReportSocket, SOL_SOCKET, SO_BROADCAST, (const char FAR *)&bcopt,4); -} - - -VOID Send_MON_Datagram(UCHAR * Msg, DWORD Len) -{ - MESSAGEX AXMSG; - PMESSAGEX AXPTR = &AXMSG; - - if (Len > 256) - { - Debugprintf("Send_MON_Datagram Error Msg = %s Len = %d", Msg, Len); - return; - } - -// ConvToAX25("GM4OAS-5", ChatMYCALL); - - // Block includes the Msg Header (7 bytes), Len Does not! - - memcpy(AXPTR->DEST, AXDEST, 7); - memcpy(AXPTR->ORIGIN, ChatMYCALL, 7); - AXPTR->DEST[6] &= 0x7e; // Clear End of Call - AXPTR->DEST[6] |= 0x80; // set Command Bit - - AXPTR->ORIGIN[6] |= 1; // Set End of Call - AXPTR->CTL = 3; //UI - AXPTR->PID = 0xf0; - memcpy(AXPTR->DATA, Msg, Len); - - SendChatReport(ChatReportSocket, (char *)&AXMSG.DEST, Len + 16); - - return; - -} - -VOID SendChatLinkStatus() -{ - char Msg[256] = {0}; - LINK * link; - int len = 0; - ChatCIRCUIT *circuit; - - if (ChatApplNum == 0) - return; - -// if (AXIPPort == 0) -// return; - - if (ChatMYCALL[0] == 0) - return; - - for (link = link_hd; link; link = link->next) - { - if (link->flags & p_linked) - { - // Verify connection - - for (circuit = circuit_hd; circuit; circuit = circuit->next) - { - if (strcmp(circuit->Callsign, link->alias) == 0) - { - if (circuit->Active == 0) - { - // BPQ Session is dead - Simulate a Disconnect - - circuit->Active = TRUE; // So disconnect will work - Disconnected(circuit->BPQStream); - NeedStatus = TRUE; // Reenter - return; // Link Chain has changed - } - break; - } - } - - if (circuit == 0) - { - // No BPQ Session - is the only answer to restart the node? - - // Logprintf(LOG_DEBUGx, NULL, '!', "Stuck Chat Sesion Detected"); - // Logprintf(LOG_DEBUGx, NULL, '!', "Chat is a mess - forcing a restart"); - // ProgramErrors = 26; - // CheckProgramErrors(); - } - } - - len += sprintf(&Msg[len], "%s %c ", link->call, '0' + link->flags); - - if (len > 240) - break; - } - Msg[len++] = '\r'; - - Send_MON_Datagram(Msg, len); -} - -VOID ClearChatLinkStatus() -{ - LINK * link; - - for (link = link_hd; link; link = link->next) - { - link->flags = 0; - } -} - -BOOL ProcessChatConnectScript(ChatCIRCUIT * conn, char * Buffer, int len) -{ - LINK * link = conn->u.link; - char ** Scripts; - - ChatWriteLogLine(conn, '<', Buffer, len-1, LOG_CHAT); - - Buffer[len] = 0; - _strupr(Buffer); - - Scripts = link->ConnectScript; - - if (strstr(Buffer, "BUSY") || strstr(Buffer, "FAILURE") || - (strstr(Buffer, "DOWNLINK") && strstr(Buffer, "ATTEMPTING") == 0) || - strstr(Buffer, "SORRY") || strstr(Buffer, "INVALID") || strstr(Buffer, "RETRIED") || - strstr(Buffer, "NO CONNECTION TO") || strstr(Buffer, "ERROR - ") || - strstr(Buffer, "UNABLE TO CONNECT") || strstr(Buffer, "DISCONNECTED") || - strstr(Buffer, "FAILED TO CONNECT") || strstr(Buffer, "REJECTED")) - { - // Connect Failed - - link->flags = p_linkfailed; - RunningConnectScript = 0; - link->scriptRunning = 0; // so it doesn't get reentered - Disconnect(conn->BPQStream); - return FALSE; - } - - // The pointer is only updated when we get the connect, so we can tell when the last line is acked - // The first entry is always from Connected event, so don't have to worry about testing entry -1 below - - - if (link->RTLSent) - { - RunningConnectScript = 0; - link->scriptRunning = 0; - link->RTLSent = 0; - - if (memcmp(Buffer, "OK", 2) == 0) - { - // Reply to *RTL - - // Make sure node isn't known. There is a window here that could cause a loop - - if (node_find(conn->u.link->call)) - { - Logprintf(LOG_CHAT, conn, '|', "Dropping link with %s to prevent a loop", conn->Callsign); - Disconnect(conn->BPQStream); - return FALSE; - } - - conn->u.link->flags = p_linked; - conn->rtcflags = p_linked; - state_tell(conn, conn->FBBReplyChars); - NeedStatus = TRUE; - - return TRUE; - } - - // Some other response to *RTL - disconnect - - Logprintf(LOG_CHAT, conn, '|', "Unexpected Response %s to *RTL - Dropping link", Buffer); - Disconnect(conn->BPQStream); - return FALSE; - - } - - if (strstr(Buffer, " CONNECTED") || strstr(Buffer, "PACLEN") || strstr(Buffer, "IDLETIME") || - strstr(Buffer, "OK") || strstr(Buffer, "###LINK MADE") || strstr(Buffer, "VIRTUAL CIRCUIT ESTABLISHED")) - { - char * Cmd; - -LoopBack: - - Cmd = Scripts[++link->ScriptIndex]; - - // Only Check until script is finished - - if (Cmd == 0 || link->ScriptIndex >= link->Lines) - { - link->MoreLines = FALSE; - return TRUE; - } - - if (Cmd && (strcmp(Cmd, " ") == 0 || Cmd[0] == ';' || Cmd[0] == '#')) - goto LoopBack; // Blank line - - // Replace \ with # so can send commands starting with # - - if (Cmd[0] == '\\') - { - Cmd[0] = '#'; - nprintf(conn, "%s\r", Cmd); - Cmd[0] = '\\'; // Put \ back in script - } - else - nprintf(conn, "%s\r", Cmd); - - return TRUE; - } - - if (memcmp(Buffer, "[BPQCHATSERVER-", 15) == 0) - { - char * ptr = strchr(Buffer, ']'); - if (ptr) - { - *ptr = 0; - strcpy(conn->FBBReplyChars, &Buffer[15]); - } - else - conn->FBBReplyChars[0] = 0; - - // Connected - Send *RTL - - nputs(conn, "*RTL\r"); // Log in to the remote RT system. - - conn->u.link->timePollSent = time(NULL); // Keepalive is a poll - nprintf(conn, "%c%c%s %s %s\r", FORMAT, id_keepalive, OurNode, conn->u.link->call, Verstring); - link->RTLSent = 1; - conn->u.link->lastMsgSent = time(NULL); - - return TRUE; - } - -// Anthing else could be ctext. etc. Ignore - - return TRUE; -} - - - -#ifdef LINBPQ - -// LINCHAT specific code - -extern struct SEM OutputSEM; - -static config_t cfg; -static config_setting_t * group; - -extern char pgm[256]; - -char ChatSYSOPCall[50] = ""; - -VOID ChatSendWelcomeMsg(int Stream, ChatCIRCUIT * conn, struct UserInfo * user); - - -int ChatConnected(int Stream) -{ - int n; - ChatCIRCUIT * conn; - struct UserInfo * user = NULL; - char callsign[10]; - int port, paclen, maxframe, l4window; - char ConnectedMsg[] = "*** CONNECTED "; - char Msg[100]; - LINK *link; - KNOWNNODE *node; - - for (n = 0; n < NumberofChatStreams; n++) - { - conn = &ChatConnections[n]; - - if (Stream == conn->BPQStream) - { - if (conn->Active) - { - // Probably an outgoing connect - - if (conn->rtcflags == p_linkini) - { - conn->paclen = chatPaclen; - - // Run first line of connect script - - ProcessChatConnectScript(conn, ConnectedMsg, 15); -// nprintf(conn, "c %s\r", conn->u.link->call); - } - return 0; - } - - memset(conn, 0, sizeof(ChatCIRCUIT)); // Clear everything - conn->Active = TRUE; - conn->BPQStream = Stream; - - conn->Secure_Session = GetConnectionInfo(Stream, callsign, - &port, &conn->SessType, &paclen, &maxframe, &l4window); - - if (paclen == 0) - paclen = 256; - - if (paclen > chatPaclen) - paclen = chatPaclen; - - conn->paclen = paclen; - - strlop(callsign, ' '); // Remove trailing spaces - - memcpy(conn->Callsign, callsign, 10); - - strlop(callsign, '-'); // Remove any SSID - - user = zalloc(sizeof(struct UserInfo)); - - strcpy(user->Call, callsign); - - conn->UserPointer = user; - - n=sprintf_s(Msg, sizeof(Msg), "Incoming Connect from %s", user->Call); - - // Send SID and Prompt - - ChatWriteLogLine(conn, '|',Msg, n, LOG_CHAT); - conn->Flags |= CHATMODE; - - nprintf(conn, ChatSID, Ver[0], Ver[1], Ver[2], Ver[3]); - - // See if from a defined node - - for (link = link_hd; link; link = link->next) - { - if (matchi(conn->Callsign, link->call)) - { - conn->rtcflags = p_linkwait; - return 0; // Wait for *RTL - } - } - - // See if from a previously known node - - node = knownnode_find(conn->Callsign); - - if (node) - { - // A node is trying to link, but we don't have it defined - close - - Logprintf(LOG_CHAT, conn, '!', "Node %s connected, but is not defined as a Node - closing", - conn->Callsign); - - nprintf(conn, "Node %s does not have %s defined as a node to link to - closing.\r", - OurNode, conn->Callsign); - - ChatFlush(conn); - - Sleep(500); - conn->rtcflags = p_nil; - - Disconnect(conn->BPQStream); - - return 0; - } - - if (user->Name[0] == 0) - { - char * Name = lookupuser(user->Call); - - if (Name) - { - if (strlen(Name) > 17) - Name[17] = 0; - - strcpy(user->Name, Name); - free(Name); - } - else - { - conn->Flags |= GETTINGUSER; - nputs(conn, NewUserPrompt); - return TRUE; - } - } - - ChatSendWelcomeMsg(Stream, conn, user); - RefreshMainWindow(); - ChatFlush(conn); - - return 0; - } - } - - return 0; -} - -int ChatDisconnected (ChatCIRCUIT * conn) -{ - struct UserInfo * user = NULL; - int Stream = conn->BPQStream; - char Msg[255]; - int len; - - if (conn->Active == FALSE) - return 0; - - ChatClearQueue(conn); - - conn->Active = FALSE; - - if (conn->Flags & CHATMODE) - { - if (conn->Flags & CHATLINK && conn->u.link) - { - // if running connect script, clear script active - - if (conn->u.link->flags & p_linkini) - { - RunningConnectScript = 0; - conn->u.link->scriptRunning = 0; - } - - len=sprintf_s(Msg, sizeof(Msg), "Chat Node %s Disconnected", conn->u.link->call); - ChatWriteLogLine(conn, '|',Msg, len, LOG_CHAT); - link_drop(conn); - - } - else - { - len=sprintf_s(Msg, sizeof(Msg), "Chat User %s Disconnected", conn->Callsign); - ChatWriteLogLine(conn, '|',Msg, len, LOG_CHAT); - - logout(conn); - - } - - conn->Flags = 0; - conn->u.link = NULL; - conn->UserPointer = NULL; - return 0; - } - - return 0; -} - -int ChatDoReceivedData(ChatCIRCUIT * conn) -{ - int count, InputLen; - UINT MsgLen; - int Stream = conn->BPQStream; - struct UserInfo * user; - char * ptr, * ptr2; - char Buffer[10000]; - - - // May have several messages per packet, or message split over packets - - if (conn->InputLen + 1000 > 10000) // Shouldnt have lines longer than this in text mode - conn->InputLen = 0; // discard - - GetMsg(Stream, &conn->InputBuffer[conn->InputLen], &InputLen, &count); - - if (InputLen == 0) return 0; - - conn->Watchdog = 900; // 15 Minutes - conn->InputLen += InputLen; - -loop: - - if (conn->InputLen == 1 && conn->InputBuffer[0] == 0) // Single Null - { - conn->InputLen = 0; - - if (conn->u.user->circuit && conn->u.user->circuit->rtcflags & p_user) // Local User - conn->u.user->lastmsgtime = time(NULL); - - return 0; - } - - ptr = memchr(conn->InputBuffer, '\r', conn->InputLen); - - if (ptr) // CR in buffer - { - user = conn->UserPointer; - - ptr2 = &conn->InputBuffer[conn->InputLen]; - - if (++ptr == ptr2) - { - // Usual Case - single meg in buffer - - if (conn->rtcflags == p_linkini) // Chat Connect - ProcessChatConnectScript(conn, conn->InputBuffer, conn->InputLen); - else - ProcessChatLine(conn, user, conn->InputBuffer, conn->InputLen); - conn->InputLen=0; - } - else - { - // buffer contains more that 1 message - - MsgLen = conn->InputLen - (int)(ptr2-ptr); - - memcpy(Buffer, conn->InputBuffer, MsgLen); - - if (conn->rtcflags == p_linkini) - ProcessChatConnectScript(conn, Buffer, MsgLen); - else - ProcessChatLine(conn, user, Buffer, MsgLen); - - if (*ptr == 0 || *ptr == '\n') - { - /// CR LF or CR Null - - ptr++; - conn->InputLen--; - } - - memmove(conn->InputBuffer, ptr, conn->InputLen-MsgLen); - conn->InputLen -= MsgLen; - - goto loop; - - } - } - return 0; -} - - -int ChatPollStreams() -{ - int state,change; - ChatCIRCUIT * conn; - int n; - struct UserInfo * user = NULL; - char ConnectedMsg[] = "*** CONNECTED "; - - for (n = 0; n < NumberofChatStreams; n++) - { - conn = &ChatConnections[n]; - - SessionState(conn->BPQStream, &state, &change); - - if (change == 1) - { - if (state == 1) // Connected - { - GetSemaphore(&ConSemaphore, 0); - ChatConnected(conn->BPQStream); - FreeSemaphore(&ConSemaphore); - } - else - { - GetSemaphore(&ConSemaphore, 0); - ChatDisconnected(conn); - FreeSemaphore(&ConSemaphore); - } - } - - ChatDoReceivedData(conn); - } - - return 0; -} - - -BOOL GetChatConfig(char * ConfigName) -{ - config_init(&cfg); - - /* Read the file. If there is an error, report it and exit. */ - - if(! config_read_file(&cfg, ConfigName)) - { - fprintf(stderr, "%d - %s\n", - config_error_line(&cfg), config_error_text(&cfg)); - config_destroy(&cfg); - return(EXIT_FAILURE); - } - - group = config_lookup (&cfg, "Chat"); - - if (group == NULL) - return EXIT_FAILURE; - - ChatApplNum = GetIntValue(group, "ApplNum"); - MaxChatStreams = GetIntValue(group, "MaxStreams"); - reportChatEvents = GetIntValue(group, "reportChatEvents"); - chatPaclen = GetIntValue(group, "chatPaclen"); - GetStringValue(group, "OtherChatNodes", OtherNodesList, 1000); - GetStringValue(group, "ChatWelcomeMsg", ChatWelcomeMsg, 1000); - GetStringValue(group, "MapPosition", Position, 81); - GetStringValue(group, "MapPopup", PopupText, 260); - PopupMode = GetIntValue(group, "PopupMode"); - - if (chatPaclen == 0) - chatPaclen = 236; - - if (chatPaclen < 60) - chatPaclen = 60; - - - return EXIT_SUCCESS; -} - -VOID SaveChatConfigFile(char * ConfigName) -{ - config_setting_t *root, *group; - - // Get rid of old config before saving - - config_init(&cfg); - - root = config_root_setting(&cfg); - - group = config_setting_add(root, "Chat", CONFIG_TYPE_GROUP); - - SaveIntValue(group, "ApplNum", ChatApplNum); - SaveIntValue(group, "MaxStreams", MaxChatStreams); - SaveIntValue(group, "reportChatEvents", reportChatEvents); - SaveIntValue(group, "chatPaclen", chatPaclen); - SaveStringValue(group, "OtherChatNodes", OtherNodesList); - SaveStringValue(group, "ChatWelcomeMsg", ChatWelcomeMsg); - - SaveStringValue(group, "MapPosition", Position); - SaveStringValue(group, "MapPopup", PopupText); - SaveIntValue(group, "PopupMode", PopupMode); - - if(! config_write_file(&cfg, ConfigName)) - { - fprintf(stderr, "Error while writing file.\n"); - config_destroy(&cfg); - return; - } - config_destroy(&cfg); -} - -BOOL ChatInit() -{ - char * ptr1 = GetApplCall(ChatApplNum); - char * ptr2; - char * Context; - int i; - ChatCIRCUIT * conn; - - - if (*ptr1 < 0x21) - { - printf("No APPLCALL for Chat APPL\n"); - return FALSE; - } - - memcpy(OurNode, ptr1, 10); - strlop(OurNode, ' '); - - ptr1 = GetApplAlias(ChatApplNum); - memcpy(OurAlias, ptr1,10); - strlop(OurAlias, ' '); - - if (ChatSYSOPCall[0] == 0) - { - strcpy(ChatSYSOPCall, OurNode); - strlop(ChatSYSOPCall, '-'); - } - - sprintf(ChatSignoffMsg, "73 de %s\r", ChatSYSOPCall); - - if (ChatWelcomeMsg[0] == 0) - sprintf(ChatWelcomeMsg, "%s's Chat Server.$WType /h for command summary.$WBringing up links to other nodes.$W" - "This may take a minute or two.$WThe /p command shows what nodes are linked.$W", ChatSYSOPCall); - - ChatApplMask = 1<<(ChatApplNum-1); - - // Set up other nodes list. rtlink messes with the string so pass copy - - // On first run config will have spaces not newlines - - if (strchr(OtherNodesList, '\r')) // Has connect script entries - { - ptr2 = ptr1 = strtok_s(_strdup(OtherNodesList), "\r\n", &Context); - - while (ptr1 && ptr1[0]) - { - rtlink(ptr1); - ptr1 = strtok_s(NULL, "\r\n", &Context); - } - } - else - { - ptr2 = ptr1 = strtok_s(_strdup(OtherNodesList), " ,\r", &Context); - - while (ptr1) - { - rtlink(ptr1); - ptr1 = strtok_s(NULL, " ,\r", &Context); - } - } - - free(ptr2); - - SetupChat(); - - // Allocate Streams - - strcpy(pgm, "CHAT"); - - for (i = 0; i < MaxChatStreams; i++) - { - conn = &ChatConnections[i]; - conn->BPQStream = FindFreeStream(); - - if (conn->BPQStream == 255) break; - - NumberofChatStreams++; - - SetAppl(conn->BPQStream, 3, ChatApplMask); - Disconnect(conn->BPQStream); - } - - strcpy(pgm, "LINBPQ"); - - return TRUE; -} - -#endif - -void ChatFlush(ChatCIRCUIT * conn) -{ - int tosend, len, sent; - - // Try to send data to user. May be stopped by user paging or node flow control - - // UCHAR * OutputQueue; // Messages to user - // int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message - // int OutputGetPointer; // Next byte to send. When Getpointer = Quele Length all is sent - free the buffer and start again. - - // BOOL Paging; // Set if user wants paging - // int LinesSent; // Count when paging - // int PageLen; // Lines per page - - - tosend = conn->OutputQueueLength - conn->OutputGetPointer; - - sent = 0; - - while (tosend > 0) - { - if (TXCount(conn->BPQStream) > 4) - return; // Busy - - if (tosend <= conn->paclen) - len = tosend; - else - len=conn->paclen; - - GetSemaphore(&OutputSEM, 0); - - SendUnbuffered(conn->BPQStream, &conn->OutputQueue[conn->OutputGetPointer], len); - - conn->OutputGetPointer += len; - - FreeSemaphore(&OutputSEM); - - tosend -= len; - sent++; - - if (sent > 4) - return; - } - - // All Sent. Free buffers and reset pointers - - ChatClearQueue(conn); -} - -VOID ChatClearQueue(ChatCIRCUIT * conn) -{ - GetSemaphore(&OutputSEM, 0); - - conn->OutputGetPointer = 0; - conn->OutputQueueLength = 0; - - FreeSemaphore(&OutputSEM); -} - -#ifdef LINBPQ -void ChatTrytoSend() -{ - // call Flush on any connected streams with queued data - - ChatCIRCUIT * conn; - - int n; - - for (n = 0; n < NumberofChatStreams; n++) - { - conn = &ChatConnections[n]; - - if (conn->Active == TRUE) - ChatFlush(conn); - } -} - -VOID CloseChat() -{ - int BPQStream, n; - - for (n = 0; n < NumberofChatStreams; n++) - { - BPQStream = ChatConnections[n].BPQStream; - - if (BPQStream) - { - SetAppl(BPQStream, 0, 0); - Disconnect(BPQStream); - DeallocateStream(BPQStream); - } - } - - ClearChatLinkStatus(); - SendChatLinkStatus(); - Sleep(1000); // A bit of time for links to close - SendChatLinkStatus(); // Send again to reduce chance of being missed - FreeChatMemory(); -} - -VOID SendChatReport(SOCKET ChatReportSocket, char * buff, int txlen) -{ - unsigned short int crc = compute_crc(buff, txlen); - - crc ^= 0xffff; - - buff[txlen++] = (crc&0xff); - buff[txlen++] = (crc>>8); - - sendto(ChatReportSocket, buff, txlen, 0, (LPSOCKADDR)&Chatreportdest, sizeof(Chatreportdest)); - -} - -#endif - - -#ifndef WIN32 -#define INVALID_HANDLE_VALUE (void *)-1 -#endif - -static FILE * LogHandle[4] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; - -static time_t LastLogTime[4] = {0, 0, 0, 0}; - -static char FilesNames[4][100] = {"", "", "", ""}; - -static char * Logs[4] = {"BBS", "CHAT", "TCP", "DEBUG"}; - - -BOOL ChatOpenLogfile(int Flags) -{ - UCHAR FN[MAX_PATH]; - time_t LT; - struct tm * tm; - - LT = time(NULL); - tm = gmtime(<); - - sprintf(FN,"%s/logs/log_%02d%02d%02d_%s.txt", GetLogDirectory(), tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, Logs[Flags]); - - LogHandle[Flags] = fopen(FN, "ab"); - -#ifndef WIN32 - - if (strcmp(FN, &FilesNames[Flags][0])) - { - UCHAR SYMLINK[MAX_PATH]; - - sprintf(SYMLINK,"%s/logLatest_%s.txt", GetBPQDirectory(), Logs[Flags]); - unlink(SYMLINK); - strcpy(&FilesNames[Flags][0], FN); - symlink(FN, SYMLINK); - } - -#endif - - return (LogHandle[Flags] != NULL); -} - -void ChatWriteLogLine(ChatCIRCUIT * conn, int Flag, char * Msg, int MsgLen, int Flags) -{ - char CRLF[2] = {0x0d,0x0a}; - struct tm * tm; - char Stamp[20]; - time_t T; - -#ifndef LINBPQ - - if (hMonitor) - { - if (Flags == LOG_CHAT) - { - WritetoMonitorWindow((char *)&Flag, 1); - - if (conn && conn->Callsign[0]) - { - char call[20]; - sprintf(call, "%s ", conn->Callsign); - WritetoMonitorWindow(call, 10); - } - else - WritetoMonitorWindow(" ", 10); - - WritetoMonitorWindow(Msg, MsgLen); - if (Msg[MsgLen-1] != '\r') - WritetoMonitorWindow(CRLF , 1); - } - else if (Flags == LOG_DEBUGx) - { - WritetoMonitorWindow((char *)&Flag, 1); - WritetoMonitorWindow(Msg, MsgLen); - WritetoMonitorWindow(CRLF , 1); - } - - } - -#endif - - if (Flags == LOG_CHAT && !LogCHAT) - return; - - if (LogHandle[Flags] == INVALID_HANDLE_VALUE) ChatOpenLogfile(Flags); - - if (LogHandle[Flags] == INVALID_HANDLE_VALUE) return; - - T = time(NULL); - tm = gmtime(&T); - - sprintf(Stamp,"%02d%02d%02d %02d:%02d:%02d %c", - tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, Flag); - - fwrite(Stamp, 1, strlen(Stamp), LogHandle[Flags]); - - if (conn && conn->Callsign[0]) - { - char call[20]; - sprintf(call, "%s ", conn->Callsign); - fwrite(call, 1, 10, LogHandle[Flags]); - } - else - fwrite(" ", 1, 10, LogHandle[Flags]); - - fwrite(Msg, 1, MsgLen, LogHandle[Flags]); - - if (Flags == LOG_CHAT && Msg[MsgLen-1] == '\r') - fwrite(&CRLF[1], 1, 1, LogHandle[Flags]); - else - fwrite(CRLF, 1, 2, LogHandle[Flags]); - - fclose(LogHandle[Flags]); - LogHandle[Flags] = INVALID_HANDLE_VALUE; -} - - +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + + +#define _CRT_SECURE_NO_DEPRECATE + +#pragma data_seg("_BPQDATA") + +#define LIBCONFIG_STATIC +#include "libconfig.h" + + +#ifdef LINBPQ +#include "cheaders.h" +#endif + +#include "bpqchat.h" + +#ifndef WIN32 + +iconv_t link_toUTF8 = NULL; + + +BOOL RunEventProgram(char * Program, char * Param); + +#endif + +BOOL ProcessChatConnectScript(ChatCIRCUIT * conn, char * Buffer, int len); +VOID ChatClearQueue(ChatCIRCUIT * conn); +VOID ChatFlush(ChatCIRCUIT * conn); +VOID APIENTRY SendChatReport(SOCKET ChatReportSocket, char * buff, int txlen); +unsigned short int compute_crc(unsigned char *buf,int len); +char * ReadInfoFile(char * File); +void ChatWriteLogLine(ChatCIRCUIT * conn, int Flag, char * Msg, int MsgLen, int Flags); +extern struct SEM ChatSemaphore; +UCHAR * APIENTRY GetLogDirectory(); +char * APIENTRY GetBPQDirectory(); +VOID WriteMiniDump(); + +extern SOCKADDR_IN Chatreportdest; + +char OurNode[10]; +char OurAlias[10]; + +#define MaxSockets 64 + +int MaxChatStreams=0; +ChatCIRCUIT ChatConnections[MaxSockets+1]; + +ULONG ChatApplMask; + +int NumberofChatStreams=0; + +char ChatSignoffMsg[100]; + +char OtherNodesList[1000]; +char ChatWelcomeMsg[1000]; + +char Position[81] = ""; +char PopupText[260] = ""; +int PopupMode = 0; +int chatPaclen = 236; + +int reportChatEvents = 0; + +char RtKnown[MAX_PATH] = "RTKnown.txt"; +char RtUsr[MAX_PATH] = "STUsers.txt"; +char RtUsrTemp[MAX_PATH] = "STUsers.tmp"; + +int AXIPPort = 0; + +ChatCIRCUIT *circuit_hd = NULL; // This is a chain of RT circuits. There may be others + +CHATNODE *node_hd = NULL; // Nodes + +LINK *link_hd = NULL; // Nodes we link to +TOPIC *topic_hd = NULL; + +USER *user_hd = NULL; + +KNOWNNODE * known_hd = NULL; + +int ChatTmr = 0; + +BOOL NeedStatus = FALSE; + + +char Verstring[80]; + +static void node_dec(CHATNODE *node); +static KNOWNNODE *knownnode_add(char *call); +VOID SendChatLinkStatus(); +char * lookupuser(char * call); +VOID ChatSendWelcomeMsg(int Stream, ChatCIRCUIT * conn, struct UserInfo * user); + +static int AutoColours[20] = {0, 4, 9, 11, 13, 16, 17, 42, 45, 50, 61, 64, 66, 72, 81, 84, 85, 86, 87, 89}; + +#define MaxSockets 64 + +extern struct SEM OutputSEM; + +int NeedINFO = 1; // Send INFO Msg after 10 Secs +time_t RunningConnectScript = 0; + +//#undef free +//#define free(p) + + +struct HistoryRec * History = NULL; +int HistoryCount = 0; + + +typedef int (WINAPI FAR *FARPROCX)(); +extern FARPROCX pRunEventProgram; + +int AddtoHistory(struct user_t * user, char * text) +{ + struct HistoryRec * Rec; + struct HistoryRec * ptr; + int n = 1; + char buf[2048]; + char Stamp[16]; + struct tm * tm; + time_t Now = time(NULL); + + // Don't want to grow indefinitely and fill memory. We only allow display up to 24 hours back, so if first record is older that that + // remove and reuse it + + if (History && History->Time < Now - 86400) + { + Rec = History; + History = Rec->next; // Remove from front of chain + } + else + Rec = malloc(sizeof (struct HistoryRec)); + + memset(Rec, 0, sizeof (struct HistoryRec)); + + tm = gmtime(&Now); + + if (strlen(text) + strlen(user->name) + strlen(user->call) > 2000) + return 0; // Shouldn't be that long, but protect buffer + + sprintf(Stamp,"%02d:%02d ", tm->tm_hour, tm->tm_min); + sprintf(buf, "%s%-6.6s %s %c %s\r", Stamp, user->call, user->name, ':', text); + + Rec->Time = Now; + + Rec->Topic = _strdup(user->topic->name); + Rec->Message = _strdup(buf); + + if (History == NULL) + History = Rec; + + else + { + ptr = History; + + while (ptr && ptr->next) + { + n++; + ptr = ptr->next; + } + + n++; + ptr->next = Rec; + } + + return n; +} + + + +int ChatIsUTF8(unsigned char *ptr, int len) +{ + int n; + unsigned char * cpt = ptr; + + // This is simpler than the Term version, as it only handles complete lines of text, so cant get split sequences + + cpt--; + + for (n = 0; n < len; n++) + { + cpt++; + + if (*cpt < 128) + continue; + + if ((*cpt & 0xF8) == 0xF0) + { // start of 4-byte sequence + if (((*(cpt + 1) & 0xC0) == 0x80) + && ((*(cpt + 2) & 0xC0) == 0x80) + && ((*(cpt + 3) & 0xC0) == 0x80)) + { + cpt += 3; + n += 3; + continue; + } + return FALSE; + } + else if ((*cpt & 0xF0) == 0xE0) + { // start of 3-byte sequence + if (((*(cpt + 1) & 0xC0) == 0x80) + && ((*(cpt + 2) & 0xC0) == 0x80)) + { + cpt += 2; + n += 2; + continue; + } + return FALSE; + } + else if ((*cpt & 0xE0) == 0xC0) + { // start of 2-byte sequence + if ((*(cpt + 1) & 0xC0) == 0x80) + { + cpt++; + n++; + continue; + } + return FALSE; + } + return FALSE; + } + + return TRUE; +} + +#ifndef LINBPQ + +char * strlop(char * buf, char delim) +{ + // Terminate buf at delim, and return rest of string + + char * ptr = strchr(buf, delim); + + if (ptr == NULL) return NULL; + + *(ptr)++=0; + + return ptr; +} + + +VOID * _zalloc(size_t len) +{ + // ?? malloc and clear + + void * ptr; + + ptr=malloc(len); + memset(ptr, 0, len); + + return ptr; +} + + +VOID * _zalloc_dbg(int len, int type, char * file, int line) +{ + // ?? malloc and clear + + void * ptr; + + ptr=_malloc_dbg(len, type, file, line); + + if (ptr == NULL) + CriticalErrorHandler("malloc failed"); + + memset(ptr, 0, len); + + return ptr; +} + +#endif + +VOID __cdecl nprintf(ChatCIRCUIT * conn, const char * format, ...) + +{ + // seems to be printf to a socket + + char buff[65536]; + va_list(arglist); + + va_start(arglist, format); + vsnprintf(buff, sizeof(buff), format, arglist); + + nputs(conn, buff); +} + + +VOID nputc(ChatCIRCUIT * conn, char chr) +{ + // Seems to send chr to socket + + ChatWriteLogLine(conn, '>',&chr, 1, LOG_CHAT); + ChatQueueMsg(conn, &chr, 1); +} + +VOID nputs(ChatCIRCUIT * conn, char * buf) +{ + // Seems to send buf to socket + + ChatQueueMsg(conn, buf, (int)strlen(buf)); + + if (*buf == 0x1b) + buf += 2; // Colour Escape + + ChatWriteLogLine(conn, '>',buf, (int)strlen(buf), LOG_CHAT); +} + +int ChatQueueMsg(ChatCIRCUIT * conn, char * msg, int len) +{ + // Add Message to queue for this connection + + if (conn->rtcflags & p_linked) + conn->u.link->lastMsgReceived = time(NULL); + + // UCHAR * OutputQueue; // Messages to user + // int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message + // int OutputGetPointer; // Next byte to send. When Getpointer = Queue Length all is sent - free the buffer and start again. + + // Create or extend buffer + + GetSemaphore(&OutputSEM, 0); + + while (conn->OutputQueueLength + len > conn->OutputQueueSize) + { + // Extend Queue + + conn->OutputQueueSize += 4096; + conn->OutputQueue = realloc(conn->OutputQueue, conn->OutputQueueSize); + } + + memcpy(&conn->OutputQueue[conn->OutputQueueLength], msg, len); + conn->OutputQueueLength += len; + + FreeSemaphore(&OutputSEM); + + return len; +} + +VOID ChatSendWelcomeMsg(int Stream, ChatCIRCUIT * conn, struct UserInfo * user) +{ + if (!rtloginu (conn, TRUE)) + { + // Already connected - close + + ChatFlush(conn); + Sleep(1000); + Disconnect(conn->BPQStream); + } + return; + +} + +VOID ChatExpandAndSendMessage(ChatCIRCUIT * conn, char * Msg, int LOG) +{ + char NewMessage[10000]; + char * OldP = Msg; + char * NewP = NewMessage; + char * ptr, * pptr; + int len; + char Dollar[] = "$"; + char CR[] = "\r"; + int Msgs = 0, Unread = 0; + + + ptr = strchr(OldP, '$'); + + while (ptr) + { + len = (int)(ptr - OldP); // Chars before $ + memcpy(NewP, OldP, len); + NewP += len; + + switch (*++ptr) + { + case 'I': // First name of the connected user. + + pptr = conn->UserPointer->Name; + break; + + + case 'U': // Callsign of the connected user. + + pptr = conn->UserPointer->Call; + break; + + case 'W': // Inserts a carriage return. + + pptr = CR; + break; + + break; + + default: + + pptr = Dollar; // Just Copy $ + } + + len = (int)strlen(pptr); + memcpy(NewP, pptr, len); + NewP += len; + + OldP = ++ptr; + ptr = strchr(OldP, '$'); + } + + strcpy(NewP, OldP); + + len = RemoveLF(NewMessage, (int)strlen(NewMessage)); + + ChatWriteLogLine(conn, '>', NewMessage, len, LOG); + ChatQueueMsg(conn, NewMessage, len); +} + + + +void chat_link_out (LINK *link) +{ + int n, p; + ChatCIRCUIT * conn; + char Msg[80]; + + for (n = NumberofChatStreams-1; n >= 0 ; n--) + { + conn = &ChatConnections[n]; + + if (conn->Active == FALSE) + { + p = conn->BPQStream; + memset(conn, 0, sizeof(ChatCIRCUIT)); // Clear everything + conn->BPQStream = p; + + conn->Active = TRUE; + circuit_new(conn, p_linkini); + conn->u.link = link; + conn->Flags = CHATMODE | CHATLINK; + + n=sprintf_s(Msg, sizeof(Msg), "Connecting to Chat Node %s", conn->u.link->alias); + + strcpy(conn->Callsign, conn->u.link->alias); + + ChatWriteLogLine(conn, '|',Msg, n, LOG_CHAT); + + link->ScriptIndex = -1; + RunningConnectScript = time(NULL); + link->MoreLines = TRUE; + link->scriptRunning = TRUE; + link->RTLSent = 0; + + ConnectUsingAppl(conn->BPQStream, ChatApplMask); + + // Connected Event will trigger connect to remote system + + return; + } + } + return; +} + + +VOID saywhat(ChatCIRCUIT *circuit) +{ + nputs(circuit, "Invalid Command\r"); +} + +VOID saydone(ChatCIRCUIT *circuit) +{ + nputs(circuit, "Ok\r"); +} + +VOID strnew(char ** new, char *f1) +{ + // seems to allocate a new string, and copy the old one to it + // how is this different to strdup?? + + *new = _strdup(f1); +} + +#define sl_ins_hd(link, hd) \ + if (hd == NULL)\ + hd=link;\ + else\ + {\ + link->next=hd->next;\ + hd->next=link;\ + } + +BOOL matchi(char * p1, char * p2) +{ + // Return TRUE is strings match + + if (_stricmp(p1, p2)) + return FALSE; + else + return TRUE; +} + + +VOID ProcessChatLine(ChatCIRCUIT * conn, struct UserInfo * user, char* OrigBuffer, int len) +{ + ChatCIRCUIT *c; + char * Buffer = OrigBuffer; + WCHAR BufferW[65536]; + UCHAR BufferB[65536]; + + // Sanity Check + + if (len > 32768) + return; + + // Convert to UTF8 if not already in UTF-8 + + if (len == 73 && memcmp(&OrigBuffer[40], " ", 20) == 0) + { + // Chat Signon Message. If Topic is present, switch to it + + char * Context; + char * Appl; + char * topic; + + Appl = strtok_s(OrigBuffer, " ,\r", &Context); + topic = strtok_s(NULL, " ,\r", &Context); + + if (topic == NULL) + return; // Just Chat + + // Have a Topic + + if (conn->Flags & GETTINGUSER) + { + // Need to log in before switching topic, so Give a dummy name here + + conn->Flags &= ~GETTINGUSER; + strcpy(user->Name, "?_name"); + ChatSendWelcomeMsg(conn->BPQStream, conn, user); + } + + OrigBuffer[40] = 0; + sprintf(&OrigBuffer[40],"/t %s\r", topic); + strcpy(OrigBuffer, &OrigBuffer[40]); + len = (int)strlen(OrigBuffer); + } + else + { + // Normal input + + if (conn->Flags & GETTINGUSER) + { + // Check not getting *RTL in response to Name prompt + + if (memcmp(Buffer, "*RTL", 4) == 0) + { + // Other end thinks this is a node-node link + + Logprintf(LOG_CHAT, conn, '!', "Station %s trying to start Node Protocol, but not defined as a Node", + conn->Callsign); + + knownnode_add(conn->Callsign); // So it won't happen again + + Disconnect(conn->BPQStream); + return; + } + + conn->Flags &= ~GETTINGUSER; + memcpy(user->Name, Buffer, len-1); + ChatSendWelcomeMsg(conn->BPQStream, conn, user); + + return; + } + } + + if (ChatIsUTF8(OrigBuffer, len) == FALSE) + { + // With Windows it is simple - convert using current codepage + // I think the only reliable way is to convert to unicode and back + +#ifdef WIN32 + + int wlen; + + wlen = MultiByteToWideChar(CP_ACP, 0, Buffer, len, BufferW, 65536); + len = WideCharToMultiByte(CP_UTF8, 0, BufferW, wlen, BufferB, 63336, NULL, NULL); + Buffer = BufferB; + +#else + size_t left = 65536; + size_t clen = len; + + UCHAR * BufferBP = BufferB; + struct user_t * icu = conn->u.user; + + if (conn->rtcflags & p_user) + { + if (icu->iconv_toUTF8 == NULL) + { + icu->iconv_toUTF8 = iconv_open("UTF-8//IGNORE", icu->Codepage); + + if (icu->iconv_toUTF8 == (iconv_t)-1) + icu->iconv_toUTF8 = iconv_open("UTF-8//IGNORE", "CP1252"); + } + + iconv(icu->iconv_toUTF8, NULL, NULL, NULL, NULL); // Reset State Machine + iconv(icu->iconv_toUTF8, &Buffer, &clen, (char ** __restrict__)&BufferBP, &left); + } + else + { + if (link_toUTF8 == NULL) + link_toUTF8 = iconv_open("UTF-8//IGNORE", "CP1252"); + + iconv(link_toUTF8, NULL, NULL, NULL, NULL); // Reset State Machine + iconv(link_toUTF8, &Buffer, &clen, (char ** __restrict__)&BufferBP, &left); + } + len = 65536 - left; + Buffer = BufferB; + +#endif + + } + ChatWriteLogLine(conn, '<',Buffer, len, LOG_CHAT); + + + Buffer[len] = 0; + + strlop(Buffer, '\r'); + + if (conn->rtcflags == p_linkwait) + { + //waiting for *RTL + + if (memcmp(Buffer, "*RTL", 4) == 0) + { + // Node - Node Connect + + if (rtloginl (conn, conn->Callsign)) + { + // Accepted + + conn->Flags |= CHATLINK; + return; + } + else + { + // Connection refused. rtlogin1 has sent error message and closed link + + return; + } + } + + if (Buffer[0] == '[' && Buffer[len-2] == ']') // SID + return; + + nprintf(conn, "Unexpected Message on Chat Node-Node Link - Disconnecting\r"); + ChatFlush(conn); + Sleep(500); + conn->rtcflags = p_nil; + + Disconnect(conn->BPQStream); + return; + } + + if (conn->Flags & CHATLINK) + { +#ifndef LINBPQ + + struct _EXCEPTION_POINTERS exinfo; + + __try + { + chkctl(conn, Buffer, len); + } + + #define EXCEPTMSG "Process Chat Line" + #include "StdExcept.c" + + Debugprintf("CHAT *** Was procesing Chat Node Message %s", Buffer); + Disconnect(conn->BPQStream); + CheckProgramErrors(); + } +#else + chkctl(conn, Buffer, len); +#endif + return; + } + + if(conn->u.user == NULL) + { + // A node link, but not activated yet, or a chat console which has dosconnected + + if (conn->BPQStream != -2) + return; + + // Log console user in + + if (rtloginu (conn, TRUE)) + conn->Flags |= CHATMODE; + + return; + + } + + if ((len <6) && (memcmp(Buffer, "*RTL", 4) == 0)) + { + // Other end thinks this is a node-node link + + Logprintf(LOG_CHAT, conn, '!', "Station %s trying to start Node Protocol, but not defined as a Node", + conn->Callsign); + + knownnode_add(conn->Callsign); // So it won't happen again + + Disconnect(conn->BPQStream); + return; + } + + if (Buffer[0] == '/') + { + // Process Command + + int cmdLen = 0; + char * param = strchr(&Buffer[1], ' '); + + if (param) + cmdLen = param - &Buffer[1]; + else + cmdLen = strlen(&Buffer[1]); + + if (_memicmp(&Buffer[1], "Bye", 1) == 0) + { + SendUnbuffered(conn->BPQStream, ChatSignoffMsg, (int)strlen(ChatSignoffMsg)); + + if (conn->BPQStream < 0) + { + logout(conn); + conn->Flags = 0; + if (conn->BPQStream == -2) + CloseConsole(conn->BPQStream); + } + else + ReturntoNode(conn->BPQStream); + + return; + } + + if (_memicmp(&Buffer[1], "Quit", 4) == 0) + { + SendUnbuffered(conn->BPQStream, ChatSignoffMsg, (int)strlen(ChatSignoffMsg)); + + if (conn->BPQStream < 0) + { + logout(conn); + conn->Flags = 0; + if (conn->BPQStream == -2) + CloseConsole(conn->BPQStream); + } + + else + { + Sleep(1000); + Disconnect(conn->BPQStream); + } + return; + } + + if (cmdLen > 1 && _memicmp(&Buffer[1], "History", cmdLen) == 0) // Accept Hi but not H + { + // Param is number of minutes to go back (max 24 hours) + + struct HistoryRec * ptr = History; + int interval = 0; + time_t start; + int n = HistoryCount; + + if (param) + interval = atoi(param); + + if (interval < 1) + { + nprintf(conn, "Format is /history n, where n is history time in minutes\r"); + conn->u.user->lastsendtime = time(NULL); + return; + } + + if (interval > 1440) + { + nprintf(conn, "History is only held for 24 Hours (1440 Minutes)\r"); + interval = 1440; // Limit to 1 day + } + + start = time(NULL) - (interval * 60); + + // Find first record to send + + while (ptr) + { + if (ptr->Time > start) + break; + n--; + ptr = ptr->next; + } + + // n is records found + + while (ptr) + { + nprintf(conn, ptr->Message); + ptr = ptr->next; + } + + conn->u.user->lastsendtime = time(NULL); + return; + } + + + + if (_memicmp(&Buffer[1], "Keepalive", 4) == 0) + { + conn->u.user->rtflags ^= u_keepalive; + upduser(conn->u.user); + nprintf(conn, "Keepalive is %s\r", (conn->u.user->rtflags & u_keepalive) ? "Enabled" : "Disabled"); + conn->u.user->lastsendtime = time(NULL); + return; + } + if (_memicmp(&Buffer[1], "AUTOCHARSET", 4) == 0) + { + conn->u.user->rtflags ^= u_auto; + upduser(conn->u.user); + nprintf(conn, "Automatic Character set selection is %s\r", (conn->u.user->rtflags & u_auto) ? "Enabled" : "Disabled"); + conn->u.user->lastsendtime = time(NULL); + return; + } + if (_memicmp(&Buffer[1], "UTF-8", 3) == 0) + { + conn->u.user->rtflags ^= u_noUTF8; + upduser(conn->u.user); + nprintf(conn, "Character set is %s\r", (conn->u.user->rtflags & u_noUTF8) ? "8 Bit" : "UTF-8"); + conn->u.user->lastsendtime = time(NULL); + return; + } + + if ((_memicmp(&Buffer[1], "CodePage", 3) == 0) || (_memicmp(&Buffer[1], "CP", 2) == 0)) + { + char * Context; + char * CP = strtok_s(&Buffer[1], " ,\r", &Context); +#ifndef WIN32 + iconv_t temp = NULL; +#else + int temp = 0; + WCHAR TempW[10]; +#endif + CP = strtok_s(NULL, " ,\r", &Context); + + if (CP == NULL || CP[0] == 0) + { +#ifndef WIN32 + if (conn->u.user->Codepage[0]) + nprintf(conn, "Codepage is %s\r", conn->u.user->Codepage); +#else + if (conn->u.user->Codepage) + nprintf(conn, "Codepage is %d\r", conn->u.user->Codepage); +#endif + else + nprintf(conn, "Codepage is not set\r"); + + return; + } + _strupr(CP); + +#ifndef WIN32 + + // Validate Code Page by trying to open an iconv descriptor + + temp = iconv_open("UTF-8", CP); + + if (temp == (iconv_t)-1) + { + nprintf(conn, "Invalid Codepage %s\r", CP); + return; + } + + iconv_close(conn->u.user->iconv_toUTF8); + iconv_close(conn->u.user->iconv_fromUTF8); + + conn->u.user->iconv_toUTF8 = temp; + conn->u.user->iconv_fromUTF8 = iconv_open(CP, "UTF-8"); + + strcpy(conn->u.user->Codepage, CP); + nprintf(conn, "Codepage set to %s\r", conn->u.user->Codepage); +#else + if (CP[0] == 'C') + CP +=2; + + // Validate by trying ot use it + + temp = atoi(CP); + + if (MultiByteToWideChar(temp, 0, "\r", 2, TempW, 10) == 0) + { + int err = GetLastError(); + + if (err == ERROR_INVALID_PARAMETER) + { + nprintf(conn, "Invalid Codepage %d\r", temp); + return; + } + } + + conn->u.user->Codepage = temp; + nprintf(conn, "Codepage set to %d\r", conn->u.user->Codepage); +#endif + upduser(conn->u.user); + + return; + } + + if (_memicmp(&Buffer[1], "Shownames", 4) == 0) + { + conn->u.user->rtflags ^= u_shownames; + upduser(conn->u.user); + nprintf(conn, "Shownames is %s\r", (conn->u.user->rtflags & u_shownames) ? "Enabled" : "Disabled"); + conn->u.user->lastsendtime = time(NULL); + return; + } + + if (_memicmp(&Buffer[1], "Time", 4) == 0) + { + conn->u.user->rtflags ^= u_showtime; + upduser(conn->u.user); + nprintf(conn, "Show Time is %s\r", (conn->u.user->rtflags & u_showtime) ? "Enabled" : "Disabled"); + conn->u.user->lastsendtime = time(NULL); + return; + } + + if (_memicmp(&Buffer[1], "colours", 4) == 0) + { + int i =0; + + while (i < 100) + { + nprintf(conn, "\x1b%c%02d XXXXX\r", i + 10, i); + i++; + if (i == 3) + i++; + } + return; + } + + rt_cmd(conn, Buffer); + + return; + } + + // Send message to all other connected users on same channel + + text_tellu(conn->u.user, Buffer, NULL, o_topic); // To local users. + + HistoryCount = AddtoHistory(conn->u.user, Buffer); + + conn->u.user->lastrealmsgtime = conn->u.user->lastmsgtime = time(NULL); + + // Send to Linked nodes + + for (c = circuit_hd; c; c = c->next) + { + if ((c->rtcflags & p_linked) && c->refcnt && ct_find(c, conn->u.user->topic)) + nprintf(c, "%c%c%s %s %s\r", FORMAT, id_data, OurNode, conn->u.user->call, Buffer); + } +} + +void upduser(USER *user) +{ + FILE *in, *out; + char *c; + char Buffer[2048]; + char *buf = Buffer; + + in = fopen(RtUsr, "r"); + + if (!(in)) + { + in = fopen(RtUsr, "w"); + if (in) + fclose(in); + in = fopen(RtUsr, "r"); + } + + if (!(in)) return; + + out = fopen(RtUsrTemp, "w"); + + if (!(out)) return; + + while(fgets(buf, 128, in)) + { + if (strstr(buf, "*RTL")) // Tidy user database + continue; + + c = strchr(buf, ' '); + if (c) *c = '\0'; + if (!matchi(buf, user->call)) + { + if (c) *c = ' '; + fputs(buf, out); + } + } + +#ifndef WIN32 + fprintf(out, "%s %d %s %s¬%d¬%s\n", user->call, user->rtflags, user->name, user->qth, user->Colour, user->Codepage); +#else + fprintf(out, "%s %d %s %s¬%d¬%d\n", user->call, user->rtflags, user->name, user->qth, user->Colour, user->Codepage); +#endif + fclose(in); + fclose(out); + + remove(RtUsr); + rename(RtUsrTemp, RtUsr); +} + +char * lookupuser(char * call) +{ + FILE *in; + char *flags; + char Buffer[2048]; + char *buf = Buffer; + char * name; + + in = fopen(RtUsr, "r"); + + if (in) + { + while(fgets(buf, 128, in)) + { + strlop(buf, '\n'); + + flags = strlop(buf, ' '); + if (!matchi(buf, call)) continue; + if (!flags) break; + + fclose(in); + name = strlop(flags, ' '); + strlop(name, ' '); + return _strdup(name); + } + fclose(in); + } + + return NULL; +} + + + +void rduser(USER *user) +{ + FILE *in; + char *name, *flags, *qth; + char Buffer[2048]; + char *buf = Buffer; + char * ptr; + + user->name = _strdup("?_name"); + user->qth = _strdup("?_qth"); + + in = fopen(RtUsr, "r"); + + if (in) + { + while(fgets(buf, 128, in)) + { + strlop(buf, '\n'); + + flags = strlop(buf, ' '); + if (!matchi(buf, user->call)) continue; + if (!flags) break; + + name = strlop(flags, ' '); + user->rtflags = atoi(flags); + + qth = strlop(name, ' '); + strnew(&user->name, name); + + if (!qth) break; + + // Colour Code may follow QTH, and Code Page may follow Colour + + ptr = strchr(qth, '¬'); + if (ptr) + { + *ptr++ = 0; + user->Colour = atoi(ptr); + + ptr = strchr(ptr, '¬'); + + if (ptr) + { + *ptr++ = 0; +#ifndef WIN32 + strcpy(user->Codepage, ptr); +#else + user->Codepage = atoi(ptr); +#endif + } + } + + strnew(&user->qth, qth); + break; + } + fclose(in); + +#ifndef WIN32 + + // Open an iconv decriptor for each conversion + + if (user->Codepage[0]) + user->iconv_toUTF8 = iconv_open("UTF-8//IGNORE", user->Codepage); + else + user->iconv_toUTF8 = (iconv_t)-1; + + if (user->iconv_toUTF8 == (iconv_t)-1) + user->iconv_toUTF8 = iconv_open("UTF-8//IGNORE", "CP1252"); + + + if (user->Codepage[0]) + user->iconv_fromUTF8 = iconv_open(user->Codepage, "UTF-8"); + else + user->iconv_fromUTF8 = (iconv_t)-1; + + if (user->iconv_fromUTF8 == (iconv_t)-1) + user->iconv_fromUTF8 = iconv_open("CP1252//IGNORE", "UTF-8"); +#endif + } +} + + +void ReportBadJoin(char * ncall, char *ucall) +{ + Logprintf(LOG_CHAT, NULL, '!', "User %s Join from Node %s but already connected", ucall, ncall); +} + +void ReportBadLeave(char * ncall, char * ucall) +{ + Logprintf(LOG_CHAT, NULL, '!', "Node %s reporting Node %s as a leaving user", ncall, ucall); +} + + +struct DUPINFO DupInfo[MAXDUPS]; + +static BOOL CheckforDups(ChatCIRCUIT * circuit, char * Call, char * Msg) +{ + // Primitive duplicate suppression - see if same call and text reeived in last few secons + + time_t Now = time(NULL); + time_t DupCheck = Now - DUPSECONDS; + int i, saveindex = -1; + + for (i = 0; i < MAXDUPS; i++) + { + if (DupInfo[i].DupTime < DupCheck) + { + // too old - use first if we need to save it + + if (saveindex == -1) + { + saveindex = i; + } + continue; + } + + if ((strcmp(Call, DupInfo[i].DupUser) == 0) && (memcmp(Msg, DupInfo[i].DupText, strlen(DupInfo[i].DupText)) == 0)) + { + // Duplicate, so discard, but save time + + DupInfo[i].DupTime = Now; + Logprintf(LOG_CHAT, circuit, '?', "Duplicate Message From %s %s suppressed", Call, Msg); + + return TRUE; // Duplicate + } + + } + + // Not in list + + if (saveindex == -1) // List is full + saveindex = MAXDUPS - 1; // Stick on end + + DupInfo[saveindex].DupTime = Now; + strcpy(DupInfo[saveindex].DupUser, Call); + + if (strlen(Msg) > 99) + { + memcpy(DupInfo[saveindex].DupText, Msg, 99); + DupInfo[saveindex].DupText[99] = 0; + } + else + strcpy(DupInfo[saveindex].DupText, Msg); + + return FALSE; +} + +void chkctl(ChatCIRCUIT *ckt_from, char * Buffer, int Len) +{ + CHATNODE * node, *ln; + ChatCIRCUIT * ckt_to; + USER * user, * su; + time_t Now = time(NULL); + LINK * Link = ckt_from->u.link; + + char * ncall, * ucall, * f1, * f2, * buf; + int i; + + if (Buffer[FORMAT_O] != FORMAT) return; // Not a control message. + + // Check for corruption + + for (i = 1; i < (Len - 1); i++) + { + if (Buffer[i] < 32) + { + if (Buffer[i] == 9) + { + Buffer[i] = 32; + continue; + } + Debugprintf("Corrupt Chat Link Messages %s", Buffer); + return; + } + } + + buf = _strdup(Buffer + DATA_O); + +// FORMAT and TYPE bytes are followed by node and user callsigns. + + ncall = buf; + ucall = strlop(buf, ' '); + if (!ucall) { free(buf); return; } // Not a control message. + +// There may be at least one field after the node and user callsigns. +// Node leave (id_unlink) has no F1. + + f1 = strlop(ucall, ' '); + strlop(ucall, 9); // some have tabs ?? + +// If the frame came from an unknown node ignore it. +// If the frame came from us ignore it (loop breaking). + + node = node_find(ncall); + if (!node || matchi(ncall, OurNode)) { free(buf); return; } + + if (ckt_from->rtcflags & p_linked) + ckt_from->u.link->lastMsgReceived = Now; + + switch(Buffer[TYPE_O]) + { + // Data from user ucall at node ncall. + + case id_data : + + // Check for dups + + if (CheckforDups(ckt_from, ucall, f1)) + break; + + user = user_find(ucall, ncall); + + if (!user) + break; + + user->lastrealmsgtime = user->lastmsgtime = time(NULL); + + text_tellu(user, f1, NULL, o_topic); + HistoryCount = AddtoHistory(user, f1); + + for (ckt_to = circuit_hd; ckt_to; ckt_to = ckt_to->next) + { + if ((ckt_to->rtcflags & p_linked) && ckt_to->refcnt && + !cn_find(ckt_to, node) && ct_find(ckt_to, user->topic)) + nprintf(ckt_to, "%s\r", Buffer); + } + break; + + // User ucall at node ncall changed their Name/QTH info. + + case id_user : + + user = user_find(ucall, ncall); + if (!user) break; + f2 = strlop(f1, ' '); + if (!f2) break; + + if ((strcmp(user->name, f1) == 0) && (strcmp(user->qth, f2) == 0)) // No Change? + break; + + echo(ckt_from, node, Buffer); // Relay to other nodes. + strnew(&user->name, f1); + strnew(&user->qth, f2); + upduser(user); + break; + + // User ucall logged into node ncall. + + case id_join : + + user = user_find(ucall, ncall); + + if (user) + { + // Already Here + + // If last join was less the 5 secs ago don't report - probably a "Join/Leave Storm" + + if (time(NULL) - user->timeconnected > 5) + ReportBadJoin(ncall, ucall); + + //if (strcmp(user->node->call, OurNode) == 0) + //{ + // Locally connected, and at another node + //} + + user->timeconnected = time(NULL); + break; // We have this user as an active Node + } + + // update join time + + echo(ckt_from, node, Buffer); // Relay to other nodes. + f2 = strlop(f1, ' '); + if (!f2) break; + user = user_join(ckt_from, ucall, ncall, NULL, FALSE); + if (!user) break; + ckt_from->refcnt++; + text_tellu_Joined(user); + strnew(&user->name, f1); + strnew(&user->qth, f2); + upduser(user); +// makelinks(); // Bring up our links if not already up + + break; + + // User ucall logged out of node ncall. + + case id_leave : + + user = user_find(ucall, ncall); + if (!user) + { + Debugprintf("CHAT: Leave for %s from %s when not on list", ucall, ncall); + break; + } + + // if connected for for less than 3 seconds ignore. May give stuck nodes but should stop "Join/Leave Storm" + // we can't just silently leave as next join will propagate + + if (time(NULL) - user->timeconnected < 3) + break; + + echo(ckt_from, node, Buffer); // Relay to other nodes. + + f2 = strlop(f1, ' '); + if (!f2) break; + + text_tellu(user, rtleave, NULL, o_all); + ckt_from->refcnt--; + strnew(&user->name, f1); + strnew(&user->qth, f2); + upduser(user); + user_leave(user); + + cn_dec(ckt_from, node); + node_dec(node); + + break; + + // Node ncall lost its link to node ucall, alias f1. + + case id_unlink : + + // Only relay to other nodes if we had node. Could get loop otherwise. + // ?? This could possibly cause stuck nodes + + ln = node_find(ucall); + + // if connected for for less than 3 seconds ignore. May give stuck nodes but should stop "Join/Leave Storm" + // we can't just silently leave as next join will propagate + + if (ln) + { + if (time(NULL) - ln->timeconnected < 3) + break; + + // is it on this circuit? + + if (cn_find(ckt_from, ln)) + { + cn_dec(ckt_from, ln); + node_dec(ln); + echo(ckt_from, node, Buffer); // Relay to other nodes if we had node. COuld get loop if + } + else + { + Debugprintf("CHAT: node %s unlink for %s when not on this link", ncall, ucall); + } + } + else + { + Debugprintf("CHAT: node %s unlink for %s when not on list", ncall, ucall); + } + + break; + + // Node ncall acquired a link to node ucall, alias f1. + // If we are not linked, is no problem, don't link. + // If we are linked, is a loop, do what? (Try ignore!) + + case id_link : + + ln = node_find(ucall); + + if (!ln && !matchi(ncall, OurNode)) + { + f2 = strlop(f1, ' '); + cn_inc(ckt_from, ucall, f1, f2); + echo(ckt_from, node, Buffer); // Relay to other nodes. + } + else + { + // If last join was less the 5 secs ago don't report - probably a "Join/Leave Storm" + + if (time(NULL) - ln->timeconnected > 5) + Debugprintf("CHAT: node %s link for %s when already on list", ncall, ucall); + + // update join time + + ln->timeconnected = time(NULL); + break; + } + + break; + + // User ucall at node ncall sent f2 to user f1. + + case id_send : + user = user_find(ucall, ncall); + if (!user) break; + f2 = strlop(f1, ' '); + if (!f2) break; + su = user_find(f1, NULL); + if (!su) break; + + if (su->circuit->rtcflags & p_user) + text_tellu(user, f2, f1, o_one); + else + echo(ckt_from, node, Buffer); // Relay to other nodes. + break; + + // User ucall at node ncall changed topic. + + case id_topic : + user = user_find(ucall, ncall); + if (user) + { + if (_stricmp(user->topic->name, f1) != 0) + { + echo(ckt_from, node, Buffer); // Relay to other nodes. + topic_chg(user, f1); + } + } + break; + + + case id_keepalive : + + ln = node_find(ncall); + if (ln) + { + if (ln->Version == NULL) + if (f1) + ln->Version = _strdup(f1); + } + + nprintf(ckt_from, "%c%c%s %s\r", FORMAT, id_pollresp, OurNode, Link->call); + break; + + case id_poll: + + // Send Poll Response + + Link->supportsPolls = Now; + nprintf(ckt_from, "%c%c%s %s\r", FORMAT, id_pollresp, OurNode, Link->call); + break; + + case id_pollresp: + + Link->supportsPolls = Now; + Link->RTT = Now - Link->timePollSent; + Link->timePollSent = 0; // Cancel Timeout + break; + + default: + break; + } + + free(buf); +} + +// Tell another node about nodes known by this node. +// Do not tell it about this node, the other node knows who it +// linked to (or who linked to it). +// Tell another node about users known by this node. +// Done at incoming or outgoing link establishment. + +void state_tell(ChatCIRCUIT *circuit, char * Version) +{ + CHATNODE *node; + USER *user; + + node = cn_inc(circuit, circuit->u.link->call, circuit->u.link->alias, Version); + node_tell(node, id_link); // Tell other nodes about this new link + + // Tell the node that just linked here about nodes known on other links. + + for (node = node_hd; node; node = node->next) + { + if (!matchi(node->call, OurNode)) + node_xmit(node, id_link, circuit); + } + + // Tell the node that just linked here about known users, and their topics. + + for (user = user_hd; user; user = user->next) + { + user_xmit(user, id_join, circuit); + topic_xmit(user, circuit); + } +} + +static void circuit_free(ChatCIRCUIT *circuit) +{ + ChatCIRCUIT *c, *cp; + CN *ncn; + CHATNODE *nn; + TOPIC *tn; + + cp = NULL; + + for (c = circuit_hd; c; cp = c, c = c->next) + { + if (c == circuit) + { + if (cp) cp->next = c->next; else circuit_hd = c->next; + + while (c->hnode) + { + ncn = c->hnode->next; + free(c->hnode); + c->hnode = ncn; + } + + break; + } + } + + if (circuit_hd) return; + +// RT has gone inactive. Clean up. + + while (node_hd) + { + nn = node_hd->next; + free(node_hd->alias); + free(node_hd->call); + free(node_hd); + node_hd = nn; + } + + while (topic_hd) + { + tn = topic_hd->next; + free(topic_hd->name); + free(topic_hd); + topic_hd = tn; + } +} + + +// Find a node in the node list. + +CHATNODE *node_find(char *call) +{ + CHATNODE *node; + + for (node = node_hd; node; node = node->next) + { + //if (node->refcnt && matchi(node->call, call)) I don't think this is right!!! + if (matchi(node->call, call)) + break; + } + + return node; +} + +// Add a reference to a node. + +static CHATNODE *node_inc(char *call, char *alias, char * Version) +{ + CHATNODE *node; + + node = node_find(call); + + if (!node) + { + knownnode_add(call); + + node = zalloc(sizeof(CHATNODE)); + sl_ins_hd(node, node_hd); + node->call = _strdup(call); + node->alias = _strdup(alias); + if (Version) + node->Version = _strdup(Version); + + node->timeconnected = time(NULL); + +// Debugprintf("New Node Rec Created at %x for %s %s", node, node->call, node->alias); + } + + node->refcnt++; + return node; +} + +// Remove a reference to a node. + +static void node_dec(CHATNODE *node) +{ + CHATNODE *t, *tp; + USER *user; + + ChatCIRCUIT *circuit; + CN *cn; + + if (--node->refcnt) return; // Other references. + + // Remove the node from the node list. + + tp = NULL; + + // Make sure there aren't any user or circuit records pointing to it + + for (user = user_hd; user; user = user->next) + { + if (user->node == node) + { + Debugprintf("Trying to remove node %s that is linked from user %s", node->call, user->call); + node->refcnt++; + } + } + + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + if (circuit->rtcflags & p_linked) + { + for (cn = circuit->hnode; cn; cn = cn->next) + { + if (cn->node == node) + { + Debugprintf("Trying to remove node %s that is linked from circuit %s", node->call, circuit->Callsign); + node->refcnt++; + } + } + } + } + + if (node->refcnt) return; // Now have other references. + + for (t = node_hd; t; tp = t, t = t->next) + { + if (t == node) + { + if (tp) tp->next = t->next; else node_hd = t->next; + free(t->alias); + t->alias = NULL; + free(t->call); + t->call = NULL; + free(t); + break; + } + } +} + +// User joins a topic. + +static TOPIC *topic_join(ChatCIRCUIT *circuit, char *s) +{ + CT *ct; + TOPIC *topic; + +// Look for an existing topic. + + for (topic = topic_hd; topic; topic = topic->next) + { + if (matchi(topic->name, s)) + break; + } + +// Create a new topic, if needed. + + if (!topic) + { + topic = zalloc(sizeof(TOPIC)); + sl_ins_hd(topic, topic_hd); + topic->name = _strdup(s); + } + + topic->refcnt++; // One more user in this topic. + + Logprintf(LOG_CHAT, circuit, '?', "topic_join complete user %s topic %s addr %x ref %d", + circuit->u.user->call, topic->name, topic, topic->refcnt); + + +// Add the circuit / topic association. + + for (ct = circuit->topic; ct; ct = ct->next) + { + if (ct->topic == topic) + { + ct->refcnt++; + return topic; + } + } + + ct = zalloc(sizeof(CT)); + sl_ins_hd(ct, circuit->topic); + ct->topic = topic; + ct->refcnt = 1; + return topic; +} + +// User leaves a topic. + +static void topic_leave(ChatCIRCUIT *circuit, TOPIC *topic) +{ + CT *ct, *ctp; + TOPIC *t, *tp; + + Logprintf(LOG_CHAT, circuit, '?', "topic_leave user %s topic %s addr %x ref %d", + circuit->u.user->call, topic->name, topic, topic->refcnt); + + topic->refcnt--; + + ctp = NULL; + + for (ct = circuit->topic; ct; ctp = ct, ct = ct->next) + { + if (ct->topic == topic) + { + if (!--ct->refcnt) + { + if (ctp) ctp->next = ct->next; else circuit->topic = ct->next; + free(ct); + break; + } + } + } + + tp = NULL; + + for (t = topic_hd; t; tp = t, t = t->next) + { + if (!t->refcnt && (t == topic)) + { + if (tp) tp->next = t->next; else topic_hd = t->next; + free(t->name); + free(t); + break; + } + } +} + +// Find a circuit/topic association. + +int ct_find(ChatCIRCUIT *circuit, TOPIC *topic) +{ + CT *ct; + + for (ct = circuit->topic; ct; ct = ct->next) + { + if (ct->topic == topic) + return ct->refcnt; + } + return 0; +} + +// Nodes reached from each circuit. Used only if the circuit is a link. + +// Remove a circuit/node association. + +static void cn_dec(ChatCIRCUIT *circuit, CHATNODE *node) +{ + CN *c, *cp; + +// Debugprintf("CHAT: Remove c/n %s ", node->call); + + cp = NULL; + + for (c = circuit->hnode; c; cp = c, c = c->next) + { + if (c->node == node) + { +// CN * cn; +// int len; +// char line[1000]=""; + + if (--c->refcnt) + { +// Debugprintf("CHAT: Remove c/n Node %s still in use refcount %d", node->call, c->refcnt); + return; // Still in use + } + + if (cp) + cp->next = c->next; + else + circuit->hnode = c->next; + + free(c); + + break; + } + } + + if (c == NULL) + { + CN * cn; + int len; + char line[1000]=""; + + // not found?? + + Debugprintf("CHAT: !! Remove c/n Node %s addr %x not found cn chain follows", node->call, node); + + line[0] = 0; + + for (cn = circuit->hnode; cn; cn = cn->next) + { + if (cn->node && cn->node->call) + { +#ifndef LINBPQ + __try + { +#endif + len += sprintf(&line[len], " %p %s", cn->node, cn->node->alias); + if (len > 80) + { + Debugprintf("%s", line); + len = sprintf(line, " "); + } +#ifndef LINBPQ + } + __except(EXCEPTION_EXECUTE_HANDLER) + {len = sprintf("%s *PE* Corrupt Rec %x %x ", line, cn, cn->node);} +#endif + } + else + { + len = sprintf("%s Corrupt Rec %x %x ", line, cn, cn->node); + } + } + Debugprintf("%s", line); + + } + + +} + +// Add a circuit/node association. + +static CHATNODE *cn_inc(ChatCIRCUIT *circuit, char *call, char *alias, char * Version) +{ + CHATNODE *node; + CN *cn; + + node = node_inc(call, alias, Version); + + for (cn = circuit->hnode; cn; cn = cn->next) + { + if (cn->node == node) + { + cn->refcnt++; +// Debugprintf("cn_inc cn Refcount for %s->%s incremented to %d - adding Call %s", +// circuit->Callsign, node->call, cn->refcnt, call); + + return node; + } + } + + cn = zalloc(sizeof(CN)); + sl_ins_hd(cn, circuit->hnode); + cn->node = node; + cn->refcnt = 1; + +// Debugprintf("cn_inc New cn for %s->%s - adding Call %s", +// circuit->Callsign, node->call, call); + + return node; +} + +// Find a circuit/node association. + +static int cn_find(ChatCIRCUIT *circuit, CHATNODE *node) +{ + CN *cn; + + for (cn = circuit->hnode; cn; cn = cn->next) + { + if (cn->node == node) + return cn->refcnt; + } + return 0; +} + +// From a local user to a specific user at another node. + +static void text_xmit(USER *user, USER *to, char *text) +{ + nprintf(to->circuit, "%c%c%s %s %s %s\r", + FORMAT, id_send, OurNode, user->call, to->call, text); +} + +void put_text(ChatCIRCUIT * circuit, USER * user, UCHAR * buf) +{ + UCHAR BufferB[4096]; + + // Text is UTF-8 internally. If user doen't want UTF-8. convert to Node's locale + + if (circuit->u.user->rtflags & u_noUTF8) + { +#ifdef WIN32 + char * Buffer = buf; + WCHAR BufferW[4096]; + int wlen, blen; + BOOL DefaultUsed = FALSE; + char Subst = '?'; + + wlen = MultiByteToWideChar(CP_UTF8, 0, buf, (int)strlen(buf) + 1, BufferW, 4096); + blen = WideCharToMultiByte(circuit->u.user->Codepage, 0, BufferW, wlen, BufferB + 2, 4096, &Subst, &DefaultUsed); + + if (blen == 0) // Probably means invalid code page + blen = WideCharToMultiByte(CP_ACP, 0, BufferW, wlen, BufferB + 2, 4096, &Subst, &DefaultUsed); + + buf = BufferB + 2; + BufferB[blen + 2] = 0; +#else + + size_t left = 4096; + UCHAR * BufferBP = BufferB; + size_t len = strlen(buf) + 1; + struct user_t * icu = circuit->u.user; + + if (icu->iconv_fromUTF8 == NULL) + { + icu->iconv_fromUTF8 = iconv_open(icu->Codepage, "UTF-8"); + + if (icu->iconv_fromUTF8 == (iconv_t)-1) + icu->iconv_fromUTF8 = iconv_open("CP1252//IGNORE", "UTF-8"); + } + + iconv(icu->iconv_fromUTF8, NULL, NULL, NULL, NULL); // Reset State Machine + iconv(icu->iconv_fromUTF8, (char ** __restrict__)&buf, &len, (char ** __restrict__)&BufferBP, &left); + + len = 4096 - left; + buf = BufferB; + +#endif + + } + + + if (circuit->u.user->rtflags & u_colour) // Use Colour + { + // Put a colour header on message + + *(--buf) = user->Colour; + *(--buf) = 0x1b; + nputs(circuit, buf); + buf +=2; + } + else + nputs(circuit, buf); + + + + circuit->u.user->lastsendtime = time(NULL); +} + +void text_tellu(USER *user, char *text, char *to, int who) +{ + ChatCIRCUIT *circuit; + UCHAR Buffer[2048]; + UCHAR *buf = &Buffer[4]; + char * Time; + struct tm * tm; + char Stamp[20]; + time_t T; + + T = time(NULL); + tm = gmtime(&T); + + sprintf(Stamp,"%02d:%02d ", tm->tm_hour, tm->tm_min); + +// Send it to all connected users in the same topic. +// Echo to originator if requested. + + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + if (!(circuit->rtcflags & p_user)) continue; // Circuit is a link. + + if ((circuit->u.user == user) && !(user->rtflags & u_echo)) continue; + + if (circuit->u.user->rtflags & u_showtime) + Time = Stamp; + else + Time = ""; + + if (circuit->u.user->rtflags & u_shownames) + sprintf(buf, "%s%-6.6s %s %c %s\r", Time, user->call, user->name, (who == o_one) ? '>' : ':', text); + else + sprintf(buf, "%s%-6.6s %c %s\r", Time, user->call, (who == o_one) ? '>' : ':', text); + + + switch(who) + { + case o_topic : + if (circuit->u.user->topic == user->topic) + put_text(circuit, user, buf); // Send adding Colour if wanted + + break; + + case o_all: + + put_text(circuit, user, buf); // Send adding Colour if wanted + + break; + + case o_one : + if (matchi(circuit->u.user->call, to)) + put_text(circuit, user, buf); // Send adding Colour if wanted + break; + } + } +} + +extern int FlashOnConnect; + +void text_tellu_Joined(USER * user) +{ + ChatCIRCUIT *circuit; + UCHAR Buffer[200]; + UCHAR *buf = &Buffer[4]; + char * Time; + struct tm * tm; + char Stamp[20]; + time_t T; + char prog[256] = ""; + + T = time(NULL); + tm = gmtime(&T); + + sprintf(Stamp,"%02d:%02d ", tm->tm_hour, tm->tm_min); + + sprintf(buf, "%s%-6.6s : %s *** Joined Chat, Topic %s", Stamp, user->call, user->name, user->topic->name); + + if (reportChatEvents) + { + +#ifdef WIN32 + if (pRunEventProgram) + pRunEventProgram("ChatNewUser.exe", user->call); +#else + sprintf(prog, "%s/%s", BPQDirectory, "ChatNewUser"); + RunEventProgram(prog, user->call); +#endif + } + +// Send it to all connected users in the same topic. +// Echo to originator if requested. + + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + if (!(circuit->rtcflags & p_user)) continue; // Circuit is a link. + if ((circuit->u.user == user) && !(user->rtflags & u_echo)) continue; + + if (circuit->u.user->rtflags & u_showtime) + Time = Stamp; + else + Time = ""; + + sprintf(buf, "%s%-6.6s : %s *** Joined Chat, Topic %s", Time, user->call, user->name, user->topic->name); + + put_text(circuit, user, buf); // Send adding Colour if wanted + + if (circuit->u.user->rtflags & u_bells) + if (circuit->BPQStream < 0) // Console + { +#ifndef LINBPQ + if (FlashOnConnect) FlashWindow(ConsHeader[1]->hConsole, TRUE); +#endif + nputc(circuit, 7); +// PlaySound ("BPQCHAT_USER_LOGIN", NULL, SND_ALIAS | SND_APPLICATION | SND_ASYNC); + } + else + nputc(circuit, 7); + + nputc(circuit, 13); + } +} +// Tell one link circuit about a local user change of topic. + +static void topic_xmit(USER *user, ChatCIRCUIT *circuit) +{ + nprintf(circuit, "%c%c%s %s %s\r", + FORMAT, id_topic, OurNode, user->call, user->topic->name); +} + +// Tell another node about one known node on a link add or drop +// if that node is from some other link. + +static void node_xmit(CHATNODE *node, char kind, ChatCIRCUIT *circuit) +{ +#ifndef LINBPQ + struct _EXCEPTION_POINTERS exinfo; + + __try + { +#endif + if (!cn_find(circuit, node)) + if (node->Version && (kind == id_link)) + nprintf(circuit, "%c%c%s %s %s %s\r", FORMAT, kind, OurNode, node->call, node->alias, node->Version); + else + nprintf(circuit, "%c%c%s %s %s\r", FORMAT, kind, OurNode, node->call, node->alias); + +#ifndef LINBPQ + } + + #define EXCEPTMSG "node_xmit" + #include "StdExcept.c" + + Debugprintf("Corrupt Rec %x %x %x", node, node->call, node->alias); + } +#endif +} + +// Tell all other nodes about one node known by this node. + +static void node_tell(CHATNODE *node, char kind) +{ + ChatCIRCUIT *circuit; + + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + if (circuit->rtcflags & p_linked) + node_xmit(node, kind, circuit); + } +} + +// Tell another node about a user login/logout at this node. + +static void user_xmit(USER *user, char kind, ChatCIRCUIT *circuit) +{ + CHATNODE *node; + + node = user->node; + + if (!cn_find(circuit, node)) + nprintf(circuit, "%c%c%s %s %s %s\r", FORMAT, kind, node->call, user->call, user->name, user->qth); +} + +// Tell all other nodes about a user login/logout at this node. + +static void user_tell(USER *user, char kind) +{ + ChatCIRCUIT *circuit; + + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + if (circuit->rtcflags & p_linked) + user_xmit(user, kind, circuit); + } +} + +// Find the user record for call@node. Node can be NULL, meaning any node + +USER *user_find(char *call, char * node) +{ + USER *user; + + for (user = user_hd; user; user = user->next) + { + if (node) + { + if (matchi(user->call, call) && matchi(user->node->call, node)) + break; + } + else + { + if (matchi(user->call, call)) + break; + } + } + + return user; +} + +static void user_leave(USER *user) +{ + USER *t, *tp; + + topic_leave(user->circuit, user->topic); + + tp = NULL; + + for (t = user_hd; t; tp = t, t = t->next) + { + if (t == user) + { + if (tp) tp->next = t->next; else user_hd = t->next; + + free(t->name); + free(t->call); + free(t->qth); +#ifndef WIN32 + if (t->iconv_fromUTF8) + iconv_close(t->iconv_fromUTF8); + if (t->iconv_toUTF8) + iconv_close(t->iconv_toUTF8); +#endif + free(t); + break; + } + } + + if (user_hd == NULL) + ChatTmr = 59; // If no users, disconnect links after 10-20 secs +} + +// User changed to a different topic. + +static BOOL topic_chg(USER *user, char *s) +{ + char buf[128]; + + if (_stricmp(user->topic->name, s) == 0) return FALSE; // Not Changed + + sprintf(buf, "*** Left Topic: %s", user->topic->name); + text_tellu(user, buf, NULL, o_topic); // Tell everyone in the old topic. + topic_leave(user->circuit, user->topic); + user->topic = topic_join(user->circuit, s); + sprintf(buf, "*** Joined Topic: %s", user->topic->name); + text_tellu(user, buf, NULL, o_topic); // Tell everyone in the new topic. + + return TRUE; +} + +// Create a user record for this user. + +static USER *user_join(ChatCIRCUIT *circuit, char *ucall, char *ncall, char *nalias, BOOL Local) +{ + CHATNODE *node; + USER *user; + + if (Local) + { + node = cn_inc(circuit, ncall, nalias, Verstring); + } + else + node = cn_inc(circuit, ncall, nalias, NULL); + +// Is this user already logged in at this node? + + for (user = user_hd; user; user = user->next) + { + if (matchi(user->call, ucall) && (user->node == node)) + return user; + } + +// User is not logged in, create a user record for them. + + user = zalloc(sizeof(USER)); + sl_ins_hd(user, user_hd); + user->circuit = circuit; + user->call = _strdup(ucall); + _strupr(user->call); + user->node = node; + rduser(user); + + if (user->Colour == 0 || user->Colour == 11) // None or default + { + // Allocate Random + int sum = 0, i; + + for (i = 0; i < 9; i++) + sum += user->call[i]; + sum %= 20; + + user->Colour = AutoColours[sum] + 10; // Best 20 colours + } + + if (circuit->rtcflags & p_user) + circuit->u.user = user; + + user->timeconnected = user->lastrealmsgtime = user->lastmsgtime = time(NULL); + + user->topic = topic_join(circuit, deftopic); + return user; +} + +// Link went away. We dropped it, or the other node dropped it. +// Drop nodes and users connected from this link. +// Tell other (still connected) links what was dropped. + +void link_drop(ChatCIRCUIT *circuit) +{ + USER *user, *usernext; + CN *cn; + +// So we don't try and send anything on this circuit. + + if (circuit->u.link) + if (circuit->rtcflags == p_linkini) + Debugprintf("Chat link %s Link Setup Failed", circuit->u.link->call); + + if (circuit->rtcflags == p_linkini) + circuit->u.link->flags = p_linkfailed; + else + circuit->u.link->flags = 0; + + circuit->rtcflags = p_nil; + +// Users connected on the dropped link are no longer connected. + + for (user = user_hd; user; user = usernext) + { + usernext = user->next; // Save next pointer in case entry is free'd + + if (user->circuit == circuit) + { + CHATNODE *node; + + node = user->node; + + text_tellu(user, rtleave, NULL, o_all); + user_tell(user, id_leave); + user_leave(user); + + circuit->refcnt--; + if (node) + node_dec(node); + } + } + +// Any node known from the dropped link is no longer known. + + for (cn = circuit->hnode; cn; cn = cn->next) + { + node_tell(cn->node, id_unlink); + node_dec(cn->node); + } + +// The circuit is no longer used. + + circuit_free(circuit); + NeedStatus = TRUE; +} + +// Handle an incoming control frame from a linked RT system. + +static void echo(ChatCIRCUIT *fc, CHATNODE *node, char * Buffer) +{ + ChatCIRCUIT *tc; + + for (tc = circuit_hd; tc; tc = tc->next) + { + if ((tc != fc) && (tc->rtcflags & p_linked) && !cn_find(tc, node)) + nprintf(tc, "%s\r", Buffer); + } +} + +char ** SeparateConnectScript(char * MultiString) +{ + char * ptr1 = MultiString; + char ** Value; + int Count = 0; + char * ptr; + + // Convert to string array + + Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values + Value[0] = NULL; + + ptr = MultiString; + + while (ptr && strlen(ptr)) + { + ptr1 = strchr(ptr, '|'); + + if (ptr1) + *(ptr1++) = 0; + + if (strlen(ptr)) + { + Value = realloc(Value, (Count + 2) * sizeof(void *)); + Value[Count++] = _strdup(ptr); + } + ptr = ptr1; + } + + Value[Count] = NULL; + return Value; +} + +// Add an entry to list of link partners + +int rtlink (char * Call) +{ + LINK *link, *temp; + char *c; + char * script; + + _strupr(Call); + script = strlop(Call, '|'); + + c = strlop(Call, ':'); + if (!c) return FALSE; + + link = zalloc(sizeof(LINK)); + + link->alias = _strdup(Call); + link->call = _strdup(c); + + if (script) + { + link->ConnectScript = SeparateConnectScript(script); + link->Lines = 0; + while (link->ConnectScript[++link->Lines]); + } + else + { + // Create Script with one entry to call partner direct; + + link->ConnectScript = zalloc(sizeof(void *) * 2); // always NULL entry on end + link->ConnectScript[0] = malloc(32); + sprintf(link->ConnectScript[0], "C %s", c); + link->Lines = 1; + } + + if (link_hd == NULL) + link_hd = link; + else + { + temp = link_hd; + while(temp->next) + temp = temp->next; + + temp->next = link; + } + + return TRUE; +} + +VOID removelinks() +{ + LINK *link, *nextlink; + + for (link = link_hd; link; link = nextlink) + { + nextlink = link->next; + + if (link->ConnectScript) + { + int n = 0; + while(link->ConnectScript[n]) + free(link->ConnectScript[n++]); + + free(link->ConnectScript); + } + + free(link->alias); + link->alias = 0; + free(link->call); + link->call = 0; + free(link); + link = 0; + } + link_hd = NULL; +} +VOID removeknown() +{ + // Save Known Nodes list and free struct + + KNOWNNODE *node, *nextnode; + FILE *out; + + out = fopen(RtKnown, "w"); + + if (!out) + return; + + for (node = known_hd; node; node = nextnode) + { + fprintf(out, "%s %u\n", node->call, (unsigned int)node->LastHeard); + + nextnode = node->next; + free(node->call); + free(node); + } + known_hd = NULL; + + fclose(out); +} + +VOID LoadKnown() +{ + // Reload Known Nodes list + + FILE *in; + char buf[128]; + char * ptr; + + in = fopen(RtKnown, "r"); + + if (in == NULL) + return; + + while(fgets(buf, 128, in)) + { + ptr = strchr(buf, ' '); + if (ptr) + { + *(ptr) = 0; + knownnode_add(buf); + } + } + + fclose(in); +} + +// We don't allocate memory for circuit, but we do chain it + +ChatCIRCUIT *circuit_new(ChatCIRCUIT *circuit, int flags) +{ + // Make sure circuit isn't already on list + + ChatCIRCUIT *c; + + circuit->rtcflags = flags; + circuit->next = NULL; + + for (c = circuit_hd; c; c = c->next) + { + if (c == circuit) + { + Debugprintf("CHAT: Attempting to add Circuit when already on list"); + return circuit; + } + } + + sl_ins_hd(circuit, circuit_hd); + + return circuit; +} + +// Handle an incoming link. We should only get here if we think the station is a node. + +int rtloginl (ChatCIRCUIT *conn, char * call) +{ + LINK * link; + + if (node_find(call)) + { + Logprintf(LOG_CHAT, conn, '|', "Refusing link from %s to %s to prevent a loop", conn->Callsign, OurNode); + + nprintf(conn, "Refusing link from %s to %s to prevent a loop.\n", conn->Callsign, OurNode); + ChatFlush(conn); + Sleep(500); + conn->rtcflags = p_nil; + Disconnect(conn->BPQStream); + return FALSE; // Already linked. + } + + for (link = link_hd; link; link = link->next) + { + if (matchi(call, link->call)) + break; + } + + if (!link) + { + // We don't link with this system. Shouldn't happen, as we checked earlier + + nprintf(conn, "Node %s does not have %s defined as a node to link to - closing.\r", + OurNode, conn->Callsign); + ChatFlush(conn); + Sleep(500); + conn->rtcflags = p_nil; + Disconnect(conn->BPQStream); + return FALSE; + } + + if (link->flags & (p_linked | p_linkini)) + { + // Already Linked. Used to Disconnect, but that can cause sync errors + // Try closing old link and keeping new + + ChatCIRCUIT *c; + int len; + char Msg[80]; + + for (c = circuit_hd; c; c = c->next) + { + if (c->u.link == link) + { + len=sprintf_s(Msg, sizeof(Msg), "Chat Node %s Connect when Connected - Old Connection Closed", call); + ChatWriteLogLine(conn, '|',Msg, len, LOG_CHAT); + + c->Active = FALSE; // So we don't try to clear circuit again + Disconnect(c->BPQStream); + link_drop(c); + RefreshMainWindow(); + break; + } + } + } + +// Accept the link request. + + circuit_new(conn, p_linked); + conn->u.link = link; + nputs(conn, "OK\r"); + link->flags = p_linked; + link->delay = 0; // Dont delay first restart + state_tell(conn, NULL); + conn->u.link->timePollSent = time(NULL); // Keepalive is a poll + nprintf(conn, "%c%c%s %s %s\r", FORMAT, id_keepalive, OurNode, conn->u.link->call, Verstring); + + NeedStatus = TRUE; + + return TRUE; +} + +// User connected to chat, or did chat command from BBS + +int rtloginu (ChatCIRCUIT *circuit, BOOL Local) +{ + USER *user; + +// Is this user already logged in to RT somewhere else? + + user = user_find(circuit->UserPointer->Call, NULL); + + if (user) + { + // if connected at this node, kill old connection and allow new login + + if (user->node == node_find(OurNode)) + { + nputs(circuit, "*** Already connected at this node - old session will be closed.\r"); + + if (user->circuit->BPQStream < 0) + { + CloseConsole(user->circuit->BPQStream); + } + else + { + Disconnect(user->circuit->BPQStream); + } + } + else + nputs(circuit, "*** Already connected at another node.\r"); + + return FALSE; + } + +// Create the user entry. + + circuit_new(circuit, p_user); + + user = user_join(circuit, circuit->UserPointer->Call, OurNode, OurAlias, Local); + circuit->u.user = user; + + if (strcmp(user->name, "?_name") == 0) + { + user->name = _strdup(circuit->UserPointer->Name); + } + upduser(user); + + ChatExpandAndSendMessage(circuit, ChatWelcomeMsg, LOG_CHAT); + text_tellu_Joined(user); + user_tell(user, id_join); + show_users(circuit); + user->lastsendtime = time(NULL); +// makelinks(); + + return TRUE; +} + +void logout(ChatCIRCUIT *circuit) +{ + USER *user; + CHATNODE *node; + + circuit->rtcflags = p_nil; + user = circuit->u.user; + + if (user) // May not have logged in if already conencted + { + node = user->node; + + user_tell(user, id_leave); + text_tellu(user, rtleave, NULL, o_all); + user_leave(user); + + // order changed so node_dec can check if a node that is about the be deleted has eny users + + if (node) + { + cn_dec(circuit, node); + node_dec(node); + } + + circuit->u.user = NULL; + } + + circuit_free(circuit); +} + +void show_users(ChatCIRCUIT *circuit) +{ + USER *user; + char * Alias; + char * Topic; + + int i = 0; + + // First count them + + for (user = user_hd; user; user = user->next) + { + i++; + } + + nprintf(circuit, "%d Station(s) connected:\r", i); + + for (user = user_hd; user; user = user->next) + { + if ((user->node == 0) || (user->node->alias == 0)) + Alias = "(Corrupt Alias)"; + else + Alias = user->node->alias; + + if ((user->topic == 0) || (user->topic->name == 0)) + Topic = "(Corrupt Topic)"; + else + Topic = user->topic->name; + +#ifndef LINBPQ + __try + { +#endif + if (circuit->u.user->rtflags & u_colour) // Use Colour + nprintf(circuit, "\x1b%c%-6.6s at %-9.9s %s, %s [%s] Idle for %d seconds\r", + user->Colour, user->call, Alias, user->name, user->qth, Topic, time(NULL) - user->lastrealmsgtime); + else + nprintf(circuit, "%-6.6s at %-9.9s %s, %s [%s] Idle for %d seconds\r", + user->call, Alias, user->name, user->qth, Topic, time(NULL) - user->lastrealmsgtime); +#ifndef LINBPQ + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + Debugprintf("MAILCHAT *** Program Error in show_users"); + CheckProgramErrors(); + } +#endif + } +} + + +static void show_nodes(ChatCIRCUIT *circuit) +{ + CHATNODE *node; + + nputs(circuit, "Known Nodes:\r"); + + for (node = node_hd; node; node = node->next) + { + if (node->refcnt) + if (node->Version) + nprintf(circuit, "%s:%s %s %u\r", node->alias, node->call, node->Version, node->refcnt); + else + nprintf(circuit, "%s:%s %s %u\r", node->alias, node->call, "Not Known", node->refcnt); + } +} + +// /P Command: List circuits and remote RT on them. + +#define xxx "\r " + +static void show_circuits(ChatCIRCUIT *conn, char Flag) +{ + ChatCIRCUIT *circuit; + CHATNODE *node; + LINK *link; + char line[1000]; + int len = 0; + CN *cn; + + int i = 0; + + // First count them + + for (node = node_hd; node; node = node->next) + { + i++; + } + + nprintf(conn, "%d Node(s)\r", i); + + if (Flag == 'c') + len = sprintf(line, "Here %-6.6s <-", OurNode); + else + len = sprintf(line, "Here %-6.6s <-", OurAlias); + + for (node = node_hd; node; node = node->next) if (node->refcnt) + { + if (Flag == 'c') + len += sprintf(&line[len], " %s", node->call); + else + len += sprintf(&line[len], " %s", node->alias); + if (len > 80) + { + nprintf(conn, "%s\r", line); + len = sprintf(line, " "); + } + } + + nprintf(conn, "%s\r", line); + + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + if (circuit->rtcflags & p_linked) + { + if (Flag == 'c') + len = sprintf(line, "Nodes via %-6.6s(%d) -", circuit->u.link->call, circuit->refcnt); + else + len = sprintf(line, "Nodes via %-6.6s(%d) -", circuit->u.link->alias, circuit->refcnt); + +#ifndef LINBPQ + __try{ + for (cn = circuit->hnode; cn; cn = cn->next) + { + if (cn->node && cn->node->alias) + { + __try + { + if (Flag == 'c') + len += sprintf(&line[len], " %s", cn->node->call); + else + len += sprintf(&line[len], " %s", cn->node->alias); + if (len > 80) + { + nprintf(conn, "%s\r", line); + len = sprintf(line, " "); + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + {len += sprintf(&line[len], " *PE* Corrupt Rec %x %x", cn, cn->node);} + } + else + len = sprintf(&line[len], " Corrupt Rec %x %x ", cn, cn->node); + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + {len += sprintf(&line[len], " *PE* Corrupt Rec %x %x ", cn, cn->node);} +#else + for (cn = circuit->hnode; cn; cn = cn->next) + { + if (cn->node && cn->node->alias) + { + if (Flag == 'c') + len += sprintf(&line[len], " %s", cn->node->call); + else + len += sprintf(&line[len], " %s", cn->node->alias); + if (len > 80) + { + nprintf(conn, "%s\r", line); + len = sprintf(line, " "); + } + } + else + len += sprintf(&line[len], " Corrupt Rec %p %p ", cn, cn->node); + } +#endif + nprintf(conn, "%s\r", line); + + } + else if (circuit->rtcflags & p_user) + nprintf(conn, "User %-6.6s\r", circuit->u.user->call); + else if (circuit->rtcflags & p_linkini) + { + if (circuit->u.link) + { if (Flag == 'c') + nprintf(conn, "Link %-6.6s (setup)\r", circuit->u.link->call); + else + nprintf(conn, "Link %-6.6s (setup)\r", circuit->u.link->alias); + + } + else + nprintf(conn, "Link ?? (setup)\r"); + } + } + + nprintf(conn, "Links Defined:\r"); + + for (link = link_hd; link; link = link->next) + { + if (link->flags & p_linked ) + if (link->supportsPolls) + nprintf(conn, " %-10.10s Open RTT %d\r", link->call, link->RTT); + else + nprintf(conn, " %-10.10s Open\r", link->call); + else if (link->flags & (p_linked | p_linkini)) + nprintf(conn, " %-10.10s Connecting\r", link->call); + else if (link->flags & p_linkfailed) + nprintf(conn, " %-10.10s Connect failed\r", link->call); + else + nprintf(conn, " %-10.10s Idle\r", link->call); + } +} + +// /T Command: List topics and users in them. + +static void show_topics(ChatCIRCUIT *conn) +{ + TOPIC *topic; + USER *user; + + nputs(conn, "Active Topics are:\r"); + + for (topic = topic_hd; topic; topic = topic->next) + { + nprintf(conn, "%s\r", topic->name); + + if (topic->refcnt) + { + nputs(conn, " "); + for (user = user_hd; user; user = user->next) + { + if (user->topic == topic) + nprintf(conn, " %s", user->call); + } + nputc(conn, '\r'); + } + } +} + +static void show_users_in_topic(ChatCIRCUIT *conn) +{ + TOPIC *topic; + USER *user; + + nputs(conn, "Users in Topic:\r"); + + topic = conn->u.user->topic; + { + if (topic->refcnt) + { + for (user = user_hd; user; user = user->next) + { + if (user->topic == topic) + nprintf(conn, "%s ", user->call); + } + nputc(conn, '\r'); + } + } +} + +// Do a user command. + +int rt_cmd(ChatCIRCUIT *circuit, char * Buffer) +{ + ChatCIRCUIT *c; + USER *user, *su; + char *f1, *f2; + + user = circuit->u.user; + +// user->lastsendtime = time(NULL); + + switch(tolower(Buffer[1])) + { + case 'a' : + user->rtflags ^= u_bells; + upduser(user); + nprintf(circuit, "Alert %s\r", (user->rtflags & u_bells) ? "Enabled" : "Disabled"); + return TRUE; + + case 'b' : return FALSE; + + case 'c' : + user->rtflags ^= u_colour; + upduser(user); + nprintf(circuit, "Colour Mode %s\r", (user->rtflags & u_colour) ? "Enabled" : "Disabled"); + return TRUE; + + case 'e' : + user->rtflags ^= u_echo; + upduser(user); + nprintf(circuit, "Echo %s\r", (user->rtflags & u_echo) ? "Enabled" : "Disabled"); + return TRUE; + + case 'f' : makelinks(); return TRUE; + + case 'h' : + case '?' : + { + char * Save; + char * MsgBytes = Save = ReadInfoFile("chathelp.txt"); + + if (MsgBytes) + { + int Length; + + // Remove lf chars + + Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); + + ChatQueueMsg(circuit, MsgBytes, Length); + free(Save); + } + else + { + nputs(circuit, "Commands can be in upper or lower case.\r"); + nputs(circuit, "/U - Show Users.\r/N - Enter your Name.\r/Q - Enter your QTH.\r/T - Show Topics.\r"); + nputs(circuit, "/T Name - Join Topic or Create new Topic. Topic Names are not case sensitive\r/P - Show Ports and Links.\r"); + nprintf(circuit, "/A - Toggle Alert on user join - %s.\r", + (user->rtflags & u_bells) ? "Enabled" : "Disabled"); + nprintf(circuit, "/C - Toggle Colour Mode on or off (only works on Console or BPQTerm/TermTCP/QtTermTCP - %s.\r", + (user->rtflags & u_colour) ? "Enabled" : "Disabled"); + nputs(circuit, "/Codepage CPnnnn - Set Codepage to use if UTF-8 is disabled.\r"); + nprintf(circuit, "/E - Toggle Echo - %s .\r", + (user->rtflags & u_echo) ? "Enabled" : "Disabled"); + nprintf(circuit, "/Keepalive - Toggle sending Keepalive messages every 10 minutes - %s.\r", + (user->rtflags & u_keepalive) ? "Enabled" : "Disabled"); + nprintf(circuit, "/ShowNames - Toggle displaying name as well as call on each message - %s\r", + (user->rtflags & u_shownames) ? "Enabled" : "Disabled"); + nprintf(circuit, "/Auto - Toggle Automatic character set selection - %s.\r", + (user->rtflags & u_auto) ? "Enabled" : "Disabled"); + nprintf(circuit, "/UTF-8 - Character set Selection - %s.\r", + (user->rtflags & u_noUTF8) ? "8 Bit" : "UTF-8"); + nprintf(circuit, "/Time - Toggle displaying timestamp on each message - %s.\r", + (user->rtflags & u_showtime) ? "Enabled" : "Disabled"); + nputs(circuit, "/S CALL Text - Send Text to that station only.\r"); + nputs(circuit, "/F - Force all links to be made.\r/K - Show Known nodes.\r"); + nputs(circuit, "/B - Leave Chat and return to node.\r/QUIT - Leave Chat and disconnect from node.\r"); + nputs(circuit, "/History nn - Display chat messages received in last nn minutes.\r"); + } + } + return TRUE; + + case 'k' : show_nodes(circuit); return TRUE; + + case 'n' : + + f1 = &Buffer[2]; + + while ((*f1 != 0) && (*f1 == ' ')) + f1++; + + if (*f1 == 0) + { + nprintf(circuit, "Name is %s\r", user->name); + return TRUE; + } + + strnew(&user->name, f1); + nprintf(circuit, "Name set to %s\r", user->name); + upduser(user); + user_tell(user, id_user); + return TRUE; + + case 'p' : show_circuits(circuit, Buffer[3]); return TRUE; + + case 'q' : + + f1 = &Buffer[2]; + + while ((*f1 != 0) && (*f1 == ' ')) + f1++; + + if (*f1 == 0) + { + nprintf(circuit, "QTH is %s\r", user->qth); + return TRUE; + } + + strnew(&user->qth, f1); + + nprintf(circuit, "QTH set to %s\r", user->qth); + upduser(user); + user_tell(user, id_user); + return TRUE; + + case 's' : + strcat(Buffer, "\r"); + f1 = strlop(Buffer, ' '); // To. + if (!f1) break; + f2 = strlop(f1, ' '); // Text to send. + if (!f2) break; + _strupr(f1); + su = user_find(f1, NULL); + + if (!su) + { + nputs(circuit, "*** That user is not logged in.\r"); + return TRUE; + } + + // Send to the desired user only. + + if (su->circuit->rtcflags & p_user) + text_tellu(user, f2, f1, o_one); + else + text_xmit(user, su, f2); + + return TRUE; + + case 't' : + f1 = strlop(Buffer, ' '); + if (f1) + { + if (topic_chg(user, f1)) + { + nprintf(circuit, "Switched to Topic %s\r", user->topic->name); + show_users_in_topic(circuit); + + // Tell all link circuits about the change of topic. + + for (c = circuit_hd; c; c = c->next) + { + if (c->rtcflags & p_linked) + topic_xmit(user, c); + } + } + else + { + // Already in topic + + nprintf(circuit, "You were already in Topic %s\r", user->topic->name); + } + } + else + show_topics(circuit); + return TRUE; + + case 'u' : show_users(circuit); return TRUE; + + default : break; + } + + saywhat(circuit); + return TRUE; +} + +void makelinks(void) +{ + LINK *link; + ChatCIRCUIT *circuit; + + // Make the links. Called every 10 seconds + + // Make sure previous link has completed or failed + + if (RunningConnectScript) + { + // Make sure Connect Script isn't taking too long + + if (time(NULL) - RunningConnectScript < 30) + return; + + // Running too long - close it + + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + // Find the link + + if (circuit->rtcflags & (p_linkini)) + { + link = circuit->u.link; + link->flags = p_linkfailed; + RunningConnectScript = 0; + link->scriptRunning = 0; // so it doesn't get reentered + Logprintf(LOG_CHAT, circuit, '|', "Connect to %s timed out", circuit->Callsign); + + Disconnect(circuit->BPQStream); + } + } + RunningConnectScript = 0; + } + + for (link = link_hd; link; link = link->next) + { + // Is this link already established? + + if (link->flags & (p_linked | p_linkini)) + continue; + + // Already linked through some other node? + // If so, making this link would create a loop. + + if (node_find(link->call)) + continue; + + // Fire up the process to handle this link. + + if (link->delay == 0) + { + link->flags = p_linkini; + link->delay = 12; // 2 mins + chat_link_out(link); + return; // One at a time + } + else + link->delay--; + } +} + +VOID node_close() +{ + // Close all Node-Node Links + + ChatCIRCUIT *circuit; + + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + if (circuit->rtcflags & (p_linked | p_linkini | p_linkwait)) + Disconnect(circuit->BPQStream); + } +} + +// Send Keepalives to all connected nodes + +static void node_keepalive() +{ + ChatCIRCUIT *circuit; + + NeedStatus = TRUE; // Send Report to Monitor + + if (user_hd) // Any Users? + { + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + if (circuit->rtcflags & p_linked && circuit->u.link) + { + nprintf(circuit, "%c%c%s %s %s\r", FORMAT, id_keepalive, OurNode, circuit->u.link->call, Verstring); + circuit->u.link->timePollSent = time(NULL); // Also acts as poll + } + } + } + else + { + // No users. Close links + + node_close(); + } +} + +VOID ChatTimer() +{ + // Entered every 10 seconds + + int i = 0; + ChatCIRCUIT *c; + +#ifndef LINBPQ + int len; + CHATNODE *node; + TOPIC *topic; + char Msg[256]; +#endif + USER *user; + time_t NOW = time(NULL); + + GetSemaphore(&ChatSemaphore, 0); + + if (NeedStatus) + { + NeedStatus = FALSE; + SendChatLinkStatus(); + } + +#ifndef LINBPQ + + ClearDebugWindow(); + + WritetoDebugWindow("Chat Nodes\r\n", 12); + + for (node = node_hd; node; node = node->next) + { + len = sprintf_s(Msg, sizeof(Msg), "%s Version %s Count %d\r\n", + node->call, node->Version, node->refcnt); + WritetoDebugWindow(Msg, len); + + i++; + } + + SetDlgItemInt(hWnd, IDC_NODES, i, FALSE); + + WritetoDebugWindow("Chat Links\r\n", 12); + + i = 0; + for (c = circuit_hd; c; c = c->next) + { + if (c->rtcflags & p_linked) + { + char buff[1000]; + int ptr; + CT * ct; + ptr = sprintf_s(buff, sizeof(buff), "%s Topics: ", c->u.user->call); + + if (c->topic) + { + for (ct = c->topic; ct; ct = ct->next) + { + ptr+= sprintf_s(&buff[ptr], sizeof(buff) - ptr, "%s ", ct->topic->name); + } + } + WritetoDebugWindow(buff, ptr); + WritetoDebugWindow("\r\n", 2); + + i++; + } + } + + SetDlgItemInt(hWnd, IDC_LINKS, i, FALSE); + + WritetoDebugWindow("Chat Topics\r\n", 12); + + i = 0; + for (topic = topic_hd; topic; topic = topic->next) + { + len = sprintf_s(Msg, sizeof(Msg), "%s %d\r\n", topic->name, topic->refcnt); + WritetoDebugWindow(Msg, len); + i++; + } + + WritetoDebugWindow("Chat Users\r\n", 12); + + i = 0; + for (user = user_hd; user; user = user->next) + { + len = sprintf_s(Msg, sizeof(Msg), "%s Topic %s\r\n", user->call, + (user->topic) ? user->topic->name : "** Missing Topic **"); + WritetoDebugWindow(Msg, len); + i++; + + if (user->circuit && user->circuit->rtcflags & p_user) // Local User + { + time_t Idle = NOW - user->lastmsgtime; + + if (Idle > 7200) + { + nprintf(user->circuit, "*** Disconnected - Idle time exceeded\r"); + Sleep(1000); + + if (user->circuit->BPQStream < 0) + { + CloseConsole(user->circuit->BPQStream); + break; + } + else + { + Disconnect(user->circuit->BPQStream); + break; + } + } + + if ((user->rtflags & u_keepalive) && (NOW - user->lastsendtime) > 600) + { + nprintf(user->circuit, "Chat Keepalive\r"); + user->lastsendtime = NOW; + } + } + } + + SetDlgItemInt(hWnd, IDC_USERS, i, FALSE); + +#else + + for (user = user_hd; user; user = user->next) + { + if (user->circuit && user->circuit->rtcflags & p_user) // Local User + { + if ((NOW - user->lastmsgtime) > 7200) + { + nprintf(user->circuit, "*** Disconnected - Idle time exceeded\r"); + Sleep(1000); + + if (user->circuit->BPQStream < 0) + { + CloseConsole(user->circuit->BPQStream); + break; + } + else + { + Disconnect(user->circuit->BPQStream); + break; + } + } + + if ((user->rtflags & u_keepalive) && (NOW - user->lastsendtime) > 600) + { + nprintf(user->circuit, "Chat Keepalive\r"); + user->lastsendtime = NOW; + } + } + } + +#endif + + // if no message on a Node-Node link, send poll + + for (c = circuit_hd; c; c = c->next) + { + if (c->rtcflags & p_linked && c->u.link) + { + time_t Now = time(NULL); + LINK * Link = c->u.link; + + if (Now - Link->lastMsgReceived > 60) + { + // if we have a poll outstanding for ? 30 secs close link + // but check other end can handle polls + + if (Link->supportsPolls && Link->timePollSent && Now - Link->timePollSent > 30) + { + Logprintf(LOG_CHAT, c, '|', "%s No Poll Response for %d Secs - Dropping Link", + c->Callsign, Now - Link->timePollSent); + + Disconnect(c->BPQStream); + continue; + } + + Link->timePollSent = Now; + nprintf(c, "%c%c%s %s\r", FORMAT, id_poll, OurNode, Link->call); + } + } + } + + ChatTmr++; + + if (user_hd) // Any Users? + makelinks(); + + if (ChatTmr > 60) // 10 Mins + { + ChatTmr = 1; + node_keepalive(); + } + + FreeSemaphore(&ChatSemaphore); + + if (NeedINFO) + { + NeedINFO--; + + if (NeedINFO == 0) + { + // Send INFO to Chatmap + + char Msg[500]; + int len; + + NeedINFO = 360; // Send Every Hour + + if (Position[0]) + { + len = sprintf(Msg, "INFO %s|%s|%d|\r", Position, PopupText, PopupMode); + + if (len < 256) + Send_MON_Datagram(Msg, len); + } + } + } +} + +VOID FreeChatMemory() +{ + removelinks(); + removeknown(); +} + +// Find a call in the known node list. + +KNOWNNODE *knownnode_find(char *call) +{ + KNOWNNODE *node; + + for (node = known_hd; node; node = node->next) + { + if (matchi(node->call, call)) + break; + } + + return node; +} + +// Add a known node. + +static KNOWNNODE *knownnode_add(char *call) +{ + KNOWNNODE *node; + + node = knownnode_find(call); + + if (!node) + { + node = zalloc(sizeof(KNOWNNODE)); + sl_ins_hd(node, known_hd); + node->call = _strdup(call); + } + + node->LastHeard = time(NULL); + return node; +} + +static char UIDEST[10] = "DUMMY"; +static char AXDEST[7]; +static char ChatMYCALL[7]; + +#pragma pack(1) + + +typedef struct _MESSAGEX +{ +// BASIC LINK LEVEL MESSAGE BUFFER LAYOUT + + struct _MESSAGE * CHAIN; + + UCHAR PORT; + USHORT LENGTH; + + UCHAR DEST[7]; + UCHAR ORIGIN[7]; + +// MAY BE UP TO 56 BYTES OF DIGIS + + UCHAR CTL; + UCHAR PID; + UCHAR DATA[256]; + +}MESSAGEX, *PMESSAGEX; + +#pragma pack() + +SOCKET ChatReportSocket = 0; + + +VOID SetupChat() +{ + u_long param=1; + BOOL bcopt=TRUE; + + ConvToAX25(OurNode, ChatMYCALL); + ConvToAX25(UIDEST, AXDEST); + + sprintf(Verstring, "%d.%d.%d.%d", Ver[0], Ver[1], Ver[2], Ver[3]); + + LoadKnown(); + + ChatReportSocket = socket(AF_INET,SOCK_DGRAM,0); + + if (ChatReportSocket == INVALID_SOCKET) + { + Debugprintf("Failed to create Chat Reporting socket"); + ChatReportSocket = 0; + return; + } + + ioctlsocket (ChatReportSocket, FIONBIO, ¶m); + setsockopt (ChatReportSocket, SOL_SOCKET, SO_BROADCAST, (const char FAR *)&bcopt,4); +} + + +VOID Send_MON_Datagram(UCHAR * Msg, DWORD Len) +{ + MESSAGEX AXMSG; + PMESSAGEX AXPTR = &AXMSG; + + if (Len > 256) + { + Debugprintf("Send_MON_Datagram Error Msg = %s Len = %d", Msg, Len); + return; + } + +// ConvToAX25("GM4OAS-5", ChatMYCALL); + + // Block includes the Msg Header (7 bytes), Len Does not! + + memcpy(AXPTR->DEST, AXDEST, 7); + memcpy(AXPTR->ORIGIN, ChatMYCALL, 7); + AXPTR->DEST[6] &= 0x7e; // Clear End of Call + AXPTR->DEST[6] |= 0x80; // set Command Bit + + AXPTR->ORIGIN[6] |= 1; // Set End of Call + AXPTR->CTL = 3; //UI + AXPTR->PID = 0xf0; + memcpy(AXPTR->DATA, Msg, Len); + + SendChatReport(ChatReportSocket, (char *)&AXMSG.DEST, Len + 16); + + return; + +} + +VOID SendChatLinkStatus() +{ + char Msg[256] = {0}; + LINK * link; + int len = 0; + ChatCIRCUIT *circuit; + + if (ChatApplNum == 0) + return; + +// if (AXIPPort == 0) +// return; + + if (ChatMYCALL[0] == 0) + return; + + for (link = link_hd; link; link = link->next) + { + if (link->flags & p_linked) + { + // Verify connection + + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + if (strcmp(circuit->Callsign, link->alias) == 0) + { + if (circuit->Active == 0) + { + // BPQ Session is dead - Simulate a Disconnect + + circuit->Active = TRUE; // So disconnect will work + Disconnected(circuit->BPQStream); + NeedStatus = TRUE; // Reenter + return; // Link Chain has changed + } + break; + } + } + + if (circuit == 0) + { + // No BPQ Session - is the only answer to restart the node? + + // Logprintf(LOG_DEBUGx, NULL, '!', "Stuck Chat Sesion Detected"); + // Logprintf(LOG_DEBUGx, NULL, '!', "Chat is a mess - forcing a restart"); + // ProgramErrors = 26; + // CheckProgramErrors(); + } + } + + len += sprintf(&Msg[len], "%s %c ", link->call, '0' + link->flags); + + if (len > 240) + break; + } + Msg[len++] = '\r'; + + Send_MON_Datagram(Msg, len); +} + +VOID ClearChatLinkStatus() +{ + LINK * link; + + for (link = link_hd; link; link = link->next) + { + link->flags = 0; + } +} + +BOOL ProcessChatConnectScript(ChatCIRCUIT * conn, char * Buffer, int len) +{ + LINK * link = conn->u.link; + char ** Scripts; + + ChatWriteLogLine(conn, '<', Buffer, len-1, LOG_CHAT); + + Buffer[len] = 0; + _strupr(Buffer); + + Scripts = link->ConnectScript; + + if (strstr(Buffer, "BUSY") || strstr(Buffer, "FAILURE") || + (strstr(Buffer, "DOWNLINK") && strstr(Buffer, "ATTEMPTING") == 0) || + strstr(Buffer, "SORRY") || strstr(Buffer, "INVALID") || strstr(Buffer, "RETRIED") || + strstr(Buffer, "NO CONNECTION TO") || strstr(Buffer, "ERROR - ") || + strstr(Buffer, "UNABLE TO CONNECT") || strstr(Buffer, "DISCONNECTED") || + strstr(Buffer, "FAILED TO CONNECT") || strstr(Buffer, "REJECTED")) + { + // Connect Failed + + link->flags = p_linkfailed; + RunningConnectScript = 0; + link->scriptRunning = 0; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + // The pointer is only updated when we get the connect, so we can tell when the last line is acked + // The first entry is always from Connected event, so don't have to worry about testing entry -1 below + + + if (link->RTLSent) + { + RunningConnectScript = 0; + link->scriptRunning = 0; + link->RTLSent = 0; + + if (memcmp(Buffer, "OK", 2) == 0) + { + // Reply to *RTL + + // Make sure node isn't known. There is a window here that could cause a loop + + if (node_find(conn->u.link->call)) + { + Logprintf(LOG_CHAT, conn, '|', "Dropping link with %s to prevent a loop", conn->Callsign); + Disconnect(conn->BPQStream); + return FALSE; + } + + conn->u.link->flags = p_linked; + conn->rtcflags = p_linked; + state_tell(conn, conn->FBBReplyChars); + NeedStatus = TRUE; + + return TRUE; + } + + // Some other response to *RTL - disconnect + + Logprintf(LOG_CHAT, conn, '|', "Unexpected Response %s to *RTL - Dropping link", Buffer); + Disconnect(conn->BPQStream); + return FALSE; + + } + + if (strstr(Buffer, " CONNECTED") || strstr(Buffer, "PACLEN") || strstr(Buffer, "IDLETIME") || + strstr(Buffer, "OK") || strstr(Buffer, "###LINK MADE") || strstr(Buffer, "VIRTUAL CIRCUIT ESTABLISHED")) + { + char * Cmd; + +LoopBack: + + Cmd = Scripts[++link->ScriptIndex]; + + // Only Check until script is finished + + if (Cmd == 0 || link->ScriptIndex >= link->Lines) + { + link->MoreLines = FALSE; + return TRUE; + } + + if (Cmd && (strcmp(Cmd, " ") == 0 || Cmd[0] == ';' || Cmd[0] == '#')) + goto LoopBack; // Blank line + + // Replace \ with # so can send commands starting with # + + if (Cmd[0] == '\\') + { + Cmd[0] = '#'; + nprintf(conn, "%s\r", Cmd); + Cmd[0] = '\\'; // Put \ back in script + } + else + nprintf(conn, "%s\r", Cmd); + + return TRUE; + } + + if (memcmp(Buffer, "[BPQCHATSERVER-", 15) == 0) + { + char * ptr = strchr(Buffer, ']'); + if (ptr) + { + *ptr = 0; + strcpy(conn->FBBReplyChars, &Buffer[15]); + } + else + conn->FBBReplyChars[0] = 0; + + // Connected - Send *RTL + + nputs(conn, "*RTL\r"); // Log in to the remote RT system. + + conn->u.link->timePollSent = time(NULL); // Keepalive is a poll + nprintf(conn, "%c%c%s %s %s\r", FORMAT, id_keepalive, OurNode, conn->u.link->call, Verstring); + link->RTLSent = 1; + conn->u.link->lastMsgSent = time(NULL); + + return TRUE; + } + +// Anthing else could be ctext. etc. Ignore + + return TRUE; +} + + + +#ifdef LINBPQ + +// LINCHAT specific code + +extern struct SEM OutputSEM; + +static config_t cfg; +static config_setting_t * group; + +extern char pgm[256]; + +char ChatSYSOPCall[50] = ""; + +VOID ChatSendWelcomeMsg(int Stream, ChatCIRCUIT * conn, struct UserInfo * user); + + +int ChatConnected(int Stream) +{ + int n; + ChatCIRCUIT * conn; + struct UserInfo * user = NULL; + char callsign[10]; + int port, paclen, maxframe, l4window; + char ConnectedMsg[] = "*** CONNECTED "; + char Msg[100]; + LINK *link; + KNOWNNODE *node; + + for (n = 0; n < NumberofChatStreams; n++) + { + conn = &ChatConnections[n]; + + if (Stream == conn->BPQStream) + { + if (conn->Active) + { + // Probably an outgoing connect + + if (conn->rtcflags == p_linkini) + { + conn->paclen = chatPaclen; + + // Run first line of connect script + + ProcessChatConnectScript(conn, ConnectedMsg, 15); +// nprintf(conn, "c %s\r", conn->u.link->call); + } + return 0; + } + + memset(conn, 0, sizeof(ChatCIRCUIT)); // Clear everything + conn->Active = TRUE; + conn->BPQStream = Stream; + + conn->Secure_Session = GetConnectionInfo(Stream, callsign, + &port, &conn->SessType, &paclen, &maxframe, &l4window); + + if (paclen == 0) + paclen = 256; + + if (paclen > chatPaclen) + paclen = chatPaclen; + + conn->paclen = paclen; + + strlop(callsign, ' '); // Remove trailing spaces + + memcpy(conn->Callsign, callsign, 10); + + strlop(callsign, '-'); // Remove any SSID + + user = zalloc(sizeof(struct UserInfo)); + + strcpy(user->Call, callsign); + + conn->UserPointer = user; + + n=sprintf_s(Msg, sizeof(Msg), "Incoming Connect from %s", user->Call); + + // Send SID and Prompt + + ChatWriteLogLine(conn, '|',Msg, n, LOG_CHAT); + conn->Flags |= CHATMODE; + + nprintf(conn, ChatSID, Ver[0], Ver[1], Ver[2], Ver[3]); + + // See if from a defined node + + for (link = link_hd; link; link = link->next) + { + if (matchi(conn->Callsign, link->call)) + { + conn->rtcflags = p_linkwait; + return 0; // Wait for *RTL + } + } + + // See if from a previously known node + + node = knownnode_find(conn->Callsign); + + if (node) + { + // A node is trying to link, but we don't have it defined - close + + Logprintf(LOG_CHAT, conn, '!', "Node %s connected, but is not defined as a Node - closing", + conn->Callsign); + + nprintf(conn, "Node %s does not have %s defined as a node to link to - closing.\r", + OurNode, conn->Callsign); + + ChatFlush(conn); + + Sleep(500); + conn->rtcflags = p_nil; + + Disconnect(conn->BPQStream); + + return 0; + } + + if (user->Name[0] == 0) + { + char * Name = lookupuser(user->Call); + + if (Name) + { + if (strlen(Name) > 17) + Name[17] = 0; + + strcpy(user->Name, Name); + free(Name); + } + else + { + conn->Flags |= GETTINGUSER; + nputs(conn, NewUserPrompt); + return TRUE; + } + } + + ChatSendWelcomeMsg(Stream, conn, user); + RefreshMainWindow(); + ChatFlush(conn); + + return 0; + } + } + + return 0; +} + +int ChatDisconnected (ChatCIRCUIT * conn) +{ + struct UserInfo * user = NULL; + int Stream = conn->BPQStream; + char Msg[255]; + int len; + + if (conn->Active == FALSE) + return 0; + + ChatClearQueue(conn); + + conn->Active = FALSE; + + if (conn->Flags & CHATMODE) + { + if (conn->Flags & CHATLINK && conn->u.link) + { + // if running connect script, clear script active + + if (conn->u.link->flags & p_linkini) + { + RunningConnectScript = 0; + conn->u.link->scriptRunning = 0; + } + + len=sprintf_s(Msg, sizeof(Msg), "Chat Node %s Disconnected", conn->u.link->call); + ChatWriteLogLine(conn, '|',Msg, len, LOG_CHAT); + link_drop(conn); + + } + else + { + len=sprintf_s(Msg, sizeof(Msg), "Chat User %s Disconnected", conn->Callsign); + ChatWriteLogLine(conn, '|',Msg, len, LOG_CHAT); + + logout(conn); + + } + + conn->Flags = 0; + conn->u.link = NULL; + conn->UserPointer = NULL; + return 0; + } + + return 0; +} + +int ChatDoReceivedData(ChatCIRCUIT * conn) +{ + int count, InputLen; + UINT MsgLen; + int Stream = conn->BPQStream; + struct UserInfo * user; + char * ptr, * ptr2; + char Buffer[10000]; + + + // May have several messages per packet, or message split over packets + + if (conn->InputLen + 1000 > 10000) // Shouldnt have lines longer than this in text mode + conn->InputLen = 0; // discard + + GetMsg(Stream, &conn->InputBuffer[conn->InputLen], &InputLen, &count); + + if (InputLen == 0) return 0; + + conn->Watchdog = 900; // 15 Minutes + conn->InputLen += InputLen; + +loop: + + if (conn->InputLen == 1 && conn->InputBuffer[0] == 0) // Single Null + { + conn->InputLen = 0; + + if (conn->u.user->circuit && conn->u.user->circuit->rtcflags & p_user) // Local User + conn->u.user->lastmsgtime = time(NULL); + + return 0; + } + + ptr = memchr(conn->InputBuffer, '\r', conn->InputLen); + + if (ptr) // CR in buffer + { + user = conn->UserPointer; + + ptr2 = &conn->InputBuffer[conn->InputLen]; + + if (++ptr == ptr2) + { + // Usual Case - single meg in buffer + + if (conn->rtcflags == p_linkini) // Chat Connect + ProcessChatConnectScript(conn, conn->InputBuffer, conn->InputLen); + else + ProcessChatLine(conn, user, conn->InputBuffer, conn->InputLen); + conn->InputLen=0; + } + else + { + // buffer contains more that 1 message + + MsgLen = conn->InputLen - (int)(ptr2-ptr); + + memcpy(Buffer, conn->InputBuffer, MsgLen); + + if (conn->rtcflags == p_linkini) + ProcessChatConnectScript(conn, Buffer, MsgLen); + else + ProcessChatLine(conn, user, Buffer, MsgLen); + + if (*ptr == 0 || *ptr == '\n') + { + /// CR LF or CR Null + + ptr++; + conn->InputLen--; + } + + memmove(conn->InputBuffer, ptr, conn->InputLen-MsgLen); + conn->InputLen -= MsgLen; + + goto loop; + + } + } + return 0; +} + + +int ChatPollStreams() +{ + int state,change; + ChatCIRCUIT * conn; + int n; + struct UserInfo * user = NULL; + char ConnectedMsg[] = "*** CONNECTED "; + + for (n = 0; n < NumberofChatStreams; n++) + { + conn = &ChatConnections[n]; + + SessionState(conn->BPQStream, &state, &change); + + if (change == 1) + { + if (state == 1) // Connected + { + GetSemaphore(&ConSemaphore, 0); + ChatConnected(conn->BPQStream); + FreeSemaphore(&ConSemaphore); + } + else + { + GetSemaphore(&ConSemaphore, 0); + ChatDisconnected(conn); + FreeSemaphore(&ConSemaphore); + } + } + + ChatDoReceivedData(conn); + } + + return 0; +} + + +BOOL GetChatConfig(char * ConfigName) +{ + config_init(&cfg); + + /* Read the file. If there is an error, report it and exit. */ + + if(! config_read_file(&cfg, ConfigName)) + { + fprintf(stderr, "%d - %s\n", + config_error_line(&cfg), config_error_text(&cfg)); + config_destroy(&cfg); + return(EXIT_FAILURE); + } + + group = config_lookup (&cfg, "Chat"); + + if (group == NULL) + return EXIT_FAILURE; + + ChatApplNum = GetIntValue(group, "ApplNum"); + MaxChatStreams = GetIntValue(group, "MaxStreams"); + reportChatEvents = GetIntValue(group, "reportChatEvents"); + chatPaclen = GetIntValue(group, "chatPaclen"); + GetStringValue(group, "OtherChatNodes", OtherNodesList, 1000); + GetStringValue(group, "ChatWelcomeMsg", ChatWelcomeMsg, 1000); + GetStringValue(group, "MapPosition", Position, 81); + GetStringValue(group, "MapPopup", PopupText, 260); + PopupMode = GetIntValue(group, "PopupMode"); + + if (chatPaclen == 0) + chatPaclen = 236; + + if (chatPaclen < 60) + chatPaclen = 60; + + + return EXIT_SUCCESS; +} + +VOID SaveChatConfigFile(char * ConfigName) +{ + config_setting_t *root, *group; + + // Get rid of old config before saving + + config_init(&cfg); + + root = config_root_setting(&cfg); + + group = config_setting_add(root, "Chat", CONFIG_TYPE_GROUP); + + SaveIntValue(group, "ApplNum", ChatApplNum); + SaveIntValue(group, "MaxStreams", MaxChatStreams); + SaveIntValue(group, "reportChatEvents", reportChatEvents); + SaveIntValue(group, "chatPaclen", chatPaclen); + SaveStringValue(group, "OtherChatNodes", OtherNodesList); + SaveStringValue(group, "ChatWelcomeMsg", ChatWelcomeMsg); + + SaveStringValue(group, "MapPosition", Position); + SaveStringValue(group, "MapPopup", PopupText); + SaveIntValue(group, "PopupMode", PopupMode); + + if(! config_write_file(&cfg, ConfigName)) + { + fprintf(stderr, "Error while writing file.\n"); + config_destroy(&cfg); + return; + } + config_destroy(&cfg); +} + +BOOL ChatInit() +{ + char * ptr1 = GetApplCall(ChatApplNum); + char * ptr2; + char * Context; + int i; + ChatCIRCUIT * conn; + + + if (*ptr1 < 0x21) + { + printf("No APPLCALL for Chat APPL\n"); + return FALSE; + } + + memcpy(OurNode, ptr1, 10); + strlop(OurNode, ' '); + + ptr1 = GetApplAlias(ChatApplNum); + memcpy(OurAlias, ptr1,10); + strlop(OurAlias, ' '); + + if (ChatSYSOPCall[0] == 0) + { + strcpy(ChatSYSOPCall, OurNode); + strlop(ChatSYSOPCall, '-'); + } + + sprintf(ChatSignoffMsg, "73 de %s\r", ChatSYSOPCall); + + if (ChatWelcomeMsg[0] == 0) + sprintf(ChatWelcomeMsg, "%s's Chat Server.$WType /h for command summary.$WBringing up links to other nodes.$W" + "This may take a minute or two.$WThe /p command shows what nodes are linked.$W", ChatSYSOPCall); + + ChatApplMask = 1<<(ChatApplNum-1); + + // Set up other nodes list. rtlink messes with the string so pass copy + + // On first run config will have spaces not newlines + + if (strchr(OtherNodesList, '\r')) // Has connect script entries + { + ptr2 = ptr1 = strtok_s(_strdup(OtherNodesList), "\r\n", &Context); + + while (ptr1 && ptr1[0]) + { + rtlink(ptr1); + ptr1 = strtok_s(NULL, "\r\n", &Context); + } + } + else + { + ptr2 = ptr1 = strtok_s(_strdup(OtherNodesList), " ,\r", &Context); + + while (ptr1) + { + rtlink(ptr1); + ptr1 = strtok_s(NULL, " ,\r", &Context); + } + } + + free(ptr2); + + SetupChat(); + + // Allocate Streams + + strcpy(pgm, "CHAT"); + + for (i = 0; i < MaxChatStreams; i++) + { + conn = &ChatConnections[i]; + conn->BPQStream = FindFreeStream(); + + if (conn->BPQStream == 255) break; + + NumberofChatStreams++; + + SetAppl(conn->BPQStream, 3, ChatApplMask); + Disconnect(conn->BPQStream); + } + + strcpy(pgm, "LINBPQ"); + + return TRUE; +} + +#endif + +void ChatFlush(ChatCIRCUIT * conn) +{ + int tosend, len, sent; + + // Try to send data to user. May be stopped by user paging or node flow control + + // UCHAR * OutputQueue; // Messages to user + // int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message + // int OutputGetPointer; // Next byte to send. When Getpointer = Quele Length all is sent - free the buffer and start again. + + // BOOL Paging; // Set if user wants paging + // int LinesSent; // Count when paging + // int PageLen; // Lines per page + + + tosend = conn->OutputQueueLength - conn->OutputGetPointer; + + sent = 0; + + while (tosend > 0) + { + if (TXCount(conn->BPQStream) > 4) + return; // Busy + + if (tosend <= conn->paclen) + len = tosend; + else + len=conn->paclen; + + GetSemaphore(&OutputSEM, 0); + + SendUnbuffered(conn->BPQStream, &conn->OutputQueue[conn->OutputGetPointer], len); + + conn->OutputGetPointer += len; + + FreeSemaphore(&OutputSEM); + + tosend -= len; + sent++; + + if (sent > 4) + return; + } + + // All Sent. Free buffers and reset pointers + + ChatClearQueue(conn); +} + +VOID ChatClearQueue(ChatCIRCUIT * conn) +{ + GetSemaphore(&OutputSEM, 0); + + conn->OutputGetPointer = 0; + conn->OutputQueueLength = 0; + + FreeSemaphore(&OutputSEM); +} + +#ifdef LINBPQ +void ChatTrytoSend() +{ + // call Flush on any connected streams with queued data + + ChatCIRCUIT * conn; + + int n; + + for (n = 0; n < NumberofChatStreams; n++) + { + conn = &ChatConnections[n]; + + if (conn->Active == TRUE) + ChatFlush(conn); + } +} + +VOID CloseChat() +{ + int BPQStream, n; + + for (n = 0; n < NumberofChatStreams; n++) + { + BPQStream = ChatConnections[n].BPQStream; + + if (BPQStream) + { + SetAppl(BPQStream, 0, 0); + Disconnect(BPQStream); + DeallocateStream(BPQStream); + } + } + + ClearChatLinkStatus(); + SendChatLinkStatus(); + Sleep(1000); // A bit of time for links to close + SendChatLinkStatus(); // Send again to reduce chance of being missed + FreeChatMemory(); +} + +VOID SendChatReport(SOCKET ChatReportSocket, char * buff, int txlen) +{ + unsigned short int crc = compute_crc(buff, txlen); + + crc ^= 0xffff; + + buff[txlen++] = (crc&0xff); + buff[txlen++] = (crc>>8); + + sendto(ChatReportSocket, buff, txlen, 0, (LPSOCKADDR)&Chatreportdest, sizeof(Chatreportdest)); + +} + +#endif + + +#ifndef WIN32 +#define INVALID_HANDLE_VALUE (void *)-1 +#endif + +static FILE * LogHandle[4] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; + +static time_t LastLogTime[4] = {0, 0, 0, 0}; + +static char FilesNames[4][100] = {"", "", "", ""}; + +static char * Logs[4] = {"BBS", "CHAT", "TCP", "DEBUG"}; + + +BOOL ChatOpenLogfile(int Flags) +{ + UCHAR FN[MAX_PATH]; + time_t LT; + struct tm * tm; + + LT = time(NULL); + tm = gmtime(<); + + sprintf(FN,"%s/logs/log_%02d%02d%02d_%s.txt", GetLogDirectory(), tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, Logs[Flags]); + + LogHandle[Flags] = fopen(FN, "ab"); + +#ifndef WIN32 + + if (strcmp(FN, &FilesNames[Flags][0])) + { + UCHAR SYMLINK[MAX_PATH]; + + sprintf(SYMLINK,"%s/logLatest_%s.txt", GetBPQDirectory(), Logs[Flags]); + unlink(SYMLINK); + strcpy(&FilesNames[Flags][0], FN); + symlink(FN, SYMLINK); + } + +#endif + + return (LogHandle[Flags] != NULL); +} + +void ChatWriteLogLine(ChatCIRCUIT * conn, int Flag, char * Msg, int MsgLen, int Flags) +{ + char CRLF[2] = {0x0d,0x0a}; + struct tm * tm; + char Stamp[20]; + time_t T; + +#ifndef LINBPQ + + if (hMonitor) + { + if (Flags == LOG_CHAT) + { + WritetoMonitorWindow((char *)&Flag, 1); + + if (conn && conn->Callsign[0]) + { + char call[20]; + sprintf(call, "%s ", conn->Callsign); + WritetoMonitorWindow(call, 10); + } + else + WritetoMonitorWindow(" ", 10); + + WritetoMonitorWindow(Msg, MsgLen); + if (Msg[MsgLen-1] != '\r') + WritetoMonitorWindow(CRLF , 1); + } + else if (Flags == LOG_DEBUGx) + { + WritetoMonitorWindow((char *)&Flag, 1); + WritetoMonitorWindow(Msg, MsgLen); + WritetoMonitorWindow(CRLF , 1); + } + + } + +#endif + + if (Flags == LOG_CHAT && !LogCHAT) + return; + + if (LogHandle[Flags] == INVALID_HANDLE_VALUE) ChatOpenLogfile(Flags); + + if (LogHandle[Flags] == INVALID_HANDLE_VALUE) return; + + T = time(NULL); + tm = gmtime(&T); + + sprintf(Stamp,"%02d%02d%02d %02d:%02d:%02d %c", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, Flag); + + fwrite(Stamp, 1, strlen(Stamp), LogHandle[Flags]); + + if (conn && conn->Callsign[0]) + { + char call[20]; + sprintf(call, "%s ", conn->Callsign); + fwrite(call, 1, 10, LogHandle[Flags]); + } + else + fwrite(" ", 1, 10, LogHandle[Flags]); + + fwrite(Msg, 1, MsgLen, LogHandle[Flags]); + + if (Flags == LOG_CHAT && Msg[MsgLen-1] == '\r') + fwrite(&CRLF[1], 1, 1, LogHandle[Flags]); + else + fwrite(CRLF, 1, 2, LogHandle[Flags]); + + fclose(LogHandle[Flags]); + LogHandle[Flags] = INVALID_HANDLE_VALUE; +} + + diff --git a/.svn/pristine/0d/0dfe91be8ae70b991986eaaa9e579a14e878d39f.svn-base b/.svn/pristine/0d/0dfe91be8ae70b991986eaaa9e579a14e878d39f.svn-base index b85b576..0a8bf06 100644 --- a/.svn/pristine/0d/0dfe91be8ae70b991986eaaa9e579a14e878d39f.svn-base +++ b/.svn/pristine/0d/0dfe91be8ae70b991986eaaa9e579a14e878d39f.svn-base @@ -1,1652 +1,1652 @@ -#ifndef WINVER // Allow use of features specific to Windows XP or later. -#define WINVER 0x0501 // Change this to the appropriate value to target other versions of Windows. -#endif - -#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. -#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. -#endif - -#ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or later. -#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later. -#endif - -#ifndef _WIN32_IE // Allow use of features specific to IE 6.0 or later. -#define _WIN32_IE 0x0600 // Change this to the appropriate value to target other versions of IE. -#endif - - -#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers -#define _CRT_SECURE_NO_DEPRECATE -#define _WINSOCK_DEPRECATED_NO_WARNINGS - -#define LIBCONFIG_STATIC -#include - -#ifndef WIN32 -#include -#endif - -#include "compatbits.h" - -#ifndef LINBPQ -#include "bpq32.h" -#include "BPQMailrc.h" -#include "dbghelp.h" -#else -#include "cheaders.h" -#endif - -#include "asmstrucs.h" - -#define MaxBPQPortNo 63 // Port 64 reserved for BBS Mon -#define MAXBPQPORTS 63 - -#define NEWROUTING - -extern int FOURCHARCONT; - -// Standard __except handler for try/except - -VOID CheckProgramErrors(); -VOID WriteMiniDump(); - -extern int ProgramErrors; - -extern struct _EXCEPTION_POINTERS exinfox; - -#ifdef WIN32 -void Dump_Process_State(struct _EXCEPTION_POINTERS * exinfo, char * Msg); - -#define My__except_Routine(Message) \ -__except(memcpy(&exinfo, GetExceptionInformation(), sizeof(struct _EXCEPTION_POINTERS)), EXCEPTION_EXECUTE_HANDLER)\ -{\ - Debugprintf("MAILCHAT *** Program Error %x at %x in %s EAX %x EBX %x ECX %x EDX %x ESI %x EDI %x",\ - exinfo.ExceptionRecord->ExceptionCode, exinfo.ExceptionRecord->ExceptionAddress, Message,\ - exinfo.ContextRecord->Eax, exinfo.ContextRecord->Ebx, exinfo.ContextRecord->Ecx,\ - exinfo.ContextRecord->Edx, exinfo.ContextRecord->Esi, exinfo.ContextRecord->Edi);\ - CheckProgramErrors();\ - WriteMiniDump();\ -} - - -/* -#define My__except_Routine(Message) \ -__except(memcpy(&exinfox, GetExceptionInformation(), sizeof(struct _EXCEPTION_POINTERS)), EXCEPTION_EXECUTE_HANDLER)\ -{\ - Dump_Process_State(&exinfox, Message);\ - CheckProgramErrors();\ -} - -#define My__except_RoutineWithDisconnect(Message) \ -__except(memcpy(&exinfo, GetExceptionInformation(), sizeof(struct _EXCEPTION_POINTERS)), EXCEPTION_EXECUTE_HANDLER)\ -{\ - Debugprintf("MAILCHAT *** Program Error %x at %x in %s EAX %x EBX %x ECX %x EDX %x ESI %x EDI %x",\ - exinfo.ExceptionRecord->ExceptionCode, exinfo.ExceptionRecord->ExceptionAddress, Message,\ - exinfo.ContextRecord->Eax, exinfo.ContextRecord->Ebx, exinfo.ContextRecord->Ecx,\ - exinfo.ContextRecord->Edx, exinfo.ContextRecord->Esi, exinfo.ContextRecord->Edi);\ - FreeSemaphore(&ChatSemaphore);\ - if (conn->BPQStream < 0)\ - CloseConsole(conn->BPQStream);\ - else\ - Disconnect(conn->BPQStream);\ -} -*/ -#define My_except_RoutineWithDiscBBS(Message) \ -__except(memcpy(&exinfo, GetExceptionInformation(), sizeof(struct _EXCEPTION_POINTERS)), EXCEPTION_EXECUTE_HANDLER)\ -{\ - Debugprintf("MAILCHAT *** Program Error %x at %x in %s EAX %x EBX %x ECX %x EDX %x ESI %x EDI %x",\ - exinfo.ExceptionRecord->ExceptionCode, exinfo.ExceptionRecord->ExceptionAddress, Message,\ - exinfo.ContextRecord->Eax, exinfo.ContextRecord->Ebx, exinfo.ContextRecord->Ecx,\ - exinfo.ContextRecord->Edx, exinfo.ContextRecord->Esi, exinfo.ContextRecord->Edi);\ - if (conn->BPQStream < 0)\ - CloseConsole(conn->BPQStream);\ - else\ - Disconnect(conn->BPQStream);\ - CheckProgramErrors();\ -} -#endif -#define MAXUSERNAMELEN 6 - -#define WSA_ACCEPT WM_USER + 1 -#define WSA_CONNECT WM_USER + 2 -#define WSA_DATA WM_USER + 3 -#define NNTP_ACCEPT WM_USER + 4 -#define NNTP_DATA WM_USER + 5 - -#ifdef _DEBUG - -VOID * _malloc_dbg_trace(int len, int type, char * file, int line); - -#define malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) -#define calloc(c, s) _calloc_dbg(c, s, _NORMAL_BLOCK, __FILE__, __LINE__) -#define realloc(p, s) _realloc_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__) -#define _recalloc(p, c, s) _recalloc_dbg(p, c, s, _NORMAL_BLOCK, __FILE__, __LINE__) -#define _expand(p, s) _expand_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__) -#define free(p) _free_dbg(p, _NORMAL_BLOCK) -#define _strdup(s) _strdup_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) - - -#define zalloc(s) _zalloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) -#else -#define zalloc(s) _zalloc(s) -#endif - -#ifdef LINBPQ - -#undef zalloc -#define zalloc _zalloc - -#endif - -VOID * _zalloc_dbg(size_t len, int type, char * file, int line); - -#define LOG_BBS 0 -#define LOG_CHAT 1 -#define LOG_TCP 2 -#define LOG_DEBUG_X 3 - - -//Chat Duplicate suppression Code - -#define MAXDUPS 10 // Number to keep -#define DUPSECONDS 5 // TIme to Keep - -struct DUPINFO -{ - time_t DupTime; - char DupUser[10]; - char DupText[100]; -}; - - -struct UserRec -{ - char * Callsign; - char * UserName; - char * Password; -}; - - - -typedef struct ConnectionInfo_S -{ - struct ConnectionInfo_S *next; - PROC *proc; - UCHAR RadioOnlyMode; // T or R flag for Radio Only mode. - - int Number; // Number of record - for Connections display - BOOL Active; - int BPQStream; - int paclen; - UCHAR Callsign[11]; // Station call including SSID - BOOL GotHeader; - UCHAR InputMode; // Line by Line or Binary or YAPP - - UCHAR * InputBuffer; - int InputBufferLen; - int InputLen; // Data we have already = Offset of end of an incomplete packet; - - struct UserInfo * UserPointer; - int Retries; - int LoginState; // 1 = user ok, 2 = password ok - int Flags; - - // Data to the user is kept in a malloc'd buffer. This can be appended to, - // and data sucked out under both terminal and system flow control. PACLEN is - // enfored when sending to node. - - UCHAR * OutputQueue; // Messages to user - int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message - int OutputGetPointer; // Next byte to send. When Getpointer = Queue Length all is sent - free the buffer and start again. - - int CloseAfterFlush; // Close session when all sent. Set to 100ms intervals to wait. - - int ErrorCount; // Invalid Command count - BOOL Paging; // Set if user wants paging - int LinesSent; // Count when paging - int PageLen; // Lines per page - - UCHAR * MailBuffer; // Mail Message being received - UCHAR * CopyBuffer; // Mail Message being forwarded - int MailBufferSize; // Total Malloc'ed size. Actual size in in Msg Struct - - long lastmsg; // Last Listed. Stored here, updated in user record only on clean close - BOOL sysop; // Set if user is authenticated as a sysop - BOOL Secure_Session; // Set if Local Terminal, or Telnet connect with SYSOP status - UINT BBSFlags; // Set if defined as a bbs and SID received - struct MsgInfo * TempMsg; // Header while message is being received - struct MsgInfo * FwdMsg; // Header while message is being forwarded - - char ** To; // May be several Recipients - int ToCount; - - int BBSNumber; // The BBS number (offset into bitlist of BBSes to forward a message to - int NextMessagetoForward; // Next index to check in forward cycle - BOOL BPQBBS; // Set if SID indicates other end is BPQ - char MSGTYPES[20]; // Any MSGTYPEFLAGS - BOOL SendT; // Send T messages - BOOL SendP; // Send P messages - BOOL SendB; // Send Bulls - BOOL SendWL2KFW; // send ;FW: - int MaxBLen; // Max Size for this session - int MaxPLen; // Max Size for this session - int MaxTLen; // Max Size for this session - BOOL DoReverse; // Request Reverse Forward - char LastForwardType; // Last type of messages forwarded - struct FBBHeaderLine * FBBHeaders; // The Headers from an FFB forward block - char FBBReplyChars[36]; //The +-=!nnnn chars for the 5 proposals - int FBBReplyIndex; // current Reply Pointer - int FBBIndex; // current propopsal number - int RestartFrom; // Restart position - BOOL NeedRestartHeader; // Set if waiting for 6 byte restart header - BOOL DontSaveRestartData; // Set if corrupt data received - BOOL FBBMsgsSent; // Messages need to be maked as complete when next command received - UCHAR FBBChecksum; // Header Checksum - BOOL OpenBCM; // OpenBCM mode (escape -xFF chars) - BOOL InTelnetExcape; // Last Char was 0xff - BOOL LocalMsg; // Set if current Send command is for a local user - BOOL NewUser; // Set if first time user has accessed BBS - BOOL Paclink; // Set if receiving messages from Paclink - BOOL RMSExpress; // Set if receiving messages from RMS Express - BOOL WL2K; // Set if communicating with a CMS - BOOL PAT; // Set if communicating with PAT - char ** PacLinkCalls; // Calls we are getting messages for - BOOL SkipPrompt; // Set if a remote node sends a > at the end of his CTEXT - BOOL SkipConn; // Node sends "connected" in its CTEXT - int Watchdog; // Hung Circuit Detect. - int SessType; // BPQ32 sesstype bits - -#define Sess_L2LINK 1 -#define Sess_SESSION 2 -#define Sess_UPLINK 4 -#define Sess_DOWNLINK 8 -#define Sess_BPQHOST 0x20 -#define Sess_PACTOR 0x40 - - HANDLE DebugHandle; // File Handle for session-based debugging - - char ARQFilename[256]; // Filename from ARQ:FILE:: Header - int ARQClearCount; // To make sure queues are flushed when sending - - int SIDResponseTimer; // Used to detect incomplete handshake - - char PQChallenge[20]; // Secure User logon challange - char SecureMsg[20]; // CMS Secure Signon Response - int MCastListenTime; // Time to run session for - - int YAPPLen; // Bytes sent/received of YAPP Message - long YAPPDate; // Date for received file - if set enables YAPPC - - int SyncCompressedLen; - int SyncXMLLen; - int SyncMsgLen; - char * SyncHost; // Saved so can send "request sync" - int SyncPort; - UCHAR * SyncMessage; // Compressed SYNC message to send - - // These are used to detect CRLF split over a packet boundary - int usingCR; // Session is (normally) using CR as terminator - int lastLineEnd; // Terminator for current line - - struct ConnectionInfo_S * SysopChatStream; // Stream sysop is chatting to - -} ConnectionInfo, CIRCUIT; - -// Flags Equates - -#define GETTINGUSER 1 -#define GETTINGBBS 2 -#define CHATMODE 4 -#define GETTINGTITLE 8 -#define GETTINGMESSAGE 16 -#define CHATLINK 32 // Link to another Chat Node -#define SENDTITLE 64 -#define SENDBODY 128 -#define WAITPROMPT 256 // Waiting for prompt after message -#define PROPOSINGSYNCMSG 512 // Sent proposal to SYNC, waiting response -#define SENDINGSYNCMSG 1024 // Sent message to SYNC, waiting response -#define REQUESTINGSYNC 2048 -#define GETTINGSYNCMESSAGE 4096 // Receiving body of a SYNC message - -// BBSFlags Equates - -#define BBS 1 -#define FBBForwarding 2 -#define FBBCompressed 4 -#define FBBB1Mode 8 -#define FBBB2Mode 16 -#define RunningConnectScript 32 -#define MBLFORWARDING 64 // MBL Style Frwarding- waiting for OK/NO or Prompt following message -#define TEXTFORWARDING 128 // Plain Text forwarding -#define OUTWARDCONNECT 256 // We connected to them -#define FLARQMODE 512 // Message from FLARQ -#define FLARQMAIL 1024 // Sending FLARQ Format Message -#define ARQMAILACK 2048 // Waiting for all data to be acked -#define NEEDLF 4096 // Add LF to forward script commands (fro Telnet -#define MCASTRX 8192 // Stream in Multicast RX Mode -#define DISCONNECTING 16384 // Disconnect sent to Node -#define YAPPTX 0x008000 // Sending YAPP file -#define SYSOPCHAT 0x010000 // Chatting to BBS console -#define WINLINKRO 0x020000 // WL2K RO (no J in SID) -#define SYNCMODE 0x040000 // RMS RELAY SYNC -#define MFJMODE 0x080000 // MFJ PMS -#define NEWPACCOM 0x100000 // PACCOM PMS 3.2 -#define SETCALLTOSENDER 0x200000 // Set calling call to message sender - - -struct FBBRestartData -{ - struct MsgInfo * TempMsg; // Header while message is being received - struct UserInfo * UserPointer; - UCHAR * MailBuffer; // Mail Message being received - int MailBufferSize; // Total Malloc'ed size. Actual size in in Msg Struct - int Count; // Give up if too many restarts -}; - -// We need to keep the B2Message file for B2 messages we are sending until the messages is acked, so -// we can restart it. Otherwise the file may change, resulting in a checksum error - - -struct B2RestartData -{ - int CSize; // Compresses Size (B2 proto) - UCHAR * CompressedMsg; // Compressed Body fo B2 - struct MsgInfo * FwdMsg; - struct UserInfo * UserPointer; - int Count; // Give up if too many restarts -}; - -//------ TAJ ----- -typedef struct PGARGS -{ - CIRCUIT * conn; - struct UserInfo * user; - char InputBuffer[80]; - int Len; -}RUNPGARGS, *RUNPGARGS_PTR; - -//--------------- - -#pragma pack(1) - -struct TempUserInfo -{ - int LastAuthCode; // Protect against playback attack - - // Fields used to allow interrupting and resuming a paged listing - - BOOL ListActive; // Doing a list - BOOL ListSuspended; // Paused doing a list - int LastListedInPagedMode; - char LastListCommand[80]; - char LastListParams[80]; - int LinesSent; - char SendFullFrom; - char ListType; - char ListDirn; - char ListStatus; - char ListSelector; // < > @ etc - - int ListRangeStart; - int ListRangeEnd; - int LLCount; // Number still to send in List Last N - int UpdateLatest; // if set, save last listed as latest - BOOL IncludeKilled; // Show Killed Messages if SYSOP - //--- TAJ --- - int PG_INDEX; // current index of PG server - int PG_SERVER; // PG server to run - RUNPGARGS_PTR RUNPGPARAMS; // pointer to RUNPGARGS for dealloc - //----------- -}; - -#define PMSG 1 -#define BMSG 2 -#define TMSG 3 - -struct OldUserInfo -{ - // Old format - without message type specific traffic counts - - char Call[10]; // Connected call without SSID -// indicat relai[8]; /* 64 Digis path */ - int lastmsg; /* 4 Last L number */ - int ConnectsIn; /* 4 Number of connexions in*/ - int TimeLastConnected; //Last connexion date */ -// long lastyap __a2__ ; /* 4 Last YN date */ - ULONG flags ; /* 4 Flags */ - - UCHAR PageLen; // Lines Per Page - UCHAR lang ; /* 1 Language */ - - int Xnewbanner; /* 4 Last Banner date */ - short Xdownload ; /* 2 download size (KB) = 100 */ - char POP3Locked ; // Nonzero if POP3 server has locked this user (stops other pop3 connections, or BBS user killing messages) - char BBSNumber; // BBS Bitmap Index Number - struct BBSForwardingInfo * ForwardingInfo; - struct UserInfo * BBSNext; // links BBS record - struct TempUserInfo * Temp; // Working Fields - not saved in user file - char xfree[6]; /* 6 Spare */ - char Xtheme; /* 1 Current topic */ - - char Name[18]; /* 18 1st Name */ - char Address[61]; /* 61 Address */ - - // Stats. Was City[31]; /* 31 City */ - - int MsgsReceived; - int MsgsSent; - int MsgsRejectedIn; // Messages we reject - int MsgsRejectedOut; // Messages Rejectd by other end - int BytesForwardedIn; - int BytesForwardedOut; - int ConnectsOut; // Forwarding Connects Out - - USHORT RMSSSIDBits; // SSID's to poll in RMS - - char Spare1; - - char HomeBBS[41]; /* 41 home BBS */ - char QRA[7]; /* 7 Qth Locator */ - char pass[13]; /* 13 Password */ - char ZIP[9]; /* 9 Zipcode */ - BOOL spare; -} ; /* Total : 360 bytes */ - -struct MsgStats -{ - int ConnectsIn; /* 4 Number of connexions in*/ - int ConnectsOut; // Forwarding Connects Out - - // Stats saveed by message type - - int MsgsReceived[4]; - int MsgsSent[4]; - int MsgsRejectedIn[4]; // Messages we reject - int MsgsRejectedOut[4]; // Messages Rejectd by other end - int BytesForwardedIn[4]; - int BytesForwardedOut[4]; -}; - -struct UserInfo -{ - // New Format - with stats maintained by message type and unused fields removed. - - // This is no longer a fixed length record so can't be saved as a binarl - - char Call[10]; // Connected call without SSID - - int Length; // To make subsequent format changes easier - - int lastmsg; /* 4 Last L number */ - int xTimeLastConnected; //Last connexion date */ - ULONG flags ; /* 4 Flags */ - - UCHAR PageLen; // Lines Per Page - - char POP3Locked ; // Nonzero if POP3 server has locked this user (stops other pop3 connections, or BBS user killing messages) - unsigned char BBSNumber; // BBS Bitmap Index Number - struct BBSForwardingInfo * ForwardingInfo; - struct UserInfo * BBSNext; // links BBS record - struct TempUserInfo * Temp; // Working Fields - not saved in user file - char Name[18]; /* 18 1st Name */ - char Address[61]; /* 61 Address */ - - USHORT RMSSSIDBits; // SSID's to poll in RMS - - char HomeBBS[41]; /* 41 home BBS */ - char QRA[7]; /* 7 Qth Locator */ - char pass[13]; /* 13 Password */ - char ZIP[9]; /* 9 Zipcode */ - - struct MsgStats Total; - struct MsgStats Last; - - char CMSPass[16]; // For Secure Signon - int WebSeqNo; - - long long TimeLastConnected; //Last connection date */ - - char Filler[44 - 8]; // So we can add a few fields wirhout another resize -}; - -// flags equates - -#define F_Excluded 0x0001 -#define F_GGG 0x0002 -#define F_Expert 0x0004 -#define F_SYSOP 0x0008 -#define F_BBS 0x0010 -#define F_RMSREDIRECT 0x0020 -#define F_BBB 0x0040 -#define F_CCC 0x0080 -#define F_DDD 0x0100 -#define F_EEE 0x0200 -#define F_FFF 0x0400 -#define F_PMS 0x0800 -#define F_EMAIL 0x1000 -#define F_HOLDMAIL 0x2000 -#define F_POLLRMS 0x4000 -#define F_SYSOP_IN_LM 0x8000 -#define F_Temp_B2_BBS 0x00010000 // "Winlink Express User" -#define F_NOWINLINK 0x00020000 // Don't add Winlink.org -#define F_NOBULLS 0x00040000 -#define F_NTSMPS 0x00080000 -#define F_APRSMFOR 0x00100000 // Send APRS message for new mail -#define F_APRSSSID 0xF0000000 // (Top 4 Bits - - -struct Override -{ - char * Call; - int Days; -}; - -struct ALIAS -{ - char * Alias; - char * Dest; -}; - -typedef struct _MESSAGEX -{ -// BASIC LINK LEVEL MESSAGE BUFFER LAYOUT - - struct _MESSAGEX * CHAIN; - - UCHAR PORT; - USHORT LENGTH; - - UCHAR DEST[7]; - UCHAR ORIGIN[7]; - -// MAY BE UP TO 56 BYTES OF DIGIS - - UCHAR CTL; - UCHAR PID; - UCHAR DATA[256]; - UCHAR DIGIS[56]; // Padding in case we have digis - -}MESSAGEX, *PMESSAGEX; - - -#pragma pack() - -// Message Database Entry. Designed to be compatible with FBB - -#define NBBBS 160 // Max BBSes we can forward to. Must be Multiple of 8, and must be 80 for FBB compatibliliy -#define NBMASK NBBBS/8 // Number of bytes in Forward bitlists. - -#pragma pack(1) - -struct OldMsgInfo -{ - char type ; - char status ; - int number ; - int length ; - int datereceived; - char bbsfrom[7] ; // ? BBS we got it from ? - char via[41] ; - char from[7] ; - char to[7] ; - char bid[13] ; - char title[61] ; - char bin; - int nntpnum; // Number within topic (ie Bull TO Addr) - used for nntp - - UCHAR B2Flags; - - char free[4]; - unsigned short nblu; - int theme ; - time_t datecreated ; - time_t datechanged ; - char fbbs[10] ; - char forw[10] ; - char emailfrom[41]; -} ; - - -struct MsgInfo -{ - char type; - char status; - int number; - int length; - int xdatereceived; - char bbsfrom[7]; // ? BBS we got it from ? - char via[41]; - char from[7]; - char to[7]; - char bid[13]; - char title[61]; - int nntpnum; // Number within topic (ie Bull TO Addr) - used for nntp - - UCHAR B2Flags; // Not all flags specific to B2 - - #define B2Msg 1 // Set if Message File is a formatted B2 message - #define Attachments 2 // Set if B2 message has attachments - #define FromPaclink 4 - #define FromCMS 8 - #define FromRMSExpress 16 - #define RadioOnlyMsg 32 // Received using call-T - #define RadioOnlyFwd 64 // Received using call-R - #define WarnNotForwardedSent 128 - - int xdatecreated; - int xdatechanged; - UCHAR fbbs[NBMASK]; - UCHAR forw[NBMASK]; - char emailfrom[41]; - char Locked; // Set if selected for sending (NTS Pickup) - char Defered; // FBB response '=' received - UCHAR UTF8; // Set if Message is in UTF8 (ie from POP/SMTP) - -// For 64 bit time_t compatibility define as long long -// (so struct is same with 32 or 64 bit time_t) - - int64_t datereceived; - int64_t datecreated; - int64_t datechanged; - - char Spare[61 - 24]; // For future use -} ; - -#define MSGTYPE_B 0 -#define MSGTYPE_P 1 - -#define MSGSTATUS_N 0 -#define MSGSTATUS_Y 1 -#define MSGSTATUS_F 2 -#define MSGSTATUS_K 3 -#define MSGSTATUS_H 4 -#define MSGSTATUS_D 5 -#define MSGSTATUS_$ 6 - -struct NNTPRec -{ - // Used for NNTP access to Bulls - - struct NNTPRec * Next; // Record held in chain, so can be held sorted - char NewsGroup[64]; // = Bull TO.at field - int FirstMsg; // Lowest Number - int LastMsg; // Highest Number - int Count; // Active Msgs - time_t DateCreated; // COntains Creation Date of First Bull in Group -}; - - -typedef struct { - char mode; - char BID[13]; - union - { /* array named screen */ - struct - { - unsigned short msgno; - unsigned short timestamp; - }; - CIRCUIT * conn; - } u; -} BIDRec, *BIDRecP; - - -typedef struct WPDBASE{ /* 194 bytes */ - char callsign[7]; - char name[13]; - unsigned char Type; - unsigned char changed; - unsigned short seen; - long long last_modif; - long long last_seen; - char first_homebbs[41]; - char secnd_homebbs[41]; - char first_zip[9]; - char secnd_zip[9]; - char first_qth[31]; - char secnd_qth[31]; -} WPRec, * WPRecP; - -#pragma pack() - -struct FWDBAND -{ - time_t FWDStartBand; - time_t FWDEndBand; -}; - - - -struct BBSForwardingInfo -{ - // Holds info for forwarding - - BOOL Enabled; // Forwarding Enabled - char ** ConnectScript; // Main Connect Script - char ** TempConnectScript; // Used with FWD command. - int ScriptIndex; // Next line in script - BOOL MoreLines; // Set until script is finsihed - - char ** TOCalls; // Calls in to field - char ** ATCalls; // Calls in ATBBS field - char ** HaddressesP; // Heirarchical Addresses for Personals to forward to (as stored) - char *** HADDRSP; // Heirarchical Addresses for Personals to forward to - char ** Haddresses; // Heirarchical Addresses to forward to (as stored) - char *** HADDRS; // Heirarchical Addresses to forward to - int * HADDROffet; // Elements added to complete the HR. At least n+1 must match to forward - char ** FWDTimes; // Time bands to forward - struct FWDBAND ** FWDBands; - int MsgCount; // Messages for this BBS - BOOL ReverseFlag; // Set if BBS wants to poll for reverse forwarding - BOOL Forwarding; // Forward in progress - int MaxFBBBlockSize; - BOOL AllowBlocked; // Allow FBB Blocked - BOOL AllowCompressed; // Allow FBB Compressed - BOOL AllowB1; // Enable B1 - BOOL AllowB2; // Enable B2 - BOOL SendCTRLZ; // Send Ctrl/z instead of /ex - BOOL PersonalOnly; // Only Forward Personals - BOOL SendNew; // Forward new messages immediately - int FwdInterval; - int RevFwdInterval; - int FwdTimer; - time_t LastReverseForward; - char *BBSHA; // HA of BBS - char ** BBSHAElements; // elements of HA of BBS - int ConTimeout; -// char UserCall[10]; // User we are forwarding on behalf of (Currently only for RMS) -// int UserIndex; // index of User we are forwarding on behalf of (Currently only for RMS) -}; - - -struct FBBHeaderLine -{ - // Holds the info from the (up to) 5 headers presented at the start of a Forward Block - - char Format; // Ascii or Binary - char MsgType; // P B etc - char From[7]; // Sender - char ATBBS[41]; // BBS of recipient (@ Field) - char To[7]; // Recipient - char BID[13]; - int Size; - int CSize; // Compresses Size (B2 proto) - BOOL B2Message; // Set if an FC type - UCHAR * CompressedMsg; // Compressed Body fo B2 - struct MsgInfo * FwdMsg; // Header so we can mark as complete -}; - -#define MAXSTACK 20 -//#define MAXLINE 10000 -#define INPUTLEN 512 - -#define MAXLINES 1000 -#define LINELEN 200 - -extern char RTFHeader[4000]; -extern int RTFHddrLen; - -struct ConsoleInfo -{ - struct ConsoleInfo * next; - CIRCUIT * Console; - int BPQStream; - WNDPROC wpOrigInputProc; - HWND hConsole; - HWND hwndInput; - HWND hwndOutput; - HMENU hMenu; // handle of menu - RECT ConsoleRect; - RECT OutputRect; - - int Height, Width, LastY; - - int ClientHeight, ClientWidth; - char kbbuf[INPUTLEN]; - int kbptr; - - char * readbuff; // Malloc'ed - int readbufflen; // Current Length - char * KbdStack[MAXSTACK]; - - int StackIndex; - - BOOL Bells; - BOOL FlashOnBell; // Flash instead of Beep - BOOL StripLF; - - BOOL WarnWrap; - BOOL FlashOnConnect; - BOOL WrapInput; - BOOL CloseWindowOnBye; - - unsigned int WrapLen; - int WarnLen; - int maxlinelen; - - int PartLinePtr; - int PartLineIndex; // Listbox index of (last) incomplete line - - DWORD dwCharX; // average width of characters - DWORD dwCharY; // height of characters - DWORD dwClientX; // width of client area - DWORD dwClientY; // height of client area - DWORD dwLineLen; // line length - int nCaretPosX; // horizontal position of caret - int nCaretPosY; // vertical position of caret - - COLORREF FGColour; // Text Colour - COLORREF BGColour; // Background Colour - COLORREF DefaultColour; // Default Text Colour - - int CurrentLine; // Line we are writing to in circular buffer. - - int Index; - BOOL SendHeader; - BOOL Finished; - - char OutputScreen[MAXLINES][LINELEN]; - - int Colourvalue[MAXLINES]; - int LineLen[MAXLINES]; - - int CurrentColour; - int Thumb; - int FirstTime; - BOOL Scrolled; // Set if scrolled back - int RTFHeight; // Height of RTF control in pixels - -}; - - -struct MSESSION -{ - struct MSESSION * Next; - unsigned int Key; - char * FileName; - char * OrigTimeStamp; - unsigned char * Message; - int MessageLen; - unsigned char * BlockList; - char * ID; - int BlockSize; - int BlockCount; - int BlocksReceived; - BOOL Completed; - time_t Created; - time_t LastUpdated; - int Index; // Line in Display -}; - -VOID __cdecl nprintf(CIRCUIT * conn, const char * format, ...); -char * strlop(char * buf, char delim); -int rt_cmd(CIRCUIT *circuit, char * Buffer); -CIRCUIT *circuit_new(CIRCUIT *circuit, int flags); -VOID BBSputs(CIRCUIT * conn, char * buf); -VOID FBBputs(CIRCUIT * conn, char * buf); -void makelinks(void); -VOID * _zalloc(size_t len); -VOID FreeChatMemory(); -VOID ChatTimer(); -VOID nputs(CIRCUIT * conn, char * buf); -VOID node_close(); -VOID removelinks(); -VOID SetupChat(); -VOID SendChatLinkStatus(); -VOID ClearChatLinkStatus(); -VOID Send_MON_Datagram(UCHAR * Msg, DWORD Len); - -#define Connect(stream) SessionControl(stream,1,0) -#define Disconnect(stream) SessionControl(stream,2,0) -#define ReturntoNode(stream) SessionControl(stream,3,0) -#define ConnectUsingAppl(stream, appl) SessionControl(stream, 0, appl) - -int EncryptPass(char * Pass, char * Encrypt); -VOID DecryptPass(char * Encrypt, unsigned char * Pass, unsigned int len); - -// TCP Connections. FOr the moment SMTP or POP3 - -typedef struct SocketConnectionInfo -{ - struct SocketConnectionInfo * Next; - int Number; // Number of record - for Connections display - SOCKET socket; - SOCKADDR_IN sin; - int Type; // SMTP or POP3 - BOOL AMPR; // Set if sending to an AMPR.ORG server - char FromDomain[50]; // Domain we are sending from - struct UserInfo * bbs; // BBS dor forwarding to AMPR - int State; // Transaction State Machine - UCHAR CallSign[10]; - UCHAR TCPBuffer[3000]; // For converting byte stream to messages - int InputLen; // Data we have alreasdy = Offset of end of an incomplete packet; - - char * MailFrom; // Envelope Sender and Receiver - char ** RecpTo; // May be several Recipients - int Recipients; - - UCHAR * MailBuffer; // Mail Message being received. malloc'ed as needed - int MailBufferSize; // Total Malloc'ed size. Actual size is in MailSize - int MailSize; - int Flags; - - struct UserInfo * POP3User; - struct MsgInfo ** POP3Msgs; // Header List of messages for this uaer - int POP3MsgCount; // No of Messages - int POP3MsgNum; // Sequence number of message being received - - struct MsgInfo * SMTPMsg; // message for this SMTP connection - - UCHAR * SendBuffer; // Message being sent if socket is busy. malloc'ed as needed - int SendBufferSize; // Total Malloc'ed size. Actual size is in MailSize - int SendSize; // Bytes in buffer - int SendPtr; // next byte to send when ready - - struct NNTPRec * NNTPGroup; // Currently Selected Group - int NNTPNum; // Currenrly Selected Msg Number - int Timeout; // Used to close a session that is open too long - -} SocketConn; - -// FBB reject.sys like filters - -typedef struct FBBFILTER -{ - struct FBBFILTER * Next; - char Action; - char Type; - char From[10]; - char AT[10]; - char TO[10]; - char BID[16]; - int MaxLen; - -} FBBFilter; - -extern FBBFilter * Filters; - -typedef struct KEYVALUES -{ - char * Key; - char * Value; -} KeyValues; - -typedef struct WEBMAILINFO -{ - // Info for HTML Forms Processing - - struct HtmlFormDir * Dir; // HTML Directory - char * txtFileName; // Template Name for current message - char * InputHTMLName; // Template to input message - char * DisplayHTMLName; // Template to display message - char * ReplyHTMLName; // Template for replying to message - char * txtFile; // Template data - char * OrigTo; // To field when template loaded - char * OrigSubject; // Subject field when template loaded - char * OrigBody; // Msg text when template loaded - char * OrigBID; - char OrigType; - char * To; - char * CC; - char * Subject; - char * Body; - char * BID; - char Type; - struct MsgInfo * Msg; // Msg record if replying - KeyValues txtKeys[1000]; // Key/Value pairs for txt template. Used when creating or displaying - KeyValues XMLKeys[1000]; // Key/Value pairs from XML attachment - BOOL isReply; - char * XMLName; - char * XML; // XML attachment - int XMLLen; - int Files; - char * FileName[100]; // Attachments - char * FileBody[100]; - int FileLen[100]; - - char * Header; - int HeaderLen; - - char * Footer; - int FooterLen; - - char * Reply; // put in here to save passing lots of parameters - int * RLen; - - BOOL Winlink; - BOOL P2P; - BOOL Packet; - - int CurrentMessageIndex; // Index of message currently displayed (for Prev and Next) -#ifdef WIN32 - void * iconv_toUTF8; // Used on Linux for char set conversion -#else - iconv_t * iconv_toUTF8; // Used on Linux for char set conversion -#endif - -}WebMailInfo; - -#define SMTPServer 1 -#define POP3SLAVE 2 -#define SMTPClient 3 -#define POP3Client 4 -#define NNTPServer 5 - -// State Values - -#define GettingUser 1 -#define GettingPass 2 -#define Authenticated 4 - -#define Connecting 8 - -// SMTP Master - -#define WaitingForGreeting 16 -#define WaitingForHELOResponse 32 -#define WaitingForFROMResponse 64 -#define WaitingForTOResponse 128 -#define WaitingForDATAResponse 256 -#define WaitingForBodyResponse 512 -#define WaitingForAUTHResponse 1024 - -// POP3 Master - -#define WaitingForUSERResponse 32 -#define WaitingForPASSResponse 64 -#define WaitingForSTATResponse 128 -#define WaitingForUIDLResponse 256 -#define WaitingForLISTResponse 512 -#define WaitingForRETRResponse 512 -#define WaitingForDELEResponse 1024 -#define WaitingForQUITResponse 2048 - - -#define SE 240 // End of subnegotiation parameters -#define NOP 241 //No operation -//#define DM 242 //Data mark Indicates the position of a Synch event within the data stream. This should always be accompanied by a TCP urgent notification. -#define BRK 243 //Break Indicates that the "break" or "attention" key was hi. -#define IP 244 //Suspend Interrupt or abort the process to which the NVT is connected. -#define AO 245 //Abort output Allows the current process to run to completion but does not send its output to the user. -#define AYT 246 //Are you there Send back to the NVT some visible evidence that the AYT was received. -#define EC 247 //Erase character The receiver should delete the last preceding undeleted character from the data stream. -#define EL 248 //Erase line Delete characters from the data stream back to but not including the previous CRLF. -#define GA 249 //Go ahead Under certain circumstances used to tell the other end that it can transmit. -#define SB 250 //Subnegotiation Subnegotiation of the indicated option follows. -#define WILL 251 //will Indicates the desire to begin performing, or confirmation that you are now performing, the indicated option. -#define WONT 252 //wont Indicates the refusal to perform, or continue performing, the indicated option. -#define DOx 253 //do Indicates the request that the other party perform, or confirmation that you are expecting the other party to perform, the indicated option. -#define DONT 254 //dont Indicates the demand that the other party stop performing, or confirmation that you are no longer expecting the other party to perform, the indicated option. -#define IAC 255 - -#define suppressgoahead 3 //858 -//#define Status 5 //859 -//#define echo 1 //857 -#define timingmark 6 //860 -#define terminaltype 24 //1091 -#define windowsize 31 //1073 -#define terminalspeed 32 //1079 -#define remoteflowcontrol 33 //1372 -#define linemode 34 //1184 -#define environmentvariables 36 //1408 - -BOOL Initialise(); -#ifdef WIN32 -INT_PTR CALLBACK ConfigWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); -#endif -int DisplaySessions(); -int DoStateChange(int Stream); -int DoReceivedData(int Stream); -int DoBBSMonitorData(int Stream); -int Connected(int Stream); -int Disconnected(int Stream); -//int Socket_Accept(int SocketId); -//int Socket_Data(int SocketId,int error, int eventcode); -int DataSocket_Read(SocketConn * sockptr, SOCKET sock); -int DataSocket_Write(SocketConn * sockptr, SOCKET sock); -int DataSocket_Disconnect(SocketConn * sockptr); -int RefreshMainWindow(); -int Terminate(); -int SendtoSocket(SOCKET sock,char * Msg); -int WriteLog(char * msg); -int ConnectState(int Stream); -UCHAR * EncodeCall(UCHAR * Call); -int ParseIniFile(char * fn); -struct UserInfo * AllocateUserRecord(char * Call); -struct MsgInfo * AllocateMsgRecord(); -BIDRec * AllocateBIDRecord(); -BIDRec * AllocateTempBIDRecord(); -struct UserInfo * LookupCall(char * Call); -BIDRec * LookupBID(char * BID); -BIDRec * LookupTempBID(char * BID); -VOID RemoveTempBIDS(CIRCUIT * conn); -VOID SaveUserDatabase(); -VOID GetUserDatabase(); -VOID GetMessageDatabase(); -VOID SaveMessageDatabase(); -VOID GetBIDDatabase(); -VOID SaveBIDDatabase(); -VOID GetWPDatabase(); -VOID CopyWPDatabase(); -VOID SaveWPDatabase(); -VOID GetBadWordFile(); -WPRec * LookupWP(char * Call); -VOID SendWelcomeMsg(int Stream, ConnectionInfo * conn, struct UserInfo * user); -VOID ProcessLine(ConnectionInfo * conn, struct UserInfo * user, char* Buffer, int len); -VOID ProcessChatLine(ConnectionInfo * conn, struct UserInfo * user, char* Buffer, int len); -VOID SendPrompt(ConnectionInfo * conn, struct UserInfo * user); -int QueueMsg( ConnectionInfo * conn, char * msg, int len); -VOID SendUnbuffered(int stream, char * msg, int len); -//int GetFileList(char * Dir); -BOOL ListMessage(struct MsgInfo * Msg, ConnectionInfo * conn, struct TempUserInfo * Temp); -void DoDeliveredCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); -void DoKillCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); -void DoListCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, BOOL Resuming, char * Context); -void DoReadCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); -void KillMessage(ConnectionInfo * conn, struct UserInfo * user, int msgno); -int KillMessagesTo(ConnectionInfo * conn, struct UserInfo * user, char * Call); -int KillMessagesFrom(ConnectionInfo * conn, struct UserInfo * user, char * Call); -void DoUnholdCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); - -VOID FlagAsKilled(struct MsgInfo * Msg, BOOL SaveDB); -int ListMessagesFrom(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start); -int ListMessagesTo(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start); -int ListMessagesAT(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start); -void ListMessagesInRange(ConnectionInfo * conn, struct UserInfo * user, char * Call, int Start, int End, BOOL SendFullFrom ); -void ListMessagesInRangeForwards(ConnectionInfo * conn, struct UserInfo * user, char * Call, int Start, int End, BOOL SendFullFrom ); -int GetUserMsg(int m, char * Call, BOOL SYSOP); -void Flush(ConnectionInfo * conn); -VOID ClearQueue(ConnectionInfo * conn); -void TrytoSend(); -void ReadMessage(ConnectionInfo * conn, struct UserInfo * user, int msgno); -struct MsgInfo * FindMessage(char * Call, int msgno, BOOL sysop); -char * ReadMessageFile(int msgno); -char * ReadInfoFile(char * File); -char * FormatDateAndTime(time_t Datim, BOOL DateOnly); -int CriticalErrorHandler(char * error); -BOOL DoSendCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); -BOOL CreateMessage(ConnectionInfo * conn, char * From, char * ToCall, char * ATBBS, char MsgType, char * BID, char * Title); -VOID ProcessMsgTitle(ConnectionInfo * conn, struct UserInfo * user, char* Buffer, int len); -VOID ProcessMsgLine(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len); -VOID CreateMessageFile(ConnectionInfo * conn, struct MsgInfo * Msg); -int ProcessConnecting(CIRCUIT * circuit, char * Buffer, int Len); -VOID SaveConfig(char * ConfigName); -BOOL GetConfig(char * ConfigName); -int GetIntValue(config_setting_t * group, char * name); -//BOOL GetStringValue(config_setting_t * group, char * name, char * value, int maxlen); -BOOL GetConfigFromRegistry(); -VOID Parse_SID(CIRCUIT * conn, char * SID, int len); -VOID ProcessMBLLine(CIRCUIT * conn, struct UserInfo * user, UCHAR* Buffer, int len); -VOID ProcessFBBLine(ConnectionInfo * conn, struct UserInfo * user, UCHAR * Buffer, int len); -VOID SetupNextFBBMessage(CIRCUIT * conn); -BOOL DecodeSendParams(CIRCUIT * conn, char * Context, char ** From, char * To, char ** ATBBS, char ** BID); -int PrintMessages(HWND hDlg, int Count, int * Indexes); -int check_fwd_bit(char *mask, int bbsnumber); -void set_fwd_bit(char *mask, int bbsnumber); -void clear_fwd_bit (char *mask, int bbsnumber); -VOID SetupForwardingStruct(struct UserInfo * user); -BOOL Forward_Message(struct UserInfo * user, struct MsgInfo * Msg); -VOID StartForwarding (int BBSNumber, char ** TempScript); -BOOL Reverse_Forward(); -int ProcessBBSConnectScript(CIRCUIT * conn, char * Buffer, int len); -BOOL FBBDoForward(CIRCUIT * conn); -BOOL FindMessagestoForward(CIRCUIT * conn); -BOOL SeeifMessagestoForward(int BBSNumber, CIRCUIT * Conn); -int CountMessagestoForward(struct UserInfo * user); -int CountBytestoForward(struct UserInfo * user); - -VOID * GetMultiLineDialogParam(HWND hDialog, int DLGItem); - - -VOID * GetMultiStringValue(config_setting_t * hKey, char * ValueName); -VOID * RegGetMultiStringValue(HKEY hKey, char * ValueName); - -int MultiLineDialogToREG_MULTI_SZ(HWND hWnd, int DLGItem, HKEY hKey, char * ValueName); -int Do_BBS_Sel_Changed(HWND hDlg); -VOID FreeForwardingStruct(struct UserInfo * user); -VOID FreeList(char ** Hddr); -int Do_User_Sel_Changed(HWND hDlg); -int Do_Msg_Sel_Changed(HWND hDlg); -VOID Do_Save_Msg(); -VOID Do_Add_User(HWND hDlg); -VOID Do_Delete_User(HWND hDlg); -VOID FlagSentMessages(CIRCUIT * conn, struct UserInfo * user); -VOID HoldSentMessages(CIRCUIT * conn, struct UserInfo * user); -VOID Do_Save_User(HWND hDlg, BOOL ShowBox); -VOID DeleteBBS(struct UserInfo * user); -VOID SaveBBSConfig(); -BOOL GetChatConfig(char * ConfigName); -VOID SaveChatConfig(); -VOID SaveISPConfig(); -VOID SaveFWDConfig(); -VOID SaveMAINTConfig(); -VOID SaveWelcomeMsgs(); -VOID SavePrompts(); -VOID ReinitializeFWDStruct(struct UserInfo * user); -VOID CopyBIDDatabase(); -VOID CopyMessageDatabase(); -VOID CopyUserDatabase(); -VOID FWDTimerProc(); -VOID CreateMessageFromBuffer(CIRCUIT * conn); -VOID __cdecl nodeprintf(ConnectionInfo * conn, const char * format, ...); -VOID __cdecl nodeprintfEx(ConnectionInfo * conn, const char * format, ...); -VOID FreeOverrides(); -VOID SendMessageToSYSOP(char * Title, char * MailBuffer, int Length); -struct UserInfo * FindRMS(); -VOID FindNextRMSUser(struct BBSForwardingInfo * FWDInfo); -BOOL ConnecttoBBS (struct UserInfo * user); -BOOL SetupNewBBS(struct UserInfo * user); -VOID CreateRegBackup(); -VOID SaveFilters(HWND hDlg); -BOOL CheckRejFilters(char * From, char * To, char * ATBBS, char * BID, char Type, int Len); -BOOL CheckHoldFilters(struct MsgInfo * Msg, char * From, char * To, char * ATBBS, char * BID); -BOOL CheckifLocalRMSUser(char * FullTo); -VOID DoWPLookup(ConnectionInfo * conn, struct UserInfo * user, char Type, char *Context); -BOOL wildcardcompare(char * Target, char * Match); -VOID SendWarningToSYSOP(struct MsgInfo * Msg); -VOID DoEditUserCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); -VOID DoPollRMSCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); -VOID DoShowRMSCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); -VOID DoSetIdleTime(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); -VOID DoFwdCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); -VOID SaveFwdParams(char * Call, struct BBSForwardingInfo * ForwardingInfo); -VOID DoAuthCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); -VOID ProcessSuspendedListCommand(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len); -VOID DoReroute(CIRCUIT * conn, struct UserInfo * user); - -// FBB Routines - -VOID SendCompressed(CIRCUIT * conn, struct MsgInfo * FwdMsg); -VOID SendCompressedB2(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader); -VOID UnpackFBBBinary(CIRCUIT * conn); -void Decode(CIRCUIT * conn, __int16 DecodeOnly); -//long Encode(char * in, char * out, long inlen, BOOL B1Protocol); - -BOOL CreateB2Message(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader, char * Rline); -VOID SaveFBBBinary(CIRCUIT * conn); -BOOL LookupRestart(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader); -BOOL DoWeWantIt(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader); - -// Console Routines - -BOOL CreateConsole(int Stream); -int WritetoConsoleWindow(int Stream, char * Msg, int len); -int ToggleParam(HMENU hMenu, HWND hWnd, BOOL * Param, int Item); -void CopyRichTextToClipboard(HWND hWnd); -void CopyToClipboard(HWND hWnd); -VOID CloseConsole(int Stream); - -// Monitor Routines - -BOOL CreateMonitor(); -int WritetoMonitorWindow(char * Msg, int len); - -BOOL CreateDebugWindow(); -VOID WritetoDebugWindow(char * Msg, int len); -VOID ClearDebugWindow(); -int RemoveLF(char * Message, int len); - -// Utilities - -BOOL isdigits(char * string); - - -void _GetSemaphore(struct SEM * Semaphore, int ID, char * File, int Line); -void FreeSemaphore(struct SEM * Semaphore); - -VOID __cdecl Debugprintf(const char * format, ...); -VOID __cdecl Logprintf(int LogMode, CIRCUIT * conn, int InOut, const char * format, ...); - -VOID SortBBSChain(); -VOID ExpandAndSendMessage(CIRCUIT * conn, char * Msg, int LOG); -int ImportMessages(CIRCUIT * conn, char * FN, BOOL Nopopup); - -// TCP Routines - -BOOL InitialiseTCP(); -VOID SetupListenSet(); -VOID TCPTimer(); -VOID TCPFastTimer(); -int Socket_Data(int sock, int error, int eventcode); -static int Socket_Accept(SOCKET SocketId); -int Socket_Connect(SOCKET sock, int Error); -VOID ProcessSMTPServerMessage(SocketConn * sockptr, char * Buffer, int Len); -int CreateSMTPMessage(SocketConn * sockptr, int i, char * MsgTitle, time_t Date, char * MsgBody, int Msglen, BOOL B2Flag); -BOOL CreateSMTPMessageFile(char * Message, struct MsgInfo * Msg); -SOCKET CreateListeningSocket(int Port); -int TidyString(char * MailFrom); -VOID ProcessPOP3ServerMessage(SocketConn * sockptr, char * Buffer, int Len); -char *str_base64_encode(char *str); -int b64decode(char *str); -SocketConn * SMTPConnect(char * Host, int Port, BOOL AMPR, struct MsgInfo * Msg, char * MsgBody); -BOOL POP3Connect(char * Host, int Port); -VOID ProcessSMTPClientMessage(SocketConn * sockptr, char * Buffer, int Len); -VOID ProcessPOP3ClientMessage(SocketConn * sockptr, char * Buffer, int Len); -int CreatePOP3Message(char * From, char * To, char * MsgTitle, time_t Date, char * MsgBody, int MsgLen, BOOL B2Flag); -void WriteLogLine(CIRCUIT * conn, int Flag, char * Msg, int MsgLen, int Flags); -int SendSock(SocketConn * sockptr, char * msg); -VOID __cdecl sockprintf(SocketConn * sockptr, const char * format, ...); -VOID SendFromQueue(SocketConn * sockptr); -VOID SendMultiPartMessage(SocketConn * sockptr, struct MsgInfo * Msg, UCHAR * msgbytes); -int CountMessagesTo(struct UserInfo * user, int * Unread); - -BOOL SendtoISP(); - -// NNTP ROutines - -VOID InitialiseNNTP(); -VOID BuildNNTPList(struct MsgInfo * Msg); -int NNTP_Data(int sock, int error, int eventcode); -int NNTP_Accept(SOCKET SocketId); - -VOID * GetOverrides(config_setting_t * group, char * ValueName); -VOID * RegGetOverrides(HKEY hKey, char * ValueName); - -VOID DoHouseKeeping(BOOL Mainual); -VOID ExpireMessages(); -VOID KillMsg(struct MsgInfo * Msg); -BOOL RemoveKilledMessages(); -VOID Renumber_Messages(); -BOOL ExpireBIDs(); -VOID MailHousekeepingResults(); -VOID CreateBBSTrafficReport(); -VOID CreateWPReport(); - -// WP Routines - -VOID ProcessWPMsg(char * MailBuffer, int Size, char * FisrtRLine); -VOID GetWPInfoFromRLine(char * From, char * FirstRLine, time_t RLineTime); -VOID UpdateWPWithUserInfo(struct UserInfo * user); -VOID GetWPBBSInfo(char * Rline); - -// UI Routines - -VOID SetupUIInterface(); -VOID Free_UI(); -VOID SendLatestUI(int Port); -VOID SendMsgUI(struct MsgInfo * Msg); -static VOID Send_AX_Datagram(UCHAR * Msg, DWORD Len, UCHAR Port, UCHAR * HWADDR, BOOL Queue); -VOID SeeifBBSUIFrame(struct _MESSAGEX * buff, int len); -struct MsgInfo * FindMessageByNumber(int msgno); -int CountConnectionsOnPort(int CheckPort); - -// Message Routing Routtines - -VOID SetupHAElements(struct BBSForwardingInfo * ForwardingInfo); -VOID SetupHAddreses(struct BBSForwardingInfo * ForwardingInfo); -VOID SetupHAddresesP(struct BBSForwardingInfo * ForwardingInfo); -VOID SetupMyHA(); -VOID SetupFwdAliases(); -struct Continent * FindContinent(char * Name); -int MatchMessagetoBBSList(struct MsgInfo * Msg, CIRCUIT * conn); -BOOL CheckABBS(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char * HRoute); -BOOL CheckBBSToList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo); -BOOL CheckBBSAtList(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS); -BOOL CheckBBSHList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char * HRoute); -BOOL CheckBBSHElements(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char ** HElements); -BOOL CheckBBSHElementsFlood(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char ** HElements); -int CheckBBSToForNTS(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo); -int CheckBBSATListWildCarded(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS); - -VOID ReRouteMessages(); - -VOID initUTF8(); -int Is8Bit(unsigned char *cpt, int len); -int IsUTF8(unsigned char *ptr, int len); -int IsUTF8(unsigned char *ptr, int len); -int WebIsUTF8(unsigned char *ptr, int len); -int Convert437toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); -int Convert1251toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); -int Convert1252toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); -int TrytoGuessCode(unsigned char * Char, int Len); - - -VOID FreeWebMailMallocs(); - -extern int _MYTIMEZONE; - -extern HKEY REGTREE; -extern char * REGTREETEXT; - -extern HBRUSH bgBrush; -extern BOOL cfgMinToTray; - -extern CIRCUIT * Console; - -extern ULONG ChatApplMask; -extern char Verstring[]; - -extern char SignoffMsg[]; -extern char AbortedMsg[]; -extern char InfoBoxText[]; // Text to display in Config Info Popup - -extern int LastVer[4]; // In case we need to do somthing the first time a version is run - -extern HWND MainWnd; -extern char BaseDir[]; -extern char BaseDirRaw[]; -extern char MailDir[]; -extern char WPDatabasePath[]; -extern char RlineVer[50]; - -extern BOOL LogBBS; -extern BOOL LogCHAT; -extern BOOL LogTCP; -extern BOOL ForwardToMe; -extern BOOL OnlyKnown; - -extern BOOL AllowAnon; -extern BOOL UserCantKillT; -extern BOOL DontNeedHomeBBS; - -extern int LatestMsg; -extern char BBSName[]; -extern char SYSOPCall[]; -extern char BBSSID[]; -extern char NewUserPrompt[]; - -extern char * WelcomeMsg; -extern char * NewWelcomeMsg; -extern char * ChatWelcomeMsg; -extern char * NewChatWelcomeMsg; -extern char * ExpertWelcomeMsg; - -extern char * Prompt; -extern char * NewPrompt; -extern char * ExpertPrompt; - -// Filter Params - -extern char ** RejFrom; // Reject on FROM Call -extern char ** RejTo; // Reject on TO Call -extern char ** RejAt; // Reject on AT Call -extern char ** RejBID; - -extern char ** HoldFrom; // Hold on FROM Call -extern char ** HoldTo; // Hold on TO Call -extern char ** HoldAt; // Hold on AT Call -extern char ** HoldBID; - -// Send WP Params - -extern BOOL SendWP; -extern char SendWPVIA[81]; -extern char SendWPTO[11]; -extern int SendWPType; - -extern int Ver[4]; - -extern struct MsgInfo ** MsgHddrPtr; - -extern BIDRec ** BIDRecPtr; -extern int NumberofBIDs; - -extern struct NNTPRec * FirstNNTPRec; -//extern int NumberofNNTPRecs; - - -extern int NumberofMessages; -extern int FirstMessageIndextoForward; - -extern WPRec ** WPRecPtr; -extern int NumberofWPrecs; - -extern struct SEM AllocSemaphore; -extern struct SEM ConSemaphore; -extern struct SEM MsgNoSemaphore; - -extern struct MsgInfo * MsgnotoMsg[]; // Message Number to Message Slot List. - - -extern char hostname[]; -extern char RtUsr[]; -extern char RtUsrTemp[]; -extern char RtKnown[]; -extern int AXIPPort; -extern BOOL NeedStatus; - -extern BOOL ISP_Gateway_Enabled; -extern BOOL SMTPAuthNeeded; - - -extern int MaxMsgno; -extern int BidLifetime; -extern int MaxAge; -extern int MaintInterval; -extern int MaintTime; -extern int UserLifetime; - -extern int MaxRXSize; -extern int MaxTXSize; - -extern char OurNode[]; -extern char OurAlias[]; -extern BOOL SMTPMsgCreated; - -extern HINSTANCE hInst; -extern HWND hWnd; -extern RECT MainRect; - -extern char BBSName[]; -extern char HRoute[]; -extern char AMPRDomain[]; -extern BOOL SendAMPRDirect; -extern int BBSApplNum; -extern int SMTPInPort; -extern int POP3InPort; -extern int NNTPInPort; -extern BOOL RemoteEmail; - -extern int MaxStreams; -extern UCHAR * OtherNodes; -extern struct UserInfo * BBSChain; // Chain of users that are BBSes -extern struct UserInfo ** UserRecPtr; -extern int NumberofUsers; -extern struct MsgInfo ** MsgHddrPtr; -extern int NumberofMessages; -extern int HighestBBSNumber; -extern HMENU hFWDMenu; // Forward Menu Handle -extern char zeros[]; // For forward bitmask tests -extern BOOL EnableUI; -extern BOOL RefuseBulls; -extern BOOL SendSYStoSYSOPCall; -extern BOOL SendBBStoSYSOPCall; -extern BOOL DontHoldNewUsers; -extern BOOL DefaultNoWINLINK; -extern BOOL UIEnabled[]; -extern BOOL UINull[]; -extern BOOL UIMF[]; -extern BOOL UIHDDR[]; -extern char * UIDigi[]; -extern int MailForInterval; -extern char MailForText[]; - -extern BOOL ISP_Gateway_Enabled; - -extern char MyDomain[]; // Mail domain for BBS<>Internet Mapping - -extern char ISPSMTPName[]; -extern char ISPEHLOName[]; -extern int ISPSMTPPort; - -extern char ISPPOP3Name[]; -extern int ISPPOP3Port; -extern int ISPPOP3Interval; - -extern char ISPAccountName[]; -extern char ISPAccountPass[]; -extern char EncryptedISPAccountPass[]; -extern int EncryptedPassLen; -extern char *month[]; - -extern HWND hDebug; -extern RECT MonitorRect; -extern RECT DebugRect; -extern HWND hMonitor; -//extern HWND hConsole; -//extern RECT ConsoleRect; -extern int LogAge; -extern BOOL DeletetoRecycleBin; -extern BOOL SuppressMaintEmail; -extern BOOL SaveRegDuringMaint; -extern BOOL SendWP; -extern BOOL OverrideUnsent; -extern BOOL SendNonDeliveryMsgs; -extern BOOL GenerateTrafficReport; - -extern double PR; -extern double PUR; -extern double PF; -extern double PNF; -extern int BF; -extern int BNF; -extern int NTSD; -extern int NTSU; -extern int NTSF; -extern int AP; -extern int AB; - -extern struct Override ** LTFROM; -extern struct Override ** LTTO; -extern struct Override ** LTAT; - -extern time_t LastHouseKeepingTime; -extern time_t LastTrafficTime; - -extern char * MyElements[]; -extern char ** AliasText; -extern struct ALIAS ** Aliases; - -extern BOOL ReaddressLocal; -extern BOOL ReaddressReceived; -extern BOOL WarnNoRoute; -extern BOOL SendPtoMultiple; -extern BOOL Localtime; - -extern struct ConsoleInfo * ConsHeader[2]; - -extern BOOL NeedHomeBBS; -extern char ConfigName[250]; -extern BOOL UsingingRegConfig; - -extern BOOL MulticastRX; - -extern BOOL FilterWPBulls; -extern BOOL NoWPGuesses; -extern char ** SendWPAddrs; // Replacers WP To and VIA - -extern BOOL DontCheckFromCall; - -extern time_t APIClock;; - -// YAPP stuff - -#define SOH 1 -#define STX 2 -#define ETX 3 -#define EOT 4 -#define ENQ 5 -#define ACK 6 -#define DLE 0x10 -#define NAK 0x15 -#define CAN 0x18 +#ifndef WINVER // Allow use of features specific to Windows XP or later. +#define WINVER 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. +#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or later. +#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later. +#endif + +#ifndef _WIN32_IE // Allow use of features specific to IE 6.0 or later. +#define _WIN32_IE 0x0600 // Change this to the appropriate value to target other versions of IE. +#endif + + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#define _CRT_SECURE_NO_DEPRECATE +#define _WINSOCK_DEPRECATED_NO_WARNINGS + +#define LIBCONFIG_STATIC +#include + +#ifndef WIN32 +#include +#endif + +#include "compatbits.h" + +#ifndef LINBPQ +#include "bpq32.h" +#include "BPQMailrc.h" +#include "dbghelp.h" +#else +#include "cheaders.h" +#endif + +#include "asmstrucs.h" + +#define MaxBPQPortNo 63 // Port 64 reserved for BBS Mon +#define MAXBPQPORTS 63 + +#define NEWROUTING + +extern int FOURCHARCONT; + +// Standard __except handler for try/except + +VOID CheckProgramErrors(); +VOID WriteMiniDump(); + +extern int ProgramErrors; + +extern struct _EXCEPTION_POINTERS exinfox; + +#ifdef WIN32 +void Dump_Process_State(struct _EXCEPTION_POINTERS * exinfo, char * Msg); + +#define My__except_Routine(Message) \ +__except(memcpy(&exinfo, GetExceptionInformation(), sizeof(struct _EXCEPTION_POINTERS)), EXCEPTION_EXECUTE_HANDLER)\ +{\ + Debugprintf("MAILCHAT *** Program Error %x at %x in %s EAX %x EBX %x ECX %x EDX %x ESI %x EDI %x",\ + exinfo.ExceptionRecord->ExceptionCode, exinfo.ExceptionRecord->ExceptionAddress, Message,\ + exinfo.ContextRecord->Eax, exinfo.ContextRecord->Ebx, exinfo.ContextRecord->Ecx,\ + exinfo.ContextRecord->Edx, exinfo.ContextRecord->Esi, exinfo.ContextRecord->Edi);\ + CheckProgramErrors();\ + WriteMiniDump();\ +} + + +/* +#define My__except_Routine(Message) \ +__except(memcpy(&exinfox, GetExceptionInformation(), sizeof(struct _EXCEPTION_POINTERS)), EXCEPTION_EXECUTE_HANDLER)\ +{\ + Dump_Process_State(&exinfox, Message);\ + CheckProgramErrors();\ +} + +#define My__except_RoutineWithDisconnect(Message) \ +__except(memcpy(&exinfo, GetExceptionInformation(), sizeof(struct _EXCEPTION_POINTERS)), EXCEPTION_EXECUTE_HANDLER)\ +{\ + Debugprintf("MAILCHAT *** Program Error %x at %x in %s EAX %x EBX %x ECX %x EDX %x ESI %x EDI %x",\ + exinfo.ExceptionRecord->ExceptionCode, exinfo.ExceptionRecord->ExceptionAddress, Message,\ + exinfo.ContextRecord->Eax, exinfo.ContextRecord->Ebx, exinfo.ContextRecord->Ecx,\ + exinfo.ContextRecord->Edx, exinfo.ContextRecord->Esi, exinfo.ContextRecord->Edi);\ + FreeSemaphore(&ChatSemaphore);\ + if (conn->BPQStream < 0)\ + CloseConsole(conn->BPQStream);\ + else\ + Disconnect(conn->BPQStream);\ +} +*/ +#define My_except_RoutineWithDiscBBS(Message) \ +__except(memcpy(&exinfo, GetExceptionInformation(), sizeof(struct _EXCEPTION_POINTERS)), EXCEPTION_EXECUTE_HANDLER)\ +{\ + Debugprintf("MAILCHAT *** Program Error %x at %x in %s EAX %x EBX %x ECX %x EDX %x ESI %x EDI %x",\ + exinfo.ExceptionRecord->ExceptionCode, exinfo.ExceptionRecord->ExceptionAddress, Message,\ + exinfo.ContextRecord->Eax, exinfo.ContextRecord->Ebx, exinfo.ContextRecord->Ecx,\ + exinfo.ContextRecord->Edx, exinfo.ContextRecord->Esi, exinfo.ContextRecord->Edi);\ + if (conn->BPQStream < 0)\ + CloseConsole(conn->BPQStream);\ + else\ + Disconnect(conn->BPQStream);\ + CheckProgramErrors();\ +} +#endif +#define MAXUSERNAMELEN 6 + +#define WSA_ACCEPT WM_USER + 1 +#define WSA_CONNECT WM_USER + 2 +#define WSA_DATA WM_USER + 3 +#define NNTP_ACCEPT WM_USER + 4 +#define NNTP_DATA WM_USER + 5 + +#ifdef _DEBUG + +VOID * _malloc_dbg_trace(int len, int type, char * file, int line); + +#define malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define calloc(c, s) _calloc_dbg(c, s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define realloc(p, s) _realloc_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define _recalloc(p, c, s) _recalloc_dbg(p, c, s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define _expand(p, s) _expand_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define free(p) _free_dbg(p, _NORMAL_BLOCK) +#define _strdup(s) _strdup_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) + + +#define zalloc(s) _zalloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) +#else +#define zalloc(s) _zalloc(s) +#endif + +#ifdef LINBPQ + +#undef zalloc +#define zalloc _zalloc + +#endif + +VOID * _zalloc_dbg(size_t len, int type, char * file, int line); + +#define LOG_BBS 0 +#define LOG_CHAT 1 +#define LOG_TCP 2 +#define LOG_DEBUG_X 3 + + +//Chat Duplicate suppression Code + +#define MAXDUPS 10 // Number to keep +#define DUPSECONDS 5 // TIme to Keep + +struct DUPINFO +{ + time_t DupTime; + char DupUser[10]; + char DupText[100]; +}; + + +struct UserRec +{ + char * Callsign; + char * UserName; + char * Password; +}; + + + +typedef struct ConnectionInfo_S +{ + struct ConnectionInfo_S *next; + PROC *proc; + UCHAR RadioOnlyMode; // T or R flag for Radio Only mode. + + int Number; // Number of record - for Connections display + BOOL Active; + int BPQStream; + int paclen; + UCHAR Callsign[11]; // Station call including SSID + BOOL GotHeader; + UCHAR InputMode; // Line by Line or Binary or YAPP + + UCHAR * InputBuffer; + int InputBufferLen; + int InputLen; // Data we have already = Offset of end of an incomplete packet; + + struct UserInfo * UserPointer; + int Retries; + int LoginState; // 1 = user ok, 2 = password ok + int Flags; + + // Data to the user is kept in a malloc'd buffer. This can be appended to, + // and data sucked out under both terminal and system flow control. PACLEN is + // enfored when sending to node. + + UCHAR * OutputQueue; // Messages to user + int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message + int OutputGetPointer; // Next byte to send. When Getpointer = Queue Length all is sent - free the buffer and start again. + + int CloseAfterFlush; // Close session when all sent. Set to 100ms intervals to wait. + + int ErrorCount; // Invalid Command count + BOOL Paging; // Set if user wants paging + int LinesSent; // Count when paging + int PageLen; // Lines per page + + UCHAR * MailBuffer; // Mail Message being received + UCHAR * CopyBuffer; // Mail Message being forwarded + int MailBufferSize; // Total Malloc'ed size. Actual size in in Msg Struct + + long lastmsg; // Last Listed. Stored here, updated in user record only on clean close + BOOL sysop; // Set if user is authenticated as a sysop + BOOL Secure_Session; // Set if Local Terminal, or Telnet connect with SYSOP status + UINT BBSFlags; // Set if defined as a bbs and SID received + struct MsgInfo * TempMsg; // Header while message is being received + struct MsgInfo * FwdMsg; // Header while message is being forwarded + + char ** To; // May be several Recipients + int ToCount; + + int BBSNumber; // The BBS number (offset into bitlist of BBSes to forward a message to + int NextMessagetoForward; // Next index to check in forward cycle + BOOL BPQBBS; // Set if SID indicates other end is BPQ + char MSGTYPES[20]; // Any MSGTYPEFLAGS + BOOL SendT; // Send T messages + BOOL SendP; // Send P messages + BOOL SendB; // Send Bulls + BOOL SendWL2KFW; // send ;FW: + int MaxBLen; // Max Size for this session + int MaxPLen; // Max Size for this session + int MaxTLen; // Max Size for this session + BOOL DoReverse; // Request Reverse Forward + char LastForwardType; // Last type of messages forwarded + struct FBBHeaderLine * FBBHeaders; // The Headers from an FFB forward block + char FBBReplyChars[36]; //The +-=!nnnn chars for the 5 proposals + int FBBReplyIndex; // current Reply Pointer + int FBBIndex; // current propopsal number + int RestartFrom; // Restart position + BOOL NeedRestartHeader; // Set if waiting for 6 byte restart header + BOOL DontSaveRestartData; // Set if corrupt data received + BOOL FBBMsgsSent; // Messages need to be maked as complete when next command received + UCHAR FBBChecksum; // Header Checksum + BOOL OpenBCM; // OpenBCM mode (escape -xFF chars) + BOOL InTelnetExcape; // Last Char was 0xff + BOOL LocalMsg; // Set if current Send command is for a local user + BOOL NewUser; // Set if first time user has accessed BBS + BOOL Paclink; // Set if receiving messages from Paclink + BOOL RMSExpress; // Set if receiving messages from RMS Express + BOOL WL2K; // Set if communicating with a CMS + BOOL PAT; // Set if communicating with PAT + char ** PacLinkCalls; // Calls we are getting messages for + BOOL SkipPrompt; // Set if a remote node sends a > at the end of his CTEXT + BOOL SkipConn; // Node sends "connected" in its CTEXT + int Watchdog; // Hung Circuit Detect. + int SessType; // BPQ32 sesstype bits + +#define Sess_L2LINK 1 +#define Sess_SESSION 2 +#define Sess_UPLINK 4 +#define Sess_DOWNLINK 8 +#define Sess_BPQHOST 0x20 +#define Sess_PACTOR 0x40 + + HANDLE DebugHandle; // File Handle for session-based debugging + + char ARQFilename[256]; // Filename from ARQ:FILE:: Header + int ARQClearCount; // To make sure queues are flushed when sending + + int SIDResponseTimer; // Used to detect incomplete handshake + + char PQChallenge[20]; // Secure User logon challange + char SecureMsg[20]; // CMS Secure Signon Response + int MCastListenTime; // Time to run session for + + int YAPPLen; // Bytes sent/received of YAPP Message + long YAPPDate; // Date for received file - if set enables YAPPC + + int SyncCompressedLen; + int SyncXMLLen; + int SyncMsgLen; + char * SyncHost; // Saved so can send "request sync" + int SyncPort; + UCHAR * SyncMessage; // Compressed SYNC message to send + + // These are used to detect CRLF split over a packet boundary + int usingCR; // Session is (normally) using CR as terminator + int lastLineEnd; // Terminator for current line + + struct ConnectionInfo_S * SysopChatStream; // Stream sysop is chatting to + +} ConnectionInfo, CIRCUIT; + +// Flags Equates + +#define GETTINGUSER 1 +#define GETTINGBBS 2 +#define CHATMODE 4 +#define GETTINGTITLE 8 +#define GETTINGMESSAGE 16 +#define CHATLINK 32 // Link to another Chat Node +#define SENDTITLE 64 +#define SENDBODY 128 +#define WAITPROMPT 256 // Waiting for prompt after message +#define PROPOSINGSYNCMSG 512 // Sent proposal to SYNC, waiting response +#define SENDINGSYNCMSG 1024 // Sent message to SYNC, waiting response +#define REQUESTINGSYNC 2048 +#define GETTINGSYNCMESSAGE 4096 // Receiving body of a SYNC message + +// BBSFlags Equates + +#define BBS 1 +#define FBBForwarding 2 +#define FBBCompressed 4 +#define FBBB1Mode 8 +#define FBBB2Mode 16 +#define RunningConnectScript 32 +#define MBLFORWARDING 64 // MBL Style Frwarding- waiting for OK/NO or Prompt following message +#define TEXTFORWARDING 128 // Plain Text forwarding +#define OUTWARDCONNECT 256 // We connected to them +#define FLARQMODE 512 // Message from FLARQ +#define FLARQMAIL 1024 // Sending FLARQ Format Message +#define ARQMAILACK 2048 // Waiting for all data to be acked +#define NEEDLF 4096 // Add LF to forward script commands (fro Telnet +#define MCASTRX 8192 // Stream in Multicast RX Mode +#define DISCONNECTING 16384 // Disconnect sent to Node +#define YAPPTX 0x008000 // Sending YAPP file +#define SYSOPCHAT 0x010000 // Chatting to BBS console +#define WINLINKRO 0x020000 // WL2K RO (no J in SID) +#define SYNCMODE 0x040000 // RMS RELAY SYNC +#define MFJMODE 0x080000 // MFJ PMS +#define NEWPACCOM 0x100000 // PACCOM PMS 3.2 +#define SETCALLTOSENDER 0x200000 // Set calling call to message sender + + +struct FBBRestartData +{ + struct MsgInfo * TempMsg; // Header while message is being received + struct UserInfo * UserPointer; + UCHAR * MailBuffer; // Mail Message being received + int MailBufferSize; // Total Malloc'ed size. Actual size in in Msg Struct + int Count; // Give up if too many restarts +}; + +// We need to keep the B2Message file for B2 messages we are sending until the messages is acked, so +// we can restart it. Otherwise the file may change, resulting in a checksum error + + +struct B2RestartData +{ + int CSize; // Compresses Size (B2 proto) + UCHAR * CompressedMsg; // Compressed Body fo B2 + struct MsgInfo * FwdMsg; + struct UserInfo * UserPointer; + int Count; // Give up if too many restarts +}; + +//------ TAJ ----- +typedef struct PGARGS +{ + CIRCUIT * conn; + struct UserInfo * user; + char InputBuffer[80]; + int Len; +}RUNPGARGS, *RUNPGARGS_PTR; + +//--------------- + +#pragma pack(1) + +struct TempUserInfo +{ + int LastAuthCode; // Protect against playback attack + + // Fields used to allow interrupting and resuming a paged listing + + BOOL ListActive; // Doing a list + BOOL ListSuspended; // Paused doing a list + int LastListedInPagedMode; + char LastListCommand[80]; + char LastListParams[80]; + int LinesSent; + char SendFullFrom; + char ListType; + char ListDirn; + char ListStatus; + char ListSelector; // < > @ etc + + int ListRangeStart; + int ListRangeEnd; + int LLCount; // Number still to send in List Last N + int UpdateLatest; // if set, save last listed as latest + BOOL IncludeKilled; // Show Killed Messages if SYSOP + //--- TAJ --- + int PG_INDEX; // current index of PG server + int PG_SERVER; // PG server to run + RUNPGARGS_PTR RUNPGPARAMS; // pointer to RUNPGARGS for dealloc + //----------- +}; + +#define PMSG 1 +#define BMSG 2 +#define TMSG 3 + +struct OldUserInfo +{ + // Old format - without message type specific traffic counts + + char Call[10]; // Connected call without SSID +// indicat relai[8]; /* 64 Digis path */ + int lastmsg; /* 4 Last L number */ + int ConnectsIn; /* 4 Number of connexions in*/ + int TimeLastConnected; //Last connexion date */ +// long lastyap __a2__ ; /* 4 Last YN date */ + ULONG flags ; /* 4 Flags */ + + UCHAR PageLen; // Lines Per Page + UCHAR lang ; /* 1 Language */ + + int Xnewbanner; /* 4 Last Banner date */ + short Xdownload ; /* 2 download size (KB) = 100 */ + char POP3Locked ; // Nonzero if POP3 server has locked this user (stops other pop3 connections, or BBS user killing messages) + char BBSNumber; // BBS Bitmap Index Number + struct BBSForwardingInfo * ForwardingInfo; + struct UserInfo * BBSNext; // links BBS record + struct TempUserInfo * Temp; // Working Fields - not saved in user file + char xfree[6]; /* 6 Spare */ + char Xtheme; /* 1 Current topic */ + + char Name[18]; /* 18 1st Name */ + char Address[61]; /* 61 Address */ + + // Stats. Was City[31]; /* 31 City */ + + int MsgsReceived; + int MsgsSent; + int MsgsRejectedIn; // Messages we reject + int MsgsRejectedOut; // Messages Rejectd by other end + int BytesForwardedIn; + int BytesForwardedOut; + int ConnectsOut; // Forwarding Connects Out + + USHORT RMSSSIDBits; // SSID's to poll in RMS + + char Spare1; + + char HomeBBS[41]; /* 41 home BBS */ + char QRA[7]; /* 7 Qth Locator */ + char pass[13]; /* 13 Password */ + char ZIP[9]; /* 9 Zipcode */ + BOOL spare; +} ; /* Total : 360 bytes */ + +struct MsgStats +{ + int ConnectsIn; /* 4 Number of connexions in*/ + int ConnectsOut; // Forwarding Connects Out + + // Stats saveed by message type + + int MsgsReceived[4]; + int MsgsSent[4]; + int MsgsRejectedIn[4]; // Messages we reject + int MsgsRejectedOut[4]; // Messages Rejectd by other end + int BytesForwardedIn[4]; + int BytesForwardedOut[4]; +}; + +struct UserInfo +{ + // New Format - with stats maintained by message type and unused fields removed. + + // This is no longer a fixed length record so can't be saved as a binarl + + char Call[10]; // Connected call without SSID + + int Length; // To make subsequent format changes easier + + int lastmsg; /* 4 Last L number */ + int xTimeLastConnected; //Last connexion date */ + ULONG flags ; /* 4 Flags */ + + UCHAR PageLen; // Lines Per Page + + char POP3Locked ; // Nonzero if POP3 server has locked this user (stops other pop3 connections, or BBS user killing messages) + unsigned char BBSNumber; // BBS Bitmap Index Number + struct BBSForwardingInfo * ForwardingInfo; + struct UserInfo * BBSNext; // links BBS record + struct TempUserInfo * Temp; // Working Fields - not saved in user file + char Name[18]; /* 18 1st Name */ + char Address[61]; /* 61 Address */ + + USHORT RMSSSIDBits; // SSID's to poll in RMS + + char HomeBBS[41]; /* 41 home BBS */ + char QRA[7]; /* 7 Qth Locator */ + char pass[13]; /* 13 Password */ + char ZIP[9]; /* 9 Zipcode */ + + struct MsgStats Total; + struct MsgStats Last; + + char CMSPass[16]; // For Secure Signon + int WebSeqNo; + + long long TimeLastConnected; //Last connection date */ + + char Filler[44 - 8]; // So we can add a few fields wirhout another resize +}; + +// flags equates + +#define F_Excluded 0x0001 +#define F_GGG 0x0002 +#define F_Expert 0x0004 +#define F_SYSOP 0x0008 +#define F_BBS 0x0010 +#define F_RMSREDIRECT 0x0020 +#define F_BBB 0x0040 +#define F_CCC 0x0080 +#define F_DDD 0x0100 +#define F_EEE 0x0200 +#define F_FFF 0x0400 +#define F_PMS 0x0800 +#define F_EMAIL 0x1000 +#define F_HOLDMAIL 0x2000 +#define F_POLLRMS 0x4000 +#define F_SYSOP_IN_LM 0x8000 +#define F_Temp_B2_BBS 0x00010000 // "Winlink Express User" +#define F_NOWINLINK 0x00020000 // Don't add Winlink.org +#define F_NOBULLS 0x00040000 +#define F_NTSMPS 0x00080000 +#define F_APRSMFOR 0x00100000 // Send APRS message for new mail +#define F_APRSSSID 0xF0000000 // (Top 4 Bits + + +struct Override +{ + char * Call; + int Days; +}; + +struct ALIAS +{ + char * Alias; + char * Dest; +}; + +typedef struct _MESSAGEX +{ +// BASIC LINK LEVEL MESSAGE BUFFER LAYOUT + + struct _MESSAGEX * CHAIN; + + UCHAR PORT; + USHORT LENGTH; + + UCHAR DEST[7]; + UCHAR ORIGIN[7]; + +// MAY BE UP TO 56 BYTES OF DIGIS + + UCHAR CTL; + UCHAR PID; + UCHAR DATA[256]; + UCHAR DIGIS[56]; // Padding in case we have digis + +}MESSAGEX, *PMESSAGEX; + + +#pragma pack() + +// Message Database Entry. Designed to be compatible with FBB + +#define NBBBS 160 // Max BBSes we can forward to. Must be Multiple of 8, and must be 80 for FBB compatibliliy +#define NBMASK NBBBS/8 // Number of bytes in Forward bitlists. + +#pragma pack(1) + +struct OldMsgInfo +{ + char type ; + char status ; + int number ; + int length ; + int datereceived; + char bbsfrom[7] ; // ? BBS we got it from ? + char via[41] ; + char from[7] ; + char to[7] ; + char bid[13] ; + char title[61] ; + char bin; + int nntpnum; // Number within topic (ie Bull TO Addr) - used for nntp + + UCHAR B2Flags; + + char free[4]; + unsigned short nblu; + int theme ; + time_t datecreated ; + time_t datechanged ; + char fbbs[10] ; + char forw[10] ; + char emailfrom[41]; +} ; + + +struct MsgInfo +{ + char type; + char status; + int number; + int length; + int xdatereceived; + char bbsfrom[7]; // ? BBS we got it from ? + char via[41]; + char from[7]; + char to[7]; + char bid[13]; + char title[61]; + int nntpnum; // Number within topic (ie Bull TO Addr) - used for nntp + + UCHAR B2Flags; // Not all flags specific to B2 + + #define B2Msg 1 // Set if Message File is a formatted B2 message + #define Attachments 2 // Set if B2 message has attachments + #define FromPaclink 4 + #define FromCMS 8 + #define FromRMSExpress 16 + #define RadioOnlyMsg 32 // Received using call-T + #define RadioOnlyFwd 64 // Received using call-R + #define WarnNotForwardedSent 128 + + int xdatecreated; + int xdatechanged; + UCHAR fbbs[NBMASK]; + UCHAR forw[NBMASK]; + char emailfrom[41]; + char Locked; // Set if selected for sending (NTS Pickup) + char Defered; // FBB response '=' received + UCHAR UTF8; // Set if Message is in UTF8 (ie from POP/SMTP) + +// For 64 bit time_t compatibility define as long long +// (so struct is same with 32 or 64 bit time_t) + + int64_t datereceived; + int64_t datecreated; + int64_t datechanged; + + char Spare[61 - 24]; // For future use +} ; + +#define MSGTYPE_B 0 +#define MSGTYPE_P 1 + +#define MSGSTATUS_N 0 +#define MSGSTATUS_Y 1 +#define MSGSTATUS_F 2 +#define MSGSTATUS_K 3 +#define MSGSTATUS_H 4 +#define MSGSTATUS_D 5 +#define MSGSTATUS_$ 6 + +struct NNTPRec +{ + // Used for NNTP access to Bulls + + struct NNTPRec * Next; // Record held in chain, so can be held sorted + char NewsGroup[64]; // = Bull TO.at field + int FirstMsg; // Lowest Number + int LastMsg; // Highest Number + int Count; // Active Msgs + time_t DateCreated; // COntains Creation Date of First Bull in Group +}; + + +typedef struct { + char mode; + char BID[13]; + union + { /* array named screen */ + struct + { + unsigned short msgno; + unsigned short timestamp; + }; + CIRCUIT * conn; + } u; +} BIDRec, *BIDRecP; + + +typedef struct WPDBASE{ /* 194 bytes */ + char callsign[7]; + char name[13]; + unsigned char Type; + unsigned char changed; + unsigned short seen; + long long last_modif; + long long last_seen; + char first_homebbs[41]; + char secnd_homebbs[41]; + char first_zip[9]; + char secnd_zip[9]; + char first_qth[31]; + char secnd_qth[31]; +} WPRec, * WPRecP; + +#pragma pack() + +struct FWDBAND +{ + time_t FWDStartBand; + time_t FWDEndBand; +}; + + + +struct BBSForwardingInfo +{ + // Holds info for forwarding + + BOOL Enabled; // Forwarding Enabled + char ** ConnectScript; // Main Connect Script + char ** TempConnectScript; // Used with FWD command. + int ScriptIndex; // Next line in script + BOOL MoreLines; // Set until script is finsihed + + char ** TOCalls; // Calls in to field + char ** ATCalls; // Calls in ATBBS field + char ** HaddressesP; // Heirarchical Addresses for Personals to forward to (as stored) + char *** HADDRSP; // Heirarchical Addresses for Personals to forward to + char ** Haddresses; // Heirarchical Addresses to forward to (as stored) + char *** HADDRS; // Heirarchical Addresses to forward to + int * HADDROffet; // Elements added to complete the HR. At least n+1 must match to forward + char ** FWDTimes; // Time bands to forward + struct FWDBAND ** FWDBands; + int MsgCount; // Messages for this BBS + BOOL ReverseFlag; // Set if BBS wants to poll for reverse forwarding + BOOL Forwarding; // Forward in progress + int MaxFBBBlockSize; + BOOL AllowBlocked; // Allow FBB Blocked + BOOL AllowCompressed; // Allow FBB Compressed + BOOL AllowB1; // Enable B1 + BOOL AllowB2; // Enable B2 + BOOL SendCTRLZ; // Send Ctrl/z instead of /ex + BOOL PersonalOnly; // Only Forward Personals + BOOL SendNew; // Forward new messages immediately + int FwdInterval; + int RevFwdInterval; + int FwdTimer; + time_t LastReverseForward; + char *BBSHA; // HA of BBS + char ** BBSHAElements; // elements of HA of BBS + int ConTimeout; +// char UserCall[10]; // User we are forwarding on behalf of (Currently only for RMS) +// int UserIndex; // index of User we are forwarding on behalf of (Currently only for RMS) +}; + + +struct FBBHeaderLine +{ + // Holds the info from the (up to) 5 headers presented at the start of a Forward Block + + char Format; // Ascii or Binary + char MsgType; // P B etc + char From[7]; // Sender + char ATBBS[41]; // BBS of recipient (@ Field) + char To[7]; // Recipient + char BID[13]; + int Size; + int CSize; // Compresses Size (B2 proto) + BOOL B2Message; // Set if an FC type + UCHAR * CompressedMsg; // Compressed Body fo B2 + struct MsgInfo * FwdMsg; // Header so we can mark as complete +}; + +#define MAXSTACK 20 +//#define MAXLINE 10000 +#define INPUTLEN 512 + +#define MAXLINES 1000 +#define LINELEN 200 + +extern char RTFHeader[4000]; +extern int RTFHddrLen; + +struct ConsoleInfo +{ + struct ConsoleInfo * next; + CIRCUIT * Console; + int BPQStream; + WNDPROC wpOrigInputProc; + HWND hConsole; + HWND hwndInput; + HWND hwndOutput; + HMENU hMenu; // handle of menu + RECT ConsoleRect; + RECT OutputRect; + + int Height, Width, LastY; + + int ClientHeight, ClientWidth; + char kbbuf[INPUTLEN]; + int kbptr; + + char * readbuff; // Malloc'ed + int readbufflen; // Current Length + char * KbdStack[MAXSTACK]; + + int StackIndex; + + BOOL Bells; + BOOL FlashOnBell; // Flash instead of Beep + BOOL StripLF; + + BOOL WarnWrap; + BOOL FlashOnConnect; + BOOL WrapInput; + BOOL CloseWindowOnBye; + + unsigned int WrapLen; + int WarnLen; + int maxlinelen; + + int PartLinePtr; + int PartLineIndex; // Listbox index of (last) incomplete line + + DWORD dwCharX; // average width of characters + DWORD dwCharY; // height of characters + DWORD dwClientX; // width of client area + DWORD dwClientY; // height of client area + DWORD dwLineLen; // line length + int nCaretPosX; // horizontal position of caret + int nCaretPosY; // vertical position of caret + + COLORREF FGColour; // Text Colour + COLORREF BGColour; // Background Colour + COLORREF DefaultColour; // Default Text Colour + + int CurrentLine; // Line we are writing to in circular buffer. + + int Index; + BOOL SendHeader; + BOOL Finished; + + char OutputScreen[MAXLINES][LINELEN]; + + int Colourvalue[MAXLINES]; + int LineLen[MAXLINES]; + + int CurrentColour; + int Thumb; + int FirstTime; + BOOL Scrolled; // Set if scrolled back + int RTFHeight; // Height of RTF control in pixels + +}; + + +struct MSESSION +{ + struct MSESSION * Next; + unsigned int Key; + char * FileName; + char * OrigTimeStamp; + unsigned char * Message; + int MessageLen; + unsigned char * BlockList; + char * ID; + int BlockSize; + int BlockCount; + int BlocksReceived; + BOOL Completed; + time_t Created; + time_t LastUpdated; + int Index; // Line in Display +}; + +VOID __cdecl nprintf(CIRCUIT * conn, const char * format, ...); +char * strlop(char * buf, char delim); +int rt_cmd(CIRCUIT *circuit, char * Buffer); +CIRCUIT *circuit_new(CIRCUIT *circuit, int flags); +VOID BBSputs(CIRCUIT * conn, char * buf); +VOID FBBputs(CIRCUIT * conn, char * buf); +void makelinks(void); +VOID * _zalloc(size_t len); +VOID FreeChatMemory(); +VOID ChatTimer(); +VOID nputs(CIRCUIT * conn, char * buf); +VOID node_close(); +VOID removelinks(); +VOID SetupChat(); +VOID SendChatLinkStatus(); +VOID ClearChatLinkStatus(); +VOID Send_MON_Datagram(UCHAR * Msg, DWORD Len); + +#define Connect(stream) SessionControl(stream,1,0) +#define Disconnect(stream) SessionControl(stream,2,0) +#define ReturntoNode(stream) SessionControl(stream,3,0) +#define ConnectUsingAppl(stream, appl) SessionControl(stream, 0, appl) + +int EncryptPass(char * Pass, char * Encrypt); +VOID DecryptPass(char * Encrypt, unsigned char * Pass, unsigned int len); + +// TCP Connections. FOr the moment SMTP or POP3 + +typedef struct SocketConnectionInfo +{ + struct SocketConnectionInfo * Next; + int Number; // Number of record - for Connections display + SOCKET socket; + SOCKADDR_IN sin; + int Type; // SMTP or POP3 + BOOL AMPR; // Set if sending to an AMPR.ORG server + char FromDomain[50]; // Domain we are sending from + struct UserInfo * bbs; // BBS dor forwarding to AMPR + int State; // Transaction State Machine + UCHAR CallSign[10]; + UCHAR TCPBuffer[3000]; // For converting byte stream to messages + int InputLen; // Data we have alreasdy = Offset of end of an incomplete packet; + + char * MailFrom; // Envelope Sender and Receiver + char ** RecpTo; // May be several Recipients + int Recipients; + + UCHAR * MailBuffer; // Mail Message being received. malloc'ed as needed + int MailBufferSize; // Total Malloc'ed size. Actual size is in MailSize + int MailSize; + int Flags; + + struct UserInfo * POP3User; + struct MsgInfo ** POP3Msgs; // Header List of messages for this uaer + int POP3MsgCount; // No of Messages + int POP3MsgNum; // Sequence number of message being received + + struct MsgInfo * SMTPMsg; // message for this SMTP connection + + UCHAR * SendBuffer; // Message being sent if socket is busy. malloc'ed as needed + int SendBufferSize; // Total Malloc'ed size. Actual size is in MailSize + int SendSize; // Bytes in buffer + int SendPtr; // next byte to send when ready + + struct NNTPRec * NNTPGroup; // Currently Selected Group + int NNTPNum; // Currenrly Selected Msg Number + int Timeout; // Used to close a session that is open too long + +} SocketConn; + +// FBB reject.sys like filters + +typedef struct FBBFILTER +{ + struct FBBFILTER * Next; + char Action; + char Type; + char From[10]; + char AT[10]; + char TO[10]; + char BID[16]; + int MaxLen; + +} FBBFilter; + +extern FBBFilter * Filters; + +typedef struct KEYVALUES +{ + char * Key; + char * Value; +} KeyValues; + +typedef struct WEBMAILINFO +{ + // Info for HTML Forms Processing + + struct HtmlFormDir * Dir; // HTML Directory + char * txtFileName; // Template Name for current message + char * InputHTMLName; // Template to input message + char * DisplayHTMLName; // Template to display message + char * ReplyHTMLName; // Template for replying to message + char * txtFile; // Template data + char * OrigTo; // To field when template loaded + char * OrigSubject; // Subject field when template loaded + char * OrigBody; // Msg text when template loaded + char * OrigBID; + char OrigType; + char * To; + char * CC; + char * Subject; + char * Body; + char * BID; + char Type; + struct MsgInfo * Msg; // Msg record if replying + KeyValues txtKeys[1000]; // Key/Value pairs for txt template. Used when creating or displaying + KeyValues XMLKeys[1000]; // Key/Value pairs from XML attachment + BOOL isReply; + char * XMLName; + char * XML; // XML attachment + int XMLLen; + int Files; + char * FileName[100]; // Attachments + char * FileBody[100]; + int FileLen[100]; + + char * Header; + int HeaderLen; + + char * Footer; + int FooterLen; + + char * Reply; // put in here to save passing lots of parameters + int * RLen; + + BOOL Winlink; + BOOL P2P; + BOOL Packet; + + int CurrentMessageIndex; // Index of message currently displayed (for Prev and Next) +#ifdef WIN32 + void * iconv_toUTF8; // Used on Linux for char set conversion +#else + iconv_t * iconv_toUTF8; // Used on Linux for char set conversion +#endif + +}WebMailInfo; + +#define SMTPServer 1 +#define POP3SLAVE 2 +#define SMTPClient 3 +#define POP3Client 4 +#define NNTPServer 5 + +// State Values + +#define GettingUser 1 +#define GettingPass 2 +#define Authenticated 4 + +#define Connecting 8 + +// SMTP Master + +#define WaitingForGreeting 16 +#define WaitingForHELOResponse 32 +#define WaitingForFROMResponse 64 +#define WaitingForTOResponse 128 +#define WaitingForDATAResponse 256 +#define WaitingForBodyResponse 512 +#define WaitingForAUTHResponse 1024 + +// POP3 Master + +#define WaitingForUSERResponse 32 +#define WaitingForPASSResponse 64 +#define WaitingForSTATResponse 128 +#define WaitingForUIDLResponse 256 +#define WaitingForLISTResponse 512 +#define WaitingForRETRResponse 512 +#define WaitingForDELEResponse 1024 +#define WaitingForQUITResponse 2048 + + +#define SE 240 // End of subnegotiation parameters +#define NOP 241 //No operation +//#define DM 242 //Data mark Indicates the position of a Synch event within the data stream. This should always be accompanied by a TCP urgent notification. +#define BRK 243 //Break Indicates that the "break" or "attention" key was hi. +#define IP 244 //Suspend Interrupt or abort the process to which the NVT is connected. +#define AO 245 //Abort output Allows the current process to run to completion but does not send its output to the user. +#define AYT 246 //Are you there Send back to the NVT some visible evidence that the AYT was received. +#define EC 247 //Erase character The receiver should delete the last preceding undeleted character from the data stream. +#define EL 248 //Erase line Delete characters from the data stream back to but not including the previous CRLF. +#define GA 249 //Go ahead Under certain circumstances used to tell the other end that it can transmit. +#define SB 250 //Subnegotiation Subnegotiation of the indicated option follows. +#define WILL 251 //will Indicates the desire to begin performing, or confirmation that you are now performing, the indicated option. +#define WONT 252 //wont Indicates the refusal to perform, or continue performing, the indicated option. +#define DOx 253 //do Indicates the request that the other party perform, or confirmation that you are expecting the other party to perform, the indicated option. +#define DONT 254 //dont Indicates the demand that the other party stop performing, or confirmation that you are no longer expecting the other party to perform, the indicated option. +#define IAC 255 + +#define suppressgoahead 3 //858 +//#define Status 5 //859 +//#define echo 1 //857 +#define timingmark 6 //860 +#define terminaltype 24 //1091 +#define windowsize 31 //1073 +#define terminalspeed 32 //1079 +#define remoteflowcontrol 33 //1372 +#define linemode 34 //1184 +#define environmentvariables 36 //1408 + +BOOL Initialise(); +#ifdef WIN32 +INT_PTR CALLBACK ConfigWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +#endif +int DisplaySessions(); +int DoStateChange(int Stream); +int DoReceivedData(int Stream); +int DoBBSMonitorData(int Stream); +int Connected(int Stream); +int Disconnected(int Stream); +//int Socket_Accept(int SocketId); +//int Socket_Data(int SocketId,int error, int eventcode); +int DataSocket_Read(SocketConn * sockptr, SOCKET sock); +int DataSocket_Write(SocketConn * sockptr, SOCKET sock); +int DataSocket_Disconnect(SocketConn * sockptr); +int RefreshMainWindow(); +int Terminate(); +int SendtoSocket(SOCKET sock,char * Msg); +int WriteLog(char * msg); +int ConnectState(int Stream); +UCHAR * EncodeCall(UCHAR * Call); +int ParseIniFile(char * fn); +struct UserInfo * AllocateUserRecord(char * Call); +struct MsgInfo * AllocateMsgRecord(); +BIDRec * AllocateBIDRecord(); +BIDRec * AllocateTempBIDRecord(); +struct UserInfo * LookupCall(char * Call); +BIDRec * LookupBID(char * BID); +BIDRec * LookupTempBID(char * BID); +VOID RemoveTempBIDS(CIRCUIT * conn); +VOID SaveUserDatabase(); +VOID GetUserDatabase(); +VOID GetMessageDatabase(); +VOID SaveMessageDatabase(); +VOID GetBIDDatabase(); +VOID SaveBIDDatabase(); +VOID GetWPDatabase(); +VOID CopyWPDatabase(); +VOID SaveWPDatabase(); +VOID GetBadWordFile(); +WPRec * LookupWP(char * Call); +VOID SendWelcomeMsg(int Stream, ConnectionInfo * conn, struct UserInfo * user); +VOID ProcessLine(ConnectionInfo * conn, struct UserInfo * user, char* Buffer, int len); +VOID ProcessChatLine(ConnectionInfo * conn, struct UserInfo * user, char* Buffer, int len); +VOID SendPrompt(ConnectionInfo * conn, struct UserInfo * user); +int QueueMsg( ConnectionInfo * conn, char * msg, int len); +VOID SendUnbuffered(int stream, char * msg, int len); +//int GetFileList(char * Dir); +BOOL ListMessage(struct MsgInfo * Msg, ConnectionInfo * conn, struct TempUserInfo * Temp); +void DoDeliveredCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); +void DoKillCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); +void DoListCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, BOOL Resuming, char * Context); +void DoReadCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); +void KillMessage(ConnectionInfo * conn, struct UserInfo * user, int msgno); +int KillMessagesTo(ConnectionInfo * conn, struct UserInfo * user, char * Call); +int KillMessagesFrom(ConnectionInfo * conn, struct UserInfo * user, char * Call); +void DoUnholdCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); + +VOID FlagAsKilled(struct MsgInfo * Msg, BOOL SaveDB); +int ListMessagesFrom(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start); +int ListMessagesTo(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start); +int ListMessagesAT(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start); +void ListMessagesInRange(ConnectionInfo * conn, struct UserInfo * user, char * Call, int Start, int End, BOOL SendFullFrom ); +void ListMessagesInRangeForwards(ConnectionInfo * conn, struct UserInfo * user, char * Call, int Start, int End, BOOL SendFullFrom ); +int GetUserMsg(int m, char * Call, BOOL SYSOP); +void Flush(ConnectionInfo * conn); +VOID ClearQueue(ConnectionInfo * conn); +void TrytoSend(); +void ReadMessage(ConnectionInfo * conn, struct UserInfo * user, int msgno); +struct MsgInfo * FindMessage(char * Call, int msgno, BOOL sysop); +char * ReadMessageFile(int msgno); +char * ReadInfoFile(char * File); +char * FormatDateAndTime(time_t Datim, BOOL DateOnly); +int CriticalErrorHandler(char * error); +BOOL DoSendCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); +BOOL CreateMessage(ConnectionInfo * conn, char * From, char * ToCall, char * ATBBS, char MsgType, char * BID, char * Title); +VOID ProcessMsgTitle(ConnectionInfo * conn, struct UserInfo * user, char* Buffer, int len); +VOID ProcessMsgLine(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len); +VOID CreateMessageFile(ConnectionInfo * conn, struct MsgInfo * Msg); +int ProcessConnecting(CIRCUIT * circuit, char * Buffer, int Len); +VOID SaveConfig(char * ConfigName); +BOOL GetConfig(char * ConfigName); +int GetIntValue(config_setting_t * group, char * name); +//BOOL GetStringValue(config_setting_t * group, char * name, char * value, int maxlen); +BOOL GetConfigFromRegistry(); +VOID Parse_SID(CIRCUIT * conn, char * SID, int len); +VOID ProcessMBLLine(CIRCUIT * conn, struct UserInfo * user, UCHAR* Buffer, int len); +VOID ProcessFBBLine(ConnectionInfo * conn, struct UserInfo * user, UCHAR * Buffer, int len); +VOID SetupNextFBBMessage(CIRCUIT * conn); +BOOL DecodeSendParams(CIRCUIT * conn, char * Context, char ** From, char * To, char ** ATBBS, char ** BID); +int PrintMessages(HWND hDlg, int Count, int * Indexes); +int check_fwd_bit(char *mask, int bbsnumber); +void set_fwd_bit(char *mask, int bbsnumber); +void clear_fwd_bit (char *mask, int bbsnumber); +VOID SetupForwardingStruct(struct UserInfo * user); +BOOL Forward_Message(struct UserInfo * user, struct MsgInfo * Msg); +VOID StartForwarding (int BBSNumber, char ** TempScript); +BOOL Reverse_Forward(); +int ProcessBBSConnectScript(CIRCUIT * conn, char * Buffer, int len); +BOOL FBBDoForward(CIRCUIT * conn); +BOOL FindMessagestoForward(CIRCUIT * conn); +BOOL SeeifMessagestoForward(int BBSNumber, CIRCUIT * Conn); +int CountMessagestoForward(struct UserInfo * user); +int CountBytestoForward(struct UserInfo * user); + +VOID * GetMultiLineDialogParam(HWND hDialog, int DLGItem); + + +VOID * GetMultiStringValue(config_setting_t * hKey, char * ValueName); +VOID * RegGetMultiStringValue(HKEY hKey, char * ValueName); + +int MultiLineDialogToREG_MULTI_SZ(HWND hWnd, int DLGItem, HKEY hKey, char * ValueName); +int Do_BBS_Sel_Changed(HWND hDlg); +VOID FreeForwardingStruct(struct UserInfo * user); +VOID FreeList(char ** Hddr); +int Do_User_Sel_Changed(HWND hDlg); +int Do_Msg_Sel_Changed(HWND hDlg); +VOID Do_Save_Msg(); +VOID Do_Add_User(HWND hDlg); +VOID Do_Delete_User(HWND hDlg); +VOID FlagSentMessages(CIRCUIT * conn, struct UserInfo * user); +VOID HoldSentMessages(CIRCUIT * conn, struct UserInfo * user); +VOID Do_Save_User(HWND hDlg, BOOL ShowBox); +VOID DeleteBBS(struct UserInfo * user); +VOID SaveBBSConfig(); +BOOL GetChatConfig(char * ConfigName); +VOID SaveChatConfig(); +VOID SaveISPConfig(); +VOID SaveFWDConfig(); +VOID SaveMAINTConfig(); +VOID SaveWelcomeMsgs(); +VOID SavePrompts(); +VOID ReinitializeFWDStruct(struct UserInfo * user); +VOID CopyBIDDatabase(); +VOID CopyMessageDatabase(); +VOID CopyUserDatabase(); +VOID FWDTimerProc(); +VOID CreateMessageFromBuffer(CIRCUIT * conn); +VOID __cdecl nodeprintf(ConnectionInfo * conn, const char * format, ...); +VOID __cdecl nodeprintfEx(ConnectionInfo * conn, const char * format, ...); +VOID FreeOverrides(); +VOID SendMessageToSYSOP(char * Title, char * MailBuffer, int Length); +struct UserInfo * FindRMS(); +VOID FindNextRMSUser(struct BBSForwardingInfo * FWDInfo); +BOOL ConnecttoBBS (struct UserInfo * user); +BOOL SetupNewBBS(struct UserInfo * user); +VOID CreateRegBackup(); +VOID SaveFilters(HWND hDlg); +BOOL CheckRejFilters(char * From, char * To, char * ATBBS, char * BID, char Type, int Len); +BOOL CheckHoldFilters(struct MsgInfo * Msg, char * From, char * To, char * ATBBS, char * BID); +BOOL CheckifLocalRMSUser(char * FullTo); +VOID DoWPLookup(ConnectionInfo * conn, struct UserInfo * user, char Type, char *Context); +BOOL wildcardcompare(char * Target, char * Match); +VOID SendWarningToSYSOP(struct MsgInfo * Msg); +VOID DoEditUserCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID DoPollRMSCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID DoShowRMSCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID DoSetIdleTime(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID DoFwdCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID SaveFwdParams(char * Call, struct BBSForwardingInfo * ForwardingInfo); +VOID DoAuthCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID ProcessSuspendedListCommand(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len); +VOID DoReroute(CIRCUIT * conn, struct UserInfo * user); + +// FBB Routines + +VOID SendCompressed(CIRCUIT * conn, struct MsgInfo * FwdMsg); +VOID SendCompressedB2(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader); +VOID UnpackFBBBinary(CIRCUIT * conn); +void Decode(CIRCUIT * conn, __int16 DecodeOnly); +//long Encode(char * in, char * out, long inlen, BOOL B1Protocol); + +BOOL CreateB2Message(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader, char * Rline); +VOID SaveFBBBinary(CIRCUIT * conn); +BOOL LookupRestart(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader); +BOOL DoWeWantIt(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader); + +// Console Routines + +BOOL CreateConsole(int Stream); +int WritetoConsoleWindow(int Stream, char * Msg, int len); +int ToggleParam(HMENU hMenu, HWND hWnd, BOOL * Param, int Item); +void CopyRichTextToClipboard(HWND hWnd); +void CopyToClipboard(HWND hWnd); +VOID CloseConsole(int Stream); + +// Monitor Routines + +BOOL CreateMonitor(); +int WritetoMonitorWindow(char * Msg, int len); + +BOOL CreateDebugWindow(); +VOID WritetoDebugWindow(char * Msg, int len); +VOID ClearDebugWindow(); +int RemoveLF(char * Message, int len); + +// Utilities + +BOOL isdigits(char * string); + + +void _GetSemaphore(struct SEM * Semaphore, int ID, char * File, int Line); +void FreeSemaphore(struct SEM * Semaphore); + +VOID __cdecl Debugprintf(const char * format, ...); +VOID __cdecl Logprintf(int LogMode, CIRCUIT * conn, int InOut, const char * format, ...); + +VOID SortBBSChain(); +VOID ExpandAndSendMessage(CIRCUIT * conn, char * Msg, int LOG); +int ImportMessages(CIRCUIT * conn, char * FN, BOOL Nopopup); + +// TCP Routines + +BOOL InitialiseTCP(); +VOID SetupListenSet(); +VOID TCPTimer(); +VOID TCPFastTimer(); +int Socket_Data(int sock, int error, int eventcode); +static int Socket_Accept(SOCKET SocketId); +int Socket_Connect(SOCKET sock, int Error); +VOID ProcessSMTPServerMessage(SocketConn * sockptr, char * Buffer, int Len); +int CreateSMTPMessage(SocketConn * sockptr, int i, char * MsgTitle, time_t Date, char * MsgBody, int Msglen, BOOL B2Flag); +BOOL CreateSMTPMessageFile(char * Message, struct MsgInfo * Msg); +SOCKET CreateListeningSocket(int Port); +int TidyString(char * MailFrom); +VOID ProcessPOP3ServerMessage(SocketConn * sockptr, char * Buffer, int Len); +char *str_base64_encode(char *str); +int b64decode(char *str); +SocketConn * SMTPConnect(char * Host, int Port, BOOL AMPR, struct MsgInfo * Msg, char * MsgBody); +BOOL POP3Connect(char * Host, int Port); +VOID ProcessSMTPClientMessage(SocketConn * sockptr, char * Buffer, int Len); +VOID ProcessPOP3ClientMessage(SocketConn * sockptr, char * Buffer, int Len); +int CreatePOP3Message(char * From, char * To, char * MsgTitle, time_t Date, char * MsgBody, int MsgLen, BOOL B2Flag); +void WriteLogLine(CIRCUIT * conn, int Flag, char * Msg, int MsgLen, int Flags); +int SendSock(SocketConn * sockptr, char * msg); +VOID __cdecl sockprintf(SocketConn * sockptr, const char * format, ...); +VOID SendFromQueue(SocketConn * sockptr); +VOID SendMultiPartMessage(SocketConn * sockptr, struct MsgInfo * Msg, UCHAR * msgbytes); +int CountMessagesTo(struct UserInfo * user, int * Unread); + +BOOL SendtoISP(); + +// NNTP ROutines + +VOID InitialiseNNTP(); +VOID BuildNNTPList(struct MsgInfo * Msg); +int NNTP_Data(int sock, int error, int eventcode); +int NNTP_Accept(SOCKET SocketId); + +VOID * GetOverrides(config_setting_t * group, char * ValueName); +VOID * RegGetOverrides(HKEY hKey, char * ValueName); + +VOID DoHouseKeeping(BOOL Mainual); +VOID ExpireMessages(); +VOID KillMsg(struct MsgInfo * Msg); +BOOL RemoveKilledMessages(); +VOID Renumber_Messages(); +BOOL ExpireBIDs(); +VOID MailHousekeepingResults(); +VOID CreateBBSTrafficReport(); +VOID CreateWPReport(); + +// WP Routines + +VOID ProcessWPMsg(char * MailBuffer, int Size, char * FisrtRLine); +VOID GetWPInfoFromRLine(char * From, char * FirstRLine, time_t RLineTime); +VOID UpdateWPWithUserInfo(struct UserInfo * user); +VOID GetWPBBSInfo(char * Rline); + +// UI Routines + +VOID SetupUIInterface(); +VOID Free_UI(); +VOID SendLatestUI(int Port); +VOID SendMsgUI(struct MsgInfo * Msg); +static VOID Send_AX_Datagram(UCHAR * Msg, DWORD Len, UCHAR Port, UCHAR * HWADDR, BOOL Queue); +VOID SeeifBBSUIFrame(struct _MESSAGEX * buff, int len); +struct MsgInfo * FindMessageByNumber(int msgno); +int CountConnectionsOnPort(int CheckPort); + +// Message Routing Routtines + +VOID SetupHAElements(struct BBSForwardingInfo * ForwardingInfo); +VOID SetupHAddreses(struct BBSForwardingInfo * ForwardingInfo); +VOID SetupHAddresesP(struct BBSForwardingInfo * ForwardingInfo); +VOID SetupMyHA(); +VOID SetupFwdAliases(); +struct Continent * FindContinent(char * Name); +int MatchMessagetoBBSList(struct MsgInfo * Msg, CIRCUIT * conn); +BOOL CheckABBS(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char * HRoute); +BOOL CheckBBSToList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo); +BOOL CheckBBSAtList(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS); +BOOL CheckBBSHList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char * HRoute); +BOOL CheckBBSHElements(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char ** HElements); +BOOL CheckBBSHElementsFlood(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char ** HElements); +int CheckBBSToForNTS(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo); +int CheckBBSATListWildCarded(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS); + +VOID ReRouteMessages(); + +VOID initUTF8(); +int Is8Bit(unsigned char *cpt, int len); +int IsUTF8(unsigned char *ptr, int len); +int IsUTF8(unsigned char *ptr, int len); +int WebIsUTF8(unsigned char *ptr, int len); +int Convert437toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); +int Convert1251toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); +int Convert1252toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); +int TrytoGuessCode(unsigned char * Char, int Len); + + +VOID FreeWebMailMallocs(); + +extern int _MYTIMEZONE; + +extern HKEY REGTREE; +extern char * REGTREETEXT; + +extern HBRUSH bgBrush; +extern BOOL cfgMinToTray; + +extern CIRCUIT * Console; + +extern ULONG ChatApplMask; +extern char Verstring[]; + +extern char SignoffMsg[]; +extern char AbortedMsg[]; +extern char InfoBoxText[]; // Text to display in Config Info Popup + +extern int LastVer[4]; // In case we need to do somthing the first time a version is run + +extern HWND MainWnd; +extern char BaseDir[]; +extern char BaseDirRaw[]; +extern char MailDir[]; +extern char WPDatabasePath[]; +extern char RlineVer[50]; + +extern BOOL LogBBS; +extern BOOL LogCHAT; +extern BOOL LogTCP; +extern BOOL ForwardToMe; +extern BOOL OnlyKnown; + +extern BOOL AllowAnon; +extern BOOL UserCantKillT; +extern BOOL DontNeedHomeBBS; + +extern int LatestMsg; +extern char BBSName[]; +extern char SYSOPCall[]; +extern char BBSSID[]; +extern char NewUserPrompt[]; + +extern char * WelcomeMsg; +extern char * NewWelcomeMsg; +extern char * ChatWelcomeMsg; +extern char * NewChatWelcomeMsg; +extern char * ExpertWelcomeMsg; + +extern char * Prompt; +extern char * NewPrompt; +extern char * ExpertPrompt; + +// Filter Params + +extern char ** RejFrom; // Reject on FROM Call +extern char ** RejTo; // Reject on TO Call +extern char ** RejAt; // Reject on AT Call +extern char ** RejBID; + +extern char ** HoldFrom; // Hold on FROM Call +extern char ** HoldTo; // Hold on TO Call +extern char ** HoldAt; // Hold on AT Call +extern char ** HoldBID; + +// Send WP Params + +extern BOOL SendWP; +extern char SendWPVIA[81]; +extern char SendWPTO[11]; +extern int SendWPType; + +extern int Ver[4]; + +extern struct MsgInfo ** MsgHddrPtr; + +extern BIDRec ** BIDRecPtr; +extern int NumberofBIDs; + +extern struct NNTPRec * FirstNNTPRec; +//extern int NumberofNNTPRecs; + + +extern int NumberofMessages; +extern int FirstMessageIndextoForward; + +extern WPRec ** WPRecPtr; +extern int NumberofWPrecs; + +extern struct SEM AllocSemaphore; +extern struct SEM ConSemaphore; +extern struct SEM MsgNoSemaphore; + +extern struct MsgInfo * MsgnotoMsg[]; // Message Number to Message Slot List. + + +extern char hostname[]; +extern char RtUsr[]; +extern char RtUsrTemp[]; +extern char RtKnown[]; +extern int AXIPPort; +extern BOOL NeedStatus; + +extern BOOL ISP_Gateway_Enabled; +extern BOOL SMTPAuthNeeded; + + +extern int MaxMsgno; +extern int BidLifetime; +extern int MaxAge; +extern int MaintInterval; +extern int MaintTime; +extern int UserLifetime; + +extern int MaxRXSize; +extern int MaxTXSize; + +extern char OurNode[]; +extern char OurAlias[]; +extern BOOL SMTPMsgCreated; + +extern HINSTANCE hInst; +extern HWND hWnd; +extern RECT MainRect; + +extern char BBSName[]; +extern char HRoute[]; +extern char AMPRDomain[]; +extern BOOL SendAMPRDirect; +extern int BBSApplNum; +extern int SMTPInPort; +extern int POP3InPort; +extern int NNTPInPort; +extern BOOL RemoteEmail; + +extern int MaxStreams; +extern UCHAR * OtherNodes; +extern struct UserInfo * BBSChain; // Chain of users that are BBSes +extern struct UserInfo ** UserRecPtr; +extern int NumberofUsers; +extern struct MsgInfo ** MsgHddrPtr; +extern int NumberofMessages; +extern int HighestBBSNumber; +extern HMENU hFWDMenu; // Forward Menu Handle +extern char zeros[]; // For forward bitmask tests +extern BOOL EnableUI; +extern BOOL RefuseBulls; +extern BOOL SendSYStoSYSOPCall; +extern BOOL SendBBStoSYSOPCall; +extern BOOL DontHoldNewUsers; +extern BOOL DefaultNoWINLINK; +extern BOOL UIEnabled[]; +extern BOOL UINull[]; +extern BOOL UIMF[]; +extern BOOL UIHDDR[]; +extern char * UIDigi[]; +extern int MailForInterval; +extern char MailForText[]; + +extern BOOL ISP_Gateway_Enabled; + +extern char MyDomain[]; // Mail domain for BBS<>Internet Mapping + +extern char ISPSMTPName[]; +extern char ISPEHLOName[]; +extern int ISPSMTPPort; + +extern char ISPPOP3Name[]; +extern int ISPPOP3Port; +extern int ISPPOP3Interval; + +extern char ISPAccountName[]; +extern char ISPAccountPass[]; +extern char EncryptedISPAccountPass[]; +extern int EncryptedPassLen; +extern char *month[]; + +extern HWND hDebug; +extern RECT MonitorRect; +extern RECT DebugRect; +extern HWND hMonitor; +//extern HWND hConsole; +//extern RECT ConsoleRect; +extern int LogAge; +extern BOOL DeletetoRecycleBin; +extern BOOL SuppressMaintEmail; +extern BOOL SaveRegDuringMaint; +extern BOOL SendWP; +extern BOOL OverrideUnsent; +extern BOOL SendNonDeliveryMsgs; +extern BOOL GenerateTrafficReport; + +extern double PR; +extern double PUR; +extern double PF; +extern double PNF; +extern int BF; +extern int BNF; +extern int NTSD; +extern int NTSU; +extern int NTSF; +extern int AP; +extern int AB; + +extern struct Override ** LTFROM; +extern struct Override ** LTTO; +extern struct Override ** LTAT; + +extern time_t LastHouseKeepingTime; +extern time_t LastTrafficTime; + +extern char * MyElements[]; +extern char ** AliasText; +extern struct ALIAS ** Aliases; + +extern BOOL ReaddressLocal; +extern BOOL ReaddressReceived; +extern BOOL WarnNoRoute; +extern BOOL SendPtoMultiple; +extern BOOL Localtime; + +extern struct ConsoleInfo * ConsHeader[2]; + +extern BOOL NeedHomeBBS; +extern char ConfigName[250]; +extern BOOL UsingingRegConfig; + +extern BOOL MulticastRX; + +extern BOOL FilterWPBulls; +extern BOOL NoWPGuesses; +extern char ** SendWPAddrs; // Replacers WP To and VIA + +extern BOOL DontCheckFromCall; + +extern time_t APIClock;; + +// YAPP stuff + +#define SOH 1 +#define STX 2 +#define ETX 3 +#define EOT 4 +#define ENQ 5 +#define ACK 6 +#define DLE 0x10 +#define NAK 0x15 +#define CAN 0x18 diff --git a/.svn/pristine/0e/0ebfb2fa079aeb6cc3c978273fe57af90ded386a.svn-base b/.svn/pristine/0e/0ebfb2fa079aeb6cc3c978273fe57af90ded386a.svn-base index 66a1577..a4021bf 100644 --- a/.svn/pristine/0e/0ebfb2fa079aeb6cc3c978273fe57af90ded386a.svn-base +++ b/.svn/pristine/0e/0ebfb2fa079aeb6cc3c978273fe57af90ded386a.svn-base @@ -1,1856 +1,1856 @@ -/* -Copyright 2001-2022 John Wiseman G8BPQ - -This file is part of LinBPQ/BPQ32. - -LinBPQ/BPQ32 is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -LinBPQ/BPQ32 is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses -*/ - - -// Module to provide a basic Gateway between IP over AX.25 and the Internet. - -// Uses WinPcap on Windows, TAP Driver on Linux - -// Basically operates as a mac level bridge, with headers converted between ax.25 and Ethernet. -// ARP frames are also reformatted, and monitored to build a simple routing table. -// Apps may not use ARP (MSYS is configured with an ax.25 default route, rather than an IP one), -// so the default route must be configured. - -// Intended as a gateway for legacy apps, rather than a full function ip over ax.25 router. -// Suggested config is to use the Internet Ethernet Adapter, behind a NAT/PAT Router. -// The ax.25 applications will appear as single addresses on the Ethernet LAN - -// The code can also switch packets between ax.25 interfaces - -// First Version, July 2008 - -// Version 1.2.1 January 2009 - -// Add IP Address Mapping option - -// June 2014. Convert to Router instead of MAC Bridge, and include a RIP44 decoder -// so packets can be routed from RF to/from encapsulated 44 net subnets. -// Routes may also be learned from received RF packets, or added from config file - -/* -TODo ?Multiple Adapters -*/ - - -// ip tuntap add dev bpqtap mode tap -// ifconfig bpqtap 44.131.4.19 mtu 256 up - - - -#pragma data_seg("_BPQDATA") - -#define _CRT_SECURE_NO_DEPRECATE - -#include -#include - -#include "cheaders.h" - -#include "ipcode.h" - -#ifdef WIN32 -#include "pcap.h" -#endif - -#ifndef LINBPQ -#include "kernelresource.h" -LRESULT CALLBACK ResWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); -#endif - -//#define s_addr S_un.S_addr - -extern BPQVECSTRUC * IPHOSTVECTORPTR; - -BOOL APIENTRY Send_AX(PMESSAGE Block, DWORD Len, UCHAR Port); -VOID SENDSABM(struct _LINKTABLE * LINK); -BOOL FindLink(UCHAR * LinkCall, UCHAR * OurCall, int Port, struct _LINKTABLE ** REQLINK); -BOOL ProcessConfig(); -VOID RemoveARP(PARPDATA Arp); - -VOID ProcessTunnelMsg(PIPMSG IPptr); -VOID ProcessRIP44Message(PIPMSG IPptr); -PROUTEENTRY LookupRoute(uint32_t IPADDR, uint32_t Mask, BOOL Add, BOOL * Found); -BOOL ProcessROUTELine(char * buf, BOOL Locked); -VOID DoRouteTimer(); -PROUTEENTRY FindRoute(uint32_t IPADDR); -VOID SendIPtoEncap(PIPMSG IPptr, uint32_t Encap); -USHORT Generate_CHECKSUM(VOID * ptr1, int Len); - -static VOID MapRouteIPMsg(PIPMSG IPptr); -BOOL Check_Checksum(VOID * ptr1, int Len); - -static BOOL Send_ETH(VOID * Block, DWORD len); - -VOID ProcessEthARPMsg(PETHARP arpptr); -static VOID SendARPMsg(PARPDATA Arp); -VOID SendICMPTimeExceeded(PIPMSG IPptr); - -#define ARPTIMEOUT 3600 - - -// ARP REQUEST/REPLY (Eth) - -static ETHARP ETHARPREQMSG = {0}; - -static ARPDATA ** ARPRecords = NULL; // ARP Table - malloc'ed as needed - -static int NumberofARPEntries = 0; - -static ROUTEENTRY ** RouteRecords = NULL; - -static int NumberofRoutes = 0; - -//HANDLE hBPQNET = INVALID_HANDLE_VALUE; - -static uint32_t OurIPAddr = 0; - -static uint32_t OurIPBroadcast = 0; -static uint32_t OurNetMask = 0xffffffff; - -static BOOL WantTAP = FALSE; -static BOOL WantEncap = 0; // Run RIP44 and Net44 Encap - -static int IPTTL = 128; - -static int FramesForwarded = 0; -static int FramesDropped = 0; -static int ARPTimeouts = 0; -static int SecTimer = 10; - -static BOOL NeedResolver = FALSE; - -static HMENU hMenu; -extern HMENU hWndMenu; -static HMENU hPopMenu; - -extern HKEY REGTREE; - -extern int OffsetH, OffsetW; - -extern HMENU hMainFrameMenu, hBaseMenu; -extern HWND ClientWnd, FrameWnd; - -static int map_table_len = 0; -//int index=0; // pointer for table search -static int ResolveIndex=-1; // pointer to entry being resolved - -static struct map_table_entry map_table[MAX_ENTRIES]; - -static int Windowlength, WindowParam; - - -static time_t ltime,lasttime; - -static char ConfigClassName[]="CONFIG"; - -HWND hIPResWnd = 0; - -BOOL IPMinimized; - -static int baseline=0; - -static unsigned char hostaddr[64]; - - -// Following two fields used by stats to get round shared memmory problem - -static ARPDATA Arp={0}; -static int ARPFlag = -1; - -// Following Buffer is used for msgs from WinPcap. Put the Enet message part way down the buffer, -// so there is room for ax.25 header instead of Enet header when we route the frame to ax.25 -// Enet Header ia 14 bytes, AX.25 UI is 16 - -// Also used to reassemble NOS Fragmented ax.25 packets - -static UCHAR Buffer[4096] = {0}; - -#define EthOffset 30 // Should be plenty - -static DWORD IPLen = 0; - - -#ifdef WIN32 -static UCHAR ourMACAddr[6] = {02,'B','P','Q',3,48}; -#else -UCHAR ourMACAddr[6] = {02,'B','P','Q',0,1}; -#endif - -static LONG DefaultIPAddr = 0; - -static IPSTATS IPStats = {0}; - -static UCHAR BPQDirectory[260]; - -static char ARPFN[MAX_PATH]; - -static HANDLE handle; - -#ifdef WIN32 -static pcap_t *adhandle = 0; -static pcap_t * (FAR * pcap_open_livex)(const char *, int, int, int, char *); - -static int pcap_reopen_delay; -#endif - -static char Adapter[256]; - -static int Promiscuous = 1; // Default to Promiscuous - -#ifdef WIN32 - -static HINSTANCE PcapDriver=0; - -typedef int (FAR *FARPROCX)(); - -static int (FAR * pcap_sendpacketx)(); - -static FARPROCX pcap_compilex; -static FARPROCX pcap_setfilterx; -static FARPROCX pcap_datalinkx; -static FARPROCX pcap_next_exx; -static FARPROCX pcap_geterrx; - - -static char Dllname[6]="wpcap"; - -FARPROCX GetAddress(char * Proc); - -#else - -#define pcap_compilex pcap_compile -#define pcap_open_livex pcap_open_live -#define pcap_setfilterx pcap_setfilter -#define pcap_datalinkx pcap_datalink -#define pcap_next_exx pcap_next_ex -#define pcap_geterrx pcap_geterr -#define pcap_sendpacketx pcap_sendpacket -#endif -VOID __cdecl Debugprintf(const char * format, ...); - -static HANDLE hInstance; - - - - -void OpenTAP(); - -Dll BOOL APIENTRY Init_PM() -{ - ARPDATA * ARPptr; - - if (hIPResWnd) - { - PostMessage(hIPResWnd, WM_CLOSE,0,0); -// DestroyWindow(hIPResWnd); - - Debugprintf("IP Init Destroying IP Resolver"); - } - - hIPResWnd= NULL; - - ARPRecords = NULL; // ARP Table - malloc'ed as needed - NumberofARPEntries=0; - - RouteRecords = NULL; - NumberofRoutes = 0; - - ReadConfigFile(); - - // Clear old packets - - memset(ETHARPREQMSG.MSGHDDR.DEST, 255, 6); - memcpy(ETHARPREQMSG.MSGHDDR.SOURCE, ourMACAddr, 6); - ETHARPREQMSG.MSGHDDR.ETYPE = 0x0608; // ARP - - ETHARPREQMSG.HWTYPE=0x0100; // Eth - ETHARPREQMSG.PID=0x0008; - ETHARPREQMSG.HWADDRLEN = 6; - ETHARPREQMSG.IPADDRLEN = 4; - -#ifdef WIN32 - - // - // Open PCAP Driver - - if (Adapter[0]) // Don't have to have ethernet, if used just as ip over ax.25 switch - { - char buf[80]; - - if (OpenPCAP()) - sprintf(buf,"Portmapper Using %s\n", Adapter); - else - sprintf(buf," Portmapper Unable to open %s\n", Adapter); - - WritetoConsoleLocal(buf); - - if (adhandle == NULL) - { - WritetoConsoleLocal("Failed to open pcap device - Portmapper Disabled\n"); - return FALSE; - } - - // Allocate ARP Entry for Default Gateway, and send ARP for it - - if (DefaultIPAddr) - { - ARPptr = AllocARPEntry(); - - if (ARPptr != NULL) - { - ARPptr->ARPINTERFACE = 255; - ARPptr->ARPTYPE = 'E'; - ARPptr->IPADDR = DefaultIPAddr; - ARPptr->LOCKED = TRUE; - - SendARPMsg(ARPptr); - } - } - } - -#else - - // Linux - if TAP requested, open it -#ifndef MACBPQ - - if (WantTAP) - OpenTAP(); - -#endif -#endif - - -#ifndef LINBPQ - - if (NeedResolver) - { - WNDCLASS wc; - int i; - char WindowTitle[100]; - int retCode, Type, Vallen; - HKEY hKey; - char Size[80]; - RECT Rect = {0,0,0,0}; - - retCode = RegOpenKeyEx (REGTREE, "SOFTWARE\\G8BPQ\\BPQ32", 0, KEY_QUERY_VALUE, &hKey); - - if (retCode == ERROR_SUCCESS) - { - Vallen=80; - - retCode = RegQueryValueEx(hKey,"IPResSize",0, - (uint32_t *)&Type,(UCHAR *)&Size,(uint32_t *)&Vallen); - - if (retCode == ERROR_SUCCESS) - sscanf(Size,"%d,%d,%d,%d,%d",&Rect.left,&Rect.right,&Rect.top,&Rect.bottom, &IPMinimized); - - if (Rect.top < - 500 || Rect.left < - 500) - { - Rect.left = 0; - Rect.top = 0; - Rect.right = 600; - Rect.bottom = 400; - } - - RegCloseKey(hKey); - } - - // Fill in window class structure with parameters that describe - // the main window. - - wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; - wc.lpfnWndProc = (WNDPROC)ResWndProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - wc.hInstance = hInstance; - wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(BPQICON)); - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); - wc.lpszMenuName = NULL ; - wc.lpszClassName = "IPAppName"; - - // Register the window classes - - RegisterClass(&wc); - - i=GetLastError(); - - Windowlength=(map_table_len)*14+100; - WindowParam=WS_OVERLAPPEDWINDOW | WS_VSCROLL; - - sprintf(WindowTitle,"PortM Resolver"); - - hIPResWnd = CreateMDIWindow("IPAppName", WindowTitle, WindowParam, - Rect.left - (OffsetW /2), Rect.top - OffsetH, Rect.right - Rect.left, Rect.bottom - Rect.top, - ClientWnd, hInstance, 1234); - - hPopMenu = CreatePopupMenu(); - AppendMenu(hPopMenu, MF_STRING, BPQREREAD, "ReRead Config"); - - SetScrollRange(hIPResWnd,SB_VERT,0, map_table_len,TRUE); - - if (IPMinimized) - ShowWindow(hIPResWnd, SW_SHOWMINIMIZED); - else - ShowWindow(hIPResWnd, SW_RESTORE); - - _beginthread(IPResolveNames, 0, NULL ); - } -#endif - - WritetoConsoleLocal("Portmapper Enabled\n"); - - return TRUE; - -} - -VOID PMClose() -{ -} - -union -{ - struct sockaddr_in rxaddr; - struct sockaddr_in6 rxaddr6; -} RXaddr; - - -Dll BOOL APIENTRY Poll_PM() -{ - int res; - struct pcap_pkthdr *header; - const u_char *pkt_data; - - // Entered every 100 mS - - // if ARPFlag set, copy requested ARP record (For BPQStatus GUI) - - if (ARPFlag != -1) - { - memcpy(&Arp, ARPRecords[ARPFlag], sizeof (ARPDATA)); - ARPFlag = -1; - } - - SecTimer--; - - if (SecTimer == 0) - { - SecTimer = 10; - DoARPTimer(); - DoRouteTimer(); - } - -Pollloop: - -#ifdef WIN32 - - if (adhandle) - { - res = pcap_next_exx(adhandle, &header, &pkt_data); - - if (res > 0) - { - PETHMSG ethptr = (PETHMSG)&Buffer[EthOffset]; - - if (header->len > 1514) - { -// Debugprintf("Ether Packet Len = %d", header->len); - goto Pollloop; - } - - memcpy(&Buffer[EthOffset],pkt_data, header->len); - - if (ethptr->ETYPE == 0x0008) - { - ProcessEthIPMsg((PETHMSG)&Buffer[EthOffset]); - // PIPMSG ipptr = (PIPMSG)&Buffer[EthOffset+14]; - // ProcessIPMsg(ipptr, ethptr->SOURCE, 'E', 255); - goto Pollloop; - } - - if (ethptr->ETYPE == 0x0608) - { - ProcessEthARPMsg((PETHARP)ethptr); - goto Pollloop; - } - - // Ignore anything else - - goto Pollloop; - } - else - { - if (res < 0) - { - char * error = (char *)pcap_geterrx(adhandle) ; - Debugprintf(error); - if (OpenPCAP() == FALSE) - pcap_reopen_delay = 300; - } - } - } - else - { - // No handle. - - if (Adapter[0]) // Don't have to have ethernet, if used just as ip over ax.25 switch - { - // Try reopening periodically - - pcap_reopen_delay --; - - if (pcap_reopen_delay < 0) - if (OpenPCAP() == FALSE) - pcap_reopen_delay = 300; // Retry every 30 seconds - } - } - -#endif - - - return TRUE; -} - - -static BOOL Send_ETH(VOID * Block, DWORD len) -{ -#ifdef WIN32 - if (adhandle) - { -// if (len < 60) len = 60; - - // Send down the packet - - pcap_sendpacketx(adhandle, // Adapter - Block, // buffer with the packet - len); // size - } -#endif - return TRUE; -} - - -static VOID SendIPtoBPQDEV(PIPMSG IPptr, UCHAR * HWADDR) -{ - // AX.25 headers are bigger, so there will always be room in buffer for enet header - - PETHMSG Ethptr = (PETHMSG)IPptr; - int Len; - - (UCHAR *)Ethptr--; - - Len = ntohs(IPptr->IPLENGTH); - - Len+=14; // Add eth Header - - memcpy(Ethptr->DEST, HWADDR, 6); - memcpy(Ethptr->SOURCE, ourMACAddr, 6); - Ethptr->ETYPE= 0x0008; - - Send_ETH(Ethptr,Len); - - return; -} - -static VOID ProcessEthIPMsg(PETHMSG Buffer) - -{ - PIPMSG ipptr = (PIPMSG)&Buffer[1]; - - if (memcmp(Buffer, ourMACAddr,6 ) != 0) - return; // Not for us - - if (memcmp(&Buffer[6], ourMACAddr,6 ) == 0) - return; // Discard our sends - - // if Checkum offload is active we get the packet before the NIC sees it (from PCAP) - - if (ipptr->IPCHECKSUM == 0) // Windows seems to do this - { - // Generate IP and TCP/UDP checksums - - int Len = ntohs(ipptr->IPLENGTH); - Len-=20; - - ipptr->IPCHECKSUM = Generate_CHECKSUM(ipptr, 20); - - if (ipptr->IPPROTOCOL == 6) // TCP - { - PTCPMSG TCP = (PTCPMSG)&ipptr->Data; - PHEADER PH = {0}; - - PH.IPPROTOCOL = 6; - PH.LENGTH = htons(Len); - memcpy(&PH.IPSOURCE, &ipptr->IPSOURCE, 4); - memcpy(&PH.IPDEST, &ipptr->IPDEST, 4); - - TCP->CHECKSUM = ~Generate_CHECKSUM(&PH, 12); - TCP->CHECKSUM = Generate_CHECKSUM(TCP, Len); - } - } - ProcessIPMsg(ipptr, Buffer->SOURCE, 'E', 255); -} - -static VOID ProcessEthARPMsg(PETHARP arpptr) -{ - int i=0; - PARPDATA Arp; - BOOL Found; - - if (memcmp(&arpptr->MSGHDDR.SOURCE, ourMACAddr,6 ) == 0 ) - return; // Discard our sends - - switch (arpptr->ARPOPCODE) - { - case 0x0100: - - // We should only accept requests from our subnet - we might have more than one net on iterface - - if ((arpptr->SENDIPADDR & OurNetMask) != (OurIPAddr & OurNetMask)) - return; - - if (arpptr->TARGETIPADDR == 0) // Request for 0.0.0.0 - return; - - // Add to our table, as we will almost certainly want to send back to it - - Arp = LookupARP(arpptr->SENDIPADDR, TRUE, &Found); - - if (Found) - goto AlreadyThere; // Already there - - if (Arp == NULL) return; // No point of table full - - Arp->IPADDR = arpptr->SENDIPADDR; - Arp->ARPTYPE = 'E'; - Arp->ARPINTERFACE = 255; - Arp->ARPTIMER = ARPTIMEOUT; - - SaveARP(); - -AlreadyThere: - - memcpy(Arp->HWADDR, arpptr->SENDHWADDR ,6); - Arp->ARPVALID = TRUE; - - if (arpptr->TARGETIPADDR == OurIPAddr) - { - uint32_t Save = arpptr->TARGETIPADDR; - - arpptr->ARPOPCODE = 0x0200; - memcpy(arpptr->TARGETHWADDR, arpptr->SENDHWADDR ,6); - memcpy(arpptr->SENDHWADDR, ourMACAddr ,6); - - arpptr->TARGETIPADDR = arpptr->SENDIPADDR; - arpptr->SENDIPADDR = Save; - - memcpy(arpptr->MSGHDDR.DEST, arpptr->MSGHDDR.SOURCE ,6); - memcpy(arpptr->MSGHDDR.SOURCE, ourMACAddr ,6); - - Send_ETH(arpptr,42); - - return; - - } - - break; - - - case 0x0200: - - if (memcmp(&arpptr->MSGHDDR.DEST, ourMACAddr,6 ) != 0 ) - return; // Not for us - - // Update ARP Cache - - Arp = LookupARP(arpptr->SENDIPADDR, TRUE, &Found); - - if (Found) - goto Update; - - if (Arp == NULL) - goto SendBack; - -Update: - Arp->IPADDR = arpptr->SENDIPADDR; - - memcpy(Arp->HWADDR, arpptr->SENDHWADDR ,6); - Arp->ARPTYPE = 'E'; - Arp->ARPINTERFACE = 255; - Arp->ARPVALID = TRUE; - Arp->ARPTIMER = ARPTIMEOUT; - SaveARP(); - -SendBack: - - // Send Back to Originator of ARP Request - - if (arpptr->TARGETIPADDR == OurIPAddr) // Reply to our request? - break; - - default: - break; - } - return; -} - -static int CheckSumAndSend(PIPMSG IPptr, PTCPMSG TCPmsg, USHORT Len) -{ - struct _IPMSG PH = {0}; - IPptr->IPCHECKSUM = 0; - - PH.IPPROTOCOL = 6; - PH.IPLENGTH = htons(Len); - memcpy(&PH.IPSOURCE, &IPptr->IPSOURCE, 4); - memcpy(&PH.IPDEST, &IPptr->IPDEST, 4); - - TCPmsg->CHECKSUM = ~Generate_CHECKSUM(&PH, 20); - TCPmsg->CHECKSUM = Generate_CHECKSUM(TCPmsg, Len); - - // No need to do IP checksum as RouteIPMessage doesit - -// CHECKSUM IT - -// IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20); - - MapRouteIPMsg(IPptr); - return 0; -} - - -static VOID ProcessIPMsg(PIPMSG IPptr, UCHAR * MACADDR, char Type, UCHAR Port) -{ - uint32_t Dest; - PARPDATA Arp; - BOOL Found; - int index, Len; - PTCPMSG TCPptr; - PUDPMSG UDPptr; - - - if (IPptr->VERLEN != 0x45) return; // Only support Type = 4, Len = 20 - - if (!CheckIPChecksum(IPptr)) return; - - // Make sure origin ia in ARP Table - - Arp = LookupARP(IPptr->IPSOURCE.addr, TRUE, &Found); - - if (!Found) - { - // Add if possible - - if (Arp != NULL) - { - Arp->IPADDR = IPptr->IPSOURCE.addr; - - if (Type == 'E') - { - memcpy(Arp->HWADDR, MACADDR, 6); - } - else - { - memcpy(Arp->HWADDR, MACADDR, 7); - Arp->HWADDR[6] &= 0x7e; - } - Arp->ARPTYPE = Type; - Arp->ARPINTERFACE = Port; - Arp->ARPVALID = TRUE; - Arp->ARPTIMER = ARPTIMEOUT; - - SaveARP(); - } - } - else - Arp->ARPTIMER = ARPTIMEOUT; // Refresh - - // See if for us - if not pass to router - - Dest = IPptr->IPDEST.addr; - - if (Dest == OurIPAddr || Dest == 0xffffffff || Dest == OurIPBroadcast) - goto ForUs; - - return; - -ForUs: - -// if (IPptr->IPPROTOCOL == 4) // AMPRNET Tunnelled Packet -// { -// ProcessTunnelMsg(IPptr); -// return; -// } - - if (IPptr->IPPROTOCOL == 1) // ICMP - { - ProcessICMPMsg(IPptr); - return; - } - - // Support UDP for SNMP - - if (IPptr->IPPROTOCOL == 17) // UDP - { - UDPptr = (PUDPMSG)&IPptr->Data; - - if (UDPptr->DESTPORT == htons(161)) - { - ProcessSNMPMessage(IPptr); - return; - } - } - - // See if for a mapped Address - - if (IPptr->IPPROTOCOL != 6) return; // Only TCP - - TCPptr = (PTCPMSG)&IPptr->Data; - - Len = ntohs(IPptr->IPLENGTH); - Len-=20; - - for (index=0; index < map_table_len; index++) - { - if ((map_table[index].sourceport == TCPptr->DESTPORT) && - map_table[index].sourceipaddr == IPptr->IPSOURCE.addr) - { - // Outgoing Message - replace Dest IP address and Port. Source Port remains unchanged - - IPptr->IPSOURCE.addr = OurIPAddr; - IPptr->IPDEST.addr = map_table[index].mappedipaddr; - TCPptr->DESTPORT = map_table[index].mappedport; - CheckSumAndSend(IPptr, TCPptr, Len); - return; - } - - if ((map_table[index].mappedport == TCPptr->SOURCEPORT) && - map_table[index].mappedipaddr == IPptr->IPSOURCE.addr) - { - // Incomming Message - replace Dest IP address and Source Port - - IPptr->IPSOURCE.addr = OurIPAddr; - IPptr->IPDEST.addr = map_table[index].sourceipaddr; - TCPptr->SOURCEPORT = map_table[index].sourceport; - CheckSumAndSend(IPptr, TCPptr, Len); - return; - } - } -} - -static VOID ProcessICMPMsg(PIPMSG IPptr) -{ - int Len; - PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data; - - Len = ntohs(IPptr->IPLENGTH); - Len-=20; - - Check_Checksum(ICMPptr, Len); - - if (ICMPptr->ICMPTYPE == 8) - { - // ICMP_ECHO - - ICMPptr->ICMPTYPE = 0; // Convert to Reply - - // CHECKSUM IT - - ICMPptr->ICMPCHECKSUM = 0; - ICMPptr->ICMPCHECKSUM = Generate_CHECKSUM(ICMPptr, Len); - - // Swap Dest to Origin - - IPptr->IPDEST = IPptr->IPSOURCE; - IPptr->IPSOURCE.addr = OurIPAddr; - IPptr->IPTTL = IPTTL; - -// IPptr->IPCHECKSUM = 0; -// IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20); // RouteIPMsg redoes checksum - - MapRouteIPMsg(IPptr); // Send Back - } - - if (ICMPptr->ICMPTYPE == 0) - { - // ICMP_REPLY: - - // I don't see how Portmapper should be getting ping responses - -/* - UCHAR * BUFFER = GetBuff(); - UCHAR * ptr1; - struct _MESSAGE * Msg; - TRANSPORTENTRY * Session = L4TABLE; - char IP[20]; - unsigned char work[4]; - - Session += ICMPptr->ICMPID; - - if (BUFFER == NULL) - return; - - ptr1 = &BUFFER[7]; - - memcpy(work, &IPptr->IPSOURCE, 4); - sprintf(IP, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]); - - *ptr1++ = 0xf0; // PID - - ptr1 += sprintf(ptr1, "Ping Response from %s", IP); - - *ptr1++ = 0x0d; // CR - - Len = ptr1 - BUFFER; - - Msg = (struct _MESSAGE *)BUFFER; - Msg->LENGTH = Len; - Msg->CHAIN = NULL; - - C_Q_ADD(&Session->L4TX_Q, (UINT *)BUFFER); - - PostDataAvailable(Session); -*/ - return; - } -} - - -static VOID SendICMPMessage(PIPMSG IPptr, int Type, int Code, int P2) -{ - PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data; - UCHAR * ptr; - - if (OurIPAddr == 0) - return; // Can't do much without one - - if (IPptr->IPPROTOCOL == ICMP && ICMPptr->ICMPTYPE == 11) - return; // Don't send Time Exceeded for TimeExceded - - // Copy the Original IP Header and first 8 bytes of packet down the buffer - - ptr = (UCHAR *) ICMPptr; - - memmove(ptr + 8, IPptr, 28); // IP header plus 8 data - -// We swap Souce to Dest, Convert to ICMP 11 and send back first 8 bytes of packet after header - - IPptr->IPDEST = IPptr->IPSOURCE; - IPptr->IPSOURCE.addr = OurIPAddr; - IPptr->IPPROTOCOL = ICMP; - IPptr->IPTTL = IPTTL; - IPptr->FRAGWORD = 0; - IPptr->IPLENGTH = htons(56); // IP Header ICMP Header IP Header 8 Data - - memset (ICMPptr, 0, 8); - ICMPptr->ICMPTYPE = Type; - ICMPptr->ICMPCODE = Code; - ICMPptr->ICMPSEQUENCE = htons(P2); - ICMPptr->ICMPCHECKSUM = Generate_CHECKSUM(ICMPptr, 36); - - MapRouteIPMsg(IPptr); -} - -static VOID MapRouteIPMsg(PIPMSG IPptr) -{ - PARPDATA Arp; - BOOL Found; - - // We rely on the ARP messages generated by either end to route frames. - // If address is not in ARP cache (say call originated by MSYS), send to our default route - - // Decremnent TTL and Recalculate header checksum - - IPptr->IPTTL--; - - if (IPptr->IPTTL == 0) - { - SendICMPTimeExceeded(IPptr); - return; // Should we send time exceeded???? - } - - IPptr->IPCHECKSUM = 0; - IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20); - - // Look up ARP - - Arp = LookupARP(IPptr->IPDEST.addr, FALSE, &Found); - - // If enabled, look in Net44 Encap Routes - - if (!Found && DefaultIPAddr) - Arp = LookupARP(DefaultIPAddr, FALSE, &Found); - - if (!Found) - return; // No route or default - - if (Arp == NULL) - return; // Should we try to ARP it? - - if (Arp->ARPVALID) - { - SendIPtoBPQDEV(IPptr, Arp->HWADDR); - } - - return; -} - -static PROUTEENTRY AllocRouteEntry() -{ - PROUTEENTRY Routeptr; - - if (NumberofRoutes == 0) - - RouteRecords = malloc(sizeof(void *)); - else - RouteRecords = realloc(RouteRecords,(NumberofRoutes + 1) * sizeof(void *)); - - Routeptr = zalloc(sizeof(ROUTEENTRY)); - - if (Routeptr == NULL) return NULL; - - RouteRecords[NumberofRoutes++] = Routeptr; - - return Routeptr; -} - - -static PARPDATA AllocARPEntry() -{ - ARPDATA * ARPptr; - - if (NumberofARPEntries == 0) - - ARPRecords = malloc(sizeof(void *)); - else - ARPRecords = realloc(ARPRecords, (NumberofARPEntries+1)*sizeof(void *)); - - ARPptr = malloc(sizeof(ARPDATA)); - - if (ARPptr == NULL) return NULL; - - memset(ARPptr, 0, sizeof(ARPDATA)); - - ARPRecords[NumberofARPEntries++] = ARPptr; - - return ARPptr; -} - - static VOID SendARPMsg(PARPDATA Arp) - { - // Send ARP. Initially used only to find default gateway - - Arp->ARPTIMER = 5; // Retry periodically - - ETHARPREQMSG.ARPOPCODE = 0x0100; // ; REQUEST - - ETHARPREQMSG.TARGETIPADDR = Arp->IPADDR; - memset(ETHARPREQMSG.TARGETHWADDR, 0, 6); - - ETHARPREQMSG.SENDIPADDR = OurIPAddr; - memcpy(ETHARPREQMSG.SENDHWADDR,ourMACAddr, 6); - - memcpy(ETHARPREQMSG.MSGHDDR.SOURCE, ourMACAddr, 6); - memset(ETHARPREQMSG.MSGHDDR.DEST, 255, 6); - - Send_ETH(ÐARPREQMSG, 42); - - return; - } - -static PROUTEENTRY FindRoute(uint32_t IPADDR) -{ - PROUTEENTRY Route = NULL; - int i; - - for (i = 0; i < NumberofRoutes; i++) - { - Route = RouteRecords[i]; - - if ((IPADDR & Route->SUBNET) == Route->NETWORK) - return Route; - } - return NULL; -} - - - -static PROUTEENTRY LookupRoute(uint32_t IPADDR, uint32_t Mask, BOOL Add, BOOL * Found) -{ - PROUTEENTRY Route = NULL; - int i; - - for (i = 0; i < NumberofRoutes; i++) - { - Route = RouteRecords[i]; - - if (Route->NETWORK == IPADDR && Route->SUBNET == Mask) - { - *Found = TRUE; - return Route; - } - } - - // Not Found - - *Found = FALSE; - - if (Add) - { - Route = AllocRouteEntry(); - return Route; - } - else - return NULL; -} - -static PARPDATA LookupARP(uint32_t IPADDR, BOOL Add, BOOL * Found) -{ - PARPDATA Arp = NULL; - int i; - - for (i=0; i < NumberofARPEntries; i++) - { - Arp = ARPRecords[i]; - - if (Arp->IPADDR == IPADDR) - { - *Found = TRUE; - return Arp; - } - } - - // Not Found - - *Found = FALSE; - - if (Add) - { - Arp = AllocARPEntry(); - return Arp; - } - else - return NULL; -} -static VOID RemoveARP(PARPDATA Arp); - -static VOID RemoveRoute(PROUTEENTRY Route) -{ - int i; - - for (i=0; i < NumberofRoutes; i++) - { - if (Route == RouteRecords[i]) - { - while (i < NumberofRoutes) - { - RouteRecords[i] = RouteRecords[i+1]; - i++; - } - - if (Route->ARP) - { - PARPDATA Arp = Route->ARP; - Route->ARP->ARPROUTE = NULL; // Avoid recursion - RemoveARP(Arp); - } - - free(Route); - NumberofRoutes--; - return; - } - } -} - - -static VOID RemoveARP(PARPDATA Arp) -{ - int i; - - if (Arp->IPADDR == DefaultIPAddr) - { - // Dont remove Default Gateway. Set to re-resolve - - Arp->ARPVALID = FALSE; - Arp->ARPTIMER = 5; - return; - } - - for (i=0; i < NumberofARPEntries; i++) - { - if (Arp == ARPRecords[i]) - { - while (i < NumberofARPEntries) - { - ARPRecords[i] = ARPRecords[i+1]; - i++; - } - - // Remove linked route - - if (Arp->ARPROUTE) - { - PROUTEENTRY Route = Arp->ARPROUTE; - Arp->ARPROUTE->ARP = NULL; // Avoid recursion - RemoveRoute(Route); - } - - free(Arp); - NumberofARPEntries--; - return; - } - } -} - - -static BOOL ReadConfigFile() -{ - -// IPAddr 192.168.0.129 -// IPBroadcast 192.168.0.255 -// IPGateway 192.168.0.1 -// IPPorts 1,4 - -// MAP 192.168.0.100 1001 n9pmo.dyndns.org 1000 - - char * Config; - char * ptr1, * ptr2; - - char buf[256],errbuf[256]; - - map_table_len = 0; // For reread - - Config = PortConfig[PortMapConfigSlot]; // Config from bpq32.cfg - - if (Config) - { - // Using config from bpq32.cfg - - ptr1 = Config; - - ptr2 = strchr(ptr1, 13); - while(ptr2) - { - memcpy(buf, ptr1, ptr2 - ptr1); - buf[ptr2 - ptr1] = 0; - ptr1 = ptr2 + 2; - ptr2 = strchr(ptr1, 13); - - strcpy(errbuf,buf); // save in case of error - - if (!ProcessLine(buf)) - { - WritetoConsoleLocal("PortMapper bad config record "); - strcat(errbuf, "\n"); - WritetoConsoleLocal(errbuf); - } - } - } - return (TRUE); -} - - -static int ProcessLine(char * buf) -{ - char * ptr, * p_value, * p_origport, * p_host, * p_port; - int port, mappedport, ipad; - - ptr = strtok(buf, " \t\n\r"); - p_value = strtok(NULL, " \t\n\r"); - - - if(ptr == NULL) return (TRUE); - - if(*ptr =='#') return (TRUE); // comment - - if(*ptr ==';') return (TRUE); // comment - - if(_stricmp(ptr,"ADAPTER") == 0) - { -#ifndef WIN32 - WritetoConsoleLocal("IPGating to Ethernet is not supported in this build\n"); - return TRUE; -#endif - strcpy(Adapter,p_value); - return (TRUE); - } - - if(_stricmp(ptr,"promiscuous") == 0) - { - Promiscuous = atoi(p_value); - return (TRUE); - } - - if (_stricmp(ptr,"IPAddr") == 0) - { - OurIPAddr = inet_addr(p_value); - - if (OurIPAddr == INADDR_NONE) return (FALSE); - - return (TRUE); - } - if (_stricmp(ptr,"IPBroadcast") == 0) - { - OurIPBroadcast = inet_addr(p_value); - - if (OurIPBroadcast == INADDR_NONE) return (FALSE); - - return (TRUE); - } - - if (_stricmp(ptr,"IPNetMask") == 0) - { - OurNetMask = inet_addr(p_value); - - if (OurNetMask == INADDR_NONE) return (FALSE); - - return (TRUE); - } - - - if (_stricmp(ptr,"IPGateway") == 0) - { - DefaultIPAddr = inet_addr(p_value); - - if (DefaultIPAddr == INADDR_NONE) return (FALSE); - - return (TRUE); - } - -// ARP 44.131.4.18 GM8BPQ-7 1 D - - if (_stricmp(ptr,"MAP") == 0) - { -#ifdef LINBPQ - - WritetoConsoleLocal("MAP not supported in LinBPQ IP Gateway\n"); - return TRUE; -#endif - if (!p_value) return FALSE; - - p_origport = strtok(NULL, " ,\t\n\r"); - if (!p_origport) return FALSE; - - p_host = strtok(NULL, " ,\t\n\r"); - if (!p_host) return FALSE; - - p_port = strtok(NULL, " ,\t\n\r"); - if (!p_port) return FALSE; - - port=atoi(p_origport); - if (port == 0) return FALSE; - - mappedport=atoi(p_port); - if (mappedport == 0) return FALSE; - - ipad = inet_addr(p_value); - - map_table[map_table_len].sourceipaddr = ipad; - strcpy(map_table[map_table_len].hostname, p_host); - map_table[map_table_len].sourceport = ntohs(port); - map_table[map_table_len++].mappedport = ntohs(mappedport); - - NeedResolver = TRUE; - - return (TRUE); - } - - // - // Bad line - // - return (FALSE); - -} - -static VOID DoARPTimer() -{ - PARPDATA Arp = NULL; - int i; - - for (i=0; i < NumberofARPEntries; i++) - { - Arp = ARPRecords[i]; - - if (!Arp->ARPVALID) - { - Arp->ARPTIMER--; - - if (Arp->ARPTIMER == 0) - { - // Retry Request - - SendARPMsg(Arp); - } - continue; - } - - // Time out active entries - - if (Arp->LOCKED == 0) - { - Arp->ARPTIMER--; - - if (Arp->ARPTIMER == 0) - { - // Remove Entry - - RemoveARP(Arp); - SaveARP(); - } - } - } -} - -static VOID DoRouteTimer() -{ - int i; - PROUTEENTRY Route; - - for (i=0; i < NumberofRoutes; i++) - { - Route = RouteRecords[i]; - if (Route->RIPTIMOUT) - Route->RIPTIMOUT--; - } -} - - -// PCAP Support Code - - -#ifdef WIN32 - -static FARPROCX GetAddress(char * Proc) -{ - FARPROCX ProcAddr; - int err=0; - char buf[256]; - int n; - - - ProcAddr=(FARPROCX) GetProcAddress(PcapDriver,Proc); - - if (ProcAddr == 0) - { - err=GetLastError(); - - n=sprintf(buf,"Error finding %s - %d", Proc,err); - WritetoConsoleLocal(buf); - - return(0); - } - - return ProcAddr; -} - - -static void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data); - -static int OpenPCAP() -{ - u_long param=1; - BOOL bcopt=TRUE; - int i=0; - char errbuf[PCAP_ERRBUF_SIZE]; - u_int netmask; - char packet_filter[64]; - struct bpf_program fcode; - char buf[256]; - int n; - - - PcapDriver=LoadLibrary(Dllname); - - if (PcapDriver == NULL) return(FALSE); - - if ((pcap_sendpacketx=GetAddress("pcap_sendpacket")) == 0 ) return FALSE; - - if ((pcap_datalinkx=GetAddress("pcap_datalink")) == 0 ) return FALSE; - - if ((pcap_compilex=GetAddress("pcap_compile")) == 0 ) return FALSE; - - if ((pcap_setfilterx=GetAddress("pcap_setfilter")) == 0 ) return FALSE; - - pcap_open_livex = (pcap_t * (__cdecl *)(const char *, int, int, int, char *)) GetProcAddress(PcapDriver,"pcap_open_live"); - - if (pcap_open_livex == NULL) return FALSE; - - if ((pcap_geterrx=GetAddress("pcap_geterr")) == 0 ) return FALSE; - - if ((pcap_next_exx=GetAddress("pcap_next_ex")) == 0 ) return FALSE; - - /* Open the adapter */ - - adhandle = pcap_open_livex(Adapter, // name of the device - 65536, // portion of the packet to capture. - // 65536 grants that the whole packet will be captured on all the MACs. - Promiscuous, // promiscuous mode (nonzero means promiscuous) - 1, // read timeout - errbuf // error buffer - ); - - if (adhandle == NULL) - return FALSE; - - /* Check the link layer. We support only Ethernet for simplicity. */ - - if(pcap_datalinkx(adhandle) != DLT_EN10MB) - { - n=sprintf(buf,"\nThis program works only on Ethernet networks.\n"); - WritetoConsoleLocal(buf); - - adhandle = 0; - return FALSE; - } - - netmask=0xffffff; - -// sprintf(packet_filter,"ether[12:2]=0x0800 or ether[12:2]=0x0806"); - - sprintf(packet_filter,"ether broadcast or ether dst %02x:%02x:%02x:%02x:%02x:%02x", - ourMACAddr[0], ourMACAddr[1], ourMACAddr[2], - ourMACAddr[3], ourMACAddr[4], ourMACAddr[5]); - - //compile the filter - - if (pcap_compilex(adhandle, &fcode, packet_filter, 1, netmask) <0 ) - { - n=sprintf(buf,"\nUnable to compile the packet filter. Check the syntax.\n"); - WritetoConsoleLocal(buf); - - adhandle = 0; - return FALSE; - } - - //set the filter - - if (pcap_setfilterx(adhandle, &fcode)<0) - { - n=sprintf(buf,"\nError setting the filter.\n"); - WritetoConsoleLocal(buf); - - adhandle = 0; - return FALSE; - } - - return TRUE; -} -#endif - - -int CompareMasks (const VOID * a, const VOID * b); - - -#ifndef LINBPQ - -extern HFONT hFont; -struct tagMSG Msg; -char buf[1024]; - -static LRESULT CALLBACK ResWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - int wmId, wmEvent; - PAINTSTRUCT ps; - HDC hdc; - HFONT hOldFont ; - struct hostent * hostptr; - struct in_addr ipad; - char line[100]; - int index,displayline; - MINMAXINFO * mmi; - - int i=1; - - int nScrollCode,nPos; - - switch (message) - { - case WM_GETMINMAXINFO: - - mmi = (MINMAXINFO *)lParam; - mmi->ptMaxSize.x = 400; - mmi->ptMaxSize.y = Windowlength; - mmi->ptMaxTrackSize.x = 400; - mmi->ptMaxTrackSize.y = Windowlength; - break; - - case WM_USER+199: - - i=WSAGETASYNCERROR(lParam); - - map_table[ResolveIndex].error=i; - - if (i ==0) - { - // resolved ok - - hostptr=(struct hostent *)&buf; - memcpy(&map_table[ResolveIndex].mappedipaddr,hostptr->h_addr,4); - } - - InvalidateRect(hWnd,NULL,FALSE); - - while (ResolveIndex < map_table_len) - { - ResolveIndex++; - - WSAAsyncGetHostByName (hWnd,WM_USER+199, - map_table[ResolveIndex].hostname, - buf,MAXGETHOSTSTRUCT); - - break; - } - break; - - case WM_MDIACTIVATE: - { - // Set the system info menu when getting activated - - if (lParam == (LPARAM) hWnd) - { - // Activate - - RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); - AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (WPARAM)hPopMenu, "Actions"); - - SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM)hWndMenu); - } - else - SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM)hMainFrameMenu, (LPARAM)NULL); - - DrawMenuBar(FrameWnd); - - return DefMDIChildProc(hWnd, message, wParam, lParam); - - } - - case WM_COMMAND: - - wmId = LOWORD(wParam); // Remember, these are... - wmEvent = HIWORD(wParam); // ...different for Win32! - - - - if (wmId == BPQREREAD) - { - if (ProcessConfig()) - { - FreeConfig(); - - ReadConfigFile(); - PostMessage(hIPResWnd, WM_TIMER,0,0); - InvalidateRect(hWnd,NULL,TRUE); - } - else - Consoleprintf("Failed to reread config file - leaving config unchanged"); - - return 0; - } -/* - if (wmId == BPQADDARP) - { - if (ConfigWnd == 0) - { - ConfigWnd=CreateDialog(hInstance,ConfigClassName,0,NULL); - - if (!ConfigWnd) - { - i=GetLastError(); - return (FALSE); - } - ShowWindow(ConfigWnd, SW_SHOW); - UpdateWindow(ConfigWnd); - } - - SetForegroundWindow(ConfigWnd); - - return(0); - } - return 0; -*/ - case WM_SYSCOMMAND: - - wmId = LOWORD(wParam); - wmEvent = HIWORD(wParam); - - switch (wmId) - { - case SC_RESTORE: - - IPMinimized = FALSE; - SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); - break; - - case SC_MINIMIZE: - - IPMinimized = TRUE; - break; - } - return DefMDIChildProc(hWnd, message, wParam, lParam); - - case WM_VSCROLL: - - nScrollCode = (int) LOWORD(wParam); // scroll bar value - nPos = (short int) HIWORD(wParam); // scroll box position - - //hwndScrollBar = (HWND) lParam; // handle of scroll bar - - if (nScrollCode == SB_LINEUP || nScrollCode == SB_PAGEUP) - { - baseline--; - if (baseline <0) - baseline=0; - } - - if (nScrollCode == SB_LINEDOWN || nScrollCode == SB_PAGEDOWN) - { - baseline++; - if (baseline > map_table_len) - baseline = map_table_len; - } - - if (nScrollCode == SB_THUMBTRACK) - { - baseline=nPos; - } - - SetScrollPos(hWnd,SB_VERT,baseline,TRUE); - - InvalidateRect(hWnd,NULL,TRUE); - break; - - - case WM_PAINT: - - hdc = BeginPaint (hWnd, &ps); - - hOldFont = SelectObject( hdc, hFont) ; - - index = baseline; - displayline=0; - - while (index < map_table_len) - { - if (map_table[index].ResolveFlag && map_table[index].error != 0) - { - // resolver error - Display Error Code - sprintf(hostaddr,"Error %d",map_table[index].error); - } - else - { - memcpy(&ipad,&map_table[index].mappedipaddr,4); - strncpy(hostaddr,inet_ntoa(ipad),16); - } - - memcpy(&ipad,&map_table[index].mappedipaddr,4); - - i=sprintf(line,"%.64s = %-.30s", - map_table[index].hostname, - hostaddr); - - TextOut(hdc,0,(displayline++)*14+2,line,i); - - index++; - } - - SelectObject( hdc, hOldFont ) ; - EndPaint (hWnd, &ps); - - break; - - case WM_DESTROY: - - -// PostQuitMessage(0); - - break; - - - case WM_TIMER: - - for (ResolveIndex=0; ResolveIndex < map_table_len; ResolveIndex++) - { - WSAAsyncGetHostByName (hWnd,WM_USER+199, - map_table[ResolveIndex].hostname, - buf,MAXGETHOSTSTRUCT); - break; - } - - default: - break; - } - return DefMDIChildProc(hWnd, message, wParam, lParam); -} - -static void IPResolveNames( void *dummy ) -{ - SetTimer(hIPResWnd,1,15*60*1000,0); - - PostMessage(hIPResWnd, WM_TIMER,0,0); - - while (GetMessage(&Msg, hIPResWnd, 0, 0)) - { - TranslateMessage(&Msg); - DispatchMessage(&Msg); - } -} - -#endif - -/* -; DO PSEUDO HEADER FIRST -; - MOV DX,600H ; PROTOCOL (REVERSED) - MOV AX,TCPLENGTH ; TCP LENGTH - XCHG AH,AL - ADD DX,AX - MOV AX,WORD PTR LOCALADDR[BX] - ADC DX,AX - MOV AX,WORD PTR LOCALADDR+2[BX] - ADC DX,AX - MOV AX,WORD PTR REMOTEADDR[BX] - ADC DX,AX - MOV AX,WORD PTR REMOTEADDR+2[BX] - ADC DX,AX - ADC DX,0 - - MOV PHSUM,DX - - PUSH BX - - MOV BX,TXBUFFER ; HEADER - - MOV CX,TCPLENGTH ; PUT LENGTH INTO HEADER - MOV BUFFLEN[BX],CX -; - MOV SI,BUFFPTR[BX] - - INC CX ; ROUND UP - SHR CX,1 ; WORD COUNT - - CALL DO_CHECKSUM - - ADD DX,PHSUM - ADC DX,0 - NOT DX - - MOV SI,BUFFPTR[BX] - MOV CHECKSUM[SI],DX - - -*/ +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + + +// Module to provide a basic Gateway between IP over AX.25 and the Internet. + +// Uses WinPcap on Windows, TAP Driver on Linux + +// Basically operates as a mac level bridge, with headers converted between ax.25 and Ethernet. +// ARP frames are also reformatted, and monitored to build a simple routing table. +// Apps may not use ARP (MSYS is configured with an ax.25 default route, rather than an IP one), +// so the default route must be configured. + +// Intended as a gateway for legacy apps, rather than a full function ip over ax.25 router. +// Suggested config is to use the Internet Ethernet Adapter, behind a NAT/PAT Router. +// The ax.25 applications will appear as single addresses on the Ethernet LAN + +// The code can also switch packets between ax.25 interfaces + +// First Version, July 2008 + +// Version 1.2.1 January 2009 + +// Add IP Address Mapping option + +// June 2014. Convert to Router instead of MAC Bridge, and include a RIP44 decoder +// so packets can be routed from RF to/from encapsulated 44 net subnets. +// Routes may also be learned from received RF packets, or added from config file + +/* +TODo ?Multiple Adapters +*/ + + +// ip tuntap add dev bpqtap mode tap +// ifconfig bpqtap 44.131.4.19 mtu 256 up + + + +#pragma data_seg("_BPQDATA") + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + +#include "cheaders.h" + +#include "ipcode.h" + +#ifdef WIN32 +#include "pcap.h" +#endif + +#ifndef LINBPQ +#include "kernelresource.h" +LRESULT CALLBACK ResWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +#endif + +//#define s_addr S_un.S_addr + +extern BPQVECSTRUC * IPHOSTVECTORPTR; + +BOOL APIENTRY Send_AX(PMESSAGE Block, DWORD Len, UCHAR Port); +VOID SENDSABM(struct _LINKTABLE * LINK); +BOOL FindLink(UCHAR * LinkCall, UCHAR * OurCall, int Port, struct _LINKTABLE ** REQLINK); +BOOL ProcessConfig(); +VOID RemoveARP(PARPDATA Arp); + +VOID ProcessTunnelMsg(PIPMSG IPptr); +VOID ProcessRIP44Message(PIPMSG IPptr); +PROUTEENTRY LookupRoute(uint32_t IPADDR, uint32_t Mask, BOOL Add, BOOL * Found); +BOOL ProcessROUTELine(char * buf, BOOL Locked); +VOID DoRouteTimer(); +PROUTEENTRY FindRoute(uint32_t IPADDR); +VOID SendIPtoEncap(PIPMSG IPptr, uint32_t Encap); +USHORT Generate_CHECKSUM(VOID * ptr1, int Len); + +static VOID MapRouteIPMsg(PIPMSG IPptr); +BOOL Check_Checksum(VOID * ptr1, int Len); + +static BOOL Send_ETH(VOID * Block, DWORD len); + +VOID ProcessEthARPMsg(PETHARP arpptr); +static VOID SendARPMsg(PARPDATA Arp); +VOID SendICMPTimeExceeded(PIPMSG IPptr); + +#define ARPTIMEOUT 3600 + + +// ARP REQUEST/REPLY (Eth) + +static ETHARP ETHARPREQMSG = {0}; + +static ARPDATA ** ARPRecords = NULL; // ARP Table - malloc'ed as needed + +static int NumberofARPEntries = 0; + +static ROUTEENTRY ** RouteRecords = NULL; + +static int NumberofRoutes = 0; + +//HANDLE hBPQNET = INVALID_HANDLE_VALUE; + +static uint32_t OurIPAddr = 0; + +static uint32_t OurIPBroadcast = 0; +static uint32_t OurNetMask = 0xffffffff; + +static BOOL WantTAP = FALSE; +static BOOL WantEncap = 0; // Run RIP44 and Net44 Encap + +static int IPTTL = 128; + +static int FramesForwarded = 0; +static int FramesDropped = 0; +static int ARPTimeouts = 0; +static int SecTimer = 10; + +static BOOL NeedResolver = FALSE; + +static HMENU hMenu; +extern HMENU hWndMenu; +static HMENU hPopMenu; + +extern HKEY REGTREE; + +extern int OffsetH, OffsetW; + +extern HMENU hMainFrameMenu, hBaseMenu; +extern HWND ClientWnd, FrameWnd; + +static int map_table_len = 0; +//int index=0; // pointer for table search +static int ResolveIndex=-1; // pointer to entry being resolved + +static struct map_table_entry map_table[MAX_ENTRIES]; + +static int Windowlength, WindowParam; + + +static time_t ltime,lasttime; + +static char ConfigClassName[]="CONFIG"; + +HWND hIPResWnd = 0; + +BOOL IPMinimized; + +static int baseline=0; + +static unsigned char hostaddr[64]; + + +// Following two fields used by stats to get round shared memmory problem + +static ARPDATA Arp={0}; +static int ARPFlag = -1; + +// Following Buffer is used for msgs from WinPcap. Put the Enet message part way down the buffer, +// so there is room for ax.25 header instead of Enet header when we route the frame to ax.25 +// Enet Header ia 14 bytes, AX.25 UI is 16 + +// Also used to reassemble NOS Fragmented ax.25 packets + +static UCHAR Buffer[4096] = {0}; + +#define EthOffset 30 // Should be plenty + +static DWORD IPLen = 0; + + +#ifdef WIN32 +static UCHAR ourMACAddr[6] = {02,'B','P','Q',3,48}; +#else +UCHAR ourMACAddr[6] = {02,'B','P','Q',0,1}; +#endif + +static LONG DefaultIPAddr = 0; + +static IPSTATS IPStats = {0}; + +static UCHAR BPQDirectory[260]; + +static char ARPFN[MAX_PATH]; + +static HANDLE handle; + +#ifdef WIN32 +static pcap_t *adhandle = 0; +static pcap_t * (FAR * pcap_open_livex)(const char *, int, int, int, char *); + +static int pcap_reopen_delay; +#endif + +static char Adapter[256]; + +static int Promiscuous = 1; // Default to Promiscuous + +#ifdef WIN32 + +static HINSTANCE PcapDriver=0; + +typedef int (FAR *FARPROCX)(); + +static int (FAR * pcap_sendpacketx)(); + +static FARPROCX pcap_compilex; +static FARPROCX pcap_setfilterx; +static FARPROCX pcap_datalinkx; +static FARPROCX pcap_next_exx; +static FARPROCX pcap_geterrx; + + +static char Dllname[6]="wpcap"; + +FARPROCX GetAddress(char * Proc); + +#else + +#define pcap_compilex pcap_compile +#define pcap_open_livex pcap_open_live +#define pcap_setfilterx pcap_setfilter +#define pcap_datalinkx pcap_datalink +#define pcap_next_exx pcap_next_ex +#define pcap_geterrx pcap_geterr +#define pcap_sendpacketx pcap_sendpacket +#endif +VOID __cdecl Debugprintf(const char * format, ...); + +static HANDLE hInstance; + + + + +void OpenTAP(); + +Dll BOOL APIENTRY Init_PM() +{ + ARPDATA * ARPptr; + + if (hIPResWnd) + { + PostMessage(hIPResWnd, WM_CLOSE,0,0); +// DestroyWindow(hIPResWnd); + + Debugprintf("IP Init Destroying IP Resolver"); + } + + hIPResWnd= NULL; + + ARPRecords = NULL; // ARP Table - malloc'ed as needed + NumberofARPEntries=0; + + RouteRecords = NULL; + NumberofRoutes = 0; + + ReadConfigFile(); + + // Clear old packets + + memset(ETHARPREQMSG.MSGHDDR.DEST, 255, 6); + memcpy(ETHARPREQMSG.MSGHDDR.SOURCE, ourMACAddr, 6); + ETHARPREQMSG.MSGHDDR.ETYPE = 0x0608; // ARP + + ETHARPREQMSG.HWTYPE=0x0100; // Eth + ETHARPREQMSG.PID=0x0008; + ETHARPREQMSG.HWADDRLEN = 6; + ETHARPREQMSG.IPADDRLEN = 4; + +#ifdef WIN32 + + // + // Open PCAP Driver + + if (Adapter[0]) // Don't have to have ethernet, if used just as ip over ax.25 switch + { + char buf[80]; + + if (OpenPCAP()) + sprintf(buf,"Portmapper Using %s\n", Adapter); + else + sprintf(buf," Portmapper Unable to open %s\n", Adapter); + + WritetoConsoleLocal(buf); + + if (adhandle == NULL) + { + WritetoConsoleLocal("Failed to open pcap device - Portmapper Disabled\n"); + return FALSE; + } + + // Allocate ARP Entry for Default Gateway, and send ARP for it + + if (DefaultIPAddr) + { + ARPptr = AllocARPEntry(); + + if (ARPptr != NULL) + { + ARPptr->ARPINTERFACE = 255; + ARPptr->ARPTYPE = 'E'; + ARPptr->IPADDR = DefaultIPAddr; + ARPptr->LOCKED = TRUE; + + SendARPMsg(ARPptr); + } + } + } + +#else + + // Linux - if TAP requested, open it +#ifndef MACBPQ + + if (WantTAP) + OpenTAP(); + +#endif +#endif + + +#ifndef LINBPQ + + if (NeedResolver) + { + WNDCLASS wc; + int i; + char WindowTitle[100]; + int retCode, Type, Vallen; + HKEY hKey; + char Size[80]; + RECT Rect = {0,0,0,0}; + + retCode = RegOpenKeyEx (REGTREE, "SOFTWARE\\G8BPQ\\BPQ32", 0, KEY_QUERY_VALUE, &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen=80; + + retCode = RegQueryValueEx(hKey,"IPResSize",0, + (uint32_t *)&Type,(UCHAR *)&Size,(uint32_t *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size,"%d,%d,%d,%d,%d",&Rect.left,&Rect.right,&Rect.top,&Rect.bottom, &IPMinimized); + + if (Rect.top < - 500 || Rect.left < - 500) + { + Rect.left = 0; + Rect.top = 0; + Rect.right = 600; + Rect.bottom = 400; + } + + RegCloseKey(hKey); + } + + // Fill in window class structure with parameters that describe + // the main window. + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wc.lpfnWndProc = (WNDPROC)ResWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(BPQICON)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wc.lpszMenuName = NULL ; + wc.lpszClassName = "IPAppName"; + + // Register the window classes + + RegisterClass(&wc); + + i=GetLastError(); + + Windowlength=(map_table_len)*14+100; + WindowParam=WS_OVERLAPPEDWINDOW | WS_VSCROLL; + + sprintf(WindowTitle,"PortM Resolver"); + + hIPResWnd = CreateMDIWindow("IPAppName", WindowTitle, WindowParam, + Rect.left - (OffsetW /2), Rect.top - OffsetH, Rect.right - Rect.left, Rect.bottom - Rect.top, + ClientWnd, hInstance, 1234); + + hPopMenu = CreatePopupMenu(); + AppendMenu(hPopMenu, MF_STRING, BPQREREAD, "ReRead Config"); + + SetScrollRange(hIPResWnd,SB_VERT,0, map_table_len,TRUE); + + if (IPMinimized) + ShowWindow(hIPResWnd, SW_SHOWMINIMIZED); + else + ShowWindow(hIPResWnd, SW_RESTORE); + + _beginthread(IPResolveNames, 0, NULL ); + } +#endif + + WritetoConsoleLocal("Portmapper Enabled\n"); + + return TRUE; + +} + +VOID PMClose() +{ +} + +union +{ + struct sockaddr_in rxaddr; + struct sockaddr_in6 rxaddr6; +} RXaddr; + + +Dll BOOL APIENTRY Poll_PM() +{ + int res; + struct pcap_pkthdr *header; + const u_char *pkt_data; + + // Entered every 100 mS + + // if ARPFlag set, copy requested ARP record (For BPQStatus GUI) + + if (ARPFlag != -1) + { + memcpy(&Arp, ARPRecords[ARPFlag], sizeof (ARPDATA)); + ARPFlag = -1; + } + + SecTimer--; + + if (SecTimer == 0) + { + SecTimer = 10; + DoARPTimer(); + DoRouteTimer(); + } + +Pollloop: + +#ifdef WIN32 + + if (adhandle) + { + res = pcap_next_exx(adhandle, &header, &pkt_data); + + if (res > 0) + { + PETHMSG ethptr = (PETHMSG)&Buffer[EthOffset]; + + if (header->len > 1514) + { +// Debugprintf("Ether Packet Len = %d", header->len); + goto Pollloop; + } + + memcpy(&Buffer[EthOffset],pkt_data, header->len); + + if (ethptr->ETYPE == 0x0008) + { + ProcessEthIPMsg((PETHMSG)&Buffer[EthOffset]); + // PIPMSG ipptr = (PIPMSG)&Buffer[EthOffset+14]; + // ProcessIPMsg(ipptr, ethptr->SOURCE, 'E', 255); + goto Pollloop; + } + + if (ethptr->ETYPE == 0x0608) + { + ProcessEthARPMsg((PETHARP)ethptr); + goto Pollloop; + } + + // Ignore anything else + + goto Pollloop; + } + else + { + if (res < 0) + { + char * error = (char *)pcap_geterrx(adhandle) ; + Debugprintf(error); + if (OpenPCAP() == FALSE) + pcap_reopen_delay = 300; + } + } + } + else + { + // No handle. + + if (Adapter[0]) // Don't have to have ethernet, if used just as ip over ax.25 switch + { + // Try reopening periodically + + pcap_reopen_delay --; + + if (pcap_reopen_delay < 0) + if (OpenPCAP() == FALSE) + pcap_reopen_delay = 300; // Retry every 30 seconds + } + } + +#endif + + + return TRUE; +} + + +static BOOL Send_ETH(VOID * Block, DWORD len) +{ +#ifdef WIN32 + if (adhandle) + { +// if (len < 60) len = 60; + + // Send down the packet + + pcap_sendpacketx(adhandle, // Adapter + Block, // buffer with the packet + len); // size + } +#endif + return TRUE; +} + + +static VOID SendIPtoBPQDEV(PIPMSG IPptr, UCHAR * HWADDR) +{ + // AX.25 headers are bigger, so there will always be room in buffer for enet header + + PETHMSG Ethptr = (PETHMSG)IPptr; + int Len; + + (UCHAR *)Ethptr--; + + Len = ntohs(IPptr->IPLENGTH); + + Len+=14; // Add eth Header + + memcpy(Ethptr->DEST, HWADDR, 6); + memcpy(Ethptr->SOURCE, ourMACAddr, 6); + Ethptr->ETYPE= 0x0008; + + Send_ETH(Ethptr,Len); + + return; +} + +static VOID ProcessEthIPMsg(PETHMSG Buffer) + +{ + PIPMSG ipptr = (PIPMSG)&Buffer[1]; + + if (memcmp(Buffer, ourMACAddr,6 ) != 0) + return; // Not for us + + if (memcmp(&Buffer[6], ourMACAddr,6 ) == 0) + return; // Discard our sends + + // if Checkum offload is active we get the packet before the NIC sees it (from PCAP) + + if (ipptr->IPCHECKSUM == 0) // Windows seems to do this + { + // Generate IP and TCP/UDP checksums + + int Len = ntohs(ipptr->IPLENGTH); + Len-=20; + + ipptr->IPCHECKSUM = Generate_CHECKSUM(ipptr, 20); + + if (ipptr->IPPROTOCOL == 6) // TCP + { + PTCPMSG TCP = (PTCPMSG)&ipptr->Data; + PHEADER PH = {0}; + + PH.IPPROTOCOL = 6; + PH.LENGTH = htons(Len); + memcpy(&PH.IPSOURCE, &ipptr->IPSOURCE, 4); + memcpy(&PH.IPDEST, &ipptr->IPDEST, 4); + + TCP->CHECKSUM = ~Generate_CHECKSUM(&PH, 12); + TCP->CHECKSUM = Generate_CHECKSUM(TCP, Len); + } + } + ProcessIPMsg(ipptr, Buffer->SOURCE, 'E', 255); +} + +static VOID ProcessEthARPMsg(PETHARP arpptr) +{ + int i=0; + PARPDATA Arp; + BOOL Found; + + if (memcmp(&arpptr->MSGHDDR.SOURCE, ourMACAddr,6 ) == 0 ) + return; // Discard our sends + + switch (arpptr->ARPOPCODE) + { + case 0x0100: + + // We should only accept requests from our subnet - we might have more than one net on iterface + + if ((arpptr->SENDIPADDR & OurNetMask) != (OurIPAddr & OurNetMask)) + return; + + if (arpptr->TARGETIPADDR == 0) // Request for 0.0.0.0 + return; + + // Add to our table, as we will almost certainly want to send back to it + + Arp = LookupARP(arpptr->SENDIPADDR, TRUE, &Found); + + if (Found) + goto AlreadyThere; // Already there + + if (Arp == NULL) return; // No point of table full + + Arp->IPADDR = arpptr->SENDIPADDR; + Arp->ARPTYPE = 'E'; + Arp->ARPINTERFACE = 255; + Arp->ARPTIMER = ARPTIMEOUT; + + SaveARP(); + +AlreadyThere: + + memcpy(Arp->HWADDR, arpptr->SENDHWADDR ,6); + Arp->ARPVALID = TRUE; + + if (arpptr->TARGETIPADDR == OurIPAddr) + { + uint32_t Save = arpptr->TARGETIPADDR; + + arpptr->ARPOPCODE = 0x0200; + memcpy(arpptr->TARGETHWADDR, arpptr->SENDHWADDR ,6); + memcpy(arpptr->SENDHWADDR, ourMACAddr ,6); + + arpptr->TARGETIPADDR = arpptr->SENDIPADDR; + arpptr->SENDIPADDR = Save; + + memcpy(arpptr->MSGHDDR.DEST, arpptr->MSGHDDR.SOURCE ,6); + memcpy(arpptr->MSGHDDR.SOURCE, ourMACAddr ,6); + + Send_ETH(arpptr,42); + + return; + + } + + break; + + + case 0x0200: + + if (memcmp(&arpptr->MSGHDDR.DEST, ourMACAddr,6 ) != 0 ) + return; // Not for us + + // Update ARP Cache + + Arp = LookupARP(arpptr->SENDIPADDR, TRUE, &Found); + + if (Found) + goto Update; + + if (Arp == NULL) + goto SendBack; + +Update: + Arp->IPADDR = arpptr->SENDIPADDR; + + memcpy(Arp->HWADDR, arpptr->SENDHWADDR ,6); + Arp->ARPTYPE = 'E'; + Arp->ARPINTERFACE = 255; + Arp->ARPVALID = TRUE; + Arp->ARPTIMER = ARPTIMEOUT; + SaveARP(); + +SendBack: + + // Send Back to Originator of ARP Request + + if (arpptr->TARGETIPADDR == OurIPAddr) // Reply to our request? + break; + + default: + break; + } + return; +} + +static int CheckSumAndSend(PIPMSG IPptr, PTCPMSG TCPmsg, USHORT Len) +{ + struct _IPMSG PH = {0}; + IPptr->IPCHECKSUM = 0; + + PH.IPPROTOCOL = 6; + PH.IPLENGTH = htons(Len); + memcpy(&PH.IPSOURCE, &IPptr->IPSOURCE, 4); + memcpy(&PH.IPDEST, &IPptr->IPDEST, 4); + + TCPmsg->CHECKSUM = ~Generate_CHECKSUM(&PH, 20); + TCPmsg->CHECKSUM = Generate_CHECKSUM(TCPmsg, Len); + + // No need to do IP checksum as RouteIPMessage doesit + +// CHECKSUM IT + +// IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20); + + MapRouteIPMsg(IPptr); + return 0; +} + + +static VOID ProcessIPMsg(PIPMSG IPptr, UCHAR * MACADDR, char Type, UCHAR Port) +{ + uint32_t Dest; + PARPDATA Arp; + BOOL Found; + int index, Len; + PTCPMSG TCPptr; + PUDPMSG UDPptr; + + + if (IPptr->VERLEN != 0x45) return; // Only support Type = 4, Len = 20 + + if (!CheckIPChecksum(IPptr)) return; + + // Make sure origin ia in ARP Table + + Arp = LookupARP(IPptr->IPSOURCE.addr, TRUE, &Found); + + if (!Found) + { + // Add if possible + + if (Arp != NULL) + { + Arp->IPADDR = IPptr->IPSOURCE.addr; + + if (Type == 'E') + { + memcpy(Arp->HWADDR, MACADDR, 6); + } + else + { + memcpy(Arp->HWADDR, MACADDR, 7); + Arp->HWADDR[6] &= 0x7e; + } + Arp->ARPTYPE = Type; + Arp->ARPINTERFACE = Port; + Arp->ARPVALID = TRUE; + Arp->ARPTIMER = ARPTIMEOUT; + + SaveARP(); + } + } + else + Arp->ARPTIMER = ARPTIMEOUT; // Refresh + + // See if for us - if not pass to router + + Dest = IPptr->IPDEST.addr; + + if (Dest == OurIPAddr || Dest == 0xffffffff || Dest == OurIPBroadcast) + goto ForUs; + + return; + +ForUs: + +// if (IPptr->IPPROTOCOL == 4) // AMPRNET Tunnelled Packet +// { +// ProcessTunnelMsg(IPptr); +// return; +// } + + if (IPptr->IPPROTOCOL == 1) // ICMP + { + ProcessICMPMsg(IPptr); + return; + } + + // Support UDP for SNMP + + if (IPptr->IPPROTOCOL == 17) // UDP + { + UDPptr = (PUDPMSG)&IPptr->Data; + + if (UDPptr->DESTPORT == htons(161)) + { + ProcessSNMPMessage(IPptr); + return; + } + } + + // See if for a mapped Address + + if (IPptr->IPPROTOCOL != 6) return; // Only TCP + + TCPptr = (PTCPMSG)&IPptr->Data; + + Len = ntohs(IPptr->IPLENGTH); + Len-=20; + + for (index=0; index < map_table_len; index++) + { + if ((map_table[index].sourceport == TCPptr->DESTPORT) && + map_table[index].sourceipaddr == IPptr->IPSOURCE.addr) + { + // Outgoing Message - replace Dest IP address and Port. Source Port remains unchanged + + IPptr->IPSOURCE.addr = OurIPAddr; + IPptr->IPDEST.addr = map_table[index].mappedipaddr; + TCPptr->DESTPORT = map_table[index].mappedport; + CheckSumAndSend(IPptr, TCPptr, Len); + return; + } + + if ((map_table[index].mappedport == TCPptr->SOURCEPORT) && + map_table[index].mappedipaddr == IPptr->IPSOURCE.addr) + { + // Incomming Message - replace Dest IP address and Source Port + + IPptr->IPSOURCE.addr = OurIPAddr; + IPptr->IPDEST.addr = map_table[index].sourceipaddr; + TCPptr->SOURCEPORT = map_table[index].sourceport; + CheckSumAndSend(IPptr, TCPptr, Len); + return; + } + } +} + +static VOID ProcessICMPMsg(PIPMSG IPptr) +{ + int Len; + PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data; + + Len = ntohs(IPptr->IPLENGTH); + Len-=20; + + Check_Checksum(ICMPptr, Len); + + if (ICMPptr->ICMPTYPE == 8) + { + // ICMP_ECHO + + ICMPptr->ICMPTYPE = 0; // Convert to Reply + + // CHECKSUM IT + + ICMPptr->ICMPCHECKSUM = 0; + ICMPptr->ICMPCHECKSUM = Generate_CHECKSUM(ICMPptr, Len); + + // Swap Dest to Origin + + IPptr->IPDEST = IPptr->IPSOURCE; + IPptr->IPSOURCE.addr = OurIPAddr; + IPptr->IPTTL = IPTTL; + +// IPptr->IPCHECKSUM = 0; +// IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20); // RouteIPMsg redoes checksum + + MapRouteIPMsg(IPptr); // Send Back + } + + if (ICMPptr->ICMPTYPE == 0) + { + // ICMP_REPLY: + + // I don't see how Portmapper should be getting ping responses + +/* + UCHAR * BUFFER = GetBuff(); + UCHAR * ptr1; + struct _MESSAGE * Msg; + TRANSPORTENTRY * Session = L4TABLE; + char IP[20]; + unsigned char work[4]; + + Session += ICMPptr->ICMPID; + + if (BUFFER == NULL) + return; + + ptr1 = &BUFFER[7]; + + memcpy(work, &IPptr->IPSOURCE, 4); + sprintf(IP, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]); + + *ptr1++ = 0xf0; // PID + + ptr1 += sprintf(ptr1, "Ping Response from %s", IP); + + *ptr1++ = 0x0d; // CR + + Len = ptr1 - BUFFER; + + Msg = (struct _MESSAGE *)BUFFER; + Msg->LENGTH = Len; + Msg->CHAIN = NULL; + + C_Q_ADD(&Session->L4TX_Q, (UINT *)BUFFER); + + PostDataAvailable(Session); +*/ + return; + } +} + + +static VOID SendICMPMessage(PIPMSG IPptr, int Type, int Code, int P2) +{ + PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data; + UCHAR * ptr; + + if (OurIPAddr == 0) + return; // Can't do much without one + + if (IPptr->IPPROTOCOL == ICMP && ICMPptr->ICMPTYPE == 11) + return; // Don't send Time Exceeded for TimeExceded + + // Copy the Original IP Header and first 8 bytes of packet down the buffer + + ptr = (UCHAR *) ICMPptr; + + memmove(ptr + 8, IPptr, 28); // IP header plus 8 data + +// We swap Souce to Dest, Convert to ICMP 11 and send back first 8 bytes of packet after header + + IPptr->IPDEST = IPptr->IPSOURCE; + IPptr->IPSOURCE.addr = OurIPAddr; + IPptr->IPPROTOCOL = ICMP; + IPptr->IPTTL = IPTTL; + IPptr->FRAGWORD = 0; + IPptr->IPLENGTH = htons(56); // IP Header ICMP Header IP Header 8 Data + + memset (ICMPptr, 0, 8); + ICMPptr->ICMPTYPE = Type; + ICMPptr->ICMPCODE = Code; + ICMPptr->ICMPSEQUENCE = htons(P2); + ICMPptr->ICMPCHECKSUM = Generate_CHECKSUM(ICMPptr, 36); + + MapRouteIPMsg(IPptr); +} + +static VOID MapRouteIPMsg(PIPMSG IPptr) +{ + PARPDATA Arp; + BOOL Found; + + // We rely on the ARP messages generated by either end to route frames. + // If address is not in ARP cache (say call originated by MSYS), send to our default route + + // Decremnent TTL and Recalculate header checksum + + IPptr->IPTTL--; + + if (IPptr->IPTTL == 0) + { + SendICMPTimeExceeded(IPptr); + return; // Should we send time exceeded???? + } + + IPptr->IPCHECKSUM = 0; + IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20); + + // Look up ARP + + Arp = LookupARP(IPptr->IPDEST.addr, FALSE, &Found); + + // If enabled, look in Net44 Encap Routes + + if (!Found && DefaultIPAddr) + Arp = LookupARP(DefaultIPAddr, FALSE, &Found); + + if (!Found) + return; // No route or default + + if (Arp == NULL) + return; // Should we try to ARP it? + + if (Arp->ARPVALID) + { + SendIPtoBPQDEV(IPptr, Arp->HWADDR); + } + + return; +} + +static PROUTEENTRY AllocRouteEntry() +{ + PROUTEENTRY Routeptr; + + if (NumberofRoutes == 0) + + RouteRecords = malloc(sizeof(void *)); + else + RouteRecords = realloc(RouteRecords,(NumberofRoutes + 1) * sizeof(void *)); + + Routeptr = zalloc(sizeof(ROUTEENTRY)); + + if (Routeptr == NULL) return NULL; + + RouteRecords[NumberofRoutes++] = Routeptr; + + return Routeptr; +} + + +static PARPDATA AllocARPEntry() +{ + ARPDATA * ARPptr; + + if (NumberofARPEntries == 0) + + ARPRecords = malloc(sizeof(void *)); + else + ARPRecords = realloc(ARPRecords, (NumberofARPEntries+1)*sizeof(void *)); + + ARPptr = malloc(sizeof(ARPDATA)); + + if (ARPptr == NULL) return NULL; + + memset(ARPptr, 0, sizeof(ARPDATA)); + + ARPRecords[NumberofARPEntries++] = ARPptr; + + return ARPptr; +} + + static VOID SendARPMsg(PARPDATA Arp) + { + // Send ARP. Initially used only to find default gateway + + Arp->ARPTIMER = 5; // Retry periodically + + ETHARPREQMSG.ARPOPCODE = 0x0100; // ; REQUEST + + ETHARPREQMSG.TARGETIPADDR = Arp->IPADDR; + memset(ETHARPREQMSG.TARGETHWADDR, 0, 6); + + ETHARPREQMSG.SENDIPADDR = OurIPAddr; + memcpy(ETHARPREQMSG.SENDHWADDR,ourMACAddr, 6); + + memcpy(ETHARPREQMSG.MSGHDDR.SOURCE, ourMACAddr, 6); + memset(ETHARPREQMSG.MSGHDDR.DEST, 255, 6); + + Send_ETH(ÐARPREQMSG, 42); + + return; + } + +static PROUTEENTRY FindRoute(uint32_t IPADDR) +{ + PROUTEENTRY Route = NULL; + int i; + + for (i = 0; i < NumberofRoutes; i++) + { + Route = RouteRecords[i]; + + if ((IPADDR & Route->SUBNET) == Route->NETWORK) + return Route; + } + return NULL; +} + + + +static PROUTEENTRY LookupRoute(uint32_t IPADDR, uint32_t Mask, BOOL Add, BOOL * Found) +{ + PROUTEENTRY Route = NULL; + int i; + + for (i = 0; i < NumberofRoutes; i++) + { + Route = RouteRecords[i]; + + if (Route->NETWORK == IPADDR && Route->SUBNET == Mask) + { + *Found = TRUE; + return Route; + } + } + + // Not Found + + *Found = FALSE; + + if (Add) + { + Route = AllocRouteEntry(); + return Route; + } + else + return NULL; +} + +static PARPDATA LookupARP(uint32_t IPADDR, BOOL Add, BOOL * Found) +{ + PARPDATA Arp = NULL; + int i; + + for (i=0; i < NumberofARPEntries; i++) + { + Arp = ARPRecords[i]; + + if (Arp->IPADDR == IPADDR) + { + *Found = TRUE; + return Arp; + } + } + + // Not Found + + *Found = FALSE; + + if (Add) + { + Arp = AllocARPEntry(); + return Arp; + } + else + return NULL; +} +static VOID RemoveARP(PARPDATA Arp); + +static VOID RemoveRoute(PROUTEENTRY Route) +{ + int i; + + for (i=0; i < NumberofRoutes; i++) + { + if (Route == RouteRecords[i]) + { + while (i < NumberofRoutes) + { + RouteRecords[i] = RouteRecords[i+1]; + i++; + } + + if (Route->ARP) + { + PARPDATA Arp = Route->ARP; + Route->ARP->ARPROUTE = NULL; // Avoid recursion + RemoveARP(Arp); + } + + free(Route); + NumberofRoutes--; + return; + } + } +} + + +static VOID RemoveARP(PARPDATA Arp) +{ + int i; + + if (Arp->IPADDR == DefaultIPAddr) + { + // Dont remove Default Gateway. Set to re-resolve + + Arp->ARPVALID = FALSE; + Arp->ARPTIMER = 5; + return; + } + + for (i=0; i < NumberofARPEntries; i++) + { + if (Arp == ARPRecords[i]) + { + while (i < NumberofARPEntries) + { + ARPRecords[i] = ARPRecords[i+1]; + i++; + } + + // Remove linked route + + if (Arp->ARPROUTE) + { + PROUTEENTRY Route = Arp->ARPROUTE; + Arp->ARPROUTE->ARP = NULL; // Avoid recursion + RemoveRoute(Route); + } + + free(Arp); + NumberofARPEntries--; + return; + } + } +} + + +static BOOL ReadConfigFile() +{ + +// IPAddr 192.168.0.129 +// IPBroadcast 192.168.0.255 +// IPGateway 192.168.0.1 +// IPPorts 1,4 + +// MAP 192.168.0.100 1001 n9pmo.dyndns.org 1000 + + char * Config; + char * ptr1, * ptr2; + + char buf[256],errbuf[256]; + + map_table_len = 0; // For reread + + Config = PortConfig[PortMapConfigSlot]; // Config from bpq32.cfg + + if (Config) + { + // Using config from bpq32.cfg + + ptr1 = Config; + + ptr2 = strchr(ptr1, 13); + while(ptr2) + { + memcpy(buf, ptr1, ptr2 - ptr1); + buf[ptr2 - ptr1] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + strcpy(errbuf,buf); // save in case of error + + if (!ProcessLine(buf)) + { + WritetoConsoleLocal("PortMapper bad config record "); + strcat(errbuf, "\n"); + WritetoConsoleLocal(errbuf); + } + } + } + return (TRUE); +} + + +static int ProcessLine(char * buf) +{ + char * ptr, * p_value, * p_origport, * p_host, * p_port; + int port, mappedport, ipad; + + ptr = strtok(buf, " \t\n\r"); + p_value = strtok(NULL, " \t\n\r"); + + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if(_stricmp(ptr,"ADAPTER") == 0) + { +#ifndef WIN32 + WritetoConsoleLocal("IPGating to Ethernet is not supported in this build\n"); + return TRUE; +#endif + strcpy(Adapter,p_value); + return (TRUE); + } + + if(_stricmp(ptr,"promiscuous") == 0) + { + Promiscuous = atoi(p_value); + return (TRUE); + } + + if (_stricmp(ptr,"IPAddr") == 0) + { + OurIPAddr = inet_addr(p_value); + + if (OurIPAddr == INADDR_NONE) return (FALSE); + + return (TRUE); + } + if (_stricmp(ptr,"IPBroadcast") == 0) + { + OurIPBroadcast = inet_addr(p_value); + + if (OurIPBroadcast == INADDR_NONE) return (FALSE); + + return (TRUE); + } + + if (_stricmp(ptr,"IPNetMask") == 0) + { + OurNetMask = inet_addr(p_value); + + if (OurNetMask == INADDR_NONE) return (FALSE); + + return (TRUE); + } + + + if (_stricmp(ptr,"IPGateway") == 0) + { + DefaultIPAddr = inet_addr(p_value); + + if (DefaultIPAddr == INADDR_NONE) return (FALSE); + + return (TRUE); + } + +// ARP 44.131.4.18 GM8BPQ-7 1 D + + if (_stricmp(ptr,"MAP") == 0) + { +#ifdef LINBPQ + + WritetoConsoleLocal("MAP not supported in LinBPQ IP Gateway\n"); + return TRUE; +#endif + if (!p_value) return FALSE; + + p_origport = strtok(NULL, " ,\t\n\r"); + if (!p_origport) return FALSE; + + p_host = strtok(NULL, " ,\t\n\r"); + if (!p_host) return FALSE; + + p_port = strtok(NULL, " ,\t\n\r"); + if (!p_port) return FALSE; + + port=atoi(p_origport); + if (port == 0) return FALSE; + + mappedport=atoi(p_port); + if (mappedport == 0) return FALSE; + + ipad = inet_addr(p_value); + + map_table[map_table_len].sourceipaddr = ipad; + strcpy(map_table[map_table_len].hostname, p_host); + map_table[map_table_len].sourceport = ntohs(port); + map_table[map_table_len++].mappedport = ntohs(mappedport); + + NeedResolver = TRUE; + + return (TRUE); + } + + // + // Bad line + // + return (FALSE); + +} + +static VOID DoARPTimer() +{ + PARPDATA Arp = NULL; + int i; + + for (i=0; i < NumberofARPEntries; i++) + { + Arp = ARPRecords[i]; + + if (!Arp->ARPVALID) + { + Arp->ARPTIMER--; + + if (Arp->ARPTIMER == 0) + { + // Retry Request + + SendARPMsg(Arp); + } + continue; + } + + // Time out active entries + + if (Arp->LOCKED == 0) + { + Arp->ARPTIMER--; + + if (Arp->ARPTIMER == 0) + { + // Remove Entry + + RemoveARP(Arp); + SaveARP(); + } + } + } +} + +static VOID DoRouteTimer() +{ + int i; + PROUTEENTRY Route; + + for (i=0; i < NumberofRoutes; i++) + { + Route = RouteRecords[i]; + if (Route->RIPTIMOUT) + Route->RIPTIMOUT--; + } +} + + +// PCAP Support Code + + +#ifdef WIN32 + +static FARPROCX GetAddress(char * Proc) +{ + FARPROCX ProcAddr; + int err=0; + char buf[256]; + int n; + + + ProcAddr=(FARPROCX) GetProcAddress(PcapDriver,Proc); + + if (ProcAddr == 0) + { + err=GetLastError(); + + n=sprintf(buf,"Error finding %s - %d", Proc,err); + WritetoConsoleLocal(buf); + + return(0); + } + + return ProcAddr; +} + + +static void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data); + +static int OpenPCAP() +{ + u_long param=1; + BOOL bcopt=TRUE; + int i=0; + char errbuf[PCAP_ERRBUF_SIZE]; + u_int netmask; + char packet_filter[64]; + struct bpf_program fcode; + char buf[256]; + int n; + + + PcapDriver=LoadLibrary(Dllname); + + if (PcapDriver == NULL) return(FALSE); + + if ((pcap_sendpacketx=GetAddress("pcap_sendpacket")) == 0 ) return FALSE; + + if ((pcap_datalinkx=GetAddress("pcap_datalink")) == 0 ) return FALSE; + + if ((pcap_compilex=GetAddress("pcap_compile")) == 0 ) return FALSE; + + if ((pcap_setfilterx=GetAddress("pcap_setfilter")) == 0 ) return FALSE; + + pcap_open_livex = (pcap_t * (__cdecl *)(const char *, int, int, int, char *)) GetProcAddress(PcapDriver,"pcap_open_live"); + + if (pcap_open_livex == NULL) return FALSE; + + if ((pcap_geterrx=GetAddress("pcap_geterr")) == 0 ) return FALSE; + + if ((pcap_next_exx=GetAddress("pcap_next_ex")) == 0 ) return FALSE; + + /* Open the adapter */ + + adhandle = pcap_open_livex(Adapter, // name of the device + 65536, // portion of the packet to capture. + // 65536 grants that the whole packet will be captured on all the MACs. + Promiscuous, // promiscuous mode (nonzero means promiscuous) + 1, // read timeout + errbuf // error buffer + ); + + if (adhandle == NULL) + return FALSE; + + /* Check the link layer. We support only Ethernet for simplicity. */ + + if(pcap_datalinkx(adhandle) != DLT_EN10MB) + { + n=sprintf(buf,"\nThis program works only on Ethernet networks.\n"); + WritetoConsoleLocal(buf); + + adhandle = 0; + return FALSE; + } + + netmask=0xffffff; + +// sprintf(packet_filter,"ether[12:2]=0x0800 or ether[12:2]=0x0806"); + + sprintf(packet_filter,"ether broadcast or ether dst %02x:%02x:%02x:%02x:%02x:%02x", + ourMACAddr[0], ourMACAddr[1], ourMACAddr[2], + ourMACAddr[3], ourMACAddr[4], ourMACAddr[5]); + + //compile the filter + + if (pcap_compilex(adhandle, &fcode, packet_filter, 1, netmask) <0 ) + { + n=sprintf(buf,"\nUnable to compile the packet filter. Check the syntax.\n"); + WritetoConsoleLocal(buf); + + adhandle = 0; + return FALSE; + } + + //set the filter + + if (pcap_setfilterx(adhandle, &fcode)<0) + { + n=sprintf(buf,"\nError setting the filter.\n"); + WritetoConsoleLocal(buf); + + adhandle = 0; + return FALSE; + } + + return TRUE; +} +#endif + + +int CompareMasks (const VOID * a, const VOID * b); + + +#ifndef LINBPQ + +extern HFONT hFont; +struct tagMSG Msg; +char buf[1024]; + +static LRESULT CALLBACK ResWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + PAINTSTRUCT ps; + HDC hdc; + HFONT hOldFont ; + struct hostent * hostptr; + struct in_addr ipad; + char line[100]; + int index,displayline; + MINMAXINFO * mmi; + + int i=1; + + int nScrollCode,nPos; + + switch (message) + { + case WM_GETMINMAXINFO: + + mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = 400; + mmi->ptMaxSize.y = Windowlength; + mmi->ptMaxTrackSize.x = 400; + mmi->ptMaxTrackSize.y = Windowlength; + break; + + case WM_USER+199: + + i=WSAGETASYNCERROR(lParam); + + map_table[ResolveIndex].error=i; + + if (i ==0) + { + // resolved ok + + hostptr=(struct hostent *)&buf; + memcpy(&map_table[ResolveIndex].mappedipaddr,hostptr->h_addr,4); + } + + InvalidateRect(hWnd,NULL,FALSE); + + while (ResolveIndex < map_table_len) + { + ResolveIndex++; + + WSAAsyncGetHostByName (hWnd,WM_USER+199, + map_table[ResolveIndex].hostname, + buf,MAXGETHOSTSTRUCT); + + break; + } + break; + + case WM_MDIACTIVATE: + { + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (WPARAM)hPopMenu, "Actions"); + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM)hWndMenu); + } + else + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM)hMainFrameMenu, (LPARAM)NULL); + + DrawMenuBar(FrameWnd); + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + } + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + + + if (wmId == BPQREREAD) + { + if (ProcessConfig()) + { + FreeConfig(); + + ReadConfigFile(); + PostMessage(hIPResWnd, WM_TIMER,0,0); + InvalidateRect(hWnd,NULL,TRUE); + } + else + Consoleprintf("Failed to reread config file - leaving config unchanged"); + + return 0; + } +/* + if (wmId == BPQADDARP) + { + if (ConfigWnd == 0) + { + ConfigWnd=CreateDialog(hInstance,ConfigClassName,0,NULL); + + if (!ConfigWnd) + { + i=GetLastError(); + return (FALSE); + } + ShowWindow(ConfigWnd, SW_SHOW); + UpdateWindow(ConfigWnd); + } + + SetForegroundWindow(ConfigWnd); + + return(0); + } + return 0; +*/ + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + case SC_RESTORE: + + IPMinimized = FALSE; + SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); + break; + + case SC_MINIMIZE: + + IPMinimized = TRUE; + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_VSCROLL: + + nScrollCode = (int) LOWORD(wParam); // scroll bar value + nPos = (short int) HIWORD(wParam); // scroll box position + + //hwndScrollBar = (HWND) lParam; // handle of scroll bar + + if (nScrollCode == SB_LINEUP || nScrollCode == SB_PAGEUP) + { + baseline--; + if (baseline <0) + baseline=0; + } + + if (nScrollCode == SB_LINEDOWN || nScrollCode == SB_PAGEDOWN) + { + baseline++; + if (baseline > map_table_len) + baseline = map_table_len; + } + + if (nScrollCode == SB_THUMBTRACK) + { + baseline=nPos; + } + + SetScrollPos(hWnd,SB_VERT,baseline,TRUE); + + InvalidateRect(hWnd,NULL,TRUE); + break; + + + case WM_PAINT: + + hdc = BeginPaint (hWnd, &ps); + + hOldFont = SelectObject( hdc, hFont) ; + + index = baseline; + displayline=0; + + while (index < map_table_len) + { + if (map_table[index].ResolveFlag && map_table[index].error != 0) + { + // resolver error - Display Error Code + sprintf(hostaddr,"Error %d",map_table[index].error); + } + else + { + memcpy(&ipad,&map_table[index].mappedipaddr,4); + strncpy(hostaddr,inet_ntoa(ipad),16); + } + + memcpy(&ipad,&map_table[index].mappedipaddr,4); + + i=sprintf(line,"%.64s = %-.30s", + map_table[index].hostname, + hostaddr); + + TextOut(hdc,0,(displayline++)*14+2,line,i); + + index++; + } + + SelectObject( hdc, hOldFont ) ; + EndPaint (hWnd, &ps); + + break; + + case WM_DESTROY: + + +// PostQuitMessage(0); + + break; + + + case WM_TIMER: + + for (ResolveIndex=0; ResolveIndex < map_table_len; ResolveIndex++) + { + WSAAsyncGetHostByName (hWnd,WM_USER+199, + map_table[ResolveIndex].hostname, + buf,MAXGETHOSTSTRUCT); + break; + } + + default: + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); +} + +static void IPResolveNames( void *dummy ) +{ + SetTimer(hIPResWnd,1,15*60*1000,0); + + PostMessage(hIPResWnd, WM_TIMER,0,0); + + while (GetMessage(&Msg, hIPResWnd, 0, 0)) + { + TranslateMessage(&Msg); + DispatchMessage(&Msg); + } +} + +#endif + +/* +; DO PSEUDO HEADER FIRST +; + MOV DX,600H ; PROTOCOL (REVERSED) + MOV AX,TCPLENGTH ; TCP LENGTH + XCHG AH,AL + ADD DX,AX + MOV AX,WORD PTR LOCALADDR[BX] + ADC DX,AX + MOV AX,WORD PTR LOCALADDR+2[BX] + ADC DX,AX + MOV AX,WORD PTR REMOTEADDR[BX] + ADC DX,AX + MOV AX,WORD PTR REMOTEADDR+2[BX] + ADC DX,AX + ADC DX,0 + + MOV PHSUM,DX + + PUSH BX + + MOV BX,TXBUFFER ; HEADER + + MOV CX,TCPLENGTH ; PUT LENGTH INTO HEADER + MOV BUFFLEN[BX],CX +; + MOV SI,BUFFPTR[BX] + + INC CX ; ROUND UP + SHR CX,1 ; WORD COUNT + + CALL DO_CHECKSUM + + ADD DX,PHSUM + ADC DX,0 + NOT DX + + MOV SI,BUFFPTR[BX] + MOV CHECKSUM[SI],DX + + +*/ diff --git a/.svn/pristine/0f/0f360bb978fa349027d73174ce2a1e0fd2560283.svn-base b/.svn/pristine/0f/0f360bb978fa349027d73174ce2a1e0fd2560283.svn-base index 6711b53..efbd1a5 100644 --- a/.svn/pristine/0f/0f360bb978fa349027d73174ce2a1e0fd2560283.svn-base +++ b/.svn/pristine/0f/0f360bb978fa349027d73174ce2a1e0fd2560283.svn-base @@ -1,964 +1,964 @@ -// -// Common definitons for Pactor-like Modules - -#include "kernelresource.h" - -#include "rigcontrol.h" - -#define MAXBLOCK 4096 - -#define MAXFREQS 20 // RigControl freqs to scan - -extern char HFCTEXT[81]; -extern int HFCTEXTLEN; - -extern HANDLE hInstance; - -extern HMENU hMainFrameMenu; -extern HMENU hWndMenu; - -/* -struct WL2KInfo -{ - struct WL2KInfo * Next; - - char * Host; - short WL2KPort; - - char RMSCall[10]; - char BaseCall[10]; - char GridSquare[7]; - char Times[80]; - char ServiceCode[17]; - - BOOL UseRigCtrlFreqs; - char WL2KFreq[12]; - char WL2KMode; // WL2K reporting mode - char WL2KModeChar; // W or N - BOOL DontReportNarrowOnWideFreqs; - -// char NARROWMODE; -// char WIDEMODE; // Mode numbers to report to WL2K - -// struct WL2KInfo WL2KInfoList[MAXFREQS]; // Freqs for sending to WL2K - - int Freq; - char Bandwidth; -// char * TimeList; // eg 06-10,12-15 - int mode; // see below (an integer) - int baud; // see below (an integer) - int power; // actual power if known, default to 100 for HF, 30 for VHF/UHF (an integer) - int height; // antenna height in feet if known, default to 25 - int gain; // antenna gain if known, default to 0 - int direction; // primary antenna direction in degrees if known, use 000 for omni (an integer) - BOOL RPonPTC; // Set if scanning for Robust Packet on a PTC -}; - -*/ -#pragma pack(1) - -// AGWPE Header Structure - -struct AGWHEADER -{ - UCHAR Port; - UCHAR filler1[3]; - char DataKind; - UCHAR filler2; - unsigned char PID; - UCHAR filler3; - unsigned char callfrom[10]; - unsigned char callto[10]; - int DataLength; - int reserved; -}; - -#pragma pack() - -// Telnet Server User Record - -struct UserRec -{ - char * Callsign; - char * UserName; - char * Password; - char * Appl; // Autoconnect APPL - BOOL Secure; // Authorised User -}; - -struct LOCALNET -{ - struct LOCALNET * Next; - uint32_t Network; - uint32_t Mask; -}; - - -#define MaxCMS 10 // Number of addresses we can keep - currently 4 are used. - -struct TCPINFO -{ - int NumberofUsers; - struct UserRec ** UserRecPtr; - int CurrentConnections; - - struct UserRec RelayUser; - - int CurrentSockets; - - int TCPPort; - int FBBPort[100]; - int RelayPort; - int HTTPPort; - int APIPort; - int TriModePort; - int SyncPort; - int SNMPPort; - int DRATSPort; - int CMDPort[33]; - char RELAYHOST[64]; - char CMSServer[64]; - BOOL FallbacktoRelay; // Use Relsy if can't connect to CMS - - BOOL IPV4; // Allow Connect using IPV4 - BOOL IPV6; // Allow Connect using IPV6 - BOOL CMS; // Allow Connect to CMS - BOOL CMSOK; // Internet link is ok. - BOOL UseCachedCMSAddrs; - struct in_addr CMSAddr[MaxCMS]; - BOOL CMSFailed[MaxCMS]; // Set if connect to CMS failed. - char * CMSName[MaxCMS]; // Reverse DNS Name of Server - int NumberofCMSAddrs; - int NextCMSAddr; // Round Robin Pointer - int CheckCMSTimer; // CMS Poll Timer - - char SecureCMSPassword[80]; // For Secure CMS Signin - char GatewayCall[10]; // Call for CMS access - char GatewayLoc[10]; // Loc - Needed to report Hybrid Mode - int ReportHybrid; // Report as Hybrod Station - char * HybridServiceCode; - char * HybridFrequencies; - char * HybridCoLocatedRMS; - - BOOL DisconnectOnClose; - - char PasswordMsg[100]; - - char cfgHOSTPROMPT[100]; - - char cfgCTEXT[300]; - - char cfgLOCALECHO[100]; - - int MaxSessions; - - char LoginMsg[100]; - - char RelayAPPL[20]; - char SyncAPPL[20]; - - SOCKET TCPSock; - SOCKET FBBsock[100]; - SOCKET Relaysock; - SOCKET HTTPsock; - SOCKET APIsock; - SOCKET TriModeSock; - SOCKET TriModeDataSock; - SOCKET Syncsock; - SOCKET DRATSsock; - SOCKET SNMPsock; - - struct ConnectionInfo * TriModeControlSession; - SOCKET sock6; - SOCKET FBBsock6[100]; - SOCKET Relaysock6; - SOCKET HTTPsock6; - SOCKET APIsock6; - SOCKET Syncsock6; - SOCKET DRATSsock6; - - fd_set ListenSet; - SOCKET maxsock; - - HMENU hActionMenu; - HMENU hLogMenu; - HMENU hDisMenu; // Disconnect Menu Handle - HWND hCMSWnd; - - int SecureTelnet; - int ReportRelayTraffic; // Send WL2K Reports for Relay Traffic - - char * WebTermCSS; // css override for web terminal - struct LOCALNET * LocalNets; - -}; - - -struct STREAMINFO -{ -// TRANSPORTENTRY * AttachedSession; - - void * PACTORtoBPQ_Q; // Frames for BPQ - void * BPQtoPACTOR_Q; // Frames for PACTOR - int FramesOutstanding; // Frames Queued - used for flow control - int FramesQueued; // Frames Queued - used for flow control - BOOL InternalCmd; // Last Command was generated internally - int IntCmdDelay; // To limit internal commands - BOOL CheckingCall; // Set on PTC if waiting for I response after a Connect RXed - - BOOL Attached; // Set what attached to a BPQ32 stream - BOOL Connected; // When set, all data is passed as data instead of commands - BOOL Connecting; // Set when Outward Connect in progress - BOOL Disconnecting; // Set when disconnect in progress - // Used when appplication disconnects the bpq session, and - // prevents new attaches while a dirty disconnect is in progress - int DisconnectingTimeout; // A hard disconnect occurs if this expires before the disconnect complete - int ReportDISC; // Need to report an incoming DISC to kernel - BOOL DiscWhenAllSent; // Close session when all msgs have been sent to node - BOOL ARQENDSent; // Set when V4 ARQEND Sent - - int DEDStream; // Stream number for DED interface (same as index except for pactor) - - char MyCall[10] ; // Call we are using - char RemoteCall[10]; // Callsign - - char callingCall[10]; // for reporting. Link and Our calls depand on which end connected - char receivingCall[10]; // for reporting. Link and Our calls depand on which end connected - char Direction[4]; // In or Out - - - - char AGWKey[21]; // Session Key for AGW Session Based Drivers - - time_t ConnectTime; // Time connection made - time_t AttachTime; - - int bytesTXed; - int BytesAcked; - int bytesRXed; - int PacketsSent; - int BytesResent; - int BytesOutstanding; // For Packet Channels - - UCHAR PTCStatus0; // Status Bytes - UCHAR PTCStatus1; // Status Bytes - UCHAR PTCStatus2; // Status Bytes - UCHAR PTCStatus3; // Status Bytes - - char * CmdSet; // A series of commands to send to the TNC - char * CmdSave; // Base address for free - - struct ConnectionInfo * ConnectionInfo; // TCP Server Connection Info - - int TimeInRX; // Too long in send mode timer - int NeedDisc; // Timer to send DISC if appl not available - - BOOL NoCMSFallback; // Dont use relay if CMS not available - struct ARQINFO * ARQInfo; // FLDIGI/FLARQ Stream Mode Specific Data - - HWND xIDC_MYCALL; - HWND xIDC_DESTCALL; - HWND xIDC_STATUS; - HWND xIDC_SEND; - HWND xIDC_RXED; - HWND xIDC_RESENT; - HWND xIDC_ACKED; - HWND xIDC_DIRN; - - int RelaySyncStream; - int VaraACMode; -}; - -typedef struct AGWINFO -{ - // Fields for AGW Session based Ports (eg UZ7HO Modem) - - struct AGWHEADER TXHeader; - struct AGWHEADER RXHeader; - int MaxSessions; - int ConnTimeOut; - int PollDelay; - time_t LastParamTime; - -#ifdef WIN32 - - // For selecting UZ7HO Mode and Freq - - COMBOBOXINFO cbinfo; // UZ7HO Modem Combo Box info - HWND hFreq; // UZ7HO Frequency Box - HWND hSpin; // UZ7HO Spin Button - -#endif - - int isQTSM; // Flag to Identify QtSM - - int CenterFreq; - int Modem; // Modem number in list - char ModemName[20]; - unsigned char Version[4]; - unsigned char fx25Flags; - unsigned char il2pFlags; - unsigned char il2pcrc; - -} *PAGWINFO; - -typedef struct ARQINFO -{ - // Fields for FLDIGI/FLARQ Ports - - // Max window is 64, though often will use less - - char OurStream; - char FarStream; - - PMSGWITHLEN TXHOLDQ[64]; // Frames waiting ACK - PMSGWITHLEN RXHOLDQ[64]; // Frames waiting missing frames. - - int TXWindow; - int RXWindow; - int MaxBlock; // Max sending block size - - int TXSeq; - int TXLastACK; // Last frame ACK'ed - - int RXHighest; - int RXNext; - int RXNoGaps; - - int Retries; - int NoAckRetries; // Status received but no data acked - int ARQTimer; - int ARQState; - -#define ARQ_ACTIVE 1 // Have a session of some type - - int ARQTimerState; - -#define ARQ_CONNECTING 1 -#define ARQ_CONNECTACK 2 -#define ARQ_DISC 3 -#define ARQ_WAITACK 4 -#define ARQ_WAITDATA 5 // Waiting for more data before polling - - char LastMsg[80]; // Last message sent that expects an ack - int LastLen; - char TXMsg[80]; // Message to aend after TXDELAY - int TXLen; - int TurnroundTimer; // RX to TX delay. - int TXDelay; - -} *ARQINFO; - -typedef struct FLINFO -{ - // Fields for MPSK Session Ports ) - - BOOL TX; // Set when FLDigi is transmitting - char DefaultMode[64]; // Mode to return to after session - int DefaultFreq; // Freq to return to after session - BOOL Beacon; // Use ALE Beacons - char LastXML[128]; // Last XML Request Sent - int XMLControl; // Controlls polling FLDigi by XML - int CmdControl; // Controlls polling FLDigi by KISS Command - BOOL FLARQ; // Connection from FLARQ - BOOL Busy; - BOOL CONOK; // Allow incoming connects - BOOL KISSMODE; // Using KISS instead of socket interface - BOOL RAW; // Raw (ARQ Socket or KISS RAW, depening on above) - int CenterFreq; - char CurrentMode[20]; // Mode to return to after session - int Responding; // If FLDigi is responding to conmands - BOOL MCASTMODE; // If port is in MCAST RX MOde - -} *FLINFO; - -typedef struct MPSKINFO -{ - // Fields for MPSK Session Ports ) - - int ConnTimeOut; - BOOL TX; // Set when Multipsk is transmitting - char DefaultMode[20]; // Mode to return to after session - BOOL Beacon; // Use ALE Beacons - int MaxSessions; -} *MPSKINFO; - -struct FreeDataINFO -{ - int startingTNC; - int TNCRunning; - int Conecting; - int Connected; - char ourCall[10]; - char toCall[10]; - char farCall[10]; // TNC Call - char useBaseCall; // Use base call (without ssid) for TNC Call - char * Capture; // Capture Device Name - char * Playback; // Playback Device Name - char * hamlibHost; - int hamlibPort; - - unsigned char toSendData[8192]; // Buffer data from node for more efficiency - int toSendCount; - int toSendTimeout; - unsigned char toSendMsg[256]; // Buffer data from node for more efficiency - int toSendMsgCount; - int toSendMsgTimeout; - char * RXDir; // Directory for Received Files - int CONOK; // Virtual Lisren Flag - int Chat; // In Chat Mode - char ChatCall[10]; - int needPoll; // Set if get data needed - int arqstate; // 1 = Disc / 2 - connecting 3 - connected - int TuningRange; // Must be 50, 100, 150, 200, 250 - int LimitBandWidth; - int TXLevel; - int Explorer; // Enable reporting to Freedata Explorer - char SSIDList[256]; - char * SSIDS[16]; -}; - -struct sixPackInfo; - -typedef struct TNCINFO -{ - HWND hDlg; // Status Window Handle - int (FAR * WebWindowProc)(struct TNCINFO * TNC, char * Buff, BOOL LOCAL); // Routine to build web status window - int WebWinX; - int WebWinY; // Size of window - char * WebBuffer; // Buffer for logs - int RigControlRow; // Rig Control Line in Dialog - struct _EXTPORTDATA * PortRecord; // BPQ32 port record for this port - struct RIGINFO * RIG; // Pointer to Rig Control RIG record for RX or Both - struct RIGINFO * TXRIG; // Pointer to Rig Control RIG record for TX - char * InitScript; // Initialisation Commands - int InitScriptLen; // Length - time_t SessionTimeLimit; // Optional limit to total session time - time_t DefaultSessionTimeLimit; // Configured value - - time_t AttachTimeLimit; // to trap port left attached for a long time without other activity - time_t AttachTime; - - int Hardware; // Hardware Type - -#define H_WINMOR 1 -#define H_SCS 2 -#define H_KAM 3 -#define H_AEA 4 -#define H_HAL 5 -#define H_TELNET 6 -#define H_TRK 7 -#define H_TRKM 7 -#define H_V4 8 -#define H_UZ7HO 9 -#define H_MPSK 10 -#define H_FLDIGI 11 -#define H_UIARQ 12 -#define H_ARDOP 13 -#define H_VARA 14 -#define H_SERIAL 15 -#define H_KISSHF 16 -#define H_WINRPR 17 -#define H_HSMODEM 18 -#define H_FREEDATA 19 -#define H_SIXPACK 20 - - - int Port; // BPQ Port Number - - struct RIGINFO DummyRig; // Used if not using Rigcontrol - - BOOL Minimized; // Start Minimized flag - - void * WINMORtoBPQ_Q; // Frames for BPQ, indexed by BPQ Port - void * BPQtoWINMOR_Q; // Frames for WINMOR. indexed by WINMOR port. Only used it TCP session is blocked - - SOCKET TCPSock; // Control Socket - SOCKET TCPDataSock; // Data Socket - SOCKET PacketSock; // Packet Over TCP (ARDOP) - - char * WINMORSignon; // Pointer to message for secure signin - char * HostName; // WINMOR Host - may be dotted decimal or DNS Name - int TCPPort; // - int PacketPort; // Packet Over TCP (ARDOP) - char * ApplCmd; // Application to connect to on incoming connect (null = leave at command handler) - BOOL SwallowSignon; // Set to suppress *** connected to APPL - - union - { - UCHAR TCPBuffer[1000]; // For converting byte stream to messages - UCHAR DEDBuffer[1000]; // For converting byte stream to messages - UCHAR KISSBuffer[1000]; // For KISS over Host Mode - }; - - UCHAR * ARDOPBuffer; // Needs to be pretty big, so Malloc - UCHAR * ARDOPDataBuffer; // Needs to be pretty big, so Malloc - - int InputLen; // Data we have already = Offset of end of an incomplete packet; - int DataInputLen; // Data we have already = Offset of end of an incomplete packet; - int KISSInputLen; // Data we have already = Offset of end of an incomplete packet; - int ESCFLAG; // KISS Escape received - - int MSGCOUNT; // DED WORKING FIELD - int MSGLENGTH; // DED Msg Len - int MSGCHANNEL; // DED Msg Channel Number - int MSGTYPE; // DED Msg Type - - int HOSTSTATE; // ded HOST state machine - - - BOOL StartSent; // Codec Start send (so will get a disconnect) - int ConnectPending; // Set if Connect Pending Received. If so, mustn't allow freq change. - BOOL GavePermission; // Set if we allowed freq change - BOOL DiscPending; // Set if Disconnect Pending Received. So we can time out stuck in Disconnecting - BOOL HadConnect; // Flag to say have been in session - BOOL FECMode; // In FEC Mode - BOOL FEC1600; // Use 1600 Hz FEC Mode - int FECIDTimer; // Time in FEC Mode. Used to trigger ID broadcasts - BOOL RestartAfterFailure; - BOOL StartInRobust; // For WINMOR, set to Robust Mode for first few packets - - int Busy; // Channel Busy Timer/Counter . Non-zero = Busy - - int BusyFlags; // Channel Busy Flags - -#define CDBusy 1 // For WINMOR - reported busy (set till reported clear) -#define PTTBusy 2 // PTT Active - - BOOL FECPending; // Need an FEC Send when channel is next idle - - time_t lasttime; - - BOOL CONNECTING; // TCP Session Flags - BOOL CONNECTED; - BOOL Alerted; // Connect Failed Prompt sent - BOOL DATACONNECTING; - BOOL DATACONNECTED; - - BOOL TNCCONNECTING; // For FreeData - BOOL TNCCONNECTED; - - BOOL QtSMConnected; - - char NodeCall[10]; // Call we listen for (PORTCALL or NODECALL - char CurrentMYC[10]; // Save current call so we don't change it unnecessarily - char * LISTENCALLS; // Calls TNC will respond to (currently only for VARA) - - char TargetCall[10]; // Call incoming connect is addressed to (for appl call support) - - struct sockaddr_in destaddr; - struct sockaddr_in Datadestaddr; - - int PTTMode; // PTT Mode Flags - int PTTState; // Current State - uint64_t PTTActivemS; // For Stats - uint64_t PTTonTime; // - - uint64_t BusyActivemS; // For channel busy stats - uint64_t BusyonTime; - - char PTTOn[60]; // Port override of RIGCONTROL config - char PTTOff[60]; - int PTTOnLen; - int PTTOffLen; - - int TXRadio; // Rigcontrol Radio Number for TX - int RXRadio; // Rigcontrol Radio Number for RX - - long long int TXFreq; // Freq to set on tx before ptt - double ActiveTXFreq; // Freq to set on tx after attach - double ActiveRXFreq; // Freq to set on rx after attach - - double DefaultTXFreq; // Freq to set on tx after close - double DefaultRXFreq; // Freq to set on rx after close - - char ** DisconnectScript; // May replace above 2 params - - int TXOffset; // Correction to TXFreq - - int PID; // Process ID for Software TNC - HWND hWnd; // Main window handle for Software TNC - - char * CaptureDevices; - char * PlaybackDevices; - char * ProgramPath; - BOOL WeStartedTNC; - - int Restarts; // TNC Kill/Restarts done - time_t LastRestart; - - int TimeSinceLast; // Time since last message from TNC (10ths of a sec) - int HeartBeat; - -// int Interlock; // Port Interlock Group - - HWND hMonitor; // Handle to Monitor control -// HMENU hPopMenu; // Actions Menu Handle - - int MaxConReq; // For ARDOP - int BusyHold; // Hold Time from SCS reporting channel free till we call - int BusyWait; // Time to wait for clear channel before connect - - BOOL OverrideBusy; - int BusyDelay; // Timer for busy timeout - int AutoStartDelay; // Time to wait for TNC to start - char * ConnectCmd; // Saved command if waiting for busy to clear - BOOL UseAPPLCalls; // Robust Packet to use Applcalls - BOOL UseAPPLCallsforPactor; // Pactor to use Applcalls - - // Fields for reporting to WL2K Map - - struct WL2KInfo * WL2K; - -/* - char * Host; - short WL2KPort; - - int UpdateWL2KTimer; - BOOL UpdateWL2K; - char RMSCall[10]; - char BaseCall[10]; - char GridSquare[7]; - char Comment[80]; - char ServiceCode[17]; - - BOOL UseRigCtrlFreqs; - char WL2KFreq[12]; - char WL2KModeChar; // W or N - BOOL DontReportNarrowOnWideFreqs; - -// char NARROWMODE; -// char WIDEMODE; // Mode numbers to report to WL2K - - struct WL2KInfo WL2KInfoList[MAXFREQS]; // Freqs for sending to WL2K -*/ - char WL2KMode; // WL2K reporting mode - - struct STREAMINFO Streams[27]; // 0 is Pactor 1 - 10 are ax.25. - int LastStream; // Last one polled for status or send - - void * BPQtoRadio_Q; // Frames to Rig Interface - void * RadiotoBPQ_Q; // Frames from Rig Interface - - void * KISSTX_Q; // Frames to Host Mode KISS interface - struct PORTCONTROL * VirtualPORT; // Pointer to Virtual Packet Port of Host Mode KISS - - char * InitPtr; // Next Command - int ReinitState; // Reinit State Machine - int ReinitCount; // Count for DED Recovery - int TermReinitCount; // Count for DED Term Mode Recovery - BOOL TNCOK; // TNC is reponding - int FramesOutstanding; // Frames Queued - used for flow control - BOOL InternalCmd; // Last Command was generated internally - int IntCmdDelay; // To limit internal commands - - - HANDLE hDevice; - int ReopenTimer; // Used to reopen device if failed (eg USB port removed) - BOOL HostMode; // Set if in DED Host Mode -// BOOL CRCMode; // Set if using SCS Extended DED Mode (JHOST4) - BOOL UsingTermMode; // Set if tnc should be left in term mode - int Timeout; // Timeout response counter - int Retries; - int Window; // Window Size for ARQ - UCHAR TXBuffer[500]; // Last message sent - saved for Retry - int TXLen; // Len of last sent - UCHAR RXBuffer[520]; // Message being received - may not arrive all at once - UINT RXLen; // Data in RXBUffer - UCHAR Toggle; // Sequence bit - int Buffers; // Free buffers in TNC - BOOL WantToChangeFreq; // Request from Scanner to Change - int OKToChangeFreq; // 1 = SCS Says OK to change, -1 = Dont Change zero = still waiting - BOOL DontWantToChangeFreq; // Change done - ok to SCS - BOOL DontReleasePermission; // Hold Permission to prevent calls on this frequency - time_t TimeEnteredSYNCMode; // To detect scan lock when using applcalls on PTC - BOOL SyncSupported; // TNC reports sync - time_t TimeScanLocked; // ditto for TNCs that don't report SYNC - int PTCStatus; // Sync, Idle, Traffic, etc - UCHAR NexttoPoll[20]; // Streams with data outstanding (from General Poll) - BOOL PollSent; // Toggle to ensure we issue a general poll regularly - int StreamtoPoll; - - char Bandwidth; // Currently set Mode W or N - - int Mode; // Mode Flag - - BOOL Dragon; // Set if P4Dragon - BOOL DragonSingle; // Set if P4Dragon using Pactor and Packet on same port - BOOL DragonKISS; // Set if P4Dragon supports sending KISS frames in Hostmode - BOOL EnterExit; // Switching to Term mode to change bandwidth - int PktStream; // Stream in use for Packet when in single port mode - BOOL MaxLevel; // Pactor Level to set for Wide Mode (3 or 4) - int MinLevel; // Mimimum accepted Pactor Level - int MinLevelTimer; // Time left to achieve Min Level - int PacketChannels; - int RobustTime; // For PTC, Spend this part of scan cycle (in 10th secs) in Robust Packet Mode - int SwitchToPactor; // Countdown to switch - - BOOL OldMode; // Use PACTOR instead of TOR (for old software) - BOOL VeryOldMode; // Use MYCALL instead of MYPTCALL (for old software) - - int Mem1; // Free Bytes (VHF /HF) - int Mem2; - - BOOL HFPacket; // Set if HF port is in Packet mode instead of Pactor Mode - BOOL Robust; // Set if SCS Tracker is in Robust Packet mode or WINMOR TNC is in Robust Mode - BOOL RobustDefault; // Set if SCS Tracker default is Robust Packet mode - BOOL ForceRobust; // Don't allow Normal Packet even if scan requests it. - BOOL TeensyRPR; // Teensy RPR TNC - don't send %R - char NormSpeed[8]; // Speed Param for Normal Packet on Tracker - char RobustSpeed[8]; // Speed Param for Robust Packet on Tracker - BOOL RPBEACON; // Send Beacon after each session - - int TimeInRX; // Time waiting for ISS before sending - char TXRXState; // Current ISS/IRS State - - BOOL NeedPACTOR; // Set if need to send PACTOR to put into Standby Mode - int CmdStream; // Stream last command was issued on - - union - { - struct TCPINFO * TCPInfo; // Telnet Server Specific Data - struct AGWINFO * AGWInfo; // AGW Stream Mode Specific Data - struct MPSKINFO * MPSKInfo; // MPSK Stream Mode Specific Data - struct FLINFO * FLInfo; // FLDIGI Stream Mode Specific Data - }; - - struct ARQINFO * ARQInfo; // FLDIGI/FLARQ Stream Mode Specific Data - - BOOL DataBusy; // Waiting for Data Ack - Don't send any more data - BOOL CommandBusy; // Waiting for Command ACK - - BOOL TEXTMODE; // Set if AEA in text mode - BOOL NeedTurnRound; // Set if we have sent data, so need to send ctrl/z - BOOL NeedTRANS; // Set if we have to send TRANS when ctrl/z is acked. - - char * CmdSet; // A series of commands to send to the TNC - char * CmdSave; // Base address for free - - BOOL PktUpdateMap; // Set if Packet MH data to be sent to NodeMap - - int DefaultMode; - int CurrentMode; // Used on HAL - - char * DefaultRadioCmd; // RADIO command to send at end of session - char * Frequency; - // For Mode Map if no Rigcontrol - // Mode Equates - - #define Clover 'C' - #define Pactor 'P' - #define AMTOR 'A' - - UCHAR DataBuffer[500]; // Data Chars split from received stream - UCHAR CmdBuffer[500]; // Cmd/Response chars split from received stream - int DataLen; // Data in DataBuffer - int CmdLen; // Data in CmdBuffer - BOOL CmdEsc; // Set if last char rxed was 0x80 - BOOL DataEsc; // Set if last char rxed was 0x81 - int PollDelay; // Don't poll too often; - int InData; // FLDigi - MCAST <....> received - int InPacket; // FLDigi - SOH or < received. - int MCASTLen; // Data still to get - - int DataMode; // How to treat data - -#define RXDATA 0x30 // Switch to Receive Data characters -#define TXDATA 0x31 // Switch to Transmit Data characters -#define SECDATA 0x32 // Switch to RX data from secondary port - - int TXMode; // Where to send data - -#define TXMODEM 0x33 // Send TX data to modem -#define TXSEC 0x34 // Send TX data to secondary port - - BOOL XONXOFF; // Set if hardware is using XON/XOFF - - double LastFreq; // Used by V4 to see if freq has changed - int ModemCentre; // Modem centre frequency - int ClientHeight; - int ClientWidth; - HWND xIDC_TNCSTATE; - HWND xIDC_COMMSSTATE; - HWND xIDC_MODE; - HWND xIDC_LEDS; - HWND xIDC_TRAFFIC; - HWND xIDC_BUFFERS; - HWND xIDC_CHANSTATE; - HWND xIDC_LEVELS; - HWND xIDC_STATE; - HWND xIDC_TXRX; - HWND xIDC_PROTOSTATE; - HWND xIDC_RESTARTTIME; - HWND xIDC_RESTARTS; - HWND xIDC_PACTORLEVEL; - HWND xIDC_TXTUNE; - HWND xIDC_TXTUNEVAL; - - char * WEB_TNCSTATE; - char * WEB_COMMSSTATE; - char * WEB_MODE; - char * WEB_LEDS; - char * WEB_TRAFFIC; - char * WEB_BUFFERS; - char * WEB_CHANSTATE; - char * WEB_STATE; - char * WEB_TXRX; - char * WEB_PROTOSTATE; - char * WEB_RESTARTTIME; - char * WEB_RESTARTS; - char * WEB_PACTORLEVEL; - char * WEB_LEVELS; - int WEB_CHANGED; // Used to speed up refresh when active - - HMENU hMenu; - HMENU hWndMenu; - - VOID (* SuspendPortProc) (struct TNCINFO * TNC, struct TNCINFO * ThisTNC); - VOID (* ReleasePortProc) (struct TNCINFO * TNC); - VOID (* ForcedCloseProc) (struct TNCINFO * TNC, int Stream); - - time_t WinmorRestartCodecTimer; - int WinmorCurrentMode; - char ARDOPCurrentMode[10]; - char ARDOPCommsMode; - char * ARDOPSerialPort; // Have Bus/Device for I2C - int ARDOPSerialSpeed; - BOOL TCPCONNECTED; // ARDOP over TCP Connected - int SlowTimer; - int ARQPorts[32]; // For ARQ over KISS - char * LogPath; - FILE * LogHandle; // Ardop Logging File - FILE * DebugHandle; // Ardop Debug File - char LastLogType; // For split packets - - UCHAR * ARDOPAPRS; // Used to reconstruct APRS datagram from FEC packets - int ARDOPAPRSLen; - - BOOL WRITELOG; // Enable debug logging - int InputLevelMin; // Sound card levels - int InputLevelMax; // Sound card levels - - int DiscardNextOK; // Used by VARA to suppress OK response to LISTEN commands - int SeenCancelPending; // Used by VARA to suppress duplicate cancel pendings - - MESSAGE * Monframe; // Used by DED Host for receiving Packet Monitor Frame - // split over 2 packets - - struct HSMODEMINFO * HSModemInfo; - struct FreeDataINFO * FreeDataInfo; - - int DontRestart; // Don't automatically restart failed TNC - int SendTandRtoRelay; // Send T and R suffix messages to RELAY instead of CMS - - double SNR; // S/N Ratio (VARA) - - int NetRomMode; - unsigned char * NetRomTxBuffer; // For Netrom over VARA - int NetRomTxLen; - char * NRNeighbour; - int NRCloseTimer; - struct _LINKTABLE * DummyLink; // Simulated link to simplify interface to ax,25 netrom code - struct sixPackPortInfo * sixPack; - int VaraACAllowed; // Set by config - int VaraACMode; // Set by first message received - int VaraModeSet; // Have decicded if VarAC mode or not - char * VARACMsg; // to build message from packets - int VarACTimer; // delayed send timer - size_t VARACSize; // malloc'ed size - -} *PTNCINFO; - -VOID * zalloc(int len); - -BOOL ReadConfigFile(int Port, int ProcLine(char * buf, int Port)); -int GetLine(char * buf); -BOOL CreatePactorWindow(struct TNCINFO * TNC, char * ClassName, char * WindowTitle, int RigControlRow, WNDPROC WndProc, - int Width, int Height, VOID ForcedCloseProc(struct TNCINFO * TNC, int Stream)); -char * CheckAppl(struct TNCINFO * TNC, char * Appl); -BOOL SendReporttoWL2K(struct TNCINFO * TNC); -struct WL2KInfo * DecodeWL2KReportLine(char * buf); -VOID UpdateMH(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction); -VOID UpdateMHEx(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction, char * LOC, BOOL Report); -VOID SaveWindowPos(int port); -VOID SaveMDIWindowPos(HWND hWnd, char * RegKey, char * Value, BOOL Minimized); -BOOL ProcessIncommingConnect(struct TNCINFO * TNC, char * Call, int Stream, BOOL SENDCTEXT); -BOOL ProcessIncommingConnectEx(struct TNCINFO * TNC, char * Call, int Stream, BOOL SENDCTEXT, BOOL AllowTR); -VOID ShowTraffic(struct TNCINFO * TNC); -int OpenCOMMPort(struct TNCINFO * conn, char * Port, int Speed, BOOL Quiet); -VOID SendMH(struct TNCINFO * TNC, char * call, char * freq, char * LOC, char * Mode); -VOID MoveWindows(struct TNCINFO * TNC); - -static VOID TidyClose(struct TNCINFO * TNC, int Stream); -static VOID ForcedClose(struct TNCINFO * TNC, int Stream); -static VOID CloseComplete(struct TNCINFO * TNC, int Stream); - -VOID CheckForDetach(struct TNCINFO * TNC, int Stream, struct STREAMINFO * STREAM, - VOID TidyCloseProc(struct TNCINFO * TNC, int Stream), VOID ForcedCloseProc(struct TNCINFO * TNC, int Stream), - VOID CloseComplete(struct TNCINFO * TNC, int Stream)); - - -BOOL InterlockedCheckBusy(struct TNCINFO * ThisTNC); - -extern UINT CRCTAB; -int BPQTRACE(MESSAGE * Msg, BOOL TOAPRS); - - -static int ProcessLine(char * buf, int Port); -VOID __cdecl Debugprintf(const char * format, ...); -VOID __cdecl Consoleprintf(const char * format, ...); - -extern BOOL MinimizetoTray; - -int standardParams(struct TNCINFO * TNC, char * buf); -void DecodePTTString(struct TNCINFO * TNC, char * ptr); - -int Rig_Command(TRANSPORTENTRY * Session, char * Command); - -BOOL Rig_Poll(); - -VOID Rig_PTT(struct TNCINFO * TNC, BOOL PTTState); -VOID Rig_PTTEx(struct RIGINFO * RIG, BOOL PTTState, struct TNCINFO * TNC); - -struct RIGINFO * Rig_GETPTTREC(int Port); - -struct ScanEntry ** CheckTimeBands(struct RIGINFO * RIG); - -#ifndef LINBPQ -LRESULT CALLBACK PacWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); -#endif - -#define Report_P1 11 -#define Report_P12 12 -#define Report_P123 13 -#define Report_P2 14 -#define Report_P23 15 -#define Report_P3 16 - -#define Report_P1234 17 -#define Report_P234 18 -#define Report_P34 19 -#define Report_P4 20 - -#define Report_WINMOR500 21 -#define Report_WINMOR1600 22 - -#define Report_Robust 30 - -#define IOCTL_SERIAL_IS_COM_OPEN CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) -#define IOCTL_SERIAL_GETDATA CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x801,METHOD_BUFFERED,FILE_ANY_ACCESS) -#define IOCTL_SERIAL_SETDATA CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x802,METHOD_BUFFERED,FILE_ANY_ACCESS) - -#define IOCTL_SERIAL_SET_CTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x803,METHOD_BUFFERED,FILE_ANY_ACCESS) -#define IOCTL_SERIAL_SET_DSR CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x804,METHOD_BUFFERED,FILE_ANY_ACCESS) -#define IOCTL_SERIAL_SET_DCD CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x805,METHOD_BUFFERED,FILE_ANY_ACCESS) - -#define IOCTL_SERIAL_CLR_CTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x806,METHOD_BUFFERED,FILE_ANY_ACCESS) -#define IOCTL_SERIAL_CLR_DSR CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x807,METHOD_BUFFERED,FILE_ANY_ACCESS) -#define IOCTL_SERIAL_CLR_DCD CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x808,METHOD_BUFFERED,FILE_ANY_ACCESS) - -#define IOCTL_BPQ_ADD_DEVICE CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x809,METHOD_BUFFERED,FILE_ANY_ACCESS) -#define IOCTL_BPQ_DELETE_DEVICE CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x80a,METHOD_BUFFERED,FILE_ANY_ACCESS) - -#define W98_SERIAL_GETDATA 0x801 -#define W98_SERIAL_SETDATA 0x802 +// +// Common definitons for Pactor-like Modules + +#include "kernelresource.h" + +#include "rigcontrol.h" + +#define MAXBLOCK 4096 + +#define MAXFREQS 20 // RigControl freqs to scan + +extern char HFCTEXT[81]; +extern int HFCTEXTLEN; + +extern HANDLE hInstance; + +extern HMENU hMainFrameMenu; +extern HMENU hWndMenu; + +/* +struct WL2KInfo +{ + struct WL2KInfo * Next; + + char * Host; + short WL2KPort; + + char RMSCall[10]; + char BaseCall[10]; + char GridSquare[7]; + char Times[80]; + char ServiceCode[17]; + + BOOL UseRigCtrlFreqs; + char WL2KFreq[12]; + char WL2KMode; // WL2K reporting mode + char WL2KModeChar; // W or N + BOOL DontReportNarrowOnWideFreqs; + +// char NARROWMODE; +// char WIDEMODE; // Mode numbers to report to WL2K + +// struct WL2KInfo WL2KInfoList[MAXFREQS]; // Freqs for sending to WL2K + + int Freq; + char Bandwidth; +// char * TimeList; // eg 06-10,12-15 + int mode; // see below (an integer) + int baud; // see below (an integer) + int power; // actual power if known, default to 100 for HF, 30 for VHF/UHF (an integer) + int height; // antenna height in feet if known, default to 25 + int gain; // antenna gain if known, default to 0 + int direction; // primary antenna direction in degrees if known, use 000 for omni (an integer) + BOOL RPonPTC; // Set if scanning for Robust Packet on a PTC +}; + +*/ +#pragma pack(1) + +// AGWPE Header Structure + +struct AGWHEADER +{ + UCHAR Port; + UCHAR filler1[3]; + char DataKind; + UCHAR filler2; + unsigned char PID; + UCHAR filler3; + unsigned char callfrom[10]; + unsigned char callto[10]; + int DataLength; + int reserved; +}; + +#pragma pack() + +// Telnet Server User Record + +struct UserRec +{ + char * Callsign; + char * UserName; + char * Password; + char * Appl; // Autoconnect APPL + BOOL Secure; // Authorised User +}; + +struct LOCALNET +{ + struct LOCALNET * Next; + uint32_t Network; + uint32_t Mask; +}; + + +#define MaxCMS 10 // Number of addresses we can keep - currently 4 are used. + +struct TCPINFO +{ + int NumberofUsers; + struct UserRec ** UserRecPtr; + int CurrentConnections; + + struct UserRec RelayUser; + + int CurrentSockets; + + int TCPPort; + int FBBPort[100]; + int RelayPort; + int HTTPPort; + int APIPort; + int TriModePort; + int SyncPort; + int SNMPPort; + int DRATSPort; + int CMDPort[33]; + char RELAYHOST[64]; + char CMSServer[64]; + BOOL FallbacktoRelay; // Use Relsy if can't connect to CMS + + BOOL IPV4; // Allow Connect using IPV4 + BOOL IPV6; // Allow Connect using IPV6 + BOOL CMS; // Allow Connect to CMS + BOOL CMSOK; // Internet link is ok. + BOOL UseCachedCMSAddrs; + struct in_addr CMSAddr[MaxCMS]; + BOOL CMSFailed[MaxCMS]; // Set if connect to CMS failed. + char * CMSName[MaxCMS]; // Reverse DNS Name of Server + int NumberofCMSAddrs; + int NextCMSAddr; // Round Robin Pointer + int CheckCMSTimer; // CMS Poll Timer + + char SecureCMSPassword[80]; // For Secure CMS Signin + char GatewayCall[10]; // Call for CMS access + char GatewayLoc[10]; // Loc - Needed to report Hybrid Mode + int ReportHybrid; // Report as Hybrod Station + char * HybridServiceCode; + char * HybridFrequencies; + char * HybridCoLocatedRMS; + + BOOL DisconnectOnClose; + + char PasswordMsg[100]; + + char cfgHOSTPROMPT[100]; + + char cfgCTEXT[300]; + + char cfgLOCALECHO[100]; + + int MaxSessions; + + char LoginMsg[100]; + + char RelayAPPL[20]; + char SyncAPPL[20]; + + SOCKET TCPSock; + SOCKET FBBsock[100]; + SOCKET Relaysock; + SOCKET HTTPsock; + SOCKET APIsock; + SOCKET TriModeSock; + SOCKET TriModeDataSock; + SOCKET Syncsock; + SOCKET DRATSsock; + SOCKET SNMPsock; + + struct ConnectionInfo * TriModeControlSession; + SOCKET sock6; + SOCKET FBBsock6[100]; + SOCKET Relaysock6; + SOCKET HTTPsock6; + SOCKET APIsock6; + SOCKET Syncsock6; + SOCKET DRATSsock6; + + fd_set ListenSet; + SOCKET maxsock; + + HMENU hActionMenu; + HMENU hLogMenu; + HMENU hDisMenu; // Disconnect Menu Handle + HWND hCMSWnd; + + int SecureTelnet; + int ReportRelayTraffic; // Send WL2K Reports for Relay Traffic + + char * WebTermCSS; // css override for web terminal + struct LOCALNET * LocalNets; + +}; + + +struct STREAMINFO +{ +// TRANSPORTENTRY * AttachedSession; + + void * PACTORtoBPQ_Q; // Frames for BPQ + void * BPQtoPACTOR_Q; // Frames for PACTOR + int FramesOutstanding; // Frames Queued - used for flow control + int FramesQueued; // Frames Queued - used for flow control + BOOL InternalCmd; // Last Command was generated internally + int IntCmdDelay; // To limit internal commands + BOOL CheckingCall; // Set on PTC if waiting for I response after a Connect RXed + + BOOL Attached; // Set what attached to a BPQ32 stream + BOOL Connected; // When set, all data is passed as data instead of commands + BOOL Connecting; // Set when Outward Connect in progress + BOOL Disconnecting; // Set when disconnect in progress + // Used when appplication disconnects the bpq session, and + // prevents new attaches while a dirty disconnect is in progress + int DisconnectingTimeout; // A hard disconnect occurs if this expires before the disconnect complete + int ReportDISC; // Need to report an incoming DISC to kernel + BOOL DiscWhenAllSent; // Close session when all msgs have been sent to node + BOOL ARQENDSent; // Set when V4 ARQEND Sent + + int DEDStream; // Stream number for DED interface (same as index except for pactor) + + char MyCall[10] ; // Call we are using + char RemoteCall[10]; // Callsign + + char callingCall[10]; // for reporting. Link and Our calls depand on which end connected + char receivingCall[10]; // for reporting. Link and Our calls depand on which end connected + char Direction[4]; // In or Out + + + + char AGWKey[21]; // Session Key for AGW Session Based Drivers + + time_t ConnectTime; // Time connection made + time_t AttachTime; + + int bytesTXed; + int BytesAcked; + int bytesRXed; + int PacketsSent; + int BytesResent; + int BytesOutstanding; // For Packet Channels + + UCHAR PTCStatus0; // Status Bytes + UCHAR PTCStatus1; // Status Bytes + UCHAR PTCStatus2; // Status Bytes + UCHAR PTCStatus3; // Status Bytes + + char * CmdSet; // A series of commands to send to the TNC + char * CmdSave; // Base address for free + + struct ConnectionInfo * ConnectionInfo; // TCP Server Connection Info + + int TimeInRX; // Too long in send mode timer + int NeedDisc; // Timer to send DISC if appl not available + + BOOL NoCMSFallback; // Dont use relay if CMS not available + struct ARQINFO * ARQInfo; // FLDIGI/FLARQ Stream Mode Specific Data + + HWND xIDC_MYCALL; + HWND xIDC_DESTCALL; + HWND xIDC_STATUS; + HWND xIDC_SEND; + HWND xIDC_RXED; + HWND xIDC_RESENT; + HWND xIDC_ACKED; + HWND xIDC_DIRN; + + int RelaySyncStream; + int VaraACMode; +}; + +typedef struct AGWINFO +{ + // Fields for AGW Session based Ports (eg UZ7HO Modem) + + struct AGWHEADER TXHeader; + struct AGWHEADER RXHeader; + int MaxSessions; + int ConnTimeOut; + int PollDelay; + time_t LastParamTime; + +#ifdef WIN32 + + // For selecting UZ7HO Mode and Freq + + COMBOBOXINFO cbinfo; // UZ7HO Modem Combo Box info + HWND hFreq; // UZ7HO Frequency Box + HWND hSpin; // UZ7HO Spin Button + +#endif + + int isQTSM; // Flag to Identify QtSM + + int CenterFreq; + int Modem; // Modem number in list + char ModemName[20]; + unsigned char Version[4]; + unsigned char fx25Flags; + unsigned char il2pFlags; + unsigned char il2pcrc; + +} *PAGWINFO; + +typedef struct ARQINFO +{ + // Fields for FLDIGI/FLARQ Ports + + // Max window is 64, though often will use less + + char OurStream; + char FarStream; + + PMSGWITHLEN TXHOLDQ[64]; // Frames waiting ACK + PMSGWITHLEN RXHOLDQ[64]; // Frames waiting missing frames. + + int TXWindow; + int RXWindow; + int MaxBlock; // Max sending block size + + int TXSeq; + int TXLastACK; // Last frame ACK'ed + + int RXHighest; + int RXNext; + int RXNoGaps; + + int Retries; + int NoAckRetries; // Status received but no data acked + int ARQTimer; + int ARQState; + +#define ARQ_ACTIVE 1 // Have a session of some type + + int ARQTimerState; + +#define ARQ_CONNECTING 1 +#define ARQ_CONNECTACK 2 +#define ARQ_DISC 3 +#define ARQ_WAITACK 4 +#define ARQ_WAITDATA 5 // Waiting for more data before polling + + char LastMsg[80]; // Last message sent that expects an ack + int LastLen; + char TXMsg[80]; // Message to aend after TXDELAY + int TXLen; + int TurnroundTimer; // RX to TX delay. + int TXDelay; + +} *ARQINFO; + +typedef struct FLINFO +{ + // Fields for MPSK Session Ports ) + + BOOL TX; // Set when FLDigi is transmitting + char DefaultMode[64]; // Mode to return to after session + int DefaultFreq; // Freq to return to after session + BOOL Beacon; // Use ALE Beacons + char LastXML[128]; // Last XML Request Sent + int XMLControl; // Controlls polling FLDigi by XML + int CmdControl; // Controlls polling FLDigi by KISS Command + BOOL FLARQ; // Connection from FLARQ + BOOL Busy; + BOOL CONOK; // Allow incoming connects + BOOL KISSMODE; // Using KISS instead of socket interface + BOOL RAW; // Raw (ARQ Socket or KISS RAW, depening on above) + int CenterFreq; + char CurrentMode[20]; // Mode to return to after session + int Responding; // If FLDigi is responding to conmands + BOOL MCASTMODE; // If port is in MCAST RX MOde + +} *FLINFO; + +typedef struct MPSKINFO +{ + // Fields for MPSK Session Ports ) + + int ConnTimeOut; + BOOL TX; // Set when Multipsk is transmitting + char DefaultMode[20]; // Mode to return to after session + BOOL Beacon; // Use ALE Beacons + int MaxSessions; +} *MPSKINFO; + +struct FreeDataINFO +{ + int startingTNC; + int TNCRunning; + int Conecting; + int Connected; + char ourCall[10]; + char toCall[10]; + char farCall[10]; // TNC Call + char useBaseCall; // Use base call (without ssid) for TNC Call + char * Capture; // Capture Device Name + char * Playback; // Playback Device Name + char * hamlibHost; + int hamlibPort; + + unsigned char toSendData[8192]; // Buffer data from node for more efficiency + int toSendCount; + int toSendTimeout; + unsigned char toSendMsg[256]; // Buffer data from node for more efficiency + int toSendMsgCount; + int toSendMsgTimeout; + char * RXDir; // Directory for Received Files + int CONOK; // Virtual Lisren Flag + int Chat; // In Chat Mode + char ChatCall[10]; + int needPoll; // Set if get data needed + int arqstate; // 1 = Disc / 2 - connecting 3 - connected + int TuningRange; // Must be 50, 100, 150, 200, 250 + int LimitBandWidth; + int TXLevel; + int Explorer; // Enable reporting to Freedata Explorer + char SSIDList[256]; + char * SSIDS[16]; +}; + +struct sixPackInfo; + +typedef struct TNCINFO +{ + HWND hDlg; // Status Window Handle + int (FAR * WebWindowProc)(struct TNCINFO * TNC, char * Buff, BOOL LOCAL); // Routine to build web status window + int WebWinX; + int WebWinY; // Size of window + char * WebBuffer; // Buffer for logs + int RigControlRow; // Rig Control Line in Dialog + struct _EXTPORTDATA * PortRecord; // BPQ32 port record for this port + struct RIGINFO * RIG; // Pointer to Rig Control RIG record for RX or Both + struct RIGINFO * TXRIG; // Pointer to Rig Control RIG record for TX + char * InitScript; // Initialisation Commands + int InitScriptLen; // Length + time_t SessionTimeLimit; // Optional limit to total session time + time_t DefaultSessionTimeLimit; // Configured value + + time_t AttachTimeLimit; // to trap port left attached for a long time without other activity + time_t AttachTime; + + int Hardware; // Hardware Type + +#define H_WINMOR 1 +#define H_SCS 2 +#define H_KAM 3 +#define H_AEA 4 +#define H_HAL 5 +#define H_TELNET 6 +#define H_TRK 7 +#define H_TRKM 7 +#define H_V4 8 +#define H_UZ7HO 9 +#define H_MPSK 10 +#define H_FLDIGI 11 +#define H_UIARQ 12 +#define H_ARDOP 13 +#define H_VARA 14 +#define H_SERIAL 15 +#define H_KISSHF 16 +#define H_WINRPR 17 +#define H_HSMODEM 18 +#define H_FREEDATA 19 +#define H_SIXPACK 20 + + + int Port; // BPQ Port Number + + struct RIGINFO DummyRig; // Used if not using Rigcontrol + + BOOL Minimized; // Start Minimized flag + + void * WINMORtoBPQ_Q; // Frames for BPQ, indexed by BPQ Port + void * BPQtoWINMOR_Q; // Frames for WINMOR. indexed by WINMOR port. Only used it TCP session is blocked + + SOCKET TCPSock; // Control Socket + SOCKET TCPDataSock; // Data Socket + SOCKET PacketSock; // Packet Over TCP (ARDOP) + + char * WINMORSignon; // Pointer to message for secure signin + char * HostName; // WINMOR Host - may be dotted decimal or DNS Name + int TCPPort; // + int PacketPort; // Packet Over TCP (ARDOP) + char * ApplCmd; // Application to connect to on incoming connect (null = leave at command handler) + BOOL SwallowSignon; // Set to suppress *** connected to APPL + + union + { + UCHAR TCPBuffer[1000]; // For converting byte stream to messages + UCHAR DEDBuffer[1000]; // For converting byte stream to messages + UCHAR KISSBuffer[1000]; // For KISS over Host Mode + }; + + UCHAR * ARDOPBuffer; // Needs to be pretty big, so Malloc + UCHAR * ARDOPDataBuffer; // Needs to be pretty big, so Malloc + + int InputLen; // Data we have already = Offset of end of an incomplete packet; + int DataInputLen; // Data we have already = Offset of end of an incomplete packet; + int KISSInputLen; // Data we have already = Offset of end of an incomplete packet; + int ESCFLAG; // KISS Escape received + + int MSGCOUNT; // DED WORKING FIELD + int MSGLENGTH; // DED Msg Len + int MSGCHANNEL; // DED Msg Channel Number + int MSGTYPE; // DED Msg Type + + int HOSTSTATE; // ded HOST state machine + + + BOOL StartSent; // Codec Start send (so will get a disconnect) + int ConnectPending; // Set if Connect Pending Received. If so, mustn't allow freq change. + BOOL GavePermission; // Set if we allowed freq change + BOOL DiscPending; // Set if Disconnect Pending Received. So we can time out stuck in Disconnecting + BOOL HadConnect; // Flag to say have been in session + BOOL FECMode; // In FEC Mode + BOOL FEC1600; // Use 1600 Hz FEC Mode + int FECIDTimer; // Time in FEC Mode. Used to trigger ID broadcasts + BOOL RestartAfterFailure; + BOOL StartInRobust; // For WINMOR, set to Robust Mode for first few packets + + int Busy; // Channel Busy Timer/Counter . Non-zero = Busy + + int BusyFlags; // Channel Busy Flags + +#define CDBusy 1 // For WINMOR - reported busy (set till reported clear) +#define PTTBusy 2 // PTT Active + + BOOL FECPending; // Need an FEC Send when channel is next idle + + time_t lasttime; + + BOOL CONNECTING; // TCP Session Flags + BOOL CONNECTED; + BOOL Alerted; // Connect Failed Prompt sent + BOOL DATACONNECTING; + BOOL DATACONNECTED; + + BOOL TNCCONNECTING; // For FreeData + BOOL TNCCONNECTED; + + BOOL QtSMConnected; + + char NodeCall[10]; // Call we listen for (PORTCALL or NODECALL + char CurrentMYC[10]; // Save current call so we don't change it unnecessarily + char * LISTENCALLS; // Calls TNC will respond to (currently only for VARA) + + char TargetCall[10]; // Call incoming connect is addressed to (for appl call support) + + struct sockaddr_in destaddr; + struct sockaddr_in Datadestaddr; + + int PTTMode; // PTT Mode Flags + int PTTState; // Current State + uint64_t PTTActivemS; // For Stats + uint64_t PTTonTime; // + + uint64_t BusyActivemS; // For channel busy stats + uint64_t BusyonTime; + + char PTTOn[60]; // Port override of RIGCONTROL config + char PTTOff[60]; + int PTTOnLen; + int PTTOffLen; + + int TXRadio; // Rigcontrol Radio Number for TX + int RXRadio; // Rigcontrol Radio Number for RX + + long long int TXFreq; // Freq to set on tx before ptt + double ActiveTXFreq; // Freq to set on tx after attach + double ActiveRXFreq; // Freq to set on rx after attach + + double DefaultTXFreq; // Freq to set on tx after close + double DefaultRXFreq; // Freq to set on rx after close + + char ** DisconnectScript; // May replace above 2 params + + int TXOffset; // Correction to TXFreq + + int PID; // Process ID for Software TNC + HWND hWnd; // Main window handle for Software TNC + + char * CaptureDevices; + char * PlaybackDevices; + char * ProgramPath; + BOOL WeStartedTNC; + + int Restarts; // TNC Kill/Restarts done + time_t LastRestart; + + int TimeSinceLast; // Time since last message from TNC (10ths of a sec) + int HeartBeat; + +// int Interlock; // Port Interlock Group + + HWND hMonitor; // Handle to Monitor control +// HMENU hPopMenu; // Actions Menu Handle + + int MaxConReq; // For ARDOP + int BusyHold; // Hold Time from SCS reporting channel free till we call + int BusyWait; // Time to wait for clear channel before connect + + BOOL OverrideBusy; + int BusyDelay; // Timer for busy timeout + int AutoStartDelay; // Time to wait for TNC to start + char * ConnectCmd; // Saved command if waiting for busy to clear + BOOL UseAPPLCalls; // Robust Packet to use Applcalls + BOOL UseAPPLCallsforPactor; // Pactor to use Applcalls + + // Fields for reporting to WL2K Map + + struct WL2KInfo * WL2K; + +/* + char * Host; + short WL2KPort; + + int UpdateWL2KTimer; + BOOL UpdateWL2K; + char RMSCall[10]; + char BaseCall[10]; + char GridSquare[7]; + char Comment[80]; + char ServiceCode[17]; + + BOOL UseRigCtrlFreqs; + char WL2KFreq[12]; + char WL2KModeChar; // W or N + BOOL DontReportNarrowOnWideFreqs; + +// char NARROWMODE; +// char WIDEMODE; // Mode numbers to report to WL2K + + struct WL2KInfo WL2KInfoList[MAXFREQS]; // Freqs for sending to WL2K +*/ + char WL2KMode; // WL2K reporting mode + + struct STREAMINFO Streams[27]; // 0 is Pactor 1 - 10 are ax.25. + int LastStream; // Last one polled for status or send + + void * BPQtoRadio_Q; // Frames to Rig Interface + void * RadiotoBPQ_Q; // Frames from Rig Interface + + void * KISSTX_Q; // Frames to Host Mode KISS interface + struct PORTCONTROL * VirtualPORT; // Pointer to Virtual Packet Port of Host Mode KISS + + char * InitPtr; // Next Command + int ReinitState; // Reinit State Machine + int ReinitCount; // Count for DED Recovery + int TermReinitCount; // Count for DED Term Mode Recovery + BOOL TNCOK; // TNC is reponding + int FramesOutstanding; // Frames Queued - used for flow control + BOOL InternalCmd; // Last Command was generated internally + int IntCmdDelay; // To limit internal commands + + + HANDLE hDevice; + int ReopenTimer; // Used to reopen device if failed (eg USB port removed) + BOOL HostMode; // Set if in DED Host Mode +// BOOL CRCMode; // Set if using SCS Extended DED Mode (JHOST4) + BOOL UsingTermMode; // Set if tnc should be left in term mode + int Timeout; // Timeout response counter + int Retries; + int Window; // Window Size for ARQ + UCHAR TXBuffer[500]; // Last message sent - saved for Retry + int TXLen; // Len of last sent + UCHAR RXBuffer[520]; // Message being received - may not arrive all at once + UINT RXLen; // Data in RXBUffer + UCHAR Toggle; // Sequence bit + int Buffers; // Free buffers in TNC + BOOL WantToChangeFreq; // Request from Scanner to Change + int OKToChangeFreq; // 1 = SCS Says OK to change, -1 = Dont Change zero = still waiting + BOOL DontWantToChangeFreq; // Change done - ok to SCS + BOOL DontReleasePermission; // Hold Permission to prevent calls on this frequency + time_t TimeEnteredSYNCMode; // To detect scan lock when using applcalls on PTC + BOOL SyncSupported; // TNC reports sync + time_t TimeScanLocked; // ditto for TNCs that don't report SYNC + int PTCStatus; // Sync, Idle, Traffic, etc + UCHAR NexttoPoll[20]; // Streams with data outstanding (from General Poll) + BOOL PollSent; // Toggle to ensure we issue a general poll regularly + int StreamtoPoll; + + char Bandwidth; // Currently set Mode W or N + + int Mode; // Mode Flag + + BOOL Dragon; // Set if P4Dragon + BOOL DragonSingle; // Set if P4Dragon using Pactor and Packet on same port + BOOL DragonKISS; // Set if P4Dragon supports sending KISS frames in Hostmode + BOOL EnterExit; // Switching to Term mode to change bandwidth + int PktStream; // Stream in use for Packet when in single port mode + BOOL MaxLevel; // Pactor Level to set for Wide Mode (3 or 4) + int MinLevel; // Mimimum accepted Pactor Level + int MinLevelTimer; // Time left to achieve Min Level + int PacketChannels; + int RobustTime; // For PTC, Spend this part of scan cycle (in 10th secs) in Robust Packet Mode + int SwitchToPactor; // Countdown to switch + + BOOL OldMode; // Use PACTOR instead of TOR (for old software) + BOOL VeryOldMode; // Use MYCALL instead of MYPTCALL (for old software) + + int Mem1; // Free Bytes (VHF /HF) + int Mem2; + + BOOL HFPacket; // Set if HF port is in Packet mode instead of Pactor Mode + BOOL Robust; // Set if SCS Tracker is in Robust Packet mode or WINMOR TNC is in Robust Mode + BOOL RobustDefault; // Set if SCS Tracker default is Robust Packet mode + BOOL ForceRobust; // Don't allow Normal Packet even if scan requests it. + BOOL TeensyRPR; // Teensy RPR TNC - don't send %R + char NormSpeed[8]; // Speed Param for Normal Packet on Tracker + char RobustSpeed[8]; // Speed Param for Robust Packet on Tracker + BOOL RPBEACON; // Send Beacon after each session + + int TimeInRX; // Time waiting for ISS before sending + char TXRXState; // Current ISS/IRS State + + BOOL NeedPACTOR; // Set if need to send PACTOR to put into Standby Mode + int CmdStream; // Stream last command was issued on + + union + { + struct TCPINFO * TCPInfo; // Telnet Server Specific Data + struct AGWINFO * AGWInfo; // AGW Stream Mode Specific Data + struct MPSKINFO * MPSKInfo; // MPSK Stream Mode Specific Data + struct FLINFO * FLInfo; // FLDIGI Stream Mode Specific Data + }; + + struct ARQINFO * ARQInfo; // FLDIGI/FLARQ Stream Mode Specific Data + + BOOL DataBusy; // Waiting for Data Ack - Don't send any more data + BOOL CommandBusy; // Waiting for Command ACK + + BOOL TEXTMODE; // Set if AEA in text mode + BOOL NeedTurnRound; // Set if we have sent data, so need to send ctrl/z + BOOL NeedTRANS; // Set if we have to send TRANS when ctrl/z is acked. + + char * CmdSet; // A series of commands to send to the TNC + char * CmdSave; // Base address for free + + BOOL PktUpdateMap; // Set if Packet MH data to be sent to NodeMap + + int DefaultMode; + int CurrentMode; // Used on HAL + + char * DefaultRadioCmd; // RADIO command to send at end of session + char * Frequency; + // For Mode Map if no Rigcontrol + // Mode Equates + + #define Clover 'C' + #define Pactor 'P' + #define AMTOR 'A' + + UCHAR DataBuffer[500]; // Data Chars split from received stream + UCHAR CmdBuffer[500]; // Cmd/Response chars split from received stream + int DataLen; // Data in DataBuffer + int CmdLen; // Data in CmdBuffer + BOOL CmdEsc; // Set if last char rxed was 0x80 + BOOL DataEsc; // Set if last char rxed was 0x81 + int PollDelay; // Don't poll too often; + int InData; // FLDigi - MCAST <....> received + int InPacket; // FLDigi - SOH or < received. + int MCASTLen; // Data still to get + + int DataMode; // How to treat data + +#define RXDATA 0x30 // Switch to Receive Data characters +#define TXDATA 0x31 // Switch to Transmit Data characters +#define SECDATA 0x32 // Switch to RX data from secondary port + + int TXMode; // Where to send data + +#define TXMODEM 0x33 // Send TX data to modem +#define TXSEC 0x34 // Send TX data to secondary port + + BOOL XONXOFF; // Set if hardware is using XON/XOFF + + double LastFreq; // Used by V4 to see if freq has changed + int ModemCentre; // Modem centre frequency + int ClientHeight; + int ClientWidth; + HWND xIDC_TNCSTATE; + HWND xIDC_COMMSSTATE; + HWND xIDC_MODE; + HWND xIDC_LEDS; + HWND xIDC_TRAFFIC; + HWND xIDC_BUFFERS; + HWND xIDC_CHANSTATE; + HWND xIDC_LEVELS; + HWND xIDC_STATE; + HWND xIDC_TXRX; + HWND xIDC_PROTOSTATE; + HWND xIDC_RESTARTTIME; + HWND xIDC_RESTARTS; + HWND xIDC_PACTORLEVEL; + HWND xIDC_TXTUNE; + HWND xIDC_TXTUNEVAL; + + char * WEB_TNCSTATE; + char * WEB_COMMSSTATE; + char * WEB_MODE; + char * WEB_LEDS; + char * WEB_TRAFFIC; + char * WEB_BUFFERS; + char * WEB_CHANSTATE; + char * WEB_STATE; + char * WEB_TXRX; + char * WEB_PROTOSTATE; + char * WEB_RESTARTTIME; + char * WEB_RESTARTS; + char * WEB_PACTORLEVEL; + char * WEB_LEVELS; + int WEB_CHANGED; // Used to speed up refresh when active + + HMENU hMenu; + HMENU hWndMenu; + + VOID (* SuspendPortProc) (struct TNCINFO * TNC, struct TNCINFO * ThisTNC); + VOID (* ReleasePortProc) (struct TNCINFO * TNC); + VOID (* ForcedCloseProc) (struct TNCINFO * TNC, int Stream); + + time_t WinmorRestartCodecTimer; + int WinmorCurrentMode; + char ARDOPCurrentMode[10]; + char ARDOPCommsMode; + char * ARDOPSerialPort; // Have Bus/Device for I2C + int ARDOPSerialSpeed; + BOOL TCPCONNECTED; // ARDOP over TCP Connected + int SlowTimer; + int ARQPorts[32]; // For ARQ over KISS + char * LogPath; + FILE * LogHandle; // Ardop Logging File + FILE * DebugHandle; // Ardop Debug File + char LastLogType; // For split packets + + UCHAR * ARDOPAPRS; // Used to reconstruct APRS datagram from FEC packets + int ARDOPAPRSLen; + + BOOL WRITELOG; // Enable debug logging + int InputLevelMin; // Sound card levels + int InputLevelMax; // Sound card levels + + int DiscardNextOK; // Used by VARA to suppress OK response to LISTEN commands + int SeenCancelPending; // Used by VARA to suppress duplicate cancel pendings + + MESSAGE * Monframe; // Used by DED Host for receiving Packet Monitor Frame + // split over 2 packets + + struct HSMODEMINFO * HSModemInfo; + struct FreeDataINFO * FreeDataInfo; + + int DontRestart; // Don't automatically restart failed TNC + int SendTandRtoRelay; // Send T and R suffix messages to RELAY instead of CMS + + double SNR; // S/N Ratio (VARA) + + int NetRomMode; + unsigned char * NetRomTxBuffer; // For Netrom over VARA + int NetRomTxLen; + char * NRNeighbour; + int NRCloseTimer; + struct _LINKTABLE * DummyLink; // Simulated link to simplify interface to ax,25 netrom code + struct sixPackPortInfo * sixPack; + int VaraACAllowed; // Set by config + int VaraACMode; // Set by first message received + int VaraModeSet; // Have decicded if VarAC mode or not + char * VARACMsg; // to build message from packets + int VarACTimer; // delayed send timer + size_t VARACSize; // malloc'ed size + +} *PTNCINFO; + +VOID * zalloc(int len); + +BOOL ReadConfigFile(int Port, int ProcLine(char * buf, int Port)); +int GetLine(char * buf); +BOOL CreatePactorWindow(struct TNCINFO * TNC, char * ClassName, char * WindowTitle, int RigControlRow, WNDPROC WndProc, + int Width, int Height, VOID ForcedCloseProc(struct TNCINFO * TNC, int Stream)); +char * CheckAppl(struct TNCINFO * TNC, char * Appl); +BOOL SendReporttoWL2K(struct TNCINFO * TNC); +struct WL2KInfo * DecodeWL2KReportLine(char * buf); +VOID UpdateMH(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction); +VOID UpdateMHEx(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction, char * LOC, BOOL Report); +VOID SaveWindowPos(int port); +VOID SaveMDIWindowPos(HWND hWnd, char * RegKey, char * Value, BOOL Minimized); +BOOL ProcessIncommingConnect(struct TNCINFO * TNC, char * Call, int Stream, BOOL SENDCTEXT); +BOOL ProcessIncommingConnectEx(struct TNCINFO * TNC, char * Call, int Stream, BOOL SENDCTEXT, BOOL AllowTR); +VOID ShowTraffic(struct TNCINFO * TNC); +int OpenCOMMPort(struct TNCINFO * conn, char * Port, int Speed, BOOL Quiet); +VOID SendMH(struct TNCINFO * TNC, char * call, char * freq, char * LOC, char * Mode); +VOID MoveWindows(struct TNCINFO * TNC); + +static VOID TidyClose(struct TNCINFO * TNC, int Stream); +static VOID ForcedClose(struct TNCINFO * TNC, int Stream); +static VOID CloseComplete(struct TNCINFO * TNC, int Stream); + +VOID CheckForDetach(struct TNCINFO * TNC, int Stream, struct STREAMINFO * STREAM, + VOID TidyCloseProc(struct TNCINFO * TNC, int Stream), VOID ForcedCloseProc(struct TNCINFO * TNC, int Stream), + VOID CloseComplete(struct TNCINFO * TNC, int Stream)); + + +BOOL InterlockedCheckBusy(struct TNCINFO * ThisTNC); + +extern UINT CRCTAB; +int BPQTRACE(MESSAGE * Msg, BOOL TOAPRS); + + +static int ProcessLine(char * buf, int Port); +VOID __cdecl Debugprintf(const char * format, ...); +VOID __cdecl Consoleprintf(const char * format, ...); + +extern BOOL MinimizetoTray; + +int standardParams(struct TNCINFO * TNC, char * buf); +void DecodePTTString(struct TNCINFO * TNC, char * ptr); + +int Rig_Command(TRANSPORTENTRY * Session, char * Command); + +BOOL Rig_Poll(); + +VOID Rig_PTT(struct TNCINFO * TNC, BOOL PTTState); +VOID Rig_PTTEx(struct RIGINFO * RIG, BOOL PTTState, struct TNCINFO * TNC); + +struct RIGINFO * Rig_GETPTTREC(int Port); + +struct ScanEntry ** CheckTimeBands(struct RIGINFO * RIG); + +#ifndef LINBPQ +LRESULT CALLBACK PacWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +#endif + +#define Report_P1 11 +#define Report_P12 12 +#define Report_P123 13 +#define Report_P2 14 +#define Report_P23 15 +#define Report_P3 16 + +#define Report_P1234 17 +#define Report_P234 18 +#define Report_P34 19 +#define Report_P4 20 + +#define Report_WINMOR500 21 +#define Report_WINMOR1600 22 + +#define Report_Robust 30 + +#define IOCTL_SERIAL_IS_COM_OPEN CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GETDATA CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x801,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SETDATA CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x802,METHOD_BUFFERED,FILE_ANY_ACCESS) + +#define IOCTL_SERIAL_SET_CTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x803,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_DSR CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x804,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_DCD CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x805,METHOD_BUFFERED,FILE_ANY_ACCESS) + +#define IOCTL_SERIAL_CLR_CTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x806,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_CLR_DSR CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x807,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_CLR_DCD CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x808,METHOD_BUFFERED,FILE_ANY_ACCESS) + +#define IOCTL_BPQ_ADD_DEVICE CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x809,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_BPQ_DELETE_DEVICE CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x80a,METHOD_BUFFERED,FILE_ANY_ACCESS) + +#define W98_SERIAL_GETDATA 0x801 +#define W98_SERIAL_SETDATA 0x802 diff --git a/.svn/pristine/10/108655b013214695f1c2c29f5492cd4f17755b62.svn-base b/.svn/pristine/10/108655b013214695f1c2c29f5492cd4f17755b62.svn-base index 3aec083..49e3f4e 100644 --- a/.svn/pristine/10/108655b013214695f1c2c29f5492cd4f17755b62.svn-base +++ b/.svn/pristine/10/108655b013214695f1c2c29f5492cd4f17755b62.svn-base @@ -1,22 +1,22 @@ -Chat : -{ - ApplNum = 2; - MaxStreams = 11; - OtherChatNodes = "RDGCHT:GB7RDG-1|C STHGTE|C 1 MB7NCR-2|C RDGCHT\r\nRDGCHT:GB7RDG-1|C STHGTE|C 1 MB7NCR-2|C RDGCHT\r\n\r\n"; - ChatWelcomeMsg = "G8BPQ Chat Server.$WType /h for command summary.$WBringing up links to other nodes.$WThis may take a minute or two.$WThe /p command shows what nodes are linked.$W"; - MapPosition = "MapPosition=5259.04N, 00107.01W"; - MapPopup = "MapPopup=G8BPQ Nottingham

BPQ32 Home PageBPQ32 Home PageEip; - SPPtr = exinfo.ContextRecord->Esp; - - __asm - { - mov eax, SPPtr - mov SPVal,eax - lea edi,Stack - mov esi,eax - mov ecx,64 - rep movsb - - lea edi,CodeDump - mov esi,eip - mov ecx,64 - rep movsb - } - - - - Debugprintf("BPQ32 *** Program Error %x at %x in %s", - exinfo.ExceptionRecord->ExceptionCode, exinfo.ExceptionRecord->ExceptionAddress, EXCEPTMSG); - - Debugprintf("EAX %x EBX %x ECX %x EDX %x ESI %x EDI %x ESP %x", - exinfo.ContextRecord->Eax, exinfo.ContextRecord->Ebx, exinfo.ContextRecord->Ecx, - exinfo.ContextRecord->Edx, exinfo.ContextRecord->Esi, exinfo.ContextRecord->Edi, SPVal); - -#endif - - Debugprintf("Stack:"); - - Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", - SPVal, Stack[0], Stack[1], Stack[2], Stack[3], Stack[4], Stack[5], Stack[6], Stack[7]); - - Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", - SPVal+32, Stack[8], Stack[9], Stack[10], Stack[11], Stack[12], Stack[13], Stack[14], Stack[15]); - - Debugprintf("Code:"); - - for (i = 0; i < 16; i++) - { - rev = (CodeDump[i] & 0xff) << 24; - rev |= (CodeDump[i] & 0xff00) << 8; - rev |= (CodeDump[i] & 0xff0000) >> 8; - rev |= (CodeDump[i] & 0xff000000) >> 24; - - CodeDump[i] = rev; - } - - Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", - eip, CodeDump[0], CodeDump[1], CodeDump[2], CodeDump[3], CodeDump[4], CodeDump[5], CodeDump[6], CodeDump[7]); - - Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", - eip+32, CodeDump[8], CodeDump[9], CodeDump[10], CodeDump[11], CodeDump[12], CodeDump[13], CodeDump[14], CodeDump[15]); - - WriteMiniDump(); - - // Note - no closing } so additional code may be run in the __except block - -#ifdef MDIKERNEL - if (CloseOnError == 1) - CloseAllNeeded = 1; -#endif - -#undef EXCEPTMSG +// +// Standard __except handler to dump stack and code around eip +// + +__except(memcpy(&exinfo, GetExceptionInformation(), sizeof(struct _EXCEPTION_POINTERS)), EXCEPTION_EXECUTE_HANDLER) +{ + unsigned __int32 SPPtr = 0; + unsigned __int32 SPVal = 0; + unsigned __int32 eip = 0; + unsigned __int32 rev = 0; + int i; + + DWORD Stack[16]; + DWORD CodeDump[16]; + +#ifndef _WIN64 + + eip = exinfo.ContextRecord->Eip; + SPPtr = exinfo.ContextRecord->Esp; + + __asm + { + mov eax, SPPtr + mov SPVal,eax + lea edi,Stack + mov esi,eax + mov ecx,64 + rep movsb + + lea edi,CodeDump + mov esi,eip + mov ecx,64 + rep movsb + } + + + + Debugprintf("BPQ32 *** Program Error %x at %x in %s", + exinfo.ExceptionRecord->ExceptionCode, exinfo.ExceptionRecord->ExceptionAddress, EXCEPTMSG); + + Debugprintf("EAX %x EBX %x ECX %x EDX %x ESI %x EDI %x ESP %x", + exinfo.ContextRecord->Eax, exinfo.ContextRecord->Ebx, exinfo.ContextRecord->Ecx, + exinfo.ContextRecord->Edx, exinfo.ContextRecord->Esi, exinfo.ContextRecord->Edi, SPVal); + +#endif + + Debugprintf("Stack:"); + + Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", + SPVal, Stack[0], Stack[1], Stack[2], Stack[3], Stack[4], Stack[5], Stack[6], Stack[7]); + + Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", + SPVal+32, Stack[8], Stack[9], Stack[10], Stack[11], Stack[12], Stack[13], Stack[14], Stack[15]); + + Debugprintf("Code:"); + + for (i = 0; i < 16; i++) + { + rev = (CodeDump[i] & 0xff) << 24; + rev |= (CodeDump[i] & 0xff00) << 8; + rev |= (CodeDump[i] & 0xff0000) >> 8; + rev |= (CodeDump[i] & 0xff000000) >> 24; + + CodeDump[i] = rev; + } + + Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", + eip, CodeDump[0], CodeDump[1], CodeDump[2], CodeDump[3], CodeDump[4], CodeDump[5], CodeDump[6], CodeDump[7]); + + Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", + eip+32, CodeDump[8], CodeDump[9], CodeDump[10], CodeDump[11], CodeDump[12], CodeDump[13], CodeDump[14], CodeDump[15]); + + WriteMiniDump(); + + // Note - no closing } so additional code may be run in the __except block + +#ifdef MDIKERNEL + if (CloseOnError == 1) + CloseAllNeeded = 1; +#endif + +#undef EXCEPTMSG diff --git a/.svn/pristine/11/11df31525d8096af91b6003047c846fb996c508a.svn-base b/.svn/pristine/11/11df31525d8096af91b6003047c846fb996c508a.svn-base index 83819b4..1ecd775 100644 --- a/.svn/pristine/11/11df31525d8096af91b6003047c846fb996c508a.svn-base +++ b/.svn/pristine/11/11df31525d8096af91b6003047c846fb996c508a.svn-base @@ -1,2201 +1,2201 @@ -/* -Copyright 2001-2022 John Wiseman G8BPQ - -This file is part of LinBPQ/BPQ32. - -LinBPQ/BPQ32 is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -LinBPQ/BPQ32 is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses -*/ - - -#pragma data_seg("_BPQDATA") - -#define _CRT_SECURE_NO_DEPRECATE - -#include - -#define SD_RECEIVE 0x00 -#define SD_SEND 0x01 -#define SD_BOTH 0x02 - - -#include "kernelresource.h" -#include "cheaders.h" -#include "tncinfo.h" -#ifndef LINBPQ -#include -#endif -//#include -#include "bpq32.h" -#include "adif.h" - - -HANDLE hInstance; -extern HBRUSH bgBrush; -extern HWND ClientWnd, FrameWnd; -extern int OffsetH, OffsetW; - -extern HMENU hMainFrameMenu; -extern HMENU hBaseMenu; -extern HANDLE hInstance; - -extern HKEY REGTREE; - -extern int Ver[]; - - -int KillTNC(struct TNCINFO * TNC); -int RestartTNC(struct TNCINFO * TNC); - -char * GetChallengeResponse(char * Call, char * ChallengeString); - -VOID __cdecl Debugprintf(const char * format, ...); -int FromLOC(char * Locator, double * pLat, double * pLon); -BOOL ToLOC(double Lat, double Lon , char * Locator); - -int GetPosnFromAPRS(char * Call, double * Lat, double * Lon); -char * stristr (char *ch1, char *ch2); - - -static RECT Rect; - -#define WSA_ACCEPT WM_USER + 1 -#define WSA_DATA WM_USER + 2 -#define WSA_CONNECT WM_USER + 3 - -int Winmor_Socket_Data(int sock, int error, int eventcode); - -struct WL2KInfo * WL2KReports; - -int WL2KTimer = 0; - -int ModetoBaud[31] = {0,0,0,0,0,0,0,0,0,0,0, // 0 = 10 - 200,600,3200,600,3200,3200, // 11 - 16 - 0,0,0,0,0,0,0,0,0,0,0,0,0,600}; // 17 - 30 - -extern char HFCTEXT[]; -extern int HFCTEXTLEN; - - -extern char WL2KCall[10]; -extern char WL2KLoc[7]; - - -VOID MoveWindows(struct TNCINFO * TNC) -{ -#ifndef LINBPQ - RECT rcClient; - int ClientHeight, ClientWidth; - - GetClientRect(TNC->hDlg, &rcClient); - - ClientHeight = rcClient.bottom; - ClientWidth = rcClient.right; - - if (TNC->hMonitor) - MoveWindow(TNC->hMonitor,2 , TNC->RigControlRow + 3, ClientWidth-4, ClientHeight - (TNC->RigControlRow + 3), TRUE); -#endif -} - -char * Config; -static char * ptr1, * ptr2; - -#ifndef LINBPQ - -LRESULT CALLBACK PacWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - int wmId, wmEvent; - MINMAXINFO * mmi; - - int i; - struct TNCINFO * TNC; - - HKEY hKey; - char Key[80]; - int retCode, disp; - - for (i=0; i<41; i++) - { - TNC = TNCInfo[i]; - if (TNC == NULL) - continue; - - if (TNC->hDlg == hWnd) - break; - } - - if (TNC == NULL) - return DefMDIChildProc(hWnd, message, wParam, lParam); - - switch (message) { - - case WM_CREATE: - - break; - - case WM_PAINT: - -// hdc = BeginPaint (hWnd, &ps); - -// SelectObject( hdc, hFont) ; - -// EndPaint (hWnd, &ps); -// -// wParam = hdc; - - break; - - - case WM_GETMINMAXINFO: - - if (TNC->ClientHeight) - { - mmi = (MINMAXINFO *)lParam; - mmi->ptMaxSize.x = TNC->ClientWidth; - mmi->ptMaxSize.y = TNC->ClientHeight; - mmi->ptMaxTrackSize.x = TNC->ClientWidth; - mmi->ptMaxTrackSize.y = TNC->ClientHeight; - } - - break; - - - case WM_MDIACTIVATE: - { - - // Set the system info menu when getting activated - - if (lParam == (LPARAM) hWnd) - { - // Activate - - RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); - - if (TNC->hMenu) - AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)TNC->hMenu, "Actions"); - - SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); - -// SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) TNC->hMenu, (LPARAM) TNC->hWndMenu); - } - else - { - // Deactivate - - SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); - } - - // call DrawMenuBar after the menu items are set - DrawMenuBar(FrameWnd); - - return DefMDIChildProc(hWnd, message, wParam, lParam); - } - - - - case WM_INITMENUPOPUP: - - if (wParam == (WPARAM)TNC->hMenu) - { - if (TNC->ProgramPath) - { - if (strstr(TNC->ProgramPath, " TNC") || strstr(TNC->ProgramPath, "ARDOP") - || strstr(TNC->ProgramPath, "VARA") || stristr(TNC->ProgramPath, "FREEDATA")) - { - EnableMenuItem(TNC->hMenu, WINMOR_RESTART, MF_BYCOMMAND | MF_ENABLED); - EnableMenuItem(TNC->hMenu, WINMOR_KILL, MF_BYCOMMAND | MF_ENABLED); - - break; - } - } - EnableMenuItem(TNC->hMenu, WINMOR_RESTART, MF_BYCOMMAND | MF_GRAYED); - EnableMenuItem(TNC->hMenu, WINMOR_KILL, MF_BYCOMMAND | MF_GRAYED); - } - - break; - - case WM_COMMAND: - - wmId = LOWORD(wParam); - wmEvent = HIWORD(wParam); - - switch (wmId) - { - case WINMOR_KILL: - - TNC->DontRestart = TRUE; - KillTNC(TNC); - break; - - case WINMOR_RESTART: - - TNC->DontRestart = FALSE; - KillTNC(TNC); - RestartTNC(TNC); - break; - - case WINMOR_RESTARTAFTERFAILURE: - - TNC->RestartAfterFailure = !TNC->RestartAfterFailure; - CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); - - sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\PACTOR\\PORT%d", TNC->Port); - - retCode = RegCreateKeyEx(REGTREE, Key, 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); - - if (retCode == ERROR_SUCCESS) - { - RegSetValueEx(hKey,"TNC->RestartAfterFailure",0,REG_DWORD,(BYTE *)&TNC->RestartAfterFailure, 4); - RegCloseKey(hKey); - } - break; - - case ARDOP_ABORT: - - if (TNC->ForcedCloseProc) - TNC->ForcedCloseProc(TNC, 0); - - break; - } - return DefMDIChildProc(hWnd, message, wParam, lParam); - - case WM_SIZING: - case WM_SIZE: - - MoveWindows(TNC); - - return DefMDIChildProc(hWnd, message, wParam, lParam); - - case WM_SYSCOMMAND: - - wmId = LOWORD(wParam); - wmEvent = HIWORD(wParam); - - switch (wmId) - { - - case SC_RESTORE: - - TNC->Minimized = FALSE; - break; - - case SC_MINIMIZE: - - TNC->Minimized = TRUE; - break; - } - - return DefMDIChildProc(hWnd, message, wParam, lParam); - - case WM_CTLCOLORDLG: - return (LONG)bgBrush; - - - case WM_CTLCOLORSTATIC: - { - HDC hdcStatic = (HDC)wParam; - SetTextColor(hdcStatic, RGB(0, 0, 0)); - SetBkMode(hdcStatic, TRANSPARENT); - return (LONG)bgBrush; - } - - case WM_HSCROLL: - { - char value[16]; - - switch (LOWORD(wParam)) - { - case TB_ENDTRACK: - case TB_THUMBTRACK: - - TNC->TXOffset = SendMessage(TNC->xIDC_TXTUNE, TBM_GETPOS, 0, 0); - sprintf(value, "%d", TNC->TXOffset); - MySetWindowText(TNC->xIDC_TXTUNEVAL, value); - - break; - } - - default: - break; - } - case WM_DESTROY: - - break; - } - return DefMDIChildProc(hWnd, message, wParam, lParam); -} -#endif - -BOOL CreatePactorWindow(struct TNCINFO * TNC, char * ClassName, char * WindowTitle, int RigControlRow, WNDPROC WndProc, int Width, int Height, - VOID ForcedCloseProc(struct TNCINFO * TNC, int Stream)) -{ -#ifdef LINBPQ - return FALSE; -#else - WNDCLASS wc; - char Title[80]; - int retCode, Type, Vallen; - HKEY hKey=0; - char Key[80]; - char Size[80]; - int Top, Left; - HANDLE hDlg = 0; - static int LP = 1235; - - if (TNC->hDlg) - { - ShowWindow(TNC->hDlg, SW_SHOWNORMAL); - SetForegroundWindow(TNC->hDlg); - return FALSE; // Already open - } - - wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; - wc.lpfnWndProc = WndProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = DLGWINDOWEXTRA; - wc.hInstance = hInstance; - wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON) ); - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.hbrBackground = bgBrush; - wc.lpszMenuName = NULL; - wc.lpszClassName = ClassName; - - RegisterClass(&wc); - -// if (TNC->Hardware == H_WINMOR || TNC->Hardware == H_TELNET ||TNC->Hardware == H_ARDOP || -// TNC->Hardware == H_V4 || TNC->Hardware == H_FLDIGI || TNC->Hardware == H_UIARQ || TNC->Hardware == H_VARA) - if (TNC->PortRecord) - sprintf(Title, "%s Status - Port %d %s", WindowTitle, TNC->Port, TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION); - else - sprintf(Title, "Rigcontrol"); - - if (TNC->Hardware == H_MPSK) - sprintf(Title, "Rigcontrol for MultiPSK Port %d", TNC->Port); - - TNC->hDlg = hDlg = CreateMDIWindow(ClassName, Title, 0, - 0, 0, Width, Height, ClientWnd, hInstance, ++LP); - - // CreateDialog(hInstance,ClassName,0,NULL); - - Rect.top = 100; - Rect.left = 20; - Rect.right = Width + 20; - Rect.bottom = Height + 100; - - - sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\PACTOR\\PORT%d", TNC->Port); - - retCode = RegOpenKeyEx (REGTREE, Key, 0, KEY_QUERY_VALUE, &hKey); - - if (retCode == ERROR_SUCCESS) - { - Vallen=80; - - retCode = RegQueryValueEx(hKey,"Size",0, - (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); - - if (retCode == ERROR_SUCCESS) - { - sscanf(Size,"%d,%d,%d,%d,%d",&Rect.left,&Rect.right,&Rect.top,&Rect.bottom, &TNC->Minimized); - - if (Rect.top < - 500 || Rect.left < - 500) - { - Rect.left = 0; - Rect.top = 0; - Rect.right = 600; - Rect.bottom = 400; - } - - if (Rect.top < OffsetH) - { - int Error = OffsetH - Rect.top; - Rect.top += Error; - Rect.bottom += Error; - } - } - - if (TNC->Hardware == H_WINMOR || TNC->Hardware == H_ARDOP|| TNC->Hardware == H_VARA) - retCode = RegQueryValueEx(hKey,"TNC->RestartAfterFailure",0, - (ULONG *)&Type,(UCHAR *)&TNC->RestartAfterFailure,(ULONG *)&Vallen); - - RegCloseKey(hKey); - } - - Top = Rect.top; - Left = Rect.left; - -// GetWindowRect(hDlg, &Rect); // Get the real size - - MoveWindow(hDlg, Left - (OffsetW /2), Top - OffsetH, Rect.right - Rect.left, Rect.bottom - Rect.top, TRUE); - - if (TNC->Minimized) - ShowWindow(hDlg, SW_SHOWMINIMIZED); - else - ShowWindow(hDlg, SW_RESTORE); - - TNC->RigControlRow = RigControlRow; - - SetWindowText(TNC->xIDC_TNCSTATE, "Free"); - - TNC->ForcedCloseProc = ForcedCloseProc; - - return TRUE; -#endif -} - - -// WL2K Reporting Code. - -static SOCKADDR_IN sinx; - - -VOID SendReporttoWL2KThread(void * unused); -VOID SendHTTPReporttoWL2KThread(void * unused); - -VOID CheckWL2KReportTimer() -{ - if (WL2KReports == NULL) - return; // Shouldn't happen! - - WL2KTimer--; - - if (WL2KTimer != 0) - return; - -#ifdef WIN32 - WL2KTimer = 2 * 32910; // Every 2 Hours - PC Tick is a bit slow -#else - WL2KTimer = 2 * 36000; // Every 2 Hours -#endif - - if (CheckAppl(NULL, "RMS ") == NULL) - if (CheckAppl(NULL, "RELAY ") == NULL) - return; - - _beginthread(SendHTTPReporttoWL2KThread, 0, 0); - - return; -} - -static char HeaderTemplate[] = "POST %s HTTP/1.1\r\n" - "Accept: application/json\r\n" -// "Accept-Encoding: gzip,deflate,gzip, deflate\r\n" - "Content-Type: application/json\r\n" - "Host: %s:%d\r\n" - "Content-Length: %d\r\n" - //r\nUser-Agent: BPQ32(G8BPQ)\r\n" -// "Expect: 100-continue\r\n" - "\r\n{%s}"; - -char Missing[] = "** Missing **"; - -VOID GetJSONValue(char * _REPLYBUFFER, char * Name, char * Value) -{ - char * ptr1, * ptr2; - - strcpy(Value, Missing); - - ptr1 = strstr(_REPLYBUFFER, Name); - - if (ptr1 == 0) - return; - - ptr1 += (strlen(Name) + 1); - - ptr2 = strchr(ptr1, '"'); - - if (ptr2) - { - size_t ValLen = ptr2 - ptr1; - memcpy(Value, ptr1, ValLen); - Value[ValLen] = 0; - } - - return; -} - - -// Send Winlink Session Record - -extern char LOC[7]; -extern char TextVerstring[50]; - -double Distance(double laa, double loa, double lah, double loh, BOOL KM); -double Bearing(double lat2, double lon2, double lat1, double lon1); -VOID SendHTTPRequest(SOCKET sock, char * Request, char * Params, int Len, char * Return); -SOCKET OpenWL2KHTTPSock(); - - - -struct WL2KMode -{ - int Mode; - char * WL2KString; - char * ADIFString; - char * BPQString; -}; - -struct WL2KMode WL2KModeList[] = -{ - {0,"Packet 1200"}, - {1,"Packet 2400"}, - {2, "Packet 4800"}, - {3, "Packet 9600"}, - {4, "Packet 19200"}, - {5, "Packet 38400"}, - {11, "Pactor 1"}, - {12, "Pactor 1,2"}, - {13, "Pactor 1,2,3"}, - {14, "Pactor 2"}, - {15, "Pactor 2,3"}, - {16, "Pactor 3"}, - {17, "Pactor 1,2,3,4"}, - {18, "Pactor 2,3,4"}, - {19, "Pactor 3,4"}, - {20, "Pactor 4"}, - {21, "WINMOR 500"}, - {22, "WINMOR 1600"}, - {30, "Robust Packet"}, - {40, "ARDOP 200"}, - {41, "ARDOP 500"}, - {42, "ARDOP 1000"}, - {43, "ARDOP 2000"}, - {44, "ARDOP 2000 FM"}, - {50, "VARA"}, - {51, "VARA FM"}, - {52, "VARA FM WIDE"}, - {53, "VARA 500"} -}; - -char WL2KModes [55][18] = { - "Packet 1200", "Packet 2400", "Packet 4800", "Packet 9600", "Packet 19200", "Packet 38400", "High Speed Packet", "", "", "", "", - "Pactor 1", "Pactor", "Pactor", "Pactor 2", "Pactor", "Pactor 3", "Pactor", "Pactor", "Pactor", "Pactor 4", // 11 - 20 - "Winmor 500", "Winmor 1600", "", "", "", "", "", "", "", // 21 - 29 - "Robust Packet", "", "", "", "", "", "", "", "", "", // 30 - 39 - "ARDOP 200", "ARDOP 500", "ARDOP 1000", "ARDOP 2000", "ARDOP 2000 FM", "", "", "", "", "", // 40 - 49 - "VARA", "VARA FM", "VARA FM WIDE", "VARA 500", "VARA 2750"}; - - -VOID SendWL2KSessionRecordThread(void * param) -{ - SOCKET sock; - char Message[512]; - - strcpy(Message, param); - free(param); - - Debugprintf("Sending %s", Message); - - sock = OpenWL2KHTTPSock(); - - if (sock) - { - SendHTTPRequest(sock, "/session/add", (char *)Message, (int)strlen(Message), NULL); - closesocket(sock); - } - - return; -} - -VOID SendWL2KRegisterHybridThread(void * param) -{ - SOCKET sock; - char Message[512]; - - strcpy(Message, param); - free(param); - - Debugprintf("Sending %s", Message); - - sock = OpenWL2KHTTPSock(); - - if (sock) - { - SendHTTPRequest(sock, "/radioNetwork/params/add", (char *)Message, (int)strlen(Message), NULL); - closesocket(sock); - } - - return; -} - -VOID SendWL2KRegisterHybrid(struct TNCINFO * TNC) -{ - char Message[512]; - char Date[80] ; - int Len; - struct TCPINFO * TCP = TNC->TCPInfo; - time_t T; - struct tm * tm; - char Call[10]; - - if (TCP == NULL || TCP->GatewayLoc[0] == 0) - return; - - strcpy(Call, TCP->GatewayCall); - strlop(Call, '-'); - - T = time(NULL); - tm = gmtime(&T); - - //2021-10-31-14=35=29 - - sprintf(Date, "%04d-%02d-%02d-%02d:%02d:%02d", - tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec); - -// "Callsign":"String","Password":"String","Param":"String","Value":"String","Key":"String" - - Len = sprintf(Message, "\"Callsign\":\"%s\",\"Password\":\"%s\",\"Param\":\"RMSRelayVersion\",\"Value\":\"%s|%s|*HARMNNNN|%s|%s|\"", - Call, TCP->SecureCMSPassword, Date, "3.1.11.2", - TCP->HybridServiceCode, TCP->GatewayLoc); - - SendWL2KRegisterHybridThread(_strdup(Message)); - - Len = sprintf(Message, "\"Callsign\":\"%s\",\"Password\":\"%s\",\"Param\":\"CoLocatedRMS\",\"Value\":\"%s\"", - Call, TCP->SecureCMSPassword, TCP->HybridCoLocatedRMS); - - SendWL2KRegisterHybridThread(_strdup(Message)); - - Len = sprintf(Message, "\"Callsign\":\"%s\",\"Password\":\"%s\",\"Param\":\"AllowFreq\",\"Value\":\"%s\"", - Call, TCP->SecureCMSPassword, TCP->HybridFrequencies); - - SendWL2KRegisterHybridThread(_strdup(Message)); - - return; -} - -BOOL NoSessionAccount = FALSE; -BOOL SessionAccountChecked = FALSE; - -BOOL SendWL2KSessionRecord(ADIF * ADIF, int BytesSent, int BytesReceived) -{ -/* -The API is /session/add https://api.winlink.org/json/metadata?op=SessionAdd - -The important parameters are (others can be omitted): - -Application (gateway program name) -Server (gateway callsign) -ServerGrid -Client (client callsign) -ClientGrid -Mode (Pactor, winmor, vara, etc) -Frequency -MessagesSent -MessagesReceived -BytesSent -BytesReceived -HoldingSeconds (duration of connection) -IdTag (random alphanumeric, 12 chars) - -"Application":"RMS Trimode", -"Version":"1.3.25.0", -"Cms":"CMS-A", -"Server":"AB4NX", -"ServerGrid":"EM73WT", -"Client":"VE2SCA","ClientGrid":"", -"Sid":"","Mode":"WINMOR16", -"Frequency":10145000, -"Kilometers":0, -"Degrees":0, -"LastCommand":">", -"MessagesSent":0, -"MessagesReceived":0, -"BytesSent":179, -"BytesReceived":0, -"HoldingSeconds":126, -"IdTag":"ATK9S3QGL2E1"} -*/ - time_t T; - - char Message[4096] = ""; - char * MessagePtr; - int MessageLen; - int Dist = 0; - int intBearing = 0; - - double Lat, Lon; - double myLat, myLon; - - char Tag[32]; - - SOCKET sock; - char Response[1024]; - int Len; - - // Only report if the CMSCall has a WL2KAccount - - if (NoSessionAccount) - return TRUE; - - if (!SessionAccountChecked) - { - // only check once - - sock = OpenWL2KHTTPSock(); - - if (sock) - { - SessionAccountChecked = TRUE; - - Len = sprintf(Message, "\"Callsign\":\"%s\"", ADIF->CMSCall); - - SendHTTPRequest(sock, "/account/exists", Message, Len, Response); - closesocket(sock); - - if (strstr(Response, "\"CallsignExists\":false")) - { - WritetoConsole("WL2K Traffic Reporting disabled - Gateway "); - WritetoConsole(ADIF->CMSCall); - WritetoConsole(" does not have a Winlink Account\r\n"); - Debugprintf("WL2K Traffic Reporting disabled - Gateway %s does not have a Winlink Account", ADIF->CMSCall); - NoSessionAccount = TRUE; - return TRUE; - } - } - } - - if (ADIF == NULL || ADIF->LOC[0] == 0 || ADIF->Call[0] == 0) - return TRUE; - - if (ADIF->StartTime == 0 || ADIF->ServerSID[0] == 0 || ADIF->CMSCall[0] == 0) - return TRUE; - - T = time(NULL); - - // Extract Info we need - - // Distance and Bearing - - if (LOC[0] && ADIF->LOC[0]) - { - if (FromLOC(LOC, &myLat, &myLon) == 0) // Basic checks on LOCs - return TRUE; - if (FromLOC(ADIF->LOC, &Lat, &Lon) == 0) - return TRUE; - - Dist = (int)Distance(myLat, myLon, Lat, Lon, TRUE); - intBearing = (int)Bearing(Lat, Lon, myLat, myLon); - } - - MessageLen = sprintf(Message, "\"Application\":\"%s\",", "BPQ32"); - MessageLen += sprintf(&Message[MessageLen], "\"Version\":\"%s\",", TextVerstring); - MessageLen += sprintf(&Message[MessageLen], "\"Cms\":\"%s\",", "CMS"); - MessageLen += sprintf(&Message[MessageLen], "\"Server\":\"%s\",", ADIF->CMSCall); - MessageLen += sprintf(&Message[MessageLen], "\"ServerGrid\":\"%s\",", LOC); - MessageLen += sprintf(&Message[MessageLen], "\"Client\":\"%s\",", ADIF->Call); - MessageLen += sprintf(&Message[MessageLen], "\"ClientGrid\":\"%s\",", ADIF->LOC); - MessageLen += sprintf(&Message[MessageLen], "\"Sid\":\"%s\",", ADIF->UserSID); - MessageLen += sprintf(&Message[MessageLen], "\"Mode\":\"%s\",", WL2KModes[ADIF->Mode]); - MessageLen += sprintf(&Message[MessageLen], "\"Frequency\":%lld,", ADIF->Freq); - MessageLen += sprintf(&Message[MessageLen], "\"Kilometers\":%d,", Dist); - MessageLen += sprintf(&Message[MessageLen], "\"Degrees\":%d,", intBearing); - MessageLen += sprintf(&Message[MessageLen], "\"LastCommand\":\"%s\",", ADIF->Termination); - MessageLen += sprintf(&Message[MessageLen], "\"MessagesSent\":%d,", ADIF->Sent); - MessageLen += sprintf(&Message[MessageLen], "\"MessagesReceived\":%d,", ADIF->Received); - MessageLen += sprintf(&Message[MessageLen], "\"BytesSent\":%d,", BytesSent); - MessageLen += sprintf(&Message[MessageLen], "\"BytesReceived\":%d,", BytesReceived); - MessageLen += sprintf(&Message[MessageLen], "\"HoldingSeconds\":%d,", (int)(T - ADIF->StartTime)); - sprintf(Tag, "%012X", (int)T * (rand() + 1)); - MessageLen += sprintf(&Message[MessageLen], "\"IdTag\":\"%s\"", Tag); - - MessagePtr = _strdup(Message); - _beginthread(SendWL2KSessionRecordThread, 0, (void *)MessagePtr); - - return TRUE; -} - -char APIKey[] = ",\"Key\":\"0D0C7AD6B38C45A7A9534E67111C38A7\""; - - -VOID SendHTTPRequest(SOCKET sock, char * Request, char * Params, int Len, char * Return) -{ - int InputLen = 0; - int inptr = 0; - char Buffer[2048]; - char Header[2048]; - char * ptr, * ptr1; - int Sent; - - strcat(Params, APIKey); - Len += (int)strlen(APIKey); - - sprintf(Header, HeaderTemplate, Request, "api.winlink.org", 80, Len + 2, Params); - Sent = send(sock, Header, (int)strlen(Header), 0); - - if (Sent == -1) - { - int Err = WSAGetLastError(); - Debugprintf("Error %d from WL2K Update send()", Err); - return; - } - - while (InputLen != -1) - { - InputLen = recv(sock, &Buffer[inptr], 2048 - inptr, 0); - - if (InputLen == -1 || InputLen == 0) - { - int Err = WSAGetLastError(); - Debugprintf("Error %d from WL2K Update recv()", Err); - return; - } - - // As we are using a persistant connection, can't look for close. Check - // for complete message - - inptr += InputLen; - - Buffer[inptr] = 0; - - ptr = strstr(Buffer, "\r\n\r\n"); - - if (ptr) - { - // got header - - int Hddrlen = (int)(ptr - Buffer); - - ptr1 = strstr(Buffer, "Content-Length:"); - - if (ptr1) - { - // Have content length - - int ContentLen = atoi(ptr1 + 16); - - if (ContentLen + Hddrlen + 4 == inptr) - { - // got whole response - - if (strstr(Buffer, " 200 OK")) - { - if (Return) - { - memcpy(Return, ptr + 4, ContentLen); - Return[ContentLen] = 0; - } - else - Debugprintf("WL2K Database update ok"); - - } - else - { - strlop(Buffer, 13); - Debugprintf("WL2K Update Params - %s", Params); - Debugprintf("WL2K Update failed - %s", Buffer); - } - return; - } - } - else - { - ptr1 = strstr(_strlwr(Buffer), "transfer-encoding:"); - - if (ptr1) - { - // Just accept anything until I've sorted things with Lee - Debugprintf("%s", ptr1); - Debugprintf("WL2K Database update ok"); - return; - } - } - } - } -} - -BOOL WL2KAccountChecked = FALSE; -BOOL NoWL2KAccount = FALSE; - -VOID SendHTTPReporttoWL2KThread(void * unused) -{ - // Uses HTTP/JSON Interface - - struct WL2KInfo * WL2KReport = WL2KReports; - char * LastHost = NULL; - char * LastRMSCall = NULL; - char Message[512]; - int LastSocket = 0; - SOCKET sock = 0; - struct sockaddr_in destaddr; - int addrlen=sizeof(sinx); - struct hostent * HostEnt; - int err; - u_long param=1; - BOOL bcopt=TRUE; - int Len; - - // Send all reports in list - - char Response[1024]; - - // Only report if the CMSCall has a WL2KAccount - - if (NoWL2KAccount) - return; - - if (!WL2KAccountChecked) - { - // only check once - - sock = OpenWL2KHTTPSock(); - - if (sock) - { - WL2KAccountChecked = TRUE; - - Len = sprintf(Message, "\"Callsign\":\"%s\"", - WL2KReport->BaseCall); - - SendHTTPRequest(sock, "/account/exists", Message, Len, Response); - closesocket(sock); - - if (strstr(Response, "\"CallsignExists\":false")) - { - WritetoConsole("WL2K Reporting disabled - Gateway "); - WritetoConsole(WL2KReport->BaseCall); - WritetoConsole(" does not have a Winlink Account\r\n"); - NoWL2KAccount = TRUE; - return; - } - } - } - - while (WL2KReport) - { - // Resolve Name if needed - - if (LastHost && strcmp(LastHost, WL2KReport->Host) == 0) // Same host? - goto SameHost; - - // New Host - Connect to it - - LastHost = WL2KReport->Host; - - destaddr.sin_family = AF_INET; - destaddr.sin_addr.s_addr = inet_addr(WL2KReport->Host); - destaddr.sin_port = htons(WL2KReport->WL2KPort); - - if (destaddr.sin_addr.s_addr == INADDR_NONE) - { - // Resolve name to address - - Debugprintf("Resolving %s", WL2KReport->Host); - HostEnt = gethostbyname (WL2KReport->Host); - - if (!HostEnt) - { - err = WSAGetLastError(); - - Debugprintf("Resolve Failed for %s %d %x", WL2KReport->Host, err, err); - return; // Resolve failed - } - - memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); - } - - // Allocate a Socket entry - - if (sock) - closesocket(sock); - - sock = socket(AF_INET, SOCK_STREAM, 0); - - if (sock == INVALID_SOCKET) - return; - -// ioctlsocket(sock, FIONBIO, ¶m); - - setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char FAR *)&bcopt, 4); - - destaddr.sin_family = AF_INET; - - if (sock == INVALID_SOCKET) - { - sock = 0; - return; - } - - setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); - - // Connect to Host - - if (connect(sock,(LPSOCKADDR) &destaddr, sizeof(destaddr)) != 0) - { - err=WSAGetLastError(); - - // - // Connect failed - // - - Debugprintf("Connect Failed"); - closesocket(sock); - sock = 0; - break; - } - - SameHost: - - Len = sprintf(Message, - "\"Callsign\":\"%s\"," - "\"BaseCallsign\":\"%s\"," - "\"GridSquare\":\"%s\"," - "\"Frequency\":%lld," - "\"Mode\":%d," - "\"Baud\":%d," - "\"Power\":%d," - "\"Height\":%d," - "\"Gain\":%d," - "\"Direction\":%d," - "\"Hours\":\"%s\"," - "\"ServiceCode\":\"%s\"", - - WL2KReport->RMSCall, WL2KReport->BaseCall, WL2KReport->GridSquare, - WL2KReport->Freq, WL2KReport->mode, WL2KReport->baud, WL2KReport->power, - WL2KReport->height, WL2KReport->gain, WL2KReport->direction, - WL2KReport->Times, WL2KReport->ServiceCode); - - Debugprintf("Sending %s", Message); - - SendHTTPRequest(sock, "/channel/add", Message, Len, NULL); - - - // Send Version Message - - - if (LastRMSCall == NULL || strcmp(WL2KReport->RMSCall, LastRMSCall) != 0) - { - int Len; - - LastRMSCall = WL2KReport->RMSCall; - - // "Callsign":"String","Program":"String","Version":"String","Comments":"String" - - Len = sprintf(Message, "\"Callsign\":\"%s\",\"Program\":\"BPQ32\"," - "\"Version\":\"%d.%d.%d.%d\",\"Comments\":\"Test Comment\"", - WL2KReport->RMSCall, Ver[0], Ver[1], Ver[2], Ver[3]); - - Debugprintf("Sending %s", Message); - - SendHTTPRequest(sock, "/version/add", Message, Len, NULL); - } - - WL2KReport = WL2KReport->Next; - } - - Sleep(100); - closesocket(sock); - sock = 0; - -} - -struct WL2KInfo * DecodeWL2KReportLine(char * buf) -{ - //06'', '', '', , , , , - // , , , '', , '' - - // WL2KREPORT service, api.winlink.org, 80, GM8BPQ, IO68VL, 00-23, 144800000, PKT1200, 10, 20, 5, 0, BPQTEST - - char * Context; - char * p_cmd; - char * param; - char errbuf[256]; - struct WL2KInfo * WL2KReport = zalloc(sizeof(struct WL2KInfo)); - char * ptr; - char Param[8][256]; - char * ptr1, * ptr2; - int n = 0; - - memset(Param, 0, 2048); - - strcpy(errbuf, buf); - - p_cmd = strtok_s(&buf[10], ", \t\n\r", &Context); - if (p_cmd == NULL) goto BadLine; - - strcpy(WL2KReport->ServiceCode, p_cmd); - - // Can default Host and Port, so cant use strtok for them - - ptr1 = Context; - - while (ptr1 && *ptr1 && n < 2) - { - while(ptr1 && *ptr1 && *ptr1 == ' ') - ptr1++; - - ptr2 = strchr(ptr1, ','); - if (ptr2) *ptr2++ = 0; - - strcpy(&Param[n][0], ptr1); - strlop(Param[n++], ' '); - ptr1 = ptr2; - - } - - if (n < 2) - goto BadLine; - - if (Param[1][0] == 0) - WL2KReport->WL2KPort = 80; // HTTP Interface - else - WL2KReport->WL2KPort = atoi(&Param[1][0]); - - if (Param[0][0] == 0) - WL2KReport->Host = _strdup("api.winlink.org"); - else - { - _strlwr(&Param[0][0]); - - if (strstr(&Param[0][0], "winlink.org")) - { - WL2KReport->WL2KPort = 80; // HTTP Interface - WL2KReport->Host = _strdup("api.winlink.org"); - } - else - WL2KReport->Host = _strdup(&Param[0][0]); - } - - Context = ptr1; - - p_cmd = strtok_s(NULL, ", \t\n\r", &Context); - if (p_cmd == NULL) goto BadLine; - - if (WL2KReport->WL2KPort == 0) goto BadLine; - - strcpy(WL2KReport->RMSCall, p_cmd); - strcpy(WL2KReport->BaseCall, p_cmd); - strlop(WL2KReport->BaseCall, '-'); // Remove any SSID - - strcpy(WL2KCall, WL2KReport->BaseCall); // For SYSOP Update - - p_cmd = strtok_s(NULL, " ,\t\n\r", &Context); - if (p_cmd == NULL) goto BadLine; - if (strlen(p_cmd) != 6) goto BadLine; - - strcpy(WL2KReport->GridSquare, p_cmd); - strcpy(WL2KLoc, p_cmd); - - p_cmd = strtok_s(NULL, " ,\t\n\r", &Context); - if (p_cmd == NULL) goto BadLine; - if (strlen(p_cmd) > 79) goto BadLine; - - // Convert any : in times to comma - - ptr = strchr(p_cmd, ':'); - - while (ptr) - { - *ptr = ','; - ptr = strchr(p_cmd, ':'); - } - - strcpy(WL2KReport->Times, p_cmd); - - p_cmd = strtok_s(NULL, " ,\t\n\r", &Context); - if (p_cmd == NULL) goto BadLine; - - WL2KReport->Freq = strtoll(p_cmd, NULL, 10); - - if (WL2KReport->Freq == 0) // Invalid - goto BadLine; - - param = strtok_s(NULL, " ,\t\n\r", &Context); - - // Mode Designator - one of - - // PKTnnnnnn - // WINMOR500 - // WINMOR1600 - // ROBUST - // P1 P12 P123 P1234 etc - - if (memcmp(param, "PKT", 3) == 0) - { - int Speed, Mode; - - Speed = atoi(¶m[3]); - - WL2KReport->baud = Speed; - - if (Speed <= 1200) - Mode = 0; // 1200 - else if (Speed <= 2400) - Mode = 1; // 2400 - else if (Speed <= 4800) - Mode = 2; // 4800 - else if (Speed <= 9600) - Mode = 3; // 9600 - else if (Speed <= 19200) - Mode = 4; // 19200 - else if (Speed <= 38400) - Mode = 5; // 38400 - else - Mode = 6; // >38400 - - WL2KReport->mode = Mode; - } - else if (_stricmp(param, "WINMOR500") == 0) - WL2KReport->mode = 21; - else if (_stricmp(param, "WINMOR1600") == 0) - WL2KReport->mode = 22; - else if (_stricmp(param, "ROBUST") == 0) - { - WL2KReport->mode = 30; - WL2KReport->baud = 600; - } - else if (_stricmp(param, "ARDOP200") == 0) - WL2KReport->mode = 40; - else if (_stricmp(param, "ARDOP500") == 0) - WL2KReport->mode = 41; - else if (_stricmp(param, "ARDOP1000") == 0) - WL2KReport->mode = 42; - else if (_stricmp(param, "ARDOP2000") == 0) - WL2KReport->mode = 43; - else if (_stricmp(param, "ARDOP2000FM") == 0) - WL2KReport->mode = 44; - else if (_stricmp(param, "P1") == 0) - WL2KReport->mode = 11; - else if (_stricmp(param, "P12") == 0) - WL2KReport->mode = 12; - else if (_stricmp(param, "P123") == 0) - WL2KReport->mode = 13; - else if (_stricmp(param, "P2") == 0) - WL2KReport->mode = 14; - else if (_stricmp(param, "P23") == 0) - WL2KReport->mode = 15; - else if (_stricmp(param, "P3") == 0) - WL2KReport->mode = 16; - else if (_stricmp(param, "P1234") == 0) - WL2KReport->mode = 17; - else if (_stricmp(param, "P234") == 0) - WL2KReport->mode = 18; - else if (_stricmp(param, "P34") == 0) - WL2KReport->mode = 19; - else if (_stricmp(param, "P4") == 0) - WL2KReport->mode = 20; - else if (_stricmp(param, "VARA") == 0) - WL2KReport->mode = 50; - else if (_stricmp(param, "VARA2300") == 0) - WL2KReport->mode = 50; - else if (_stricmp(param, "VARAFM") == 0) - WL2KReport->mode = 51; - else if (_stricmp(param, "VARAFM12") == 0) - WL2KReport->mode = 51; - else if (_stricmp(param, "VARAFM96") == 0) - WL2KReport->mode = 52; - else if (_stricmp(param, "VARA500") == 0) - WL2KReport->mode = 53; - else if (_stricmp(param, "VARA2750") == 0) - WL2KReport->mode = 54; - else - goto BadLine; - - param = strtok_s(NULL, " ,\t\n\r", &Context); - - // Optional Params - - WL2KReport->power = (param)? atoi(param) : 0; - param = strtok_s(NULL, " ,\t\n\r", &Context); - WL2KReport->height = (param)? atoi(param) : 0; - param = strtok_s(NULL, " ,\t\n\r", &Context); - WL2KReport->gain = (param)? atoi(param) : 0; - param = strtok_s(NULL, " ,\t\n\r", &Context); - WL2KReport->direction = (param)? atoi(param) : 0; - - WL2KTimer = 60; - - WL2KReport->Next = WL2KReports; - WL2KReports = WL2KReport; - - return WL2KReport; - -BadLine: - - WritetoConsole(" Bad config record "); - WritetoConsole(errbuf); - WritetoConsole("\r\n"); - - return 0; -} - -VOID UpdateMHSupport(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction, char * Loc, BOOL Report, BOOL Digis); - -VOID UpdateMHwithDigis(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction) -{ - UpdateMHSupport(TNC, Call, Mode, Direction, NULL, TRUE, TRUE); -} -VOID UpdateMHEx(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction, char * LOC, BOOL Report) -{ - UpdateMHSupport(TNC, Call, Mode, Direction, LOC, Report, FALSE); -} - -VOID UpdateMH(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction) -{ - UpdateMHSupport(TNC, Call, Mode, Direction, NULL, TRUE, FALSE); -} - -VOID UpdateMHSupport(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction, char * Loc, BOOL Report, BOOL Digis) -{ - PMHSTRUC MH = TNC->PortRecord->PORTCONTROL.PORTMHEARD; - PMHSTRUC MHBASE = MH; - UCHAR AXCall[72] = ""; - int i; - char * LOC, * LOCEND; - char ReportMode[20]; - char NoLOC[7] = ""; - double Freq; - char ReportFreq[350] = ""; - int OldCount = 0; - char ReportCall[16]; - - if (MH == 0) return; - - if (Digis) - { - // Call is an ax.25 digi string not a text call - - memcpy(AXCall, Call, 7 * 9); - ReportCall[ConvFromAX25(Call, ReportCall)] = 0; - - // if this is a UI frame with a locator or APRS position - // we could derive a position from it - - } - else - { - strcpy(ReportCall, Call); - ConvToAX25(Call, AXCall); - AXCall[6] |= 1; // Set End of address - } - - // Adjust freq to centre - -// if (Mode != ' ' && TNC->RIG->Valchar[0]) - if (TNC->RIG->Valchar[0]) - { - if (TNC->Hardware == H_UZ7HO) - { - // See if we have Center Freq Info - if (TNC->AGWInfo->CenterFreq) - { - Freq = atof(TNC->RIG->Valchar) + ((TNC->AGWInfo->CenterFreq * 1.0) / 1000000.0); - } -#ifdef WIN32 - else if (TNC->AGWInfo->hFreq) - { - char Centre[16]; - double ModemFreq; - - SendMessage(TNC->AGWInfo->hFreq, WM_GETTEXT, 15, (LPARAM)Centre); - - ModemFreq = atof(Centre); - - Freq = atof(TNC->RIG->Valchar) + (ModemFreq / 1000000); - } -#endif - else - Freq = atof(TNC->RIG->Valchar) + 0.0015; // Assume 1500 - } - else - - // Not UZ7HO or Linux - - Freq = atof(TNC->RIG->Valchar) + 0.0015; - - _gcvt(Freq, 9, ReportFreq); - } - - if (TNC->Hardware == H_ARDOP) - { - LOC = memchr(Call, '[', 20); - - if (LOC) - { - LOCEND = memchr(Call, ']', 30); - if (LOCEND) - { - LOC--; - *(LOC++) = 0; - *(LOCEND) = 0; - LOC++; - if (strlen(LOC) != 6 && strlen(LOC) != 0) - { - Debugprintf("Corrupt LOC %s %s", Call, LOC); - LOC = NoLOC; - } - goto NOLOC; - } - } - } - - else if (TNC->Hardware != H_WINMOR) // Only WINMOR has a locator - { - LOC = NoLOC; - goto NOLOC; - } - - LOC = memchr(Call, '(', 20); - - if (LOC) - { - LOCEND = memchr(Call, ')', 30); - if (LOCEND) - { - LOC--; - *(LOC++) = 0; - *(LOCEND) = 0; - LOC++; - if (strlen(LOC) != 6 && strlen(LOC) != 0) - { - Debugprintf("Corrupt LOC %s %s", Call, LOC); - LOC = NoLOC; - } - } - } - else - LOC = NoLOC; - -NOLOC: - - if (Loc) - LOC = Loc; // Supplied Locator overrides - - for (i = 0; i < MHENTRIES; i++) - { - if (Mode == ' ' || Mode == '*') // Packet - { - if ((MH->MHCALL[0] == 0) || ((memcmp(AXCall, MH->MHCALL, 7) == 0) && MH->MHDIGI == Mode)) // Spare or our entry - { - OldCount = MH->MHCOUNT; - goto DoMove; - } - } - else - { - if ((MH->MHCALL[0] == 0) || ((memcmp(AXCall, MH->MHCALL, 7) == 0) && - MH->MHDIGI == Mode && strcmp(MH->MHFreq, ReportFreq) == 0)) // Spare or our entry - { - OldCount = MH->MHCOUNT; - goto DoMove; - } - } - MH++; - } - - // TABLE FULL AND ENTRY NOT FOUND - MOVE DOWN ONE, AND ADD TO TOP - - i = MHENTRIES - 1; - - // Move others down and add at front -DoMove: - - if (i != 0) // First - memmove(MHBASE + 1, MHBASE, i * sizeof(MHSTRUC)); - -// memcpy (MHBASE->MHCALL, Buffer->ORIGIN, 7 * 9); - memcpy (MHBASE->MHCALL, AXCall, 7 * 9); // Save Digis - MHBASE->MHDIGI = Mode; - MHBASE->MHTIME = time(NULL); - MHBASE->MHCOUNT = ++OldCount; - - memcpy(MHBASE->MHLocator, LOC, 6); - strcpy(MHBASE->MHFreq, ReportFreq); - - // Report to NodeMap - - if (Report == FALSE) - return; - - if (Mode == '*') - return; // Digi'ed Packet - - if (Mode == ' ') // Packet Data - { - if (TNC->PktUpdateMap == 1) - Mode = '!'; - else - return; - } - - ReportMode[0] = TNC->Hardware + '@'; - ReportMode[1] = Mode; - if (TNC->Hardware == H_HAL) - ReportMode[2] = TNC->CurrentMode; - else - ReportMode[2] = (TNC->RIG->CurrentBandWidth) ? TNC->RIG->CurrentBandWidth : '?'; - ReportMode[3] = Direction; - ReportMode[4] = 0; - - // If no position see if we have an APRS posn - - if (LOC[0] == 0) - { - double Lat, Lon; - - if (GetPosnFromAPRS(ReportCall, &Lat, &Lon) && Lat != 0.0) - { - ToLOC(Lat, Lon, LOC); - } - } - - SendMH(TNC, ReportCall, ReportFreq, LOC, ReportMode); - - return; -} - -VOID CloseDriverWindow(int port) -{ -#ifndef LINBPQ - - struct TNCINFO * TNC; - - TNC = TNCInfo[port]; - if (TNC == NULL) - return; - - if (TNC->hDlg == NULL) - return; - - PostMessage(TNC->hDlg, WM_CLOSE,0,0); -// DestroyWindow(TNC->hDlg); - - TNC->hDlg = NULL; -#endif - return; -} - -VOID SaveWindowPos(int port) -{ -#ifndef LINBPQ - - struct TNCINFO * TNC; - char Key[80]; - - TNC = TNCInfo[port]; - - if (TNC == NULL) - return; - - if (TNC->hDlg == NULL) - return; - - sprintf(Key, "PACTOR\\PORT%d", port); - - SaveMDIWindowPos(TNC->hDlg, Key, "Size", TNC->Minimized); - -#endif - return; -} - -VOID ShowTraffic(struct TNCINFO * TNC) -{ - char Status[80]; - - sprintf(Status, "RX %d TX %d ACKED %d ", - TNC->Streams[0].bytesRXed, TNC->Streams[0].bytesTXed, TNC->Streams[0].BytesAcked); -#ifndef LINBPQ - SetDlgItemText(TNC->hDlg, IDC_TRAFFIC, Status); -#endif -} - -BOOL InterlockedCheckBusy(struct TNCINFO * ThisTNC) -{ - // See if this port, or any interlocked ports are reporting channel busy - - struct TNCINFO * TNC; - int i; - int rxInterlock = ThisTNC->RXRadio; - int txInterlock = ThisTNC->TXRadio; - - if (ThisTNC->Busy) - return TRUE; // Our port is busy - - if (rxInterlock == 0 && txInterlock == 0) - return ThisTNC->Busy; // No Interlock - - for (i=1; i <= MAXBPQPORTS; i++) - { - TNC = TNCInfo[i]; - - if (TNC == NULL) - continue; - - if (TNC == ThisTNC) - continue; - - if (rxInterlock == TNC->RXRadio || txInterlock == TNC->TXRadio) // Same Group - if (TNC->Busy) - return TRUE; // Interlocked port is busy - - } - return FALSE; // None Busy -} - -char ChallengeResponse[13]; - -char * GetChallengeResponse(char * Call, char * ChallengeString) -{ - // Generates a response to the CMS challenge string... - - long long Challenge = _atoi64(ChallengeString); - long long CallSum = 0; - long long Mask; - long long Response; - long long XX = 1065484730; - - char CallCopy[10]; - UINT i; - - - if (Challenge == 0) - return "000000000000"; - -// Calculate Mask from Callsign - - memcpy(CallCopy, Call, 10); - strlop(CallCopy, '-'); - strlop(CallCopy, ' '); - - for (i = 0; i < strlen(CallCopy); i++) - { - CallSum += CallCopy[i]; - } - - Mask = CallSum + CallSum * 4963 + CallSum * 782386; - - Response = (Challenge % 930249781); - Response ^= Mask; - - sprintf(ChallengeResponse, "%012lld", Response); - - return ChallengeResponse; -} - -SOCKET OpenWL2KHTTPSock() -{ - SOCKET sock = 0; - struct sockaddr_in destaddr; - struct sockaddr_in sinx; - int addrlen=sizeof(sinx); - struct hostent * HostEnt; - int err; - u_long param=1; - BOOL bcopt=TRUE; - - destaddr.sin_family = AF_INET; - destaddr.sin_port = htons(80); - - // Resolve name to address - - HostEnt = gethostbyname ("api.winlink.org"); - - if (!HostEnt) - { - err = WSAGetLastError(); - - Debugprintf("Resolve Failed for %s %d %x", "api.winlink.org", err, err); - return 0 ; // Resolve failed - } - - memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); - - // Allocate a Socket entry - - sock = socket(AF_INET,SOCK_STREAM,0); - - if (sock == INVALID_SOCKET) - return 0; - - setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); - - sinx.sin_family = AF_INET; - sinx.sin_addr.s_addr = INADDR_ANY; - sinx.sin_port = 0; - - if (bind(sock, (struct sockaddr *) &sinx, addrlen) != 0 ) - return FALSE; - - if (connect(sock,(struct sockaddr *) &destaddr, sizeof(destaddr)) != 0) - { - err=WSAGetLastError(); - closesocket(sock); - return 0; - } - - return sock; -} - -BOOL GetWL2KSYSOPInfo(char * Call, char * _REPLYBUFFER) -{ - SOCKET sock = 0; - int Len; - char Message[1000]; - - sock = OpenWL2KHTTPSock(); - - if (sock == 0) - return 0; - - // {"Callsign":"String"} - - Len = sprintf(Message, "\"Callsign\":\"%s\"", Call); - - SendHTTPRequest(sock, "/sysop/get", Message, Len, _REPLYBUFFER); - - closesocket(sock); - - return _REPLYBUFFER[0]; -} - -BOOL UpdateWL2KSYSOPInfo(char * Call, char * SQL) -{ - SOCKET sock = 0; - struct sockaddr_in destaddr; - struct sockaddr_in sinx; - int len = 100; - int addrlen=sizeof(sinx); - struct hostent * HostEnt; - int err; - u_long param=1; - BOOL bcopt=TRUE; - char Buffer[1000]; - char SendBuffer[1000]; - - destaddr.sin_family = AF_INET; - destaddr.sin_addr.s_addr = inet_addr("api.winlink.org"); - destaddr.sin_port = htons(80); - - HostEnt = gethostbyname ("api.winlink.org"); - - if (!HostEnt) - { - err = WSAGetLastError(); - - Debugprintf("Resolve Failed for %s %d %x", "api.winlink.org", err, err); - return 0 ; // Resolve failed - } - - memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); - - // Allocate a Socket entry - - sock = socket(AF_INET,SOCK_STREAM,0); - - if (sock == INVALID_SOCKET) - return 0; - - setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); - - sinx.sin_family = AF_INET; - sinx.sin_addr.s_addr = INADDR_ANY; - sinx.sin_port = 0; - - if (bind(sock, (struct sockaddr *) &sinx, addrlen) != 0 ) - return FALSE; - - if (connect(sock,(struct sockaddr *) &destaddr, sizeof(destaddr)) != 0) - { - err=WSAGetLastError(); - closesocket(sock); - return 0; - } - - len = recv(sock, &Buffer[0], len, 0); - - len = sprintf(SendBuffer, "02%07d%-12s%s%s", (int)strlen(SQL), Call, GetChallengeResponse(Call, Buffer), SQL); - - send(sock, SendBuffer, len, 0); - - len = 1000; - - len = recv(sock, &Buffer[0], len, 0); - - Buffer[len] = 0; - Debugprintf(Buffer); - - closesocket(sock); - - return TRUE; - -} -// http://server.winlink.org:8085/csv/reply/ChannelList?Modes=40,41,42,43,44&ServiceCodes=BPQTEST,PUBLIC - -// Process config lines that are common to a number of HF modes - -static char ** SeparateMultiString(char * MultiString) -{ - char ** Value; - int Count = 0; - char * ptr, * ptr1; - - // Convert to string array - - Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values - Value[0] = NULL; - - strlop(MultiString, 13); - ptr = MultiString; - - while (ptr && strlen(ptr)) - { - ptr1 = strchr(ptr, '|'); - - if (ptr1) - *(ptr1++) = 0; - - if (strlen(ptr)) - { - Value = realloc(Value, (Count+2) * sizeof(void *)); - Value[Count++] = _strdup(ptr); - } - ptr = ptr1; - } - - Value[Count] = NULL; - return Value; -} - - - - -extern int nextDummyInterlock; - -int standardParams(struct TNCINFO * TNC, char * buf) -{ - if (_memicmp(buf, "WL2KREPORT", 10) == 0) - TNC->WL2K = DecodeWL2KReportLine(buf); - else if (_memicmp(buf, "SESSIONTIMELIMIT", 16) == 0) - TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit = atoi(&buf[17]) * 60; - else if (_memicmp(buf, "ATTACHTIMELIMIT", 15) == 0) - TNC->AttachTimeLimit = atoi(&buf[16]) * 60; - else if (_memicmp(buf, "BUSYHOLD", 8) == 0) // Hold Time for Busy Detect - TNC->BusyHold = atoi(&buf[8]); - else if (_memicmp(buf, "BUSYWAIT", 8) == 0) // Wait time before failing connect if busy - TNC->BusyWait = atoi(&buf[8]); - else if (_memicmp(buf, "AUTOSTARTDELAY", 14) == 0) // Time to wait for TNC to start - TNC->AutoStartDelay = atoi(&buf[15]); - else if (_memicmp(buf, "DEFAULTRADIOCOMMAND", 19) == 0) - TNC->DefaultRadioCmd = _strdup(&buf[20]); - else if (_memicmp(buf, "MYCALLS", 7) == 0) - { - TNC->LISTENCALLS = _strdup(&buf[8]); - strlop(TNC->LISTENCALLS, '\r'); - } - else if (_memicmp(buf, "NRNEIGHBOUR", 11) == 0) - TNC->NRNeighbour = _strdup(&buf[12]); - else if (_memicmp(buf, "MAXCONREQ", 9) == 0) // Hold Time for Busy Detect - TNC->MaxConReq = atoi(&buf[9]); - - else if (_memicmp(buf, "FREQUENCY", 9) == 0) - TNC->Frequency = _strdup(&buf[10]); - else if (_memicmp(buf, "SendTandRtoRelay", 16) == 0) - TNC->SendTandRtoRelay = atoi(&buf[17]); - else if (_memicmp(buf, "Radio", 5) == 0) // Rig Control RADIO for TX amd RX (Equiv to INTERLOCK) - TNC->RXRadio = TNC->TXRadio = atoi(&buf[6]); - else if (_memicmp(buf, "TXRadio", 7) == 0) // Rig Control RADIO for TX - TNC->TXRadio = atoi(&buf[8]); - else if (_memicmp(buf, "RXRadio", 7) == 0) // Rig Control RADIO for RXFRETRIES - TNC->RXRadio = atoi(&buf[8]); - else if (_memicmp(buf, "TXFreq", 6) == 0) // For PTT Sets Freq mode - TNC->TXFreq = strtoll(&buf[7], NULL, 10); - else if (_memicmp(buf, "DefaultTXFreq", 13) == 0) // Set at end of session - TNC->DefaultTXFreq = atof(&buf[14]); - else if (_memicmp(buf, "DefaultRXFreq", 13) == 0) // Set at end of session - TNC->DefaultRXFreq = atof(&buf[14]); - else if (_memicmp(buf, "ActiveTXFreq", 12) == 0) // Set at start of session - TNC->ActiveTXFreq = atof(&buf[13]); - else if (_memicmp(buf, "ActiveRXFreq", 12) == 0) // Set at start of session - TNC->ActiveRXFreq = atof(&buf[13]); - else if (_memicmp(buf, "DisconnectScript", 16) == 0) // Set at start of session - TNC->DisconnectScript = SeparateMultiString(&buf[17]); - else if (_memicmp(buf, "PTTONHEX", 8) == 0) - { - // Hex String to use for PTT on for this port - - char * ptr1 = &buf[9]; - char * ptr2 = TNC->PTTOn; - int i, j, len; - - _strupr(ptr1); - - TNC->PTTOnLen = len = strlen(ptr1) / 2; - - if (len < 240) - { - while ((len--) > 0) - { - i = *(ptr1++); - i -= '0'; - if (i > 9) - i -= 7; - - j = i << 4; - - i = *(ptr1++); - i -= '0'; - if (i > 9) - i -= 7; - - *(ptr2++) = j | i; - } - } - } - else if (_memicmp(buf, "PTTOFFHEX", 9) == 0) - { - // Hex String to use for PTT off - - char * ptr = &buf[10]; - char * ptr2 = TNC->PTTOff; - int i, j, len; - - _strupr(ptr); - - TNC->PTTOffLen = len = strlen(ptr) / 2; - - if (len < 240) - { - while ((len--) > 0) - { - i = *(ptr++); - i -= '0'; - if (i > 9) - i -= 7; - - j = i << 4; - - i = *(ptr++); - i -= '0'; - if (i > 9) - i -= 7; - - *(ptr2++) = j | i; - } - } - } - else - return FALSE; - - return TRUE; -} - -void DecodePTTString(struct TNCINFO * TNC, char * ptr) -{ - if (_stricmp(ptr, "CI-V") == 0) - TNC->PTTMode = PTTCI_V; - else if (_stricmp(ptr, "CAT") == 0) - TNC->PTTMode = PTTCI_V; - else if (_stricmp(ptr, "RTS") == 0) - TNC->PTTMode = PTTRTS; - else if (_stricmp(ptr, "DTR") == 0) - TNC->PTTMode = PTTDTR; - else if (_stricmp(ptr, "DTRRTS") == 0) - TNC->PTTMode = PTTDTR | PTTRTS; - else if (_stricmp(ptr, "CM108") == 0) - TNC->PTTMode = PTTCM108; - else if (_stricmp(ptr, "HAMLIB") == 0) - TNC->PTTMode = PTTHAMLIB; - else if (_stricmp(ptr, "FLRIG") == 0) - TNC->PTTMode = PTTFLRIG; -} - -extern SOCKET ReportSocket; -extern char LOCATOR[80]; -extern char ReportDest[7]; -extern int NumberofPorts; -extern struct RIGPORTINFO * PORTInfo[34]; // Records are Malloc'd - -time_t LastModeReportTime; -time_t LastFreqReportTime; - -VOID SendReportMsg(char * buff, int txlen); - -void sendModeReport() -{ - // if TNC is connected send mode and frequencies to Node Map as a MODE record - // Are we better sending scan info as a separate record ?? - - // MODE Port, HWType, Interlock - - struct PORTCONTROL * PORT = PORTTABLE; - - struct TNCINFO * TNC; - MESSAGE AXMSG; - PMESSAGE AXPTR = &AXMSG; - char Msg[300] = "MODE "; - int i, Len = 5; - - if ((CurrentSecs - LastModeReportTime) < 900) // Every 15 Mins - return; - - LastModeReportTime = CurrentSecs; - - for (i = 0; i < NUMBEROFPORTS; i++) - { - if (PORT->PROTOCOL == 10) - { - PEXTPORTDATA PORTVEC = (PEXTPORTDATA)PORT; - TNC = TNCInfo[PORT->PORTNUMBER]; - PORT = PORT->PORTPOINTER; - - if (TNC == NULL) - continue; - - if (TNC->CONNECTED == 0 && TNC->TNCOK == 0) - continue; - - if (ReportSocket == 0 || LOCATOR[0] == 0) - continue; - - if (TNC->Frequency) - Len += sprintf(&Msg[Len], "%d,%d,%d,%.6f/", TNC->Port, TNC->Hardware, TNC->RXRadio, atof(TNC->Frequency)); - else - Len += sprintf(&Msg[Len], "%d,%d,%d/", TNC->Port, TNC->Hardware, TNC->RXRadio); - - if (Len > 240) - break; - } - else - PORT = PORT->PORTPOINTER; - } - - if (Len == 5) - return; // Nothing to send - - // Block includes the Msg Header (7 bytes), Len Does not! - - memcpy(AXPTR->DEST, ReportDest, 7); - memcpy(AXPTR->ORIGIN, MYCALL, 7); - AXPTR->DEST[6] &= 0x7e; // Clear End of Call - AXPTR->DEST[6] |= 0x80; // set Command Bit - - AXPTR->ORIGIN[6] |= 1; // Set End of Call - AXPTR->CTL = 3; //UI - AXPTR->PID = 0xf0; - memcpy(AXPTR->L2DATA, Msg, Len); - - SendReportMsg((char *)&AXMSG.DEST, Len + 16) ; -} - -void sendFreqReport(char * From) -{ - // Send info from rig control or Port Frequency info to Node Map for Mode page. - - MESSAGE AXMSG; - PMESSAGE AXPTR = &AXMSG; - char Msg[300] = "FREQ "; - int i, Len = 5, p; - - struct RIGPORTINFO * RIGPORT; - struct RIGINFO * RIG; - struct TimeScan * Band; - struct PORTCONTROL * PORT = PORTTABLE; - struct TNCINFO * TNC; - - if ((CurrentSecs - LastFreqReportTime) < 7200) // Every 2 Hours - return; - - LastFreqReportTime = CurrentSecs; - - for (p = 0; p < NumberofPorts; p++) - { - RIGPORT = PORTInfo[p]; - - for (i = 0; i < RIGPORT->ConfiguredRigs; i++) - { - int j = 1, k = 0; - - RIG = &RIGPORT->Rigs[i]; - - if (RIG->reportFreqs) - { - Len += sprintf(&Msg[Len], "%d/00:00/%s,\\|",RIG->Interlock,RIG->reportFreqs); - } - else - { - if (RIG->TimeBands) - { - Len += sprintf(&Msg[Len], "%d/",RIG->Interlock); - while (RIG->TimeBands[j]) - { - Band = RIG->TimeBands[j]; - k = 0; - - if (Band->Scanlist[0]) - { - Len += sprintf(&Msg[Len], "%02d:%02d/", Band->Start / 3600, Band->Start % 3600); - - while (Band->Scanlist[k]) - { - Len += sprintf(&Msg[Len],"%.0f,", Band->Scanlist[k]->Freq + RIG->rxOffset); - k++; - } - Len += sprintf(&Msg[Len], "\\"); - } - j++; - } - Len += sprintf(&Msg[Len], "|"); - } - } - } - } - - // Look for Port freq info - - for (i = 0; i < NUMBEROFPORTS; i++) - { - if (PORT->PROTOCOL == 10) - { - PEXTPORTDATA PORTVEC = (PEXTPORTDATA)PORT; - TNC = TNCInfo[PORT->PORTNUMBER]; - PORT = PORT->PORTPOINTER; - - if (TNC == NULL) - continue; - - if (TNC->Frequency == NULL) - continue; - - if (TNC->RIG->TimeBands && TNC->RIG->TimeBands[1]->Scanlist) - continue; // Have freq info from Rigcontrol - - if (TNC->RXRadio == 0) // Replace with dummy - TNC->RXRadio = nextDummyInterlock++; - - // Use negative port no instead of interlock group - - Len += sprintf(&Msg[Len], "%d/00:00/%.0f|", TNC->RXRadio, atof(TNC->Frequency) * 1000000.0); - } - else - PORT = PORT->PORTPOINTER; - } - - if (Len == 5) - return; // Nothing to send - - // Block includes the Msg Header (7 bytes), Len Does not! - - memcpy(AXPTR->DEST, ReportDest, 7); - memcpy(AXPTR->ORIGIN, MYCALL, 7); - AXPTR->DEST[6] &= 0x7e; // Clear End of Call - AXPTR->DEST[6] |= 0x80; // set Command Bit - - AXPTR->ORIGIN[6] |= 1; // Set End of Call - AXPTR->CTL = 3; //UI - AXPTR->PID = 0xf0; - memcpy(AXPTR->L2DATA, Msg, Len); - - SendReportMsg((char *)&AXMSG.DEST, Len + 16) ; -} - - +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + + +#pragma data_seg("_BPQDATA") + +#define _CRT_SECURE_NO_DEPRECATE + +#include + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + + +#include "kernelresource.h" +#include "cheaders.h" +#include "tncinfo.h" +#ifndef LINBPQ +#include +#endif +//#include +#include "bpq32.h" +#include "adif.h" + + +HANDLE hInstance; +extern HBRUSH bgBrush; +extern HWND ClientWnd, FrameWnd; +extern int OffsetH, OffsetW; + +extern HMENU hMainFrameMenu; +extern HMENU hBaseMenu; +extern HANDLE hInstance; + +extern HKEY REGTREE; + +extern int Ver[]; + + +int KillTNC(struct TNCINFO * TNC); +int RestartTNC(struct TNCINFO * TNC); + +char * GetChallengeResponse(char * Call, char * ChallengeString); + +VOID __cdecl Debugprintf(const char * format, ...); +int FromLOC(char * Locator, double * pLat, double * pLon); +BOOL ToLOC(double Lat, double Lon , char * Locator); + +int GetPosnFromAPRS(char * Call, double * Lat, double * Lon); +char * stristr (char *ch1, char *ch2); + + +static RECT Rect; + +#define WSA_ACCEPT WM_USER + 1 +#define WSA_DATA WM_USER + 2 +#define WSA_CONNECT WM_USER + 3 + +int Winmor_Socket_Data(int sock, int error, int eventcode); + +struct WL2KInfo * WL2KReports; + +int WL2KTimer = 0; + +int ModetoBaud[31] = {0,0,0,0,0,0,0,0,0,0,0, // 0 = 10 + 200,600,3200,600,3200,3200, // 11 - 16 + 0,0,0,0,0,0,0,0,0,0,0,0,0,600}; // 17 - 30 + +extern char HFCTEXT[]; +extern int HFCTEXTLEN; + + +extern char WL2KCall[10]; +extern char WL2KLoc[7]; + + +VOID MoveWindows(struct TNCINFO * TNC) +{ +#ifndef LINBPQ + RECT rcClient; + int ClientHeight, ClientWidth; + + GetClientRect(TNC->hDlg, &rcClient); + + ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + + if (TNC->hMonitor) + MoveWindow(TNC->hMonitor,2 , TNC->RigControlRow + 3, ClientWidth-4, ClientHeight - (TNC->RigControlRow + 3), TRUE); +#endif +} + +char * Config; +static char * ptr1, * ptr2; + +#ifndef LINBPQ + +LRESULT CALLBACK PacWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + MINMAXINFO * mmi; + + int i; + struct TNCINFO * TNC; + + HKEY hKey; + char Key[80]; + int retCode, disp; + + for (i=0; i<41; i++) + { + TNC = TNCInfo[i]; + if (TNC == NULL) + continue; + + if (TNC->hDlg == hWnd) + break; + } + + if (TNC == NULL) + return DefMDIChildProc(hWnd, message, wParam, lParam); + + switch (message) { + + case WM_CREATE: + + break; + + case WM_PAINT: + +// hdc = BeginPaint (hWnd, &ps); + +// SelectObject( hdc, hFont) ; + +// EndPaint (hWnd, &ps); +// +// wParam = hdc; + + break; + + + case WM_GETMINMAXINFO: + + if (TNC->ClientHeight) + { + mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = TNC->ClientWidth; + mmi->ptMaxSize.y = TNC->ClientHeight; + mmi->ptMaxTrackSize.x = TNC->ClientWidth; + mmi->ptMaxTrackSize.y = TNC->ClientHeight; + } + + break; + + + case WM_MDIACTIVATE: + { + + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + + if (TNC->hMenu) + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)TNC->hMenu, "Actions"); + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); + +// SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) TNC->hMenu, (LPARAM) TNC->hWndMenu); + } + else + { + // Deactivate + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + + // call DrawMenuBar after the menu items are set + DrawMenuBar(FrameWnd); + + return DefMDIChildProc(hWnd, message, wParam, lParam); + } + + + + case WM_INITMENUPOPUP: + + if (wParam == (WPARAM)TNC->hMenu) + { + if (TNC->ProgramPath) + { + if (strstr(TNC->ProgramPath, " TNC") || strstr(TNC->ProgramPath, "ARDOP") + || strstr(TNC->ProgramPath, "VARA") || stristr(TNC->ProgramPath, "FREEDATA")) + { + EnableMenuItem(TNC->hMenu, WINMOR_RESTART, MF_BYCOMMAND | MF_ENABLED); + EnableMenuItem(TNC->hMenu, WINMOR_KILL, MF_BYCOMMAND | MF_ENABLED); + + break; + } + } + EnableMenuItem(TNC->hMenu, WINMOR_RESTART, MF_BYCOMMAND | MF_GRAYED); + EnableMenuItem(TNC->hMenu, WINMOR_KILL, MF_BYCOMMAND | MF_GRAYED); + } + + break; + + case WM_COMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + case WINMOR_KILL: + + TNC->DontRestart = TRUE; + KillTNC(TNC); + break; + + case WINMOR_RESTART: + + TNC->DontRestart = FALSE; + KillTNC(TNC); + RestartTNC(TNC); + break; + + case WINMOR_RESTARTAFTERFAILURE: + + TNC->RestartAfterFailure = !TNC->RestartAfterFailure; + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\PACTOR\\PORT%d", TNC->Port); + + retCode = RegCreateKeyEx(REGTREE, Key, 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + RegSetValueEx(hKey,"TNC->RestartAfterFailure",0,REG_DWORD,(BYTE *)&TNC->RestartAfterFailure, 4); + RegCloseKey(hKey); + } + break; + + case ARDOP_ABORT: + + if (TNC->ForcedCloseProc) + TNC->ForcedCloseProc(TNC, 0); + + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_SIZING: + case WM_SIZE: + + MoveWindows(TNC); + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + + case SC_RESTORE: + + TNC->Minimized = FALSE; + break; + + case SC_MINIMIZE: + + TNC->Minimized = TRUE; + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + case WM_HSCROLL: + { + char value[16]; + + switch (LOWORD(wParam)) + { + case TB_ENDTRACK: + case TB_THUMBTRACK: + + TNC->TXOffset = SendMessage(TNC->xIDC_TXTUNE, TBM_GETPOS, 0, 0); + sprintf(value, "%d", TNC->TXOffset); + MySetWindowText(TNC->xIDC_TXTUNEVAL, value); + + break; + } + + default: + break; + } + case WM_DESTROY: + + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); +} +#endif + +BOOL CreatePactorWindow(struct TNCINFO * TNC, char * ClassName, char * WindowTitle, int RigControlRow, WNDPROC WndProc, int Width, int Height, + VOID ForcedCloseProc(struct TNCINFO * TNC, int Stream)) +{ +#ifdef LINBPQ + return FALSE; +#else + WNDCLASS wc; + char Title[80]; + int retCode, Type, Vallen; + HKEY hKey=0; + char Key[80]; + char Size[80]; + int Top, Left; + HANDLE hDlg = 0; + static int LP = 1235; + + if (TNC->hDlg) + { + ShowWindow(TNC->hDlg, SW_SHOWNORMAL); + SetForegroundWindow(TNC->hDlg); + return FALSE; // Already open + } + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wc.lpfnWndProc = WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + wc.lpszMenuName = NULL; + wc.lpszClassName = ClassName; + + RegisterClass(&wc); + +// if (TNC->Hardware == H_WINMOR || TNC->Hardware == H_TELNET ||TNC->Hardware == H_ARDOP || +// TNC->Hardware == H_V4 || TNC->Hardware == H_FLDIGI || TNC->Hardware == H_UIARQ || TNC->Hardware == H_VARA) + if (TNC->PortRecord) + sprintf(Title, "%s Status - Port %d %s", WindowTitle, TNC->Port, TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION); + else + sprintf(Title, "Rigcontrol"); + + if (TNC->Hardware == H_MPSK) + sprintf(Title, "Rigcontrol for MultiPSK Port %d", TNC->Port); + + TNC->hDlg = hDlg = CreateMDIWindow(ClassName, Title, 0, + 0, 0, Width, Height, ClientWnd, hInstance, ++LP); + + // CreateDialog(hInstance,ClassName,0,NULL); + + Rect.top = 100; + Rect.left = 20; + Rect.right = Width + 20; + Rect.bottom = Height + 100; + + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\PACTOR\\PORT%d", TNC->Port); + + retCode = RegOpenKeyEx (REGTREE, Key, 0, KEY_QUERY_VALUE, &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen=80; + + retCode = RegQueryValueEx(hKey,"Size",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + { + sscanf(Size,"%d,%d,%d,%d,%d",&Rect.left,&Rect.right,&Rect.top,&Rect.bottom, &TNC->Minimized); + + if (Rect.top < - 500 || Rect.left < - 500) + { + Rect.left = 0; + Rect.top = 0; + Rect.right = 600; + Rect.bottom = 400; + } + + if (Rect.top < OffsetH) + { + int Error = OffsetH - Rect.top; + Rect.top += Error; + Rect.bottom += Error; + } + } + + if (TNC->Hardware == H_WINMOR || TNC->Hardware == H_ARDOP|| TNC->Hardware == H_VARA) + retCode = RegQueryValueEx(hKey,"TNC->RestartAfterFailure",0, + (ULONG *)&Type,(UCHAR *)&TNC->RestartAfterFailure,(ULONG *)&Vallen); + + RegCloseKey(hKey); + } + + Top = Rect.top; + Left = Rect.left; + +// GetWindowRect(hDlg, &Rect); // Get the real size + + MoveWindow(hDlg, Left - (OffsetW /2), Top - OffsetH, Rect.right - Rect.left, Rect.bottom - Rect.top, TRUE); + + if (TNC->Minimized) + ShowWindow(hDlg, SW_SHOWMINIMIZED); + else + ShowWindow(hDlg, SW_RESTORE); + + TNC->RigControlRow = RigControlRow; + + SetWindowText(TNC->xIDC_TNCSTATE, "Free"); + + TNC->ForcedCloseProc = ForcedCloseProc; + + return TRUE; +#endif +} + + +// WL2K Reporting Code. + +static SOCKADDR_IN sinx; + + +VOID SendReporttoWL2KThread(void * unused); +VOID SendHTTPReporttoWL2KThread(void * unused); + +VOID CheckWL2KReportTimer() +{ + if (WL2KReports == NULL) + return; // Shouldn't happen! + + WL2KTimer--; + + if (WL2KTimer != 0) + return; + +#ifdef WIN32 + WL2KTimer = 2 * 32910; // Every 2 Hours - PC Tick is a bit slow +#else + WL2KTimer = 2 * 36000; // Every 2 Hours +#endif + + if (CheckAppl(NULL, "RMS ") == NULL) + if (CheckAppl(NULL, "RELAY ") == NULL) + return; + + _beginthread(SendHTTPReporttoWL2KThread, 0, 0); + + return; +} + +static char HeaderTemplate[] = "POST %s HTTP/1.1\r\n" + "Accept: application/json\r\n" +// "Accept-Encoding: gzip,deflate,gzip, deflate\r\n" + "Content-Type: application/json\r\n" + "Host: %s:%d\r\n" + "Content-Length: %d\r\n" + //r\nUser-Agent: BPQ32(G8BPQ)\r\n" +// "Expect: 100-continue\r\n" + "\r\n{%s}"; + +char Missing[] = "** Missing **"; + +VOID GetJSONValue(char * _REPLYBUFFER, char * Name, char * Value) +{ + char * ptr1, * ptr2; + + strcpy(Value, Missing); + + ptr1 = strstr(_REPLYBUFFER, Name); + + if (ptr1 == 0) + return; + + ptr1 += (strlen(Name) + 1); + + ptr2 = strchr(ptr1, '"'); + + if (ptr2) + { + size_t ValLen = ptr2 - ptr1; + memcpy(Value, ptr1, ValLen); + Value[ValLen] = 0; + } + + return; +} + + +// Send Winlink Session Record + +extern char LOC[7]; +extern char TextVerstring[50]; + +double Distance(double laa, double loa, double lah, double loh, BOOL KM); +double Bearing(double lat2, double lon2, double lat1, double lon1); +VOID SendHTTPRequest(SOCKET sock, char * Request, char * Params, int Len, char * Return); +SOCKET OpenWL2KHTTPSock(); + + + +struct WL2KMode +{ + int Mode; + char * WL2KString; + char * ADIFString; + char * BPQString; +}; + +struct WL2KMode WL2KModeList[] = +{ + {0,"Packet 1200"}, + {1,"Packet 2400"}, + {2, "Packet 4800"}, + {3, "Packet 9600"}, + {4, "Packet 19200"}, + {5, "Packet 38400"}, + {11, "Pactor 1"}, + {12, "Pactor 1,2"}, + {13, "Pactor 1,2,3"}, + {14, "Pactor 2"}, + {15, "Pactor 2,3"}, + {16, "Pactor 3"}, + {17, "Pactor 1,2,3,4"}, + {18, "Pactor 2,3,4"}, + {19, "Pactor 3,4"}, + {20, "Pactor 4"}, + {21, "WINMOR 500"}, + {22, "WINMOR 1600"}, + {30, "Robust Packet"}, + {40, "ARDOP 200"}, + {41, "ARDOP 500"}, + {42, "ARDOP 1000"}, + {43, "ARDOP 2000"}, + {44, "ARDOP 2000 FM"}, + {50, "VARA"}, + {51, "VARA FM"}, + {52, "VARA FM WIDE"}, + {53, "VARA 500"} +}; + +char WL2KModes [55][18] = { + "Packet 1200", "Packet 2400", "Packet 4800", "Packet 9600", "Packet 19200", "Packet 38400", "High Speed Packet", "", "", "", "", + "Pactor 1", "Pactor", "Pactor", "Pactor 2", "Pactor", "Pactor 3", "Pactor", "Pactor", "Pactor", "Pactor 4", // 11 - 20 + "Winmor 500", "Winmor 1600", "", "", "", "", "", "", "", // 21 - 29 + "Robust Packet", "", "", "", "", "", "", "", "", "", // 30 - 39 + "ARDOP 200", "ARDOP 500", "ARDOP 1000", "ARDOP 2000", "ARDOP 2000 FM", "", "", "", "", "", // 40 - 49 + "VARA", "VARA FM", "VARA FM WIDE", "VARA 500", "VARA 2750"}; + + +VOID SendWL2KSessionRecordThread(void * param) +{ + SOCKET sock; + char Message[512]; + + strcpy(Message, param); + free(param); + + Debugprintf("Sending %s", Message); + + sock = OpenWL2KHTTPSock(); + + if (sock) + { + SendHTTPRequest(sock, "/session/add", (char *)Message, (int)strlen(Message), NULL); + closesocket(sock); + } + + return; +} + +VOID SendWL2KRegisterHybridThread(void * param) +{ + SOCKET sock; + char Message[512]; + + strcpy(Message, param); + free(param); + + Debugprintf("Sending %s", Message); + + sock = OpenWL2KHTTPSock(); + + if (sock) + { + SendHTTPRequest(sock, "/radioNetwork/params/add", (char *)Message, (int)strlen(Message), NULL); + closesocket(sock); + } + + return; +} + +VOID SendWL2KRegisterHybrid(struct TNCINFO * TNC) +{ + char Message[512]; + char Date[80] ; + int Len; + struct TCPINFO * TCP = TNC->TCPInfo; + time_t T; + struct tm * tm; + char Call[10]; + + if (TCP == NULL || TCP->GatewayLoc[0] == 0) + return; + + strcpy(Call, TCP->GatewayCall); + strlop(Call, '-'); + + T = time(NULL); + tm = gmtime(&T); + + //2021-10-31-14=35=29 + + sprintf(Date, "%04d-%02d-%02d-%02d:%02d:%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + +// "Callsign":"String","Password":"String","Param":"String","Value":"String","Key":"String" + + Len = sprintf(Message, "\"Callsign\":\"%s\",\"Password\":\"%s\",\"Param\":\"RMSRelayVersion\",\"Value\":\"%s|%s|*HARMNNNN|%s|%s|\"", + Call, TCP->SecureCMSPassword, Date, "3.1.11.2", + TCP->HybridServiceCode, TCP->GatewayLoc); + + SendWL2KRegisterHybridThread(_strdup(Message)); + + Len = sprintf(Message, "\"Callsign\":\"%s\",\"Password\":\"%s\",\"Param\":\"CoLocatedRMS\",\"Value\":\"%s\"", + Call, TCP->SecureCMSPassword, TCP->HybridCoLocatedRMS); + + SendWL2KRegisterHybridThread(_strdup(Message)); + + Len = sprintf(Message, "\"Callsign\":\"%s\",\"Password\":\"%s\",\"Param\":\"AllowFreq\",\"Value\":\"%s\"", + Call, TCP->SecureCMSPassword, TCP->HybridFrequencies); + + SendWL2KRegisterHybridThread(_strdup(Message)); + + return; +} + +BOOL NoSessionAccount = FALSE; +BOOL SessionAccountChecked = FALSE; + +BOOL SendWL2KSessionRecord(ADIF * ADIF, int BytesSent, int BytesReceived) +{ +/* +The API is /session/add https://api.winlink.org/json/metadata?op=SessionAdd + +The important parameters are (others can be omitted): + +Application (gateway program name) +Server (gateway callsign) +ServerGrid +Client (client callsign) +ClientGrid +Mode (Pactor, winmor, vara, etc) +Frequency +MessagesSent +MessagesReceived +BytesSent +BytesReceived +HoldingSeconds (duration of connection) +IdTag (random alphanumeric, 12 chars) + +"Application":"RMS Trimode", +"Version":"1.3.25.0", +"Cms":"CMS-A", +"Server":"AB4NX", +"ServerGrid":"EM73WT", +"Client":"VE2SCA","ClientGrid":"", +"Sid":"","Mode":"WINMOR16", +"Frequency":10145000, +"Kilometers":0, +"Degrees":0, +"LastCommand":">", +"MessagesSent":0, +"MessagesReceived":0, +"BytesSent":179, +"BytesReceived":0, +"HoldingSeconds":126, +"IdTag":"ATK9S3QGL2E1"} +*/ + time_t T; + + char Message[4096] = ""; + char * MessagePtr; + int MessageLen; + int Dist = 0; + int intBearing = 0; + + double Lat, Lon; + double myLat, myLon; + + char Tag[32]; + + SOCKET sock; + char Response[1024]; + int Len; + + // Only report if the CMSCall has a WL2KAccount + + if (NoSessionAccount) + return TRUE; + + if (!SessionAccountChecked) + { + // only check once + + sock = OpenWL2KHTTPSock(); + + if (sock) + { + SessionAccountChecked = TRUE; + + Len = sprintf(Message, "\"Callsign\":\"%s\"", ADIF->CMSCall); + + SendHTTPRequest(sock, "/account/exists", Message, Len, Response); + closesocket(sock); + + if (strstr(Response, "\"CallsignExists\":false")) + { + WritetoConsole("WL2K Traffic Reporting disabled - Gateway "); + WritetoConsole(ADIF->CMSCall); + WritetoConsole(" does not have a Winlink Account\r\n"); + Debugprintf("WL2K Traffic Reporting disabled - Gateway %s does not have a Winlink Account", ADIF->CMSCall); + NoSessionAccount = TRUE; + return TRUE; + } + } + } + + if (ADIF == NULL || ADIF->LOC[0] == 0 || ADIF->Call[0] == 0) + return TRUE; + + if (ADIF->StartTime == 0 || ADIF->ServerSID[0] == 0 || ADIF->CMSCall[0] == 0) + return TRUE; + + T = time(NULL); + + // Extract Info we need + + // Distance and Bearing + + if (LOC[0] && ADIF->LOC[0]) + { + if (FromLOC(LOC, &myLat, &myLon) == 0) // Basic checks on LOCs + return TRUE; + if (FromLOC(ADIF->LOC, &Lat, &Lon) == 0) + return TRUE; + + Dist = (int)Distance(myLat, myLon, Lat, Lon, TRUE); + intBearing = (int)Bearing(Lat, Lon, myLat, myLon); + } + + MessageLen = sprintf(Message, "\"Application\":\"%s\",", "BPQ32"); + MessageLen += sprintf(&Message[MessageLen], "\"Version\":\"%s\",", TextVerstring); + MessageLen += sprintf(&Message[MessageLen], "\"Cms\":\"%s\",", "CMS"); + MessageLen += sprintf(&Message[MessageLen], "\"Server\":\"%s\",", ADIF->CMSCall); + MessageLen += sprintf(&Message[MessageLen], "\"ServerGrid\":\"%s\",", LOC); + MessageLen += sprintf(&Message[MessageLen], "\"Client\":\"%s\",", ADIF->Call); + MessageLen += sprintf(&Message[MessageLen], "\"ClientGrid\":\"%s\",", ADIF->LOC); + MessageLen += sprintf(&Message[MessageLen], "\"Sid\":\"%s\",", ADIF->UserSID); + MessageLen += sprintf(&Message[MessageLen], "\"Mode\":\"%s\",", WL2KModes[ADIF->Mode]); + MessageLen += sprintf(&Message[MessageLen], "\"Frequency\":%lld,", ADIF->Freq); + MessageLen += sprintf(&Message[MessageLen], "\"Kilometers\":%d,", Dist); + MessageLen += sprintf(&Message[MessageLen], "\"Degrees\":%d,", intBearing); + MessageLen += sprintf(&Message[MessageLen], "\"LastCommand\":\"%s\",", ADIF->Termination); + MessageLen += sprintf(&Message[MessageLen], "\"MessagesSent\":%d,", ADIF->Sent); + MessageLen += sprintf(&Message[MessageLen], "\"MessagesReceived\":%d,", ADIF->Received); + MessageLen += sprintf(&Message[MessageLen], "\"BytesSent\":%d,", BytesSent); + MessageLen += sprintf(&Message[MessageLen], "\"BytesReceived\":%d,", BytesReceived); + MessageLen += sprintf(&Message[MessageLen], "\"HoldingSeconds\":%d,", (int)(T - ADIF->StartTime)); + sprintf(Tag, "%012X", (int)T * (rand() + 1)); + MessageLen += sprintf(&Message[MessageLen], "\"IdTag\":\"%s\"", Tag); + + MessagePtr = _strdup(Message); + _beginthread(SendWL2KSessionRecordThread, 0, (void *)MessagePtr); + + return TRUE; +} + +char APIKey[] = ",\"Key\":\"0D0C7AD6B38C45A7A9534E67111C38A7\""; + + +VOID SendHTTPRequest(SOCKET sock, char * Request, char * Params, int Len, char * Return) +{ + int InputLen = 0; + int inptr = 0; + char Buffer[2048]; + char Header[2048]; + char * ptr, * ptr1; + int Sent; + + strcat(Params, APIKey); + Len += (int)strlen(APIKey); + + sprintf(Header, HeaderTemplate, Request, "api.winlink.org", 80, Len + 2, Params); + Sent = send(sock, Header, (int)strlen(Header), 0); + + if (Sent == -1) + { + int Err = WSAGetLastError(); + Debugprintf("Error %d from WL2K Update send()", Err); + return; + } + + while (InputLen != -1) + { + InputLen = recv(sock, &Buffer[inptr], 2048 - inptr, 0); + + if (InputLen == -1 || InputLen == 0) + { + int Err = WSAGetLastError(); + Debugprintf("Error %d from WL2K Update recv()", Err); + return; + } + + // As we are using a persistant connection, can't look for close. Check + // for complete message + + inptr += InputLen; + + Buffer[inptr] = 0; + + ptr = strstr(Buffer, "\r\n\r\n"); + + if (ptr) + { + // got header + + int Hddrlen = (int)(ptr - Buffer); + + ptr1 = strstr(Buffer, "Content-Length:"); + + if (ptr1) + { + // Have content length + + int ContentLen = atoi(ptr1 + 16); + + if (ContentLen + Hddrlen + 4 == inptr) + { + // got whole response + + if (strstr(Buffer, " 200 OK")) + { + if (Return) + { + memcpy(Return, ptr + 4, ContentLen); + Return[ContentLen] = 0; + } + else + Debugprintf("WL2K Database update ok"); + + } + else + { + strlop(Buffer, 13); + Debugprintf("WL2K Update Params - %s", Params); + Debugprintf("WL2K Update failed - %s", Buffer); + } + return; + } + } + else + { + ptr1 = strstr(_strlwr(Buffer), "transfer-encoding:"); + + if (ptr1) + { + // Just accept anything until I've sorted things with Lee + Debugprintf("%s", ptr1); + Debugprintf("WL2K Database update ok"); + return; + } + } + } + } +} + +BOOL WL2KAccountChecked = FALSE; +BOOL NoWL2KAccount = FALSE; + +VOID SendHTTPReporttoWL2KThread(void * unused) +{ + // Uses HTTP/JSON Interface + + struct WL2KInfo * WL2KReport = WL2KReports; + char * LastHost = NULL; + char * LastRMSCall = NULL; + char Message[512]; + int LastSocket = 0; + SOCKET sock = 0; + struct sockaddr_in destaddr; + int addrlen=sizeof(sinx); + struct hostent * HostEnt; + int err; + u_long param=1; + BOOL bcopt=TRUE; + int Len; + + // Send all reports in list + + char Response[1024]; + + // Only report if the CMSCall has a WL2KAccount + + if (NoWL2KAccount) + return; + + if (!WL2KAccountChecked) + { + // only check once + + sock = OpenWL2KHTTPSock(); + + if (sock) + { + WL2KAccountChecked = TRUE; + + Len = sprintf(Message, "\"Callsign\":\"%s\"", + WL2KReport->BaseCall); + + SendHTTPRequest(sock, "/account/exists", Message, Len, Response); + closesocket(sock); + + if (strstr(Response, "\"CallsignExists\":false")) + { + WritetoConsole("WL2K Reporting disabled - Gateway "); + WritetoConsole(WL2KReport->BaseCall); + WritetoConsole(" does not have a Winlink Account\r\n"); + NoWL2KAccount = TRUE; + return; + } + } + } + + while (WL2KReport) + { + // Resolve Name if needed + + if (LastHost && strcmp(LastHost, WL2KReport->Host) == 0) // Same host? + goto SameHost; + + // New Host - Connect to it + + LastHost = WL2KReport->Host; + + destaddr.sin_family = AF_INET; + destaddr.sin_addr.s_addr = inet_addr(WL2KReport->Host); + destaddr.sin_port = htons(WL2KReport->WL2KPort); + + if (destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + Debugprintf("Resolving %s", WL2KReport->Host); + HostEnt = gethostbyname (WL2KReport->Host); + + if (!HostEnt) + { + err = WSAGetLastError(); + + Debugprintf("Resolve Failed for %s %d %x", WL2KReport->Host, err, err); + return; // Resolve failed + } + + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + // Allocate a Socket entry + + if (sock) + closesocket(sock); + + sock = socket(AF_INET, SOCK_STREAM, 0); + + if (sock == INVALID_SOCKET) + return; + +// ioctlsocket(sock, FIONBIO, ¶m); + + setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char FAR *)&bcopt, 4); + + destaddr.sin_family = AF_INET; + + if (sock == INVALID_SOCKET) + { + sock = 0; + return; + } + + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); + + // Connect to Host + + if (connect(sock,(LPSOCKADDR) &destaddr, sizeof(destaddr)) != 0) + { + err=WSAGetLastError(); + + // + // Connect failed + // + + Debugprintf("Connect Failed"); + closesocket(sock); + sock = 0; + break; + } + + SameHost: + + Len = sprintf(Message, + "\"Callsign\":\"%s\"," + "\"BaseCallsign\":\"%s\"," + "\"GridSquare\":\"%s\"," + "\"Frequency\":%lld," + "\"Mode\":%d," + "\"Baud\":%d," + "\"Power\":%d," + "\"Height\":%d," + "\"Gain\":%d," + "\"Direction\":%d," + "\"Hours\":\"%s\"," + "\"ServiceCode\":\"%s\"", + + WL2KReport->RMSCall, WL2KReport->BaseCall, WL2KReport->GridSquare, + WL2KReport->Freq, WL2KReport->mode, WL2KReport->baud, WL2KReport->power, + WL2KReport->height, WL2KReport->gain, WL2KReport->direction, + WL2KReport->Times, WL2KReport->ServiceCode); + + Debugprintf("Sending %s", Message); + + SendHTTPRequest(sock, "/channel/add", Message, Len, NULL); + + + // Send Version Message + + + if (LastRMSCall == NULL || strcmp(WL2KReport->RMSCall, LastRMSCall) != 0) + { + int Len; + + LastRMSCall = WL2KReport->RMSCall; + + // "Callsign":"String","Program":"String","Version":"String","Comments":"String" + + Len = sprintf(Message, "\"Callsign\":\"%s\",\"Program\":\"BPQ32\"," + "\"Version\":\"%d.%d.%d.%d\",\"Comments\":\"Test Comment\"", + WL2KReport->RMSCall, Ver[0], Ver[1], Ver[2], Ver[3]); + + Debugprintf("Sending %s", Message); + + SendHTTPRequest(sock, "/version/add", Message, Len, NULL); + } + + WL2KReport = WL2KReport->Next; + } + + Sleep(100); + closesocket(sock); + sock = 0; + +} + +struct WL2KInfo * DecodeWL2KReportLine(char * buf) +{ + //06'', '', '', , , , , + // , , , '', , '' + + // WL2KREPORT service, api.winlink.org, 80, GM8BPQ, IO68VL, 00-23, 144800000, PKT1200, 10, 20, 5, 0, BPQTEST + + char * Context; + char * p_cmd; + char * param; + char errbuf[256]; + struct WL2KInfo * WL2KReport = zalloc(sizeof(struct WL2KInfo)); + char * ptr; + char Param[8][256]; + char * ptr1, * ptr2; + int n = 0; + + memset(Param, 0, 2048); + + strcpy(errbuf, buf); + + p_cmd = strtok_s(&buf[10], ", \t\n\r", &Context); + if (p_cmd == NULL) goto BadLine; + + strcpy(WL2KReport->ServiceCode, p_cmd); + + // Can default Host and Port, so cant use strtok for them + + ptr1 = Context; + + while (ptr1 && *ptr1 && n < 2) + { + while(ptr1 && *ptr1 && *ptr1 == ' ') + ptr1++; + + ptr2 = strchr(ptr1, ','); + if (ptr2) *ptr2++ = 0; + + strcpy(&Param[n][0], ptr1); + strlop(Param[n++], ' '); + ptr1 = ptr2; + + } + + if (n < 2) + goto BadLine; + + if (Param[1][0] == 0) + WL2KReport->WL2KPort = 80; // HTTP Interface + else + WL2KReport->WL2KPort = atoi(&Param[1][0]); + + if (Param[0][0] == 0) + WL2KReport->Host = _strdup("api.winlink.org"); + else + { + _strlwr(&Param[0][0]); + + if (strstr(&Param[0][0], "winlink.org")) + { + WL2KReport->WL2KPort = 80; // HTTP Interface + WL2KReport->Host = _strdup("api.winlink.org"); + } + else + WL2KReport->Host = _strdup(&Param[0][0]); + } + + Context = ptr1; + + p_cmd = strtok_s(NULL, ", \t\n\r", &Context); + if (p_cmd == NULL) goto BadLine; + + if (WL2KReport->WL2KPort == 0) goto BadLine; + + strcpy(WL2KReport->RMSCall, p_cmd); + strcpy(WL2KReport->BaseCall, p_cmd); + strlop(WL2KReport->BaseCall, '-'); // Remove any SSID + + strcpy(WL2KCall, WL2KReport->BaseCall); // For SYSOP Update + + p_cmd = strtok_s(NULL, " ,\t\n\r", &Context); + if (p_cmd == NULL) goto BadLine; + if (strlen(p_cmd) != 6) goto BadLine; + + strcpy(WL2KReport->GridSquare, p_cmd); + strcpy(WL2KLoc, p_cmd); + + p_cmd = strtok_s(NULL, " ,\t\n\r", &Context); + if (p_cmd == NULL) goto BadLine; + if (strlen(p_cmd) > 79) goto BadLine; + + // Convert any : in times to comma + + ptr = strchr(p_cmd, ':'); + + while (ptr) + { + *ptr = ','; + ptr = strchr(p_cmd, ':'); + } + + strcpy(WL2KReport->Times, p_cmd); + + p_cmd = strtok_s(NULL, " ,\t\n\r", &Context); + if (p_cmd == NULL) goto BadLine; + + WL2KReport->Freq = strtoll(p_cmd, NULL, 10); + + if (WL2KReport->Freq == 0) // Invalid + goto BadLine; + + param = strtok_s(NULL, " ,\t\n\r", &Context); + + // Mode Designator - one of + + // PKTnnnnnn + // WINMOR500 + // WINMOR1600 + // ROBUST + // P1 P12 P123 P1234 etc + + if (memcmp(param, "PKT", 3) == 0) + { + int Speed, Mode; + + Speed = atoi(¶m[3]); + + WL2KReport->baud = Speed; + + if (Speed <= 1200) + Mode = 0; // 1200 + else if (Speed <= 2400) + Mode = 1; // 2400 + else if (Speed <= 4800) + Mode = 2; // 4800 + else if (Speed <= 9600) + Mode = 3; // 9600 + else if (Speed <= 19200) + Mode = 4; // 19200 + else if (Speed <= 38400) + Mode = 5; // 38400 + else + Mode = 6; // >38400 + + WL2KReport->mode = Mode; + } + else if (_stricmp(param, "WINMOR500") == 0) + WL2KReport->mode = 21; + else if (_stricmp(param, "WINMOR1600") == 0) + WL2KReport->mode = 22; + else if (_stricmp(param, "ROBUST") == 0) + { + WL2KReport->mode = 30; + WL2KReport->baud = 600; + } + else if (_stricmp(param, "ARDOP200") == 0) + WL2KReport->mode = 40; + else if (_stricmp(param, "ARDOP500") == 0) + WL2KReport->mode = 41; + else if (_stricmp(param, "ARDOP1000") == 0) + WL2KReport->mode = 42; + else if (_stricmp(param, "ARDOP2000") == 0) + WL2KReport->mode = 43; + else if (_stricmp(param, "ARDOP2000FM") == 0) + WL2KReport->mode = 44; + else if (_stricmp(param, "P1") == 0) + WL2KReport->mode = 11; + else if (_stricmp(param, "P12") == 0) + WL2KReport->mode = 12; + else if (_stricmp(param, "P123") == 0) + WL2KReport->mode = 13; + else if (_stricmp(param, "P2") == 0) + WL2KReport->mode = 14; + else if (_stricmp(param, "P23") == 0) + WL2KReport->mode = 15; + else if (_stricmp(param, "P3") == 0) + WL2KReport->mode = 16; + else if (_stricmp(param, "P1234") == 0) + WL2KReport->mode = 17; + else if (_stricmp(param, "P234") == 0) + WL2KReport->mode = 18; + else if (_stricmp(param, "P34") == 0) + WL2KReport->mode = 19; + else if (_stricmp(param, "P4") == 0) + WL2KReport->mode = 20; + else if (_stricmp(param, "VARA") == 0) + WL2KReport->mode = 50; + else if (_stricmp(param, "VARA2300") == 0) + WL2KReport->mode = 50; + else if (_stricmp(param, "VARAFM") == 0) + WL2KReport->mode = 51; + else if (_stricmp(param, "VARAFM12") == 0) + WL2KReport->mode = 51; + else if (_stricmp(param, "VARAFM96") == 0) + WL2KReport->mode = 52; + else if (_stricmp(param, "VARA500") == 0) + WL2KReport->mode = 53; + else if (_stricmp(param, "VARA2750") == 0) + WL2KReport->mode = 54; + else + goto BadLine; + + param = strtok_s(NULL, " ,\t\n\r", &Context); + + // Optional Params + + WL2KReport->power = (param)? atoi(param) : 0; + param = strtok_s(NULL, " ,\t\n\r", &Context); + WL2KReport->height = (param)? atoi(param) : 0; + param = strtok_s(NULL, " ,\t\n\r", &Context); + WL2KReport->gain = (param)? atoi(param) : 0; + param = strtok_s(NULL, " ,\t\n\r", &Context); + WL2KReport->direction = (param)? atoi(param) : 0; + + WL2KTimer = 60; + + WL2KReport->Next = WL2KReports; + WL2KReports = WL2KReport; + + return WL2KReport; + +BadLine: + + WritetoConsole(" Bad config record "); + WritetoConsole(errbuf); + WritetoConsole("\r\n"); + + return 0; +} + +VOID UpdateMHSupport(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction, char * Loc, BOOL Report, BOOL Digis); + +VOID UpdateMHwithDigis(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction) +{ + UpdateMHSupport(TNC, Call, Mode, Direction, NULL, TRUE, TRUE); +} +VOID UpdateMHEx(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction, char * LOC, BOOL Report) +{ + UpdateMHSupport(TNC, Call, Mode, Direction, LOC, Report, FALSE); +} + +VOID UpdateMH(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction) +{ + UpdateMHSupport(TNC, Call, Mode, Direction, NULL, TRUE, FALSE); +} + +VOID UpdateMHSupport(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction, char * Loc, BOOL Report, BOOL Digis) +{ + PMHSTRUC MH = TNC->PortRecord->PORTCONTROL.PORTMHEARD; + PMHSTRUC MHBASE = MH; + UCHAR AXCall[72] = ""; + int i; + char * LOC, * LOCEND; + char ReportMode[20]; + char NoLOC[7] = ""; + double Freq; + char ReportFreq[350] = ""; + int OldCount = 0; + char ReportCall[16]; + + if (MH == 0) return; + + if (Digis) + { + // Call is an ax.25 digi string not a text call + + memcpy(AXCall, Call, 7 * 9); + ReportCall[ConvFromAX25(Call, ReportCall)] = 0; + + // if this is a UI frame with a locator or APRS position + // we could derive a position from it + + } + else + { + strcpy(ReportCall, Call); + ConvToAX25(Call, AXCall); + AXCall[6] |= 1; // Set End of address + } + + // Adjust freq to centre + +// if (Mode != ' ' && TNC->RIG->Valchar[0]) + if (TNC->RIG->Valchar[0]) + { + if (TNC->Hardware == H_UZ7HO) + { + // See if we have Center Freq Info + if (TNC->AGWInfo->CenterFreq) + { + Freq = atof(TNC->RIG->Valchar) + ((TNC->AGWInfo->CenterFreq * 1.0) / 1000000.0); + } +#ifdef WIN32 + else if (TNC->AGWInfo->hFreq) + { + char Centre[16]; + double ModemFreq; + + SendMessage(TNC->AGWInfo->hFreq, WM_GETTEXT, 15, (LPARAM)Centre); + + ModemFreq = atof(Centre); + + Freq = atof(TNC->RIG->Valchar) + (ModemFreq / 1000000); + } +#endif + else + Freq = atof(TNC->RIG->Valchar) + 0.0015; // Assume 1500 + } + else + + // Not UZ7HO or Linux + + Freq = atof(TNC->RIG->Valchar) + 0.0015; + + _gcvt(Freq, 9, ReportFreq); + } + + if (TNC->Hardware == H_ARDOP) + { + LOC = memchr(Call, '[', 20); + + if (LOC) + { + LOCEND = memchr(Call, ']', 30); + if (LOCEND) + { + LOC--; + *(LOC++) = 0; + *(LOCEND) = 0; + LOC++; + if (strlen(LOC) != 6 && strlen(LOC) != 0) + { + Debugprintf("Corrupt LOC %s %s", Call, LOC); + LOC = NoLOC; + } + goto NOLOC; + } + } + } + + else if (TNC->Hardware != H_WINMOR) // Only WINMOR has a locator + { + LOC = NoLOC; + goto NOLOC; + } + + LOC = memchr(Call, '(', 20); + + if (LOC) + { + LOCEND = memchr(Call, ')', 30); + if (LOCEND) + { + LOC--; + *(LOC++) = 0; + *(LOCEND) = 0; + LOC++; + if (strlen(LOC) != 6 && strlen(LOC) != 0) + { + Debugprintf("Corrupt LOC %s %s", Call, LOC); + LOC = NoLOC; + } + } + } + else + LOC = NoLOC; + +NOLOC: + + if (Loc) + LOC = Loc; // Supplied Locator overrides + + for (i = 0; i < MHENTRIES; i++) + { + if (Mode == ' ' || Mode == '*') // Packet + { + if ((MH->MHCALL[0] == 0) || ((memcmp(AXCall, MH->MHCALL, 7) == 0) && MH->MHDIGI == Mode)) // Spare or our entry + { + OldCount = MH->MHCOUNT; + goto DoMove; + } + } + else + { + if ((MH->MHCALL[0] == 0) || ((memcmp(AXCall, MH->MHCALL, 7) == 0) && + MH->MHDIGI == Mode && strcmp(MH->MHFreq, ReportFreq) == 0)) // Spare or our entry + { + OldCount = MH->MHCOUNT; + goto DoMove; + } + } + MH++; + } + + // TABLE FULL AND ENTRY NOT FOUND - MOVE DOWN ONE, AND ADD TO TOP + + i = MHENTRIES - 1; + + // Move others down and add at front +DoMove: + + if (i != 0) // First + memmove(MHBASE + 1, MHBASE, i * sizeof(MHSTRUC)); + +// memcpy (MHBASE->MHCALL, Buffer->ORIGIN, 7 * 9); + memcpy (MHBASE->MHCALL, AXCall, 7 * 9); // Save Digis + MHBASE->MHDIGI = Mode; + MHBASE->MHTIME = time(NULL); + MHBASE->MHCOUNT = ++OldCount; + + memcpy(MHBASE->MHLocator, LOC, 6); + strcpy(MHBASE->MHFreq, ReportFreq); + + // Report to NodeMap + + if (Report == FALSE) + return; + + if (Mode == '*') + return; // Digi'ed Packet + + if (Mode == ' ') // Packet Data + { + if (TNC->PktUpdateMap == 1) + Mode = '!'; + else + return; + } + + ReportMode[0] = TNC->Hardware + '@'; + ReportMode[1] = Mode; + if (TNC->Hardware == H_HAL) + ReportMode[2] = TNC->CurrentMode; + else + ReportMode[2] = (TNC->RIG->CurrentBandWidth) ? TNC->RIG->CurrentBandWidth : '?'; + ReportMode[3] = Direction; + ReportMode[4] = 0; + + // If no position see if we have an APRS posn + + if (LOC[0] == 0) + { + double Lat, Lon; + + if (GetPosnFromAPRS(ReportCall, &Lat, &Lon) && Lat != 0.0) + { + ToLOC(Lat, Lon, LOC); + } + } + + SendMH(TNC, ReportCall, ReportFreq, LOC, ReportMode); + + return; +} + +VOID CloseDriverWindow(int port) +{ +#ifndef LINBPQ + + struct TNCINFO * TNC; + + TNC = TNCInfo[port]; + if (TNC == NULL) + return; + + if (TNC->hDlg == NULL) + return; + + PostMessage(TNC->hDlg, WM_CLOSE,0,0); +// DestroyWindow(TNC->hDlg); + + TNC->hDlg = NULL; +#endif + return; +} + +VOID SaveWindowPos(int port) +{ +#ifndef LINBPQ + + struct TNCINFO * TNC; + char Key[80]; + + TNC = TNCInfo[port]; + + if (TNC == NULL) + return; + + if (TNC->hDlg == NULL) + return; + + sprintf(Key, "PACTOR\\PORT%d", port); + + SaveMDIWindowPos(TNC->hDlg, Key, "Size", TNC->Minimized); + +#endif + return; +} + +VOID ShowTraffic(struct TNCINFO * TNC) +{ + char Status[80]; + + sprintf(Status, "RX %d TX %d ACKED %d ", + TNC->Streams[0].bytesRXed, TNC->Streams[0].bytesTXed, TNC->Streams[0].BytesAcked); +#ifndef LINBPQ + SetDlgItemText(TNC->hDlg, IDC_TRAFFIC, Status); +#endif +} + +BOOL InterlockedCheckBusy(struct TNCINFO * ThisTNC) +{ + // See if this port, or any interlocked ports are reporting channel busy + + struct TNCINFO * TNC; + int i; + int rxInterlock = ThisTNC->RXRadio; + int txInterlock = ThisTNC->TXRadio; + + if (ThisTNC->Busy) + return TRUE; // Our port is busy + + if (rxInterlock == 0 && txInterlock == 0) + return ThisTNC->Busy; // No Interlock + + for (i=1; i <= MAXBPQPORTS; i++) + { + TNC = TNCInfo[i]; + + if (TNC == NULL) + continue; + + if (TNC == ThisTNC) + continue; + + if (rxInterlock == TNC->RXRadio || txInterlock == TNC->TXRadio) // Same Group + if (TNC->Busy) + return TRUE; // Interlocked port is busy + + } + return FALSE; // None Busy +} + +char ChallengeResponse[13]; + +char * GetChallengeResponse(char * Call, char * ChallengeString) +{ + // Generates a response to the CMS challenge string... + + long long Challenge = _atoi64(ChallengeString); + long long CallSum = 0; + long long Mask; + long long Response; + long long XX = 1065484730; + + char CallCopy[10]; + UINT i; + + + if (Challenge == 0) + return "000000000000"; + +// Calculate Mask from Callsign + + memcpy(CallCopy, Call, 10); + strlop(CallCopy, '-'); + strlop(CallCopy, ' '); + + for (i = 0; i < strlen(CallCopy); i++) + { + CallSum += CallCopy[i]; + } + + Mask = CallSum + CallSum * 4963 + CallSum * 782386; + + Response = (Challenge % 930249781); + Response ^= Mask; + + sprintf(ChallengeResponse, "%012lld", Response); + + return ChallengeResponse; +} + +SOCKET OpenWL2KHTTPSock() +{ + SOCKET sock = 0; + struct sockaddr_in destaddr; + struct sockaddr_in sinx; + int addrlen=sizeof(sinx); + struct hostent * HostEnt; + int err; + u_long param=1; + BOOL bcopt=TRUE; + + destaddr.sin_family = AF_INET; + destaddr.sin_port = htons(80); + + // Resolve name to address + + HostEnt = gethostbyname ("api.winlink.org"); + + if (!HostEnt) + { + err = WSAGetLastError(); + + Debugprintf("Resolve Failed for %s %d %x", "api.winlink.org", err, err); + return 0 ; // Resolve failed + } + + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + + // Allocate a Socket entry + + sock = socket(AF_INET,SOCK_STREAM,0); + + if (sock == INVALID_SOCKET) + return 0; + + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (bind(sock, (struct sockaddr *) &sinx, addrlen) != 0 ) + return FALSE; + + if (connect(sock,(struct sockaddr *) &destaddr, sizeof(destaddr)) != 0) + { + err=WSAGetLastError(); + closesocket(sock); + return 0; + } + + return sock; +} + +BOOL GetWL2KSYSOPInfo(char * Call, char * _REPLYBUFFER) +{ + SOCKET sock = 0; + int Len; + char Message[1000]; + + sock = OpenWL2KHTTPSock(); + + if (sock == 0) + return 0; + + // {"Callsign":"String"} + + Len = sprintf(Message, "\"Callsign\":\"%s\"", Call); + + SendHTTPRequest(sock, "/sysop/get", Message, Len, _REPLYBUFFER); + + closesocket(sock); + + return _REPLYBUFFER[0]; +} + +BOOL UpdateWL2KSYSOPInfo(char * Call, char * SQL) +{ + SOCKET sock = 0; + struct sockaddr_in destaddr; + struct sockaddr_in sinx; + int len = 100; + int addrlen=sizeof(sinx); + struct hostent * HostEnt; + int err; + u_long param=1; + BOOL bcopt=TRUE; + char Buffer[1000]; + char SendBuffer[1000]; + + destaddr.sin_family = AF_INET; + destaddr.sin_addr.s_addr = inet_addr("api.winlink.org"); + destaddr.sin_port = htons(80); + + HostEnt = gethostbyname ("api.winlink.org"); + + if (!HostEnt) + { + err = WSAGetLastError(); + + Debugprintf("Resolve Failed for %s %d %x", "api.winlink.org", err, err); + return 0 ; // Resolve failed + } + + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + + // Allocate a Socket entry + + sock = socket(AF_INET,SOCK_STREAM,0); + + if (sock == INVALID_SOCKET) + return 0; + + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (bind(sock, (struct sockaddr *) &sinx, addrlen) != 0 ) + return FALSE; + + if (connect(sock,(struct sockaddr *) &destaddr, sizeof(destaddr)) != 0) + { + err=WSAGetLastError(); + closesocket(sock); + return 0; + } + + len = recv(sock, &Buffer[0], len, 0); + + len = sprintf(SendBuffer, "02%07d%-12s%s%s", (int)strlen(SQL), Call, GetChallengeResponse(Call, Buffer), SQL); + + send(sock, SendBuffer, len, 0); + + len = 1000; + + len = recv(sock, &Buffer[0], len, 0); + + Buffer[len] = 0; + Debugprintf(Buffer); + + closesocket(sock); + + return TRUE; + +} +// http://server.winlink.org:8085/csv/reply/ChannelList?Modes=40,41,42,43,44&ServiceCodes=BPQTEST,PUBLIC + +// Process config lines that are common to a number of HF modes + +static char ** SeparateMultiString(char * MultiString) +{ + char ** Value; + int Count = 0; + char * ptr, * ptr1; + + // Convert to string array + + Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values + Value[0] = NULL; + + strlop(MultiString, 13); + ptr = MultiString; + + while (ptr && strlen(ptr)) + { + ptr1 = strchr(ptr, '|'); + + if (ptr1) + *(ptr1++) = 0; + + if (strlen(ptr)) + { + Value = realloc(Value, (Count+2) * sizeof(void *)); + Value[Count++] = _strdup(ptr); + } + ptr = ptr1; + } + + Value[Count] = NULL; + return Value; +} + + + + +extern int nextDummyInterlock; + +int standardParams(struct TNCINFO * TNC, char * buf) +{ + if (_memicmp(buf, "WL2KREPORT", 10) == 0) + TNC->WL2K = DecodeWL2KReportLine(buf); + else if (_memicmp(buf, "SESSIONTIMELIMIT", 16) == 0) + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit = atoi(&buf[17]) * 60; + else if (_memicmp(buf, "ATTACHTIMELIMIT", 15) == 0) + TNC->AttachTimeLimit = atoi(&buf[16]) * 60; + else if (_memicmp(buf, "BUSYHOLD", 8) == 0) // Hold Time for Busy Detect + TNC->BusyHold = atoi(&buf[8]); + else if (_memicmp(buf, "BUSYWAIT", 8) == 0) // Wait time before failing connect if busy + TNC->BusyWait = atoi(&buf[8]); + else if (_memicmp(buf, "AUTOSTARTDELAY", 14) == 0) // Time to wait for TNC to start + TNC->AutoStartDelay = atoi(&buf[15]); + else if (_memicmp(buf, "DEFAULTRADIOCOMMAND", 19) == 0) + TNC->DefaultRadioCmd = _strdup(&buf[20]); + else if (_memicmp(buf, "MYCALLS", 7) == 0) + { + TNC->LISTENCALLS = _strdup(&buf[8]); + strlop(TNC->LISTENCALLS, '\r'); + } + else if (_memicmp(buf, "NRNEIGHBOUR", 11) == 0) + TNC->NRNeighbour = _strdup(&buf[12]); + else if (_memicmp(buf, "MAXCONREQ", 9) == 0) // Hold Time for Busy Detect + TNC->MaxConReq = atoi(&buf[9]); + + else if (_memicmp(buf, "FREQUENCY", 9) == 0) + TNC->Frequency = _strdup(&buf[10]); + else if (_memicmp(buf, "SendTandRtoRelay", 16) == 0) + TNC->SendTandRtoRelay = atoi(&buf[17]); + else if (_memicmp(buf, "Radio", 5) == 0) // Rig Control RADIO for TX amd RX (Equiv to INTERLOCK) + TNC->RXRadio = TNC->TXRadio = atoi(&buf[6]); + else if (_memicmp(buf, "TXRadio", 7) == 0) // Rig Control RADIO for TX + TNC->TXRadio = atoi(&buf[8]); + else if (_memicmp(buf, "RXRadio", 7) == 0) // Rig Control RADIO for RXFRETRIES + TNC->RXRadio = atoi(&buf[8]); + else if (_memicmp(buf, "TXFreq", 6) == 0) // For PTT Sets Freq mode + TNC->TXFreq = strtoll(&buf[7], NULL, 10); + else if (_memicmp(buf, "DefaultTXFreq", 13) == 0) // Set at end of session + TNC->DefaultTXFreq = atof(&buf[14]); + else if (_memicmp(buf, "DefaultRXFreq", 13) == 0) // Set at end of session + TNC->DefaultRXFreq = atof(&buf[14]); + else if (_memicmp(buf, "ActiveTXFreq", 12) == 0) // Set at start of session + TNC->ActiveTXFreq = atof(&buf[13]); + else if (_memicmp(buf, "ActiveRXFreq", 12) == 0) // Set at start of session + TNC->ActiveRXFreq = atof(&buf[13]); + else if (_memicmp(buf, "DisconnectScript", 16) == 0) // Set at start of session + TNC->DisconnectScript = SeparateMultiString(&buf[17]); + else if (_memicmp(buf, "PTTONHEX", 8) == 0) + { + // Hex String to use for PTT on for this port + + char * ptr1 = &buf[9]; + char * ptr2 = TNC->PTTOn; + int i, j, len; + + _strupr(ptr1); + + TNC->PTTOnLen = len = strlen(ptr1) / 2; + + if (len < 240) + { + while ((len--) > 0) + { + i = *(ptr1++); + i -= '0'; + if (i > 9) + i -= 7; + + j = i << 4; + + i = *(ptr1++); + i -= '0'; + if (i > 9) + i -= 7; + + *(ptr2++) = j | i; + } + } + } + else if (_memicmp(buf, "PTTOFFHEX", 9) == 0) + { + // Hex String to use for PTT off + + char * ptr = &buf[10]; + char * ptr2 = TNC->PTTOff; + int i, j, len; + + _strupr(ptr); + + TNC->PTTOffLen = len = strlen(ptr) / 2; + + if (len < 240) + { + while ((len--) > 0) + { + i = *(ptr++); + i -= '0'; + if (i > 9) + i -= 7; + + j = i << 4; + + i = *(ptr++); + i -= '0'; + if (i > 9) + i -= 7; + + *(ptr2++) = j | i; + } + } + } + else + return FALSE; + + return TRUE; +} + +void DecodePTTString(struct TNCINFO * TNC, char * ptr) +{ + if (_stricmp(ptr, "CI-V") == 0) + TNC->PTTMode = PTTCI_V; + else if (_stricmp(ptr, "CAT") == 0) + TNC->PTTMode = PTTCI_V; + else if (_stricmp(ptr, "RTS") == 0) + TNC->PTTMode = PTTRTS; + else if (_stricmp(ptr, "DTR") == 0) + TNC->PTTMode = PTTDTR; + else if (_stricmp(ptr, "DTRRTS") == 0) + TNC->PTTMode = PTTDTR | PTTRTS; + else if (_stricmp(ptr, "CM108") == 0) + TNC->PTTMode = PTTCM108; + else if (_stricmp(ptr, "HAMLIB") == 0) + TNC->PTTMode = PTTHAMLIB; + else if (_stricmp(ptr, "FLRIG") == 0) + TNC->PTTMode = PTTFLRIG; +} + +extern SOCKET ReportSocket; +extern char LOCATOR[80]; +extern char ReportDest[7]; +extern int NumberofPorts; +extern struct RIGPORTINFO * PORTInfo[34]; // Records are Malloc'd + +time_t LastModeReportTime; +time_t LastFreqReportTime; + +VOID SendReportMsg(char * buff, int txlen); + +void sendModeReport() +{ + // if TNC is connected send mode and frequencies to Node Map as a MODE record + // Are we better sending scan info as a separate record ?? + + // MODE Port, HWType, Interlock + + struct PORTCONTROL * PORT = PORTTABLE; + + struct TNCINFO * TNC; + MESSAGE AXMSG; + PMESSAGE AXPTR = &AXMSG; + char Msg[300] = "MODE "; + int i, Len = 5; + + if ((CurrentSecs - LastModeReportTime) < 900) // Every 15 Mins + return; + + LastModeReportTime = CurrentSecs; + + for (i = 0; i < NUMBEROFPORTS; i++) + { + if (PORT->PROTOCOL == 10) + { + PEXTPORTDATA PORTVEC = (PEXTPORTDATA)PORT; + TNC = TNCInfo[PORT->PORTNUMBER]; + PORT = PORT->PORTPOINTER; + + if (TNC == NULL) + continue; + + if (TNC->CONNECTED == 0 && TNC->TNCOK == 0) + continue; + + if (ReportSocket == 0 || LOCATOR[0] == 0) + continue; + + if (TNC->Frequency) + Len += sprintf(&Msg[Len], "%d,%d,%d,%.6f/", TNC->Port, TNC->Hardware, TNC->RXRadio, atof(TNC->Frequency)); + else + Len += sprintf(&Msg[Len], "%d,%d,%d/", TNC->Port, TNC->Hardware, TNC->RXRadio); + + if (Len > 240) + break; + } + else + PORT = PORT->PORTPOINTER; + } + + if (Len == 5) + return; // Nothing to send + + // Block includes the Msg Header (7 bytes), Len Does not! + + memcpy(AXPTR->DEST, ReportDest, 7); + memcpy(AXPTR->ORIGIN, MYCALL, 7); + AXPTR->DEST[6] &= 0x7e; // Clear End of Call + AXPTR->DEST[6] |= 0x80; // set Command Bit + + AXPTR->ORIGIN[6] |= 1; // Set End of Call + AXPTR->CTL = 3; //UI + AXPTR->PID = 0xf0; + memcpy(AXPTR->L2DATA, Msg, Len); + + SendReportMsg((char *)&AXMSG.DEST, Len + 16) ; +} + +void sendFreqReport(char * From) +{ + // Send info from rig control or Port Frequency info to Node Map for Mode page. + + MESSAGE AXMSG; + PMESSAGE AXPTR = &AXMSG; + char Msg[300] = "FREQ "; + int i, Len = 5, p; + + struct RIGPORTINFO * RIGPORT; + struct RIGINFO * RIG; + struct TimeScan * Band; + struct PORTCONTROL * PORT = PORTTABLE; + struct TNCINFO * TNC; + + if ((CurrentSecs - LastFreqReportTime) < 7200) // Every 2 Hours + return; + + LastFreqReportTime = CurrentSecs; + + for (p = 0; p < NumberofPorts; p++) + { + RIGPORT = PORTInfo[p]; + + for (i = 0; i < RIGPORT->ConfiguredRigs; i++) + { + int j = 1, k = 0; + + RIG = &RIGPORT->Rigs[i]; + + if (RIG->reportFreqs) + { + Len += sprintf(&Msg[Len], "%d/00:00/%s,\\|",RIG->Interlock,RIG->reportFreqs); + } + else + { + if (RIG->TimeBands) + { + Len += sprintf(&Msg[Len], "%d/",RIG->Interlock); + while (RIG->TimeBands[j]) + { + Band = RIG->TimeBands[j]; + k = 0; + + if (Band->Scanlist[0]) + { + Len += sprintf(&Msg[Len], "%02d:%02d/", Band->Start / 3600, Band->Start % 3600); + + while (Band->Scanlist[k]) + { + Len += sprintf(&Msg[Len],"%.0f,", Band->Scanlist[k]->Freq + RIG->rxOffset); + k++; + } + Len += sprintf(&Msg[Len], "\\"); + } + j++; + } + Len += sprintf(&Msg[Len], "|"); + } + } + } + } + + // Look for Port freq info + + for (i = 0; i < NUMBEROFPORTS; i++) + { + if (PORT->PROTOCOL == 10) + { + PEXTPORTDATA PORTVEC = (PEXTPORTDATA)PORT; + TNC = TNCInfo[PORT->PORTNUMBER]; + PORT = PORT->PORTPOINTER; + + if (TNC == NULL) + continue; + + if (TNC->Frequency == NULL) + continue; + + if (TNC->RIG->TimeBands && TNC->RIG->TimeBands[1]->Scanlist) + continue; // Have freq info from Rigcontrol + + if (TNC->RXRadio == 0) // Replace with dummy + TNC->RXRadio = nextDummyInterlock++; + + // Use negative port no instead of interlock group + + Len += sprintf(&Msg[Len], "%d/00:00/%.0f|", TNC->RXRadio, atof(TNC->Frequency) * 1000000.0); + } + else + PORT = PORT->PORTPOINTER; + } + + if (Len == 5) + return; // Nothing to send + + // Block includes the Msg Header (7 bytes), Len Does not! + + memcpy(AXPTR->DEST, ReportDest, 7); + memcpy(AXPTR->ORIGIN, MYCALL, 7); + AXPTR->DEST[6] &= 0x7e; // Clear End of Call + AXPTR->DEST[6] |= 0x80; // set Command Bit + + AXPTR->ORIGIN[6] |= 1; // Set End of Call + AXPTR->CTL = 3; //UI + AXPTR->PID = 0xf0; + memcpy(AXPTR->L2DATA, Msg, Len); + + SendReportMsg((char *)&AXMSG.DEST, Len + 16) ; +} + + diff --git a/.svn/pristine/12/129588435d73a332d9a80b3904b723c535bcd9c1.svn-base b/.svn/pristine/12/129588435d73a332d9a80b3904b723c535bcd9c1.svn-base index 95563d4..1c3dce1 100644 --- a/.svn/pristine/12/129588435d73a332d9a80b3904b723c535bcd9c1.svn-base +++ b/.svn/pristine/12/129588435d73a332d9a80b3904b723c535bcd9c1.svn-base @@ -1,219 +1,219 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.svn/pristine/13/1340af3f5aabaa27f5c011eb6fba97496d3ca296.svn-base b/.svn/pristine/13/1340af3f5aabaa27f5c011eb6fba97496d3ca296.svn-base index 12abf4b..1ceea42 100644 --- a/.svn/pristine/13/1340af3f5aabaa27f5c011eb6fba97496d3ca296.svn-base +++ b/.svn/pristine/13/1340af3f5aabaa27f5c011eb6fba97496d3ca296.svn-base @@ -1,1329 +1,1329 @@ -/* -Copyright 2001-2018 John Wiseman G8BPQ - -This file is part of LinBPQ/BPQ32. - -LinBPQ/BPQ32 is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -LinBPQ/BPQ32 is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses -*/ - -// Mail and Chat Server for BPQ32 Packet Switch -// -// White Pages Database Support Routines - -#include "bpqmail.h" - -VOID __cdecl Debugprintf(const char * format, ...); -VOID ReleaseSock(SOCKET sock); -void MQTTMessageEvent(void* message); - -#define GetSemaphore(Semaphore,ID) _GetSemaphore(Semaphore, ID, __FILE__, __LINE__) -void _GetSemaphore(struct SEM * Semaphore, int ID, char * File, int Line); - -struct NNTPRec * FirstNNTPRec = NULL; - -//int NumberofNNTPRecs=0; - -SOCKET nntpsock; - -extern SocketConn * Sockets; // Chain of active sockets - -int NNTPInPort = 0; - -char *day[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; - - -VOID ReleaseNNTPSock(SOCKET sock); - -int NNTPSendSock(SocketConn * sockptr, char * msg) -{ - int len = (int)strlen(msg); - char * newmsg = malloc(len+10); - - WriteLogLine(NULL, '>',msg, len, LOG_TCP); - - strcpy(newmsg, msg); - - strcat(newmsg, "\r\n"); - - len+=2; - - // Attempt to fix Thunderbird - Queue all and send at end - - if ((sockptr->SendSize + len) > sockptr->SendBufferSize) - { - sockptr->SendBufferSize += (10000 + len); - sockptr->SendBuffer = realloc(sockptr->SendBuffer, sockptr->SendBufferSize); - } - - memcpy(&sockptr->SendBuffer[sockptr->SendSize], newmsg, len); - sockptr->SendSize += len; - free (newmsg); - return len; -} - -void NNTPFlush(SocketConn * sockptr) -{ - int sent; - - sent = send(sockptr->socket, sockptr->SendBuffer, sockptr->SendSize, 0); - - if (sent < sockptr->SendSize) - { - int error, remains; - - // Not all could be sent - queue rest - - if (sent == SOCKET_ERROR) - { - error = WSAGetLastError(); - if (error == WSAEWOULDBLOCK) - sent=0; - - // What else?? - } - - remains = sockptr->SendSize - sent; - - sockptr->SendBufferSize += (10000 + remains); - sockptr->SendBuffer = malloc(sockptr->SendBufferSize); - - memmove(sockptr->SendBuffer, &sockptr->SendBuffer[sent], remains); - - sockptr->SendSize = remains; - sockptr->SendPtr = 0; - - return; - } - - free(sockptr->SendBuffer); - sockptr->SendBuffer = NULL; - sockptr->SendSize = 0; - sockptr->SendBufferSize = 0; - return; -} - -VOID __cdecl NNTPsockprintf(SocketConn * sockptr, const char * format, ...) -{ - // printf to a socket - - char buff[1000]; - va_list(arglist); - - va_start(arglist, format); - vsprintf(buff, format, arglist); - - NNTPSendSock(sockptr, buff); -} - -struct NNTPRec * LookupNNTP(char * Group) -{ - struct NNTPRec * ptr = FirstNNTPRec; - - while (ptr) - { - if (_stricmp(ptr->NewsGroup, Group) == 0) - return ptr; - - ptr = ptr->Next; - } - - return NULL; -} - - -VOID BuildNNTPList(struct MsgInfo * Msg) -{ - struct NNTPRec * REC; - struct NNTPRec * OLDREC; - struct NNTPRec * PREVREC = 0; - - char FullGroup[100]; - - if (Msg->type != 'B' || Msg->status == 'K' || Msg->status == 'H') - return; - - sprintf(FullGroup, "%s.%s", Msg->to, Msg->via); - - REC = LookupNNTP(FullGroup); - - if (REC == NULL) - { - // New Group. Allocate a record, and put at correct place in chain (alpha order) - - GetSemaphore(&AllocSemaphore, 0); - - REC = zalloc(sizeof (struct NNTPRec)); - OLDREC = FirstNNTPRec; - - if (OLDREC == 0) // First record - { - FirstNNTPRec = REC; - goto DoneIt; - } - else - { - // Follow chain till we find one with a later name - - while(OLDREC) - { - if (strcmp(OLDREC->NewsGroup, FullGroup) > 0) - { - // chain in here - - REC->Next = OLDREC; - if (PREVREC) - PREVREC->Next = REC; - else - FirstNNTPRec = REC; - goto DoneIt; - - } - else - { - PREVREC = OLDREC; - OLDREC = OLDREC->Next; - } - } - - // Run off end - chain to PREVREC - - PREVREC->Next = REC; - } -DoneIt: - strcpy(REC->NewsGroup, FullGroup); - REC->FirstMsg = Msg->number; - REC->DateCreated = (time_t)Msg->datecreated; - - FreeSemaphore(&AllocSemaphore); - } - - REC->LastMsg = Msg->number; - REC->Count++; -} - -void RebuildNNTPList() -{ - struct NNTPRec * NNTPREC = FirstNNTPRec; - struct NNTPRec * SaveNNTPREC; - struct MsgInfo * Msg; - int i; - - // Free old list - - while (NNTPREC) - { - SaveNNTPREC = NNTPREC->Next; - free(NNTPREC); - NNTPREC = SaveNNTPREC; - } - - FirstNNTPRec = NULL; - - for (i = 1; i <= NumberofMessages; i++) - { - Msg = MsgHddrPtr[i]; - BuildNNTPList(Msg); - } -} - - - -char * GetPathFromHeaders(char * MsgBytes) -{ - char * Path = zalloc(10000); - char * ptr1; - - ptr1 = MsgBytes; - -nextline: - - if (memcmp(ptr1, "R:", 2) == 0) - { - char * ptr4 = strchr(ptr1, '\r'); - char * ptr5 = strchr(ptr1, '.'); - ptr1 = strchr(ptr1, '@'); - - if (!ptr1) - return Path; - - if (*++ptr1 == ':') - ptr1++; // Format 2 - - *(ptr5) = 0; - - strcat(Path, "|"); - strcat(Path, ptr1); - - *(ptr5) = '.'; - - ptr1 = ptr4; - - ptr1++; - if (*ptr1 == '\n') ptr1++; - - goto nextline; - } - - return Path; -} - -char * FormatNNTPDateAndTime(time_t Datim) -{ - struct tm *tm; - static char Date[30]; - - // Fri, 19 Nov 82 16:14:55 GMT - // A#asctime gives Wed Jan 02 02:03:55 1980\n\0. - - tm = gmtime(&Datim); - - - - if (tm) - sprintf_s(Date, sizeof(Date), "%s, %02d %3s %02d %02d:%02d:%02d Z", - day[tm->tm_wday], tm->tm_mday, month[tm->tm_mon], tm->tm_year - 100, tm->tm_hour, tm->tm_min, tm->tm_sec); - - return Date; -} - -VOID InitialiseNNTP() - -{ - if (NNTPInPort) - nntpsock = CreateListeningSocket(NNTPInPort); -} - -int CreateNNTPMessage(char * From, char * To, char * MsgTitle, time_t Date, char * MsgBody, int MsgLen) -{ - struct MsgInfo * Msg; - BIDRec * BIDRec; - char * Via; - - // Allocate a message Record slot - - Msg = AllocateMsgRecord(); - - // Set number here so they remain in sequence - - Msg->number = ++LatestMsg; - MsgnotoMsg[Msg->number] = Msg; - Msg->length = MsgLen; - - sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); - - Msg->type = 'B'; - Msg->status = 'N'; - Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); - - if (Date) - Msg->datecreated = Date; - - BIDRec = AllocateBIDRecord(); - - strcpy(BIDRec->BID, Msg->bid); - BIDRec->mode = Msg->type; - BIDRec->u.msgno = LOWORD(Msg->number); - BIDRec->u.timestamp = LOWORD(time(NULL)/86400); - - - TidyString(To); - Via = strlop(To, '@'); - - if (strlen(To) > 6) To[6]=0; - - strcpy(Msg->to, To); - strcpy(Msg->from, From); - strcpy(Msg->title, MsgTitle); - strcpy(Msg->via, Via); - - // Set up forwarding bitmap - - MatchMessagetoBBSList(Msg, 0); - - if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) - Msg->status = '$'; // Has forwarding - - BuildNNTPList(Msg); // Build NNTP Groups list - -#ifndef NOMQTT - if (MQTT) - MQTTMessageEvent(Msg); -#endif - - - return CreateSMTPMessageFile(MsgBody, Msg); - -} - - - -VOID ProcessNNTPServerMessage(SocketConn * sockptr, char * Buffer, int Len) -{ - SOCKET sock; - time_t Date = 0; - - sock=sockptr->socket; - - WriteLogLine(NULL, '<',Buffer, Len-2, LOG_TCP); - - if (sockptr->Flags == GETTINGMESSAGE) - { - if(memcmp(Buffer, ".\r\n", 3) == 0) - { - // File Message - - char * ptr1, * ptr2; - int linelen, MsgLen; - char MsgFrom[62], MsgTo[62], Msgtitle[62]; - - // Scan headers for From: To: and Subject: Line (Headers end at blank line) - - ptr1 = sockptr->MailBuffer; - Loop: - ptr2 = strchr(ptr1, '\r'); - - if (ptr2 == NULL) - { - NNTPSendSock(sockptr, "500 Eh"); - return; - } - - linelen = (int)(ptr2 - ptr1); - - // From: "John Wiseman" - // To: - // - - - if (_memicmp(ptr1, "From:", 5) == 0) - { - if (linelen > 65) linelen = 65; - memcpy(MsgFrom, ptr1, linelen); - MsgFrom[linelen]=0; - } - else - if (_memicmp(ptr1, "Newsgroups:", 3) == 0) - { - char * sep = strchr(ptr1, '.'); - - if (sep) - *(sep) = '@'; - - if (linelen > 63) linelen = 63; - memcpy(MsgTo, &ptr1[11], linelen-11); - MsgTo[linelen-11]=0; - } - else - if (_memicmp(ptr1, "Subject:", 8) == 0) - { - if (linelen > 68) linelen = 68; - memcpy(Msgtitle, &ptr1[9], linelen-9); - Msgtitle[linelen-9]=0; - } - else - if (_memicmp(ptr1, "Date:", 5) == 0) - { - struct tm rtime; - char * Context; - char seps[] = " ,\t\r"; - char Offset[10] = ""; - int i, HH, MM; - - memset(&rtime, 0, sizeof(struct tm)); - - // Date: Tue, 9 Jun 2009 20:54:55 +0100 - - ptr1 = strtok_s(&ptr1[5], seps, &Context); // Skip Day - ptr1 = strtok_s(NULL, seps, &Context); // Day - - rtime.tm_mday = atoi(ptr1); - - ptr1 = strtok_s(NULL, seps, &Context); // Month - - for (i=0; i < 12; i++) - { - if (strcmp(month[i], ptr1) == 0) - { - rtime.tm_mon = i; - break; - } - } - - sscanf(Context, "%04d %02d:%02d:%02d%s", - &rtime.tm_year, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec, Offset); - - rtime.tm_year -= 1900; - - Date = mktime(&rtime) - (time_t)_MYTIMEZONE; - - if (Date == (time_t)-1) - Date = 0; - else - { - if ((Offset[0] == '+') || (Offset[0] == '-')) - { - MM = atoi(&Offset[3]); - Offset[3] = 0; - HH = atoi(&Offset[1]); - MM = MM + (60 * HH); - - if (Offset[0] == '+') - Date -= (60*MM); - else - Date += (60*MM); - - - } - } - } - - if (linelen) // Not Null line - { - ptr1 = ptr2 + 2; // Skip crlf - goto Loop; - } - - ptr1 = sockptr->MailBuffer; - - MsgLen = sockptr->MailSize - (int)(ptr2 - ptr1); - - ptr1 = strchr(MsgFrom, '<'); - - if (ptr1) - { - char * ptr3 = strchr(ptr1, '@'); - ptr1++; - if (ptr3) - *(ptr3) = 0; - } - else - ptr1 = MsgFrom; - - CreateNNTPMessage(_strupr(ptr1), _strupr(MsgTo), Msgtitle, Date, ptr2, MsgLen); - - free(sockptr->MailBuffer); - sockptr->MailBufferSize=0; - sockptr->MailBuffer=0; - sockptr->MailSize = 0; - - sockptr->Flags &= ~GETTINGMESSAGE; - - NNTPSendSock(sockptr, "240 OK"); - - return; - } - - if ((sockptr->MailSize + Len) > sockptr->MailBufferSize) - { - sockptr->MailBufferSize += 10000; - sockptr->MailBuffer = realloc(sockptr->MailBuffer, sockptr->MailBufferSize); - - if (sockptr->MailBuffer == NULL) - { - CriticalErrorHandler("Failed to extend Message Buffer"); - shutdown(sock, 0); - return; - } - } - - memcpy(&sockptr->MailBuffer[sockptr->MailSize], Buffer, Len); - sockptr->MailSize += Len; - - return; - } - - Buffer[Len-2] = 0; - - if(_memicmp(Buffer, "AUTHINFO USER", 13) == 0) - { - if (Len > 22) Buffer[22]=0; - strcpy(sockptr->CallSign, &Buffer[14]); - sockptr->State = GettingPass; - NNTPsockprintf(sockptr, "381 More authentication information required"); - return; - } - - if (sockptr->State == GettingUser) - { - NNTPsockprintf(sockptr, "480 Authentication required"); - return; - } - - if (sockptr->State == GettingPass) - { - struct UserInfo * user = NULL; - - if(_memicmp(Buffer, "AUTHINFO PASS", 13) == 0) - { - user = LookupCall(sockptr->CallSign); - - if (user) - { - if (strcmp(user->pass, &Buffer[14]) == 0) - { - NNTPsockprintf(sockptr, "281 Authentication accepted"); - - sockptr->State = Authenticated; - sockptr->POP3User = user; - return; - } - } - NNTPSendSock(sockptr, "482 Authentication rejected"); - sockptr->State = GettingUser; - return; - } - - NNTPsockprintf(sockptr, "480 Authentication required"); - return; - } - - - if(_memicmp(Buffer, "GROUP", 5) == 0) - { - struct NNTPRec * REC = FirstNNTPRec; - - while (REC) - { - if (_stricmp(REC->NewsGroup, &Buffer[6]) == 0) - { - NNTPsockprintf(sockptr, "211 %d %d %d %s", REC->Count, REC->FirstMsg, REC->LastMsg, REC->NewsGroup); - sockptr->NNTPNum = 0; - sockptr->NNTPGroup = REC; - return; - } - REC =REC->Next; - } - - NNTPsockprintf(sockptr, "411 no such news group"); - return; - } - - if(_memicmp(Buffer, "LISTGROUP", 9) == 0) - { - struct NNTPRec * REC = sockptr->NNTPGroup; - struct MsgInfo * Msg; - int MsgNo ; - - // Either currently selected, or a param follows - - if (REC == NULL && Buffer[10] == 0) - { - NNTPsockprintf(sockptr, "412 No Group Selected"); - return; - } - - if (Buffer[10] == 0) - goto GotGroup; - - REC = FirstNNTPRec; - - while(REC) - { - if (_stricmp(REC->NewsGroup, &Buffer[10]) == 0) - { - GotGroup: - - NNTPsockprintf(sockptr, "211 Article Numbers Follows"); - sockptr->NNTPNum = 0; - sockptr->NNTPGroup = REC; - - for (MsgNo = REC->FirstMsg; MsgNo <= REC->LastMsg; MsgNo++) - { - Msg=MsgnotoMsg[MsgNo]; - - if (Msg) - { - char FullGroup[100]; - sprintf(FullGroup, "%s.%s", Msg->to, Msg->via ); - if (_stricmp(FullGroup, REC->NewsGroup) == 0) - { - NNTPsockprintf(sockptr, "%d", MsgNo); - } - } - } - NNTPSendSock(sockptr,"."); - return; - } - REC = REC->Next; - } - NNTPsockprintf(sockptr, "411 no such news group"); - return; - } - - if(_memicmp(Buffer, "MODE READER", 11) == 0) - { - NNTPSendSock(sockptr, "200 Hello"); - return; - } - - if(_memicmp(Buffer, "LIST",4) == 0) - { - struct NNTPRec * REC = FirstNNTPRec; - - NNTPSendSock(sockptr, "215 list of newsgroups follows"); - - while (REC) - { - NNTPsockprintf(sockptr, "%s %d %d y", REC->NewsGroup, REC->LastMsg, REC->FirstMsg); - REC = REC->Next; - } - - NNTPSendSock(sockptr,"."); - return; - } - - //NEWGROUPS YYMMDD HHMMSS [GMT] [] - - if(_memicmp(Buffer, "NEWGROUPS", 9) == 0) - { - struct NNTPRec * REC = FirstNNTPRec; - struct tm rtime; - char Offset[20] = ""; - time_t Time; - - memset(&rtime, 0, sizeof(struct tm)); - - sscanf(&Buffer[10], "%02d%02d%02d %02d%02d%02d %s", - &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, - &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec, Offset); - - rtime.tm_year+=100; - rtime.tm_mon--; - - if (_stricmp(Offset, "GMT") == 0) - Time = mktime(&rtime) - (time_t)_MYTIMEZONE; - else - Time = mktime(&rtime); - - NNTPSendSock(sockptr, "231 list of new newsgroups follows"); - - while(REC) - { - if (REC->DateCreated > Time) - NNTPsockprintf(sockptr, "%s %d %d y", REC->NewsGroup, REC->LastMsg, REC->FirstMsg); - REC = REC->Next; - } - - NNTPSendSock(sockptr,"."); - return; - } - - if(_memicmp(Buffer, "HEAD",4) == 0) - { - struct NNTPRec * REC = sockptr->NNTPGroup; - struct MsgInfo * Msg; - int MsgNo = atoi(&Buffer[5]); - - if (REC == NULL) - { - NNTPSendSock(sockptr,"412 no newsgroup has been selected"); - return; - } - - if (MsgNo == 0) - { - MsgNo = sockptr->NNTPNum; - - if (MsgNo == 0) - { - NNTPSendSock(sockptr,"420 no current article has been selected"); - return; - } - } - else - { - sockptr->NNTPNum = MsgNo; - } - - Msg=MsgnotoMsg[MsgNo]; - - if (Msg) - { - NNTPsockprintf(sockptr, "221 %d <%s>", MsgNo, Msg->bid); - - NNTPsockprintf(sockptr, "From: %s", Msg->from); - NNTPsockprintf(sockptr, "Date: %s", FormatNNTPDateAndTime((time_t)Msg->datecreated)); - NNTPsockprintf(sockptr, "Newsgroups: %s.s", Msg->to, Msg->via); - NNTPsockprintf(sockptr, "Subject: %s", Msg->title); - NNTPsockprintf(sockptr, "Message-ID: <%s>", Msg->bid); - NNTPsockprintf(sockptr, "Path: %s", BBSName); - - NNTPSendSock(sockptr,"."); - return; - } - - NNTPSendSock(sockptr,"423 No such article in this newsgroup"); - return; - } - - if(_memicmp(Buffer, "ARTICLE", 7) == 0) - { - struct NNTPRec * REC = sockptr->NNTPGroup; - struct MsgInfo * Msg; - int MsgNo = atoi(&Buffer[8]); - char * msgbytes; - char * Path; - - if (REC == NULL) - { - NNTPSendSock(sockptr,"412 no newsgroup has been selected"); - return; - } - - if (MsgNo == 0) - { - MsgNo = sockptr->NNTPNum; - - if (MsgNo == 0) - { - NNTPSendSock(sockptr,"420 no current article has been selected"); - return; - } - } - else - { - sockptr->NNTPNum = MsgNo; - } - - Msg=MsgnotoMsg[MsgNo]; - - if (Msg) - { - NNTPsockprintf(sockptr, "220 %d <%s>", MsgNo, Msg->bid); - msgbytes = ReadMessageFile(Msg->number); - - Path = GetPathFromHeaders(msgbytes); - - NNTPsockprintf(sockptr, "From: %s", Msg->from); - NNTPsockprintf(sockptr, "Date: %s", FormatNNTPDateAndTime((time_t)Msg->datecreated)); - NNTPsockprintf(sockptr, "Newsgroups: %s.%s", Msg->to, Msg->via); - NNTPsockprintf(sockptr, "Subject: %s", Msg->title); - NNTPsockprintf(sockptr, "Message-ID: <%s>", Msg->bid); - NNTPsockprintf(sockptr, "Path: %s", &Path[1]); - - NNTPSendSock(sockptr,""); - - - NNTPSendSock(sockptr,msgbytes); - NNTPSendSock(sockptr,""); - - NNTPSendSock(sockptr,"."); - - free(msgbytes); - free(Path); - - return; - - } - NNTPSendSock(sockptr,"423 No such article in this newsgroup"); - return; - } - - if(_memicmp(Buffer, "BODY", 4) == 0) - { - struct NNTPRec * REC = sockptr->NNTPGroup; - struct MsgInfo * Msg; - int MsgNo = atoi(&Buffer[8]); - char * msgbytes; - char * Path; - - if (REC == NULL) - { - NNTPSendSock(sockptr,"412 no newsgroup has been selected"); - return; - } - - if (MsgNo == 0) - { - MsgNo = sockptr->NNTPNum; - - if (MsgNo == 0) - { - NNTPSendSock(sockptr,"420 no current article has been selected"); - return; - } - } - else - { - sockptr->NNTPNum = MsgNo; - } - - Msg=MsgnotoMsg[MsgNo]; - - if (Msg) - { - NNTPsockprintf(sockptr, "222 %d <%s>", MsgNo, Msg->bid); - msgbytes = ReadMessageFile(Msg->number); - - Path = GetPathFromHeaders(msgbytes); - - NNTPSendSock(sockptr,msgbytes); - NNTPSendSock(sockptr,""); - - NNTPSendSock(sockptr,"."); - - free(msgbytes); - free(Path); - - return; - - } - NNTPSendSock(sockptr,"423 No such article in this newsgroup"); - return; - } - - if(_memicmp(Buffer, "XHDR",4) == 0) - { - struct NNTPRec * REC = sockptr->NNTPGroup; - struct MsgInfo * Msg; - int MsgStart, MsgEnd, MsgNo, fields; - char Header[100]; - - if (REC == NULL) - { - NNTPSendSock(sockptr,"412 no newsgroup has been selected"); - return; - } - - // XHDR subject nnnn-nnnn - - fields = sscanf(&Buffer[5], "%s %d-%d", &Header[0], &MsgStart, &MsgEnd); - - if (fields > 1) - MsgNo = MsgStart; - - if (fields == 2) - MsgEnd = MsgStart; - - if (MsgNo == 0) - { - MsgStart = MsgEnd = sockptr->NNTPNum; - - if (MsgStart == 0) - { - NNTPSendSock(sockptr,"420 no current article has been selected"); - return; - } - } - else - { - sockptr->NNTPNum = MsgEnd; - } - - NNTPsockprintf(sockptr, "221 "); - - for (MsgNo = MsgStart; MsgNo <= MsgEnd; MsgNo++) - { - Msg=MsgnotoMsg[MsgNo]; - - if (Msg) - { - char FullGroup[100]; - sprintf(FullGroup, "%s.%s", Msg->to, Msg->via ); - if (_stricmp(FullGroup, REC->NewsGroup) == 0) - { - if (_stricmp(Header, "subject") == 0) - NNTPsockprintf(sockptr, "%d Subject: %s", MsgNo, Msg->title); - else if (_stricmp(Header, "from") == 0) - NNTPsockprintf(sockptr, "%d From: %s", MsgNo, Msg->from); - else if (_stricmp(Header, "date") == 0) - NNTPsockprintf(sockptr, "%d Date: %s", MsgNo, FormatNNTPDateAndTime((time_t)Msg->datecreated)); - else if (_stricmp(Header, "message-id") == 0) - NNTPsockprintf(sockptr, "%d Message-ID: <%s>", MsgNo, Msg->bid); - else if (_stricmp(Header, "lines") == 0) - NNTPsockprintf(sockptr, "%d Lines: %d", MsgNo, Msg->length); - } - } - } - - NNTPSendSock(sockptr,"."); - return; - - } - - if(_memicmp(Buffer, "XOVER", 5) == 0) - { - struct NNTPRec * REC = sockptr->NNTPGroup; - struct MsgInfo * Msg; - int MsgStart, MsgEnd, MsgNo, fields; - - if (REC == NULL) - { - NNTPSendSock(sockptr,"412 no newsgroup has been selected"); - return; - } - - fields = sscanf(&Buffer[6], "%d-%d", &MsgStart, &MsgEnd); - - if (fields > 0) - MsgNo = MsgStart; - - if (fields == 1) - MsgEnd = MsgStart; - - if (MsgNo == 0) - { - MsgStart = MsgEnd = sockptr->NNTPNum; - - if (MsgStart == 0) - { - NNTPSendSock(sockptr,"420 no current article has been selected"); - return; - } - } - else - { - sockptr->NNTPNum = MsgEnd; - } - - NNTPsockprintf(sockptr, "224 "); - - for (MsgNo = MsgStart; MsgNo <= MsgEnd; MsgNo++) - { - Msg=MsgnotoMsg[MsgNo]; - - if (Msg) - { - char FullGroup[100]; - sprintf(FullGroup, "%s.%s", Msg->to, Msg->via ); - if (_stricmp(FullGroup, REC->NewsGroup) == 0) - { - // subject, author, date, message-id, references, byte count, and line count. - NNTPsockprintf(sockptr, "%d\t%s\t%s\t%s\t%s\t%s\t%d\t%d", - MsgNo, Msg->title, Msg->from, FormatNNTPDateAndTime((time_t)Msg->datecreated), Msg->bid, - "", Msg->length, Msg->length); - } - } - } - - NNTPSendSock(sockptr,"."); - return; - - } - - - /* - 240 article posted ok - 340 send article to be posted. End with . - 440 posting not allowed - 441 posting failed -*/ - if(_memicmp(Buffer, "POST", 4) == 0) - { - if (sockptr->State != Authenticated) - { - NNTPsockprintf(sockptr, "480 Authentication required"); - return; - } - - sockptr->MailBuffer=malloc(10000); - sockptr->MailBufferSize=10000; - - if (sockptr->MailBuffer == NULL) - { - CriticalErrorHandler("Failed to create POP3 Message Buffer"); - NNTPSendSock(sockptr, "QUIT"); - sockptr->State = WaitingForQUITResponse; - shutdown(sock, 0); - - return; - } - - sockptr->Flags |= GETTINGMESSAGE; - - NNTPSendSock(sockptr, "340 OK"); - return; - } - - - - if(_memicmp(Buffer, "QUIT", 4) == 0) - { - NNTPSendSock(sockptr, "205 OK"); - Sleep(500); - shutdown(sock, 0); - return; - } - -/* if(memcmp(Buffer, "RSET\r\n", 6) == 0) - { - NNTPSendSock(sockptr, "250 Ok"); - sockptr->State = 0; - sockptr->Recipients; - return; - } -*/ - - if(memcmp(Buffer, "DATE", 4) == 0) - { - //This command returns a one-line response code of 111 followed by the - //GMT date and time on the server in the form YYYYMMDDhhmmss. - // 111 YYYYMMDDhhmmss - - struct tm *tm; - char Date[32]; - time_t Time = time(NULL); - - tm = gmtime(&Time); - - if (tm) - { - sprintf_s(Date, sizeof(Date), "111 %04d%02d%02d%02d%02d%02d", - tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); - - NNTPSendSock(sockptr, Date); - } - else - NNTPSendSock(sockptr, "500 command not recognized"); - - return; - } - - NNTPSendSock(sockptr, "500 command not recognized"); - - return; -} - - - - -int NNTP_Read(SocketConn * sockptr, SOCKET sock) -{ - int InputLen, MsgLen; - char * ptr, * ptr2; - char Buffer[2000]; - - // May have several messages per packet, or message split over packets - - if (sockptr->InputLen > 1000) // Shouldnt have lines longer than this in text mode - { - sockptr->InputLen=0; - } - - InputLen=recv(sock, &sockptr->TCPBuffer[sockptr->InputLen], 1000, 0); - - if (InputLen <= 0) - { - int x = WSAGetLastError(); - - closesocket(sock); - ReleaseSock(sock); - - return 0; // Does this mean closed? - } - - sockptr->InputLen += InputLen; - -loop: - - ptr = memchr(sockptr->TCPBuffer, '\n', sockptr->InputLen); - - if (ptr) // CR in buffer - { - ptr2 = &sockptr->TCPBuffer[sockptr->InputLen]; - ptr++; // Assume LF Follows CR - - if (ptr == ptr2) - { - // Usual Case - single meg in buffer - - ProcessNNTPServerMessage(sockptr, sockptr->TCPBuffer, sockptr->InputLen); - sockptr->InputLen=0; - } - else - { - // buffer contains more that 1 message - - MsgLen = sockptr->InputLen - (int)(ptr2-ptr); - - memcpy(Buffer, sockptr->TCPBuffer, MsgLen); - - ProcessNNTPServerMessage(sockptr, Buffer, MsgLen); - - memmove(sockptr->TCPBuffer, ptr, sockptr->InputLen-MsgLen); - - sockptr->InputLen -= MsgLen; - - goto loop; - - } - } - - NNTPFlush(sockptr); - - return 0; -} - - -int NNTP_Accept(SOCKET SocketId) -{ - int addrlen; - SocketConn * sockptr; - u_long param = 1; - - SOCKET sock; - - addrlen=sizeof(struct sockaddr); - - // Allocate a Socket entry - - sockptr=zalloc(sizeof(SocketConn)+100); - - sockptr->Next = Sockets; - Sockets=sockptr; - - sock = accept(SocketId, (struct sockaddr *)&sockptr->sin, &addrlen); - - if (sock == INVALID_SOCKET) - { - Logprintf(LOG_TCP, NULL, '|', "NNTP accept() failed Error %d", WSAGetLastError()); - - // get rid of socket record - - Sockets = sockptr->Next; - free(sockptr); - - return FALSE; - } - - - sockptr->Type = NNTPServer; - - ioctl(sock, FIONBIO, ¶m); - sockptr->socket = sock; - sockptr->State = 0; - - NNTPSendSock(sockptr, "200 BPQMail NNTP Server ready"); - Logprintf(LOG_TCP, NULL, '|', "Incoming NNTP Connect Socket = %d", sock); - - NNTPFlush(sockptr); - - return 0; -} -/* -int NNTP_Data(int sock, int error, int eventcode) -{ - SocketConn * sockptr; - - // Find Connection Record - - sockptr=Sockets; - - while (sockptr) - { - if (sockptr->socket == sock) - { - switch (eventcode) - { - case FD_READ: - - return NNTP_Read(sockptr,sock); - - case FD_WRITE: - - // Either Just connected, or flow contorl cleared - - if (sockptr->SendBuffer) - // Data Queued - - SendFromQueue(sockptr); - else - { - NNTPSendSock(sockptr, "200 BPQMail NNTP Server ready"); -// sockptr->State = GettingUser; - } - - return 0; - - case FD_OOB: - - return 0; - - case FD_ACCEPT: - - return 0; - - case FD_CONNECT: - - return 0; - - case FD_CLOSE: - - closesocket(sock); - ReleaseNNTPSock(sock); - return 0; - } - return 0; - } - else - sockptr=sockptr->Next; - } - - return 0; -} -*/ -VOID ReleaseNNTPSock(SOCKET sock) -{ - // remove and free the socket record - - SocketConn * sockptr, * lastptr; - - sockptr=Sockets; - lastptr=NULL; - - while (sockptr) - { - if (sockptr->socket == sock) - { - if (lastptr) - lastptr->Next=sockptr->Next; - else - Sockets=sockptr->Next; - - free(sockptr); - return; - } - else - { - lastptr=sockptr; - sockptr=sockptr->Next; - } - } - - return; -} - -VOID SendFromQueue(SocketConn * sockptr) -{ - int bytestosend = sockptr->SendSize - sockptr->SendPtr; - int bytessent; - - Debugprintf("TCP - Sending %d bytes from buffer", bytestosend); - - bytessent = send(sockptr->socket, &sockptr->SendBuffer[sockptr->SendPtr], bytestosend, 0); - - if (bytessent == bytestosend) - { - // All Sent - - free(sockptr->SendBuffer); - sockptr->SendBuffer = NULL; - } - else - { - sockptr->SendPtr += bytessent; - } - - return; -} +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// Mail and Chat Server for BPQ32 Packet Switch +// +// White Pages Database Support Routines + +#include "bpqmail.h" + +VOID __cdecl Debugprintf(const char * format, ...); +VOID ReleaseSock(SOCKET sock); +void MQTTMessageEvent(void* message); + +#define GetSemaphore(Semaphore,ID) _GetSemaphore(Semaphore, ID, __FILE__, __LINE__) +void _GetSemaphore(struct SEM * Semaphore, int ID, char * File, int Line); + +struct NNTPRec * FirstNNTPRec = NULL; + +//int NumberofNNTPRecs=0; + +SOCKET nntpsock; + +extern SocketConn * Sockets; // Chain of active sockets + +int NNTPInPort = 0; + +char *day[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + + +VOID ReleaseNNTPSock(SOCKET sock); + +int NNTPSendSock(SocketConn * sockptr, char * msg) +{ + int len = (int)strlen(msg); + char * newmsg = malloc(len+10); + + WriteLogLine(NULL, '>',msg, len, LOG_TCP); + + strcpy(newmsg, msg); + + strcat(newmsg, "\r\n"); + + len+=2; + + // Attempt to fix Thunderbird - Queue all and send at end + + if ((sockptr->SendSize + len) > sockptr->SendBufferSize) + { + sockptr->SendBufferSize += (10000 + len); + sockptr->SendBuffer = realloc(sockptr->SendBuffer, sockptr->SendBufferSize); + } + + memcpy(&sockptr->SendBuffer[sockptr->SendSize], newmsg, len); + sockptr->SendSize += len; + free (newmsg); + return len; +} + +void NNTPFlush(SocketConn * sockptr) +{ + int sent; + + sent = send(sockptr->socket, sockptr->SendBuffer, sockptr->SendSize, 0); + + if (sent < sockptr->SendSize) + { + int error, remains; + + // Not all could be sent - queue rest + + if (sent == SOCKET_ERROR) + { + error = WSAGetLastError(); + if (error == WSAEWOULDBLOCK) + sent=0; + + // What else?? + } + + remains = sockptr->SendSize - sent; + + sockptr->SendBufferSize += (10000 + remains); + sockptr->SendBuffer = malloc(sockptr->SendBufferSize); + + memmove(sockptr->SendBuffer, &sockptr->SendBuffer[sent], remains); + + sockptr->SendSize = remains; + sockptr->SendPtr = 0; + + return; + } + + free(sockptr->SendBuffer); + sockptr->SendBuffer = NULL; + sockptr->SendSize = 0; + sockptr->SendBufferSize = 0; + return; +} + +VOID __cdecl NNTPsockprintf(SocketConn * sockptr, const char * format, ...) +{ + // printf to a socket + + char buff[1000]; + va_list(arglist); + + va_start(arglist, format); + vsprintf(buff, format, arglist); + + NNTPSendSock(sockptr, buff); +} + +struct NNTPRec * LookupNNTP(char * Group) +{ + struct NNTPRec * ptr = FirstNNTPRec; + + while (ptr) + { + if (_stricmp(ptr->NewsGroup, Group) == 0) + return ptr; + + ptr = ptr->Next; + } + + return NULL; +} + + +VOID BuildNNTPList(struct MsgInfo * Msg) +{ + struct NNTPRec * REC; + struct NNTPRec * OLDREC; + struct NNTPRec * PREVREC = 0; + + char FullGroup[100]; + + if (Msg->type != 'B' || Msg->status == 'K' || Msg->status == 'H') + return; + + sprintf(FullGroup, "%s.%s", Msg->to, Msg->via); + + REC = LookupNNTP(FullGroup); + + if (REC == NULL) + { + // New Group. Allocate a record, and put at correct place in chain (alpha order) + + GetSemaphore(&AllocSemaphore, 0); + + REC = zalloc(sizeof (struct NNTPRec)); + OLDREC = FirstNNTPRec; + + if (OLDREC == 0) // First record + { + FirstNNTPRec = REC; + goto DoneIt; + } + else + { + // Follow chain till we find one with a later name + + while(OLDREC) + { + if (strcmp(OLDREC->NewsGroup, FullGroup) > 0) + { + // chain in here + + REC->Next = OLDREC; + if (PREVREC) + PREVREC->Next = REC; + else + FirstNNTPRec = REC; + goto DoneIt; + + } + else + { + PREVREC = OLDREC; + OLDREC = OLDREC->Next; + } + } + + // Run off end - chain to PREVREC + + PREVREC->Next = REC; + } +DoneIt: + strcpy(REC->NewsGroup, FullGroup); + REC->FirstMsg = Msg->number; + REC->DateCreated = (time_t)Msg->datecreated; + + FreeSemaphore(&AllocSemaphore); + } + + REC->LastMsg = Msg->number; + REC->Count++; +} + +void RebuildNNTPList() +{ + struct NNTPRec * NNTPREC = FirstNNTPRec; + struct NNTPRec * SaveNNTPREC; + struct MsgInfo * Msg; + int i; + + // Free old list + + while (NNTPREC) + { + SaveNNTPREC = NNTPREC->Next; + free(NNTPREC); + NNTPREC = SaveNNTPREC; + } + + FirstNNTPRec = NULL; + + for (i = 1; i <= NumberofMessages; i++) + { + Msg = MsgHddrPtr[i]; + BuildNNTPList(Msg); + } +} + + + +char * GetPathFromHeaders(char * MsgBytes) +{ + char * Path = zalloc(10000); + char * ptr1; + + ptr1 = MsgBytes; + +nextline: + + if (memcmp(ptr1, "R:", 2) == 0) + { + char * ptr4 = strchr(ptr1, '\r'); + char * ptr5 = strchr(ptr1, '.'); + ptr1 = strchr(ptr1, '@'); + + if (!ptr1) + return Path; + + if (*++ptr1 == ':') + ptr1++; // Format 2 + + *(ptr5) = 0; + + strcat(Path, "|"); + strcat(Path, ptr1); + + *(ptr5) = '.'; + + ptr1 = ptr4; + + ptr1++; + if (*ptr1 == '\n') ptr1++; + + goto nextline; + } + + return Path; +} + +char * FormatNNTPDateAndTime(time_t Datim) +{ + struct tm *tm; + static char Date[30]; + + // Fri, 19 Nov 82 16:14:55 GMT + // A#asctime gives Wed Jan 02 02:03:55 1980\n\0. + + tm = gmtime(&Datim); + + + + if (tm) + sprintf_s(Date, sizeof(Date), "%s, %02d %3s %02d %02d:%02d:%02d Z", + day[tm->tm_wday], tm->tm_mday, month[tm->tm_mon], tm->tm_year - 100, tm->tm_hour, tm->tm_min, tm->tm_sec); + + return Date; +} + +VOID InitialiseNNTP() + +{ + if (NNTPInPort) + nntpsock = CreateListeningSocket(NNTPInPort); +} + +int CreateNNTPMessage(char * From, char * To, char * MsgTitle, time_t Date, char * MsgBody, int MsgLen) +{ + struct MsgInfo * Msg; + BIDRec * BIDRec; + char * Via; + + // Allocate a message Record slot + + Msg = AllocateMsgRecord(); + + // Set number here so they remain in sequence + + Msg->number = ++LatestMsg; + MsgnotoMsg[Msg->number] = Msg; + Msg->length = MsgLen; + + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); + + Msg->type = 'B'; + Msg->status = 'N'; + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + if (Date) + Msg->datecreated = Date; + + BIDRec = AllocateBIDRecord(); + + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + + TidyString(To); + Via = strlop(To, '@'); + + if (strlen(To) > 6) To[6]=0; + + strcpy(Msg->to, To); + strcpy(Msg->from, From); + strcpy(Msg->title, MsgTitle); + strcpy(Msg->via, Via); + + // Set up forwarding bitmap + + MatchMessagetoBBSList(Msg, 0); + + if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) + Msg->status = '$'; // Has forwarding + + BuildNNTPList(Msg); // Build NNTP Groups list + +#ifndef NOMQTT + if (MQTT) + MQTTMessageEvent(Msg); +#endif + + + return CreateSMTPMessageFile(MsgBody, Msg); + +} + + + +VOID ProcessNNTPServerMessage(SocketConn * sockptr, char * Buffer, int Len) +{ + SOCKET sock; + time_t Date = 0; + + sock=sockptr->socket; + + WriteLogLine(NULL, '<',Buffer, Len-2, LOG_TCP); + + if (sockptr->Flags == GETTINGMESSAGE) + { + if(memcmp(Buffer, ".\r\n", 3) == 0) + { + // File Message + + char * ptr1, * ptr2; + int linelen, MsgLen; + char MsgFrom[62], MsgTo[62], Msgtitle[62]; + + // Scan headers for From: To: and Subject: Line (Headers end at blank line) + + ptr1 = sockptr->MailBuffer; + Loop: + ptr2 = strchr(ptr1, '\r'); + + if (ptr2 == NULL) + { + NNTPSendSock(sockptr, "500 Eh"); + return; + } + + linelen = (int)(ptr2 - ptr1); + + // From: "John Wiseman" + // To: + // + + + if (_memicmp(ptr1, "From:", 5) == 0) + { + if (linelen > 65) linelen = 65; + memcpy(MsgFrom, ptr1, linelen); + MsgFrom[linelen]=0; + } + else + if (_memicmp(ptr1, "Newsgroups:", 3) == 0) + { + char * sep = strchr(ptr1, '.'); + + if (sep) + *(sep) = '@'; + + if (linelen > 63) linelen = 63; + memcpy(MsgTo, &ptr1[11], linelen-11); + MsgTo[linelen-11]=0; + } + else + if (_memicmp(ptr1, "Subject:", 8) == 0) + { + if (linelen > 68) linelen = 68; + memcpy(Msgtitle, &ptr1[9], linelen-9); + Msgtitle[linelen-9]=0; + } + else + if (_memicmp(ptr1, "Date:", 5) == 0) + { + struct tm rtime; + char * Context; + char seps[] = " ,\t\r"; + char Offset[10] = ""; + int i, HH, MM; + + memset(&rtime, 0, sizeof(struct tm)); + + // Date: Tue, 9 Jun 2009 20:54:55 +0100 + + ptr1 = strtok_s(&ptr1[5], seps, &Context); // Skip Day + ptr1 = strtok_s(NULL, seps, &Context); // Day + + rtime.tm_mday = atoi(ptr1); + + ptr1 = strtok_s(NULL, seps, &Context); // Month + + for (i=0; i < 12; i++) + { + if (strcmp(month[i], ptr1) == 0) + { + rtime.tm_mon = i; + break; + } + } + + sscanf(Context, "%04d %02d:%02d:%02d%s", + &rtime.tm_year, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec, Offset); + + rtime.tm_year -= 1900; + + Date = mktime(&rtime) - (time_t)_MYTIMEZONE; + + if (Date == (time_t)-1) + Date = 0; + else + { + if ((Offset[0] == '+') || (Offset[0] == '-')) + { + MM = atoi(&Offset[3]); + Offset[3] = 0; + HH = atoi(&Offset[1]); + MM = MM + (60 * HH); + + if (Offset[0] == '+') + Date -= (60*MM); + else + Date += (60*MM); + + + } + } + } + + if (linelen) // Not Null line + { + ptr1 = ptr2 + 2; // Skip crlf + goto Loop; + } + + ptr1 = sockptr->MailBuffer; + + MsgLen = sockptr->MailSize - (int)(ptr2 - ptr1); + + ptr1 = strchr(MsgFrom, '<'); + + if (ptr1) + { + char * ptr3 = strchr(ptr1, '@'); + ptr1++; + if (ptr3) + *(ptr3) = 0; + } + else + ptr1 = MsgFrom; + + CreateNNTPMessage(_strupr(ptr1), _strupr(MsgTo), Msgtitle, Date, ptr2, MsgLen); + + free(sockptr->MailBuffer); + sockptr->MailBufferSize=0; + sockptr->MailBuffer=0; + sockptr->MailSize = 0; + + sockptr->Flags &= ~GETTINGMESSAGE; + + NNTPSendSock(sockptr, "240 OK"); + + return; + } + + if ((sockptr->MailSize + Len) > sockptr->MailBufferSize) + { + sockptr->MailBufferSize += 10000; + sockptr->MailBuffer = realloc(sockptr->MailBuffer, sockptr->MailBufferSize); + + if (sockptr->MailBuffer == NULL) + { + CriticalErrorHandler("Failed to extend Message Buffer"); + shutdown(sock, 0); + return; + } + } + + memcpy(&sockptr->MailBuffer[sockptr->MailSize], Buffer, Len); + sockptr->MailSize += Len; + + return; + } + + Buffer[Len-2] = 0; + + if(_memicmp(Buffer, "AUTHINFO USER", 13) == 0) + { + if (Len > 22) Buffer[22]=0; + strcpy(sockptr->CallSign, &Buffer[14]); + sockptr->State = GettingPass; + NNTPsockprintf(sockptr, "381 More authentication information required"); + return; + } + + if (sockptr->State == GettingUser) + { + NNTPsockprintf(sockptr, "480 Authentication required"); + return; + } + + if (sockptr->State == GettingPass) + { + struct UserInfo * user = NULL; + + if(_memicmp(Buffer, "AUTHINFO PASS", 13) == 0) + { + user = LookupCall(sockptr->CallSign); + + if (user) + { + if (strcmp(user->pass, &Buffer[14]) == 0) + { + NNTPsockprintf(sockptr, "281 Authentication accepted"); + + sockptr->State = Authenticated; + sockptr->POP3User = user; + return; + } + } + NNTPSendSock(sockptr, "482 Authentication rejected"); + sockptr->State = GettingUser; + return; + } + + NNTPsockprintf(sockptr, "480 Authentication required"); + return; + } + + + if(_memicmp(Buffer, "GROUP", 5) == 0) + { + struct NNTPRec * REC = FirstNNTPRec; + + while (REC) + { + if (_stricmp(REC->NewsGroup, &Buffer[6]) == 0) + { + NNTPsockprintf(sockptr, "211 %d %d %d %s", REC->Count, REC->FirstMsg, REC->LastMsg, REC->NewsGroup); + sockptr->NNTPNum = 0; + sockptr->NNTPGroup = REC; + return; + } + REC =REC->Next; + } + + NNTPsockprintf(sockptr, "411 no such news group"); + return; + } + + if(_memicmp(Buffer, "LISTGROUP", 9) == 0) + { + struct NNTPRec * REC = sockptr->NNTPGroup; + struct MsgInfo * Msg; + int MsgNo ; + + // Either currently selected, or a param follows + + if (REC == NULL && Buffer[10] == 0) + { + NNTPsockprintf(sockptr, "412 No Group Selected"); + return; + } + + if (Buffer[10] == 0) + goto GotGroup; + + REC = FirstNNTPRec; + + while(REC) + { + if (_stricmp(REC->NewsGroup, &Buffer[10]) == 0) + { + GotGroup: + + NNTPsockprintf(sockptr, "211 Article Numbers Follows"); + sockptr->NNTPNum = 0; + sockptr->NNTPGroup = REC; + + for (MsgNo = REC->FirstMsg; MsgNo <= REC->LastMsg; MsgNo++) + { + Msg=MsgnotoMsg[MsgNo]; + + if (Msg) + { + char FullGroup[100]; + sprintf(FullGroup, "%s.%s", Msg->to, Msg->via ); + if (_stricmp(FullGroup, REC->NewsGroup) == 0) + { + NNTPsockprintf(sockptr, "%d", MsgNo); + } + } + } + NNTPSendSock(sockptr,"."); + return; + } + REC = REC->Next; + } + NNTPsockprintf(sockptr, "411 no such news group"); + return; + } + + if(_memicmp(Buffer, "MODE READER", 11) == 0) + { + NNTPSendSock(sockptr, "200 Hello"); + return; + } + + if(_memicmp(Buffer, "LIST",4) == 0) + { + struct NNTPRec * REC = FirstNNTPRec; + + NNTPSendSock(sockptr, "215 list of newsgroups follows"); + + while (REC) + { + NNTPsockprintf(sockptr, "%s %d %d y", REC->NewsGroup, REC->LastMsg, REC->FirstMsg); + REC = REC->Next; + } + + NNTPSendSock(sockptr,"."); + return; + } + + //NEWGROUPS YYMMDD HHMMSS [GMT] [] + + if(_memicmp(Buffer, "NEWGROUPS", 9) == 0) + { + struct NNTPRec * REC = FirstNNTPRec; + struct tm rtime; + char Offset[20] = ""; + time_t Time; + + memset(&rtime, 0, sizeof(struct tm)); + + sscanf(&Buffer[10], "%02d%02d%02d %02d%02d%02d %s", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, + &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec, Offset); + + rtime.tm_year+=100; + rtime.tm_mon--; + + if (_stricmp(Offset, "GMT") == 0) + Time = mktime(&rtime) - (time_t)_MYTIMEZONE; + else + Time = mktime(&rtime); + + NNTPSendSock(sockptr, "231 list of new newsgroups follows"); + + while(REC) + { + if (REC->DateCreated > Time) + NNTPsockprintf(sockptr, "%s %d %d y", REC->NewsGroup, REC->LastMsg, REC->FirstMsg); + REC = REC->Next; + } + + NNTPSendSock(sockptr,"."); + return; + } + + if(_memicmp(Buffer, "HEAD",4) == 0) + { + struct NNTPRec * REC = sockptr->NNTPGroup; + struct MsgInfo * Msg; + int MsgNo = atoi(&Buffer[5]); + + if (REC == NULL) + { + NNTPSendSock(sockptr,"412 no newsgroup has been selected"); + return; + } + + if (MsgNo == 0) + { + MsgNo = sockptr->NNTPNum; + + if (MsgNo == 0) + { + NNTPSendSock(sockptr,"420 no current article has been selected"); + return; + } + } + else + { + sockptr->NNTPNum = MsgNo; + } + + Msg=MsgnotoMsg[MsgNo]; + + if (Msg) + { + NNTPsockprintf(sockptr, "221 %d <%s>", MsgNo, Msg->bid); + + NNTPsockprintf(sockptr, "From: %s", Msg->from); + NNTPsockprintf(sockptr, "Date: %s", FormatNNTPDateAndTime((time_t)Msg->datecreated)); + NNTPsockprintf(sockptr, "Newsgroups: %s.s", Msg->to, Msg->via); + NNTPsockprintf(sockptr, "Subject: %s", Msg->title); + NNTPsockprintf(sockptr, "Message-ID: <%s>", Msg->bid); + NNTPsockprintf(sockptr, "Path: %s", BBSName); + + NNTPSendSock(sockptr,"."); + return; + } + + NNTPSendSock(sockptr,"423 No such article in this newsgroup"); + return; + } + + if(_memicmp(Buffer, "ARTICLE", 7) == 0) + { + struct NNTPRec * REC = sockptr->NNTPGroup; + struct MsgInfo * Msg; + int MsgNo = atoi(&Buffer[8]); + char * msgbytes; + char * Path; + + if (REC == NULL) + { + NNTPSendSock(sockptr,"412 no newsgroup has been selected"); + return; + } + + if (MsgNo == 0) + { + MsgNo = sockptr->NNTPNum; + + if (MsgNo == 0) + { + NNTPSendSock(sockptr,"420 no current article has been selected"); + return; + } + } + else + { + sockptr->NNTPNum = MsgNo; + } + + Msg=MsgnotoMsg[MsgNo]; + + if (Msg) + { + NNTPsockprintf(sockptr, "220 %d <%s>", MsgNo, Msg->bid); + msgbytes = ReadMessageFile(Msg->number); + + Path = GetPathFromHeaders(msgbytes); + + NNTPsockprintf(sockptr, "From: %s", Msg->from); + NNTPsockprintf(sockptr, "Date: %s", FormatNNTPDateAndTime((time_t)Msg->datecreated)); + NNTPsockprintf(sockptr, "Newsgroups: %s.%s", Msg->to, Msg->via); + NNTPsockprintf(sockptr, "Subject: %s", Msg->title); + NNTPsockprintf(sockptr, "Message-ID: <%s>", Msg->bid); + NNTPsockprintf(sockptr, "Path: %s", &Path[1]); + + NNTPSendSock(sockptr,""); + + + NNTPSendSock(sockptr,msgbytes); + NNTPSendSock(sockptr,""); + + NNTPSendSock(sockptr,"."); + + free(msgbytes); + free(Path); + + return; + + } + NNTPSendSock(sockptr,"423 No such article in this newsgroup"); + return; + } + + if(_memicmp(Buffer, "BODY", 4) == 0) + { + struct NNTPRec * REC = sockptr->NNTPGroup; + struct MsgInfo * Msg; + int MsgNo = atoi(&Buffer[8]); + char * msgbytes; + char * Path; + + if (REC == NULL) + { + NNTPSendSock(sockptr,"412 no newsgroup has been selected"); + return; + } + + if (MsgNo == 0) + { + MsgNo = sockptr->NNTPNum; + + if (MsgNo == 0) + { + NNTPSendSock(sockptr,"420 no current article has been selected"); + return; + } + } + else + { + sockptr->NNTPNum = MsgNo; + } + + Msg=MsgnotoMsg[MsgNo]; + + if (Msg) + { + NNTPsockprintf(sockptr, "222 %d <%s>", MsgNo, Msg->bid); + msgbytes = ReadMessageFile(Msg->number); + + Path = GetPathFromHeaders(msgbytes); + + NNTPSendSock(sockptr,msgbytes); + NNTPSendSock(sockptr,""); + + NNTPSendSock(sockptr,"."); + + free(msgbytes); + free(Path); + + return; + + } + NNTPSendSock(sockptr,"423 No such article in this newsgroup"); + return; + } + + if(_memicmp(Buffer, "XHDR",4) == 0) + { + struct NNTPRec * REC = sockptr->NNTPGroup; + struct MsgInfo * Msg; + int MsgStart, MsgEnd, MsgNo, fields; + char Header[100]; + + if (REC == NULL) + { + NNTPSendSock(sockptr,"412 no newsgroup has been selected"); + return; + } + + // XHDR subject nnnn-nnnn + + fields = sscanf(&Buffer[5], "%s %d-%d", &Header[0], &MsgStart, &MsgEnd); + + if (fields > 1) + MsgNo = MsgStart; + + if (fields == 2) + MsgEnd = MsgStart; + + if (MsgNo == 0) + { + MsgStart = MsgEnd = sockptr->NNTPNum; + + if (MsgStart == 0) + { + NNTPSendSock(sockptr,"420 no current article has been selected"); + return; + } + } + else + { + sockptr->NNTPNum = MsgEnd; + } + + NNTPsockprintf(sockptr, "221 "); + + for (MsgNo = MsgStart; MsgNo <= MsgEnd; MsgNo++) + { + Msg=MsgnotoMsg[MsgNo]; + + if (Msg) + { + char FullGroup[100]; + sprintf(FullGroup, "%s.%s", Msg->to, Msg->via ); + if (_stricmp(FullGroup, REC->NewsGroup) == 0) + { + if (_stricmp(Header, "subject") == 0) + NNTPsockprintf(sockptr, "%d Subject: %s", MsgNo, Msg->title); + else if (_stricmp(Header, "from") == 0) + NNTPsockprintf(sockptr, "%d From: %s", MsgNo, Msg->from); + else if (_stricmp(Header, "date") == 0) + NNTPsockprintf(sockptr, "%d Date: %s", MsgNo, FormatNNTPDateAndTime((time_t)Msg->datecreated)); + else if (_stricmp(Header, "message-id") == 0) + NNTPsockprintf(sockptr, "%d Message-ID: <%s>", MsgNo, Msg->bid); + else if (_stricmp(Header, "lines") == 0) + NNTPsockprintf(sockptr, "%d Lines: %d", MsgNo, Msg->length); + } + } + } + + NNTPSendSock(sockptr,"."); + return; + + } + + if(_memicmp(Buffer, "XOVER", 5) == 0) + { + struct NNTPRec * REC = sockptr->NNTPGroup; + struct MsgInfo * Msg; + int MsgStart, MsgEnd, MsgNo, fields; + + if (REC == NULL) + { + NNTPSendSock(sockptr,"412 no newsgroup has been selected"); + return; + } + + fields = sscanf(&Buffer[6], "%d-%d", &MsgStart, &MsgEnd); + + if (fields > 0) + MsgNo = MsgStart; + + if (fields == 1) + MsgEnd = MsgStart; + + if (MsgNo == 0) + { + MsgStart = MsgEnd = sockptr->NNTPNum; + + if (MsgStart == 0) + { + NNTPSendSock(sockptr,"420 no current article has been selected"); + return; + } + } + else + { + sockptr->NNTPNum = MsgEnd; + } + + NNTPsockprintf(sockptr, "224 "); + + for (MsgNo = MsgStart; MsgNo <= MsgEnd; MsgNo++) + { + Msg=MsgnotoMsg[MsgNo]; + + if (Msg) + { + char FullGroup[100]; + sprintf(FullGroup, "%s.%s", Msg->to, Msg->via ); + if (_stricmp(FullGroup, REC->NewsGroup) == 0) + { + // subject, author, date, message-id, references, byte count, and line count. + NNTPsockprintf(sockptr, "%d\t%s\t%s\t%s\t%s\t%s\t%d\t%d", + MsgNo, Msg->title, Msg->from, FormatNNTPDateAndTime((time_t)Msg->datecreated), Msg->bid, + "", Msg->length, Msg->length); + } + } + } + + NNTPSendSock(sockptr,"."); + return; + + } + + + /* + 240 article posted ok + 340 send article to be posted. End with . + 440 posting not allowed + 441 posting failed +*/ + if(_memicmp(Buffer, "POST", 4) == 0) + { + if (sockptr->State != Authenticated) + { + NNTPsockprintf(sockptr, "480 Authentication required"); + return; + } + + sockptr->MailBuffer=malloc(10000); + sockptr->MailBufferSize=10000; + + if (sockptr->MailBuffer == NULL) + { + CriticalErrorHandler("Failed to create POP3 Message Buffer"); + NNTPSendSock(sockptr, "QUIT"); + sockptr->State = WaitingForQUITResponse; + shutdown(sock, 0); + + return; + } + + sockptr->Flags |= GETTINGMESSAGE; + + NNTPSendSock(sockptr, "340 OK"); + return; + } + + + + if(_memicmp(Buffer, "QUIT", 4) == 0) + { + NNTPSendSock(sockptr, "205 OK"); + Sleep(500); + shutdown(sock, 0); + return; + } + +/* if(memcmp(Buffer, "RSET\r\n", 6) == 0) + { + NNTPSendSock(sockptr, "250 Ok"); + sockptr->State = 0; + sockptr->Recipients; + return; + } +*/ + + if(memcmp(Buffer, "DATE", 4) == 0) + { + //This command returns a one-line response code of 111 followed by the + //GMT date and time on the server in the form YYYYMMDDhhmmss. + // 111 YYYYMMDDhhmmss + + struct tm *tm; + char Date[32]; + time_t Time = time(NULL); + + tm = gmtime(&Time); + + if (tm) + { + sprintf_s(Date, sizeof(Date), "111 %04d%02d%02d%02d%02d%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + NNTPSendSock(sockptr, Date); + } + else + NNTPSendSock(sockptr, "500 command not recognized"); + + return; + } + + NNTPSendSock(sockptr, "500 command not recognized"); + + return; +} + + + + +int NNTP_Read(SocketConn * sockptr, SOCKET sock) +{ + int InputLen, MsgLen; + char * ptr, * ptr2; + char Buffer[2000]; + + // May have several messages per packet, or message split over packets + + if (sockptr->InputLen > 1000) // Shouldnt have lines longer than this in text mode + { + sockptr->InputLen=0; + } + + InputLen=recv(sock, &sockptr->TCPBuffer[sockptr->InputLen], 1000, 0); + + if (InputLen <= 0) + { + int x = WSAGetLastError(); + + closesocket(sock); + ReleaseSock(sock); + + return 0; // Does this mean closed? + } + + sockptr->InputLen += InputLen; + +loop: + + ptr = memchr(sockptr->TCPBuffer, '\n', sockptr->InputLen); + + if (ptr) // CR in buffer + { + ptr2 = &sockptr->TCPBuffer[sockptr->InputLen]; + ptr++; // Assume LF Follows CR + + if (ptr == ptr2) + { + // Usual Case - single meg in buffer + + ProcessNNTPServerMessage(sockptr, sockptr->TCPBuffer, sockptr->InputLen); + sockptr->InputLen=0; + } + else + { + // buffer contains more that 1 message + + MsgLen = sockptr->InputLen - (int)(ptr2-ptr); + + memcpy(Buffer, sockptr->TCPBuffer, MsgLen); + + ProcessNNTPServerMessage(sockptr, Buffer, MsgLen); + + memmove(sockptr->TCPBuffer, ptr, sockptr->InputLen-MsgLen); + + sockptr->InputLen -= MsgLen; + + goto loop; + + } + } + + NNTPFlush(sockptr); + + return 0; +} + + +int NNTP_Accept(SOCKET SocketId) +{ + int addrlen; + SocketConn * sockptr; + u_long param = 1; + + SOCKET sock; + + addrlen=sizeof(struct sockaddr); + + // Allocate a Socket entry + + sockptr=zalloc(sizeof(SocketConn)+100); + + sockptr->Next = Sockets; + Sockets=sockptr; + + sock = accept(SocketId, (struct sockaddr *)&sockptr->sin, &addrlen); + + if (sock == INVALID_SOCKET) + { + Logprintf(LOG_TCP, NULL, '|', "NNTP accept() failed Error %d", WSAGetLastError()); + + // get rid of socket record + + Sockets = sockptr->Next; + free(sockptr); + + return FALSE; + } + + + sockptr->Type = NNTPServer; + + ioctl(sock, FIONBIO, ¶m); + sockptr->socket = sock; + sockptr->State = 0; + + NNTPSendSock(sockptr, "200 BPQMail NNTP Server ready"); + Logprintf(LOG_TCP, NULL, '|', "Incoming NNTP Connect Socket = %d", sock); + + NNTPFlush(sockptr); + + return 0; +} +/* +int NNTP_Data(int sock, int error, int eventcode) +{ + SocketConn * sockptr; + + // Find Connection Record + + sockptr=Sockets; + + while (sockptr) + { + if (sockptr->socket == sock) + { + switch (eventcode) + { + case FD_READ: + + return NNTP_Read(sockptr,sock); + + case FD_WRITE: + + // Either Just connected, or flow contorl cleared + + if (sockptr->SendBuffer) + // Data Queued + + SendFromQueue(sockptr); + else + { + NNTPSendSock(sockptr, "200 BPQMail NNTP Server ready"); +// sockptr->State = GettingUser; + } + + return 0; + + case FD_OOB: + + return 0; + + case FD_ACCEPT: + + return 0; + + case FD_CONNECT: + + return 0; + + case FD_CLOSE: + + closesocket(sock); + ReleaseNNTPSock(sock); + return 0; + } + return 0; + } + else + sockptr=sockptr->Next; + } + + return 0; +} +*/ +VOID ReleaseNNTPSock(SOCKET sock) +{ + // remove and free the socket record + + SocketConn * sockptr, * lastptr; + + sockptr=Sockets; + lastptr=NULL; + + while (sockptr) + { + if (sockptr->socket == sock) + { + if (lastptr) + lastptr->Next=sockptr->Next; + else + Sockets=sockptr->Next; + + free(sockptr); + return; + } + else + { + lastptr=sockptr; + sockptr=sockptr->Next; + } + } + + return; +} + +VOID SendFromQueue(SocketConn * sockptr) +{ + int bytestosend = sockptr->SendSize - sockptr->SendPtr; + int bytessent; + + Debugprintf("TCP - Sending %d bytes from buffer", bytestosend); + + bytessent = send(sockptr->socket, &sockptr->SendBuffer[sockptr->SendPtr], bytestosend, 0); + + if (bytessent == bytestosend) + { + // All Sent + + free(sockptr->SendBuffer); + sockptr->SendBuffer = NULL; + } + else + { + sockptr->SendPtr += bytessent; + } + + return; +} diff --git a/.svn/pristine/13/1368bcb6660d3eb378f376ef8fa9a0acfad8af7a.svn-base b/.svn/pristine/13/1368bcb6660d3eb378f376ef8fa9a0acfad8af7a.svn-base index 1a7dea1..6d5f618 100644 --- a/.svn/pristine/13/1368bcb6660d3eb378f376ef8fa9a0acfad8af7a.svn-base +++ b/.svn/pristine/13/1368bcb6660d3eb378f376ef8fa9a0acfad8af7a.svn-base @@ -1,237 +1,237 @@ -/* -Copyright 2001-2022 John Wiseman G8BPQ - -This file is part of LinBPQ/BPQ32. - -LinBPQ/BPQ32 is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -LinBPQ/BPQ32 is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses -*/ - -#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers - -#define _CRT_SECURE_NO_DEPRECATE - -#include "compatbits.h" -#include -#include "asmstrucs.h" -#include "tncinfo.h" - -VOID __cdecl Debugprintf(const char * format, ...); - -#ifndef WIN32 - -#define APIENTRY -#define DllExport -#define VOID void - -#else -#include -#endif - -extern BOOL EventsEnabled; -void MQTTReportSession(char * Msg); -extern int MQTT; - - -extern char Modenames[19][10]; - -// Runs use specified routine on certain event -#ifndef WIN32 - -void RunEventProgram(char * Program, char * Param) -{ - char * arg_list[] = {Program, NULL, NULL}; - pid_t child_pid; - - if (EventsEnabled == 0) - return; - - signal(SIGCHLD, SIG_IGN); // Silently (and portably) reap children. - - if (Param && Param[0]) - arg_list[1] = Param; - - // Fork and Exec Specified program - - // Duplicate this process. - - child_pid = fork (); - - if (child_pid == -1) - { - printf ("Event fork() Failed\n"); - return; - } - - if (child_pid == 0) - { - execvp (arg_list[0], arg_list); - - // The execvp function returns only if an error occurs. - - printf ("Failed to run %s\n", arg_list[0]); - exit(0); // Kill the new process - } - -#else - -DllExport void APIENTRY RunEventProgram(char * Program, char * Param) -{ - int n = 0; - char cmdLine[256]; - - STARTUPINFO SInfo; // pointer to STARTUPINFO - PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION - - if (EventsEnabled == 0) - return; - - - SInfo.cb=sizeof(SInfo); - SInfo.lpReserved=NULL; - SInfo.lpDesktop=NULL; - SInfo.lpTitle=NULL; - SInfo.dwFlags=0; - SInfo.cbReserved2=0; - SInfo.lpReserved2=NULL; - - sprintf(cmdLine, "%s %s", Program, Param); - - if (!CreateProcess(NULL, cmdLine, NULL, NULL, FALSE,0 ,NULL ,NULL, &SInfo, &PInfo)) - Debugprintf("Failed to Start %s Error %d ", Program, GetLastError()); - -#endif - - return; -} - -void hookL2SessionAccepted(int Port, char * remotecall, char * ourcall, struct _LINKTABLE * LINK) -{ - // Incoming SABM - - LINK->ConnectTime = time(NULL); - LINK->bytesTXed = LINK->bytesRXed = 0; - - strcpy(LINK->callingCall, remotecall); - strcpy(LINK->receivingCall, ourcall); - strcpy(LINK->Direction, "In"); -} - -void hookL2SessionDeleted(struct _LINKTABLE * LINK) -{ - // calculate session time and av bytes/min in and out - - if (LINK->ConnectTime) - { - if (LINK->bytesTXed == 0 && LINK->bytesRXed == 0) - { - // assume failed connect and ignore for now - maybe log later - - } - else - { - char Msg[256]; - char timestamp[16]; - time_t sessionTime = time(NULL) - LINK->ConnectTime; - double avBytesSent = LINK->bytesTXed / (sessionTime / 60.0); - double avBytesRXed = LINK->bytesRXed / (sessionTime / 60.0); - time_t Now = time(NULL); - struct tm * TM = localtime(&Now); - - sprintf(timestamp, "%02d:%02d:%02d", TM->tm_hour, TM->tm_min, TM->tm_sec); - - if (sessionTime == 0) - sessionTime = 1; // Or will get divide by zero error - - Debugprintf("KISS Session Stats Port %d %s %s %d secs Bytes Sent %d BPM %4.2f Bytes Received %d %4.2f BPM ", - LINK->LINKPORT->PORTNUMBER, LINK->callingCall, LINK->receivingCall, sessionTime, LINK->bytesTXed, avBytesSent, LINK->bytesRXed, avBytesRXed, timestamp); - - - sprintf(Msg, "{\"mode\": \"%s\", \"direction\": \"%s\", \"port\": %d, \"callfrom\": \"%s\", \"callto\": \"%s\", \"time\": %d, \"bytesSent\": %d," - "\"BPMSent\": %4.2f, \"BytesReceived\": %d, \"BPMReceived\": %4.2f, \"timestamp\": \"%s\"}", - "KISS", LINK->Direction, LINK->LINKPORT->PORTNUMBER, LINK->callingCall, LINK->receivingCall, sessionTime, - LINK->bytesTXed, avBytesSent, LINK->bytesRXed, avBytesRXed, timestamp); - - if (MQTT) - MQTTReportSession(Msg); - } - - LINK->ConnectTime = 0; - } -} - -void hookL2SessionAttempt(int Port, char * ourcall, char * remotecall, struct _LINKTABLE * LINK) -{ - LINK->ConnectTime = time(NULL); - LINK->bytesTXed = LINK->bytesRXed = 0; - - strcpy(LINK->callingCall, ourcall); - strcpy(LINK->receivingCall, remotecall); - strcpy(LINK->Direction, "Out"); -} - -void hookL4SessionAttempt(struct STREAMINFO * STREAM, char * remotecall, char * ourcall) -{ - // Outgoing Connect - - STREAM->ConnectTime = time(NULL); - STREAM->bytesTXed = STREAM->bytesRXed = 0; - - strcpy(STREAM->callingCall, ourcall); - strcpy(STREAM->receivingCall, remotecall); - strcpy(STREAM->Direction, "Out"); -} - -void hookL4SessionAccepted(struct STREAMINFO * STREAM, char * remotecall, char * ourcall) -{ - // Incoming Connect - - STREAM->ConnectTime = time(NULL); - STREAM->bytesTXed = STREAM->bytesRXed = 0; - - strcpy(STREAM->callingCall, remotecall); - strcpy(STREAM->receivingCall, ourcall); - strcpy(STREAM->Direction, "In"); -} - -void hookL4SessionDeleted(struct TNCINFO * TNC, struct STREAMINFO * STREAM) -{ - char Msg[256]; - - char timestamp[16]; - - if (STREAM->ConnectTime) - { - time_t sessionTime = time(NULL) - STREAM->ConnectTime; - double avBytesRXed = STREAM->bytesRXed / (sessionTime / 60.0); - double avBytesSent = STREAM->bytesTXed / (sessionTime / 60.0); - time_t Now = time(NULL); - struct tm * TM = localtime(&Now); - sprintf(timestamp, "%02d:%02d:%02d", TM->tm_hour, TM->tm_min, TM->tm_sec); - - if (sessionTime == 0) - sessionTime = 1; // Or will get divide by zero error - - sprintf(Msg, "{\"mode\": \"%s\", \"direction\": \"%s\", \"port\": %d, \"callfrom\": \"%s\", \"callto\": \"%s\", \"time\": %d, \"bytesSent\": %d," - "\"BPMSent\": %4.2f, \"BytesReceived\": %d, \"BPMReceived\": %4.2f, \"timestamp\": \"%s\"}", - Modenames[TNC->Hardware - 1], STREAM->Direction, TNC->Port, STREAM->callingCall, STREAM->receivingCall, sessionTime, - STREAM->bytesTXed, avBytesSent, STREAM->bytesRXed, avBytesRXed, timestamp); - - if (MQTT) - MQTTReportSession(Msg); - - STREAM->ConnectTime = 0; - } -} - - +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +#define _CRT_SECURE_NO_DEPRECATE + +#include "compatbits.h" +#include +#include "asmstrucs.h" +#include "tncinfo.h" + +VOID __cdecl Debugprintf(const char * format, ...); + +#ifndef WIN32 + +#define APIENTRY +#define DllExport +#define VOID void + +#else +#include +#endif + +extern BOOL EventsEnabled; +void MQTTReportSession(char * Msg); +extern int MQTT; + + +extern char Modenames[19][10]; + +// Runs use specified routine on certain event +#ifndef WIN32 + +void RunEventProgram(char * Program, char * Param) +{ + char * arg_list[] = {Program, NULL, NULL}; + pid_t child_pid; + + if (EventsEnabled == 0) + return; + + signal(SIGCHLD, SIG_IGN); // Silently (and portably) reap children. + + if (Param && Param[0]) + arg_list[1] = Param; + + // Fork and Exec Specified program + + // Duplicate this process. + + child_pid = fork (); + + if (child_pid == -1) + { + printf ("Event fork() Failed\n"); + return; + } + + if (child_pid == 0) + { + execvp (arg_list[0], arg_list); + + // The execvp function returns only if an error occurs. + + printf ("Failed to run %s\n", arg_list[0]); + exit(0); // Kill the new process + } + +#else + +DllExport void APIENTRY RunEventProgram(char * Program, char * Param) +{ + int n = 0; + char cmdLine[256]; + + STARTUPINFO SInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION + + if (EventsEnabled == 0) + return; + + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + sprintf(cmdLine, "%s %s", Program, Param); + + if (!CreateProcess(NULL, cmdLine, NULL, NULL, FALSE,0 ,NULL ,NULL, &SInfo, &PInfo)) + Debugprintf("Failed to Start %s Error %d ", Program, GetLastError()); + +#endif + + return; +} + +void hookL2SessionAccepted(int Port, char * remotecall, char * ourcall, struct _LINKTABLE * LINK) +{ + // Incoming SABM + + LINK->ConnectTime = time(NULL); + LINK->bytesTXed = LINK->bytesRXed = 0; + + strcpy(LINK->callingCall, remotecall); + strcpy(LINK->receivingCall, ourcall); + strcpy(LINK->Direction, "In"); +} + +void hookL2SessionDeleted(struct _LINKTABLE * LINK) +{ + // calculate session time and av bytes/min in and out + + if (LINK->ConnectTime) + { + if (LINK->bytesTXed == 0 && LINK->bytesRXed == 0) + { + // assume failed connect and ignore for now - maybe log later + + } + else + { + char Msg[256]; + char timestamp[16]; + time_t sessionTime = time(NULL) - LINK->ConnectTime; + double avBytesSent = LINK->bytesTXed / (sessionTime / 60.0); + double avBytesRXed = LINK->bytesRXed / (sessionTime / 60.0); + time_t Now = time(NULL); + struct tm * TM = localtime(&Now); + + sprintf(timestamp, "%02d:%02d:%02d", TM->tm_hour, TM->tm_min, TM->tm_sec); + + if (sessionTime == 0) + sessionTime = 1; // Or will get divide by zero error + + Debugprintf("KISS Session Stats Port %d %s %s %d secs Bytes Sent %d BPM %4.2f Bytes Received %d %4.2f BPM ", + LINK->LINKPORT->PORTNUMBER, LINK->callingCall, LINK->receivingCall, sessionTime, LINK->bytesTXed, avBytesSent, LINK->bytesRXed, avBytesRXed, timestamp); + + + sprintf(Msg, "{\"mode\": \"%s\", \"direction\": \"%s\", \"port\": %d, \"callfrom\": \"%s\", \"callto\": \"%s\", \"time\": %d, \"bytesSent\": %d," + "\"BPMSent\": %4.2f, \"BytesReceived\": %d, \"BPMReceived\": %4.2f, \"timestamp\": \"%s\"}", + "KISS", LINK->Direction, LINK->LINKPORT->PORTNUMBER, LINK->callingCall, LINK->receivingCall, sessionTime, + LINK->bytesTXed, avBytesSent, LINK->bytesRXed, avBytesRXed, timestamp); + + if (MQTT) + MQTTReportSession(Msg); + } + + LINK->ConnectTime = 0; + } +} + +void hookL2SessionAttempt(int Port, char * ourcall, char * remotecall, struct _LINKTABLE * LINK) +{ + LINK->ConnectTime = time(NULL); + LINK->bytesTXed = LINK->bytesRXed = 0; + + strcpy(LINK->callingCall, ourcall); + strcpy(LINK->receivingCall, remotecall); + strcpy(LINK->Direction, "Out"); +} + +void hookL4SessionAttempt(struct STREAMINFO * STREAM, char * remotecall, char * ourcall) +{ + // Outgoing Connect + + STREAM->ConnectTime = time(NULL); + STREAM->bytesTXed = STREAM->bytesRXed = 0; + + strcpy(STREAM->callingCall, ourcall); + strcpy(STREAM->receivingCall, remotecall); + strcpy(STREAM->Direction, "Out"); +} + +void hookL4SessionAccepted(struct STREAMINFO * STREAM, char * remotecall, char * ourcall) +{ + // Incoming Connect + + STREAM->ConnectTime = time(NULL); + STREAM->bytesTXed = STREAM->bytesRXed = 0; + + strcpy(STREAM->callingCall, remotecall); + strcpy(STREAM->receivingCall, ourcall); + strcpy(STREAM->Direction, "In"); +} + +void hookL4SessionDeleted(struct TNCINFO * TNC, struct STREAMINFO * STREAM) +{ + char Msg[256]; + + char timestamp[16]; + + if (STREAM->ConnectTime) + { + time_t sessionTime = time(NULL) - STREAM->ConnectTime; + double avBytesRXed = STREAM->bytesRXed / (sessionTime / 60.0); + double avBytesSent = STREAM->bytesTXed / (sessionTime / 60.0); + time_t Now = time(NULL); + struct tm * TM = localtime(&Now); + sprintf(timestamp, "%02d:%02d:%02d", TM->tm_hour, TM->tm_min, TM->tm_sec); + + if (sessionTime == 0) + sessionTime = 1; // Or will get divide by zero error + + sprintf(Msg, "{\"mode\": \"%s\", \"direction\": \"%s\", \"port\": %d, \"callfrom\": \"%s\", \"callto\": \"%s\", \"time\": %d, \"bytesSent\": %d," + "\"BPMSent\": %4.2f, \"BytesReceived\": %d, \"BPMReceived\": %4.2f, \"timestamp\": \"%s\"}", + Modenames[TNC->Hardware - 1], STREAM->Direction, TNC->Port, STREAM->callingCall, STREAM->receivingCall, sessionTime, + STREAM->bytesTXed, avBytesSent, STREAM->bytesRXed, avBytesRXed, timestamp); + + if (MQTT) + MQTTReportSession(Msg); + + STREAM->ConnectTime = 0; + } +} + + diff --git a/.svn/pristine/13/136e510d90c53fc02930e5a4a63a91f73204a63a.svn-base b/.svn/pristine/13/136e510d90c53fc02930e5a4a63a91f73204a63a.svn-base index eca826f..2d1a74b 100644 --- a/.svn/pristine/13/136e510d90c53fc02930e5a4a63a91f73204a63a.svn-base +++ b/.svn/pristine/13/136e510d90c53fc02930e5a4a63a91f73204a63a.svn-base @@ -1,988 +1,988 @@ - -// Program to Convert RTS changes on Virtual COM Port to hamlib PTT commands - - -// Version 1. 0. 0. 1 December 2020 - -// Version 1. 0. 2. 1 April 2018 - - -#define _CRT_SECURE_NO_WARNINGS -#define _CRT_SECURE_NO_DEPRECATE - -#include - -#include -#include -#include -#include -#include - -#define LIBCONFIG_STATIC -#include "libconfig.h" - - -#include "BPQRemotePTTRes.h" - -#define BPQICON 400 - -WSADATA WsaData; // receives data from WSAStartup - -#define WSA_READ WM_USER + 1 - -HINSTANCE hInst; -char AppName[] = "BPQRemotePTT"; -char Title[80] = "BPQRemotePTT"; - -TCHAR szTitle[]="GPSMuxPC"; // The title bar text -TCHAR szWindowClass[]="GPSMAINWINDOW"; // the main window class name - - -// Foward declarations of functions included in this code module: - -ATOM MyRegisterClass(HINSTANCE hInstance); -BOOL InitInstance(HINSTANCE, int); -LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); -LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM); -VOID WINAPI CompletedReadRoutine(DWORD dwErr, DWORD cbBytesRead, LPOVERLAPPED lpOverLap); -VOID WINAPI txCompletedReadRoutine(DWORD dwErr, DWORD cbBytesRead, LPOVERLAPPED lpOverLap); -VOID CATThread(); -VOID runTimer(); - -int TimerHandle = 0; - -LOGFONT LFTTYFONT ; - -HFONT hFont; - -struct sockaddr_in sinx; -struct sockaddr rx; - -int addrlen = sizeof(struct sockaddr_in); - -int HAMLIBPORT; // Port Number for HAMLIB (rigctld) Emulator -char * HAMLIBHOST[128]; - -BOOL MinimizetoTray=FALSE; - -VOID __cdecl Debugprintf(const char * format, ...) -{ - char Mess[255]; - va_list(arglist); - - va_start(arglist, format); - vsprintf(Mess, format, arglist); - strcat(Mess, "\r\n"); - - OutputDebugString(Mess); - - return; -} - - -char * strlop(char * buf, char delim) -{ - // Terminate buf at delim, and return rest of string - - char * ptr = strchr(buf, delim); - - if (ptr == NULL) return NULL; - - *(ptr)++=0; - - return ptr; -} - -char * RigPort = NULL; -char * RigSpeed = NULL; -HANDLE RigHandle = 0; -int RigType = 0; // Flag for possible RTS/DTR - - -char BPQHostIP[128]; - -char PTTCATPort[4][16]; -HANDLE PTTCATHandle[4]; -short HamLibPort[4]; -SOCKET HamLibSock[4]; // rigctld socket - -HWND comWnd[4]; -HWND portWnd[4]; -HWND stateWnd[4]; -int stateCtl[4]; - -struct sockaddr_in remoteDest[4]; - -int RealMux[4]; // BPQ Virtual or Real - -int EndPTTCATThread = 0; - -char ConfigName[256] = "BPQRemotePTT.cfg"; - -BOOL GetStringValue(config_setting_t * group, char * name, char * value) -{ - const char * str; - config_setting_t *setting; - - setting = config_setting_get_member (group, name); - if (setting) - { - str = config_setting_get_string (setting); - strcpy(value, str); - return TRUE; - } - return FALSE; -} - -int GetIntValue(config_setting_t * group, char * name) -{ - config_setting_t *setting; - - setting = config_setting_get_member (group, name); - if (setting) - return config_setting_get_int (setting); - - return 0; -} - -VOID SaveStringValue(config_setting_t * group, char * name, char * value) -{ - config_setting_t *setting; - - setting = config_setting_add(group, name, CONFIG_TYPE_STRING); - if (setting) - config_setting_set_string(setting, value); - -} - - -VOID SaveIntValue(config_setting_t * group, char * name, int value) -{ - config_setting_t *setting; - - setting = config_setting_add(group, name, CONFIG_TYPE_INT); - if(setting) - config_setting_set_int(setting, value); -} -VOID GetConfig() -{ - config_setting_t *group; - config_t cfg; - - memset((void *)&cfg, 0, sizeof(config_t)); - - config_init(&cfg); - - if(!config_read_file(&cfg, ConfigName)) - { - fprintf(stderr, "Config read error line %d - %s\n", config_error_line(&cfg), config_error_text(&cfg)); - config_destroy(&cfg); - return; - } - - group = config_lookup(&cfg, "main"); - - if (group == NULL) - { - config_destroy(&cfg); - return; - } - - - GetStringValue(group, "BPQHostIP", BPQHostIP); - - GetStringValue(group, "COM1", PTTCATPort[0]); - GetStringValue(group, "COM2", PTTCATPort[1]); - GetStringValue(group, "COM3", PTTCATPort[2]); - GetStringValue(group, "COM4", PTTCATPort[3]); - - HamLibPort[0] = GetIntValue(group, "HamLibPort1"); - HamLibPort[1] = GetIntValue(group, "HamLibPort2"); - HamLibPort[2] = GetIntValue(group, "HamLibPort3"); - HamLibPort[3] = GetIntValue(group, "HamLibPort4"); - - config_destroy(&cfg); -} - -VOID SaveConfig() -{ - config_setting_t *root, *group; - config_t cfg; - - // Get rid of old config before saving - - config_init(&cfg); - - root = config_root_setting(&cfg); - - group = config_setting_add(root, "main", CONFIG_TYPE_GROUP); - - SaveStringValue(group, "BPQHostIP", BPQHostIP); - - SaveStringValue(group, "COM1", PTTCATPort[0]); - SaveStringValue(group, "COM2", PTTCATPort[1]); - SaveStringValue(group, "COM3", PTTCATPort[2]); - SaveStringValue(group, "COM4", PTTCATPort[3]); - - SaveIntValue(group, "HamLibPort1", HamLibPort[0]); - SaveIntValue(group, "HamLibPort2", HamLibPort[1]); - SaveIntValue(group, "HamLibPort3", HamLibPort[2]); - SaveIntValue(group, "HamLibPort4", HamLibPort[3]); - - if(!config_write_file(&cfg, ConfigName)) - { - fprintf(stderr, "Error while writing file.\n"); - config_destroy(&cfg); - return; - } - - config_destroy(&cfg); -} - - - - -int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) -{ - MSG msg; - - if (lpCmdLine[0]) - { - // Port Name and Speed for Remote CAT - - RigPort = _strdup(lpCmdLine); - RigSpeed = strlop(RigPort, ':'); - } - - MyRegisterClass(hInstance); - - GetConfig(); - - if (!InitInstance(hInstance, nCmdShow)) - return (FALSE); - - // Main message loop: - - while (GetMessage(&msg, NULL, 0, 0)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - - KillTimer(NULL, TimerHandle); - - return (msg.wParam); -} - -// - -// -// FUNCTION: InitApplication(HANDLE) -// -// PURPOSE: Initializes window data and registers window class -// -// COMMENTS: -// -// In this function, we initialize a window class by filling out a data -// structure of type WNDCLASS and calling either RegisterClass or -// the internal MyRegisterClass. -// - -#define BGCOLOUR RGB(236,233,216) -HBRUSH bgBrush; -HBRUSH RedBrush; -HBRUSH GreenBrush; - -HWND hWnd; - -BOOL InitApplication(HINSTANCE hInstance) -{ - return TRUE; -} - -// -// FUNCTION: InitInstance(HANDLE, int) -// -// PURPOSE: Saves instance handle and creates main window -// -// COMMENTS: -// -// In this function, we save the instance handle in a global variable and -// create and display the main program window. -// - -HFONT FAR PASCAL MyCreateFont( void ) -{ - CHOOSEFONT cf; - LOGFONT lf; - HFONT hfont; - - // Initialize members of the CHOOSEFONT structure. - - cf.lStructSize = sizeof(CHOOSEFONT); - cf.hwndOwner = (HWND)NULL; - cf.hDC = (HDC)NULL; - cf.lpLogFont = &lf; - cf.iPointSize = 0; - cf.Flags = CF_SCREENFONTS | CF_FIXEDPITCHONLY; - cf.rgbColors = RGB(0,0,0); - cf.lCustData = 0L; - cf.lpfnHook = (LPCFHOOKPROC)NULL; - cf.lpTemplateName = (LPSTR)NULL; - cf.hInstance = (HINSTANCE) NULL; - cf.lpszStyle = (LPSTR)NULL; - cf.nFontType = SCREEN_FONTTYPE; - cf.nSizeMin = 0; - cf.nSizeMax = 0; - - // Display the CHOOSEFONT common-dialog box. - - ChooseFont(&cf); - - // Create a logical font based on the user's - // selection and return a handle identifying - // that font. - - hfont = CreateFontIndirect(cf.lpLogFont); - return (hfont); -} - -VOID Rig_PTT(int n, BOOL PTTState) -{ - - char Msg[16]; - int Len = sprintf(Msg, "T %d\n", PTTState); - - if (HamLibSock[n]) - { - send(HamLibSock[n], Msg, Len, 0); - - if (PTTState) - SetDlgItemText(hWnd, stateCtl[n], "PTT"); - else - SetDlgItemText(hWnd, stateCtl[n], ""); - } -} - - - -VOID PTTCATThread() -{ - DWORD dwLength = 0; - int Length, ret, i; - UCHAR * ptr1; - UCHAR * ptr2; - UCHAR c; - UCHAR Block[4][80]; - UCHAR CurrentState[4] = {0}; -#define RTS 2 -#define DTR 4 - HANDLE Event; - DWORD EvtMask[4]; - OVERLAPPED Overlapped[4]; - char Port[32]; - int PIndex = 0; - int HIndex = 0; - int rc; - - EndPTTCATThread = FALSE; - - while (PIndex < 4 && PTTCATPort[PIndex][0]) - { - RealMux[HIndex] = 0; - - sprintf(Port, "\\\\.\\pipe\\BPQ%s", PTTCATPort[PIndex]); - - PTTCATHandle[HIndex] = CreateFile(Port, GENERIC_READ | GENERIC_WRITE, - 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); - - if (PTTCATHandle[HIndex] == INVALID_HANDLE_VALUE) - { - int Err = GetLastError(); -// Consoleprintf("PTTMUX port BPQCOM%s Open failed code %d", RIG->PTTCATPort[PIndex], Err); - - // See if real com port - - sprintf(Port, "\\\\.\\\\%s", PTTCATPort[PIndex]); - - PTTCATHandle[HIndex] = CreateFile(Port, GENERIC_READ | GENERIC_WRITE, - 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); - - RealMux[HIndex] = 1; - - if (PTTCATHandle[HIndex] == INVALID_HANDLE_VALUE) - { - int Err = GetLastError(); - PTTCATHandle[HIndex] = 0; - Debugprintf("PTTMUX port COM%s Open failed code %d", PTTCATPort[PIndex], Err); - } - else - { - rc = SetCommMask(PTTCATHandle[HIndex], EV_CTS | EV_DSR); // Request notifications - HIndex++; - } - } - else - HIndex++; - - PIndex++; - - } - - if (PIndex == 0) - return; // No ports - - Event = CreateEvent(NULL, TRUE, FALSE, NULL); - - for (i = 0; i < HIndex; i ++) - { - memset(&Overlapped[i], 0, sizeof(OVERLAPPED)); - Overlapped[i].hEvent = Event; - - if (RealMux[i]) - { - // Request Interface change notifications - - rc = WaitCommEvent(PTTCATHandle[i], &EvtMask[i], &Overlapped[i]); - rc = GetLastError(); - - } - else - { - // Prime a read on each PTTCATHandle - - ReadFile(PTTCATHandle[i], Block[i], 80, &Length, &Overlapped[i]); - } - } - - while (EndPTTCATThread == FALSE) - { - -WaitAgain: - - ret = WaitForSingleObject(Event, 1000); - - if (ret == WAIT_TIMEOUT) - { - if (EndPTTCATThread) - { - for (i = 0; i < HIndex; i ++) - { - CancelIo(PTTCATHandle[i]); - CloseHandle(PTTCATHandle[i]); - PTTCATHandle[i] = INVALID_HANDLE_VALUE; - } - CloseHandle(Event); - return; - } - goto WaitAgain; - } - - ResetEvent(Event); - - // See which request(s) have completed - - for (i = 0; i < HIndex; i ++) - { - ret = GetOverlappedResult(PTTCATHandle[i], &Overlapped[i], &Length, FALSE); - - if (ret) - { - if (RealMux[i]) - { - // Request Interface change notifications - - DWORD Mask; - - GetCommModemStatus(PTTCATHandle[i], &Mask); - - if (Mask & MS_CTS_ON) - Rig_PTT(i, TRUE); - else - Rig_PTT(i, FALSE); - - memset(&Overlapped[i], 0, sizeof(OVERLAPPED)); - Overlapped[i].hEvent = Event; - WaitCommEvent(PTTCATHandle[i], &EvtMask[i], &Overlapped[i]); - - } - else - { - - ptr1 = Block[i]; - ptr2 = Block[i]; - - while (Length > 0) - { - c = *(ptr1++); - - Length--; - - if (c == 0xff) - { - c = *(ptr1++); - Length--; - - if (c == 0xff) // ff ff means ff - { - Length--; - } - else - { - // This is connection / RTS/DTR statua from other end - // Convert to CAT Command - - if (c == CurrentState[i]) - continue; - - if (c & RTS) - Rig_PTT(i, TRUE); - else - Rig_PTT(i, FALSE); - - CurrentState[i] = c; - continue; - } - } - } - - memset(&Overlapped[i], 0, sizeof(OVERLAPPED)); - Overlapped[i].hEvent = Event; - - ReadFile(PTTCATHandle[i], Block[i], 80, &Length, &Overlapped[i]); - } - } - } - } - EndPTTCATThread = FALSE; -} - -char ClassName[]="BPQMAIL"; - -ATOM MyRegisterClass(HINSTANCE hInstance) -{ - WNDCLASS wc; - - bgBrush = CreateSolidBrush(BGCOLOUR); - RedBrush = CreateSolidBrush(RGB(255,0,0)); - GreenBrush = CreateSolidBrush(RGB(0,255,0)); -// BlueBrush = CreateSolidBrush(RGB(0,0,255)); - - wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; - wc.lpfnWndProc = WndProc; - - wc.cbClsExtra = 0; - wc.cbWndExtra = DLGWINDOWEXTRA; - wc.hInstance = hInstance; - wc.hIcon = NULL; //LoadIcon( hInstance, MAKEINTRESOURCE(IDI_GPSMUXPC)); - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.hbrBackground = bgBrush; - - wc.lpszMenuName = NULL;//"MENU_1"; - wc.lpszClassName = szWindowClass; - -// RegisterClass(&wc); - - // wc.lpfnWndProc = TraceWndProc; - // wc.lpszClassName = TraceClassName; - -// RegisterClass(&wc); - -// wc.lpfnWndProc = ConfigWndProc; -// wc.lpszClassName = ConfigClassName; - - return (RegisterClass(&wc)); - -} - - -BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) -{ - u_long param=1; - BOOL bcopt=TRUE; - int ret; - WNDCLASS wc = {0}; - int n = 0; - - - hInst = hInstance; // Store instance handle in our global variable - - WSAStartup(MAKEWORD(2, 0), &WsaData); - - - hWnd=CreateDialog(hInst,szWindowClass,0,NULL); - - if (!hWnd) - { - ret=GetLastError(); - return FALSE; - } - - // setup default font information - - LFTTYFONT.lfHeight = 12; - LFTTYFONT.lfWidth = 8 ; - LFTTYFONT.lfEscapement = 0 ; - LFTTYFONT.lfOrientation = 0 ; - LFTTYFONT.lfWeight = 0 ; - LFTTYFONT.lfItalic = 0 ; - LFTTYFONT.lfUnderline = 0 ; - LFTTYFONT.lfStrikeOut = 0 ; - LFTTYFONT.lfCharSet = OEM_CHARSET ; - LFTTYFONT.lfOutPrecision = OUT_DEFAULT_PRECIS ; - LFTTYFONT.lfClipPrecision = CLIP_DEFAULT_PRECIS ; - LFTTYFONT.lfQuality = DEFAULT_QUALITY ; - LFTTYFONT.lfPitchAndFamily = FIXED_PITCH | FF_MODERN ; - lstrcpy( LFTTYFONT.lfFaceName, "Fixedsys" ) ; - - hFont = CreateFontIndirect(&LFTTYFONT) ; -// hFont = MyCreateFont(); - - SetWindowText(hWnd,Title); - - comWnd[0] = GetDlgItem(hWnd, IDC_COM1); - comWnd[1] = GetDlgItem(hWnd, IDC_COM2); - comWnd[2] = GetDlgItem(hWnd, IDC_COM3); - comWnd[3] = GetDlgItem(hWnd, IDC_COM4); - - portWnd[0] = GetDlgItem(hWnd, IDC_HAMLIBPORT1); - portWnd[1] = GetDlgItem(hWnd, IDC_HAMLIBPORT2); - portWnd[2] = GetDlgItem(hWnd, IDC_HAMLIBPORT3); - portWnd[3] = GetDlgItem(hWnd, IDC_HAMLIBPORT4); - - stateWnd[0] = GetDlgItem(hWnd, IDC_STATE1); - stateWnd[1] = GetDlgItem(hWnd, IDC_STATE2); - stateWnd[2] = GetDlgItem(hWnd, IDC_STATE3); - stateWnd[3] = GetDlgItem(hWnd, IDC_STATE4); - - - stateCtl[0] = IDC_STATE1; - stateCtl[1] = IDC_STATE2; - stateCtl[2] = IDC_STATE3; - stateCtl[3] = IDC_STATE4; - - ShowWindow(hWnd, nCmdShow); - - SetDlgItemText(hWnd, IDC_BPQHOST, BPQHostIP); - - SetDlgItemText(hWnd, IDC_COM1, PTTCATPort[0]); - SetDlgItemText(hWnd, IDC_COM2, PTTCATPort[1]); - SetDlgItemText(hWnd, IDC_COM3, PTTCATPort[2]); - SetDlgItemText(hWnd, IDC_COM4, PTTCATPort[3]); - - SetDlgItemInt(hWnd, IDC_HAMLIBPORT1, HamLibPort[0], 0); - SetDlgItemInt(hWnd, IDC_HAMLIBPORT2, HamLibPort[1], 0); - SetDlgItemInt(hWnd, IDC_HAMLIBPORT3, HamLibPort[2], 0); - SetDlgItemInt(hWnd, IDC_HAMLIBPORT4, HamLibPort[3], 0); - -// ioctlsocket (sock, FIONBIO, ¶m); - - if (RigPort) - { - int Speed = 9600; - - if (RigSpeed) - Speed = atoi(RigSpeed); - } - - TimerHandle = SetTimer(hWnd, 1, 10000, NULL); - - runTimer(); // Open Hamlib connections - - _beginthread(PTTCATThread, 0); - - return TRUE; -} - -VOID ConnecttoHAMLIB(int n); - -VOID runTimer() -{ - int n; - - for (n = 0; n < 4; n++) - { - if (HamLibPort[n] && HamLibSock[n] == 0) - { - // try to connect - - ConnecttoHAMLIB(n); - - } - } -} -LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - int wmId, wmEvent; - - switch (message) - { - case WM_TIMER: - - runTimer(); - break; - - case WM_CTLCOLOREDIT: - { - HDC hdc = (HDC) wParam; // handle to display context - HWND hwnd = (HWND) lParam; // handle to control window - int n; - - for (n = 0; n < 4; n++) - { - if (hwnd == comWnd[n] && PTTCATPort[n][0]) - if (PTTCATHandle[n]) - return (INT_PTR)GreenBrush; - else - return (INT_PTR)RedBrush; - - if (hwnd == portWnd[n] && HamLibPort[n]) - if (HamLibSock[n]) - return (INT_PTR)GreenBrush; - else - return (INT_PTR)RedBrush; - } - - //return (INT_PTR)RedBrush; - - return (DefWindowProc(hWnd, message, wParam, lParam)); - } - - case WM_SYSCOMMAND: - - wmId = LOWORD(wParam); // Remember, these are... - wmEvent = HIWORD(wParam); // ...different for Win32! - - switch (wmId) - { - case SC_MINIMIZE: - - if (MinimizetoTray) - - return ShowWindow(hWnd, SW_HIDE); - else - return (DefWindowProc(hWnd, message, wParam, lParam)); - - - break; - - - default: - - return (DefWindowProc(hWnd, message, wParam, lParam)); - } - - case WM_CLOSE: - - PostQuitMessage(0); - break; - - case WM_COMMAND: - - wmId = LOWORD(wParam); - wmEvent = HIWORD(wParam); - - switch (wmId) - { - int OK; - - case IDC_TEST1: - - Rig_PTT(0, 1); - Sleep(1500); - Rig_PTT(0, 0); - break; - - case IDC_TEST2: - - Rig_PTT(1, 1); - Sleep(1500); - Rig_PTT(1, 0); - break; - - case IDC_TEST3: - - Rig_PTT(2, 1); - Sleep(1500); - Rig_PTT(2, 0); - break; - - case IDC_TEST4: - - Rig_PTT(3, 1); - Sleep(1500); - Rig_PTT(3, 0); - break; - - case IDOK: - - GetDlgItemText(hWnd, IDC_BPQHOST, BPQHostIP, 127); - - GetDlgItemText(hWnd, IDC_COM1, PTTCATPort[0], 15); - GetDlgItemText(hWnd, IDC_COM2, PTTCATPort[1], 15); - GetDlgItemText(hWnd, IDC_COM3, PTTCATPort[2], 15); - GetDlgItemText(hWnd, IDC_COM4, PTTCATPort[3], 15); - - HamLibPort[0] = GetDlgItemInt(hWnd, IDC_HAMLIBPORT1, &OK, 0); - HamLibPort[1] = GetDlgItemInt(hWnd, IDC_HAMLIBPORT2, &OK, 0); - HamLibPort[2] = GetDlgItemInt(hWnd, IDC_HAMLIBPORT3, &OK, 0); - HamLibPort[3] = GetDlgItemInt(hWnd, IDC_HAMLIBPORT4, &OK, 0); - - SaveConfig(); - - // EndPTTCATThread = 1; - - // Sleep(1100); - - // _beginthread(PTTCATThread, 0); - - break; - - default: - return (DefWindowProc(hWnd, message, wParam, lParam)); - } - } - - return (DefWindowProc(hWnd, message, wParam, lParam)); - -} - -void HAMLIBProcessMessage(int n) -{ - char RXBuffer[256]; - - int InputLen = recv(HamLibSock[n], RXBuffer, 256, 0); - - if (InputLen == 0 || InputLen == SOCKET_ERROR) - { - if (HamLibSock[n]) - closesocket(HamLibSock[n]); - - HamLibSock[n] = 0; - RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE); - - return; - } -} - - -VOID HAMLIBThread(int n); - -VOID ConnecttoHAMLIB(int n) -{ - _beginthread(HAMLIBThread, 0, n); - return ; -} - -VOID HAMLIBThread(int n) -{ - // Opens sockets and looks for data - - char Msg[255]; - int err, i, ret; - u_long param=1; - BOOL bcopt=TRUE; - fd_set readfs; - fd_set errorfs; - struct timeval timeout; - - if (HamLibSock[n]) - closesocket(HamLibSock[n]); - - // Param is IPADDR:PORT. Only Allow numeric addresses - - if (HamLibPort[n] == 0) - return; - - remoteDest[n].sin_family = AF_INET; - remoteDest[n].sin_addr.s_addr = inet_addr(BPQHostIP); - remoteDest[n].sin_port = htons(HamLibPort[n]); - - HamLibSock[n] = 0; - HamLibSock[n] = socket(AF_INET,SOCK_STREAM,0); - - if (HamLibSock[n] == INVALID_SOCKET) - { - i=sprintf(Msg, "Socket Failed for HAMLIB socket - error code = %d\r\n", WSAGetLastError()); - Debugprintf(Msg); - - return; - } - - setsockopt(HamLibSock[n], SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); - setsockopt(HamLibSock[n], IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); - - if (connect(HamLibSock[n],(LPSOCKADDR)&remoteDest[n],sizeof(remoteDest[n])) == 0) - { - // - // Connected successful - // - } - else - { - err=WSAGetLastError(); - i=sprintf(Msg, "Connect Failed for HAMLIB socket %d - error code = %d\r\n", n, err); - Debugprintf(Msg); - - closesocket(HamLibSock[n]); - - HamLibSock[n] = 0; - - RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE); - - return; - } - - RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE); - - ret = GetLastError(); - - while (HamLibSock[n]) - { - FD_ZERO(&readfs); - FD_ZERO(&errorfs); - - FD_SET(HamLibSock[n],&readfs); - FD_SET(HamLibSock[n],&errorfs); - - timeout.tv_sec = 60; - timeout.tv_usec = 0; - - ret = select((int)HamLibSock[n] + 1, &readfs, NULL, &errorfs, &timeout); - - if (ret == SOCKET_ERROR) - { - Debugprintf("HAMLIB Select failed %d ", WSAGetLastError()); - goto Lost; - } - - if (ret > 0) - { - // See what happened - - if (FD_ISSET(HamLibSock[n], &readfs)) - { - HAMLIBProcessMessage(n); - } - - if (FD_ISSET(HamLibSock[n], &errorfs)) - { -Lost: - sprintf(Msg, "HAMLIB Connection lost for Port %d\r\n", n); - Debugprintf(Msg); - - closesocket(HamLibSock[n]); - HamLibSock[n] = 0; - RedrawWindow(hWnd, NULL, NULL, 0); - - return; - } - continue; - } - else - { - } - } - sprintf(Msg, "HAMLIB Thread Terminated Port %d\r\n", n); - Debugprintf(Msg); -} + +// Program to Convert RTS changes on Virtual COM Port to hamlib PTT commands + + +// Version 1. 0. 0. 1 December 2020 + +// Version 1. 0. 2. 1 April 2018 + + +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_DEPRECATE + +#include + +#include +#include +#include +#include +#include + +#define LIBCONFIG_STATIC +#include "libconfig.h" + + +#include "BPQRemotePTTRes.h" + +#define BPQICON 400 + +WSADATA WsaData; // receives data from WSAStartup + +#define WSA_READ WM_USER + 1 + +HINSTANCE hInst; +char AppName[] = "BPQRemotePTT"; +char Title[80] = "BPQRemotePTT"; + +TCHAR szTitle[]="GPSMuxPC"; // The title bar text +TCHAR szWindowClass[]="GPSMAINWINDOW"; // the main window class name + + +// Foward declarations of functions included in this code module: + +ATOM MyRegisterClass(HINSTANCE hInstance); +BOOL InitInstance(HINSTANCE, int); +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM); +VOID WINAPI CompletedReadRoutine(DWORD dwErr, DWORD cbBytesRead, LPOVERLAPPED lpOverLap); +VOID WINAPI txCompletedReadRoutine(DWORD dwErr, DWORD cbBytesRead, LPOVERLAPPED lpOverLap); +VOID CATThread(); +VOID runTimer(); + +int TimerHandle = 0; + +LOGFONT LFTTYFONT ; + +HFONT hFont; + +struct sockaddr_in sinx; +struct sockaddr rx; + +int addrlen = sizeof(struct sockaddr_in); + +int HAMLIBPORT; // Port Number for HAMLIB (rigctld) Emulator +char * HAMLIBHOST[128]; + +BOOL MinimizetoTray=FALSE; + +VOID __cdecl Debugprintf(const char * format, ...) +{ + char Mess[255]; + va_list(arglist); + + va_start(arglist, format); + vsprintf(Mess, format, arglist); + strcat(Mess, "\r\n"); + + OutputDebugString(Mess); + + return; +} + + +char * strlop(char * buf, char delim) +{ + // Terminate buf at delim, and return rest of string + + char * ptr = strchr(buf, delim); + + if (ptr == NULL) return NULL; + + *(ptr)++=0; + + return ptr; +} + +char * RigPort = NULL; +char * RigSpeed = NULL; +HANDLE RigHandle = 0; +int RigType = 0; // Flag for possible RTS/DTR + + +char BPQHostIP[128]; + +char PTTCATPort[4][16]; +HANDLE PTTCATHandle[4]; +short HamLibPort[4]; +SOCKET HamLibSock[4]; // rigctld socket + +HWND comWnd[4]; +HWND portWnd[4]; +HWND stateWnd[4]; +int stateCtl[4]; + +struct sockaddr_in remoteDest[4]; + +int RealMux[4]; // BPQ Virtual or Real + +int EndPTTCATThread = 0; + +char ConfigName[256] = "BPQRemotePTT.cfg"; + +BOOL GetStringValue(config_setting_t * group, char * name, char * value) +{ + const char * str; + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + { + str = config_setting_get_string (setting); + strcpy(value, str); + return TRUE; + } + return FALSE; +} + +int GetIntValue(config_setting_t * group, char * name) +{ + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + return config_setting_get_int (setting); + + return 0; +} + +VOID SaveStringValue(config_setting_t * group, char * name, char * value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_STRING); + if (setting) + config_setting_set_string(setting, value); + +} + + +VOID SaveIntValue(config_setting_t * group, char * name, int value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_INT); + if(setting) + config_setting_set_int(setting, value); +} +VOID GetConfig() +{ + config_setting_t *group; + config_t cfg; + + memset((void *)&cfg, 0, sizeof(config_t)); + + config_init(&cfg); + + if(!config_read_file(&cfg, ConfigName)) + { + fprintf(stderr, "Config read error line %d - %s\n", config_error_line(&cfg), config_error_text(&cfg)); + config_destroy(&cfg); + return; + } + + group = config_lookup(&cfg, "main"); + + if (group == NULL) + { + config_destroy(&cfg); + return; + } + + + GetStringValue(group, "BPQHostIP", BPQHostIP); + + GetStringValue(group, "COM1", PTTCATPort[0]); + GetStringValue(group, "COM2", PTTCATPort[1]); + GetStringValue(group, "COM3", PTTCATPort[2]); + GetStringValue(group, "COM4", PTTCATPort[3]); + + HamLibPort[0] = GetIntValue(group, "HamLibPort1"); + HamLibPort[1] = GetIntValue(group, "HamLibPort2"); + HamLibPort[2] = GetIntValue(group, "HamLibPort3"); + HamLibPort[3] = GetIntValue(group, "HamLibPort4"); + + config_destroy(&cfg); +} + +VOID SaveConfig() +{ + config_setting_t *root, *group; + config_t cfg; + + // Get rid of old config before saving + + config_init(&cfg); + + root = config_root_setting(&cfg); + + group = config_setting_add(root, "main", CONFIG_TYPE_GROUP); + + SaveStringValue(group, "BPQHostIP", BPQHostIP); + + SaveStringValue(group, "COM1", PTTCATPort[0]); + SaveStringValue(group, "COM2", PTTCATPort[1]); + SaveStringValue(group, "COM3", PTTCATPort[2]); + SaveStringValue(group, "COM4", PTTCATPort[3]); + + SaveIntValue(group, "HamLibPort1", HamLibPort[0]); + SaveIntValue(group, "HamLibPort2", HamLibPort[1]); + SaveIntValue(group, "HamLibPort3", HamLibPort[2]); + SaveIntValue(group, "HamLibPort4", HamLibPort[3]); + + if(!config_write_file(&cfg, ConfigName)) + { + fprintf(stderr, "Error while writing file.\n"); + config_destroy(&cfg); + return; + } + + config_destroy(&cfg); +} + + + + +int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +{ + MSG msg; + + if (lpCmdLine[0]) + { + // Port Name and Speed for Remote CAT + + RigPort = _strdup(lpCmdLine); + RigSpeed = strlop(RigPort, ':'); + } + + MyRegisterClass(hInstance); + + GetConfig(); + + if (!InitInstance(hInstance, nCmdShow)) + return (FALSE); + + // Main message loop: + + while (GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + KillTimer(NULL, TimerHandle); + + return (msg.wParam); +} + +// + +// +// FUNCTION: InitApplication(HANDLE) +// +// PURPOSE: Initializes window data and registers window class +// +// COMMENTS: +// +// In this function, we initialize a window class by filling out a data +// structure of type WNDCLASS and calling either RegisterClass or +// the internal MyRegisterClass. +// + +#define BGCOLOUR RGB(236,233,216) +HBRUSH bgBrush; +HBRUSH RedBrush; +HBRUSH GreenBrush; + +HWND hWnd; + +BOOL InitApplication(HINSTANCE hInstance) +{ + return TRUE; +} + +// +// FUNCTION: InitInstance(HANDLE, int) +// +// PURPOSE: Saves instance handle and creates main window +// +// COMMENTS: +// +// In this function, we save the instance handle in a global variable and +// create and display the main program window. +// + +HFONT FAR PASCAL MyCreateFont( void ) +{ + CHOOSEFONT cf; + LOGFONT lf; + HFONT hfont; + + // Initialize members of the CHOOSEFONT structure. + + cf.lStructSize = sizeof(CHOOSEFONT); + cf.hwndOwner = (HWND)NULL; + cf.hDC = (HDC)NULL; + cf.lpLogFont = &lf; + cf.iPointSize = 0; + cf.Flags = CF_SCREENFONTS | CF_FIXEDPITCHONLY; + cf.rgbColors = RGB(0,0,0); + cf.lCustData = 0L; + cf.lpfnHook = (LPCFHOOKPROC)NULL; + cf.lpTemplateName = (LPSTR)NULL; + cf.hInstance = (HINSTANCE) NULL; + cf.lpszStyle = (LPSTR)NULL; + cf.nFontType = SCREEN_FONTTYPE; + cf.nSizeMin = 0; + cf.nSizeMax = 0; + + // Display the CHOOSEFONT common-dialog box. + + ChooseFont(&cf); + + // Create a logical font based on the user's + // selection and return a handle identifying + // that font. + + hfont = CreateFontIndirect(cf.lpLogFont); + return (hfont); +} + +VOID Rig_PTT(int n, BOOL PTTState) +{ + + char Msg[16]; + int Len = sprintf(Msg, "T %d\n", PTTState); + + if (HamLibSock[n]) + { + send(HamLibSock[n], Msg, Len, 0); + + if (PTTState) + SetDlgItemText(hWnd, stateCtl[n], "PTT"); + else + SetDlgItemText(hWnd, stateCtl[n], ""); + } +} + + + +VOID PTTCATThread() +{ + DWORD dwLength = 0; + int Length, ret, i; + UCHAR * ptr1; + UCHAR * ptr2; + UCHAR c; + UCHAR Block[4][80]; + UCHAR CurrentState[4] = {0}; +#define RTS 2 +#define DTR 4 + HANDLE Event; + DWORD EvtMask[4]; + OVERLAPPED Overlapped[4]; + char Port[32]; + int PIndex = 0; + int HIndex = 0; + int rc; + + EndPTTCATThread = FALSE; + + while (PIndex < 4 && PTTCATPort[PIndex][0]) + { + RealMux[HIndex] = 0; + + sprintf(Port, "\\\\.\\pipe\\BPQ%s", PTTCATPort[PIndex]); + + PTTCATHandle[HIndex] = CreateFile(Port, GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + + if (PTTCATHandle[HIndex] == INVALID_HANDLE_VALUE) + { + int Err = GetLastError(); +// Consoleprintf("PTTMUX port BPQCOM%s Open failed code %d", RIG->PTTCATPort[PIndex], Err); + + // See if real com port + + sprintf(Port, "\\\\.\\\\%s", PTTCATPort[PIndex]); + + PTTCATHandle[HIndex] = CreateFile(Port, GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + + RealMux[HIndex] = 1; + + if (PTTCATHandle[HIndex] == INVALID_HANDLE_VALUE) + { + int Err = GetLastError(); + PTTCATHandle[HIndex] = 0; + Debugprintf("PTTMUX port COM%s Open failed code %d", PTTCATPort[PIndex], Err); + } + else + { + rc = SetCommMask(PTTCATHandle[HIndex], EV_CTS | EV_DSR); // Request notifications + HIndex++; + } + } + else + HIndex++; + + PIndex++; + + } + + if (PIndex == 0) + return; // No ports + + Event = CreateEvent(NULL, TRUE, FALSE, NULL); + + for (i = 0; i < HIndex; i ++) + { + memset(&Overlapped[i], 0, sizeof(OVERLAPPED)); + Overlapped[i].hEvent = Event; + + if (RealMux[i]) + { + // Request Interface change notifications + + rc = WaitCommEvent(PTTCATHandle[i], &EvtMask[i], &Overlapped[i]); + rc = GetLastError(); + + } + else + { + // Prime a read on each PTTCATHandle + + ReadFile(PTTCATHandle[i], Block[i], 80, &Length, &Overlapped[i]); + } + } + + while (EndPTTCATThread == FALSE) + { + +WaitAgain: + + ret = WaitForSingleObject(Event, 1000); + + if (ret == WAIT_TIMEOUT) + { + if (EndPTTCATThread) + { + for (i = 0; i < HIndex; i ++) + { + CancelIo(PTTCATHandle[i]); + CloseHandle(PTTCATHandle[i]); + PTTCATHandle[i] = INVALID_HANDLE_VALUE; + } + CloseHandle(Event); + return; + } + goto WaitAgain; + } + + ResetEvent(Event); + + // See which request(s) have completed + + for (i = 0; i < HIndex; i ++) + { + ret = GetOverlappedResult(PTTCATHandle[i], &Overlapped[i], &Length, FALSE); + + if (ret) + { + if (RealMux[i]) + { + // Request Interface change notifications + + DWORD Mask; + + GetCommModemStatus(PTTCATHandle[i], &Mask); + + if (Mask & MS_CTS_ON) + Rig_PTT(i, TRUE); + else + Rig_PTT(i, FALSE); + + memset(&Overlapped[i], 0, sizeof(OVERLAPPED)); + Overlapped[i].hEvent = Event; + WaitCommEvent(PTTCATHandle[i], &EvtMask[i], &Overlapped[i]); + + } + else + { + + ptr1 = Block[i]; + ptr2 = Block[i]; + + while (Length > 0) + { + c = *(ptr1++); + + Length--; + + if (c == 0xff) + { + c = *(ptr1++); + Length--; + + if (c == 0xff) // ff ff means ff + { + Length--; + } + else + { + // This is connection / RTS/DTR statua from other end + // Convert to CAT Command + + if (c == CurrentState[i]) + continue; + + if (c & RTS) + Rig_PTT(i, TRUE); + else + Rig_PTT(i, FALSE); + + CurrentState[i] = c; + continue; + } + } + } + + memset(&Overlapped[i], 0, sizeof(OVERLAPPED)); + Overlapped[i].hEvent = Event; + + ReadFile(PTTCATHandle[i], Block[i], 80, &Length, &Overlapped[i]); + } + } + } + } + EndPTTCATThread = FALSE; +} + +char ClassName[]="BPQMAIL"; + +ATOM MyRegisterClass(HINSTANCE hInstance) +{ + WNDCLASS wc; + + bgBrush = CreateSolidBrush(BGCOLOUR); + RedBrush = CreateSolidBrush(RGB(255,0,0)); + GreenBrush = CreateSolidBrush(RGB(0,255,0)); +// BlueBrush = CreateSolidBrush(RGB(0,0,255)); + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; + wc.lpfnWndProc = WndProc; + + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = NULL; //LoadIcon( hInstance, MAKEINTRESOURCE(IDI_GPSMUXPC)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL;//"MENU_1"; + wc.lpszClassName = szWindowClass; + +// RegisterClass(&wc); + + // wc.lpfnWndProc = TraceWndProc; + // wc.lpszClassName = TraceClassName; + +// RegisterClass(&wc); + +// wc.lpfnWndProc = ConfigWndProc; +// wc.lpszClassName = ConfigClassName; + + return (RegisterClass(&wc)); + +} + + +BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) +{ + u_long param=1; + BOOL bcopt=TRUE; + int ret; + WNDCLASS wc = {0}; + int n = 0; + + + hInst = hInstance; // Store instance handle in our global variable + + WSAStartup(MAKEWORD(2, 0), &WsaData); + + + hWnd=CreateDialog(hInst,szWindowClass,0,NULL); + + if (!hWnd) + { + ret=GetLastError(); + return FALSE; + } + + // setup default font information + + LFTTYFONT.lfHeight = 12; + LFTTYFONT.lfWidth = 8 ; + LFTTYFONT.lfEscapement = 0 ; + LFTTYFONT.lfOrientation = 0 ; + LFTTYFONT.lfWeight = 0 ; + LFTTYFONT.lfItalic = 0 ; + LFTTYFONT.lfUnderline = 0 ; + LFTTYFONT.lfStrikeOut = 0 ; + LFTTYFONT.lfCharSet = OEM_CHARSET ; + LFTTYFONT.lfOutPrecision = OUT_DEFAULT_PRECIS ; + LFTTYFONT.lfClipPrecision = CLIP_DEFAULT_PRECIS ; + LFTTYFONT.lfQuality = DEFAULT_QUALITY ; + LFTTYFONT.lfPitchAndFamily = FIXED_PITCH | FF_MODERN ; + lstrcpy( LFTTYFONT.lfFaceName, "Fixedsys" ) ; + + hFont = CreateFontIndirect(&LFTTYFONT) ; +// hFont = MyCreateFont(); + + SetWindowText(hWnd,Title); + + comWnd[0] = GetDlgItem(hWnd, IDC_COM1); + comWnd[1] = GetDlgItem(hWnd, IDC_COM2); + comWnd[2] = GetDlgItem(hWnd, IDC_COM3); + comWnd[3] = GetDlgItem(hWnd, IDC_COM4); + + portWnd[0] = GetDlgItem(hWnd, IDC_HAMLIBPORT1); + portWnd[1] = GetDlgItem(hWnd, IDC_HAMLIBPORT2); + portWnd[2] = GetDlgItem(hWnd, IDC_HAMLIBPORT3); + portWnd[3] = GetDlgItem(hWnd, IDC_HAMLIBPORT4); + + stateWnd[0] = GetDlgItem(hWnd, IDC_STATE1); + stateWnd[1] = GetDlgItem(hWnd, IDC_STATE2); + stateWnd[2] = GetDlgItem(hWnd, IDC_STATE3); + stateWnd[3] = GetDlgItem(hWnd, IDC_STATE4); + + + stateCtl[0] = IDC_STATE1; + stateCtl[1] = IDC_STATE2; + stateCtl[2] = IDC_STATE3; + stateCtl[3] = IDC_STATE4; + + ShowWindow(hWnd, nCmdShow); + + SetDlgItemText(hWnd, IDC_BPQHOST, BPQHostIP); + + SetDlgItemText(hWnd, IDC_COM1, PTTCATPort[0]); + SetDlgItemText(hWnd, IDC_COM2, PTTCATPort[1]); + SetDlgItemText(hWnd, IDC_COM3, PTTCATPort[2]); + SetDlgItemText(hWnd, IDC_COM4, PTTCATPort[3]); + + SetDlgItemInt(hWnd, IDC_HAMLIBPORT1, HamLibPort[0], 0); + SetDlgItemInt(hWnd, IDC_HAMLIBPORT2, HamLibPort[1], 0); + SetDlgItemInt(hWnd, IDC_HAMLIBPORT3, HamLibPort[2], 0); + SetDlgItemInt(hWnd, IDC_HAMLIBPORT4, HamLibPort[3], 0); + +// ioctlsocket (sock, FIONBIO, ¶m); + + if (RigPort) + { + int Speed = 9600; + + if (RigSpeed) + Speed = atoi(RigSpeed); + } + + TimerHandle = SetTimer(hWnd, 1, 10000, NULL); + + runTimer(); // Open Hamlib connections + + _beginthread(PTTCATThread, 0); + + return TRUE; +} + +VOID ConnecttoHAMLIB(int n); + +VOID runTimer() +{ + int n; + + for (n = 0; n < 4; n++) + { + if (HamLibPort[n] && HamLibSock[n] == 0) + { + // try to connect + + ConnecttoHAMLIB(n); + + } + } +} +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + + switch (message) + { + case WM_TIMER: + + runTimer(); + break; + + case WM_CTLCOLOREDIT: + { + HDC hdc = (HDC) wParam; // handle to display context + HWND hwnd = (HWND) lParam; // handle to control window + int n; + + for (n = 0; n < 4; n++) + { + if (hwnd == comWnd[n] && PTTCATPort[n][0]) + if (PTTCATHandle[n]) + return (INT_PTR)GreenBrush; + else + return (INT_PTR)RedBrush; + + if (hwnd == portWnd[n] && HamLibPort[n]) + if (HamLibSock[n]) + return (INT_PTR)GreenBrush; + else + return (INT_PTR)RedBrush; + } + + //return (INT_PTR)RedBrush; + + return (DefWindowProc(hWnd, message, wParam, lParam)); + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_MINIMIZE: + + if (MinimizetoTray) + + return ShowWindow(hWnd, SW_HIDE); + else + return (DefWindowProc(hWnd, message, wParam, lParam)); + + + break; + + + default: + + return (DefWindowProc(hWnd, message, wParam, lParam)); + } + + case WM_CLOSE: + + PostQuitMessage(0); + break; + + case WM_COMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + int OK; + + case IDC_TEST1: + + Rig_PTT(0, 1); + Sleep(1500); + Rig_PTT(0, 0); + break; + + case IDC_TEST2: + + Rig_PTT(1, 1); + Sleep(1500); + Rig_PTT(1, 0); + break; + + case IDC_TEST3: + + Rig_PTT(2, 1); + Sleep(1500); + Rig_PTT(2, 0); + break; + + case IDC_TEST4: + + Rig_PTT(3, 1); + Sleep(1500); + Rig_PTT(3, 0); + break; + + case IDOK: + + GetDlgItemText(hWnd, IDC_BPQHOST, BPQHostIP, 127); + + GetDlgItemText(hWnd, IDC_COM1, PTTCATPort[0], 15); + GetDlgItemText(hWnd, IDC_COM2, PTTCATPort[1], 15); + GetDlgItemText(hWnd, IDC_COM3, PTTCATPort[2], 15); + GetDlgItemText(hWnd, IDC_COM4, PTTCATPort[3], 15); + + HamLibPort[0] = GetDlgItemInt(hWnd, IDC_HAMLIBPORT1, &OK, 0); + HamLibPort[1] = GetDlgItemInt(hWnd, IDC_HAMLIBPORT2, &OK, 0); + HamLibPort[2] = GetDlgItemInt(hWnd, IDC_HAMLIBPORT3, &OK, 0); + HamLibPort[3] = GetDlgItemInt(hWnd, IDC_HAMLIBPORT4, &OK, 0); + + SaveConfig(); + + // EndPTTCATThread = 1; + + // Sleep(1100); + + // _beginthread(PTTCATThread, 0); + + break; + + default: + return (DefWindowProc(hWnd, message, wParam, lParam)); + } + } + + return (DefWindowProc(hWnd, message, wParam, lParam)); + +} + +void HAMLIBProcessMessage(int n) +{ + char RXBuffer[256]; + + int InputLen = recv(HamLibSock[n], RXBuffer, 256, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + if (HamLibSock[n]) + closesocket(HamLibSock[n]); + + HamLibSock[n] = 0; + RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE); + + return; + } +} + + +VOID HAMLIBThread(int n); + +VOID ConnecttoHAMLIB(int n) +{ + _beginthread(HAMLIBThread, 0, n); + return ; +} + +VOID HAMLIBThread(int n) +{ + // Opens sockets and looks for data + + char Msg[255]; + int err, i, ret; + u_long param=1; + BOOL bcopt=TRUE; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + + if (HamLibSock[n]) + closesocket(HamLibSock[n]); + + // Param is IPADDR:PORT. Only Allow numeric addresses + + if (HamLibPort[n] == 0) + return; + + remoteDest[n].sin_family = AF_INET; + remoteDest[n].sin_addr.s_addr = inet_addr(BPQHostIP); + remoteDest[n].sin_port = htons(HamLibPort[n]); + + HamLibSock[n] = 0; + HamLibSock[n] = socket(AF_INET,SOCK_STREAM,0); + + if (HamLibSock[n] == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for HAMLIB socket - error code = %d\r\n", WSAGetLastError()); + Debugprintf(Msg); + + return; + } + + setsockopt(HamLibSock[n], SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(HamLibSock[n], IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + if (connect(HamLibSock[n],(LPSOCKADDR)&remoteDest[n],sizeof(remoteDest[n])) == 0) + { + // + // Connected successful + // + } + else + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for HAMLIB socket %d - error code = %d\r\n", n, err); + Debugprintf(Msg); + + closesocket(HamLibSock[n]); + + HamLibSock[n] = 0; + + RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE); + + return; + } + + RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE); + + ret = GetLastError(); + + while (HamLibSock[n]) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(HamLibSock[n],&readfs); + FD_SET(HamLibSock[n],&errorfs); + + timeout.tv_sec = 60; + timeout.tv_usec = 0; + + ret = select((int)HamLibSock[n] + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + Debugprintf("HAMLIB Select failed %d ", WSAGetLastError()); + goto Lost; + } + + if (ret > 0) + { + // See what happened + + if (FD_ISSET(HamLibSock[n], &readfs)) + { + HAMLIBProcessMessage(n); + } + + if (FD_ISSET(HamLibSock[n], &errorfs)) + { +Lost: + sprintf(Msg, "HAMLIB Connection lost for Port %d\r\n", n); + Debugprintf(Msg); + + closesocket(HamLibSock[n]); + HamLibSock[n] = 0; + RedrawWindow(hWnd, NULL, NULL, 0); + + return; + } + continue; + } + else + { + } + } + sprintf(Msg, "HAMLIB Thread Terminated Port %d\r\n", n); + Debugprintf(Msg); +} diff --git a/.svn/pristine/14/147067e4beec11a2c197551503067c2e0ff303a1.svn-base b/.svn/pristine/14/147067e4beec11a2c197551503067c2e0ff303a1.svn-base index fdffe2a..46cb0b7 100644 --- a/.svn/pristine/14/147067e4beec11a2c197551503067c2e0ff303a1.svn-base +++ b/.svn/pristine/14/147067e4beec11a2c197551503067c2e0ff303a1.svn-base @@ -1,45 +1,45 @@ -// -// HTTP Session Control. Used In Kernel HTTPCode, BBSHTMLConfig -// and ChatHTMLConfig - -// On Windows changes to layout or length of this struct require rebuilding BPQ32.dll, BPQMail and BPQChat - -struct HTTPConnectionInfo // Used for Web Server for thread-specific stuff -{ - struct HTTPConnectionInfo * Next; - struct STATIONRECORD * SelCall; // Station Record for individual station display - char Callsign[12]; - int WindDirn, WindSpeed, WindGust, Temp, RainLastHour, RainLastDay, RainToday, Humidity, Pressure; //WX Fields - char * ScreenLines[100]; // Screen Image for Teminal access mode - max 100 lines (cyclic buffer) - int ScreenLineLen[100]; // Length of each lime - int LastLine; // Pointer to last line of data - BOOL PartLine; // Last line does not have CR on end - char HTTPCall[10]; // Call of HTTP user - BOOL Changed; // Changed since last poll. If set, reply immediately, else set timer and wait - SOCKET sock; // Socket for pending send - int ResponseTimer; // Timer for delayed response - int KillTimer; // Clean up timer (no activity timeout) - int Stream; // BPQ Stream Number - char Key[20]; // Session Key - BOOL Connected; - // Use by Mail Module -#ifdef MAIL - struct UserInfo * User; // Selected User - struct MsgInfo * Msg; // Selected Message - WPRec * WP; // Selected WP record - WebMailInfo * WebMail; // Webmail Forms Info -#else - VOID * User; // Selected User - VOID * Msg; // Selected Message - VOID * WP; // Selected WP record - VOID * WebMail; // Webmail Forms Info -#endif - struct UserRec * USER; // Telnet Server USER record - int WebMailSkip; // Number to skip at start of list (for paging) - char WebMailTypes[4]; // Types To List - BOOL WebMailMine; // List all meessage to or from me - BOOL WebMailMyTX; // List all meessage from me - BOOL WebMailMyRX; // List all meessage to me - time_t WebMailLastUsed; - struct TNCINFO * TNC; // Session -> TNC link -}; +// +// HTTP Session Control. Used In Kernel HTTPCode, BBSHTMLConfig +// and ChatHTMLConfig + +// On Windows changes to layout or length of this struct require rebuilding BPQ32.dll, BPQMail and BPQChat + +struct HTTPConnectionInfo // Used for Web Server for thread-specific stuff +{ + struct HTTPConnectionInfo * Next; + struct STATIONRECORD * SelCall; // Station Record for individual station display + char Callsign[12]; + int WindDirn, WindSpeed, WindGust, Temp, RainLastHour, RainLastDay, RainToday, Humidity, Pressure; //WX Fields + char * ScreenLines[100]; // Screen Image for Teminal access mode - max 100 lines (cyclic buffer) + int ScreenLineLen[100]; // Length of each lime + int LastLine; // Pointer to last line of data + BOOL PartLine; // Last line does not have CR on end + char HTTPCall[10]; // Call of HTTP user + BOOL Changed; // Changed since last poll. If set, reply immediately, else set timer and wait + SOCKET sock; // Socket for pending send + int ResponseTimer; // Timer for delayed response + int KillTimer; // Clean up timer (no activity timeout) + int Stream; // BPQ Stream Number + char Key[20]; // Session Key + BOOL Connected; + // Use by Mail Module +#ifdef MAIL + struct UserInfo * User; // Selected User + struct MsgInfo * Msg; // Selected Message + WPRec * WP; // Selected WP record + WebMailInfo * WebMail; // Webmail Forms Info +#else + VOID * User; // Selected User + VOID * Msg; // Selected Message + VOID * WP; // Selected WP record + VOID * WebMail; // Webmail Forms Info +#endif + struct UserRec * USER; // Telnet Server USER record + int WebMailSkip; // Number to skip at start of list (for paging) + char WebMailTypes[4]; // Types To List + BOOL WebMailMine; // List all meessage to or from me + BOOL WebMailMyTX; // List all meessage from me + BOOL WebMailMyRX; // List all meessage to me + time_t WebMailLastUsed; + struct TNCINFO * TNC; // Session -> TNC link +}; diff --git a/.svn/pristine/14/148f6414536f7829e65ade1ec3c7a09edd84765d.svn-base b/.svn/pristine/14/148f6414536f7829e65ade1ec3c7a09edd84765d.svn-base index 3e3d08b..7c60f9f 100644 --- a/.svn/pristine/14/148f6414536f7829e65ade1ec3c7a09edd84765d.svn-base +++ b/.svn/pristine/14/148f6414536f7829e65ade1ec3c7a09edd84765d.svn-base @@ -1,1907 +1,1907 @@ -/* -Copyright 2001-2018 John Wiseman G8BPQ - -This file is part of LinBPQ/BPQ32. - -LinBPQ/BPQ32 is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -LinBPQ/BPQ32 is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses -*/ - -// -// DLL to inteface HAL Communications Corp Clover/Pacor controllers to BPQ32 switch -// -// Uses BPQ EXTERNAL interface -// - -#define _CRT_SECURE_NO_WARNINGS -#define _CRT_SECURE_NO_DEPRECATE - -#include "time.h" - -#include "CHeaders.h" -#include "tncinfo.h" - -#include "bpq32.h" - -#define HAL 1 - -#define SetMYCALL 0x13 -#define ConnectEnable 0x52 -#define ConnectDisable 0x42 -#define SetEAS 0x59 // Echo as Sent -#define SetTones 0xec -#define ClearOnDisc 0x57 - -static char ClassName[]="HALSTATUS"; - -static char WindowTitle[] = "HAL"; -static int RigControlRow = 185; - -struct TNCINFO * TNCInfo[34]; // Records are Malloc'd - -#define SOH 0x01 // CONTROL CODES -#define ETB 0x17 -#define DLE 0x10 - -//int MaxStreams = 0; - -#ifndef LINBPQ -extern HFONT hFont; -#endif - -static char status[23][50] = {"IDLE", "TFC", "RQ", "ERR", "PHS", "OVER", "FSK TX", - "FSK RX", "P-MODE100", "P-MODE200", "HUFMAN ON", "HUFMAN OFF", "P-MODE SBY(LISTEN ON)", - "P-MODE SBY(LISTEN OFF)", "ISS", "IRS", - "AMTOR SBY(LISTEN ON)", "AMTOR SBY(LISTEN OFF)", "AMTOR FEC TX", "AMTOR FEC RX", "P-MODE FEC TX", - "FREE SIGNAL TX (AMTOR)", "FREE SIGNAL TX TIMED OUT (AMTOR)"}; - -struct TNCINFO * CreateTTYInfo(int port, int speed); -BOOL OpenConnection(int); -BOOL SetupConnection(int); -static BOOL WriteCommBlock(struct TNCINFO * TNC); -static void CheckRX(struct TNCINFO * TNC); -VOID HALPoll(int Port); -VOID ProcessDEDFrame(struct TNCINFO * TNC, UCHAR * rxbuff, int len); -VOID ProcessTermModeResponse(struct TNCINFO * TNC); -static VOID DoTNCReinit(struct TNCINFO * TNC); -VOID DoTermModeTimeout(struct TNCINFO * TNC); -VOID ProcessHALBuffer(struct TNCINFO * TNC, int Length); -VOID ProcessHALCmd(struct TNCINFO * TNC); -VOID ProcessHALData(struct TNCINFO * TNC); -VOID ProcessKHOSTPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); -VOID ProcessKNormCommand(struct TNCINFO * TNC, UCHAR * rxbuffer); -VOID ProcessHostFrame(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); -VOID DoMonitor(struct TNCINFO * TNC, UCHAR * Msg, int Len); - -BOOL HALConnected(struct TNCINFO * TNC, char * Call); -VOID HALDisconnected(struct TNCINFO * TNC); - -static VOID EncodeAndSend(struct TNCINFO * TNC, UCHAR * txbuffer, int Len); -VOID SendCmd(struct TNCINFO * TNC, UCHAR * txbuffer, int Len); -int DLEEncode(UCHAR * inbuff, UCHAR * outbuff, int len); -int DLEDecode(UCHAR * inbuff, UCHAR * outbuff, int len); - -VOID COMClearDTR(HANDLE fd); -VOID COMClearRTS(HANDLE fd); -int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); - - - -//static HANDLE LogHandle[4] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; - -//char * Logs[4] = {"1", "2", "3", "4"}; - -//char BaseDir[]="c:"; - -static VOID CloseLogfile(int Flags) -{ -// CloseHandle(LogHandle[Flags]); -// LogHandle[Flags] = INVALID_HANDLE_VALUE; -} - -static VOID OpenLogfile(int Flags) -{ -/* -UCHAR FN[MAX_PATH]; - time_t T; - struct tm * tm; - - T = time(NULL); - tm = gmtime(&T); - - sprintf(FN,"%s\\HALLog_%02d%02d%02d_%s.bin", BaseDir, tm->tm_mday, tm->tm_hour, tm->tm_min, Logs[Flags]); - - LogHandle[Flags] = CreateFile(FN, - GENERIC_WRITE, - FILE_SHARE_READ, - NULL, - OPEN_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - NULL); - - SetFilePointer(LogHandle[Flags], 0, 0, FILE_END); - - return (LogHandle[Flags] != INVALID_HANDLE_VALUE); -*/ -} - -static void WriteLogLine(int Flags, char * Msg, int MsgLen) -{ -// int cnt; -// WriteFile(LogHandle[Flags] ,Msg , MsgLen, &cnt, NULL); -} - - - -int ProcessLine(char * buf, int Port) -{ - UCHAR * ptr,* p_cmd; - char * p_ipad = 0; - char * p_port = 0; - unsigned short WINMORport = 0; - int BPQport; - int len=510; - struct TNCINFO * TNC; - char errbuf[256]; - - strcpy(errbuf, buf); - - ptr = strtok(buf, " \t\n\r"); - - if(ptr == NULL) return (TRUE); - - if(*ptr =='#') return (TRUE); // comment - - if(*ptr ==';') return (TRUE); // comment - - ptr = strtok(NULL, " \t\n\r"); - - if (_stricmp(buf, "APPL") == 0) // Using BPQ32 COnfig - { - BPQport = Port; - p_cmd = ptr; - } - else - if (_stricmp(buf, "PORT") != 0) // Using Old Config - { - // New config without a PORT or APPL - this is a Config Command - - strcpy(buf, errbuf); - strcat(buf, "\r"); - - BPQport = Port; - - TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO)); - - TNC->InitScript = malloc(1000); - TNC->InitScript[0] = 0; - goto ConfigLine; - } - else - - { - - // Old Config from file - - BPQport=0; - BPQport = atoi(ptr); - - p_cmd = strtok(NULL, " \t\n\r"); - - if (Port && Port != BPQport) - { - // Want a particular port, and this isn't it - - while(TRUE) - { - if (GetLine(buf) == 0) - return TRUE; - - if (memcmp(buf, "****", 4) == 0) - return TRUE; - - } - } - } - if(BPQport > 0 && BPQport < 33) - { - TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO)); - TNC->InitScript = malloc(1000); - TNC->InitScript[0] = 0; - - if (p_cmd != NULL) - { - if (p_cmd[0] != ';' && p_cmd[0] != '#') - TNC->ApplCmd=_strdup(p_cmd); - } - - // Read Initialisation lines - - while(TRUE) - { - if (GetLine(buf) == 0) - return TRUE; -ConfigLine: - strcpy(errbuf, buf); - - if (memcmp(buf, "****", 4) == 0) - return TRUE; - - ptr = strchr(buf, ';'); - if (ptr) - { - *ptr++ = 13; - *ptr = 0; - } - - if (_memicmp(buf, "WL2KREPORT", 10) == 0) - { - TNC->WL2K = DecodeWL2KReportLine(buf); - continue; - } - if (_memicmp(buf, "NEEDXONXOFF", 10) == 0) - { - TNC->XONXOFF = TRUE; - continue; - } - - if (_memicmp(buf, "TONES", 5) == 0) - { - int tone1 = 0, tone2 = 0; - - ptr = strtok(&buf[6], " ,/\t\n\r"); - if (ptr) - { - tone1 = atoi(ptr); - ptr = strtok(NULL, " ,/\t\n\r"); - if (ptr) - { - tone2 = atoi(ptr); - ptr = &TNC->InitScript[TNC->InitScriptLen]; - - // Try putting into FSK mode first - - *(ptr++) = 0x84; - *(ptr++) = SetTones; // Set Tones (Mark, Space HI byte first) - *(ptr++) = tone1 >> 8; - *(ptr++) = tone1 & 0xff; - *(ptr++) = tone2 >> 8; - *(ptr++) = tone2 & 0xff; - - TNC->InitScriptLen += 6; - - continue; - } - } - goto BadLine; - } - if (_memicmp(buf, "DEFAULTMODE ", 12) == 0) - { - - ptr = strtok(&buf[12], " ,\t\n\r"); - if (ptr) - { - if (_stricmp(ptr, "CLOVER") == 0) - TNC->DefaultMode = Clover; - else if (_stricmp(ptr, "PACTOR") == 0) - TNC->DefaultMode = Pactor; - else if (_stricmp(ptr, "AMTOR") == 0) - TNC->DefaultMode = AMTOR; - else goto BadLine; - - continue; - } - goto BadLine; - } - } - BadLine: - WritetoConsole(" Bad config record "); - WritetoConsole(errbuf); - WritetoConsole("\r\n"); - } - - return (TRUE); -} - -static size_t ExtProc(int fn, int port , PDATAMESSAGE buff) -{ - int txlen = 0; - PMSGWITHLEN buffptr; - struct TNCINFO * TNC = TNCInfo[port]; - struct STREAMINFO * STREAM; - int Stream; - - if (TNC == NULL) - return 0; - - if (fn < 4 || fn > 5) - if (TNC->hDevice == 0) - return 0; // Port not open - - STREAM = &TNC->Streams[0]; - - switch (fn) - { - case 1: // poll - - while (TNC->PortRecord->UI_Q) // Release anything accidentally put on UI_Q - { - buffptr = Q_REM(&TNC->PortRecord->UI_Q); - ReleaseBuffer(buffptr); - } - - //for (Stream = 0; Stream <= MaxStreams; Stream++) - { - if (STREAM->ReportDISC) - { - STREAM->ReportDISC = FALSE; - buff->PORT = 0; - - return -1; - } - } - - CheckRX(TNC); - HALPoll(port); - - //for (Stream = 0; Stream <= MaxStreams; Stream++) - { - if (STREAM->PACTORtoBPQ_Q !=0) - { - int datalen; - - buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q); - - datalen = (int)buffptr->Len; - - buff->PORT = 0; // Compatibility with Kam Driver - buff->PID = 0xf0; - memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte - datalen += sizeof(void *) + 4; - - PutLengthinBuffer(buff, datalen); - - - ReleaseBuffer(buffptr); - - return (1); - } - } - - return 0; - - case 2: // send - - buffptr = GetBuff(); - - if (buffptr == 0) return (0); // No buffers, so ignore - - // Find TNC Record - - Stream = buff->PORT; - - if (!TNC->TNCOK) - { - // Send Error Response - - PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); - - if (buffptr == 0) return (0); // No buffers, so ignore - - buffptr->Len = 27; - memcpy(&buffptr->Data[0], "No Connection to PACTOR TNC\r", 27); - - C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); - return 0; - } - - txlen = GetLengthfromBuffer(buff) - (sizeof(void *) + 4); - - buffptr->Len = txlen; - memcpy(&buffptr->Data[0], &buff->L2DATA[0], txlen); - - C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); - - STREAM->FramesQueued++; - - return (0); - - - case 3: // CHECK IF OK TO SEND. Also used to check if TNC is responding - - Stream = (int)(size_t)buff; - - if (STREAM->FramesQueued > 4) - return (1 | TNC->HostMode << 8); - - return TNC->HostMode << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting - - case 4: // reinit - - return (0); - - case 5: // Close - - CloseCOMPort(TNCInfo[port]->hDevice); - return (0); - - case 6: // Scan Control - - return 0; // None Yet - - } - return 0; - -} - -static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) -{ - int Len = sprintf(Buff, "" - "HAL Status

HAL Status

"); - - Len += sprintf(&Buff[Len], ""); - - Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); - Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); - Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); - Len += sprintf(&Buff[Len], "", TNC->WEB_STATE); - Len += sprintf(&Buff[Len], "", TNC->WEB_TXRX); - Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); - Len += sprintf(&Buff[Len], ""); - Len += sprintf(&Buff[Len], "", TNC->WEB_LEDS); - Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Status%s
TX/RX State%s
Traffic%s
LEDSSTBY CALL LINK ERROR TX RX
%s
"); - - Len = DoScanLine(TNC, Buff, Len); - - return Len; -} - - -VOID * HALExtInit(EXTPORTDATA * PortEntry) -{ - char msg[500]; - struct TNCINFO * TNC; - int port; - char * ptr; - int len; - char Msg[80]; -#ifndef LINBPQ - HWND x; -#endif - // - // Will be called once for each Pactor Port - // The COM port number is in IOBASE - // - - sprintf(msg,"HAL Driver %s", PortEntry->PORTCONTROL.SerialPortName); - WritetoConsole(msg); - - port=PortEntry->PORTCONTROL.PORTNUMBER; - - ReadConfigFile(port, ProcessLine); - TNC = TNCInfo[port]; - - if (TNC == NULL) - { - // Not defined in Config file - - sprintf(msg," ** Error - no info in BPQ32.cfg for this port"); - WritetoConsole(msg); - - return ExtProc; - } - - TNC->Port = port; - - TNC->Hardware = H_HAL; - - TNC->Interlock = PortEntry->PORTCONTROL.PORTINTERLOCK; - - PortEntry->MAXHOSTMODESESSIONS = 1; // Default - - TNC->PortRecord = PortEntry; - - if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) - { - memcpy(TNC->NodeCall, MYNODECALL, 10); - } - else - { - ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); - } - - PortEntry->PORTCONTROL.PROTOCOL = 10; - PortEntry->PORTCONTROL.PORTQUALITY = 0; - - if (PortEntry->PORTCONTROL.PORTPACLEN == 0) - PortEntry->PORTCONTROL.PORTPACLEN = 100; - - ptr=strchr(TNC->NodeCall, ' '); - if (ptr) *(ptr) = 0; // Null Terminate - - if (TNC->DefaultMode) - TNC->CurrentMode = TNC->DefaultMode; - else - TNC->CurrentMode = Clover; - - TNC->PollDelay = 999999999; - - // Set Disable +?, ExpandedStatus , Channel Stats Off, ClearOnDisc, EAS and MYCALL - - len = sprintf(Msg, "%c%c%c%c%c%c%s", 0xcc, 0x56, 0x41, ClearOnDisc, SetEAS, SetMYCALL, TNC->NodeCall); - len++; // We include the NULL - - memcpy(&TNC->InitScript[TNC->InitScriptLen], Msg, len); - TNC->InitScriptLen += len; - - PortEntry->PORTCONTROL.TNC = TNC; - - TNC->WebWindowProc = WebProc; - TNC->WebWinX = 510; - TNC->WebWinY = 280; - - TNC->WEB_COMMSSTATE = zalloc(100); - TNC->WEB_TNCSTATE = zalloc(100); - strcpy(TNC->WEB_TNCSTATE, "Free"); - TNC->WEB_MODE = zalloc(100); - TNC->WEB_TRAFFIC = zalloc(100); - TNC->WEB_BUFFERS = zalloc(100); - TNC->WEB_STATE = zalloc(100); - TNC->WEB_TXRX = zalloc(100); - TNC->WEB_LEDS = zalloc(100); - strcpy(TNC->WEB_LEDS, " X X X X X X"); - -#ifndef LINBPQ - - CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 233, ForcedClose); - - x = CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); - x = TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); - - x = CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); - x = TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); - - x = CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); - x = TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); - - x = CreateWindowEx(0, "STATIC", "Status", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); - x = TNC->xIDC_STATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); - - x = CreateWindowEx(0, "STATIC", "TX/RX State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); - x = TNC->xIDC_TXRX = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); - - x = CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); - x = TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "RX 0 TX 0 ACKED 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); - - x = CreateWindowEx(0, "STATIC", "LEDS", WS_CHILD | WS_VISIBLE,10,138,60,20, TNC->hDlg, NULL, hInstance, NULL); - SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0); - x = CreateWindowEx(0, "STATIC", "STBY CALL LINK ERROR TX RX", WS_CHILD | WS_VISIBLE,116,138,280,20, TNC->hDlg, NULL, hInstance, NULL); - SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0); - x = TNC->xIDC_LEDS = CreateWindowEx(0, "STATIC", " X X X X X X", WS_CHILD | WS_VISIBLE,116,158,280,20 , TNC->hDlg, NULL, hInstance, NULL); - SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0); - - TNC->ClientHeight = 233; - TNC->ClientWidth = 500; - - MoveWindows(TNC); -#endif - - OpenCOMMPort(TNC, PortEntry->PORTCONTROL.SerialPortName, PortEntry->PORTCONTROL.BAUDRATE, FALSE); - - SendCmd(TNC, "\x09" , 1); // Reset - - WritetoConsole("\n"); - - return ExtProc; -} - - - -static VOID KISSCLOSE(int Port) -{ - struct TNCINFO * conn = TNCInfo[Port]; - - // drop DTR and RTS - - COMClearDTR(conn->hDevice); - COMClearRTS(conn->hDevice); - - // purge any outstanding reads/writes and close device handle - - CloseCOMPort(conn->hDevice); - - return; -} - - -static void CheckRX(struct TNCINFO * TNC) -{ - int Length, Len; - UCHAR * Xptr; - - // only try to read number of bytes in queue - - if (TNC->RXLen == 500) - TNC->RXLen = 0; - - Len = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); - - if (Len == 0) - return; - - TNC->RXLen += Len; - - Length = TNC->RXLen; - - // We need to konw whether data is received or echoed, so we can't split commands and data here. - // Pass everything to the Command Handler. It will check that there are enough bytes for the command, - // and wait for more if not. - - // The USB version also uses 0x91 0x31 to eacape 0x11, 0x91 0x33 for 0x13 and 0x91 0xB1 for 0x91 - - // If USB version, we might get unescaped xon and xoff, which we must ignore - - if (TNC->XONXOFF) - { - Xptr = memchr(&TNC->RXBuffer, 0x11, Length); - - while(Xptr) - { - Debugprintf("XON Port %d", TNC->Port); - memmove(Xptr, Xptr + 1, Length-- - (TNC->RXBuffer - Xptr)); - Xptr = memchr(&TNC->RXBuffer, 0x11, Length); - } - - Xptr = memchr(&TNC->RXBuffer, 0x13, Length); - - while(Xptr) - { - Debugprintf("XOFF Port %d", TNC->Port); - memmove(Xptr, Xptr + 1, Length-- - (TNC->RXBuffer - Xptr)); - Xptr = memchr(&TNC->RXBuffer, 0x13, Length); - } - - Xptr = memchr(&TNC->RXBuffer, 0x91, Length); // See if packet contains 0x91 escape - - if (Xptr) - - // Make sure we have the escaped char as well - - if ((Xptr - &TNC->RXBuffer[0]) == Length - 1) // x91 is last char - return; - } - - ProcessHALBuffer(TNC, Length); - - TNC->RXLen = 0; - - return; - -} - - - -static BOOL WriteCommBlock(struct TNCINFO * TNC) -{ - WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); - return TRUE; -} - -VOID HALPoll(int Port) -{ - struct TNCINFO * TNC = TNCInfo[Port]; - struct STREAMINFO * STREAM = &TNC->Streams[0]; - - UCHAR * Poll = TNC->TXBuffer; - char Status[80]; - UCHAR TXMsg[1000]; - int datalen; - - if (TNC->Timeout) - { - TNC->Timeout--; - - if (TNC->Timeout) // Still waiting - return; - - // Timed Out - - TNC->TNCOK = FALSE; - TNC->HostMode = 0; - - sprintf(TNC->WEB_COMMSSTATE,"%s Open but TNC not responding", TNC->PortRecord->PORTCONTROL.SerialPortName); - MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); - - //for (Stream = 0; Stream <= MaxStreams; Stream++) - { - if (TNC->PortRecord->ATTACHEDSESSIONS[0]) // Connected - { - STREAM->Connected = FALSE; // Back to Command Mode - STREAM->ReportDISC = TRUE; // Tell Node - } - } - - } - - // if we have just restarted or TNC appears to be in terminal mode, run Initialisation Sequence - - if (TNC->TNCOK) - if (!TNC->HostMode) - { - DoTNCReinit(TNC); - return; - } - - if (TNC->PortRecord->ATTACHEDSESSIONS[0] && STREAM->Attached == 0) - { - // New Attach - - int calllen; - char Msg[80]; - - STREAM->Attached = TRUE; - - STREAM->BytesRXed = STREAM->BytesTXed = STREAM->BytesAcked = 0; - - calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, STREAM->MyCall); - STREAM->MyCall[calllen] = 0; - - datalen = sprintf(TXMsg, "%c%s", SetMYCALL, STREAM->MyCall); - SendCmd(TNC, TXMsg, datalen + 1); // Send the NULL - - sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); - MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - - // Stop Scanning - - sprintf(Msg, "%d SCANSTOP", TNC->Port); - - Rig_Command(-1, Msg); - - SendCmd(TNC, "\x42", 1); // Connect Enable off - - return; - - } - - //for (Stream = 0; Stream <= MaxStreams; Stream++) - - if (STREAM->Attached) - CheckForDetach(TNC, 0, STREAM, TidyClose, ForcedClose, CloseComplete); - - if (TNC->NeedPACTOR) - { - TNC->NeedPACTOR--; - - if (TNC->NeedPACTOR == 0) - { - int datalen; - - UCHAR TXMsg[80]; - - datalen = sprintf(TXMsg, "%c%s", SetMYCALL, TNC->NodeCall); - SendCmd(TNC, TXMsg, datalen + 1); // Send the NULL - - // Set Listen Mode - - switch (TNC->CurrentMode) - { - case Pactor: - - SendCmd(TNC, "\x84", 1); // FSK - SendCmd(TNC, "\x83", 1); // Select P-MODE Standby - SendCmd(TNC, "\x58", 1); // Listen - - break; - - case Clover: - - SendCmd(TNC, "\x80", 1); // Clover - SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format - SendCmd(TNC, "\x41", 1); // No Statistics - SendCmd(TNC, "\x60\x09", 2); // Robust Retries - SendCmd(TNC, "\x61\x09", 2); // Normal Retries - - break; - } - - SendCmd(TNC, "\x52", 1); // ConnectEnable - - // Restart Scanning - - sprintf(Status, "%d SCANSTART 15", TNC->Port); - - Rig_Command(-1, Status); - - return; - } - } - -#define MAXHALTX 256 - - //for (Stream = 0; Stream <= MaxStreams; Stream++) - { - if (TNC->TNCOK && STREAM->BPQtoPACTOR_Q && (STREAM->BytesTXed - STREAM->BytesAcked < 600)) - { - int datalen; - UINT * buffptr; - UCHAR * MsgPtr; - unsigned char TXMsg[500]; - - buffptr = (UINT * )STREAM->BPQtoPACTOR_Q; - datalen=buffptr[1]; - MsgPtr = (UCHAR *)&buffptr[2]; - - if (STREAM->Connected) - { - if (TNC->SwallowSignon) - { - TNC->SwallowSignon = FALSE; - if (strstr(MsgPtr, "Connected")) // Discard *** connected - { - ReleaseBuffer(buffptr); - STREAM->FramesQueued--; - return; - } - } - - // Must send data in small chunks - the Hal has limited buffer space - - // If in IRS force a turnround - - if (TNC->TXRXState == 'R' && TNC->CurrentMode != Clover) - { - if (TNC->TimeInRX++ > 15) - SendCmd(TNC, "\x87", 1); // Changeover to ISS - else - goto Poll; - } - - TNC->TimeInRX = 0; - - EncodeAndSend(TNC, MsgPtr, datalen); - buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q); - ReleaseBuffer(buffptr); - WriteLogLine(2, MsgPtr, datalen); - - STREAM->BytesTXed += datalen; - STREAM->FramesQueued--; - - ShowTraffic(TNC); - - return; - } - else - { - buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q); - STREAM->FramesQueued--; - - // Command. Do some sanity checking and look for things to process locally - - datalen--; // Exclude CR - MsgPtr[datalen] = 0; // Null Terminate - _strupr(MsgPtr); - - if (memcmp(MsgPtr, "RADIO ", 6) == 0) - { - sprintf(&MsgPtr[40], "%d %s", TNC->Port, &MsgPtr[6]); - if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, &MsgPtr[40])) - { - ReleaseBuffer(buffptr); - } - else - { - buffptr[1] = sprintf((UCHAR *)&buffptr[2], "%s", &MsgPtr[40]); - C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); - } - return; - } - - if (memcmp(MsgPtr, "MODE CLOVER", 11) == 0) - { - TNC->CurrentMode = Clover; - buffptr[1] = sprintf((UCHAR *)&buffptr[2],"HAL} Ok\r"); - C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); - - MySetWindowText(TNC->xIDC_MODE, "Clover"); - strcpy(TNC->WEB_MODE, "Clover"); - - SendCmd(TNC, "\x80", 1); // Clover - SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format - SendCmd(TNC, "\x41", 1); // No Statistics - - return; - } - - if (memcmp(MsgPtr, "MODE PACTOR", 11) == 0) - { - TNC->CurrentMode = Pactor; - buffptr[1] = sprintf((UCHAR *)&buffptr[2],"HAL} Ok\r"); - C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); - - SendCmd(TNC, "\x84", 1); // FSK - SendCmd(TNC, "\x83", 1); // Select P-MODE Standby - SendCmd(TNC, "\x48", 1); // Listen Off - - return; - } - if (memcmp(MsgPtr, "MODE AMTOR", 11) == 0) - { - TNC->CurrentMode = AMTOR; - buffptr[1] = sprintf((UCHAR *)&buffptr[2],"HAL} Ok\r"); - C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); - - return; - } - - if (MsgPtr[0] == 'C' && MsgPtr[1] == ' ' && datalen > 2) // Connect - { - memcpy(STREAM->RemoteCall, &MsgPtr[2], 9); - - switch (TNC->CurrentMode) - { - case Pactor: - - SendCmd(TNC, "\x84", 1); // FSK - SendCmd(TNC, "\x83", 1); // Select P-MODE Standby - - datalen = sprintf(TXMsg, "\x19%s", STREAM->RemoteCall); - - sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s - PACTOR", STREAM->MyCall, STREAM->RemoteCall); - - // DOnt set connecting till we get the 19 response so we can trap listen as a fail - break; - - case Clover: - - SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format - SendCmd(TNC, "\x57", 1); // Enable TX buffer clear on disconnect - - datalen = sprintf(TXMsg, "\x11%s", STREAM->RemoteCall); - - sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s - CLOVER", STREAM->MyCall, STREAM->RemoteCall); - - break; - } - - MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - SendCmd(TNC, TXMsg, datalen + 1); // Include NULL - - ReleaseBuffer(buffptr); - - return; - } - - if (memcmp(MsgPtr, "CLOVER ", 7) == 0) - { - memcpy(STREAM->RemoteCall, &MsgPtr[2], 9); - - SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format - SendCmd(TNC, "\x57", 1); // Enable TX buffer clear on disconnect - - datalen = sprintf(TXMsg, "\x11%s", STREAM->RemoteCall); - SendCmd(TNC, TXMsg, datalen + 1); // Include NULL - - sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s - CLOVER", - STREAM->MyCall, STREAM->RemoteCall); - MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - - ReleaseBuffer(buffptr); - - return; - } - - if (memcmp(MsgPtr, "DISCONNECT", datalen) == 0) // Disconnect - { - SendCmd(TNC, "\x07", 1); // Normal Disconnect - TNC->NeedPACTOR = 50; - - STREAM->Connecting = FALSE; - STREAM->ReportDISC = TRUE; - ReleaseBuffer(buffptr); - - return; - } - - // Other Command ?? Treat as HEX string - - datalen = sscanf(MsgPtr, "%X %X %X %X %X %X %X %X %X %X %X %X %X %X %X %X", - (UINT *)&TXMsg[0], (UINT *)&TXMsg[1], (UINT *)&TXMsg[2], (UINT *)&TXMsg[3], (UINT *)&TXMsg[4], - (UINT *)&TXMsg[5], (UINT *)&TXMsg[6], (UINT *)&TXMsg[7], (UINT *)&TXMsg[8], (UINT *)&TXMsg[9], - (UINT *)&TXMsg[10], (UINT *)&TXMsg[11], (UINT *)&TXMsg[12], (UINT *)&TXMsg[13], - (UINT *)&TXMsg[14], (UINT *)&TXMsg[15]); - -// SendCmd(TNC, TXMsg, datalen); - ReleaseBuffer(buffptr); - TNC->InternalCmd = 0; - } - } - } -Poll: - // Nothing doing - send Poll (but not too often) - - TNC->PollDelay++; - - if (TNC->PollDelay < 20) - return; - - TNC->PollDelay = 0; - - if (TNC->TNCOK) - SendCmd(TNC, "\x7d" , 1); // Use Get LEDS as Poll - else - SendCmd(TNC, "\x09" , 1); // Reset - - TNC->Timeout = 100; - - return; -} - -static VOID DoTNCReinit(struct TNCINFO * TNC) -{ - // TNC Has Restarted, send init commands (can probably send all at once) - -// TNC->TXBuffer[0] = 0x1b; -// TNC->TXLen = 1; - - WriteCommBlock(TNC); - - SendCmd(TNC, TNC->InitScript, TNC->InitScriptLen); - - TNC->HostMode = TRUE; // Should now be in Host Mode - TNC->NeedPACTOR = 20; // Need to set Calls and start scan - - TNC->DataMode = RXDATA; // Start with RX Data - - SendCmd(TNC, "\x7d" , 1); // Use Get LEDS as Poll -// SendCmd(TNC, "\xc9" , 1); // Huffman Off - SendCmd(TNC, "\x57", 1); // Enable TX buffer clear on disconnect - - SendCmd(TNC, "\x60\x06", 2); // Robust Mode Retries - -// SendCmd(TNC, "\x6f\x03" , 2); // Undocumented XON/XOFF On - used to see if old or new style modem - - TNC->Timeout = 50; - - return; - -} - -VOID ProcessHALData(struct TNCINFO * TNC) -{ - // Received Data just pass to Appl - - UINT * buffptr; - int Len = TNC->DataLen; - struct STREAMINFO * STREAM = &TNC->Streams[0]; - - TNC->DataLen = 0; - - if (TNC->DataMode == TXDATA) - { - STREAM->BytesAcked += Len; -// Debugprintf("Acked %d", Len); - - if (STREAM->BytesAcked > STREAM->BytesTXed) - Debugprintf("Too Much Acked"); - - if ((STREAM->BPQtoPACTOR_Q == 0) && STREAM->BytesAcked >= STREAM->BytesTXed) - { - // All sent - - if (STREAM->Disconnecting) - TidyClose(TNC, 0); - else - if (TNC->CurrentMode != Clover) - - // turn round link - - SendCmd(TNC, "\x0c" , 1); // Turnround - - } - } - else - { - if (TNC->DataMode == RXDATA) - { -// Debugprintf("RXed %d", Len); - buffptr = GetBuff(); - if (buffptr == NULL) - return; // No buffers, so ignore - - buffptr[1] = Len; // Length - - WriteLogLine(1, TNC->DataBuffer, Len); - - STREAM->BytesRXed += Len; - - memcpy(&buffptr[2], TNC->DataBuffer, Len); - - C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); - } - } - - ShowTraffic(TNC); - - return; -} - - - -VOID ProcessHALBuffer(struct TNCINFO * TNC, int Length) -{ - UCHAR Char; - UCHAR * inptr; - UCHAR * cmdptr; - UCHAR * dataptr; - BOOL CmdEsc, DataEsc; - - inptr = TNC->RXBuffer; - - cmdptr = &TNC->CmdBuffer[TNC->CmdLen]; - dataptr = &TNC->DataBuffer[TNC->DataLen]; - CmdEsc = TNC->CmdEsc; - DataEsc = TNC->DataEsc; - - // HAL uses HEX 80 as a command escape, 81 to ESCAPE 80 and 81 - - // The USB version also uses 0x91 0x31 to eacape 0x11, 0x91 0x33 for 0x13 and 0x91 0xB1 for 0x91 - - // Command Responses can be variable length - - // Command Handler will check for each command/response if it has enough - if not it will wait till more arrives - - while(Length--) - { - Char = *(inptr++); - - if (CmdEsc) - { - CmdEsc = FALSE; - - if (TNC->XONXOFF && Char == 0x91) - { - // XON/XOFF escape. We ensured above that data follows so we can process it inline - - Length--; - Char = *(inptr++) - 0x20; - } - *(cmdptr++) = Char; - } - else if (DataEsc) - { - DataEsc = FALSE; - goto DataChar; - } - else -NotData: - if (Char == 0x80) // Next Char is Command - CmdEsc = TRUE; - else if (Char == 0x81) // Next Char is escaped data (80 or 81) - DataEsc = TRUE; - else - { - // This is a Data Char. We must process any Commands received so far, so we know the type of data - - DataChar: - - TNC->CmdLen = (int)(cmdptr - TNC->CmdBuffer); - ProcessHALCmd(TNC); - cmdptr = &TNC->CmdBuffer[TNC->CmdLen]; - dataptr = &TNC->DataBuffer[TNC->DataLen]; - - *(dataptr++) = Char; // Normal Data - - // Now process any other data chars - - while(Length--) - { - Char = *(inptr++); - - if (TNC->XONXOFF && Char == 0x91) - { - // XON/XOFF escape within data. We ensured above that data follows so we - // can process it here - - Length--; - Char = *(inptr++) - 0x20; - } - - if (Char == 0x80 || Char == 0x81) - { - // Process any data we have, then loop back - - TNC->DataLen = (int)(dataptr - TNC->DataBuffer); - ProcessHALData(TNC); - - goto NotData; - } - *(dataptr++) = Char; // Normal Data - } - - // Used all data - - TNC->DataLen = (int)(dataptr - TNC->DataBuffer); - - ProcessHALData(TNC); - TNC->CmdEsc = CmdEsc; - TNC->DataEsc = DataEsc; - - return; - } - } - - // Save State - - TNC->CmdLen = (int)(cmdptr - TNC->CmdBuffer); - - TNC->CmdEsc = CmdEsc; - TNC->DataEsc = DataEsc; - - if (TNC->DataLen) - ProcessHALData(TNC); - - if (TNC->CmdLen) - ProcessHALCmd(TNC); -} - -VOID mySetWindowText(struct TNCINFO * TNC, char * Msg) -{ - MySetWindowText(TNC->xIDC_STATE, Msg); - strcpy(TNC->WEB_STATE, Msg); -} - -VOID ProcessHALCmd(struct TNCINFO * TNC) -{ - char * Call; - int Stream = 0; - int Opcode; - int StatusByte; - int Leds; - int Len; - int Used; - struct STREAMINFO * STREAM = &TNC->Streams[0]; - -CmdLoop: - - Opcode = TNC->CmdBuffer[0]; - Len = TNC->CmdLen; - - if (Len == 0) - return; - - TNC->TNCOK = TRUE; - TNC->Timeout = 0; - - sprintf(TNC->WEB_COMMSSTATE,"%s TNC link OK", TNC->PortRecord->PORTCONTROL.SerialPortName); - MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); - - // We may have more than one response in the buffer, and only each cmd/response decoder knows how many it needs - - switch(Opcode) - { - case 0x09: //Hardware Reset - equivalent to power on reset - - // Hardware has reset - need to reinitialise - - TNC->HostMode = 0; // Force Reinit - - Used = 1; - break; - - case 0x7a: // FSK Modes Status - - // Mixture of mode and state - eg listen huffman on/off irs/iss, so cant just display - - if (Len < 2) return; // Wait for more - - StatusByte = TNC->CmdBuffer[1]; - - switch (StatusByte) - { - case 0x06: // FSK TX (RTTY) - case 0x07: // FSK RX (RTTY) - case 0x10: // AMTOR STANDBY (LISTEN ON) - case 0x11: // AMTOR STANDBY (LISTEN OFF) - case 0x12: // AMTOR FEC TX (AMTOR) - case 0x13: // AMTOR FEC RX (AMTOR) - case 0x14: // P-MODE FEC TX (P-MODE) - case 0x15: // FREE SIGNAL TX (AMTOR) - case 0x16: // FREE SIGNAL TX TIMED OUT (AMTOR) - - // Diaplay Linke Status - - MySetWindowText(TNC->xIDC_MODE, status[StatusByte]); - strcpy(TNC->WEB_MODE, status[StatusByte]); - - break; - - case 0x0C: // P-MODE STANDBY (LISTEN ON) - case 0x0D: // P-MODE STANDBY (LISTEN OFF) - - // if we were connecting, this means connect failed. - - MySetWindowText(TNC->xIDC_MODE, status[StatusByte]); - strcpy(TNC->WEB_MODE, status[StatusByte]); - - if (STREAM->Connecting) - HALDisconnected(TNC); - - break; - - case 0x0E: // ISS (AMTOR/P-MODE) - - MySetWindowText(TNC->xIDC_TXRX,"ISS"); - strcpy(TNC->WEB_TXRX, "ISS"); - TNC->TXRXState = 'S'; - break; - - case 0x0F: // IRS (AMTOR/P-MODE) - - MySetWindowText(TNC->xIDC_TXRX,"IRS"); - strcpy(TNC->WEB_TXRX, "IRS"); - TNC->TXRXState = 'R'; - break; - - case 0x00: // IDLE (AMTOR/P-MODE) - case 0x01: // TFC (AMTOR/P-MODE) - case 0x02: // RQ (AMTOR/P-MODE) - case 0x03: // ERR (AMTOR/P-MODE) - case 0x04: // PHS (AMTOR/P-MODE) - case 0x05: // OVER (AMTOR/P-MODE) (not implemented) - - MySetWindowText(TNC->xIDC_STATE, status[StatusByte]); - strcpy(TNC->WEB_MODE, status[StatusByte]); - - - -//$807A $8008 P-MODE100 (P-MODE) -//$807A $8009 P-MODE200 (P-MODE) -//$807A $800A HUFFMAN ON (P-MODE) -//$807A $800B HUFFMAN OFF (P-MODE) - ; - } - Used = 2; - break; - - - case 0x7d: // Get LED Status - - // We use Get LED Status as a Poll - - if (Len < 2) return; // Wait for more - - Leds = TNC->CmdBuffer[1]; - sprintf(TNC->WEB_LEDS," %c %c %c %c %c %c ", - (Leds & 0x20)? 'X' : ' ', - (Leds & 0x10)? 'X' : ' ', - (Leds & 0x08)? 'X' : ' ', - (Leds & 0x04)? 'X' : ' ', - (Leds & 0x02)? 'X' : ' ', - (Leds & 0x01)? 'X' : ' '); - -// STBY CALL LINK ERROR TX RX - MySetWindowText(TNC->xIDC_LEDS, TNC->WEB_LEDS); - - Used = 2; - break; - - case 0x21: // Monitored FEC CCB - case 0x22: // Monitored ARQ CCB - - // As the reply is variable, make sure we have the terminating NULL - - if (memchr(TNC->CmdBuffer, 0, Len) == NULL) - return; // Wait for more - - Call = &TNC->CmdBuffer[1]; - Used = (int)strlen(Call) + 2; // Opcode and Null - - UpdateMH(TNC, Call, '!', 0); - - break; - - case 0x27: // Clover ARQ LINK REQUEST status message - - //indicates an incoming link request to either MYCALL ($8027 $8000), or MYALTCALL ($8027 $8001). - - if (Len < 2) return; // Wait for more - - // Don't need to do anything (but may eventally use ALTCALL as an APPLCALL - Used = 2; - break; - - case 0x2D: // FSK ARQ Link Request status message - - // $802D $8001 $8000 CLOVER Link Request (not implemented) - // $802D $8002 $8000 AMTOR CCIR-476 Link Request - // $802D $8003 $8000 AMTOR CCIR-625 Link Request - // $802D $8004 $8000 P-MODE Link Request - - if (Len < 3) return; // Wait for more - - // Don't need to do anything (but may save Session type later - - Used = 3; - break; - - - case 0x28: // Monitored Call - - // As the reply is variable, make sure we have the terminating NULL - - if (memchr(TNC->CmdBuffer, 0, Len) == NULL) - return; // Wait for more - - Call = &TNC->CmdBuffer[1]; - Used = strlen(Call) + 2; // Opcode and Null - - // Could possibly be used for APPLCALLS by changing MYCALL when we see a call to one of our calls - - break; - - - case 0x20: // Clover Linked with - Call Connected - case 0x29: // The Linked 476 message indicates the start of a CCIR 476 linked session. - case 0x2A: // The Linked 625 message indicates the start of a CCIR 625 linked session to . - case 0x2B: // P-MODE link to - - // As the reply is variable, make sure we have the terminating NULL - - if (memchr(TNC->CmdBuffer, 0, Len) == NULL) - return; // Wait for more - - Call = &TNC->CmdBuffer[1]; - Used = (int)strlen(Call) + 2; // Opcode and Null - - HALConnected(TNC, Call); - - break; - - case 0x23: // Normal Disconnected - followed by $8000 - case 0x24: // Link failed (any of the link errors) - case 0x25: // Signal Lost (LOS) - - if (Len < 2) return; // Wait for more - - HALDisconnected(TNC); - - Used = 2; - break; - - - // Stream Switch Reports - we will need to do something with these if Echo as Sent is set - // or we do something with the secondary port - - case 0x30: // Switch to Receive Data characters - case 0x31: // Switch to Transmit Data characters - case 0x32: // Switch to RX data from secondary port - - TNC->DataMode = Opcode; - Used = 1; - break; - - case 0x33: // Send TX data to modem - case 0x34: // Send TX data to secondary port - - TNC->TXMode = Opcode; - Used = 1; - break; - - case 0x70: // Channel Spectra Data - // $807F $80xx $8030 Invalid or unimplemented command code - if (Len < 9) return; // Wait for more - - Used = 9; - break; - - case 0x71: // SelCall On/Off - - if (Len < 2) return; // Wait for more - - Used = 2; - break; - - case 0x72: // Channel Spectra Data - // $807F $80xx $8030 Invalid or unimplemented command code - if (Len < 15) return; // Wait for more - - Used = 15; - break; - - case 0x73: // Clover Link state - - if (Len < 2) return; // Wait for more - - StatusByte = TNC->CmdBuffer[1]; - - switch (StatusByte) - { - case 0x00: mySetWindowText(TNC, "Channel idle"); break; - case 0x01: mySetWindowText(TNC, "Channel occupied with non-Clover signal"); break; - case 0x42: mySetWindowText(TNC, "Linked stations monitored"); break; - case 0x64: mySetWindowText(TNC, "Attempting normal link"); break; - case 0x65: mySetWindowText(TNC, "Attempting robust link"); break; - case 0x66: mySetWindowText(TNC, "Calling ARQ CQ"); break; - case 0x78: mySetWindowText(TNC, "Clover Control Block (CCB) send retry"); break; - case 0x79: mySetWindowText(TNC, "Clover Control Block (CCB) receive retry"); break; - case 0x7D: mySetWindowText(TNC, "Clover Control Block (CCB) received successfully"); break; - case 0x8A: mySetWindowText(TNC, "TX data block sent"); break; - case 0x8B: mySetWindowText(TNC, "RX data block received ok (precedes data block)"); break; - case 0x8C: mySetWindowText(TNC, "TX data block re-sent"); break; - case 0x8D: mySetWindowText(TNC, "RX data block decode failed (precedes data block)"); break; - case 0x8E: mySetWindowText(TNC, "TX idle"); break; - case 0x8F: mySetWindowText(TNC, "RX idle"); break; - case 0x9C: mySetWindowText(TNC, "Link failed: CCB send retries exceeded"); break; - case 0x9D: mySetWindowText(TNC, "Link failed: CCB receive retries exceeded"); break; - case 0x9E: mySetWindowText(TNC, "Link failed: protocol error"); break; - case 0xA0: mySetWindowText(TNC, "Receiving FEC SYNC sequence"); break; - } - - Used = 2; - break; - - case 0x75: // Clover waveform format - - if (Len < 5) return; // Wait for more - - Used = 5; - break; - - case 0x7F: // Error $80xx $80yy Error in command $80xx of type $80yy - // $807F $80xx $8030 Invalid or unimplemented command code - // $807F $80xx $8031 Invalid parameter value - // $807F $80xx $8032 Not allowed when connected - // $807F $80xx $8033 Not allowed when disconnected - // $807F $80xx $8034 Not valid in this mode - // $807F $80xx $8035 Not valid in this code - // $807F $8096 $8036 EEPROM write error - - if (Len < 3) return; // Wait for more - - if (TNC->CmdBuffer[1] == 0x6f && TNC->CmdBuffer[2] == 0x31) - { - // Reject of XON/XOFF enable - -// TNC->XONXOFF = FALSE; -// Debugprintf("BPQ32 HAL Port %d - Disabling XON/XOFF mode", TNC->Port); - } - else - Debugprintf("HAL Port %d Command Error Cmd %X Error %X", TNC->Port, TNC->CmdBuffer[1], TNC->CmdBuffer[2]); - - Used = 3; - break; - - // Following are all immediate commands - response is echo of command - - case 0x6f: // XON/XOFF on - -// TNC->XONXOFF = TRUE; // And drop through -// Debugprintf("BPQ32 HAL Port %d - Enabling XON/XOFF mode", TNC->Port); - - case 0x19: // Call P-MODE to - case 0x10: // Robust Link to using MYCALL - case 0x11: // Normal Link to using MYCALL - - STREAM->Connecting = TRUE; - - case 0x00: // P Load LOD file - case 0x01: // P Load S28 file - case 0x02: //Check Unit Error Status - case 0x03: //F Check System Clock - case 0x04: //C Close PTT and transmit Clover waveform - case 0x05: //Open PTT and stop transmit test - case 0x06: //Immediate Abort (Panic Kill) - case 0x07: //Normal disconnect (wait for ACK) - case 0x08: //Software reset - restore all program defaults - case 0x0A: //Send CW ID - case 0x0B: //Close PTT and transmit Single Tone - case 0x0C: //F Normal OVER (AMTOR,P-MODE) - case 0x0D: //F Force RTTY TX (Baudot/ASCII) - case 0x0E: //F Go to RTTY RX (Baudot/ASCII) - case 0x0F: //Go to LOD/S28 file loader - case SetMYCALL: // Set MYCALL Response - - case 0x1E: // Set MYALTCALL Response - - case 0x41: - case 0x42: - case 0x46: - case 0x47: - case 0x48: - case 0x4d: - case 0x52: // Enable adaptive Clover format - case 0x54: // Enable adaptive Clover format - - case 0x56: // Expanded Link State Reports OFF/ON - case 0x57: // Clear buffers on disc - case 0x58: - case 0x59: - case 0x60: // Robust Mode Retries - case 0x61: // Normal Mode Retries - case 0x80: //Switch to CLOVER mode - case 0x81: //Select AMTOR Standby - case 0x82: //Select AMTOR FEC - case 0x83: //Select P-MODE Standby - case 0x84: //Switch to FSK modes - case 0x85: //Select Baudot - case 0x86: //Select ASCII - case 0x87: //Forced OVER (AMTOR, P-MODE) - case 0x88: //Forced END (AMTOR, P-MODE) - case 0x89: //Force LTRS shift - case 0x8A: //Force FIGS shift - case 0x8B: //Send MARK tone - case 0x8C: //Send SPACE tone - case 0x8D: //Send MARK/SPACE tones - case 0x8E: //Received first character on line - case 0x8F: //Close PTT only (no tones) - - case 0xC9: //Huffman Off/On - case 0xCC: - case 0xD9: //Close PTT only (no tones) - - case SetTones: - - Used = 1; - break; - - case 0x91: // ???? - -// if (Len < 2) return; // Wait for more - - Used = 1; - break; - - default: - - // We didn't recognise command, so don't know how long it is - disaster! - - Debugprintf("HAL Port %d Unrecognised Command %x", TNC->Port, Opcode); - TNC->CmdLen = 0; - - return; - } - - if (Used == Len) - { - // All used - most likely case - - TNC->CmdLen = 0; - return; - } - - // Move Command Down buffer, and reenter - - TNC->CmdLen -= Used; - - memmove(TNC->CmdBuffer, &TNC->CmdBuffer[Used], TNC->CmdLen); - - goto CmdLoop; - - -} - - -VOID HALDisconnected(struct TNCINFO * TNC) -{ - struct STREAMINFO * STREAM = &TNC->Streams[0]; - - CloseLogfile(0); - CloseLogfile(1); - CloseLogfile(2); - - if ((STREAM->Connecting | STREAM->Connected) == 0) - { - // Not connected or Connecting. Probably response to going into Pactor Listen Mode - - return; - } - - if (STREAM->Connecting && STREAM->Disconnecting == FALSE) - { - UINT * buffptr; - - // Connect Failed - actually I think HAL uses another code for connect failed, but leave here for now - - buffptr = GetBuff(); - - if (buffptr) - { - buffptr[1] = sprintf((UCHAR *)&buffptr[2], "*** Failure with %s\r", STREAM->RemoteCall); - - C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); - } - - STREAM->Connecting = FALSE; - STREAM->Connected = FALSE; // In case! - STREAM->FramesQueued = 0; - - sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); - MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - - return; - } - - // Connected, or Disconnecting - Release Session - - STREAM->Connecting = FALSE; - STREAM->Connected = FALSE; // Back to Command Mode - STREAM->FramesQueued = 0; - - if (STREAM->Disconnecting == FALSE) - STREAM->ReportDISC = TRUE; // Tell Node - - STREAM->Disconnecting = FALSE; - - // Need to reset Pactor Call in case it was changed - - TNC->NeedPACTOR = 20; -} - -BOOL HALConnected(struct TNCINFO * TNC, char * Call) -{ - char Msg[80]; - UINT * buffptr; - struct STREAMINFO * STREAM = &TNC->Streams[0]; - char CallCopy[80]; - - strcpy(CallCopy, Call); - strcat(CallCopy, " "); // Some routines expect 10 char calls - - STREAM->BytesRXed = STREAM->BytesTXed = STREAM->BytesAcked = 0; - STREAM->ConnectTime = time(NULL); - - // Stop Scanner - - sprintf(Msg, "%d SCANSTOP", TNC->Port); - - Rig_Command(-1, Msg); - - ShowTraffic(TNC); - - TNC->DataMode = RXDATA; - - OpenLogfile(0); - OpenLogfile(1); - OpenLogfile(2); - - if (TNC->PortRecord->ATTACHEDSESSIONS[0] == 0) - { - // Incoming Connect - - ProcessIncommingConnect(TNC, CallCopy, 0, TRUE); - - sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", STREAM->RemoteCall, TNC->NodeCall); - MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - - if (TNC->CurrentMode != Clover) - SendCmd(TNC, "\x87", 1); // Changeover to ISS - - // If an autoconnect APPL is defined, send it - - if (TNC->ApplCmd) - { - buffptr = GetBuff(); - if (buffptr == 0) return TRUE; // No buffers, so ignore - - buffptr[1] = sprintf((UCHAR *)&buffptr[2], "%s\r", TNC->ApplCmd); - C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); - TNC->SwallowSignon = TRUE; - - return TRUE; - } - - if (FULL_CTEXT && HFCTEXTLEN == 0) - { - EncodeAndSend(TNC, CTEXTMSG, CTEXTLEN); - WriteLogLine(2, CTEXTMSG, CTEXTLEN); - - STREAM->BytesTXed += CTEXTLEN; - } - return TRUE; - } - - // Connect Complete - - buffptr = GetBuff(); - if (buffptr == 0) return TRUE; // No buffers, so ignore - - buffptr[1] = sprintf((UCHAR *)&buffptr[2], "*** Connected to %s\r", Call);; - - C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); - - STREAM->Connecting = FALSE; - STREAM->Connected = TRUE; // Subsequent data to data channel - - sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", TNC->NodeCall, STREAM->RemoteCall); - MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - - UpdateMH(TNC, CallCopy, '+', 'O'); - - - return TRUE; -} - - -static VOID EncodeAndSend(struct TNCINFO * TNC, UCHAR * txbuffer, int Len) -{ - // Send A Packet With DLE Encoding Encoding - - TNC->TXLen = DLEEncode(txbuffer, TNC->TXBuffer, Len); - - WriteCommBlock(TNC); -} - -VOID SendCmd(struct TNCINFO * TNC, UCHAR * txbuffer, int Len) -{ - // Send A Packet With Command Encoding (preceed each with 0x80 - - int i,txptr=0; - UCHAR * outbuff = TNC->TXBuffer; - - for (i=0; iTXLen = txptr; - WriteCommBlock(TNC); -} - -int DLEEncode(UCHAR * inbuff, UCHAR * outbuff, int len) -{ - int i, txptr = 0; - UCHAR c; - - // Escape x80 and x81 with x81 - -// outbuff[0] = 0x80; -// outbuff[1] = 0x33; // Send data to modem - - for (i=0;iNeedPACTOR = 30; -} - - - - +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// DLL to inteface HAL Communications Corp Clover/Pacor controllers to BPQ32 switch +// +// Uses BPQ EXTERNAL interface +// + +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_DEPRECATE + +#include "time.h" + +#include "CHeaders.h" +#include "tncinfo.h" + +#include "bpq32.h" + +#define HAL 1 + +#define SetMYCALL 0x13 +#define ConnectEnable 0x52 +#define ConnectDisable 0x42 +#define SetEAS 0x59 // Echo as Sent +#define SetTones 0xec +#define ClearOnDisc 0x57 + +static char ClassName[]="HALSTATUS"; + +static char WindowTitle[] = "HAL"; +static int RigControlRow = 185; + +struct TNCINFO * TNCInfo[34]; // Records are Malloc'd + +#define SOH 0x01 // CONTROL CODES +#define ETB 0x17 +#define DLE 0x10 + +//int MaxStreams = 0; + +#ifndef LINBPQ +extern HFONT hFont; +#endif + +static char status[23][50] = {"IDLE", "TFC", "RQ", "ERR", "PHS", "OVER", "FSK TX", + "FSK RX", "P-MODE100", "P-MODE200", "HUFMAN ON", "HUFMAN OFF", "P-MODE SBY(LISTEN ON)", + "P-MODE SBY(LISTEN OFF)", "ISS", "IRS", + "AMTOR SBY(LISTEN ON)", "AMTOR SBY(LISTEN OFF)", "AMTOR FEC TX", "AMTOR FEC RX", "P-MODE FEC TX", + "FREE SIGNAL TX (AMTOR)", "FREE SIGNAL TX TIMED OUT (AMTOR)"}; + +struct TNCINFO * CreateTTYInfo(int port, int speed); +BOOL OpenConnection(int); +BOOL SetupConnection(int); +static BOOL WriteCommBlock(struct TNCINFO * TNC); +static void CheckRX(struct TNCINFO * TNC); +VOID HALPoll(int Port); +VOID ProcessDEDFrame(struct TNCINFO * TNC, UCHAR * rxbuff, int len); +VOID ProcessTermModeResponse(struct TNCINFO * TNC); +static VOID DoTNCReinit(struct TNCINFO * TNC); +VOID DoTermModeTimeout(struct TNCINFO * TNC); +VOID ProcessHALBuffer(struct TNCINFO * TNC, int Length); +VOID ProcessHALCmd(struct TNCINFO * TNC); +VOID ProcessHALData(struct TNCINFO * TNC); +VOID ProcessKHOSTPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); +VOID ProcessKNormCommand(struct TNCINFO * TNC, UCHAR * rxbuffer); +VOID ProcessHostFrame(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); +VOID DoMonitor(struct TNCINFO * TNC, UCHAR * Msg, int Len); + +BOOL HALConnected(struct TNCINFO * TNC, char * Call); +VOID HALDisconnected(struct TNCINFO * TNC); + +static VOID EncodeAndSend(struct TNCINFO * TNC, UCHAR * txbuffer, int Len); +VOID SendCmd(struct TNCINFO * TNC, UCHAR * txbuffer, int Len); +int DLEEncode(UCHAR * inbuff, UCHAR * outbuff, int len); +int DLEDecode(UCHAR * inbuff, UCHAR * outbuff, int len); + +VOID COMClearDTR(HANDLE fd); +VOID COMClearRTS(HANDLE fd); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); + + + +//static HANDLE LogHandle[4] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; + +//char * Logs[4] = {"1", "2", "3", "4"}; + +//char BaseDir[]="c:"; + +static VOID CloseLogfile(int Flags) +{ +// CloseHandle(LogHandle[Flags]); +// LogHandle[Flags] = INVALID_HANDLE_VALUE; +} + +static VOID OpenLogfile(int Flags) +{ +/* +UCHAR FN[MAX_PATH]; + time_t T; + struct tm * tm; + + T = time(NULL); + tm = gmtime(&T); + + sprintf(FN,"%s\\HALLog_%02d%02d%02d_%s.bin", BaseDir, tm->tm_mday, tm->tm_hour, tm->tm_min, Logs[Flags]); + + LogHandle[Flags] = CreateFile(FN, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + SetFilePointer(LogHandle[Flags], 0, 0, FILE_END); + + return (LogHandle[Flags] != INVALID_HANDLE_VALUE); +*/ +} + +static void WriteLogLine(int Flags, char * Msg, int MsgLen) +{ +// int cnt; +// WriteFile(LogHandle[Flags] ,Msg , MsgLen, &cnt, NULL); +} + + + +int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + ptr = strtok(NULL, " \t\n\r"); + + if (_stricmp(buf, "APPL") == 0) // Using BPQ32 COnfig + { + BPQport = Port; + p_cmd = ptr; + } + else + if (_stricmp(buf, "PORT") != 0) // Using Old Config + { + // New config without a PORT or APPL - this is a Config Command + + strcpy(buf, errbuf); + strcat(buf, "\r"); + + BPQport = Port; + + TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + goto ConfigLine; + } + else + + { + + // Old Config from file + + BPQport=0; + BPQport = atoi(ptr); + + p_cmd = strtok(NULL, " \t\n\r"); + + if (Port && Port != BPQport) + { + // Want a particular port, and this isn't it + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + } + } + } + if(BPQport > 0 && BPQport < 33) + { + TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO)); + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_cmd != NULL) + { + if (p_cmd[0] != ';' && p_cmd[0] != '#') + TNC->ApplCmd=_strdup(p_cmd); + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; +ConfigLine: + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "WL2KREPORT", 10) == 0) + { + TNC->WL2K = DecodeWL2KReportLine(buf); + continue; + } + if (_memicmp(buf, "NEEDXONXOFF", 10) == 0) + { + TNC->XONXOFF = TRUE; + continue; + } + + if (_memicmp(buf, "TONES", 5) == 0) + { + int tone1 = 0, tone2 = 0; + + ptr = strtok(&buf[6], " ,/\t\n\r"); + if (ptr) + { + tone1 = atoi(ptr); + ptr = strtok(NULL, " ,/\t\n\r"); + if (ptr) + { + tone2 = atoi(ptr); + ptr = &TNC->InitScript[TNC->InitScriptLen]; + + // Try putting into FSK mode first + + *(ptr++) = 0x84; + *(ptr++) = SetTones; // Set Tones (Mark, Space HI byte first) + *(ptr++) = tone1 >> 8; + *(ptr++) = tone1 & 0xff; + *(ptr++) = tone2 >> 8; + *(ptr++) = tone2 & 0xff; + + TNC->InitScriptLen += 6; + + continue; + } + } + goto BadLine; + } + if (_memicmp(buf, "DEFAULTMODE ", 12) == 0) + { + + ptr = strtok(&buf[12], " ,\t\n\r"); + if (ptr) + { + if (_stricmp(ptr, "CLOVER") == 0) + TNC->DefaultMode = Clover; + else if (_stricmp(ptr, "PACTOR") == 0) + TNC->DefaultMode = Pactor; + else if (_stricmp(ptr, "AMTOR") == 0) + TNC->DefaultMode = AMTOR; + else goto BadLine; + + continue; + } + goto BadLine; + } + } + BadLine: + WritetoConsole(" Bad config record "); + WritetoConsole(errbuf); + WritetoConsole("\r\n"); + } + + return (TRUE); +} + +static size_t ExtProc(int fn, int port , PDATAMESSAGE buff) +{ + int txlen = 0; + PMSGWITHLEN buffptr; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM; + int Stream; + + if (TNC == NULL) + return 0; + + if (fn < 4 || fn > 5) + if (TNC->hDevice == 0) + return 0; // Port not open + + STREAM = &TNC->Streams[0]; + + switch (fn) + { + case 1: // poll + + while (TNC->PortRecord->UI_Q) // Release anything accidentally put on UI_Q + { + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (STREAM->ReportDISC) + { + STREAM->ReportDISC = FALSE; + buff->PORT = 0; + + return -1; + } + } + + CheckRX(TNC); + HALPoll(port); + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (STREAM->PACTORtoBPQ_Q !=0) + { + int datalen; + + buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = 0; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + + + ReleaseBuffer(buffptr); + + return (1); + } + } + + return 0; + + case 2: // send + + buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + // Find TNC Record + + Stream = buff->PORT; + + if (!TNC->TNCOK) + { + // Send Error Response + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = 27; + memcpy(&buffptr->Data[0], "No Connection to PACTOR TNC\r", 27); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (sizeof(void *) + 4); + + buffptr->Len = txlen; + memcpy(&buffptr->Data[0], &buff->L2DATA[0], txlen); + + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + + STREAM->FramesQueued++; + + return (0); + + + case 3: // CHECK IF OK TO SEND. Also used to check if TNC is responding + + Stream = (int)(size_t)buff; + + if (STREAM->FramesQueued > 4) + return (1 | TNC->HostMode << 8); + + return TNC->HostMode << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting + + case 4: // reinit + + return (0); + + case 5: // Close + + CloseCOMPort(TNCInfo[port]->hDevice); + return (0); + + case 6: // Scan Control + + return 0; // None Yet + + } + return 0; + +} + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "HAL Status

HAL Status

"); + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_STATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TXRX); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], "", TNC->WEB_LEDS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Status%s
TX/RX State%s
Traffic%s
LEDSSTBY CALL LINK ERROR TX RX
%s
"); + + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + + +VOID * HALExtInit(EXTPORTDATA * PortEntry) +{ + char msg[500]; + struct TNCINFO * TNC; + int port; + char * ptr; + int len; + char Msg[80]; +#ifndef LINBPQ + HWND x; +#endif + // + // Will be called once for each Pactor Port + // The COM port number is in IOBASE + // + + sprintf(msg,"HAL Driver %s", PortEntry->PORTCONTROL.SerialPortName); + WritetoConsole(msg); + + port=PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(msg," ** Error - no info in BPQ32.cfg for this port"); + WritetoConsole(msg); + + return ExtProc; + } + + TNC->Port = port; + + TNC->Hardware = H_HAL; + + TNC->Interlock = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->MAXHOSTMODESESSIONS = 1; // Default + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + { + memcpy(TNC->NodeCall, MYNODECALL, 10); + } + else + { + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + } + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 100; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + if (TNC->DefaultMode) + TNC->CurrentMode = TNC->DefaultMode; + else + TNC->CurrentMode = Clover; + + TNC->PollDelay = 999999999; + + // Set Disable +?, ExpandedStatus , Channel Stats Off, ClearOnDisc, EAS and MYCALL + + len = sprintf(Msg, "%c%c%c%c%c%c%s", 0xcc, 0x56, 0x41, ClearOnDisc, SetEAS, SetMYCALL, TNC->NodeCall); + len++; // We include the NULL + + memcpy(&TNC->InitScript[TNC->InitScriptLen], Msg, len); + TNC->InitScriptLen += len; + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 510; + TNC->WebWinY = 280; + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + strcpy(TNC->WEB_TNCSTATE, "Free"); + TNC->WEB_MODE = zalloc(100); + TNC->WEB_TRAFFIC = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_STATE = zalloc(100); + TNC->WEB_TXRX = zalloc(100); + TNC->WEB_LEDS = zalloc(100); + strcpy(TNC->WEB_LEDS, " X X X X X X"); + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 233, ForcedClose); + + x = CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "Status", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_STATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "TX/RX State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_TXRX = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "RX 0 TX 0 ACKED 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "LEDS", WS_CHILD | WS_VISIBLE,10,138,60,20, TNC->hDlg, NULL, hInstance, NULL); + SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0); + x = CreateWindowEx(0, "STATIC", "STBY CALL LINK ERROR TX RX", WS_CHILD | WS_VISIBLE,116,138,280,20, TNC->hDlg, NULL, hInstance, NULL); + SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0); + x = TNC->xIDC_LEDS = CreateWindowEx(0, "STATIC", " X X X X X X", WS_CHILD | WS_VISIBLE,116,158,280,20 , TNC->hDlg, NULL, hInstance, NULL); + SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0); + + TNC->ClientHeight = 233; + TNC->ClientWidth = 500; + + MoveWindows(TNC); +#endif + + OpenCOMMPort(TNC, PortEntry->PORTCONTROL.SerialPortName, PortEntry->PORTCONTROL.BAUDRATE, FALSE); + + SendCmd(TNC, "\x09" , 1); // Reset + + WritetoConsole("\n"); + + return ExtProc; +} + + + +static VOID KISSCLOSE(int Port) +{ + struct TNCINFO * conn = TNCInfo[Port]; + + // drop DTR and RTS + + COMClearDTR(conn->hDevice); + COMClearRTS(conn->hDevice); + + // purge any outstanding reads/writes and close device handle + + CloseCOMPort(conn->hDevice); + + return; +} + + +static void CheckRX(struct TNCINFO * TNC) +{ + int Length, Len; + UCHAR * Xptr; + + // only try to read number of bytes in queue + + if (TNC->RXLen == 500) + TNC->RXLen = 0; + + Len = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); + + if (Len == 0) + return; + + TNC->RXLen += Len; + + Length = TNC->RXLen; + + // We need to konw whether data is received or echoed, so we can't split commands and data here. + // Pass everything to the Command Handler. It will check that there are enough bytes for the command, + // and wait for more if not. + + // The USB version also uses 0x91 0x31 to eacape 0x11, 0x91 0x33 for 0x13 and 0x91 0xB1 for 0x91 + + // If USB version, we might get unescaped xon and xoff, which we must ignore + + if (TNC->XONXOFF) + { + Xptr = memchr(&TNC->RXBuffer, 0x11, Length); + + while(Xptr) + { + Debugprintf("XON Port %d", TNC->Port); + memmove(Xptr, Xptr + 1, Length-- - (TNC->RXBuffer - Xptr)); + Xptr = memchr(&TNC->RXBuffer, 0x11, Length); + } + + Xptr = memchr(&TNC->RXBuffer, 0x13, Length); + + while(Xptr) + { + Debugprintf("XOFF Port %d", TNC->Port); + memmove(Xptr, Xptr + 1, Length-- - (TNC->RXBuffer - Xptr)); + Xptr = memchr(&TNC->RXBuffer, 0x13, Length); + } + + Xptr = memchr(&TNC->RXBuffer, 0x91, Length); // See if packet contains 0x91 escape + + if (Xptr) + + // Make sure we have the escaped char as well + + if ((Xptr - &TNC->RXBuffer[0]) == Length - 1) // x91 is last char + return; + } + + ProcessHALBuffer(TNC, Length); + + TNC->RXLen = 0; + + return; + +} + + + +static BOOL WriteCommBlock(struct TNCINFO * TNC) +{ + WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); + return TRUE; +} + +VOID HALPoll(int Port) +{ + struct TNCINFO * TNC = TNCInfo[Port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + UCHAR * Poll = TNC->TXBuffer; + char Status[80]; + UCHAR TXMsg[1000]; + int datalen; + + if (TNC->Timeout) + { + TNC->Timeout--; + + if (TNC->Timeout) // Still waiting + return; + + // Timed Out + + TNC->TNCOK = FALSE; + TNC->HostMode = 0; + + sprintf(TNC->WEB_COMMSSTATE,"%s Open but TNC not responding", TNC->PortRecord->PORTCONTROL.SerialPortName); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[0]) // Connected + { + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + } + } + + } + + // if we have just restarted or TNC appears to be in terminal mode, run Initialisation Sequence + + if (TNC->TNCOK) + if (!TNC->HostMode) + { + DoTNCReinit(TNC); + return; + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] && STREAM->Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + STREAM->Attached = TRUE; + + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->BytesAcked = 0; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, STREAM->MyCall); + STREAM->MyCall[calllen] = 0; + + datalen = sprintf(TXMsg, "%c%s", SetMYCALL, STREAM->MyCall); + SendCmd(TNC, TXMsg, datalen + 1); // Send the NULL + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + + SendCmd(TNC, "\x42", 1); // Connect Enable off + + return; + + } + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + + if (STREAM->Attached) + CheckForDetach(TNC, 0, STREAM, TidyClose, ForcedClose, CloseComplete); + + if (TNC->NeedPACTOR) + { + TNC->NeedPACTOR--; + + if (TNC->NeedPACTOR == 0) + { + int datalen; + + UCHAR TXMsg[80]; + + datalen = sprintf(TXMsg, "%c%s", SetMYCALL, TNC->NodeCall); + SendCmd(TNC, TXMsg, datalen + 1); // Send the NULL + + // Set Listen Mode + + switch (TNC->CurrentMode) + { + case Pactor: + + SendCmd(TNC, "\x84", 1); // FSK + SendCmd(TNC, "\x83", 1); // Select P-MODE Standby + SendCmd(TNC, "\x58", 1); // Listen + + break; + + case Clover: + + SendCmd(TNC, "\x80", 1); // Clover + SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format + SendCmd(TNC, "\x41", 1); // No Statistics + SendCmd(TNC, "\x60\x09", 2); // Robust Retries + SendCmd(TNC, "\x61\x09", 2); // Normal Retries + + break; + } + + SendCmd(TNC, "\x52", 1); // ConnectEnable + + // Restart Scanning + + sprintf(Status, "%d SCANSTART 15", TNC->Port); + + Rig_Command(-1, Status); + + return; + } + } + +#define MAXHALTX 256 + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->TNCOK && STREAM->BPQtoPACTOR_Q && (STREAM->BytesTXed - STREAM->BytesAcked < 600)) + { + int datalen; + UINT * buffptr; + UCHAR * MsgPtr; + unsigned char TXMsg[500]; + + buffptr = (UINT * )STREAM->BPQtoPACTOR_Q; + datalen=buffptr[1]; + MsgPtr = (UCHAR *)&buffptr[2]; + + if (STREAM->Connected) + { + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; + if (strstr(MsgPtr, "Connected")) // Discard *** connected + { + ReleaseBuffer(buffptr); + STREAM->FramesQueued--; + return; + } + } + + // Must send data in small chunks - the Hal has limited buffer space + + // If in IRS force a turnround + + if (TNC->TXRXState == 'R' && TNC->CurrentMode != Clover) + { + if (TNC->TimeInRX++ > 15) + SendCmd(TNC, "\x87", 1); // Changeover to ISS + else + goto Poll; + } + + TNC->TimeInRX = 0; + + EncodeAndSend(TNC, MsgPtr, datalen); + buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q); + ReleaseBuffer(buffptr); + WriteLogLine(2, MsgPtr, datalen); + + STREAM->BytesTXed += datalen; + STREAM->FramesQueued--; + + ShowTraffic(TNC); + + return; + } + else + { + buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q); + STREAM->FramesQueued--; + + // Command. Do some sanity checking and look for things to process locally + + datalen--; // Exclude CR + MsgPtr[datalen] = 0; // Null Terminate + _strupr(MsgPtr); + + if (memcmp(MsgPtr, "RADIO ", 6) == 0) + { + sprintf(&MsgPtr[40], "%d %s", TNC->Port, &MsgPtr[6]); + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, &MsgPtr[40])) + { + ReleaseBuffer(buffptr); + } + else + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "%s", &MsgPtr[40]); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + return; + } + + if (memcmp(MsgPtr, "MODE CLOVER", 11) == 0) + { + TNC->CurrentMode = Clover; + buffptr[1] = sprintf((UCHAR *)&buffptr[2],"HAL} Ok\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + MySetWindowText(TNC->xIDC_MODE, "Clover"); + strcpy(TNC->WEB_MODE, "Clover"); + + SendCmd(TNC, "\x80", 1); // Clover + SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format + SendCmd(TNC, "\x41", 1); // No Statistics + + return; + } + + if (memcmp(MsgPtr, "MODE PACTOR", 11) == 0) + { + TNC->CurrentMode = Pactor; + buffptr[1] = sprintf((UCHAR *)&buffptr[2],"HAL} Ok\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + SendCmd(TNC, "\x84", 1); // FSK + SendCmd(TNC, "\x83", 1); // Select P-MODE Standby + SendCmd(TNC, "\x48", 1); // Listen Off + + return; + } + if (memcmp(MsgPtr, "MODE AMTOR", 11) == 0) + { + TNC->CurrentMode = AMTOR; + buffptr[1] = sprintf((UCHAR *)&buffptr[2],"HAL} Ok\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + return; + } + + if (MsgPtr[0] == 'C' && MsgPtr[1] == ' ' && datalen > 2) // Connect + { + memcpy(STREAM->RemoteCall, &MsgPtr[2], 9); + + switch (TNC->CurrentMode) + { + case Pactor: + + SendCmd(TNC, "\x84", 1); // FSK + SendCmd(TNC, "\x83", 1); // Select P-MODE Standby + + datalen = sprintf(TXMsg, "\x19%s", STREAM->RemoteCall); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s - PACTOR", STREAM->MyCall, STREAM->RemoteCall); + + // DOnt set connecting till we get the 19 response so we can trap listen as a fail + break; + + case Clover: + + SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format + SendCmd(TNC, "\x57", 1); // Enable TX buffer clear on disconnect + + datalen = sprintf(TXMsg, "\x11%s", STREAM->RemoteCall); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s - CLOVER", STREAM->MyCall, STREAM->RemoteCall); + + break; + } + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + SendCmd(TNC, TXMsg, datalen + 1); // Include NULL + + ReleaseBuffer(buffptr); + + return; + } + + if (memcmp(MsgPtr, "CLOVER ", 7) == 0) + { + memcpy(STREAM->RemoteCall, &MsgPtr[2], 9); + + SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format + SendCmd(TNC, "\x57", 1); // Enable TX buffer clear on disconnect + + datalen = sprintf(TXMsg, "\x11%s", STREAM->RemoteCall); + SendCmd(TNC, TXMsg, datalen + 1); // Include NULL + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s - CLOVER", + STREAM->MyCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + ReleaseBuffer(buffptr); + + return; + } + + if (memcmp(MsgPtr, "DISCONNECT", datalen) == 0) // Disconnect + { + SendCmd(TNC, "\x07", 1); // Normal Disconnect + TNC->NeedPACTOR = 50; + + STREAM->Connecting = FALSE; + STREAM->ReportDISC = TRUE; + ReleaseBuffer(buffptr); + + return; + } + + // Other Command ?? Treat as HEX string + + datalen = sscanf(MsgPtr, "%X %X %X %X %X %X %X %X %X %X %X %X %X %X %X %X", + (UINT *)&TXMsg[0], (UINT *)&TXMsg[1], (UINT *)&TXMsg[2], (UINT *)&TXMsg[3], (UINT *)&TXMsg[4], + (UINT *)&TXMsg[5], (UINT *)&TXMsg[6], (UINT *)&TXMsg[7], (UINT *)&TXMsg[8], (UINT *)&TXMsg[9], + (UINT *)&TXMsg[10], (UINT *)&TXMsg[11], (UINT *)&TXMsg[12], (UINT *)&TXMsg[13], + (UINT *)&TXMsg[14], (UINT *)&TXMsg[15]); + +// SendCmd(TNC, TXMsg, datalen); + ReleaseBuffer(buffptr); + TNC->InternalCmd = 0; + } + } + } +Poll: + // Nothing doing - send Poll (but not too often) + + TNC->PollDelay++; + + if (TNC->PollDelay < 20) + return; + + TNC->PollDelay = 0; + + if (TNC->TNCOK) + SendCmd(TNC, "\x7d" , 1); // Use Get LEDS as Poll + else + SendCmd(TNC, "\x09" , 1); // Reset + + TNC->Timeout = 100; + + return; +} + +static VOID DoTNCReinit(struct TNCINFO * TNC) +{ + // TNC Has Restarted, send init commands (can probably send all at once) + +// TNC->TXBuffer[0] = 0x1b; +// TNC->TXLen = 1; + + WriteCommBlock(TNC); + + SendCmd(TNC, TNC->InitScript, TNC->InitScriptLen); + + TNC->HostMode = TRUE; // Should now be in Host Mode + TNC->NeedPACTOR = 20; // Need to set Calls and start scan + + TNC->DataMode = RXDATA; // Start with RX Data + + SendCmd(TNC, "\x7d" , 1); // Use Get LEDS as Poll +// SendCmd(TNC, "\xc9" , 1); // Huffman Off + SendCmd(TNC, "\x57", 1); // Enable TX buffer clear on disconnect + + SendCmd(TNC, "\x60\x06", 2); // Robust Mode Retries + +// SendCmd(TNC, "\x6f\x03" , 2); // Undocumented XON/XOFF On - used to see if old or new style modem + + TNC->Timeout = 50; + + return; + +} + +VOID ProcessHALData(struct TNCINFO * TNC) +{ + // Received Data just pass to Appl + + UINT * buffptr; + int Len = TNC->DataLen; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + TNC->DataLen = 0; + + if (TNC->DataMode == TXDATA) + { + STREAM->BytesAcked += Len; +// Debugprintf("Acked %d", Len); + + if (STREAM->BytesAcked > STREAM->BytesTXed) + Debugprintf("Too Much Acked"); + + if ((STREAM->BPQtoPACTOR_Q == 0) && STREAM->BytesAcked >= STREAM->BytesTXed) + { + // All sent + + if (STREAM->Disconnecting) + TidyClose(TNC, 0); + else + if (TNC->CurrentMode != Clover) + + // turn round link + + SendCmd(TNC, "\x0c" , 1); // Turnround + + } + } + else + { + if (TNC->DataMode == RXDATA) + { +// Debugprintf("RXed %d", Len); + buffptr = GetBuff(); + if (buffptr == NULL) + return; // No buffers, so ignore + + buffptr[1] = Len; // Length + + WriteLogLine(1, TNC->DataBuffer, Len); + + STREAM->BytesRXed += Len; + + memcpy(&buffptr[2], TNC->DataBuffer, Len); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + } + + ShowTraffic(TNC); + + return; +} + + + +VOID ProcessHALBuffer(struct TNCINFO * TNC, int Length) +{ + UCHAR Char; + UCHAR * inptr; + UCHAR * cmdptr; + UCHAR * dataptr; + BOOL CmdEsc, DataEsc; + + inptr = TNC->RXBuffer; + + cmdptr = &TNC->CmdBuffer[TNC->CmdLen]; + dataptr = &TNC->DataBuffer[TNC->DataLen]; + CmdEsc = TNC->CmdEsc; + DataEsc = TNC->DataEsc; + + // HAL uses HEX 80 as a command escape, 81 to ESCAPE 80 and 81 + + // The USB version also uses 0x91 0x31 to eacape 0x11, 0x91 0x33 for 0x13 and 0x91 0xB1 for 0x91 + + // Command Responses can be variable length + + // Command Handler will check for each command/response if it has enough - if not it will wait till more arrives + + while(Length--) + { + Char = *(inptr++); + + if (CmdEsc) + { + CmdEsc = FALSE; + + if (TNC->XONXOFF && Char == 0x91) + { + // XON/XOFF escape. We ensured above that data follows so we can process it inline + + Length--; + Char = *(inptr++) - 0x20; + } + *(cmdptr++) = Char; + } + else if (DataEsc) + { + DataEsc = FALSE; + goto DataChar; + } + else +NotData: + if (Char == 0x80) // Next Char is Command + CmdEsc = TRUE; + else if (Char == 0x81) // Next Char is escaped data (80 or 81) + DataEsc = TRUE; + else + { + // This is a Data Char. We must process any Commands received so far, so we know the type of data + + DataChar: + + TNC->CmdLen = (int)(cmdptr - TNC->CmdBuffer); + ProcessHALCmd(TNC); + cmdptr = &TNC->CmdBuffer[TNC->CmdLen]; + dataptr = &TNC->DataBuffer[TNC->DataLen]; + + *(dataptr++) = Char; // Normal Data + + // Now process any other data chars + + while(Length--) + { + Char = *(inptr++); + + if (TNC->XONXOFF && Char == 0x91) + { + // XON/XOFF escape within data. We ensured above that data follows so we + // can process it here + + Length--; + Char = *(inptr++) - 0x20; + } + + if (Char == 0x80 || Char == 0x81) + { + // Process any data we have, then loop back + + TNC->DataLen = (int)(dataptr - TNC->DataBuffer); + ProcessHALData(TNC); + + goto NotData; + } + *(dataptr++) = Char; // Normal Data + } + + // Used all data + + TNC->DataLen = (int)(dataptr - TNC->DataBuffer); + + ProcessHALData(TNC); + TNC->CmdEsc = CmdEsc; + TNC->DataEsc = DataEsc; + + return; + } + } + + // Save State + + TNC->CmdLen = (int)(cmdptr - TNC->CmdBuffer); + + TNC->CmdEsc = CmdEsc; + TNC->DataEsc = DataEsc; + + if (TNC->DataLen) + ProcessHALData(TNC); + + if (TNC->CmdLen) + ProcessHALCmd(TNC); +} + +VOID mySetWindowText(struct TNCINFO * TNC, char * Msg) +{ + MySetWindowText(TNC->xIDC_STATE, Msg); + strcpy(TNC->WEB_STATE, Msg); +} + +VOID ProcessHALCmd(struct TNCINFO * TNC) +{ + char * Call; + int Stream = 0; + int Opcode; + int StatusByte; + int Leds; + int Len; + int Used; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + +CmdLoop: + + Opcode = TNC->CmdBuffer[0]; + Len = TNC->CmdLen; + + if (Len == 0) + return; + + TNC->TNCOK = TRUE; + TNC->Timeout = 0; + + sprintf(TNC->WEB_COMMSSTATE,"%s TNC link OK", TNC->PortRecord->PORTCONTROL.SerialPortName); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + // We may have more than one response in the buffer, and only each cmd/response decoder knows how many it needs + + switch(Opcode) + { + case 0x09: //Hardware Reset - equivalent to power on reset + + // Hardware has reset - need to reinitialise + + TNC->HostMode = 0; // Force Reinit + + Used = 1; + break; + + case 0x7a: // FSK Modes Status + + // Mixture of mode and state - eg listen huffman on/off irs/iss, so cant just display + + if (Len < 2) return; // Wait for more + + StatusByte = TNC->CmdBuffer[1]; + + switch (StatusByte) + { + case 0x06: // FSK TX (RTTY) + case 0x07: // FSK RX (RTTY) + case 0x10: // AMTOR STANDBY (LISTEN ON) + case 0x11: // AMTOR STANDBY (LISTEN OFF) + case 0x12: // AMTOR FEC TX (AMTOR) + case 0x13: // AMTOR FEC RX (AMTOR) + case 0x14: // P-MODE FEC TX (P-MODE) + case 0x15: // FREE SIGNAL TX (AMTOR) + case 0x16: // FREE SIGNAL TX TIMED OUT (AMTOR) + + // Diaplay Linke Status + + MySetWindowText(TNC->xIDC_MODE, status[StatusByte]); + strcpy(TNC->WEB_MODE, status[StatusByte]); + + break; + + case 0x0C: // P-MODE STANDBY (LISTEN ON) + case 0x0D: // P-MODE STANDBY (LISTEN OFF) + + // if we were connecting, this means connect failed. + + MySetWindowText(TNC->xIDC_MODE, status[StatusByte]); + strcpy(TNC->WEB_MODE, status[StatusByte]); + + if (STREAM->Connecting) + HALDisconnected(TNC); + + break; + + case 0x0E: // ISS (AMTOR/P-MODE) + + MySetWindowText(TNC->xIDC_TXRX,"ISS"); + strcpy(TNC->WEB_TXRX, "ISS"); + TNC->TXRXState = 'S'; + break; + + case 0x0F: // IRS (AMTOR/P-MODE) + + MySetWindowText(TNC->xIDC_TXRX,"IRS"); + strcpy(TNC->WEB_TXRX, "IRS"); + TNC->TXRXState = 'R'; + break; + + case 0x00: // IDLE (AMTOR/P-MODE) + case 0x01: // TFC (AMTOR/P-MODE) + case 0x02: // RQ (AMTOR/P-MODE) + case 0x03: // ERR (AMTOR/P-MODE) + case 0x04: // PHS (AMTOR/P-MODE) + case 0x05: // OVER (AMTOR/P-MODE) (not implemented) + + MySetWindowText(TNC->xIDC_STATE, status[StatusByte]); + strcpy(TNC->WEB_MODE, status[StatusByte]); + + + +//$807A $8008 P-MODE100 (P-MODE) +//$807A $8009 P-MODE200 (P-MODE) +//$807A $800A HUFFMAN ON (P-MODE) +//$807A $800B HUFFMAN OFF (P-MODE) + ; + } + Used = 2; + break; + + + case 0x7d: // Get LED Status + + // We use Get LED Status as a Poll + + if (Len < 2) return; // Wait for more + + Leds = TNC->CmdBuffer[1]; + sprintf(TNC->WEB_LEDS," %c %c %c %c %c %c ", + (Leds & 0x20)? 'X' : ' ', + (Leds & 0x10)? 'X' : ' ', + (Leds & 0x08)? 'X' : ' ', + (Leds & 0x04)? 'X' : ' ', + (Leds & 0x02)? 'X' : ' ', + (Leds & 0x01)? 'X' : ' '); + +// STBY CALL LINK ERROR TX RX + MySetWindowText(TNC->xIDC_LEDS, TNC->WEB_LEDS); + + Used = 2; + break; + + case 0x21: // Monitored FEC CCB + case 0x22: // Monitored ARQ CCB + + // As the reply is variable, make sure we have the terminating NULL + + if (memchr(TNC->CmdBuffer, 0, Len) == NULL) + return; // Wait for more + + Call = &TNC->CmdBuffer[1]; + Used = (int)strlen(Call) + 2; // Opcode and Null + + UpdateMH(TNC, Call, '!', 0); + + break; + + case 0x27: // Clover ARQ LINK REQUEST status message + + //indicates an incoming link request to either MYCALL ($8027 $8000), or MYALTCALL ($8027 $8001). + + if (Len < 2) return; // Wait for more + + // Don't need to do anything (but may eventally use ALTCALL as an APPLCALL + Used = 2; + break; + + case 0x2D: // FSK ARQ Link Request status message + + // $802D $8001 $8000 CLOVER Link Request (not implemented) + // $802D $8002 $8000 AMTOR CCIR-476 Link Request + // $802D $8003 $8000 AMTOR CCIR-625 Link Request + // $802D $8004 $8000 P-MODE Link Request + + if (Len < 3) return; // Wait for more + + // Don't need to do anything (but may save Session type later + + Used = 3; + break; + + + case 0x28: // Monitored Call + + // As the reply is variable, make sure we have the terminating NULL + + if (memchr(TNC->CmdBuffer, 0, Len) == NULL) + return; // Wait for more + + Call = &TNC->CmdBuffer[1]; + Used = strlen(Call) + 2; // Opcode and Null + + // Could possibly be used for APPLCALLS by changing MYCALL when we see a call to one of our calls + + break; + + + case 0x20: // Clover Linked with - Call Connected + case 0x29: // The Linked 476 message indicates the start of a CCIR 476 linked session. + case 0x2A: // The Linked 625 message indicates the start of a CCIR 625 linked session to . + case 0x2B: // P-MODE link to + + // As the reply is variable, make sure we have the terminating NULL + + if (memchr(TNC->CmdBuffer, 0, Len) == NULL) + return; // Wait for more + + Call = &TNC->CmdBuffer[1]; + Used = (int)strlen(Call) + 2; // Opcode and Null + + HALConnected(TNC, Call); + + break; + + case 0x23: // Normal Disconnected - followed by $8000 + case 0x24: // Link failed (any of the link errors) + case 0x25: // Signal Lost (LOS) + + if (Len < 2) return; // Wait for more + + HALDisconnected(TNC); + + Used = 2; + break; + + + // Stream Switch Reports - we will need to do something with these if Echo as Sent is set + // or we do something with the secondary port + + case 0x30: // Switch to Receive Data characters + case 0x31: // Switch to Transmit Data characters + case 0x32: // Switch to RX data from secondary port + + TNC->DataMode = Opcode; + Used = 1; + break; + + case 0x33: // Send TX data to modem + case 0x34: // Send TX data to secondary port + + TNC->TXMode = Opcode; + Used = 1; + break; + + case 0x70: // Channel Spectra Data + // $807F $80xx $8030 Invalid or unimplemented command code + if (Len < 9) return; // Wait for more + + Used = 9; + break; + + case 0x71: // SelCall On/Off + + if (Len < 2) return; // Wait for more + + Used = 2; + break; + + case 0x72: // Channel Spectra Data + // $807F $80xx $8030 Invalid or unimplemented command code + if (Len < 15) return; // Wait for more + + Used = 15; + break; + + case 0x73: // Clover Link state + + if (Len < 2) return; // Wait for more + + StatusByte = TNC->CmdBuffer[1]; + + switch (StatusByte) + { + case 0x00: mySetWindowText(TNC, "Channel idle"); break; + case 0x01: mySetWindowText(TNC, "Channel occupied with non-Clover signal"); break; + case 0x42: mySetWindowText(TNC, "Linked stations monitored"); break; + case 0x64: mySetWindowText(TNC, "Attempting normal link"); break; + case 0x65: mySetWindowText(TNC, "Attempting robust link"); break; + case 0x66: mySetWindowText(TNC, "Calling ARQ CQ"); break; + case 0x78: mySetWindowText(TNC, "Clover Control Block (CCB) send retry"); break; + case 0x79: mySetWindowText(TNC, "Clover Control Block (CCB) receive retry"); break; + case 0x7D: mySetWindowText(TNC, "Clover Control Block (CCB) received successfully"); break; + case 0x8A: mySetWindowText(TNC, "TX data block sent"); break; + case 0x8B: mySetWindowText(TNC, "RX data block received ok (precedes data block)"); break; + case 0x8C: mySetWindowText(TNC, "TX data block re-sent"); break; + case 0x8D: mySetWindowText(TNC, "RX data block decode failed (precedes data block)"); break; + case 0x8E: mySetWindowText(TNC, "TX idle"); break; + case 0x8F: mySetWindowText(TNC, "RX idle"); break; + case 0x9C: mySetWindowText(TNC, "Link failed: CCB send retries exceeded"); break; + case 0x9D: mySetWindowText(TNC, "Link failed: CCB receive retries exceeded"); break; + case 0x9E: mySetWindowText(TNC, "Link failed: protocol error"); break; + case 0xA0: mySetWindowText(TNC, "Receiving FEC SYNC sequence"); break; + } + + Used = 2; + break; + + case 0x75: // Clover waveform format + + if (Len < 5) return; // Wait for more + + Used = 5; + break; + + case 0x7F: // Error $80xx $80yy Error in command $80xx of type $80yy + // $807F $80xx $8030 Invalid or unimplemented command code + // $807F $80xx $8031 Invalid parameter value + // $807F $80xx $8032 Not allowed when connected + // $807F $80xx $8033 Not allowed when disconnected + // $807F $80xx $8034 Not valid in this mode + // $807F $80xx $8035 Not valid in this code + // $807F $8096 $8036 EEPROM write error + + if (Len < 3) return; // Wait for more + + if (TNC->CmdBuffer[1] == 0x6f && TNC->CmdBuffer[2] == 0x31) + { + // Reject of XON/XOFF enable + +// TNC->XONXOFF = FALSE; +// Debugprintf("BPQ32 HAL Port %d - Disabling XON/XOFF mode", TNC->Port); + } + else + Debugprintf("HAL Port %d Command Error Cmd %X Error %X", TNC->Port, TNC->CmdBuffer[1], TNC->CmdBuffer[2]); + + Used = 3; + break; + + // Following are all immediate commands - response is echo of command + + case 0x6f: // XON/XOFF on + +// TNC->XONXOFF = TRUE; // And drop through +// Debugprintf("BPQ32 HAL Port %d - Enabling XON/XOFF mode", TNC->Port); + + case 0x19: // Call P-MODE to + case 0x10: // Robust Link to using MYCALL + case 0x11: // Normal Link to using MYCALL + + STREAM->Connecting = TRUE; + + case 0x00: // P Load LOD file + case 0x01: // P Load S28 file + case 0x02: //Check Unit Error Status + case 0x03: //F Check System Clock + case 0x04: //C Close PTT and transmit Clover waveform + case 0x05: //Open PTT and stop transmit test + case 0x06: //Immediate Abort (Panic Kill) + case 0x07: //Normal disconnect (wait for ACK) + case 0x08: //Software reset - restore all program defaults + case 0x0A: //Send CW ID + case 0x0B: //Close PTT and transmit Single Tone + case 0x0C: //F Normal OVER (AMTOR,P-MODE) + case 0x0D: //F Force RTTY TX (Baudot/ASCII) + case 0x0E: //F Go to RTTY RX (Baudot/ASCII) + case 0x0F: //Go to LOD/S28 file loader + case SetMYCALL: // Set MYCALL Response + + case 0x1E: // Set MYALTCALL Response + + case 0x41: + case 0x42: + case 0x46: + case 0x47: + case 0x48: + case 0x4d: + case 0x52: // Enable adaptive Clover format + case 0x54: // Enable adaptive Clover format + + case 0x56: // Expanded Link State Reports OFF/ON + case 0x57: // Clear buffers on disc + case 0x58: + case 0x59: + case 0x60: // Robust Mode Retries + case 0x61: // Normal Mode Retries + case 0x80: //Switch to CLOVER mode + case 0x81: //Select AMTOR Standby + case 0x82: //Select AMTOR FEC + case 0x83: //Select P-MODE Standby + case 0x84: //Switch to FSK modes + case 0x85: //Select Baudot + case 0x86: //Select ASCII + case 0x87: //Forced OVER (AMTOR, P-MODE) + case 0x88: //Forced END (AMTOR, P-MODE) + case 0x89: //Force LTRS shift + case 0x8A: //Force FIGS shift + case 0x8B: //Send MARK tone + case 0x8C: //Send SPACE tone + case 0x8D: //Send MARK/SPACE tones + case 0x8E: //Received first character on line + case 0x8F: //Close PTT only (no tones) + + case 0xC9: //Huffman Off/On + case 0xCC: + case 0xD9: //Close PTT only (no tones) + + case SetTones: + + Used = 1; + break; + + case 0x91: // ???? + +// if (Len < 2) return; // Wait for more + + Used = 1; + break; + + default: + + // We didn't recognise command, so don't know how long it is - disaster! + + Debugprintf("HAL Port %d Unrecognised Command %x", TNC->Port, Opcode); + TNC->CmdLen = 0; + + return; + } + + if (Used == Len) + { + // All used - most likely case + + TNC->CmdLen = 0; + return; + } + + // Move Command Down buffer, and reenter + + TNC->CmdLen -= Used; + + memmove(TNC->CmdBuffer, &TNC->CmdBuffer[Used], TNC->CmdLen); + + goto CmdLoop; + + +} + + +VOID HALDisconnected(struct TNCINFO * TNC) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + CloseLogfile(0); + CloseLogfile(1); + CloseLogfile(2); + + if ((STREAM->Connecting | STREAM->Connected) == 0) + { + // Not connected or Connecting. Probably response to going into Pactor Listen Mode + + return; + } + + if (STREAM->Connecting && STREAM->Disconnecting == FALSE) + { + UINT * buffptr; + + // Connect Failed - actually I think HAL uses another code for connect failed, but leave here for now + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "*** Failure with %s\r", STREAM->RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // In case! + STREAM->FramesQueued = 0; + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + return; + } + + // Connected, or Disconnecting - Release Session + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->FramesQueued = 0; + + if (STREAM->Disconnecting == FALSE) + STREAM->ReportDISC = TRUE; // Tell Node + + STREAM->Disconnecting = FALSE; + + // Need to reset Pactor Call in case it was changed + + TNC->NeedPACTOR = 20; +} + +BOOL HALConnected(struct TNCINFO * TNC, char * Call) +{ + char Msg[80]; + UINT * buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + char CallCopy[80]; + + strcpy(CallCopy, Call); + strcat(CallCopy, " "); // Some routines expect 10 char calls + + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->BytesAcked = 0; + STREAM->ConnectTime = time(NULL); + + // Stop Scanner + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + + ShowTraffic(TNC); + + TNC->DataMode = RXDATA; + + OpenLogfile(0); + OpenLogfile(1); + OpenLogfile(2); + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] == 0) + { + // Incoming Connect + + ProcessIncommingConnect(TNC, CallCopy, 0, TRUE); + + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", STREAM->RemoteCall, TNC->NodeCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + if (TNC->CurrentMode != Clover) + SendCmd(TNC, "\x87", 1); // Changeover to ISS + + // If an autoconnect APPL is defined, send it + + if (TNC->ApplCmd) + { + buffptr = GetBuff(); + if (buffptr == 0) return TRUE; // No buffers, so ignore + + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "%s\r", TNC->ApplCmd); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + + return TRUE; + } + + if (FULL_CTEXT && HFCTEXTLEN == 0) + { + EncodeAndSend(TNC, CTEXTMSG, CTEXTLEN); + WriteLogLine(2, CTEXTMSG, CTEXTLEN); + + STREAM->BytesTXed += CTEXTLEN; + } + return TRUE; + } + + // Connect Complete + + buffptr = GetBuff(); + if (buffptr == 0) return TRUE; // No buffers, so ignore + + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "*** Connected to %s\r", Call);; + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + STREAM->Connecting = FALSE; + STREAM->Connected = TRUE; // Subsequent data to data channel + + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", TNC->NodeCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + UpdateMH(TNC, CallCopy, '+', 'O'); + + + return TRUE; +} + + +static VOID EncodeAndSend(struct TNCINFO * TNC, UCHAR * txbuffer, int Len) +{ + // Send A Packet With DLE Encoding Encoding + + TNC->TXLen = DLEEncode(txbuffer, TNC->TXBuffer, Len); + + WriteCommBlock(TNC); +} + +VOID SendCmd(struct TNCINFO * TNC, UCHAR * txbuffer, int Len) +{ + // Send A Packet With Command Encoding (preceed each with 0x80 + + int i,txptr=0; + UCHAR * outbuff = TNC->TXBuffer; + + for (i=0; iTXLen = txptr; + WriteCommBlock(TNC); +} + +int DLEEncode(UCHAR * inbuff, UCHAR * outbuff, int len) +{ + int i, txptr = 0; + UCHAR c; + + // Escape x80 and x81 with x81 + +// outbuff[0] = 0x80; +// outbuff[1] = 0x33; // Send data to modem + + for (i=0;iNeedPACTOR = 30; +} + + + + diff --git a/.svn/pristine/14/14b226e8aa8a9b46d989b00b887bcc82fd050d7a.svn-base b/.svn/pristine/14/14b226e8aa8a9b46d989b00b887bcc82fd050d7a.svn-base index ea562b6..9ecc6b4 100644 --- a/.svn/pristine/14/14b226e8aa8a9b46d989b00b887bcc82fd050d7a.svn-base +++ b/.svn/pristine/14/14b226e8aa8a9b46d989b00b887bcc82fd050d7a.svn-base @@ -1,706 +1,706 @@ -/* -Copyright 2001-2015 John Wiseman G8BPQ - -This file is part of LinBPQ/BPQ32. - -LinBPQ/BPQ32 is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -LinBPQ/BPQ32 is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses -*/ - -// -// DLL to provide BPQEther support for G8BPQ switch in a -// 32bit environment, -// -// Uses BPQ EXTERNAL interface -// -// Uses WINPACAP library - -// Version 1.0 December 2005 - -// Version 1.1 January 2006 -// -// Get config file location from Node (will check bpq directory) - -// Version 1.2 October 2006 - -// Write diagnostics to BPQ console window instead of STDOUT - -// Version 1.3 February 2008 - -// Changes for dynamic unload of bpq32.dll - -// Version 1.3.1 Spetmber 2010 - -// Add option to get config from bpq32.dll - - -#define _CRT_SECURE_NO_DEPRECATE - -#include - -//#include - -#include "CHeaders.h" -#include -#include "pcap.h" - -#include "../CommonSource/bpq32.h" - -//#include "packet32.h" -//#include "ntddndis.h" - -extern char * PortConfig[33]; - -typedef struct PCAPStruct -{ - pcap_t *adhandle; - UCHAR EthSource[6]; - UCHAR EthDest[6]; - short EtherType; - BOOL RLITX; - BOOL RLIRX; - BOOL Promiscuous; - int pcap_reopen_delay; - char Adapter[256]; - -} PCAPINFO, *PPCAPINFO ; - -PCAPINFO * PCAPInfo[32]; - -//PPCAPINFO PCAPInfo[16]={0}; - -UCHAR EthDest[7]={01,'B','P','Q',0,0}; - -char EtherType[10]="0x08FF"; - -//pcap_t *adhandle; - -struct tagMSG Msg; - -short udpport=0; - -extern UCHAR BPQDirectory[]; - -unsigned int OurInst = 0; - -HWND hWnd; -BOOL GotMsg; - -DWORD n; - -#ifdef WIN32 - -static HINSTANCE PcapDriver=0; - -typedef int (FAR *FARPROCX)(); - -int (FAR * pcap_sendpacketx)(); - -FARPROCX pcap_compilex; -FARPROCX pcap_setfilterx; -FARPROCX pcap_datalinkx; -FARPROCX pcap_next_exx; -FARPROCX pcap_geterrx; -pcap_t * (FAR * pcap_open_livex)(const char *, int, int, int, char *); - -static char Dllname[6]="wpcap"; - -FARPROCX GetAddress(char * Proc); - -#else - -#define pcap_compilex pcap_compile -#define pcap_open_livex pcap_open_live -#define pcap_setfilterx pcap_setfilter -#define pcap_datalinkx pcap_datalink -#define pcap_next_exx pcap_next_ex -#define pcap_geterrx pcap_geterr -#define pcap_sendpacketx pcap_sendpacket -#endif - -int InitPCAP(void); -static FARPROCX GetAddress(char * Proc); - -static BOOL ReadConfigFile(int Port); -static int ProcessLine(char * buf,int Port, BOOL CheckPort); -static int OpenPCAP(PPCAPINFO PCAP); - - -int ExtProc(int fn, int port,unsigned char * buff) -{ - int len,txlen=0,res; - char txbuff[500]; - struct pcap_pkthdr *header; - u_char *pkt_data; - PPCAPINFO PCAP = PCAPInfo[port]; - - // char dcall[10],scall[10]; - - if (PCAP->adhandle == 0) - { - // No handle. - - if (PCAP->Adapter[0]) - { - // Try reopening periodically - - PCAP->pcap_reopen_delay --; - - if (PCAP->pcap_reopen_delay < 0) - if (OpenPCAP(PCAP) == FALSE) - PCAP->pcap_reopen_delay = 300; // Retry every 30 seconds - } - return 0; - } - switch (fn) - { - case 1: // poll - - res = pcap_next_exx(PCAP->adhandle, &header, &pkt_data); - - if (res == 0) - /* Timeout elapsed */ - return 0; - - if (res == -1) - { - // Failed - try to reopen - - if (OpenPCAP(PCAP) == FALSE) - PCAP->pcap_reopen_delay = 300; - return 0; - } - - if (PCAP->RLIRX) - - // RLI MODE - An extra 3 bytes before len, seem to be 00 00 41 - - { - len=pkt_data[18]*256 + pkt_data[17]; - - if ((len < 16) || (len > 320)) return 0; // Probably BPQ Mode Frame - - len-=3; - - memcpy(&buff[7],&pkt_data[19],len); - - len+=5; - } - else - { - len=pkt_data[15]*256 + pkt_data[14]; - - if ((len < 16) || (len > 320)) return 0; // Probably RLI Mode Frame - - len-=3; - - memcpy(&buff[7],&pkt_data[16],len); - - len+=5; - } - - buff[5]=(len & 0xff); - buff[6]=(len >> 8); - - return 1; - - - case 2: // send - - if (PCAP->RLITX) - - // RLI MODE - An extra 3 bytes before len, seem to be 00 00 41 - - { - txlen=(buff[6]<<8) + buff[5]; // BPQEther is DOS-based - chain word is 2 bytes - - txlen-=2; - txbuff[16]=0x41; - txbuff[17]=(txlen & 0xff); - txbuff[18]=(txlen >> 8); - - if (txlen < 1 || txlen > 400) - return 0; - - memcpy(&txbuff[19],&buff[7],txlen); - - } - else - { - txlen=(buff[6]<<8) + buff[5]; // BPQEther is DOS-based - chain word is 2 bytes - - txlen-=2; - - txbuff[14]=(txlen & 0xff); - txbuff[15]=(txlen >> 8); - - if (txlen < 1 || txlen > 400) - return 0; - - - memcpy(&txbuff[16],&buff[7],txlen); - } - - memcpy(&txbuff[0],&PCAP->EthDest[0],6); - memcpy(&txbuff[6],&PCAP->EthSource[0],6); - memcpy(&txbuff[12],&PCAP->EtherType,2); - - txlen+=14; - - - if (txlen < 60) txlen = 60; - - // Send down the packet - - if (pcap_sendpacketx(PCAP->adhandle, // Adapter - txbuff, // buffer with the packet - txlen // size - ) != 0) - { - - // n=sprintf(buf,"\nError sending the packet: \n", pcap_geterrx(PCAPInfo[port].adhandle)); - // WritetoConsole(buf); - - return 3; - } - - - return (0); - - - case 3: // CHECK IF OK TO SEND - - return (0); // OK - - case 4: // reinit - - return 0; - - case 5: // reinit - - return 0; - } - - return (0); -} - - -UINT ETHERExtInit(struct PORTCONTROL * PortEntry) -{ - // Can have multiple ports, each mapping to a different Ethernet Adapter - - // The Adapter number is in IOADDR - - int Port = PortEntry->PORTNUMBER; - PPCAPINFO PCAP; - char buf[80]; - - if (InitPCAP()) // Make sure pcap is installed - { - WritetoConsole("BPQEther "); - - // - // Read config first, to get UDP info if needed - // - - PCAP = PCAPInfo[Port] = zalloc(sizeof(struct PCAPStruct)); - - if (!ReadConfigFile(Port)) - return (FALSE); - - if (OpenPCAP(PCAP)) - sprintf(buf,"Using %s\n", PCAP->Adapter); - else - sprintf(buf,"Failed to open %s\n", PCAP->Adapter); - - WritetoConsole(buf); - } - return ((int) ExtProc); -} - - -InitPCAP() -{ - char Msg[255]; - int err; - u_long param=1; - BOOL bcopt=TRUE; - -// Use LoadLibrary/GetProcADDR to get round link problem - -#ifndef WIN32 - - return TRUE; - -#endif - - if (PcapDriver) - return TRUE; // Already loaded - - PcapDriver=LoadLibrary(Dllname); - - if (PcapDriver == NULL) - { - err=GetLastError(); - sprintf(Msg,"Error loading Driver %s - Error code %d", Dllname,err); - WritetoConsole(Msg); - return(FALSE); - } - - if ((pcap_sendpacketx=GetAddress("pcap_sendpacket")) == 0 ) return FALSE; - - if ((pcap_datalinkx=GetAddress("pcap_datalink")) == 0 ) return FALSE; - - if ((pcap_compilex=GetAddress("pcap_compile")) == 0 ) return FALSE; - - if ((pcap_setfilterx=GetAddress("pcap_setfilter")) == 0 ) return FALSE; - - if ((pcap_open_livex = (pcap_t * (__cdecl *)()) - GetProcAddress(PcapDriver,"pcap_open_live")) == 0 ) return FALSE; - - if ((pcap_geterrx=GetAddress("pcap_geterr")) == 0 ) return FALSE; - - if ((pcap_next_exx=GetAddress("pcap_next_ex")) == 0 ) return FALSE; - - return (TRUE); -} - -#ifdef WIN32 - -FARPROCX GetAddress(char * Proc) -{ - FARPROCX ProcAddr; - int err=0; - char buf[256]; - int n; - - - ProcAddr=(FARPROCX) GetProcAddress(PcapDriver,Proc); - - if (ProcAddr == 0) - { - err=GetLastError(); - - n=sprintf(buf,"Error finding %s - %d\n", Proc,err); - WritetoConsole(buf); - - return(0); - } - - return ProcAddr; -} -#endif - -#include - -#pragma comment(lib, "IPHLPAPI.lib") - -void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data); - -int OpenPCAP(PPCAPINFO PCAP) -{ - int i=0; - char errbuf[PCAP_ERRBUF_SIZE]; - u_int netmask; - char packet_filter[30]; - struct bpf_program fcode; - char buf[256]; - int n; - - /* Open the adapter */ - if ((PCAP->adhandle= pcap_open_livex(PCAP->Adapter, // name of the device - 65536, // portion of the packet to capture. - // 65536 grants that the whole packet will be captured on all the MACs. - PCAP->Promiscuous, // promiscuous mode (nonzero means promiscuous) - 1, // read timeout - errbuf // error buffer - )) == NULL) - { - return FALSE; - } - - /* Check the link layer. We support only Ethernet for simplicity. */ - if(pcap_datalinkx(PCAP->adhandle) != DLT_EN10MB) - { - n=sprintf(buf,"This program works only on Ethernet networks.\n"); - WritetoConsole(buf); - - return FALSE; - } - - netmask=0xffffff; - - sprintf(packet_filter,"ether[12:2]=0x%x", - ntohs(PCAP->EtherType)); - - //compile the filter - if (pcap_compilex(PCAP->adhandle, &fcode, packet_filter, 1, netmask) <0 ) - { - n=sprintf(buf,"Unable to compile the packet filter. Check the syntax.\n"); - WritetoConsole(buf); - - /* Free the device list */ - return FALSE; - } - - //set the filter - - if (pcap_setfilterx(PCAP->adhandle, &fcode)<0) - { - n=sprintf(buf,"Error setting the filter.\n"); - WritetoConsole(buf); - - /* Free the device list */ - return FALSE; - } - - return TRUE; - -} - - -static BOOL ReadConfigFile(int Port) -{ -//TYPE 1 08FF # Ethernet Type -//ETH 1 FF:FF:FF:FF:FF:FF # Target Ethernet AddrMAP G8BPQ-7 10.2.77.1 # IP 93 for compatibility -//ADAPTER 1 \Device\NPF_{21B601E8-8088-4F7D-96 29-EDE2A9243CF4} # Adapter Name - - char buf[256],errbuf[256]; - char * Config; - PPCAPINFO PCAP = PCAPInfo[Port]; - - Config = PortConfig[Port]; - - PCAP->Promiscuous = 1; // Defaults - PCAP->EtherType=htons(0x08FF); - memset(PCAP->EthDest, 0xff, 6); - - if (Config) - { - // Using config from bpq32.cfg - - char * ptr1 = Config, * ptr2; - - ptr2 = strchr(ptr1, 13); - while(ptr2) - { - memcpy(buf, ptr1, ptr2 - ptr1); - buf[ptr2 - ptr1] = 0; - ptr1 = ptr2 + 2; - ptr2 = strchr(ptr1, 13); - - strcpy(errbuf,buf); // save in case of error - - if (!ProcessLine(buf, Port, FALSE)) - { - WritetoConsole("BPQEther - Bad config record "); - WritetoConsole(errbuf); - WritetoConsole("\n"); - } - } - return (TRUE); - } - - n=sprintf(buf,"No config info found in bpq32.cfg\n"); - WritetoConsole(buf); - - return (FALSE); -} - - -static int ProcessLine(char * buf, int Port, BOOL CheckPort) -{ - char * ptr; - char * p_port; - char * p_mac; - char * p_Adapter; - char * p_type; - - int port; - int a,b,c,d,e,f,num; - - PPCAPINFO PCAP = PCAPInfo[Port]; - - ptr = strtok(buf, " \t\n\r"); - - if(ptr == NULL) return (TRUE); - - if(*ptr =='#') return (TRUE); // comment - - if(*ptr ==';') return (TRUE); // comment - - if (CheckPort) - { - p_port = strtok(NULL, " \t\n\r"); - - if (p_port == NULL) return (FALSE); - - port = atoi(p_port); - - if (Port != port) return TRUE; // Not for us - } - - if(_stricmp(ptr,"ADAPTER") == 0) - { - IP_ADAPTER_INFO AdapterInfo[16]; // Allocate information for up to 16 NICs - DWORD dwBufLen = sizeof(AdapterInfo); // Save memory size of buffer - DWORD dwStatus = GetAdaptersInfo(AdapterInfo, &dwBufLen); - - PIP_ADAPTER_INFO pAdapterInfo = AdapterInfo; - - p_Adapter = strtok(NULL, " \t\n\r"); - - strcpy(PCAP->Adapter, p_Adapter); - - // Look for MAC Address - - do - { - if (strcmp(pAdapterInfo->AdapterName, &PCAP->Adapter[12]) == 0) - { - memcpy(PCAP->EthSource, pAdapterInfo->Address, 6); - break; - } - - pAdapterInfo = pAdapterInfo->Next; // Progress through linked list - - } while(pAdapterInfo); // Terminate if last adapter - - return (TRUE); - } - - if(_stricmp(ptr,"TYPE") == 0) - { - p_type = strtok(NULL, " \t\n\r"); - - if (p_type == NULL) return (FALSE); - - num=sscanf(p_type,"%x",&a); - - if (num != 1) return FALSE; - - PCAP->EtherType=htons(a); - return (TRUE); - - } - - if(_stricmp(ptr,"promiscuous") == 0) - { - ptr = strtok(NULL, " \t\n\r"); - - if (ptr == NULL) return (FALSE); - - PCAP->Promiscuous = atoi(ptr); - - return (TRUE); - - } - - if(_stricmp(ptr,"RXMODE") == 0) - { - p_port = strtok(NULL, " \t\n\r"); - - if (p_port == NULL) return (FALSE); - - if(_stricmp(p_port,"RLI") == 0) - { - PCAP->RLIRX=TRUE; - return (TRUE); - } - - if(_stricmp(p_port,"BPQ") == 0) - { - PCAP->RLIRX=FALSE; - return (TRUE); - } - - return FALSE; - - } - - if(_stricmp(ptr,"TXMODE") == 0) - { - p_port = strtok(NULL, " \t\n\r"); - - if (p_port == NULL) return (FALSE); - - if(_stricmp(p_port,"RLI") == 0) - { - PCAP->RLITX=TRUE; - return (TRUE); - } - - if(_stricmp(p_port,"BPQ") == 0) - { - PCAP->RLITX=FALSE; - return (TRUE); - } - - return FALSE; - - } - - if(_stricmp(ptr,"DEST") == 0) - { - p_mac = strtok(NULL, " \t\n\r"); - - if (p_mac == NULL) return (FALSE); - - num=sscanf(p_mac,"%x-%x-%x-%x-%x-%x",&a,&b,&c,&d,&e,&f); - - if (num != 6) return FALSE; - - PCAP->EthDest[0]=a; - PCAP->EthDest[1]=b; - PCAP->EthDest[2]=c; - PCAP->EthDest[3]=d; - PCAP->EthDest[4]=e; - PCAP->EthDest[5]=f; - - - // strcpy(Adapter,p_Adapter); - - return (TRUE); - } - - if(_stricmp(ptr,"SOURCE") == 0) - { - p_mac = strtok(NULL, " \t\n\r"); - - if (p_mac == NULL) return (FALSE); - - num=sscanf(p_mac,"%x-%x-%x-%x-%x-%x",&a,&b,&c,&d,&e,&f); - - if (num != 6) return FALSE; - - PCAP->EthSource[0]=a; - PCAP->EthSource[1]=b; - PCAP->EthSource[2]=c; - PCAP->EthSource[3]=d; - PCAP->EthSource[4]=e; - PCAP->EthSource[5]=f; - - // strcpy(Adapter,p_Adapter); - - return (TRUE); - - } - // - // Bad line - // - return (FALSE); - -} - - - +/* +Copyright 2001-2015 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// DLL to provide BPQEther support for G8BPQ switch in a +// 32bit environment, +// +// Uses BPQ EXTERNAL interface +// +// Uses WINPACAP library + +// Version 1.0 December 2005 + +// Version 1.1 January 2006 +// +// Get config file location from Node (will check bpq directory) + +// Version 1.2 October 2006 + +// Write diagnostics to BPQ console window instead of STDOUT + +// Version 1.3 February 2008 + +// Changes for dynamic unload of bpq32.dll + +// Version 1.3.1 Spetmber 2010 + +// Add option to get config from bpq32.dll + + +#define _CRT_SECURE_NO_DEPRECATE + +#include + +//#include + +#include "CHeaders.h" +#include +#include "pcap.h" + +#include "../CommonSource/bpq32.h" + +//#include "packet32.h" +//#include "ntddndis.h" + +extern char * PortConfig[33]; + +typedef struct PCAPStruct +{ + pcap_t *adhandle; + UCHAR EthSource[6]; + UCHAR EthDest[6]; + short EtherType; + BOOL RLITX; + BOOL RLIRX; + BOOL Promiscuous; + int pcap_reopen_delay; + char Adapter[256]; + +} PCAPINFO, *PPCAPINFO ; + +PCAPINFO * PCAPInfo[32]; + +//PPCAPINFO PCAPInfo[16]={0}; + +UCHAR EthDest[7]={01,'B','P','Q',0,0}; + +char EtherType[10]="0x08FF"; + +//pcap_t *adhandle; + +struct tagMSG Msg; + +short udpport=0; + +extern UCHAR BPQDirectory[]; + +unsigned int OurInst = 0; + +HWND hWnd; +BOOL GotMsg; + +DWORD n; + +#ifdef WIN32 + +static HINSTANCE PcapDriver=0; + +typedef int (FAR *FARPROCX)(); + +int (FAR * pcap_sendpacketx)(); + +FARPROCX pcap_compilex; +FARPROCX pcap_setfilterx; +FARPROCX pcap_datalinkx; +FARPROCX pcap_next_exx; +FARPROCX pcap_geterrx; +pcap_t * (FAR * pcap_open_livex)(const char *, int, int, int, char *); + +static char Dllname[6]="wpcap"; + +FARPROCX GetAddress(char * Proc); + +#else + +#define pcap_compilex pcap_compile +#define pcap_open_livex pcap_open_live +#define pcap_setfilterx pcap_setfilter +#define pcap_datalinkx pcap_datalink +#define pcap_next_exx pcap_next_ex +#define pcap_geterrx pcap_geterr +#define pcap_sendpacketx pcap_sendpacket +#endif + +int InitPCAP(void); +static FARPROCX GetAddress(char * Proc); + +static BOOL ReadConfigFile(int Port); +static int ProcessLine(char * buf,int Port, BOOL CheckPort); +static int OpenPCAP(PPCAPINFO PCAP); + + +int ExtProc(int fn, int port,unsigned char * buff) +{ + int len,txlen=0,res; + char txbuff[500]; + struct pcap_pkthdr *header; + u_char *pkt_data; + PPCAPINFO PCAP = PCAPInfo[port]; + + // char dcall[10],scall[10]; + + if (PCAP->adhandle == 0) + { + // No handle. + + if (PCAP->Adapter[0]) + { + // Try reopening periodically + + PCAP->pcap_reopen_delay --; + + if (PCAP->pcap_reopen_delay < 0) + if (OpenPCAP(PCAP) == FALSE) + PCAP->pcap_reopen_delay = 300; // Retry every 30 seconds + } + return 0; + } + switch (fn) + { + case 1: // poll + + res = pcap_next_exx(PCAP->adhandle, &header, &pkt_data); + + if (res == 0) + /* Timeout elapsed */ + return 0; + + if (res == -1) + { + // Failed - try to reopen + + if (OpenPCAP(PCAP) == FALSE) + PCAP->pcap_reopen_delay = 300; + return 0; + } + + if (PCAP->RLIRX) + + // RLI MODE - An extra 3 bytes before len, seem to be 00 00 41 + + { + len=pkt_data[18]*256 + pkt_data[17]; + + if ((len < 16) || (len > 320)) return 0; // Probably BPQ Mode Frame + + len-=3; + + memcpy(&buff[7],&pkt_data[19],len); + + len+=5; + } + else + { + len=pkt_data[15]*256 + pkt_data[14]; + + if ((len < 16) || (len > 320)) return 0; // Probably RLI Mode Frame + + len-=3; + + memcpy(&buff[7],&pkt_data[16],len); + + len+=5; + } + + buff[5]=(len & 0xff); + buff[6]=(len >> 8); + + return 1; + + + case 2: // send + + if (PCAP->RLITX) + + // RLI MODE - An extra 3 bytes before len, seem to be 00 00 41 + + { + txlen=(buff[6]<<8) + buff[5]; // BPQEther is DOS-based - chain word is 2 bytes + + txlen-=2; + txbuff[16]=0x41; + txbuff[17]=(txlen & 0xff); + txbuff[18]=(txlen >> 8); + + if (txlen < 1 || txlen > 400) + return 0; + + memcpy(&txbuff[19],&buff[7],txlen); + + } + else + { + txlen=(buff[6]<<8) + buff[5]; // BPQEther is DOS-based - chain word is 2 bytes + + txlen-=2; + + txbuff[14]=(txlen & 0xff); + txbuff[15]=(txlen >> 8); + + if (txlen < 1 || txlen > 400) + return 0; + + + memcpy(&txbuff[16],&buff[7],txlen); + } + + memcpy(&txbuff[0],&PCAP->EthDest[0],6); + memcpy(&txbuff[6],&PCAP->EthSource[0],6); + memcpy(&txbuff[12],&PCAP->EtherType,2); + + txlen+=14; + + + if (txlen < 60) txlen = 60; + + // Send down the packet + + if (pcap_sendpacketx(PCAP->adhandle, // Adapter + txbuff, // buffer with the packet + txlen // size + ) != 0) + { + + // n=sprintf(buf,"\nError sending the packet: \n", pcap_geterrx(PCAPInfo[port].adhandle)); + // WritetoConsole(buf); + + return 3; + } + + + return (0); + + + case 3: // CHECK IF OK TO SEND + + return (0); // OK + + case 4: // reinit + + return 0; + + case 5: // reinit + + return 0; + } + + return (0); +} + + +UINT ETHERExtInit(struct PORTCONTROL * PortEntry) +{ + // Can have multiple ports, each mapping to a different Ethernet Adapter + + // The Adapter number is in IOADDR + + int Port = PortEntry->PORTNUMBER; + PPCAPINFO PCAP; + char buf[80]; + + if (InitPCAP()) // Make sure pcap is installed + { + WritetoConsole("BPQEther "); + + // + // Read config first, to get UDP info if needed + // + + PCAP = PCAPInfo[Port] = zalloc(sizeof(struct PCAPStruct)); + + if (!ReadConfigFile(Port)) + return (FALSE); + + if (OpenPCAP(PCAP)) + sprintf(buf,"Using %s\n", PCAP->Adapter); + else + sprintf(buf,"Failed to open %s\n", PCAP->Adapter); + + WritetoConsole(buf); + } + return ((int) ExtProc); +} + + +InitPCAP() +{ + char Msg[255]; + int err; + u_long param=1; + BOOL bcopt=TRUE; + +// Use LoadLibrary/GetProcADDR to get round link problem + +#ifndef WIN32 + + return TRUE; + +#endif + + if (PcapDriver) + return TRUE; // Already loaded + + PcapDriver=LoadLibrary(Dllname); + + if (PcapDriver == NULL) + { + err=GetLastError(); + sprintf(Msg,"Error loading Driver %s - Error code %d", Dllname,err); + WritetoConsole(Msg); + return(FALSE); + } + + if ((pcap_sendpacketx=GetAddress("pcap_sendpacket")) == 0 ) return FALSE; + + if ((pcap_datalinkx=GetAddress("pcap_datalink")) == 0 ) return FALSE; + + if ((pcap_compilex=GetAddress("pcap_compile")) == 0 ) return FALSE; + + if ((pcap_setfilterx=GetAddress("pcap_setfilter")) == 0 ) return FALSE; + + if ((pcap_open_livex = (pcap_t * (__cdecl *)()) + GetProcAddress(PcapDriver,"pcap_open_live")) == 0 ) return FALSE; + + if ((pcap_geterrx=GetAddress("pcap_geterr")) == 0 ) return FALSE; + + if ((pcap_next_exx=GetAddress("pcap_next_ex")) == 0 ) return FALSE; + + return (TRUE); +} + +#ifdef WIN32 + +FARPROCX GetAddress(char * Proc) +{ + FARPROCX ProcAddr; + int err=0; + char buf[256]; + int n; + + + ProcAddr=(FARPROCX) GetProcAddress(PcapDriver,Proc); + + if (ProcAddr == 0) + { + err=GetLastError(); + + n=sprintf(buf,"Error finding %s - %d\n", Proc,err); + WritetoConsole(buf); + + return(0); + } + + return ProcAddr; +} +#endif + +#include + +#pragma comment(lib, "IPHLPAPI.lib") + +void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data); + +int OpenPCAP(PPCAPINFO PCAP) +{ + int i=0; + char errbuf[PCAP_ERRBUF_SIZE]; + u_int netmask; + char packet_filter[30]; + struct bpf_program fcode; + char buf[256]; + int n; + + /* Open the adapter */ + if ((PCAP->adhandle= pcap_open_livex(PCAP->Adapter, // name of the device + 65536, // portion of the packet to capture. + // 65536 grants that the whole packet will be captured on all the MACs. + PCAP->Promiscuous, // promiscuous mode (nonzero means promiscuous) + 1, // read timeout + errbuf // error buffer + )) == NULL) + { + return FALSE; + } + + /* Check the link layer. We support only Ethernet for simplicity. */ + if(pcap_datalinkx(PCAP->adhandle) != DLT_EN10MB) + { + n=sprintf(buf,"This program works only on Ethernet networks.\n"); + WritetoConsole(buf); + + return FALSE; + } + + netmask=0xffffff; + + sprintf(packet_filter,"ether[12:2]=0x%x", + ntohs(PCAP->EtherType)); + + //compile the filter + if (pcap_compilex(PCAP->adhandle, &fcode, packet_filter, 1, netmask) <0 ) + { + n=sprintf(buf,"Unable to compile the packet filter. Check the syntax.\n"); + WritetoConsole(buf); + + /* Free the device list */ + return FALSE; + } + + //set the filter + + if (pcap_setfilterx(PCAP->adhandle, &fcode)<0) + { + n=sprintf(buf,"Error setting the filter.\n"); + WritetoConsole(buf); + + /* Free the device list */ + return FALSE; + } + + return TRUE; + +} + + +static BOOL ReadConfigFile(int Port) +{ +//TYPE 1 08FF # Ethernet Type +//ETH 1 FF:FF:FF:FF:FF:FF # Target Ethernet AddrMAP G8BPQ-7 10.2.77.1 # IP 93 for compatibility +//ADAPTER 1 \Device\NPF_{21B601E8-8088-4F7D-96 29-EDE2A9243CF4} # Adapter Name + + char buf[256],errbuf[256]; + char * Config; + PPCAPINFO PCAP = PCAPInfo[Port]; + + Config = PortConfig[Port]; + + PCAP->Promiscuous = 1; // Defaults + PCAP->EtherType=htons(0x08FF); + memset(PCAP->EthDest, 0xff, 6); + + if (Config) + { + // Using config from bpq32.cfg + + char * ptr1 = Config, * ptr2; + + ptr2 = strchr(ptr1, 13); + while(ptr2) + { + memcpy(buf, ptr1, ptr2 - ptr1); + buf[ptr2 - ptr1] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + strcpy(errbuf,buf); // save in case of error + + if (!ProcessLine(buf, Port, FALSE)) + { + WritetoConsole("BPQEther - Bad config record "); + WritetoConsole(errbuf); + WritetoConsole("\n"); + } + } + return (TRUE); + } + + n=sprintf(buf,"No config info found in bpq32.cfg\n"); + WritetoConsole(buf); + + return (FALSE); +} + + +static int ProcessLine(char * buf, int Port, BOOL CheckPort) +{ + char * ptr; + char * p_port; + char * p_mac; + char * p_Adapter; + char * p_type; + + int port; + int a,b,c,d,e,f,num; + + PPCAPINFO PCAP = PCAPInfo[Port]; + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if (CheckPort) + { + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + port = atoi(p_port); + + if (Port != port) return TRUE; // Not for us + } + + if(_stricmp(ptr,"ADAPTER") == 0) + { + IP_ADAPTER_INFO AdapterInfo[16]; // Allocate information for up to 16 NICs + DWORD dwBufLen = sizeof(AdapterInfo); // Save memory size of buffer + DWORD dwStatus = GetAdaptersInfo(AdapterInfo, &dwBufLen); + + PIP_ADAPTER_INFO pAdapterInfo = AdapterInfo; + + p_Adapter = strtok(NULL, " \t\n\r"); + + strcpy(PCAP->Adapter, p_Adapter); + + // Look for MAC Address + + do + { + if (strcmp(pAdapterInfo->AdapterName, &PCAP->Adapter[12]) == 0) + { + memcpy(PCAP->EthSource, pAdapterInfo->Address, 6); + break; + } + + pAdapterInfo = pAdapterInfo->Next; // Progress through linked list + + } while(pAdapterInfo); // Terminate if last adapter + + return (TRUE); + } + + if(_stricmp(ptr,"TYPE") == 0) + { + p_type = strtok(NULL, " \t\n\r"); + + if (p_type == NULL) return (FALSE); + + num=sscanf(p_type,"%x",&a); + + if (num != 1) return FALSE; + + PCAP->EtherType=htons(a); + return (TRUE); + + } + + if(_stricmp(ptr,"promiscuous") == 0) + { + ptr = strtok(NULL, " \t\n\r"); + + if (ptr == NULL) return (FALSE); + + PCAP->Promiscuous = atoi(ptr); + + return (TRUE); + + } + + if(_stricmp(ptr,"RXMODE") == 0) + { + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + if(_stricmp(p_port,"RLI") == 0) + { + PCAP->RLIRX=TRUE; + return (TRUE); + } + + if(_stricmp(p_port,"BPQ") == 0) + { + PCAP->RLIRX=FALSE; + return (TRUE); + } + + return FALSE; + + } + + if(_stricmp(ptr,"TXMODE") == 0) + { + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + if(_stricmp(p_port,"RLI") == 0) + { + PCAP->RLITX=TRUE; + return (TRUE); + } + + if(_stricmp(p_port,"BPQ") == 0) + { + PCAP->RLITX=FALSE; + return (TRUE); + } + + return FALSE; + + } + + if(_stricmp(ptr,"DEST") == 0) + { + p_mac = strtok(NULL, " \t\n\r"); + + if (p_mac == NULL) return (FALSE); + + num=sscanf(p_mac,"%x-%x-%x-%x-%x-%x",&a,&b,&c,&d,&e,&f); + + if (num != 6) return FALSE; + + PCAP->EthDest[0]=a; + PCAP->EthDest[1]=b; + PCAP->EthDest[2]=c; + PCAP->EthDest[3]=d; + PCAP->EthDest[4]=e; + PCAP->EthDest[5]=f; + + + // strcpy(Adapter,p_Adapter); + + return (TRUE); + } + + if(_stricmp(ptr,"SOURCE") == 0) + { + p_mac = strtok(NULL, " \t\n\r"); + + if (p_mac == NULL) return (FALSE); + + num=sscanf(p_mac,"%x-%x-%x-%x-%x-%x",&a,&b,&c,&d,&e,&f); + + if (num != 6) return FALSE; + + PCAP->EthSource[0]=a; + PCAP->EthSource[1]=b; + PCAP->EthSource[2]=c; + PCAP->EthSource[3]=d; + PCAP->EthSource[4]=e; + PCAP->EthSource[5]=f; + + // strcpy(Adapter,p_Adapter); + + return (TRUE); + + } + // + // Bad line + // + return (FALSE); + +} + + + diff --git a/.svn/pristine/15/1557275e0b72cb1376626ed0c9e0e37f9edb65c4.svn-base b/.svn/pristine/15/1557275e0b72cb1376626ed0c9e0e37f9edb65c4.svn-base new file mode 100644 index 0000000..90ae26a --- /dev/null +++ b/.svn/pristine/15/1557275e0b72cb1376626ed0c9e0e37f9edb65c4.svn-base @@ -0,0 +1,6465 @@ +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +#define _CRT_SECURE_NO_DEPRECATE + +#include "cheaders.h" +#include "bpqmail.h" + +#define MAIL +#include "httpconnectioninfo.h" + +#ifdef WIN32 +//#include "C:\Program Files (x86)\GnuWin32\include\iconv.h" +#else +#include +#include +#endif + +static struct HTTPConnectionInfo * FindSession(char * Key); +int APIENTRY SessionControl(int stream, int command, int param); +int SetupNodeMenu(char * Buff); +VOID SetMultiStringValue(char ** values, char * Multi); +char * GetTemplateFromFile(int Version, char * FN); +VOID FormatTime(char * Time, time_t cTime); +struct MsgInfo * GetMsgFromNumber(int msgno); +BOOL CheckUserMsg(struct MsgInfo * Msg, char * Call, BOOL SYSOP); +BOOL OkToKillMessage(BOOL SYSOP, char * Call, struct MsgInfo * Msg); +int DisplayWebForm(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, char * FileName, char * XML, char * Reply, char * RawMessage, int RawLen); +struct HTTPConnectionInfo * AllocateWebMailSession(); +VOID SaveNewMessage(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest, int InputLen); +void ConvertTitletoUTF8(WebMailInfo * WebMail, char * Title, char * UTF8Title, int Len); +char *stristr (char *ch1, char *ch2); +char * ReadTemplate(char * FormSet, char * DirName, char * FileName); +VOID DoStandardTemplateSubsitutions(struct HTTPConnectionInfo * Session, char * txtFile); +BOOL CheckifPacket(char * Via); +int GetHTMLFormSet(char * FormSet); +void ProcessFormInput(struct HTTPConnectionInfo * Session, char * input, char * Reply, int * RLen, int InputLen); +char * WebFindPart(char ** Msg, char * Boundary, int * PartLen, char * End); +struct HTTPConnectionInfo * FindWMSession(char * Key); +int SendWebMailHeaderEx(char * Reply, char * Key, struct HTTPConnectionInfo * Session, char * Alert); +char * BuildFormMessage(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, char * Keys[1000], char * Values[1000], int NumKeys); +char * FindXMLVariable(WebMailInfo * WebMail, char * Var); +int ReplyToFormsMessage(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, char * Reply, BOOL Reenter); +BOOL ParsetxtTemplate(struct HTTPConnectionInfo * Session, struct HtmlFormDir * Dir, char * FN, BOOL isReply); +VOID UpdateFormAction(char * Template, char * Key); +BOOL APIENTRY GetAPRSLatLon(double * PLat, double * PLon); +BOOL APIENTRY GetAPRSLatLonString(char * PLat, char * PLon); +void FreeWebMailFields(WebMailInfo * WebMail); +VOID BuildXMLAttachment(struct HTTPConnectionInfo * Session, char * Keys[1000], char * Values[1000], int NumKeys); +VOID SaveTemplateMessage(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest); +VOID DownloadAttachments(struct HTTPConnectionInfo * Session, char * Reply, int * RLen, char * Rest); +VOID getAttachmentList(struct HTTPConnectionInfo * Session, char * Reply, int * RLen, char * Rest); +char * BuildB2Header(WebMailInfo * WebMail, struct MsgInfo * Msg, char ** ToCalls, int Calls); +VOID FormatTime2(char * Time, time_t cTime); +VOID ProcessSelectResponse(struct HTTPConnectionInfo * Session, char * URLParams); +VOID ProcessAskResponse(struct HTTPConnectionInfo * Session, char * URLParams); +char * CheckFile(struct HtmlFormDir * Dir, char * FN); +VOID GetPage(struct HTTPConnectionInfo * Session, char * NodeURL); +VOID SendTemplateSelectScreen(struct HTTPConnectionInfo * Session, char *URLParams, int InputLen); +BOOL isAMPRMsg(char * Addr); +char * doXMLTransparency(char * string); +Dll BOOL APIENTRY APISendAPRSMessage(char * Text, char * ToCall); +void SendMessageReadEvent(char * Call, struct MsgInfo * Msg); +void SendNewMessageEvent(char * call, struct MsgInfo * Msg); +void MQTTMessageEvent(void* message); + +extern char NodeTail[]; +extern char BBSName[10]; + +extern char LTFROMString[2048]; +extern char LTTOString[2048]; +extern char LTATString[2048]; + + UCHAR BPQDirectory[260]; + +int LineCount = 35; // Lines per page on message list + +// Forms + +struct HtmlFormDir ** HtmlFormDirs = NULL; +int FormDirCount = 0; + +struct HtmlForm +{ + char * FileName; + BOOL HasInitial; + BOOL HasViewer; + BOOL HasReply; + BOOL HasReplyViewer; +}; + +struct HtmlFormDir +{ + char * FormSet; + char * DirName; + struct HtmlForm ** Forms; + int FormCount; + struct HtmlFormDir ** Dirs; // Nested Directories + int DirCount; +}; + + +char FormDirList[4][MAX_PATH] = {"Standard_Templates", "Standard Templates", "Local_Templates"}; + +static char PassError[] = "

Sorry, User or Password is invalid - please try again

"; +static char BusyError[] = "

Sorry, No sessions available - please try later

"; + +extern char MailSignon[]; + +char WebMailSignon[] = "BPQ32 Mail Server Access" + "

BPQ32 Mail Server %s Access

" + "

Please enter Callsign and Password to access WebMail

" + "
" + "" + "" + "
User
Password
" + "

"; + +static char MsgInputPage[] = "" + "" + "" + "" + "" + "

Webmail Interface - Message Input Form

" + "
" + "
" + "To      %s
" + "Subject      " +// "" +// "
" + "" + "
Type    " + "" + " BID
" + "" + "" + "
" + "
" + "
"; + +static char CheckFormMsgPage[] = "" + "" + "" + "

Webmail Forms Interface - Check Message

" + "
" + + "
" + "To      
" + "CC      
" + "Subject " +//"" + "
Type    " + "" + " BID

" + "
" + + "
" + "
"; + + +extern char * WebMailTemplate; +extern char * WebMailMsgTemplate; +extern char * jsTemplate; + +static char *dat[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; +char *longday[] = {"Sunday", "Monday", "Tusday", "Wednesday", "Thusday", "Friday", "Saturday"}; + +static struct HTTPConnectionInfo * WebSessionList = NULL; // active WebMail sessions + +#ifdef LINBPQ +UCHAR * GetBPQDirectory(); +#endif + +void UndoTransparency(char * input); + +#ifndef LINBPQ + +void UndoTransparency(char * input) +{ + char * ptr1, * ptr2; + char c; + int hex; + + if (input == NULL) + return; + + ptr1 = ptr2 = input; + + // Convert any %xx constructs + + while (1) + { + c = *(ptr1++); + + if (c == 0) + break; + + if (c == '%') + { + c = *(ptr1++); + if(isdigit(c)) + hex = (c - '0') << 4; + else + hex = (tolower(c) - 'a' + 10) << 4; + + c = *(ptr1++); + if(isdigit(c)) + hex += (c - '0'); + else + hex += (tolower(c) - 'a' + 10); + + *(ptr2++) = hex; + } + else if (c == '+') + *(ptr2++) = 32; + else + *(ptr2++) = c; + } + *ptr2 = 0; +} +#endif + +void ReleaseWebMailStruct(WebMailInfo * WebMail) +{ + // release any malloc'ed resources + + if (WebMail == NULL) + return; + + FreeWebMailFields(WebMail); + free(WebMail); + return; +} + +VOID FreeWebMailMallocs() +{ + // called when closing. Not really needed, but simplifies tracking down real memory leaks + + struct HTTPConnectionInfo * Session, * SaveNext; + int i; + Session = WebSessionList; + + while (Session) + { + SaveNext = Session->Next; + + // Release amy malloc'ed resouces + + ReleaseWebMailStruct(Session->WebMail); + free(Session); + Session = SaveNext; + } + + for (i = 0; i < FormDirCount; i++) + { + struct HtmlFormDir * Dir = HtmlFormDirs[i]; + + int j; + + for (j = 0; j < Dir->FormCount; j++) + { + free(Dir->Forms[j]->FileName); + free(Dir->Forms[j]); + } + + if (Dir->DirCount) + { + struct HtmlFormDir * SubDir; + + int k, l; + + for (l = 0; l < Dir->DirCount; l++) + { + SubDir = Dir->Dirs[l]; + + for (k = 0; k < Dir->Dirs[l]->FormCount; k++) + { + free(SubDir->Forms[k]->FileName); + free(SubDir->Forms[k]); + } + free(SubDir->DirName); + free(SubDir->Forms); + free(SubDir->FormSet); + + free(Dir->Dirs[l]); + } + } + free(Dir->DirName); + free(Dir->Forms); + free(Dir->FormSet); + free(Dir); + } + + free(HtmlFormDirs); + return; +} + +char * initMultipartUnpack(char ** Input) +{ + // Check if Multipart and return Boundary. Update Input to first part + + // look through header for Content-Type line, and if multipart + // find boundary string. + + char * ptr, * ptr2; + char Boundary[128]; + BOOL Multipart = FALSE; + + ptr = *Input; + + while(*ptr != 13) + { + ptr2 = strchr(ptr, 10); // Find CR + + while(ptr2[1] == ' ' || ptr2[1] == 9) // Whitespace - continuation line + ptr2 = strchr(&ptr2[1], 10); // Find CR + + if (_memicmp(ptr, "Content-Type: ", 14) == 0) + { + char Line[256] = ""; + char * ptr3; + size_t len = ptr2-ptr-14; + + if (len >255) + return NULL; + + memcpy(Line, &ptr[14], len); + + if (_memicmp(Line, "Multipart/", 10) == 0) + { + ptr3 = stristr(Line, "boundary"); + if (ptr3) + { + ptr3+=9; + + if ((*ptr3) == '"') + ptr3++; + + strcpy(Boundary, ptr3); + ptr3 = strchr(Boundary, '"'); + if (ptr3) *ptr3 = 0; + ptr3 = strchr(Boundary, 13); // CR + if (ptr3) *ptr3 = 0; + break; + } + else + return NULL; // Can't do anything without a boundary ?? + } + } + ptr = ptr2; + ptr++; + } + + // Find First part - there is a boundary before it + + ptr = strstr(ptr2, Boundary); + + // Next should be crlf then part + + ptr = strstr(ptr, "\r\n"); + + if (ptr) + ptr += 2; // Over CRLF + + *Input = ptr; // Return first part or NULL + return _strdup(Boundary); +} + +BOOL unpackPart(char * Boundary, char ** Input, char ** Name, char ** Value, int * ValLen, char * End) +{ + // Format seems to be +/* + ------WebKitFormBoundaryABJaEbBWB5SuAHmq + Content-Disposition: form-data; name="Subj" + + subj + ------WebKitFormBoundaryABJaEbBWB5SuAHmq + Content-Disposition: form-data; name="myFile[]"; filename="exiftool.txt" + Content-Type: text/plain + + c:\exiftool "-filenameTo = _strdup(Value); + else if (strcmp(Name, "CC") == 0) + WebMail->CC = _strdup(Value); + else if (strcmp(Name, "Subj") == 0) + WebMail->Subject = _strdup(Value); + else if (strcmp(Name, "Type") == 0) + WebMail->Type = Value[0]; + else if (strcmp(Name, "BID") == 0) + WebMail->BID = _strdup(Value); + else if (strcmp(Name, "Msg") == 0) + WebMail->Body = _strdup(Value); + + else if (_memicmp(Name, "myFile[]", 8) == 0) + { + // Get File Name from param string - myFile[]"; filename="exiftool.txt" \r\nContent-Type: text/plain + + char * fn = strstr(Name, "filename="); + char * endfn; + if (fn) + { + fn += 10; + + endfn = strchr(fn, '"'); + if (endfn) + { + *endfn = 0; + + if (strlen(fn)) + { + WebMail->FileName[WebMail->Files] = _strdup(fn); + WebMail->FileBody[WebMail->Files] = malloc(ValLength); + memcpy(WebMail->FileBody[WebMail->Files], Value, ValLength); + WebMail->FileLen[WebMail->Files++] = ValLength; + } + } + } + } + + else if (_memicmp(Name, "myFile2[]", 8) == 0) + { + // Get File Name from param string - myFile[]"; filename="exiftool.txt" \r\nContent-Type: text/plain + + char * fn = strstr(Name, "filename="); + char * endfn; + if (fn) + { + fn += 10; + + endfn = strchr(fn, '"'); + if (endfn) + { + *endfn = 0; + + if (strlen(fn)) + { + WebMail->Header = malloc(ValLength + 1); + memcpy(WebMail->Header, Value, ValLength + 1); + WebMail->HeaderLen = RemoveLF(WebMail->Header, ValLength); + } + } + } + } + + else if (_memicmp(Name, "myFile3[]", 8) == 0) + { + // Get File Name from param string - myFile[]"; filename="exiftool.txt" \r\nContent-Type: text/plain + + char * fn = strstr(Name, "filename="); + char * endfn; + if (fn) + { + fn += 10; + + endfn = strchr(fn, '"'); + if (endfn) + { + *endfn = 0; + + if (strlen(fn)) + { + WebMail->Footer = malloc(ValLength + 1); + memcpy(WebMail->Footer, Value, ValLength + 1); + WebMail->FooterLen = RemoveLF(WebMail->Footer, ValLength); + } + } + } + } + + return TRUE; +} + + +struct HTTPConnectionInfo * AllocateWebMailSession() +{ + int KeyVal; + struct HTTPConnectionInfo * Session, * SaveNext; + time_t NOW = time(NULL); + + // First see if any session records havent been used for a while + + Session = WebSessionList; + + while (Session) + { + if (NOW - Session->WebMailLastUsed > 1200) // 20 Mins + { + SaveNext = Session->Next; + + // Release amy malloc'ed resouces + + ReleaseWebMailStruct(Session->WebMail); + + memset(Session, 0, sizeof(struct HTTPConnectionInfo)); + + Session->Next = SaveNext; + goto UseThis; + } + Session = Session->Next; + } + + Session = zalloc(sizeof(struct HTTPConnectionInfo)); + + if (Session == NULL) + return NULL; + + if (WebSessionList) + Session->Next = WebSessionList; + + WebSessionList = Session; + +UseThis: + + Session->WebMail = zalloc(sizeof(WebMailInfo)); + + KeyVal = ((rand() % 100) + 1); + + KeyVal *= (int)time(NULL); + + sprintf(Session->Key, "%c%08X", 'W', KeyVal); + + return Session; +} + +struct HTTPConnectionInfo * FindWMSession(char * Key) +{ + struct HTTPConnectionInfo * Session = WebSessionList; + + while (Session) + { + if (strcmp(Session->Key, Key) == 0) + { + Session->WebMailLastUsed = time(NULL); + return Session; + } + Session = Session->Next; + } + + return NULL; +} + + +// Build list of available forms + +VOID ProcessFormDir(char * FormSet, char * DirName, struct HtmlFormDir *** xxx, int * DirCount) +{ + struct HtmlFormDir * FormDir; + struct HtmlFormDir ** FormDirs = *xxx; + struct HtmlForm * Form; + char Search[MAX_PATH]; + int count = *DirCount; + +#ifdef WIN32 + HANDLE hFind = INVALID_HANDLE_VALUE; + WIN32_FIND_DATA ffd; +#else + DIR *dir; + struct dirent *entry; + char name[256]; +#endif + + FormDir = zalloc(sizeof (struct HtmlFormDir)); + + FormDir->DirName = _strdup(DirName); + FormDir->FormSet = _strdup(FormSet); + FormDirs=realloc(FormDirs, (count + 1) * sizeof(void *)); + FormDirs[count++] = FormDir; + + *DirCount = count; + *xxx = FormDirs; + + + // Scan Directory for .txt files + + sprintf(Search, "%s/%s/%s/*", GetBPQDirectory(), FormSet, DirName); + + // Find the first file in the directory. + +#ifdef WIN32 + + hFind = FindFirstFile(Search, &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + return; + + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + char Dir[MAX_PATH]; + + if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) + continue; + + // Recurse in subdir + + sprintf(Dir, "%s/%s", DirName, ffd.cFileName); + + ProcessFormDir(FormSet, Dir, &FormDir->Dirs, &FormDir->DirCount); + + continue; + + } + + // Add to list + + Form = zalloc(sizeof (struct HtmlForm)); + + Form->FileName = _strdup(ffd.cFileName); + + FormDir->Forms=realloc(FormDir->Forms, (FormDir->FormCount + 1) * sizeof(void *)); + FormDir->Forms[FormDir->FormCount++] = Form; + } + + while (FindNextFile(hFind, &ffd) != 0); + + FindClose(hFind); + +#else + + sprintf(Search, "%s/%s/%s", GetBPQDirectory(), FormSet, DirName); + + if (!(dir = opendir(Search))) + { + Debugprintf("%s %d %d", "cant open forms dir", errno, dir); + return ; + } + while ((entry = readdir(dir)) != NULL) + { + if (entry->d_type == DT_DIR) + { + char Dir[MAX_PATH]; + + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + + // Recurse in subdir + + sprintf(Dir, "%s/%s", DirName, entry->d_name); + + ProcessFormDir(FormSet, Dir, &FormDir->Dirs, &FormDir->DirCount); + continue; + } + + // Add to list + + Form = zalloc(sizeof (struct HtmlForm)); + + Form->FileName = _strdup(entry->d_name); + + FormDir->Forms=realloc(FormDir->Forms, (FormDir->FormCount + 1) * sizeof(void *)); + FormDir->Forms[FormDir->FormCount++] = Form; + } + closedir(dir); +#endif + return; +} + +int GetHTMLForms() +{ + int n = 0; + + while (FormDirList[n][0]) + GetHTMLFormSet(FormDirList[n++]); + + return 0; +} + +int GetHTMLFormSet(char * FormSet) +{ + int i; + +#ifdef WIN32 + + WIN32_FIND_DATA ffd; + char szDir[MAX_PATH]; + HANDLE hFind = INVALID_HANDLE_VALUE; + DWORD dwError=0; + + sprintf(szDir, "%s/%s/*", BPQDirectory, FormSet); + + // Find the first file in the directory. + + hFind = FindFirstFile(szDir, &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + { + // Accept either + return 0; + } + + // Scan all directories looking for file + + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) + continue; + + // Add to Directory List + + ProcessFormDir(FormSet, ffd.cFileName, &HtmlFormDirs, &FormDirCount); + } + } + + while (FindNextFile(hFind, &ffd) != 0); + + FindClose(hFind); + +#else + + DIR *dir; + struct dirent *entry; + char name[256]; + + sprintf(name, "%s/%s", BPQDirectory, FormSet); + + if (!(dir = opendir(name))) + { + Debugprintf("cant open forms dir %s %d %d", name, errno, dir); + } + else + { + while ((entry = readdir(dir)) != NULL) + { + if (entry->d_type == DT_DIR) + { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + + // Add to Directory List + + ProcessFormDir(FormSet, entry->d_name, &HtmlFormDirs, &FormDirCount); + } + } + closedir(dir); + } +#endif + + // List for testing + + return 0; + + Debugprintf("%d form dirs", FormDirCount); + + for (i = 0; i < FormDirCount; i++) + { + struct HtmlFormDir * Dir = HtmlFormDirs[i]; + + int j; + Debugprintf("%3d %s", Dir->FormCount, Dir->DirName); + + for (j = 0; j < Dir->FormCount; j++) + Debugprintf(" %s", Dir->Forms[j]->FileName); + + if (Dir->DirCount) + { + int k, l; + + for (l = 0; l < Dir->DirCount; l++) + { + Debugprintf("Subdir %3d %s", Dir->Dirs[l]->DirCount, Dir->Dirs[l]->DirName); + for (k = 0; k < Dir->Dirs[l]->FormCount; k++) + Debugprintf(" %s", Dir->Dirs[l]->Forms[k]->FileName); + } + } + } + + + return 0; +} + + +static int compare(const void *arg1, const void *arg2) +{ + // Compare Calls. Fortunately call is at start of stuct + + return _stricmp(*(char**)arg1 , *(char**)arg2); +} + + +int SendWebMailHeader(char * Reply, char * Key, struct HTTPConnectionInfo * Session) +{ + return SendWebMailHeaderEx(Reply, Key, Session, NULL); +} + + +int SendWebMailHeaderEx(char * Reply, char * Key, struct HTTPConnectionInfo * Session, char * Alert) +{ + // Ex includes an alert string to be sent before message + + struct UserInfo * User = Session->User; + char Messages[245000]; + int m; + struct MsgInfo * Msg; + char * ptr = Messages; + int n = NumberofMessages; //LineCount; + char Via[64]; + int Count = 0; + + Messages[0] = 0; + + if (Alert && Alert[0]) + ptr += sprintf(Messages, "", Alert, Key); + + ptr += sprintf(ptr, "%s", " # Date XX Len To @ From Subject\r\n\r\n"); + + for (m = LatestMsg; m >= 1; m--) + { + if (ptr > &Messages[244000]) + break; // protect buffer + + Msg = GetMsgFromNumber(m); + + if (Msg == 0 || Msg->type == 0 || Msg->status == 0) + continue; // Protect against corrupt messages + + if (Msg && CheckUserMsg(Msg, User->Call, User->flags & F_SYSOP)) + { + char UTF8Title[4096]; + char * EncodedTitle; + + // List if it is the right type and in the page range we want + + if (Session->WebMailTypes[0] && strchr(Session->WebMailTypes, Msg->type) == 0) + continue; + + // All Types or right Type. Check Mine Flag + + if (Session->WebMailMine) + { + // Only list if to or from me + + if (strcmp(User->Call, Msg->to) != 0 && strcmp(User->Call, Msg->from) != 0) + continue; + } + + if (Session->WebMailMyTX) + { + // Only list if to or from me + + if (strcmp(User->Call, Msg->from) != 0) + continue; + } + + if (Session->WebMailMyRX) + { + // Only list if to or from me + + if (strcmp(User->Call, Msg->to)!= 0) + continue; + } + + if (Count++ < Session->WebMailSkip) + continue; + + strcpy(Via, Msg->via); + strlop(Via, '.'); + + // make sure title is HTML safe (no < > etc) and UTF 8 encoded + + EncodedTitle = doXMLTransparency(Msg->title); + + memset(UTF8Title, 0, 4096); // In case convert fails part way through + ConvertTitletoUTF8(Session->WebMail, EncodedTitle, UTF8Title, 4095); + + free(EncodedTitle); + + ptr += sprintf(ptr, "
%6d %s %c%c %5d %-8s%-8s%-8s%s\r\n", + Key, Msg->number, Msg->number, + FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, + Msg->status, Msg->length, Msg->to, Via, + Msg->from, UTF8Title); + + n--; + + if (n == 0) + break; + } + } + + if (WebMailTemplate == NULL) + WebMailTemplate = GetTemplateFromFile(6, "WebMailPage.txt"); + + return sprintf(Reply, WebMailTemplate, BBSName, User->Call, Key, Key, Key, Key, Key, Key, Key, Key, Key, Key, Messages); +} + +int ViewWebMailMessage(struct HTTPConnectionInfo * Session, char * Reply, int Number, BOOL DisplayHTML) +{ + char * Key = Session->Key; + struct UserInfo * User = Session->User; + WebMailInfo * WebMail = Session->WebMail; + char * DisplayStyle; + + char Message[200000] = ""; + struct MsgInfo * Msg; + char * ptr = Message; + char * MsgBytes, * Save; + int msgLen; + + char FullTo[100]; + char UTF8Title[4096]; + int Index; + char * crcrptr; + char DownLoad[256] = ""; + + DisplayStyle = "textarea"; // Prevents interpretation of html and xml + + Msg = GetMsgFromNumber(Number); + + if (Msg == NULL) + { + ptr += sprintf(ptr, "Message %d not found\r\n", Number); + return sprintf(Reply, WebMailTemplate, BBSName, User->Call, Key, Key, Key, Key, Key, Key, Key, Message); + } + + // New Display so free any old values + + FreeWebMailFields(WebMail); + + WebMail->CurrentMessageIndex = Number; + + + if (!CheckUserMsg(Msg, User->Call, User->flags & F_SYSOP)) + { + ptr += sprintf(ptr, "Message %d not for you\r", Number); + return sprintf(Reply, WebMailTemplate, BBSName, User->Call, Key, Key, Key, Key, Key, Key, Key, Message); + } + + if (_stricmp(Msg->to, "RMS") == 0) + sprintf(FullTo, "RMS:%s", Msg->via); + else + if (Msg->to[0] == 0) + sprintf(FullTo, "smtp:%s", Msg->via); + else + strcpy(FullTo, Msg->to); + + // make sure title is UTF 8 encoded + + memset(UTF8Title, 0, 4096); // In case convert fails part way through + ConvertTitletoUTF8(Session->WebMail, Msg->title, UTF8Title, 4095); + + // if a B2 message diplay B2 Header instead of a locally generated one + + if ((Msg->B2Flags & B2Msg) == 0) + { + ptr += sprintf(ptr, "From: %s%s\nTo: %s\nType/Status: %c%c\nDate/Time: %s\nBid: %s\nTitle: %s\n\n", + Msg->from, Msg->emailfrom, FullTo, Msg->type, Msg->status, FormatDateAndTime((time_t)Msg->datecreated, FALSE), Msg->bid, UTF8Title); + } + + MsgBytes = Save = ReadMessageFile(Number); + + msgLen = Msg->length; + + if (Msg->type == 'P') + Index = PMSG; + else if (Msg->type == 'B') + Index = BMSG; + else + Index = TMSG; + + if (MsgBytes) + { + if (Msg->B2Flags & B2Msg) + { + char * ptr1; + + // if message has attachments, display them if plain text + + if (Msg->B2Flags & Attachments) + { + int BodyLen, NewLen; + int i; + char *ptr2, *attptr; + + sprintf(DownLoad, "Save Attachments", Key, Msg->number); + + WebMail->Files = 0; + + ptr1 = MsgBytes; + + // ptr += sprintf(ptr, "Message has Attachments\r\n\r\n"); + + while(*ptr1 != 13) + { + ptr2 = strchr(ptr1, 10); // Find CR + + if (memcmp(ptr1, "Body: ", 6) == 0) + { + BodyLen = atoi(&ptr1[6]); + } + + if (memcmp(ptr1, "File: ", 6) == 0) + { + char * ptr3 = strchr(&ptr1[6], ' '); // Find Space + *(ptr2 - 1) = 0; + + WebMail->FileLen[WebMail->Files] = atoi(&ptr1[6]); + WebMail->FileName[WebMail->Files++] = _strdup(&ptr3[1]); + *(ptr2 - 1) = ' '; // put space back + } + + ptr1 = ptr2; + ptr1++; + } + + ptr1 += 2; // Over Blank Line and Separator + + // ptr1 is pointing to body. Save for possible reply + + WebMail->Body = malloc(BodyLen + 2); + memcpy(WebMail->Body, ptr1, BodyLen); + WebMail->Body[BodyLen] = 0; + + *(ptr1 + BodyLen) = 0; + + ptr += sprintf(ptr, "%s", MsgBytes); // B2 Header and Body + + ptr1 += BodyLen + 2; // to first file + + // Save pointers to file + + attptr = ptr1; + + for (i = 0; i < WebMail->Files; i++) + { + WebMail->FileBody[i] = malloc(WebMail->FileLen[i]); + memcpy(WebMail->FileBody[i], attptr, WebMail->FileLen[i]); + attptr += (WebMail->FileLen[i] + 2); + } + + // if first (only??) attachment is XML and filename + // starts "RMS_Express_Form" process as HTML Form + + if (DisplayHTML && _memicmp(ptr1, "FileName[0], "RMS_Express_Form_", 16) == 0) + { + int Len = DisplayWebForm(Session, Msg, WebMail->FileName[0], ptr1, Reply, MsgBytes, BodyLen + 32); // 32 for added "has attachments" + free(MsgBytes); + + // Flag as read + + if ((_stricmp(Msg->to, User->Call) == 0) || ((User->flags & F_SYSOP) && (_stricmp(Msg->to, "SYSOP") == 0))) + { + if ((Msg->status != 'K') && (Msg->status != 'H') && (Msg->status != 'F') && (Msg->status != 'D')) + { + if (Msg->status != 'Y') + { + Msg->status = 'Y'; + Msg->datechanged=time(NULL); + SaveMessageDatabase(); + SendMessageReadEvent(Session->Callsign, Msg); + } + } + } + + return Len; + } + + for (i = 0; i < WebMail->Files; i++) + { + int n; + char * p = ptr1; + char c; + + // Check if message is probably binary + + int BinCount = 0; + + NewLen = WebMail->FileLen[i]; + + for (n = 0; n < NewLen; n++) + { + c = *p; + + if (c==0 || (c & 128)) + BinCount++; + + p++; + + } + + if (BinCount > NewLen/10) + { + // File is probably Binary + + ptr += sprintf(ptr, "\rAttachment %s is a binary file\r", WebMail->FileName[i]); + } + else + { + *(ptr1 + NewLen) = 0; + ptr += sprintf(ptr, "\rAttachment %s\r\r", WebMail->FileName[i]); + RemoveLF(ptr1, NewLen + 1); // Removes LF after CR but not on its own + + ptr += sprintf(ptr, "%s\r\r", ptr1); + + User->Total.MsgsSent[Index] ++; + User->Total.BytesForwardedOut[Index] += NewLen; + } + + ptr1 += WebMail->FileLen[i]; + ptr1 +=2; // Over separator + } + + free(Save); + + ptr += sprintf(ptr, "\r\r[End of Message #%d from %s]\r", Number, Msg->from); + + RemoveLF(Message, (int)strlen(Message) + 1); // Removes LF after CR but not on its own + + if ((_stricmp(Msg->to, User->Call) == 0) || ((User->flags & F_SYSOP) && (_stricmp(Msg->to, "SYSOP") == 0))) + { + if ((Msg->status != 'K') && (Msg->status != 'H') && (Msg->status != 'F') && (Msg->status != 'D')) + { + if (Msg->status != 'Y') + { + Msg->status = 'Y'; + Msg->datechanged=time(NULL); + SaveMessageDatabase(); + SendMessageReadEvent(Session->Callsign, Msg); + } + } + } + + if (DisplayHTML && stristr(Message, "")) + DisplayStyle = "div"; // Use div so HTML and XML are interpreted + + return sprintf(Reply, WebMailMsgTemplate, BBSName, User->Call, Msg->number, Msg->number, Key, Msg->number, Key, DownLoad, Key, Key, Key, DisplayStyle, Message, DisplayStyle); + } + + // Remove B2 Headers (up to the File: Line) + + // ptr1 = strstr(MsgBytes, "Body:"); + + // if (ptr1) + // MsgBytes = ptr1; + } + + // Body may have cr cr lf which causes double space + + crcrptr = strstr(MsgBytes, "\r\r\n"); + + while (crcrptr) + { + *crcrptr = ' '; + crcrptr = strstr(crcrptr, "\r\r\n"); + } + + // Remove lf chars + + msgLen = RemoveLF(MsgBytes, msgLen); + + User->Total.MsgsSent[Index] ++; + // User->Total.BytesForwardedOut[Index] += Length; + + // if body not UTF-8, convert it + + if (WebIsUTF8(MsgBytes, msgLen) == FALSE) + { + int code = TrytoGuessCode(MsgBytes, msgLen); + + UCHAR * UTF = malloc(msgLen * 3); + + if (code == 437) + msgLen = Convert437toUTF8(MsgBytes, msgLen, UTF); + else if (code == 1251) + msgLen = Convert1251toUTF8(MsgBytes, msgLen, UTF); + else + msgLen = Convert1252toUTF8(MsgBytes, msgLen, UTF); + + free(MsgBytes); + Save = MsgBytes = UTF; + + MsgBytes[msgLen] = 0; + } + + // ptr += sprintf(ptr, "%s", MsgBytes); + + memcpy(ptr, MsgBytes, msgLen); + ptr += msgLen; + ptr[0] = 0; + + free(Save); + + ptr += sprintf(ptr, "\r\r[End of Message #%d from %s]\r", Number, Msg->from); + + if ((_stricmp(Msg->to, User->Call) == 0) || ((User->flags & F_SYSOP) && (_stricmp(Msg->to, "SYSOP") == 0))) + { + if ((Msg->status != 'K') && (Msg->status != 'H') && (Msg->status != 'F') && (Msg->status != 'D')) + { + if (Msg->status != 'Y') + { + Msg->status = 'Y'; + Msg->datechanged=time(NULL); + SaveMessageDatabase(); + SendMessageReadEvent(Session->Callsign, Msg); + } + } + } + } + else + { + ptr += sprintf(ptr, "File for Message %d not found\r", Number); + } + + if (DisplayHTML && stristr(Message, "")) + DisplayStyle = "div"; // Use div so HTML and XML are interpreted + + + return sprintf(Reply, WebMailMsgTemplate, BBSName, User->Call, Msg->number, Msg->number, Key, Msg->number, Key, DownLoad, Key, Key, Key, DisplayStyle, Message, DisplayStyle); +} + +int KillWebMailMessage(char * Reply, char * Key, struct UserInfo * User, int Number) +{ + struct MsgInfo * Msg; + char Message[100] = ""; + + Msg = GetMsgFromNumber(Number); + + if (Msg == NULL) + { + sprintf(Message, "Message %d not found", Number); + goto returnit; + } + + if (OkToKillMessage(User->flags & F_SYSOP, User->Call, Msg)) + { + FlagAsKilled(Msg, TRUE); + sprintf(Message, "Message #%d Killed\r", Number); + goto returnit; + } + + sprintf(Message, "Not your message\r"); + +returnit: + return sprintf(Reply, WebMailMsgTemplate, BBSName, User->Call, Msg->number, Msg->number, Key, Msg->number, Key, "", Key, Key, Key, "div", Message, "div"); +} + +void freeKeys(KeyValues * Keys) +{ + while (Keys->Key) + { + free(Keys->Key); + free(Keys->Value); + Keys++; + } +} + +void FreeWebMailFields(WebMailInfo * WebMail) +{ + // release any malloc'ed resources + + int i; + char * SaveReply; + int * SaveRlen; + + if (WebMail == NULL) + return; + + if (WebMail->txtFile) + free(WebMail->txtFile); + + if (WebMail->txtFileName) + free(WebMail->txtFileName); + + if (WebMail->InputHTMLName) + free(WebMail->InputHTMLName); + + if (WebMail->DisplayHTMLName) + free(WebMail->DisplayHTMLName); + + if (WebMail->ReplyHTMLName) + free(WebMail->ReplyHTMLName); + + if (WebMail->To) + free(WebMail->To); + if (WebMail->CC) + free(WebMail->CC); + if (WebMail->Subject) + free(WebMail->Subject); + if (WebMail->BID) + free(WebMail->BID); + if (WebMail->Body) + free(WebMail->Body); + if (WebMail->XML) + free(WebMail->XML); + if (WebMail->XMLName) + free(WebMail->XMLName); + + if (WebMail->OrigTo) + free(WebMail->OrigTo); + if (WebMail->OrigSubject) + free(WebMail->OrigSubject); + if (WebMail->OrigBID) + free(WebMail->OrigBID); + if (WebMail->OrigBody) + free(WebMail->OrigBody); + + freeKeys(WebMail->txtKeys); + freeKeys(WebMail->XMLKeys); + + for (i = 0; i < WebMail->Files; i++) + { + free(WebMail->FileBody[i]); + free(WebMail->FileName[i]); + } + + if (WebMail->Header) + free(WebMail->Header); + if (WebMail->Footer) + free(WebMail->Footer); + + SaveReply = WebMail->Reply; + SaveRlen = WebMail->RLen; + +#ifndef WIN32 + if (WebMail->iconv_toUTF8) + iconv_close(WebMail->iconv_toUTF8); +#endif + + memset(WebMail, 0, sizeof(WebMailInfo)); + + WebMail->Reply = SaveReply; + WebMail->RLen = SaveRlen; + + return; +} + + +void ProcessWebMailMessage(struct HTTPConnectionInfo * Session, char * Key, BOOL LOCAL, char * Method, char * NodeURL, char * input, char * Reply, int * RLen, int InputLen) +{ + char * URLParams = strlop(Key, '&'); + int ReplyLen; + char Appl = 'M'; + + // Webmail doesn't use the normal Mail Key. + + // webscript.js doesn't need a key + + if (_stricmp(NodeURL, "/WebMail/webscript.js") == 0) + { + if (jsTemplate) + free(jsTemplate); + + jsTemplate = GetTemplateFromFile(2, "webscript.js"); + + ReplyLen = sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n" + "Cache-Control: max-age=60\r\nContent-Type: text/javascript\r\n\r\n%s", (int)strlen(jsTemplate), jsTemplate); + *RLen = ReplyLen; + return; + } + + // Neither do js or file downloads + + // This could be a request for a Template file + // WebMail/Local_Templates/My Forms/inc/logo_ad63.png + // WebMail/Standard Templates/ + + + if (_memicmp(NodeURL, "/WebMail/Local", 14) == 0 || (_memicmp(NodeURL, "/WebMail/Standard", 17) == 0)) + { + int FileSize; + char * MsgBytes; + char MsgFile[512]; + FILE * hFile; + size_t ReadLen; + char TimeString[64]; + char FileTimeString[64]; + struct stat STAT; + char * FN = &NodeURL[9]; + char * fileBit = FN; + char * ext; + char Type[64] = "Content-Type: text/html\r\n"; + + UndoTransparency(FN); + ext = strchr(FN, '.'); + + sprintf(MsgFile, "%s/%s", BPQDirectory, FN); + + while (strchr(fileBit, '/')) + fileBit = strlop(fileBit, '/'); + + if (stat(MsgFile, &STAT) == -1) + { + *RLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n"); + return; + } + + hFile = fopen(MsgFile, "rb"); + + if (hFile == 0) + { + *RLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n"); + return; + } + + FileSize = STAT.st_size; + MsgBytes = malloc(FileSize + 1); + ReadLen = fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + + FormatTime2(FileTimeString, STAT.st_ctime); + FormatTime2(TimeString, time(NULL)); + + ext++; + + if (_stricmp(ext, "js") == 0) + strcpy(Type, "Content-Type: text/javascript\r\n"); + + if (_stricmp(ext, "css") == 0) + strcpy(Type, "Content-Type: text/css\r\n"); + + if (_stricmp(ext, "pdf") == 0) + strcpy(Type, "Content-Type: application/pdf\r\n"); + + if (_stricmp(ext, "jpg") == 0 || _stricmp(ext, "jpeg") == 0 || _stricmp(ext, "png") == 0 || + _stricmp(ext, "gif") == 0 || _stricmp(ext, "bmp") == 0 || _stricmp(ext, "ico") == 0) + strcpy(Type, "Content-Type: image\r\n"); + + // File may be binary so output header then copy in message + + *RLen = sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n" + "%s" + "Date: %s\r\n" + "Last-Modified: %s\r\n" + "\r\n", FileSize, Type,TimeString, FileTimeString); + + memcpy(&Reply[*RLen], MsgBytes, FileSize); + *RLen += FileSize; + free (MsgBytes); + return; + } + + // + + if (_memicmp(NodeURL, "/WebMail/WMFile/", 16) == 0) + { + int FileSize; + char * MsgBytes; + char MsgFile[512]; + FILE * hFile; + size_t ReadLen; + char TimeString[64]; + char FileTimeString[64]; + struct stat STAT; + char * FN = &NodeURL[16]; + char * fileBit = FN; + char * ext; + char Type[64] = "Content-Type: text/html\r\n"; + + + UndoTransparency(FN); + ext = strchr(FN, '.'); + + sprintf(MsgFile, "%s/%s", BPQDirectory, FN); + + while (strchr(fileBit, '/')) + fileBit = strlop(fileBit, '/'); + + if (stat(MsgFile, &STAT) == -1) + { + *RLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n"); + return; + } + + hFile = fopen(MsgFile, "rb"); + + if (hFile == 0) + { + *RLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n"); + return; + } + + FileSize = STAT.st_size; + MsgBytes = malloc(FileSize + 1); + ReadLen = fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + + FormatTime2(FileTimeString, STAT.st_ctime); + FormatTime2(TimeString, time(NULL)); + + ext++; + + if (_stricmp(ext, "js") == 0) + strcpy(Type, "Content-Type: text/javascript\r\n"); + + if (_stricmp(ext, "css") == 0) + strcpy(Type, "Content-Type: text/css\r\n"); + + if (_stricmp(ext, "pdf") == 0) + strcpy(Type, "Content-Type: application/pdf\r\n"); + + if (_stricmp(ext, "jpg") == 0 || _stricmp(ext, "jpeg") == 0 || _stricmp(ext, "png") == 0 || + _stricmp(ext, "gif") == 0 || _stricmp(ext, "bmp") == 0 || _stricmp(ext, "ico") == 0) + strcpy(Type, "Content-Type: image\r\n"); + + // File may be binary so output header then copy in message + + *RLen = sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n" + "%s" + "Date: %s\r\n" + "Last-Modified: %s\r\n" + "\r\n", FileSize, Type,TimeString, FileTimeString); + + memcpy(&Reply[*RLen], MsgBytes, FileSize); + *RLen += FileSize; + free (MsgBytes); + return; + } + + Session = NULL; + + if (Key && Key[0]) + Session = FindWMSession(Key); + + if (Session == NULL) + { + // Lost Session + + if (LOCAL) + { + Session = AllocateWebMailSession(); + + Key = Session->Key; + + if (SYSOPCall[0]) + Session->User = LookupCall(SYSOPCall); + else + Session->User = LookupCall(BBSName); + + if (Session->User) + { + strcpy(NodeURL, "/WebMail/WebMail"); + Session->WebMailSkip = 0; + Session->WebMailLastUsed = time(NULL); + } + } + else + { + // Send Login Page unless Signon request + + if (_stricmp(NodeURL, "/WebMail/Signon") != 0 || strcmp(Method, "POST") != 0) + { + ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName); + *RLen = ReplyLen; + return; + } + } + } + + if (strcmp(Method, "POST") == 0) + { + if (_stricmp(NodeURL, "/WebMail/Signon") == 0) + { + char * msg = strstr(input, "\r\n\r\n"); // End of headers + char * user, * password, * Key; + char Msg[128]; + int n; + + if (msg) + { + struct UserInfo * User; + + if (strstr(msg, "Cancel=Cancel")) + { + *RLen = sprintf(Reply, ""); + return; + } + // Webmail Gets Here with a dummy Session + + Session = AllocateWebMailSession(); + Session->WebMail->Reply = Reply; + Session->WebMail->RLen = RLen; + + + Key = Session->Key; + + user = strtok_s(&msg[9], "&", &Key); + password = strtok_s(NULL, "=", &Key); + password = Key; + + Session->User = User = LookupCall(user); + + if (User) + { + // Check Password + + if (password[0] && strcmp(User->pass, password) == 0) + { + // send Message Index + + Session->WebMailLastUsed = time(NULL); + Session->WebMailSkip = 0; + Session->WebMailMyTX = FALSE; + Session->WebMailMyRX = FALSE; + Session->WebMailMine = FALSE; + + if (WebMailTemplate) + { + free(WebMailTemplate); + WebMailTemplate = NULL; + } + + if (User->flags & F_Excluded) + { + n = sprintf_s(Msg, sizeof(Msg), "Webmail Connect from %s Rejected by Exclude Flag", _strupr(user)); + WriteLogLine(NULL, '|',Msg, n, LOG_BBS); + ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName); + *RLen = ReplyLen; + return; + } + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + n=sprintf_s(Msg, sizeof(Msg), "Webmail Connect from %s", _strupr(user)); + WriteLogLine(NULL, '|',Msg, n, LOG_BBS); + + return; + } + + } + + // Bad User or Pass + + ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName); + *RLen = ReplyLen; + return; + } + } + + Session->WebMail->Reply = Reply; + Session->WebMail->RLen = RLen; + + if (_stricmp(NodeURL, "/WebMail/EMSave") == 0) + { + // Save New Message + + SaveNewMessage(Session, input, Reply, RLen, Key, InputLen); + return; + } + + if (_stricmp(NodeURL, "/WebMail/Submit") == 0) + { + // Get the POST data from the page and place in message + + char * param = strstr(input, "\r\n\r\n"); // End of headers + WebMailInfo * WebMail = Session->WebMail; + + if (WebMail == NULL) + return; // Can't proceed if we have no info on form + + ProcessFormInput(Session, input, Reply, RLen, InputLen); + return; + } + + if (_stricmp(NodeURL, "/WebMail/FormMsgSave") == 0) + { + // Save New Message + + SaveTemplateMessage(Session, input, Reply, RLen, Key); + return; + } + + if (_stricmp(NodeURL, "/WebMail/GetTemplates") == 0) + { + SendTemplateSelectScreen(Session, input, InputLen); + return; + } + + // End of POST section + } + + if (_stricmp(NodeURL, "/WebMail/WMLogout") == 0) + { + Session->Key[0] = 0; + Session->WebMailLastUsed = 0; + ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName); + *RLen = ReplyLen; + return; + } + + if ((_stricmp(NodeURL, "/WebMail/MailEntry") == 0) || + (_stricmp(NodeURL, "/WebMail") == 0) || + (_stricmp(NodeURL, "/WebMail/") == 0)) + { + // Entry from Menu if signed in, continue. If not and Localhost + // signin as sysop. + + if (Session->User == NULL) + { + // Not yet signed in + + if (LOCAL) + { + // Webmail Gets Here with a dummy Session + + Session = AllocateWebMailSession(); + Session->WebMail->Reply = Reply; + Session->WebMail->RLen = RLen; + + Key = Session->Key; + + if (SYSOPCall[0]) + Session->User = LookupCall(SYSOPCall); + else + Session->User = LookupCall(BBSName); + + if (Session->User) + { + strcpy(NodeURL, "/WebMail/WebMail"); + Session->WebMailSkip = 0; + Session->WebMailLastUsed = time(NULL); + } + } + else + { + // Send Login Page + + ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName); + *RLen = ReplyLen; + return; + } + } + } + + Session->WebMail->Reply = Reply; + Session->WebMail->RLen = RLen; + + if (_stricmp(NodeURL, "/WebMail/WebMail") == 0) + { + if (WebMailTemplate) + { + free(WebMailTemplate); + WebMailTemplate = NULL; + } + + Session->WebMailSkip = 0; + Session->WebMailMine = FALSE; + Session->WebMailMyTX = FALSE; + Session->WebMailMyRX = FALSE; + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + if (_stricmp(NodeURL, "/WebMail/WMAll") == 0) + { + Session->WebMailSkip = 0; + Session->WebMailTypes[0] = 0; + Session->WebMailMine = FALSE; + Session->WebMailMyTX = FALSE; + Session->WebMailMyRX = FALSE; + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + if (_stricmp(NodeURL, "/WebMail/WMB") == 0) + { + Session->WebMailSkip = 0; + strcpy(Session->WebMailTypes, "B"); + Session->WebMailMine = FALSE; + Session->WebMailMyTX = FALSE; + Session->WebMailMyRX = FALSE; + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + if (_stricmp(NodeURL, "/WebMail/WMP") == 0) + { + Session->WebMailSkip = 0; + strcpy(Session->WebMailTypes, "P"); + Session->WebMailMine = FALSE; + Session->WebMailMyTX = FALSE; + Session->WebMailMyRX = FALSE; + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + if (_stricmp(NodeURL, "/WebMail/WMT") == 0) + { + Session->WebMailSkip = 0; + strcpy(Session->WebMailTypes, "T"); + Session->WebMailMine = FALSE; + Session->WebMailMyTX = FALSE; + Session->WebMailMyRX = FALSE; + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + if (_stricmp(NodeURL, "/WebMail/WMMine") == 0) + { + Session->WebMailSkip = 0; + Session->WebMailTypes[0] = 0; + Session->WebMailMine = TRUE; + Session->WebMailMyTX = FALSE; + Session->WebMailMyRX = FALSE; + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + if (_stricmp(NodeURL, "/WebMail/WMtoMe") == 0) + { + Session->WebMailSkip = 0; + Session->WebMailTypes[0] = 0; + Session->WebMailMine = FALSE; + Session->WebMailMyTX = FALSE; + Session->WebMailMyRX = TRUE; + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + if (_stricmp(NodeURL, "/WebMail/WMfromMe") == 0) + { + Session->WebMailSkip = 0; + Session->WebMailTypes[0] = 0; + Session->WebMailMine = TRUE; + Session->WebMailMyTX = TRUE; + Session->WebMailMyRX = FALSE; + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + + if (_stricmp(NodeURL, "/WebMail/WMSame") == 0) + { + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + if (_stricmp(NodeURL, "/WebMail/WMAuto") == 0) + { + // Auto Refresh Version of index page. Uses Web Sockets + + char Page[4096]; + + char WebSockPage[] = + "\r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + "\r\n" + "\r\n" + + "\r\n" + "WebMail \r\n" + "\r\n" + + "\r\n" + "

%s Webmail Interface - User %s - Message List

\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "
BullsPersonalNTSAll TypesMineMy SentMy RxedAuto RefreshSend MessageLogoutNode Menu
\r\n" + "
\r\n" + + "
Waiting for data...
\r\n" + "\r\n"; + + sprintf(Page, WebSockPage, Key, Key ,BBSName, Session->User->Call, Key, Key, Key, Key, Key, Key, Key, Key, Key, Key); + + *RLen = sprintf(Reply, "%s", Page); + return; + } + + + if (memcmp(NodeURL, "/WebMail/QuoteOriginal/", 15) == 0) + { + // Reply to Message + + int n, len; + struct MsgInfo * Msg; + char Message[100] = ""; + char Title[100]; + char * MsgBytes, * Save, * NewBytes; + char * ptr; + char * ptr1, * ptr2; + char * EncodedTitle; + + n = Session->WebMail->CurrentMessageIndex; + + Msg = GetMsgFromNumber(n); + + if (Msg == NULL) + { + sprintf(Message, "Message %d not found", n); + *RLen = sprintf(Reply, "%s", Message); + return; + } + + Session->WebMail->Msg = Msg; + + if (stristr(Msg->title, "Re:") == 0) + sprintf(Title, "Re:%s", Msg->title); + else + sprintf(Title, "%s", Msg->title); + + MsgBytes = Save = ReadMessageFile(n); + + + ptr = NewBytes = malloc((Msg->length * 2) + 256); + + // Copy a line at a time with "> " in front of each + + ptr += sprintf(ptr, "%s", "\r\n\r\n\r\n\r\n\r\nOriginal Message\r\n\r\n> "); + + ptr1 = ptr2 = MsgBytes; + len = (int)strlen(MsgBytes); + + while (len-- > 0) + { + *ptr++ = *ptr1; + + if (*(ptr1) == '\n') + { + *ptr++ = '>'; + *ptr++ = ' '; + } + + ptr1++; + } + + *ptr++ = 0; + + EncodedTitle = doXMLTransparency(Msg->title); + + *RLen = sprintf(Reply, MsgInputPage, Key, Msg->from, "", EncodedTitle , NewBytes); + + free(EncodedTitle); + + free(MsgBytes); + free(NewBytes); + + return; + } + + + + if (memcmp(NodeURL, "/WebMail/Reply/", 15) == 0) + { + // Reply to Message + + int n = atoi(&NodeURL[15]); + struct MsgInfo * Msg; + char Message[100] = ""; + char Title[100]; + char * EncodedTitle; + + // Quote Original + + char Button[] = + "      " + ""; + + char Temp[1024]; + char ReplyAddr[128]; + + Msg = GetMsgFromNumber(n); + + if (Msg == NULL) + { + sprintf(Message, "Message %d not found", n); + *RLen = sprintf(Reply, "%s", Message); + return; + } + + Session->WebMail->Msg = Msg; + + // See if the message was displayed in an HTML form with a reply template + + *RLen = ReplyToFormsMessage(Session, Msg, Reply, FALSE); + + // If couldn't build reply form use normal text reply + + if (*RLen) + return; + + + sprintf(Temp, Button, Key); + + if (stristr(Msg->title, "Re:") == 0) + sprintf(Title, "Re:%s", Msg->title); + else + sprintf(Title, "%s", Msg->title); + + strcpy(ReplyAddr, Msg->from); + strcat(ReplyAddr, Msg->emailfrom); + + EncodedTitle = doXMLTransparency(Msg->title); + + *RLen = sprintf(Reply, MsgInputPage, Key, Msg->from, Temp, EncodedTitle , ""); + + free(EncodedTitle); + return; + } + + if (strcmp(NodeURL, "/WebMail/WM") == 0) + { + // Read Message + + int n = 0; + + if (URLParams) + n = atoi(URLParams); + + if (WebMailMsgTemplate) + free(WebMailMsgTemplate); + + WebMailMsgTemplate = GetTemplateFromFile(5, "WebMailMsg.txt"); + + *RLen = ViewWebMailMessage(Session, Reply, n, TRUE); + + return; + } + + if (strcmp(NodeURL, "/WebMail/WMPrev") == 0) + { + // Read Previous Message + + int m; + struct MsgInfo * Msg; + struct UserInfo * User = Session->User; + + + for (m = Session->WebMail->CurrentMessageIndex - 1; m >= 1; m--) + { + + Msg = GetMsgFromNumber(m); + + if (Msg == 0 || Msg->type == 0 || Msg->status == 0) + continue; // Protect against corrupt messages + + if (Msg && CheckUserMsg(Msg, User->Call, User->flags & F_SYSOP)) + { + // Display if it is the right type and in the page range we want + + if (Session->WebMailTypes[0] && strchr(Session->WebMailTypes, Msg->type) == 0) + continue; + + // All Types or right Type. Check Mine Flag + + if (Session->WebMailMine) + { + // Only list if to or from me + + if (strcmp(User->Call, Msg->to) != 0 && strcmp(User->Call, Msg->from) != 0) + continue; + } + + if (Session->WebMailMyTX) + { + // Only list if to or from me + + if (strcmp(User->Call, Msg->from) != 0) + continue; + } + + if (Session->WebMailMyRX) + { + // Only list if to or from me + + if (strcmp(User->Call, Msg->to) != 0) + continue; + } + *RLen = ViewWebMailMessage(Session, Reply, m, TRUE); + + return; + } + } + + // No More + + *RLen = sprintf(Reply, "", Session->Key); + return; + + } + + if (strcmp(NodeURL, "/WebMail/WMNext") == 0) + { + // Read Previous Message + + int m; + struct MsgInfo * Msg; + struct UserInfo * User = Session->User; + + for (m = Session->WebMail->CurrentMessageIndex + 1; m <= LatestMsg; m++) + { + Msg = GetMsgFromNumber(m); + + if (Msg == 0 || Msg->type == 0 || Msg->status == 0) + continue; // Protect against corrupt messages + + if (Msg && CheckUserMsg(Msg, User->Call, User->flags & F_SYSOP)) + { + // Display if it is the right type and in the page range we want + + if (Session->WebMailTypes[0] && strchr(Session->WebMailTypes, Msg->type) == 0) + continue; + + // All Types or right Type. Check Mine Flag + + if (Session->WebMailMine) + { + // Only list if to or from me + + if (strcmp(User->Call, Msg->to) != 0 && strcmp(User->Call, Msg->from) != 0) + continue; + } + + if (Session->WebMailMyTX) + { + // Only list if to or from me + + if (strcmp(User->Call, Msg->from) != 0) + continue; + } + + if (Session->WebMailMyRX) + { + // Only list if to or from me + + if (strcmp(User->Call, Msg->to) != 0) + continue; + } + *RLen = ViewWebMailMessage(Session, Reply, m, TRUE); + + return; + } + } + + // No More + + *RLen = sprintf(Reply, "", Session->Key); + return; + + } + + + if (strcmp(NodeURL, "/WebMail/DisplayText") == 0) + { + // Read Message + + int n = 0; + + if (URLParams) + n = atoi(URLParams); + + if (WebMailMsgTemplate) + free(WebMailMsgTemplate); + + WebMailMsgTemplate = GetTemplateFromFile(5, "WebMailMsg.txt"); + + *RLen = ViewWebMailMessage(Session, Reply, n, FALSE); + + return; + } + if (memcmp(NodeURL, "/WebMail/WMDel/", 15) == 0) + { + // Kill Message + + int n = atoi(&NodeURL[15]); + + *RLen = KillWebMailMessage(Reply, Session->Key, Session->User, n); + + return; + } + + if (_stricmp(NodeURL, "/WebMail/NewMsg") == 0) + { + // Add HTML Template Button if we have any HTML Form + + char Button[] = + "      " + ""; + + char Temp[1024]; + + FreeWebMailFields(Session->WebMail); // Tidy up for new message + + sprintf(Temp, Button, Key); + + if (FormDirCount == 0) + *RLen = sprintf(Reply, MsgInputPage, Key, "", "", "", ""); + else + *RLen = sprintf(Reply, MsgInputPage, Key, "", Temp, "", ""); + + return; + } + + if (_memicmp(NodeURL, "/WebMail/GetPage/", 17) == 0) + { + // Read and Parse Template File + + GetPage(Session, NodeURL); + return; + } + + if (_memicmp(NodeURL, "/WebMail/GetList/", 17) == 0) + { + // Send Select Template Popup + + char * SubDir; + int DirNo = 0; + int SubDirNo = 0; + char popup[10000]; + + char popuphddr[] = + + "" + "" + "

" + "Select Required Template from %s

" + "

"); + + *RLen = sprintf(Reply, "%s", popup); + return; + } + + if (_stricmp(NodeURL, "/WebMail/DL") == 0) + { + getAttachmentList(Session, Reply, RLen, URLParams); + return; + } + + if (_stricmp(NodeURL, "/WebMail/GetDownLoad") == 0) + { + DownloadAttachments(Session, Reply, RLen, URLParams); + return; + } + + if (_stricmp(NodeURL, "/WebMail/DoSelect") == 0) + { + // User has selected item from Template "; + + char NewGroup [] = + "" + "

"); + + *WebMail->RLen = sprintf(WebMail->Reply, "%s", popup); + + free(Boundary); + return; +} + +static char WinlinkAddr[] = "WINLINK.ORG"; + +VOID SaveNewMessage(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest, int InputLen) +{ + int i, ReplyLen = 0; + struct MsgInfo * Msg; + FILE * hFile; + int Template=0; + char * via = NULL; + BIDRec * BIDRec; + char MsgFile[MAX_PATH]; + size_t WriteLen=0; + char * HDest; + char * HDestCopy; + char * HDestRest; + char * Vptr = NULL; + char * FileList = NULL; + char Prompt[256] = "Message Saved"; + char OrigTo[256]; + WebMailInfo * WebMail = Session->WebMail; + struct UserInfo * user; + CIRCUIT conn; + + // So we can have attachments input is now Content-Type: multipart/form-data; + + char * Input; + size_t MsgLen = 0; + char * Boundary; + + strcpy(conn.Callsign, Session->User->Call); + + Input = MsgPtr; + + Boundary = initMultipartUnpack(&Input); + + if (Boundary == NULL) + return; // Can't work without one + + // Input points to start of part. Normally preceeded by \r\n which is Boundary Terminator. If preceeded by -- we have used last part + + while(Input && Input[-1] != '-') + { + char * Name, * Value; + int ValLen; + + if (unpackPart(Boundary, &Input, &Name, &Value, &ValLen, MsgPtr + InputLen) == FALSE) + { + // ReportCorrupt(WebMail); + free(Boundary); + return; + } + if (SaveInputValue(WebMail, Name, Value, ValLen) == FALSE) + { + *RLen = sprintf(Reply, "", Session->Key); + return; + } + } + + + if (WebMail->txtFileName) + { + // Processing Form Input + + SaveTemplateMessage(Session, MsgPtr, Reply, RLen, Rest); + + // Prevent re-entry + + free(WebMail->txtFileName); + WebMail->txtFileName = NULL; + + return; + } + + // If we aren't using a template then all the information is in the WebMail fields, as we haven't been here before. + + strlop(WebMail->BID, ' '); + if (strlen(WebMail->BID) > 12) + WebMail->BID[12] = 0; + + UndoTransparency(WebMail->BID); + UndoTransparency(WebMail->To); + UndoTransparency(WebMail->Subject); + UndoTransparency(WebMail->Body); + + MsgLen = strlen(WebMail->Body); + + // We will need to mess about with To field. Create a copy so the original can go in B2 header if we use one + + if (WebMail->To[0] == 0) + { + *RLen = sprintf(Reply, "%s", ""); + FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise + return; + } + + if (strlen(WebMail->To) > 255) + { + *RLen = sprintf(Reply, "%s", ""); + FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise + return; + } + + HDest = _strdup(WebMail->To); + + if (strlen(WebMail->BID)) + { + if (LookupBID(WebMail->BID)) + { + // Duplicate bid + *RLen = sprintf(Reply, "%s", ""); + FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise + return; + } + } + + if (WebMail->Type == 'B') + { + if (RefuseBulls) + { + *RLen = sprintf(Reply, "%s", ""); + FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise + return; + } + } + + // ?? Can we just loop though the rest of the code to allow multiple dests ?? + + HDestCopy = HDest; + + while (HDest && HDest[0]) + { + HDestRest = strlop(HDest, ';'); + + Msg = AllocateMsgRecord(); + + // Set number here so they remain in sequence + + Msg->number = ++LatestMsg; + MsgnotoMsg[Msg->number] = Msg; + + strcpy(Msg->from, Session->User->Call); + + if (_memicmp(HDest, "rms:", 4) == 0 || _memicmp(HDest, "rms/", 4) == 0) + { + Vptr=&HDest[4]; + strcpy(Msg->to, "RMS"); + } + else if (_memicmp(HDest, "smtp:", 5) == 0) + { + if (ISP_Gateway_Enabled) + { + Vptr=&HDest[5]; + Msg->to[0] = 0; + } + } + else if (strchr(HDest, '@')) + { + strcpy(OrigTo, HDest); + + Vptr = strlop(HDest, '@'); + + if (Vptr) + { + // If looks like a valid email address, treat as such + + if (strlen(HDest) > 6 || !CheckifPacket(Vptr)) + { + // Assume Email address + + Vptr = OrigTo; + + if (FindRMS() || strchr(Vptr, '!')) // have RMS or source route + strcpy(Msg->to, "RMS"); + else if (ISP_Gateway_Enabled) + Msg->to[0] = 0; + else if (isAMPRMsg(OrigTo)) + strcpy(Msg->to, "RMS"); // Routing will redirect it + else + { + *RLen = sprintf(Reply, "%s", ""); + FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise + return; + + } + } + else + strcpy(Msg->to, _strupr(HDest)); + } + } + else + { + // No @ + + if (strlen(HDest) > 6) + HDest[6] = 0; + + strcpy(Msg->to, _strupr(HDest)); + } + + if (SendBBStoSYSOPCall) + if (_stricmp(HDest, BBSName) == 0) + strcpy(Msg->to, SYSOPCall); + + if (Vptr) + { + if (strlen(Vptr) > 40) + Vptr[40] = 0; + + strcpy(Msg->via, _strupr(Vptr)); + } + else + { + // No via. If not local user try to add BBS + + struct UserInfo * ToUser = LookupCall(Msg->to); + + if (ToUser) + { + // Local User. If Home BBS is specified, use it + + if (ToUser->flags & F_RMSREDIRECT) + { + // sent to Winlink + + strcpy(Msg->via, WinlinkAddr); + sprintf(Prompt, "Redirecting to winlink.org\r"); + } + else if (ToUser->HomeBBS[0]) + { + strcpy(Msg->via, ToUser->HomeBBS); + sprintf(Prompt, "%s added from HomeBBS. Message Saved", Msg->via); + } + } + else + { + // Not local user - Check WP + + WPRecP WP = LookupWP(Msg->to); + + if (WP) + { + strcpy(Msg->via, WP->first_homebbs); + sprintf(Prompt, "%s added from WP", Msg->via); + } + } + } + + if (strlen(WebMail->Subject) > 60) + WebMail->Subject[60] = 0; + + strcpy(Msg->title, WebMail->Subject); + Msg->type = WebMail->Type; + + if (Session->User->flags & F_HOLDMAIL) + { + int Length=0; + char * MailBuffer = malloc(100); + char Title[100]; + + Msg->status = 'H'; + + Length = sprintf(MailBuffer, "Message %d Held\r\n", Msg->number); + sprintf(Title, "Message %d Held - %s", Msg->number, "User has Hold Messages flag set"); + SendMessageToSYSOP(Title, MailBuffer, Length); + } + else + Msg->status = 'N'; + + if (strlen(WebMail->BID) == 0) + sprintf(Msg->bid, "%d_%s", LatestMsg, BBSName); + else + strcpy(Msg->bid, WebMail->BID); + + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + BIDRec = AllocateBIDRecord(); + + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + Msg->length = (int)MsgLen + WebMail->HeaderLen + WebMail->FooterLen; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + // BuildFormMessage(Session, Msg); + + if (WebMail->Files) + { + // Send as B2 + + char * B2Header = BuildB2Header(WebMail, Msg, NULL, 0); + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + WriteLen = fwrite(B2Header, 1, strlen(B2Header), hFile); + WriteLen += fwrite(WebMail->Header, 1, WebMail->HeaderLen, hFile); + WriteLen += fwrite(WebMail->Body, 1, MsgLen, hFile); + WriteLen += fwrite(WebMail->Footer, 1, WebMail->FooterLen, hFile); + WriteLen += fwrite("\r\n", 1, 2, hFile); + + for (i = 0; i < WebMail->Files; i++) + { + WriteLen += fwrite(WebMail->FileBody[i], 1, WebMail->FileLen[i], hFile); + WriteLen += fwrite("\r\n", 1, 2, hFile); + } + fclose(hFile); + free(B2Header); + + Msg->length = (int)WriteLen; + } + } + else + { + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + WriteLen += fwrite(WebMail->Header, 1, WebMail->HeaderLen, hFile); + WriteLen += fwrite(WebMail->Body, 1, MsgLen, hFile); + WriteLen += fwrite(WebMail->Footer, 1, WebMail->FooterLen, hFile); + fclose(hFile); + } + } + MatchMessagetoBBSList(Msg, &conn); + + BuildNNTPList(Msg); // Build NNTP Groups list + + if (Msg->status != 'H' && Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0) + Msg->status = '$'; // Has forwarding + + + if (EnableUI) + SendMsgUI(Msg); + + user = LookupCall(Msg->to); + + // If Event Notifications enabled report a new message event + + SendNewMessageEvent(user->Call, Msg); + +#ifndef NOMQTT + if (MQTT) + MQTTMessageEvent(Msg); +#endif + + if (user && (user->flags & F_APRSMFOR)) + { + char APRS[128]; + char Call[16]; + int SSID = user->flags >> 28; + + if (SSID) + sprintf(Call, "%s-%d", Msg->to, SSID); + else + strcpy(Call, Msg->to); + + sprintf(APRS, "New BBS Message %s From %s", Msg->title, Msg->from); + APISendAPRSMessage(APRS, Call); + } + + HDest = HDestRest; + } + + *RLen = SendWebMailHeaderEx(Reply, Session->Key, Session, Prompt); + + SaveMessageDatabase(); + SaveBIDDatabase(); + FreeWebMailFields(WebMail); + free(HDestCopy); + + return; +} + + + + + + +// RMS Express Forms Support + +char * GetHTMLViewerTemplate(char * FN, struct HtmlFormDir ** FormDir) +{ + int i, j, k, l; + + // Seach list of forms for base file (without .html) + + for (i = 0; i < FormDirCount; i++) + { + struct HtmlFormDir * Dir = HtmlFormDirs[i]; + + for (j = 0; j < Dir->FormCount; j++) + { + if (strcmp(FN, Dir->Forms[j]->FileName) == 0) + { + *FormDir = Dir; + return CheckFile(Dir, FN); + } + } + + if (Dir->DirCount) + { + for (l = 0; l < Dir->DirCount; l++) + { + struct HtmlFormDir * SDir = Dir->Dirs[l]; + + if (SDir->DirCount) + { + struct HtmlFormDir * SSDir = SDir->Dirs[0]; + int x = 1; + } + + for (k = 0; k < SDir->FormCount; k++) + { + if (_stricmp(FN, SDir->Forms[k]->FileName) == 0) + { + *FormDir = SDir; + return CheckFile(SDir, SDir->Forms[k]->FileName); + } + } + if (SDir->DirCount) + { + struct HtmlFormDir * SSDir = SDir->Dirs[0]; + int x = 1; + } + } + } + } + + return NULL; +} +VOID GetReply(struct HTTPConnectionInfo * Session, char * NodeURL) +{ +} + +VOID GetPage(struct HTTPConnectionInfo * Session, char * NodeURL) +{ + // Read the HTML Template file and do any needed substitutions + + WebMailInfo * WebMail = Session->WebMail; + KeyValues * txtKey = WebMail->txtKeys; + + int DirNo; + char * ptr; + int FileNo = 0; + char * SubDir; + int SubDirNo; + int i; + struct HtmlFormDir * Dir; + char * Template; + char * inptr; + char FormDir[MAX_PATH] = ""; + char FN[MAX_PATH] = ""; + char * InputName = NULL; // HTML to input message + char * ReplyName = NULL; + char * To = NULL; + char * CC = NULL; + char * BID = NULL; + char Type = 0; + char * Subject = NULL; + char * MsgBody = NULL; + char * varptr; + char * endptr; + size_t varlen, vallen = 0; + char val[256]=""; // replacement text + char var[100] = "\""; + char * MsgBytes; + char Submit[64]; + + if (NodeURL == NULL) + { + //rentry after processing or substitutions + + // if Dir not specified search all for Filename + + if (Dir == NULL) + { + for (i = 0; i < FormDirCount; i++) + { + int n; + + Dir = HtmlFormDirs[i]; + + MsgBytes = CheckFile(Dir, WebMail->txtFileName); + if (MsgBytes) + goto gotFile; + + // Recurse any Subdirs + + n = 0; + while (n < Dir->DirCount) + { + MsgBytes = CheckFile(Dir->Dirs[n], FN); + if (MsgBytes) + { + Dir = Dir->Dirs[n]; + goto gotFile; + } + n++; + } + } + return; + } + else + MsgBytes = CheckFile(Dir, WebMail->txtFileName); + +gotFile: + + WebMail->Dir = Dir; + + if (WebMail->txtFile) + free(WebMail->txtFile); + + WebMail->txtFile = MsgBytes; + +reEnter: + + if (ParsetxtTemplate(Session, Dir, WebMail->txtFileName, FALSE) == FALSE) + { + // Template has or + + if (WebMail->InputHTMLName == NULL) + { + // This is a plain text template without HTML +/* + if (To == NULL) + To = ""; + + if (To[0] == 0 && WebMail->To && WebMail->To[0]) + To = WebMail->To; + + if (CC == NULL) + CC = ""; + + if (CC[0] == 0 && WebMail->CC && WebMail->CC[0]) + CC = WebMail->CC; + + if (Subject == NULL) + Subject = ""; + + if (Subject[0] == 0 && WebMail->Subject && WebMail->Subject[0]) + Subject = WebMail->Subject; + + if (MsgBody == NULL) + MsgBody = ""; + + if (MsgBody[0] == 0 && WebMail->Body && WebMail->Body[0]) + MsgBody = WebMail->Body; + + *WebMail->RLen = sprintf(WebMail->Reply, CheckFormMsgPage, Session->Key, To, CC, Subject, MsgBody); + */ + return *WebMail->RLen; + } + + Template = CheckFile(WebMail->Dir, WebMail->InputHTMLName); + + if (Template == NULL) + { + // Missing HTML + + *WebMail->RLen = sprintf(WebMail->Reply, "", WebMail->InputHTMLName); + return *WebMail->RLen; + } + + // I've going to update the template in situ, as I can't see a better way + // of making sure all occurances of variables in any order are substituted. + // The space allocated to Template is twice the size of the file + // to allow for insertions + + UpdateFormAction(Template, Session->Key); // Update "Submit" Action + + // Search for "{var }" strings in form and replace with + // corresponding variable from XML + + while (txtKey->Key) + { + char Key[256] = "{var "; + + strcpy(&Key[5], txtKey->Key); + strcat(Key, "}"); + + inptr = Template; + varptr = stristr(inptr, Key); + + while (varptr) + { + // Move the remaining message up/down the buffer to make space for substitution + + varlen = (int)strlen(Key); + if (txtKey->Value) + vallen = (int)strlen(txtKey->Value); + else vallen = 0; + + endptr = varptr + varlen; + + memmove(varptr + vallen, endptr, strlen(endptr) + 1); // copy null on end + memcpy(varptr, txtKey->Value, vallen); + + inptr = endptr + 1; + + varptr = stristr(inptr, Key); + } + txtKey++; + } + + // Remove from end as we add it on later + + ptr = stristr(Template, ""); + + if (ptr) + *ptr = 0; + + Len = sprintf(Reply, "%s", Template); + free(Template); + return Len; +} + +char * CheckFile(struct HtmlFormDir * Dir, char * FN) +{ + struct stat STAT; + FILE * hFile; + char MsgFile[MAX_PATH]; + char * MsgBytes; + int ReadLen; + int FileSize; + +#ifndef WIN32 + + // Need to do case insensitive file search + + DIR *dir; + struct dirent *entry; + char name[256]; + + sprintf(name, "%s/%s/%s", BPQDirectory, Dir->FormSet, Dir->DirName); + + if (!(dir = opendir(name))) + { + Debugprintf("cant open forms dir %s %s %d", Dir->DirName, name, errno); + return 0; + } + + while ((entry = readdir(dir)) != NULL) + { + if (entry->d_type == DT_DIR) + continue; + + if (stricmp(entry->d_name, FN) == 0) + { + sprintf(MsgFile, "%s/%s/%s/%s", GetBPQDirectory(), Dir->FormSet, Dir->DirName, entry->d_name); + break; + } + } + closedir(dir); + +#else + + sprintf(MsgFile, "%s/%s/%s/%s", GetBPQDirectory(), Dir->FormSet, Dir->DirName, FN); + +#endif + + if (stat(MsgFile, &STAT) != -1) + { + hFile = fopen(MsgFile, "rb"); + + if (hFile == 0) + { + MsgBytes = _strdup("File is missing"); + return MsgBytes; + } + + FileSize = STAT.st_size; + MsgBytes = malloc(FileSize * 10); // Allow plenty of room for template substitution + ReadLen = (int)fread(MsgBytes, 1, FileSize, hFile); + MsgBytes[FileSize] = 0; + fclose(hFile); + + return MsgBytes; + } + return NULL; +} + +BOOL DoSelectPrompt(struct HTTPConnectionInfo * Session, char * Select) +{ + // Send a Popup window to select value. Reply handling code will update template then reenter ParsetxtTemplate + + char popuphddr[] = + + "" + "" + "
%s

" + "" + "
'); + + if (ptr) + *ptr = 0; + + ptr = SelCopy; + + if (*ptr == '"') + { + // String has " " round it + + ptr++; + + ptr1 = strchr(ptr, '"'); + if (ptr1 == NULL) + goto returnDuff; + + *(ptr1++) = 0; + prompt = ptr; + } + else + { + // Normal comma terminated + + ptr1 = strchr(ptr, ','); + if (ptr1 == NULL) + goto returnDuff; + + *(ptr1++) = 0; + prompt = ptr; + } + + ptr = ptr1; + + while (ptr && ptr[0]) + { + if (*ptr == '"') + { + // String has " " round it + + ptr++; + + ptr1 = strchr(ptr, '"'); + if (ptr1 == NULL) + goto returnDuff; + + *(ptr1++) = 0; + while(ptr1 && *ptr1 == ',') + ptr1++; + } + else + { + // Normal comma terminated + + ptr1 = strchr(ptr, ','); + if (ptr1) + *(ptr1++) = 0; + } + + var[vars++] = ptr; + + ptr = ptr1; + } + + len = sprintf(popup, popuphddr, Session->Key, prompt, vars + 1); + + for (i = 0; i < vars; i++) + { + char * key = strlop(var[i], '='); + + if (key == NULL) + key = var[i]; + + len += sprintf(&popup[len], "

"); + + *WebMail->RLen = sprintf(WebMail->Reply, "%s", popup); + free(SelCopy); + return TRUE; + +returnDuff: + *WebMail->RLen = sprintf(WebMail->Reply, "", Session->Key); + free(SelCopy); + return TRUE; + +} + +BOOL DoAskPrompt(struct HTTPConnectionInfo * Session, char * Select) +{ + return TRUE; +} + +VOID ProcessSelectResponse(struct HTTPConnectionInfo * Session, char * URLParams) +{ + // User has entered a response for a Template RLen = sprintf(WebMail->Reply, "", Session->Key); + return; + } + + varptr = Select; + endptr = strchr(Select, '>'); + + if (endptr == NULL) + { + *WebMail->RLen = sprintf(WebMail->Reply, "", Session->Key); + return; + } + + *endptr = 0; + varlen = endptr - varptr; + + + valptr = URLParams; + + // Move the remaining message up/down the buffer to make space for substitution + + vallen = strlen(valptr); + + endptr = varptr + varlen; + + memcpy(varptr, valptr, vallen); + memmove(varptr + vallen, endptr + 1, strlen(endptr + 1) + 1); // copy null on end + + if (WebMail->isReply) + *WebMail->RLen = ReplyToFormsMessage(Session, Session->Msg, WebMail->Reply, TRUE); + else + GetPage(Session, NULL); + + return ; +} + +BOOL ParsetxtTemplate(struct HTTPConnectionInfo * Session, struct HtmlFormDir * Dir, char * FN, BOOL isReply) +{ + WebMailInfo * WebMail = Session->WebMail; + KeyValues * txtKey = WebMail->txtKeys; + + char * MsgBytes; + + char * txtFile; + char * ptr, *ptr1; + char * InputName = NULL; // HTML to input message + char * ReplyName = NULL; + char * To = NULL; + char * Subject = NULL; + char * MsgBody = NULL; + char * Select = NULL; + char * Ask = NULL; + + char Date[16]; + char UDate[16]; + char DateTime[32]; + char UDateTime[32]; + char Day[16]; + char UDay[16]; + char UDTG[32]; + char Seq[16]; + char FormDir[MAX_PATH]; + double Lat; + double Lon; + char LatString[32], LonString[32], GPSString[32]; + BOOL GPSOK; + + struct tm * tm; + time_t NOW; + + // Template is now read before entering here + + MsgBytes = WebMail->txtFile; + + // if Template uses tm_year + 1900,tm->tm_mon + 1, tm->tm_mday); + + sprintf(DateTime, "%04d-%02d-%02d %02d:%02d:%02d", + tm->tm_year + 1900,tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + strcpy(Day, longday[tm->tm_wday]); + + tm = gmtime(&NOW); + + sprintf(UDate, "%04d-%02d-%02dZ", + tm->tm_year + 1900,tm->tm_mon + 1, tm->tm_mday); + + sprintf(UDateTime, "%04d-%02d-%02d %02d:%02d:%02dZ", + tm->tm_year + 100,tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + sprintf(UDTG, "%02d%02d%02dZ %s %04d", + tm->tm_mday, tm->tm_hour, tm->tm_min, month[tm->tm_mon], tm->tm_year + 1900); + + strcpy(UDay, longday[tm->tm_wday]); + + sprintf(Seq, "%d", Session->User->WebSeqNo); + sprintf(FormDir, "/WebMail/WMFile/%s/%s/", WebMail->Dir->FormSet, WebMail->Dir->DirName); + + // Keep SeqNo at front + + txtKey->Key = _strdup(""); + txtKey++->Value = _strdup(Seq); + + txtKey->Key = _strdup(""); + txtKey++->Value = _strdup(DateTime); + txtKey->Key = _strdup(""); + txtKey++->Value = _strdup(UDateTime); + txtKey->Key = _strdup(""); + txtKey++->Value = _strdup(Date); + txtKey->Key = _strdup(""); + txtKey++->Value = _strdup(UDate); + txtKey->Key = _strdup("