diff --git a/Tracker/.svn/all-wcprops b/Tracker/.svn/all-wcprops new file mode 100644 index 0000000..3e2407e --- /dev/null +++ b/Tracker/.svn/all-wcprops @@ -0,0 +1,161 @@ +K 25 +svn:wc:ra_dav:version-url +V 36 +/svn/!svn/ver/2492/trunk/xbt/Tracker +END +XBT Tracker.cpp +K 25 +svn:wc:ra_dav:version-url +V 54 +/svn/!svn/ver/2367/trunk/xbt/Tracker/XBT%20Tracker.cpp +END +tracker_input.cpp +K 25 +svn:wc:ra_dav:version-url +V 54 +/svn/!svn/ver/2487/trunk/xbt/Tracker/tracker_input.cpp +END +config.h +K 25 +svn:wc:ra_dav:version-url +V 45 +/svn/!svn/ver/2324/trunk/xbt/Tracker/config.h +END +epoll.cpp +K 25 +svn:wc:ra_dav:version-url +V 46 +/svn/!svn/ver/2438/trunk/xbt/Tracker/epoll.cpp +END +server.h +K 25 +svn:wc:ra_dav:version-url +V 45 +/svn/!svn/ver/2491/trunk/xbt/Tracker/server.h +END +tracker_input.h +K 25 +svn:wc:ra_dav:version-url +V 52 +/svn/!svn/ver/2476/trunk/xbt/Tracker/tracker_input.h +END +epoll.h +K 25 +svn:wc:ra_dav:version-url +V 44 +/svn/!svn/ver/2438/trunk/xbt/Tracker/epoll.h +END +XBT Tracker.nsi +K 25 +svn:wc:ra_dav:version-url +V 54 +/svn/!svn/ver/2020/trunk/xbt/Tracker/XBT%20Tracker.nsi +END +XBT Tracker.vcxproj +K 25 +svn:wc:ra_dav:version-url +V 58 +/svn/!svn/ver/2395/trunk/xbt/Tracker/XBT%20Tracker.vcxproj +END +XBT Tracker.sln +K 25 +svn:wc:ra_dav:version-url +V 54 +/svn/!svn/ver/2020/trunk/xbt/Tracker/XBT%20Tracker.sln +END +client.h +K 25 +svn:wc:ra_dav:version-url +V 45 +/svn/!svn/ver/2450/trunk/xbt/Tracker/client.h +END +xbt_tracker.sql +K 25 +svn:wc:ra_dav:version-url +V 52 +/svn/!svn/ver/2392/trunk/xbt/Tracker/xbt_tracker.sql +END +transaction.cpp +K 25 +svn:wc:ra_dav:version-url +V 52 +/svn/!svn/ver/2491/trunk/xbt/Tracker/transaction.cpp +END +COPYING +K 25 +svn:wc:ra_dav:version-url +V 44 +/svn/!svn/ver/2025/trunk/xbt/Tracker/COPYING +END +stdafx.cpp +K 25 +svn:wc:ra_dav:version-url +V 47 +/svn/!svn/ver/2020/trunk/xbt/Tracker/stdafx.cpp +END +transaction.h +K 25 +svn:wc:ra_dav:version-url +V 50 +/svn/!svn/ver/2446/trunk/xbt/Tracker/transaction.h +END +XBT Tracker.rc +K 25 +svn:wc:ra_dav:version-url +V 53 +/svn/!svn/ver/2020/trunk/xbt/Tracker/XBT%20Tracker.rc +END +CMakeLists.txt +K 25 +svn:wc:ra_dav:version-url +V 51 +/svn/!svn/ver/2448/trunk/xbt/Tracker/CMakeLists.txt +END +resource.h +K 25 +svn:wc:ra_dav:version-url +V 47 +/svn/!svn/ver/2159/trunk/xbt/Tracker/resource.h +END +xbt_tracker.conf.default +K 25 +svn:wc:ra_dav:version-url +V 61 +/svn/!svn/ver/2020/trunk/xbt/Tracker/xbt_tracker.conf.default +END +stdafx.h +K 25 +svn:wc:ra_dav:version-url +V 45 +/svn/!svn/ver/2404/trunk/xbt/Tracker/stdafx.h +END +connection.cpp +K 25 +svn:wc:ra_dav:version-url +V 51 +/svn/!svn/ver/2394/trunk/xbt/Tracker/connection.cpp +END +connection.h +K 25 +svn:wc:ra_dav:version-url +V 49 +/svn/!svn/ver/2325/trunk/xbt/Tracker/connection.h +END +make.sh +K 25 +svn:wc:ra_dav:version-url +V 44 +/svn/!svn/ver/2444/trunk/xbt/Tracker/make.sh +END +config.cpp +K 25 +svn:wc:ra_dav:version-url +V 47 +/svn/!svn/ver/2324/trunk/xbt/Tracker/config.cpp +END +server.cpp +K 25 +svn:wc:ra_dav:version-url +V 47 +/svn/!svn/ver/2492/trunk/xbt/Tracker/server.cpp +END diff --git a/Tracker/.svn/entries b/Tracker/.svn/entries new file mode 100644 index 0000000..68a3444 --- /dev/null +++ b/Tracker/.svn/entries @@ -0,0 +1,918 @@ +10 + +dir +2494 +http://xbt.googlecode.com/svn/trunk/xbt/Tracker +http://xbt.googlecode.com/svn + + + +2015-06-05T14:11:56.523727Z +2492 +olafvdspek + + + + + + + + + + + + + + +a4ef9278-c6d2-effe-6dce-f9b4ebbbbc1a + +XBT Tracker.vcxproj +file + + + + +2015-07-14T06:50:49.524094Z +bce6d803cadec9102816e355f6f29a0a +2013-10-29T07:54:40.309310Z +2395 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +7964 + +XBT Tracker.sln +file + + + + +2015-07-14T06:50:49.524094Z +a88cfe3cab82cc023c8f59feaa3bcaed +2010-04-15T16:24:59.778639Z +2020 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +868 + +client.h +file + + + + +2015-07-14T06:50:49.524094Z +abb41d6050f701bb744d36553464d914 +2014-11-03T16:11:16.608452Z +2450 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +192 + +xbt_tracker.sql +file + + + + +2015-07-14T06:50:49.524094Z +ba5ebc0ed9f5c01b17e0e225743ed90d +2013-06-12T09:40:11.584177Z +2392 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +2348 + +transaction.cpp +file + + + + +2015-07-14T06:50:49.524094Z +ed5c3133b04de784726e781f8ce37cb6 +2015-06-05T14:10:08.304181Z +2491 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +4386 + +COPYING +file + + + + +2015-07-14T06:50:49.528094Z +d32239bcb673463ab874e80d47fae504 +2010-04-16T13:56:12.453459Z +2025 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +35147 + +stdafx.cpp +file + + + + +2015-07-14T06:50:49.528094Z +111598f83f5212c0b09b9b0b9f3b75a0 +2010-04-15T16:24:59.778639Z +2020 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +20 + +transaction.h +file + + + + +2015-07-14T06:50:49.528094Z +c91a9ba94c350f4f856683b071a96685 +2014-10-31T15:12:08.183725Z +2446 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +337 + +XBT Tracker.rc +file + + + + +2015-07-14T06:50:49.528094Z +21a60f1fc8574abe516d3de7abe9142b +2010-04-15T16:24:59.778639Z +2020 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +94 + +CMakeLists.txt +file + + + + +2015-07-14T06:50:49.528094Z +e6eeeadd7a8ed7ad2faac50b97e0fa4d +2014-10-31T15:26:49.592657Z +2448 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +964 + +resource.h +file + + + + +2015-07-14T06:50:49.528094Z +ec0190f36a44ce339ba72c4a288e7eb6 +2011-07-24T15:06:34.966572Z +2159 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +442 + +xbt_tracker.conf.default +file + + + + +2015-07-14T06:50:49.520094Z +292ed02bd4e01e5dee73a53dba25bc77 +2010-04-15T16:24:59.778639Z +2020 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +78 + +stdafx.h +file + + + + +2015-07-14T06:50:49.520094Z +6a4f57488b154321bd9ed94cb238da78 +2014-01-09T15:55:26.844940Z +2404 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +714 + +connection.cpp +file + + + + +2015-07-14T06:50:49.520094Z +88e7b751bccd3a697fc1b312e08b192f +2013-10-24T12:26:07.344675Z +2394 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +5874 + +htdocs +dir + +connection.h +file + + + + +2015-07-14T06:50:49.520094Z +6f91fe43848598f109ae12702d0b4478 +2012-05-04T14:03:51.585783Z +2325 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +506 + +make.sh +file + + + + +2015-07-14T06:50:49.520094Z +474e7b95dea62f123c21238c2f715dec +2014-10-27T13:58:00.872711Z +2444 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +375 + +config.cpp +file + + + + +2015-07-14T06:50:49.520094Z +f63cb3b6ca18bc86b233f98420ddf3ff +2012-05-04T13:48:49.642302Z +2324 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +3413 + +server.cpp +file + + + + +2015-07-14T06:50:49.520094Z +d03a283ef649d38e791677fc0b4382f4 +2015-06-05T14:11:56.523727Z +2492 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +29830 + +res +dir + +XBT Tracker.cpp +file + + + + +2015-07-14T06:50:49.524094Z +1c81f6fcf7a3fa3c5d3b1c838b5c476a +2012-11-08T12:41:29.335665Z +2367 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +3567 + +tracker_input.cpp +file + + + + +2015-07-14T06:50:49.524094Z +0cf886c7db9509965c0404aab0805274 +2015-05-18T22:33:45.302256Z +2487 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +1236 + +config.h +file + + + + +2015-07-14T06:50:49.524094Z +b571b508aa91903dc9f7b848a59a0140 +2012-05-04T13:48:49.642302Z +2324 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +1443 + +epoll.cpp +file + + + + +2015-07-14T06:50:49.524094Z +b7137e64b54ea2505de14188369e3d9f +2014-08-29T23:05:34.068058Z +2438 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +578 + +server.h +file + + + + +2015-07-14T06:50:49.524094Z +b95abc01252f51923805f3cc733d2d8c +2015-06-05T14:10:08.304181Z +2491 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +2467 + +tracker_input.h +file + + + + +2015-07-14T06:50:49.524094Z +411585e6c4f1154bd3c36c273cce74db +2015-03-11T18:17:06.717113Z +2476 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +543 + +XBT Tracker.nsi +file + + + + +2015-07-14T06:50:49.524094Z +b7a570fcce50eb66f3aaba69f591f317 +2010-04-15T16:24:59.778639Z +2020 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +1685 + +epoll.h +file + + + + +2015-07-14T06:50:49.524094Z +40edd79a0575965c794c8a0db60dd026 +2014-08-29T23:05:34.068058Z +2438 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +527 + diff --git a/Tracker/.svn/prop-base/CMakeLists.txt.svn-base b/Tracker/.svn/prop-base/CMakeLists.txt.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/.svn/prop-base/CMakeLists.txt.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/.svn/prop-base/COPYING.svn-base b/Tracker/.svn/prop-base/COPYING.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/.svn/prop-base/COPYING.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/.svn/prop-base/XBT Tracker.cpp.svn-base b/Tracker/.svn/prop-base/XBT Tracker.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/.svn/prop-base/XBT Tracker.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/.svn/prop-base/XBT Tracker.nsi.svn-base b/Tracker/.svn/prop-base/XBT Tracker.nsi.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/.svn/prop-base/XBT Tracker.nsi.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/.svn/prop-base/XBT Tracker.rc.svn-base b/Tracker/.svn/prop-base/XBT Tracker.rc.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/.svn/prop-base/XBT Tracker.rc.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/.svn/prop-base/XBT Tracker.sln.svn-base b/Tracker/.svn/prop-base/XBT Tracker.sln.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/.svn/prop-base/XBT Tracker.sln.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/.svn/prop-base/XBT Tracker.vcxproj.svn-base b/Tracker/.svn/prop-base/XBT Tracker.vcxproj.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/.svn/prop-base/XBT Tracker.vcxproj.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/.svn/prop-base/client.h.svn-base b/Tracker/.svn/prop-base/client.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/.svn/prop-base/client.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/.svn/prop-base/config.cpp.svn-base b/Tracker/.svn/prop-base/config.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/.svn/prop-base/config.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/.svn/prop-base/config.h.svn-base b/Tracker/.svn/prop-base/config.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/.svn/prop-base/config.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/.svn/prop-base/connection.cpp.svn-base b/Tracker/.svn/prop-base/connection.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/.svn/prop-base/connection.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/.svn/prop-base/connection.h.svn-base b/Tracker/.svn/prop-base/connection.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/.svn/prop-base/connection.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/.svn/prop-base/epoll.cpp.svn-base b/Tracker/.svn/prop-base/epoll.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/.svn/prop-base/epoll.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/.svn/prop-base/epoll.h.svn-base b/Tracker/.svn/prop-base/epoll.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/.svn/prop-base/epoll.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/.svn/prop-base/make.sh.svn-base b/Tracker/.svn/prop-base/make.sh.svn-base new file mode 100644 index 0000000..2e70e8f --- /dev/null +++ b/Tracker/.svn/prop-base/make.sh.svn-base @@ -0,0 +1,9 @@ +K 13 +svn:eol-style +V 2 +LF +K 14 +svn:executable +V 1 +* +END diff --git a/Tracker/.svn/prop-base/resource.h.svn-base b/Tracker/.svn/prop-base/resource.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/.svn/prop-base/resource.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/.svn/prop-base/server.cpp.svn-base b/Tracker/.svn/prop-base/server.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/.svn/prop-base/server.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/.svn/prop-base/server.h.svn-base b/Tracker/.svn/prop-base/server.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/.svn/prop-base/server.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/.svn/prop-base/stdafx.cpp.svn-base b/Tracker/.svn/prop-base/stdafx.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/.svn/prop-base/stdafx.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/.svn/prop-base/stdafx.h.svn-base b/Tracker/.svn/prop-base/stdafx.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/.svn/prop-base/stdafx.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/.svn/prop-base/tracker_input.cpp.svn-base b/Tracker/.svn/prop-base/tracker_input.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/.svn/prop-base/tracker_input.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/.svn/prop-base/tracker_input.h.svn-base b/Tracker/.svn/prop-base/tracker_input.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/.svn/prop-base/tracker_input.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/.svn/prop-base/transaction.cpp.svn-base b/Tracker/.svn/prop-base/transaction.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/.svn/prop-base/transaction.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/.svn/prop-base/transaction.h.svn-base b/Tracker/.svn/prop-base/transaction.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/.svn/prop-base/transaction.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/.svn/prop-base/xbt_tracker.conf.default.svn-base b/Tracker/.svn/prop-base/xbt_tracker.conf.default.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/.svn/prop-base/xbt_tracker.conf.default.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/.svn/prop-base/xbt_tracker.sql.svn-base b/Tracker/.svn/prop-base/xbt_tracker.sql.svn-base new file mode 100644 index 0000000..bdbd305 --- /dev/null +++ b/Tracker/.svn/prop-base/xbt_tracker.sql.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 6 +native +END diff --git a/Tracker/.svn/text-base/CMakeLists.txt.svn-base b/Tracker/.svn/text-base/CMakeLists.txt.svn-base new file mode 100644 index 0000000..1b249e6 --- /dev/null +++ b/Tracker/.svn/text-base/CMakeLists.txt.svn-base @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 2.4) +project(xbt-tracker) +set(CMAKE_BUILD_TYPE release) +# set(CMAKE_CXX_COMPILER clang++) +set(CMAKE_CXX_FLAGS -std=c++11) +set(CPACK_GENERATOR DEB) +set(CPACK_PACKAGE_CONTACT "Olaf van der Spek ") +set(CPACK_STRIP_FILES true) +include_directories(. ../misc) +include(CheckIncludeFileCXX) +check_include_file_cxx(sys/epoll.h HAVE_SYS_EPOLL) +if(HAVE_SYS_EPOLL) + add_definitions(-DEPOLL) +endif() +# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -Wall -Wextra") +# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wno-parentheses -Wno-sign-compare") +add_executable( + xbt_tracker + ../misc/bt_misc.cpp + ../misc/database.cpp + ../misc/sha1.cpp + ../misc/socket.cpp + ../misc/sql_query.cpp + ../misc/xcc_z.cpp + config.cpp + connection.cpp + epoll.cpp + server.cpp + tracker_input.cpp + transaction.cpp + "XBT Tracker.cpp" +) +target_link_libraries(xbt_tracker mysqlclient z) +install(TARGETS xbt_tracker DESTINATION bin) +include(CPack) diff --git a/Tracker/.svn/text-base/COPYING.svn-base b/Tracker/.svn/text-base/COPYING.svn-base new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/Tracker/.svn/text-base/COPYING.svn-base @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Tracker/.svn/text-base/XBT Tracker.cpp.svn-base b/Tracker/.svn/text-base/XBT Tracker.cpp.svn-base new file mode 100644 index 0000000..b31b0c9 --- /dev/null +++ b/Tracker/.svn/text-base/XBT Tracker.cpp.svn-base @@ -0,0 +1,140 @@ +#include "stdafx.h" +#include +#include "config.h" +#include "server.h" + +std::string g_conf_file = "xbt_tracker.conf"; +const char* g_service_name = "XBT Tracker"; + +int main1() +{ + srand(static_cast(time(NULL))); + Cconfig config; + if (config.load(g_conf_file)) +#ifdef WIN32 + { + char b[MAX_PATH]; + *b = 0; + GetModuleFileName(NULL, b, MAX_PATH); + if (*b) + strrchr(b, '\\')[1] = 0; + strcat(b, "xbt_tracker.conf"); + if (config.load(b)) + std::cerr << "Unable to read " << g_conf_file << std::endl; + else + g_conf_file = b; + } +#else + std::cerr << "Unable to read " << g_conf_file << std::endl; +#endif + try + { + if (config.m_mysql_host != "-") + srv_database().open(config.m_mysql_host, config.m_mysql_user, config.m_mysql_password, config.m_mysql_database, true); + } + catch (bad_query& e) + { + std::cerr << e.what() << std::endl; + return 1; + } + if (!config.m_query_log.empty()) + { + static std::ofstream os(config.m_query_log.c_str()); + srv_database().set_query_log(&os); + } + return srv_run(config.m_mysql_table_prefix, config.m_mysql_host != "-", g_conf_file); +} + +#ifdef WIN32 +static SERVICE_STATUS g_service_status; +static SERVICE_STATUS_HANDLE gh_service_status; + +void WINAPI nt_service_handler(DWORD op) +{ + switch (op) + { + case SERVICE_CONTROL_STOP: + g_service_status.dwCurrentState = SERVICE_STOP_PENDING; + SetServiceStatus(gh_service_status, &g_service_status); + srv_term(); + break; + } + SetServiceStatus(gh_service_status, &g_service_status); +} + +void WINAPI nt_service_main(DWORD argc, LPTSTR* argv) +{ + g_service_status.dwCheckPoint = 0; + g_service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP; + g_service_status.dwCurrentState = SERVICE_START_PENDING; + g_service_status.dwServiceSpecificExitCode = 0; + g_service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + g_service_status.dwWaitHint = 0; + g_service_status.dwWin32ExitCode = NO_ERROR; + if (!(gh_service_status = RegisterServiceCtrlHandler(g_service_name, nt_service_handler))) + return; + SetServiceStatus(gh_service_status, &g_service_status); + g_service_status.dwCurrentState = SERVICE_RUNNING; + SetServiceStatus(gh_service_status, &g_service_status); + main1(); + g_service_status.dwCurrentState = SERVICE_STOPPED; + SetServiceStatus(gh_service_status, &g_service_status); +} +#endif + +int main(int argc, char* argv[]) +{ +#ifdef WIN32 + if (argc >= 2) + { + if (!strcmp(argv[1], "--install")) + { + if (nt_service_install(g_service_name)) + { + std::cerr << "Failed to install service " << g_service_name << "." << std::endl; + return 1; + } + std::cout << "Service " << g_service_name << " has been installed." << std::endl; + return 0; + } + else if (!strcmp(argv[1], "--uninstall")) + { + if (nt_service_uninstall(g_service_name)) + { + std::cerr << "Failed to uninstall service " << g_service_name << "." << std::endl; + return 1; + } + std::cout << "Service " << g_service_name << " has been uninstalled." << std::endl; + return 0; + } + else if (!strcmp(argv[1], "--conf_file") && argc >= 3) + g_conf_file = argv[2]; + else + return 1; + } +#ifdef NDEBUG + SERVICE_TABLE_ENTRY st[] = + { + { "", nt_service_main }, + { NULL, NULL } + }; + if (StartServiceCtrlDispatcher(st)) + return 0; + if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED + && GetLastError() != ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) + return 1; +#endif +#else + if (argc >= 2) + { + if (!strcmp(argv[1], "--conf_file") && argc >= 3) + g_conf_file = argv[2]; + else + { + std::cerr << " --conf_file arg (=xbt_tracker.conf)" << std::endl; + return 1; + } + } +#endif + return main1(); +} diff --git a/Tracker/.svn/text-base/XBT Tracker.nsi.svn-base b/Tracker/.svn/text-base/XBT Tracker.nsi.svn-base new file mode 100644 index 0000000..cb5c4cf --- /dev/null +++ b/Tracker/.svn/text-base/XBT Tracker.nsi.svn-base @@ -0,0 +1,46 @@ +!define VERSION "0.3.0" + +Name "XBT Tracker ${VERSION}" +Outfile "XBT_Tracker-${VERSION}.exe" +InstallDir "$PROGRAMFILES\XBT\Tracker" +InstallDirRegKey HKLM "Software\XBT\Tracker" "InstallDir" +XPStyle on +Page directory +Page instfiles +UninstPage uninstConfirm +UninstPage instfiles + +Section "Install" + SetShellVarContext all + SetOutPath "$INSTDIR" + + Delete "$INSTDIR\XBT Tracker.exe" + Delete "$INSTDIR\XBT Tracker Old.exe" + Rename "$INSTDIR\XBT Tracker.exe" "$INSTDIR\XBT Tracker Old.exe" + File "release\XBT Tracker.exe" + File xbt_tracker.conf.default + File xbt_tracker.sql + SetOverwrite off + File /oname=xbt_tracker.conf xbt_tracker.conf.default + SetOutPath "$INSTDIR\htdocs" + File htdocs\* + Exec "$INSTDIR\XBT Tracker.exe --install" + WriteUninstaller "$INSTDIR\Uninstall.exe" + CreateShortCut "$SMPROGRAMS\XBT Tracker.lnk" "$INSTDIR\XBT Tracker.exe" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\XBT Tracker" "DisplayName" "XBT Tracker ${VERSION}" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\XBT Tracker" "UninstallString" '"$INSTDIR\Uninstall.exe"' + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\XBT Tracker" "NoModify" 1 + WriteRegStr HKLM "Software\XBT\Tracker" "InstallDir" "$INSTDIR" +SectionEnd + +Section "Uninstall" + SetShellVarContext all + ExecWait 'net stop "XBT Tracker"' + ExecWait "$INSTDIR\XBT Tracker.exe --uninstall" + Delete "$SMPROGRAMS\XBT Tracker.lnk" + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\XBT Tracker" + DeleteRegKey HKLM "Software\XBT\Tracker" + DeleteRegKey /ifempty HKLM "Software\XBT" + RMDir /r "$PROGRAMFILES\XBT\Tracker" + RMDir "$PROGRAMFILES\XBT" +SectionEnd diff --git a/Tracker/.svn/text-base/XBT Tracker.rc.svn-base b/Tracker/.svn/text-base/XBT Tracker.rc.svn-base new file mode 100644 index 0000000..c7dafe6 --- /dev/null +++ b/Tracker/.svn/text-base/XBT Tracker.rc.svn-base @@ -0,0 +1,3 @@ +#include "resource.h" + +IDR_MAINFRAME ICON "res\\XBT Tracker.ico" diff --git a/Tracker/.svn/text-base/XBT Tracker.sln.svn-base b/Tracker/.svn/text-base/XBT Tracker.sln.svn-base new file mode 100644 index 0000000..2a4ad44 --- /dev/null +++ b/Tracker/.svn/text-base/XBT Tracker.sln.svn-base @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "XBT Tracker", "XBT Tracker.vcxproj", "{73C570AC-E7C3-451D-A5C6-7A2532F0121B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {73C570AC-E7C3-451D-A5C6-7A2532F0121B}.Debug|Win32.ActiveCfg = Debug|Win32 + {73C570AC-E7C3-451D-A5C6-7A2532F0121B}.Debug|Win32.Build.0 = Debug|Win32 + {73C570AC-E7C3-451D-A5C6-7A2532F0121B}.Release|Win32.ActiveCfg = Release|Win32 + {73C570AC-E7C3-451D-A5C6-7A2532F0121B}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Tracker/.svn/text-base/XBT Tracker.vcxproj.svn-base b/Tracker/.svn/text-base/XBT Tracker.vcxproj.svn-base new file mode 100644 index 0000000..06564ce --- /dev/null +++ b/Tracker/.svn/text-base/XBT Tracker.vcxproj.svn-base @@ -0,0 +1,173 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {73C570AC-E7C3-451D-A5C6-7A2532F0121B} + XBT Tracker + $(VCTargetsPath11) + + + + Application + false + MultiByte + v120 + + + Application + false + MultiByte + v120 + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + + + + + + + + MaxSpeed + OnlyExplicitInline + true + ..\misc;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + true + Use + stdafx.h + Level3 + stdafx.h + + + NDEBUG;%(PreprocessorDefinitions) + 0x0413 + + + zlib.lib;%(AdditionalDependencies) + Console + UseLinkTimeCodeGeneration + false + + + MachineX86 + + + + + + + + + Disabled + ..\misc;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + Use + stdafx.h + Level1 + stdafx.h + + + _DEBUG;%(PreprocessorDefinitions) + 0x0413 + + + zlibd.lib;%(AdditionalDependencies) + + + true + Console + false + + + MachineX86 + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Tracker/.svn/text-base/client.h.svn-base b/Tracker/.svn/text-base/client.h.svn-base new file mode 100644 index 0000000..d24cca5 --- /dev/null +++ b/Tracker/.svn/text-base/client.h.svn-base @@ -0,0 +1,18 @@ +#pragma once + +class Cserver; + +class Cclient +{ +public: + virtual void process_events(int) = 0; + + virtual ~Cclient() {} + + const Csocket& s() const + { + return m_s; + } +protected: + Csocket m_s; +}; diff --git a/Tracker/.svn/text-base/config.cpp.svn-base b/Tracker/.svn/text-base/config.cpp.svn-base new file mode 100644 index 0000000..f1df4e8 --- /dev/null +++ b/Tracker/.svn/text-base/config.cpp.svn-base @@ -0,0 +1,118 @@ +#include "stdafx.h" +#include "config.h" + +Cconfig::Cconfig() +{ + fill_maps(NULL); +} + +Cconfig::Cconfig(const Cconfig& v) +{ + fill_maps(&v); +} + +const Cconfig& Cconfig::operator=(const Cconfig& v) +{ + fill_maps(&v); + return *this; +} + +void Cconfig::fill_maps(const Cconfig* v) +{ + { + t_attribute attributes[] = + { + { "auto_register", &m_auto_register, false }, + { "anonymous_announce", &m_anonymous_announce, true }, + { "anonymous_scrape", &m_anonymous_scrape, true }, + { "daemon", &m_daemon, true }, + { "debug", &m_debug, false }, + { "full_scrape", &m_full_scrape, false }, + { "gzip_scrape", &m_gzip_scrape, true }, + { "log_access", &m_log_access, false }, + { "log_announce", &m_log_announce, false }, + { "log_scrape", &m_log_scrape, false }, + { NULL, NULL, false } + }; + fill_map(attributes, v ? &v->m_attributes_bool : NULL, m_attributes_bool); + } + { + t_attribute attributes[] = + { + { "announce_interval", &m_announce_interval, 1800 }, + { "clean_up_interval", &m_clean_up_interval, 60 }, + { "read_config_interval", &m_read_config_interval, 60 }, + { "read_db_interval", &m_read_db_interval, 60 }, + { "scrape_interval", &m_scrape_interval, 0 }, + { "write_db_interval", &m_write_db_interval, 15 }, + { NULL, NULL, 0 } + }; + fill_map(attributes, v ? &v->m_attributes_int : NULL, m_attributes_int); + } + { + t_attribute attributes[] = + { + { "column_files_completed", &m_column_files_completed, "completed" }, + { "column_files_fid", &m_column_files_fid, "fid" }, + { "column_files_leechers", &m_column_files_leechers, "leechers" }, + { "column_files_seeders", &m_column_files_seeders, "seeders" }, + { "column_users_uid", &m_column_users_uid, "uid" }, + { "mysql_database", &m_mysql_database, "xbt" }, + { "mysql_host", &m_mysql_host, "" }, + { "mysql_password", &m_mysql_password, "" }, + { "mysql_table_prefix", &m_mysql_table_prefix, "xbt_" }, + { "mysql_user", &m_mysql_user, "" }, + { "offline_message", &m_offline_message, "" }, + { "pid_file", &m_pid_file, "" }, + { "query_log", &m_query_log, "" }, + { "redirect_url", &m_redirect_url, "" }, + { "table_announce_log", &m_table_announce_log, "" }, + { "table_files", &m_table_torrents, "" }, + { "table_files_users", &m_table_torrents_users, "" }, + { "table_scrape_log", &m_table_scrape_log, "" }, + { "table_users", &m_table_users, "" }, + { "torrent_pass_private_key", &m_torrent_pass_private_key, "" }, + { NULL, NULL, "" } + }; + fill_map(attributes, v ? &v->m_attributes_string : NULL, m_attributes_string); + } + if (v) + { + m_listen_ipas = v->m_listen_ipas; + m_listen_ports = v->m_listen_ports; + } +} + +int Cconfig::set(const std::string& name, const std::string& value) +{ + if (t_attribute* i = find_ptr(m_attributes_string, name)) + *i->value = value; + else if (name == "listen_ipa") + { + if (value != "*") + m_listen_ipas.insert(inet_addr(value.c_str())); + } + else + return set(name, atoi(value.c_str())); + return 0; +} + +int Cconfig::set(const std::string& name, int value) +{ + if (t_attribute* i = find_ptr(m_attributes_int, name)) + *i->value = value; + else if (name == "listen_port") + m_listen_ports.insert(value); + else + return set(name, static_cast(value)); + return 0; +} + +int Cconfig::set(const std::string& name, bool value) +{ + if (t_attribute* i = find_ptr(m_attributes_bool, name)) + *i->value = value; + else + return 1; + return 0; +} diff --git a/Tracker/.svn/text-base/config.h.svn-base b/Tracker/.svn/text-base/config.h.svn-base new file mode 100644 index 0000000..e59ece4 --- /dev/null +++ b/Tracker/.svn/text-base/config.h.svn-base @@ -0,0 +1,55 @@ +#pragma once + +#include + +class Cconfig: public Cconfig_base +{ +public: + int set(const std::string& name, const std::string& value); + int set(const std::string& name, int value); + int set(const std::string& name, bool value); + Cconfig(); + Cconfig(const Cconfig&); + const Cconfig& operator=(const Cconfig&); + + bool m_anonymous_announce; + bool m_anonymous_scrape; + bool m_auto_register; + bool m_daemon; + bool m_debug; + bool m_full_scrape; + bool m_gzip_scrape; + bool m_log_access; + bool m_log_announce; + bool m_log_scrape; + int m_announce_interval; + int m_clean_up_interval; + int m_read_config_interval; + int m_read_db_interval; + int m_scrape_interval; + int m_write_db_interval; + std::string m_column_files_completed; + std::string m_column_files_fid; + std::string m_column_files_leechers; + std::string m_column_files_seeders; + std::string m_column_users_uid; + std::string m_mysql_database; + std::string m_mysql_host; + std::string m_mysql_password; + std::string m_mysql_table_prefix; + std::string m_mysql_user; + std::string m_offline_message; + std::string m_query_log; + std::string m_pid_file; + std::string m_redirect_url; + std::string m_table_announce_log; + std::string m_table_torrents; + std::string m_table_torrents_users; + std::string m_table_scrape_log; + std::string m_table_users; + std::string m_torrent_pass_private_key; + std::set m_listen_ipas; + std::set m_listen_ports; +private: + void fill_maps(const Cconfig*); +}; diff --git a/Tracker/.svn/text-base/connection.cpp.svn-base b/Tracker/.svn/text-base/connection.cpp.svn-base new file mode 100644 index 0000000..6ba4263 --- /dev/null +++ b/Tracker/.svn/text-base/connection.cpp.svn-base @@ -0,0 +1,278 @@ +#include "stdafx.h" +#include "connection.h" + +#include "epoll.h" +#include "server.h" + +Cconnection::Cconnection(const Csocket& s, const sockaddr_in& a) +{ + m_s = s; + m_a = a; + m_ctime = srv_time(); + + m_state = 0; + m_w = m_read_b; +} + +int Cconnection::pre_select(fd_set* fd_read_set, fd_set* fd_write_set) +{ + FD_SET(m_s, fd_read_set); + if (!m_r.empty()) + FD_SET(m_s, fd_write_set); + return m_s; +} + +int Cconnection::post_select(fd_set* fd_read_set, fd_set* fd_write_set) +{ + return FD_ISSET(m_s, fd_read_set) && recv() + || FD_ISSET(m_s, fd_write_set) && send() + || srv_time() - m_ctime > 10 + || m_state == 5 && m_r.empty(); +} + +int Cconnection::recv() +{ + int r = m_s.recv(m_w); + if (!r) + { + m_state = 5; + return 0; + } + if (r == SOCKET_ERROR) + { + int e = WSAGetLastError(); + switch (e) + { + case WSAECONNABORTED: + case WSAECONNRESET: + return 1; + case WSAEWOULDBLOCK: + return 0; + } + std::cerr << "recv failed: " << Csocket::error2a(e) << std::endl; + return 1; + } + if (m_state == 5) + return 0; + const char* a = m_w.data(); + m_w.advance_begin(r); + int state; + do + { + state = m_state; + while (a < m_w.begin() && *a != '\n' && *a != '\r') + { + a++; + if (m_state) + m_state = 1; + } + if (a < m_w.begin()) + { + switch (m_state) + { + case 0: + read(std::string(&m_read_b.front(), a - &m_read_b.front())); + m_state = 1; + case 1: + case 3: + m_state += *a == '\n' ? 2 : 1; + break; + case 2: + case 4: + m_state++; + break; + } + a++; + } + } + while (state != m_state); + return 0; +} + +int Cconnection::send() +{ + if (m_r.empty()) + return 0; + int r = m_s.send(m_r); + if (r == SOCKET_ERROR) + { + int e = WSAGetLastError(); + switch (e) + { + case WSAECONNABORTED: + case WSAECONNRESET: + return 1; + case WSAEWOULDBLOCK: + return 0; + } + std::cerr << "send failed: " << Csocket::error2a(e) << std::endl; + return 1; + } + m_r.advance_begin(r); + if (m_r.empty()) + m_write_b.clear(); + return 0; +} + +void Cconnection::read(const std::string& v) +{ +#ifndef NDEBUG + std::cout << v << std::endl; +#endif + if (srv_config().m_log_access) + { + static std::ofstream f("xbt_tracker_raw.log"); + f << srv_time() << '\t' << inet_ntoa(m_a.sin_addr) << '\t' << ntohs(m_a.sin_port) << '\t' << v << std::endl; + } + Ctracker_input ti; + size_t e = v.find('?'); + if (e == std::string::npos) + e = v.size(); + else + { + size_t a = e + 1; + size_t b = v.find(' ', a); + if (b == std::string::npos) + return; + while (a < b) + { + size_t c = v.find('=', a); + if (c++ == std::string::npos) + break; + size_t d = v.find_first_of(" &", c); + if (d == std::string::npos) + break; + ti.set(v.substr(a, c - a - 1), uri_decode(v.substr(c, d - c))); + a = d + 1; + } + } + if (!ti.m_ipa || !is_private_ipa(m_a.sin_addr.s_addr)) + ti.m_ipa = m_a.sin_addr.s_addr; + str_ref torrent_pass; + size_t a = 4; + if (a < e && v[a] == '/') + { + a++; + if (a + 32 < e && v[a + 32] == '/') + { + torrent_pass.assign(&v[a], 32); + a += 33; + } + } + std::string h = "HTTP/1.0 200 OK\r\n"; + std::string s; + bool gzip = true; + switch (a < v.size() ? v[a] : 0) + { + case 'a': + if (ti.valid()) + { + gzip = false; + std::string error = srv_insert_peer(ti, false, find_user_by_torrent_pass(torrent_pass, ti.m_info_hash)); + s = error.empty() ? srv_select_peers(ti) : (boost::format("d14:failure reason%d:%se") % error.size() % error).str(); + } + break; + case 'd': + if (srv_config().m_debug) + { + h += "Content-Type: text/html; charset=us-ascii\r\n"; + s = srv_debug(ti); + } + break; + case 's': + if (v.size() >= 7 && v[6] == 't') + { + h += "Content-Type: text/html; charset=us-ascii\r\n"; + s = srv_statistics(); + } + else if (srv_config().m_full_scrape || !ti.m_info_hash.empty()) + { + gzip = srv_config().m_gzip_scrape && ti.m_info_hash.empty(); + s = srv_scrape(ti, find_user_by_torrent_pass(torrent_pass, ti.m_info_hash)); + } + break; + } + if (s.empty()) + { + if (!ti.m_info_hash.empty() || srv_config().m_redirect_url.empty()) + h = "HTTP/1.0 404 Not Found\r\n"; + else + { + h = "HTTP/1.0 302 Found\r\n" + "Location: " + srv_config().m_redirect_url + (ti.m_info_hash.empty() ? "" : "?info_hash=" + uri_encode(ti.m_info_hash)) + "\r\n"; + } + } + else if (gzip) + { + shared_data s2 = xcc_z::gzip(s); +#ifndef NDEBUG + static std::ofstream f("xbt_tracker_gzip.log"); + f << srv_time() << '\t' << v[5] << '\t' << s.size() << '\t' << s2.size() << std::endl; +#endif + if (s2.size() + 24 < s.size()) + { + h += "Content-Encoding: gzip\r\n"; + s = to_string(s2); + } + } + h += "\r\n"; +#ifdef WIN32 + m_write_b = shared_data(h.size() + s.size()); + memcpy(m_write_b.data(), h); + memcpy(m_write_b.data() + h.size(), s); + int r = m_s.send(m_write_b); +#else + std::array d; + d[0].iov_base = const_cast(h.data()); + d[0].iov_len = h.size(); + d[1].iov_base = const_cast(s.data()); + d[1].iov_len = s.size(); + msghdr m; + m.msg_name = NULL; + m.msg_namelen = 0; + m.msg_iov = const_cast(d.data()); + m.msg_iovlen = d.size(); + m.msg_control = NULL; + m.msg_controllen = 0; + m.msg_flags = 0; + int r = sendmsg(m_s, &m, MSG_NOSIGNAL); +#endif + if (r == SOCKET_ERROR) + { + if (WSAGetLastError() != WSAECONNRESET) + std::cerr << "send failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; + } + else if (r != h.size() + s.size()) + { +#ifndef WIN32 + if (r < h.size()) + { + m_write_b = shared_data(h.size() + s.size()); + memcpy(m_write_b.data(), h); + memcpy(m_write_b.data() + h.size(), s); + } + else + { + m_write_b = make_shared_data(s); + r -= h.size(); + } +#endif + m_r = m_write_b; + m_r.advance_begin(r); + } + if (m_r.empty()) + m_write_b.clear(); +} + +void Cconnection::process_events(int events) +{ + if (events & (EPOLLIN | EPOLLPRI | EPOLLERR | EPOLLHUP) && recv() + || events & EPOLLOUT && send() + || m_state == 5 && m_write_b.empty()) + m_s.close(); +} + +int Cconnection::run() +{ + return s() == INVALID_SOCKET || srv_time() - m_ctime > 10; +} diff --git a/Tracker/.svn/text-base/connection.h.svn-base b/Tracker/.svn/text-base/connection.h.svn-base new file mode 100644 index 0000000..861832c --- /dev/null +++ b/Tracker/.svn/text-base/connection.h.svn-base @@ -0,0 +1,24 @@ +#pragma once + +#include "client.h" + +class Cconnection : public Cclient, boost::noncopyable +{ +public: + int run(); + void read(const std::string&); + int recv(); + int send(); + virtual void process_events(int); + int pre_select(fd_set* read, fd_set* write); + int post_select(fd_set* read, fd_set* write); + Cconnection(const Csocket&, const sockaddr_in&); +private: + sockaddr_in m_a; + time_t m_ctime; + int m_state; + std::array m_read_b; + shared_data m_write_b; + str_ref m_r; + mutable_str_ref m_w; +}; diff --git a/Tracker/.svn/text-base/epoll.cpp.svn-base b/Tracker/.svn/text-base/epoll.cpp.svn-base new file mode 100644 index 0000000..0aa26b4 --- /dev/null +++ b/Tracker/.svn/text-base/epoll.cpp.svn-base @@ -0,0 +1,43 @@ +#include "epoll.h" + +#ifndef WIN32 +#include +#endif + +Cepoll::~Cepoll() +{ +#ifdef EPOLL + if (m_fd != -1) + close(m_fd); +#endif +} + +int Cepoll::create() +{ +#ifdef EPOLL + return m_fd = epoll_create(1); +#else + return 0; +#endif +} + +int Cepoll::ctl(int op, int fd, int events, void* p) +{ +#ifdef EPOLL + epoll_event e; + e.data.ptr = p; + e.events = events; + return epoll_ctl(m_fd, op, fd, &e); +#else + return 0; +#endif +} + +int Cepoll::wait(epoll_event* events, int maxevents, int timeout) +{ +#ifdef EPOLL + return epoll_wait(m_fd, events, maxevents, timeout); +#else + return 0; +#endif +} diff --git a/Tracker/.svn/text-base/epoll.h.svn-base b/Tracker/.svn/text-base/epoll.h.svn-base new file mode 100644 index 0000000..c3c9484 --- /dev/null +++ b/Tracker/.svn/text-base/epoll.h.svn-base @@ -0,0 +1,38 @@ +#pragma once + +#include + +#ifdef EPOLL +#include +#else +enum +{ + EPOLLIN = 1, + EPOLLOUT = 2, + EPOLLPRI = 4, + EPOLLERR = 8, + EPOLLHUP = 0x10, + EPOLLET = 0x20, + EPOLLONESHOT = 0x40, +}; + +enum +{ + EPOLL_CTL_ADD = 1, + EPOLL_CTL_MOD = 2, + EPOLL_CTL_DEL = 4, +}; + +typedef void epoll_event; +#endif + +class Cepoll: boost::noncopyable +{ +public: + int create(); + int ctl(int op, int fd, int events, void* p); + int wait(epoll_event* events, int maxevents, int timeout); + ~Cepoll(); +private: + int m_fd = -1; +}; diff --git a/Tracker/.svn/text-base/make.sh.svn-base b/Tracker/.svn/text-base/make.sh.svn-base new file mode 100644 index 0000000..d1f0af4 --- /dev/null +++ b/Tracker/.svn/text-base/make.sh.svn-base @@ -0,0 +1,15 @@ +g++ $@ -DEPOLL -DNDEBUG -I ../misc -I . -O3 -o xbt_tracker -std=c++11 \ + ../misc/bt_misc.cpp \ + ../misc/database.cpp \ + ../misc/sha1.cpp \ + ../misc/socket.cpp \ + ../misc/sql_query.cpp \ + ../misc/xcc_z.cpp \ + config.cpp \ + connection.cpp \ + epoll.cpp \ + server.cpp \ + tracker_input.cpp \ + transaction.cpp \ + "XBT Tracker.cpp" \ + `mysql_config --libs` -lz && strip xbt_tracker diff --git a/Tracker/.svn/text-base/resource.h.svn-base b/Tracker/.svn/text-base/resource.h.svn-base new file mode 100644 index 0000000..1a8193f --- /dev/null +++ b/Tracker/.svn/text-base/resource.h.svn-base @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by XBT Client.rc +// +#define IDR_MAINFRAME 128 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 149 +#define _APS_NEXT_COMMAND_VALUE 32849 +#define _APS_NEXT_CONTROL_VALUE 1091 +#define _APS_NEXT_SYMED_VALUE 105 +#endif +#endif diff --git a/Tracker/.svn/text-base/server.cpp.svn-base b/Tracker/.svn/text-base/server.cpp.svn-base new file mode 100644 index 0000000..932d92f --- /dev/null +++ b/Tracker/.svn/text-base/server.cpp.svn-base @@ -0,0 +1,978 @@ +#include "stdafx.h" +#include "server.h" + +#include +#include "connection.h" +#include "epoll.h" +#include "transaction.h" + +namespace boost +{ + template + struct hash> + { + size_t operator()(const std::array& v) const + { + return boost::hash_range(v.begin(), v.end()); + } + }; +} + +static volatile bool g_sig_term = false; +boost::ptr_list m_connections; +boost::unordered_map, t_torrent> m_torrents; +boost::unordered_map m_users; +boost::unordered_map, t_user*> m_users_torrent_passes; +Cconfig m_config; +Cdatabase m_database; +Cepoll m_epoll; +Cstats m_stats; +std::string m_announce_log_buffer; +std::string m_conf_file; +std::string m_scrape_log_buffer; +std::string m_table_prefix; +std::string m_torrents_users_updates_buffer; +std::string m_users_updates_buffer; +time_t m_clean_up_time; +time_t m_read_config_time; +time_t m_read_db_torrents_time; +time_t m_read_db_users_time; +time_t m_time = time(NULL); +time_t m_write_db_torrents_time; +time_t m_write_db_users_time; +unsigned long long m_secret; +int m_fid_end = 0; +bool m_read_users_can_leech; +bool m_read_users_peers_limit; +bool m_read_users_torrent_pass; +bool m_read_users_wait_time; +bool m_use_sql; + +void accept(const Csocket&); + +static void async_query(const std::string& v) +{ + m_database.query_nothrow(v); +} + +static void sig_handler(int v) +{ + if (v == SIGTERM) + g_sig_term = true; +} + +class Ctcp_listen_socket : public Cclient +{ +public: + Ctcp_listen_socket(const Csocket& s) + { + m_s = s; + } + + virtual void process_events(int) + { + accept(m_s); + } +}; + +class Cudp_listen_socket : public Cclient +{ +public: + Cudp_listen_socket(const Csocket& s) + { + m_s = s; + } + + virtual void process_events(int events) + { + if (events & EPOLLIN) + Ctransaction(m_s).recv(); + } +}; + +const Cconfig& srv_config() +{ + return m_config; +} + +Cdatabase& srv_database() +{ + return m_database; +} + +const t_torrent* find_torrent(const std::string& id) +{ + return find_ptr(m_torrents, to_array(id)); +} + +t_user* find_user_by_uid(int v) +{ + return find_ptr(m_users, v); +} + +long long srv_secret() +{ + return m_secret; +} + +Cstats& srv_stats() +{ + return m_stats; +} + +time_t srv_time() +{ + return m_time; +} + +void read_config() +{ + if (m_use_sql) + { + try + { + Csql_result result = Csql_query(m_database, "select name, value from @config where value is not null").execute(); + Cconfig config; + while (Csql_row row = result.fetch_row()) + { + if (config.set(row[0].s(), row[1].s())) + std::cerr << "unknown config name: " << row[0].s() << std::endl; + } + config.load(m_conf_file); + if (config.m_torrent_pass_private_key.empty()) + { + config.m_torrent_pass_private_key = generate_random_string(27); + Csql_query(m_database, "insert into @config (name, value) values ('torrent_pass_private_key', ?)")(config.m_torrent_pass_private_key).execute(); + } + m_config = config; + m_database.set_name("completed", m_config.m_column_files_completed); + m_database.set_name("leechers", m_config.m_column_files_leechers); + m_database.set_name("seeders", m_config.m_column_files_seeders); + m_database.set_name("fid", m_config.m_column_files_fid); + m_database.set_name("uid", m_config.m_column_users_uid); + m_database.set_name("announce_log", m_config.m_table_announce_log.empty() ? m_table_prefix + "announce_log" : m_config.m_table_announce_log); + m_database.set_name("files", m_config.m_table_torrents.empty() ? m_table_prefix + "files" : m_config.m_table_torrents); + m_database.set_name("files_users", m_config.m_table_torrents_users.empty() ? m_table_prefix + "files_users" : m_config.m_table_torrents_users); + m_database.set_name("scrape_log", m_config.m_table_scrape_log.empty() ? m_table_prefix + "scrape_log" : m_config.m_table_scrape_log); + m_database.set_name("users", m_config.m_table_users.empty() ? m_table_prefix + "users" : m_config.m_table_users); + } + catch (bad_query&) + { + } + } + else + { + Cconfig config; + if (!config.load(m_conf_file)) + m_config = config; + } + if (m_config.m_listen_ipas.empty()) + m_config.m_listen_ipas.insert(htonl(INADDR_ANY)); + if (m_config.m_listen_ports.empty()) + m_config.m_listen_ports.insert(2710); + m_read_config_time = srv_time(); +} + +void read_db_torrents_sql() +{ + try + { + if (!m_config.m_auto_register) + { + Csql_result result = Csql_query(m_database, "select info_hash, @fid from @files where flags & 1").execute(); + while (Csql_row row = result.fetch_row()) + { + m_torrents.erase(to_array(row[0])); + Csql_query(m_database, "delete from @files where @fid = ?")(row[1]).execute(); + } + } + if (m_config.m_auto_register && !m_torrents.empty()) + return; + Csql_result result = Csql_query(m_database, "select info_hash, @completed, @fid, ctime from @files where @fid >= ?")(m_fid_end).execute(); + // m_torrents.reserve(m_torrents.size() + result.size()); + while (Csql_row row = result.fetch_row()) + { + m_fid_end = std::max(m_fid_end, row[2].i() + 1); + if (row[0].size() != 20 || find_torrent(row[0].s())) + continue; + t_torrent& file = m_torrents[to_array(row[0])]; + if (file.fid) + continue; + file.completed = row[1].i(); + file.dirty = false; + file.fid = row[2].i(); + file.ctime = row[3].i(); + } + } + catch (bad_query&) + { + } +} + +void read_db_torrents() +{ + m_read_db_torrents_time = srv_time(); + if (m_use_sql) + read_db_torrents_sql(); + else if (!m_config.m_auto_register) + { + std::set new_torrents; + std::ifstream is("xbt_torrents.txt"); + for (std::string s; getline(is, s); ) + { + s = hex_decode(s); + if (s.size() == 20) + new_torrents.insert(&m_torrents[to_array(s)]); + } + for (auto i = m_torrents.begin(); i != m_torrents.end(); ) + { + if (new_torrents.count(&i->second)) + i++; + else + m_torrents.erase(i++); + } + } +} + +void read_db_users() +{ + m_read_db_users_time = srv_time(); + if (!m_use_sql) + return; + try + { + Csql_query q(m_database, "select @uid"); + if (m_read_users_can_leech) + q += ", can_leech"; + if (m_read_users_peers_limit) + q += ", peers_limit"; + if (m_read_users_torrent_pass) + q += ", torrent_pass"; + q += ", torrent_pass_version"; + if (m_read_users_wait_time) + q += ", wait_time"; + q += " from @users"; + Csql_result result = q.execute(); + // m_users.reserve(result.size()); + for (auto& i : m_users) + i.second.marked = true; + m_users_torrent_passes.clear(); + while (Csql_row row = result.fetch_row()) + { + t_user& user = m_users[row[0].i()]; + user.marked = false; + int c = 0; + user.uid = row[c++].i(); + if (m_read_users_can_leech) + user.can_leech = row[c++].i(); + if (m_read_users_peers_limit) + user.peers_limit = row[c++].i(); + if (m_read_users_torrent_pass) + { + if (row[c].size() == 32) + m_users_torrent_passes[to_array(row[c])] = &user; + c++; + } + user.torrent_pass_version = row[c++].i(); + if (m_read_users_wait_time) + user.wait_time = row[c++].i(); + } + for (auto i = m_users.begin(); i != m_users.end(); ) + { + if (i->second.marked) + m_users.erase(i++); + else + i++; + } + } + catch (bad_query&) + { + } +} + +const std::string& db_name(const std::string& v) +{ + return m_database.name(v); +} + +void write_db_torrents() +{ + m_write_db_torrents_time = srv_time(); + if (!m_use_sql) + return; + try + { + std::string buffer; + while (1) + { + for (auto& i : m_torrents) + { + t_torrent& file = i.second; + if (!file.dirty) + continue; + if (!file.fid) + { + Csql_query(m_database, "insert into @files (info_hash, mtime, ctime) values (?, unix_timestamp(), unix_timestamp())")(i.first).execute(); + file.fid = m_database.insert_id(); + } + buffer += Csql_query(m_database, "(?,?,?,?),")(file.leechers)(file.seeders)(file.completed)(file.fid).read(); + file.dirty = false; + if (buffer.size() > 255 << 10) + break; + } + if (buffer.empty()) + break; + buffer.pop_back(); + async_query("insert into " + db_name("files") + " (" + db_name("leechers") + ", " + db_name("seeders") + ", " + db_name("completed") + ", " + db_name("fid") + ") values " + + buffer + + " on duplicate key update" + + " " + db_name("leechers") + " = values(" + db_name("leechers") + ")," + + " " + db_name("seeders") + " = values(" + db_name("seeders") + ")," + + " " + db_name("completed") + " = values(" + db_name("completed") + ")," + + " mtime = unix_timestamp()"); + buffer.clear(); + } + } + catch (bad_query&) + { + } + if (!m_announce_log_buffer.empty()) + { + m_announce_log_buffer.pop_back(); + async_query("insert delayed into " + db_name("announce_log") + " (ipa, port, event, info_hash, peer_id, downloaded, left0, uploaded, uid, mtime) values " + m_announce_log_buffer); + m_announce_log_buffer.erase(); + } + if (!m_scrape_log_buffer.empty()) + { + m_scrape_log_buffer.pop_back(); + async_query("insert delayed into " + db_name("scrape_log") + " (ipa, uid, mtime) values " + m_scrape_log_buffer); + m_scrape_log_buffer.erase(); + } +} + +void write_db_users() +{ + m_write_db_users_time = srv_time(); + if (!m_use_sql) + return; + if (!m_torrents_users_updates_buffer.empty()) + { + m_torrents_users_updates_buffer.pop_back(); + async_query("insert into " + db_name("files_users") + " (active, announced, completed, downloaded, `left`, uploaded, mtime, fid, uid) values " + + m_torrents_users_updates_buffer + + " on duplicate key update" + + " active = values(active)," + + " announced = announced + values(announced)," + + " completed = completed + values(completed)," + + " downloaded = downloaded + values(downloaded)," + + " `left` = values(`left`)," + + " uploaded = uploaded + values(uploaded)," + + " mtime = values(mtime)"); + m_torrents_users_updates_buffer.erase(); + } + async_query("update " + db_name("files_users") + " set active = 0 where mtime < unix_timestamp() - 60 * 60"); + if (!m_users_updates_buffer.empty()) + { + m_users_updates_buffer.pop_back(); + async_query("insert into " + db_name("users") + " (downloaded, uploaded, " + db_name("uid") + ") values " + + m_users_updates_buffer + + " on duplicate key update" + + " downloaded = downloaded + values(downloaded)," + + " uploaded = uploaded + values(uploaded)"); + m_users_updates_buffer.erase(); + } +} + +int test_sql() +{ + if (!m_use_sql) + return 0; + try + { + mysql_get_server_version(m_database); + if (m_config.m_log_announce) + Csql_query(m_database, "select id, ipa, port, event, info_hash, peer_id, downloaded, left0, uploaded, uid, mtime from @announce_log where 0").execute(); + Csql_query(m_database, "select name, value from @config where 0").execute(); + Csql_query(m_database, "select @fid, info_hash, @leechers, @seeders, flags, mtime, ctime from @files where 0").execute(); + Csql_query(m_database, "select fid, uid, active, announced, completed, downloaded, `left`, uploaded from @files_users where 0").execute(); + if (m_config.m_log_scrape) + Csql_query(m_database, "select id, ipa, uid, mtime from @scrape_log where 0").execute(); + Csql_query(m_database, "select @uid, torrent_pass_version, downloaded, uploaded from @users where 0").execute(); + Csql_query(m_database, "update @files set @leechers = 0, @seeders = 0").execute(); + // Csql_query(m_database, "update @files_users set active = 0").execute(); + m_read_users_can_leech = Csql_query(m_database, "show columns from @users like 'can_leech'").execute(); + m_read_users_peers_limit = Csql_query(m_database, "show columns from @users like 'peers_limit'").execute(); + m_read_users_torrent_pass = Csql_query(m_database, "show columns from @users like 'torrent_pass'").execute(); + m_read_users_wait_time = Csql_query(m_database, "show columns from @users like 'wait_time'").execute(); + return 0; + } + catch (bad_query&) + { + } + return 1; +} + +void clean_up(t_torrent& t, time_t time) +{ + for (auto i = t.peers.begin(); i != t.peers.end(); ) + { + if (i->second.mtime < time) + { + (i->second.left ? t.leechers : t.seeders)--; + t.peers.erase(i++); + t.dirty = true; + } + else + i++; + } +} + +void clean_up() +{ + for (auto& i : m_torrents) + clean_up(i.second, srv_time() - static_cast(1.5 * m_config.m_announce_interval)); + m_clean_up_time = srv_time(); +} + +int srv_run(const std::string& table_prefix, bool use_sql, const std::string& conf_file) +{ + for (int i = 0; i < 8; i++) + m_secret = m_secret << 8 ^ rand(); + m_conf_file = conf_file; + m_database.set_name("config", table_prefix + "config"); + m_table_prefix = table_prefix; + m_use_sql = use_sql; + + read_config(); + if (test_sql()) + return 1; + if (m_epoll.create() == -1) + { + std::cerr << "epoll_create failed" << std::endl; + return 1; + } + std::list lt; + std::list lu; + for (auto& j : m_config.m_listen_ipas) + { + for (auto& i : m_config.m_listen_ports) + { + Csocket l; + if (l.open(SOCK_STREAM) == INVALID_SOCKET) + std::cerr << "socket failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; + else if (l.setsockopt(SOL_SOCKET, SO_REUSEADDR, true), + l.bind(j, htons(i))) + std::cerr << "bind failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; + else if (l.listen()) + std::cerr << "listen failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; + else + { +#ifdef SO_ACCEPTFILTER + accept_filter_arg afa; + bzero(&afa, sizeof(afa)); + strcpy(afa.af_name, "httpready"); + if (l.setsockopt(SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa))) + std::cerr << "setsockopt failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; +#elif 0 // TCP_DEFER_ACCEPT + if (l.setsockopt(IPPROTO_TCP, TCP_DEFER_ACCEPT, 90)) + std::cerr << "setsockopt failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; +#endif + lt.push_back(Ctcp_listen_socket(l)); + if (!m_epoll.ctl(EPOLL_CTL_ADD, l, EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLERR | EPOLLHUP, <.back())) + continue; + } + return 1; + } + for (auto& i : m_config.m_listen_ports) + { + Csocket l; + if (l.open(SOCK_DGRAM) == INVALID_SOCKET) + std::cerr << "socket failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; + else if (l.setsockopt(SOL_SOCKET, SO_REUSEADDR, true), + l.bind(j, htons(i))) + std::cerr << "bind failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; + else + { + lu.push_back(Cudp_listen_socket(l)); + if (!m_epoll.ctl(EPOLL_CTL_ADD, l, EPOLLIN | EPOLLPRI | EPOLLERR | EPOLLHUP, &lu.back())) + continue; + } + return 1; + } + } + clean_up(); + read_db_torrents(); + read_db_users(); + write_db_torrents(); + write_db_users(); +#ifndef NDEBUG + // test_announce(); +#endif +#ifndef WIN32 + if (m_config.m_daemon) + { + if (daemon(true, false)) + std::cerr << "daemon failed" << std::endl; + std::ofstream(m_config.m_pid_file.c_str()) << getpid() << std::endl; + struct sigaction act; + act.sa_handler = sig_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + if (sigaction(SIGTERM, &act, NULL)) + std::cerr << "sigaction failed" << std::endl; + act.sa_handler = SIG_IGN; + if (sigaction(SIGPIPE, &act, NULL)) + std::cerr << "sigaction failed" << std::endl; + } +#endif +#ifdef EPOLL + std::array events; +#else + fd_set fd_read_set; + fd_set fd_write_set; + fd_set fd_except_set; +#endif + while (!g_sig_term) + { +#ifdef EPOLL + int r = m_epoll.wait(events.data(), events.size(), 5000); + if (r == -1) + std::cerr << "epoll_wait failed: " << errno << std::endl; + else + { + time_t prev_time = m_time; + m_time = ::time(NULL); + for (int i = 0; i < r; i++) + reinterpret_cast(events[i].data.ptr)->process_events(events[i].events); + if (m_time == prev_time) + continue; + for (auto i = m_connections.begin(); i != m_connections.end(); ) + { + if (i->run()) + i = m_connections.erase(i); + else + i++; + } + } +#else + FD_ZERO(&fd_read_set); + FD_ZERO(&fd_write_set); + FD_ZERO(&fd_except_set); + int n = 0; + for (auto& i : m_connections) + { + int z = i.pre_select(&fd_read_set, &fd_write_set); + n = std::max(n, z); + } + for (auto& i : lt) + { + FD_SET(i.s(), &fd_read_set); + n = std::max(n, i.s()); + } + for (auto& i : lu) + { + FD_SET(i.s(), &fd_read_set); + n = std::max(n, i.s()); + } + timeval tv; + tv.tv_sec = 5; + tv.tv_usec = 0; + if (select(n + 1, &fd_read_set, &fd_write_set, &fd_except_set, &tv) == SOCKET_ERROR) + std::cerr << "select failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; + else + { + m_time = ::time(NULL); + for (auto& i : lt) + { + if (FD_ISSET(i.s(), &fd_read_set)) + accept(i.s()); + } + for (auto& i : lu) + { + if (FD_ISSET(i.s(), &fd_read_set)) + Ctransaction(i.s()).recv(); + } + for (auto i = m_connections.begin(); i != m_connections.end(); ) + { + if (i->post_select(&fd_read_set, &fd_write_set)) + i = m_connections.erase(i); + else + i++; + } + } +#endif + if (srv_time() - m_read_config_time > m_config.m_read_config_interval) + read_config(); + else if (srv_time() - m_clean_up_time > m_config.m_clean_up_interval) + clean_up(); + else if (srv_time() - m_read_db_torrents_time > m_config.m_read_db_interval) + read_db_torrents(); + else if (srv_time() - m_read_db_users_time > m_config.m_read_db_interval) + read_db_users(); + else if (m_config.m_write_db_interval && srv_time() - m_write_db_torrents_time > m_config.m_write_db_interval) + write_db_torrents(); + else if (m_config.m_write_db_interval && srv_time() - m_write_db_users_time > m_config.m_write_db_interval) + write_db_users(); + } + write_db_torrents(); + write_db_users(); + unlink(m_config.m_pid_file.c_str()); + return 0; +} + +void accept(const Csocket& l) +{ + sockaddr_in a; + for (int i = 0; i < 10000; i++) + { + socklen_t cb_a = sizeof(sockaddr_in); +#ifdef SOCK_NONBLOCK + Csocket s = accept4(l, reinterpret_cast(&a), &cb_a, SOCK_NONBLOCK); +#else + Csocket s = ::accept(l, reinterpret_cast(&a), &cb_a); +#endif + if (s == SOCKET_ERROR) + { + if (WSAGetLastError() == WSAECONNABORTED) + continue; + if (WSAGetLastError() != WSAEWOULDBLOCK) + { + m_stats.accept_errors++; + std::cerr << "accept failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; + xbt_syslog("accept failed: " + Csocket::error2a(WSAGetLastError())); + } + break; + } + m_stats.accepted_tcp++; +#ifndef SOCK_NONBLOCK + if (s.blocking(false)) + std::cerr << "ioctlsocket failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; +#endif + std::unique_ptr connection(new Cconnection(s, a)); + connection->process_events(EPOLLIN); + if (connection->s() != INVALID_SOCKET) + { + m_stats.slow_tcp++; + m_connections.push_back(connection.release()); + m_epoll.ctl(EPOLL_CTL_ADD, m_connections.back().s(), EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLERR | EPOLLHUP | EPOLLET, &m_connections.back()); + } + } +} + +std::string srv_insert_peer(const Ctracker_input& v, bool udp, t_user* user) +{ + if (m_use_sql && m_config.m_log_announce) + { + m_announce_log_buffer += Csql_query(m_database, "(?,?,?,?,?,?,?,?,?,?),") + (ntohl(v.m_ipa)) + (ntohs(v.m_port)) + (v.m_event) + (v.m_info_hash) + (v.m_peer_id) + (v.m_downloaded) + (v.m_left) + (v.m_uploaded) + (user ? user->uid : 0) + (srv_time()) + .read(); + } + if (!m_config.m_offline_message.empty()) + return m_config.m_offline_message; + if (0) + return bts_banned_client; + if (!m_config.m_anonymous_announce && !user) + return bts_unregistered_torrent_pass; + if (!m_config.m_auto_register && !find_torrent(v.m_info_hash)) + return bts_unregistered_torrent; + if (v.m_left && user && !user->can_leech) + return bts_can_not_leech; + t_torrent& file = m_torrents[to_array(v.m_info_hash)]; + if (!file.ctime) + file.ctime = srv_time(); + if (v.m_left && user && user->wait_time && file.ctime + user->wait_time > srv_time()) + return bts_wait_time; + peer_key_c peer_key(v.m_ipa, user ? user->uid : 0); + t_peer* i = find_ptr(file.peers, peer_key); + if (i) + (i->left ? file.leechers : file.seeders)--; + else if (v.m_left && user && user->peers_limit) + { + int c = 0; + for (auto& j : file.peers) + c += j.second.left && j.second.uid == user->uid; + if (c >= user->peers_limit) + return bts_peers_limit_reached; + } + if (m_use_sql && user && file.fid) + { + long long downloaded = 0; + long long uploaded = 0; + if (i + && i->uid == user->uid + && boost::equals(i->peer_id, v.m_peer_id) + && v.m_downloaded >= i->downloaded + && v.m_uploaded >= i->uploaded) + { + downloaded = v.m_downloaded - i->downloaded; + uploaded = v.m_uploaded - i->uploaded; + } + m_torrents_users_updates_buffer += Csql_query(m_database, "(?,1,?,?,?,?,?,?,?),") + (v.m_event != Ctracker_input::e_stopped) + (v.m_event == Ctracker_input::e_completed) + (downloaded) + (v.m_left) + (uploaded) + (srv_time()) + (file.fid) + (user->uid) + .read(); + if (downloaded || uploaded) + m_users_updates_buffer += Csql_query(m_database, "(?,?,?),")(downloaded)(uploaded)(user->uid).read(); + if (m_torrents_users_updates_buffer.size() > 255 << 10) + write_db_users(); + } + if (v.m_event == Ctracker_input::e_stopped) + file.peers.erase(peer_key); + else + { + t_peer& peer = i ? *i : file.peers[peer_key]; + peer.downloaded = v.m_downloaded; + peer.left = v.m_left; + peer.peer_id = v.m_peer_id; + peer.port = v.m_port; + peer.uid = user ? user->uid : 0; + peer.uploaded = v.m_uploaded; + (peer.left ? file.leechers : file.seeders)++; + peer.mtime = srv_time(); + } + if (v.m_event == Ctracker_input::e_completed) + file.completed++; + (udp ? m_stats.announced_udp : m_stats.announced_http)++; + file.dirty = true; + return std::string(); +} + +void t_torrent::select_peers(mutable_str_ref& d, const Ctracker_input& ti) const +{ + if (ti.m_event == Ctracker_input::e_stopped) + return; + std::vector> candidates; + candidates.reserve(peers.size()); + for (auto& i : peers) + { + if (!ti.m_left && !i.second.left) + continue; + std::array v; + memcpy(&v[0], &i.first.host_, 4); + memcpy(&v[4], &i.second.port, 2); + candidates.push_back(v); + } + size_t c = d.size() / 6; + if (candidates.size() <= c) + { + memcpy(d.data(), candidates); + d.advance_begin(6 * candidates.size()); + return; + } + const char* d0 = d.begin(); + while (c--) + { + int i = rand() % candidates.size(); + memcpy(d.data(), candidates[i]); + d.advance_begin(6); + candidates[i] = candidates.back(); + candidates.pop_back(); + } +} + +std::string srv_select_peers(const Ctracker_input& ti) +{ + const t_torrent* f = find_torrent(ti.m_info_hash); + if (!f) + return std::string(); + std::array peers0; + mutable_str_ref peers = peers0; + f->select_peers(peers, ti); + peers.assign(peers0.data(), peers.data()); + return (boost::format("d8:completei%de10:incompletei%de8:intervali%de12:min intervali%de5:peers%d:%se") + % f->seeders % f->leechers % m_config.m_announce_interval % m_config.m_announce_interval % peers.size() % peers).str(); +} + +std::string srv_scrape(const Ctracker_input& ti, t_user* user) +{ + if (m_use_sql && m_config.m_log_scrape) + m_scrape_log_buffer += Csql_query(m_database, "(?,?,?),")(ntohl(ti.m_ipa))(user ? user->uid : 0)(srv_time()).read(); + if (!m_config.m_anonymous_scrape && !user) + return "d14:failure reason25:unregistered torrent passe"; + std::string d; + d += "d5:filesd"; + if (ti.m_info_hashes.empty()) + { + m_stats.scraped_full++; + d.reserve(90 * m_torrents.size()); + for (auto& i : m_torrents) + { + if (i.second.leechers || i.second.seeders) + d += (boost::format("20:%sd8:completei%de10:downloadedi%de10:incompletei%dee") % boost::make_iterator_range(i.first) % i.second.seeders % i.second.completed % i.second.leechers).str(); + } + } + else + { + m_stats.scraped_http++; + if (ti.m_info_hashes.size() > 1) + m_stats.scraped_multi++; + for (auto& j : ti.m_info_hashes) + { + if (const t_torrent* i = find_torrent(j)) + d += (boost::format("20:%sd8:completei%de10:downloadedi%de10:incompletei%dee") % j % i->seeders % i->completed % i->leechers).str(); + } + } + d += "e"; + if (m_config.m_scrape_interval) + d += (boost::format("5:flagsd20:min_request_intervali%dee") % m_config.m_scrape_interval).str(); + d += "e"; + return d; +} + +void debug(const t_torrent& t, std::ostream& os) +{ + for (auto& i : t.peers) + { + os << "" + Csocket::inet_ntoa(i.first.host_) + << "" << ntohs(i.second.port) + << "" << i.second.uid + << "" << i.second.left + << "" << srv_time() - i.second.mtime + << "" << hex_encode(i.second.peer_id); + } +} + +std::string srv_debug(const Ctracker_input& ti) +{ + std::ostringstream os; + os << "XBT Tracker"; + os << ""; + if (ti.m_info_hash.empty()) + { + for (auto& i : m_torrents) + { + if (!i.second.leechers && !i.second.seeders) + continue; + os << "
" << i.second.fid + << "" << hex_encode(i.first) << "" + << "" << (i.second.dirty ? '*' : ' ') + << "" << i.second.leechers + << "" << i.second.seeders; + } + } + else if (const t_torrent* i = find_torrent(ti.m_info_hash)) + debug(*i, os); + os << "
"; + return os.str(); +} + +std::string srv_statistics() +{ + std::ostringstream os; + os << "XBT Tracker"; + os << ""; + long long leechers = 0; + long long seeders = 0; + int torrents = 0; + for (auto& i : m_torrents) + { + leechers += i.second.leechers; + seeders += i.second.seeders; + torrents += i.second.leechers || i.second.seeders; + } + int peers = leechers + seeders; + time_t t = srv_time(); + time_t up_time = std::max(1, t - m_stats.start_time); + os << "" + << "
peers" << peers; + if (peers) + { + os << "
seeders" << seeders << "" << seeders * 100 / peers << " %" + << "
leechers" << leechers << "" << leechers * 100 / peers << " %"; + } + os << "
torrents" << torrents + << "
" + << "
accepted tcp" << m_stats.accepted_tcp << "" << m_stats.accepted_tcp / up_time << " /s" + << "
slow tcp" << m_stats.slow_tcp << "" << m_stats.slow_tcp / up_time << " /s" + << "
rejected tcp" << m_stats.rejected_tcp + << "
accept errors" << m_stats.accept_errors + << "
received udp" << m_stats.received_udp << "" << m_stats.received_udp / up_time << " /s" + << "
sent udp" << m_stats.sent_udp << "" << m_stats.sent_udp / up_time << " /s"; + if (m_stats.announced()) + { + os << "
announced" << m_stats.announced() << "" << m_stats.announced() * 100 / m_stats.accepted_tcp << " %" + << "
announced http " << m_stats.announced_http << "" << m_stats.announced_http * 100 / m_stats.announced() << " %" + << "
announced udp" << m_stats.announced_udp << "" << m_stats.announced_udp * 100 / m_stats.announced() << " %"; + } + os << "
scraped full" << m_stats.scraped_full; + os << "
scraped multi" << m_stats.scraped_multi; + if (m_stats.scraped()) + { + os << "
scraped" << m_stats.scraped() << "" << m_stats.scraped() * 100 / m_stats.accepted_tcp << " %" + << "
scraped http" << m_stats.scraped_http << "" << m_stats.scraped_http * 100 / m_stats.scraped() << " %" + << "
scraped udp" << m_stats.scraped_udp << "" << m_stats.scraped_udp * 100 / m_stats.scraped() << " %"; + } + os << "
" + << "
up time" << duration2a(up_time) + << "
" + << "
anonymous announce" << m_config.m_anonymous_announce + << "
anonymous scrape" << m_config.m_anonymous_scrape + << "
auto register" << m_config.m_auto_register + << "
full scrape" << m_config.m_full_scrape + << "
read config time" << t - m_read_config_time << " / " << m_config.m_read_config_interval + << "
clean up time" << t - m_clean_up_time << " / " << m_config.m_clean_up_interval + << "
read db files time" << t - m_read_db_torrents_time << " / " << m_config.m_read_db_interval; + if (m_use_sql) + { + os << "
read db users time" << t - m_read_db_users_time << " / " << m_config.m_read_db_interval + << "
write db files time" << t - m_write_db_torrents_time << " / " << m_config.m_write_db_interval + << "
write db users time" << t - m_write_db_users_time << " / " << m_config.m_write_db_interval; + } + os << "
"; + return os.str(); +} + +t_user* find_user_by_torrent_pass(str_ref v, str_ref info_hash) +{ + if (v.size() != 32) + return NULL; + if (t_user* user = find_user_by_uid(read_int(4, hex_decode(v.substr(0, 8))))) + { + if (Csha1((boost::format("%s %d %d %s") % m_config.m_torrent_pass_private_key % user->torrent_pass_version % user->uid % info_hash).str()).read().substr(0, 12) == hex_decode(v.substr(8, 24))) + return user; + } + return find_ptr2(m_users_torrent_passes, to_array(v)); +} + +void srv_term() +{ + g_sig_term = true; +} + +void test_announce() +{ + t_user* u = find_ptr(m_users, 1); + Ctracker_input i; + i.m_info_hash = "IHIHIHIHIHIHIHIHIHIH"; + memcpy(i.m_peer_id.data(), str_ref("PIPIPIPIPIPIPIPIPIPI")); + i.m_ipa = htonl(0x7f000063); + i.m_port = 54321; + std::cout << srv_insert_peer(i, false, u) << std::endl; + write_db_torrents(); + write_db_users(); + m_time++; + i.m_uploaded = 1 << 30; + i.m_downloaded = 1 << 20; + std::cout << srv_insert_peer(i, false, u) << std::endl; + write_db_torrents(); + write_db_users(); + m_time += 3600; + clean_up(); + write_db_torrents(); + write_db_users(); +} diff --git a/Tracker/.svn/text-base/server.h.svn-base b/Tracker/.svn/text-base/server.h.svn-base new file mode 100644 index 0000000..b82fa46 --- /dev/null +++ b/Tracker/.svn/text-base/server.h.svn-base @@ -0,0 +1,129 @@ +#pragma once + +#include "config.h" +#include "tracker_input.h" + +class Cstats +{ +public: + long long announced() const + { + return announced_http + announced_udp; + } + + long long scraped() const + { + return scraped_http + scraped_udp; + } + + long long accept_errors = 0; + long long accepted_tcp = 0; + long long announced_http = 0; + long long announced_udp = 0; + long long received_udp = 0; + long long rejected_tcp = 0; + long long scraped_full = 0; + long long scraped_http = 0; + long long scraped_multi = 0; + long long scraped_udp = 0; + long long sent_udp = 0; + long long slow_tcp = 0; + time_t start_time = time(NULL); +}; + +class peer_key_c +{ +public: + peer_key_c(int host, int uid) + { + host_ = host; +#ifdef PEERS_KEY + uid_ = uid; +#else + (void)uid; +#endif + } + + bool operator==(peer_key_c v) const + { +#ifdef PEERS_KEY + return host_ == v.host_ && uid_ == v.uid_; +#else + return host_ == v.host_; +#endif + } + + bool operator<(peer_key_c v) const + { +#ifdef PEERS_KEY + return host_ < v.host_ || host_ == v.host_ && uid_ < v.uid_; +#else + return host_ < v.host_; +#endif + } + + friend std::size_t hash_value(const peer_key_c& v) + { + std::size_t seed = boost::hash_value(v.host_); +#ifdef PEERS_KEY + boost::hash_combine(seed, v.uid_); +#endif + return seed; + } + + int host_; +#ifdef PEERS_KEY + int uid_; +#endif +}; + +struct t_peer +{ + long long downloaded; + long long uploaded; + time_t mtime = 0; + int uid; + short port; + bool left; + std::array peer_id; +}; + +struct t_torrent +{ + void select_peers(mutable_str_ref& d, const Ctracker_input&) const; + + boost::unordered_map peers; + time_t ctime; + int completed = 0; + int fid = 0; + int leechers = 0; + int seeders = 0; + bool dirty = true; +}; + +struct t_user +{ + int uid; + int peers_limit = 0; + int torrent_pass_version = 0; + int wait_time = 0; + bool can_leech = true; + bool marked; +}; + +const t_torrent* find_torrent(const std::string& id); +t_user* find_user_by_torrent_pass(str_ref, str_ref info_hash); +t_user* find_user_by_uid(int v); +long long srv_secret(); +const Cconfig& srv_config(); +Cdatabase& srv_database(); +Cstats& srv_stats(); +time_t srv_time(); + +int srv_run(const std::string& table_prefix, bool use_sql, const std::string& conf_file); +void srv_term(); +std::string srv_debug(const Ctracker_input&); +std::string srv_insert_peer(const Ctracker_input&, bool udp, t_user*); +std::string srv_scrape(const Ctracker_input&, t_user*); +std::string srv_select_peers(const Ctracker_input&); +std::string srv_statistics(); diff --git a/Tracker/.svn/text-base/stdafx.cpp.svn-base b/Tracker/.svn/text-base/stdafx.cpp.svn-base new file mode 100644 index 0000000..fd4f341 --- /dev/null +++ b/Tracker/.svn/text-base/stdafx.cpp.svn-base @@ -0,0 +1 @@ +#include "stdafx.h" diff --git a/Tracker/.svn/text-base/stdafx.h.svn-base b/Tracker/.svn/text-base/stdafx.h.svn-base new file mode 100644 index 0000000..5e63134 --- /dev/null +++ b/Tracker/.svn/text-base/stdafx.h.svn-base @@ -0,0 +1,36 @@ +#pragma once + +#define FD_SETSIZE 1024 +#define NOMINMAX + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef unsigned char byte; diff --git a/Tracker/.svn/text-base/tracker_input.cpp.svn-base b/Tracker/.svn/text-base/tracker_input.cpp.svn-base new file mode 100644 index 0000000..ec8c836 --- /dev/null +++ b/Tracker/.svn/text-base/tracker_input.cpp.svn-base @@ -0,0 +1,62 @@ +#include "stdafx.h" +#include "tracker_input.h" + +void Ctracker_input::set(const std::string& name, const std::string& value) +{ + if (name.empty()) + return; + switch (name[0]) + { + case 'd': + if (name == "downloaded") + m_downloaded = to_int(value); + break; + case 'e': + if (name == "event") + { + if (value == "completed") + m_event = e_completed; + else if (value == "started") + m_event = e_started; + else if (value == "stopped") + m_event = e_stopped; + else + m_event = e_none; + } + break; + case 'i': + if (name == "info_hash" && value.size() == 20) + { + m_info_hash = value; + m_info_hashes.push_back(value); + } + else if (name == "ip") + m_ipa = inet_addr(value.c_str()); + break; + case 'l': + if (name == "left") + m_left = to_int(value); + break; + case 'p': + if (name == "peer_id" && value.size() == 20) + memcpy(m_peer_id, value); + else if (name == "port") + m_port = htons(to_int(value)); + break; + case 'u': + if (name == "uploaded") + m_uploaded = to_int(value); + break; + } +} + +bool Ctracker_input::valid() const +{ + return m_downloaded >= 0 + && (m_event != e_completed || !m_left) + && m_info_hash.size() == 20 + && m_left >= -1 + && m_peer_id.size() == 20 + && m_port >= 0 + && m_uploaded >= 0; +} diff --git a/Tracker/.svn/text-base/tracker_input.h.svn-base b/Tracker/.svn/text-base/tracker_input.h.svn-base new file mode 100644 index 0000000..6375481 --- /dev/null +++ b/Tracker/.svn/text-base/tracker_input.h.svn-base @@ -0,0 +1,36 @@ +#pragma once + +class Ctracker_input +{ +public: + void set(const std::string& name, const std::string& value); + bool valid() const; + + bool is_seeder() const + { + return !m_left; + } + + bool is_leecher() const + { + return m_left; + } + + enum t_event + { + e_none, + e_completed, + e_started, + e_stopped, + }; + + t_event m_event = e_none; + std::string m_info_hash; + std::vector m_info_hashes; + std::array m_peer_id; + long long m_downloaded = 0; + long long m_left = 0; + long long m_uploaded = 0; + int m_ipa = 0; + int m_port = 0; +}; diff --git a/Tracker/.svn/text-base/transaction.cpp.svn-base b/Tracker/.svn/text-base/transaction.cpp.svn-base new file mode 100644 index 0000000..88584cb --- /dev/null +++ b/Tracker/.svn/text-base/transaction.cpp.svn-base @@ -0,0 +1,157 @@ +#include "stdafx.h" +#include "transaction.h" + +#include +#include "server.h" + +Ctransaction::Ctransaction(const Csocket& s) : + m_s(s) +{ +} + +long long Ctransaction::connection_id() const +{ + const int cb_s = 12; + char s[cb_s]; + write_int(8, s, srv_secret()); + write_int(4, s + 8, m_a.sin_addr.s_addr); + char d[20]; + Csha1(data_ref(s, cb_s)).read(d); + return read_int(8, d); +} + +void Ctransaction::recv() +{ + const int cb_b = 2 << 10; + char b[cb_b]; + for (int i = 0; i < 10000; i++) + { + socklen_t cb_a = sizeof(sockaddr_in); + int r = m_s.recvfrom(mutable_data_ref(b, cb_b), reinterpret_cast(&m_a), &cb_a); + if (r == SOCKET_ERROR) + { + if (WSAGetLastError() != WSAEWOULDBLOCK) + std::cerr << "recv failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; + return; + } + srv_stats().received_udp++; + if (r < uti_size) + return; + switch (read_int(4, b + uti_action, b + r)) + { + case uta_connect: + if (r >= utic_size) + send_connect(data_ref(b, r)); + break; + case uta_announce: + if (r >= utia_size) + send_announce(data_ref(b, r)); + break; + case uta_scrape: + if (r >= utis_size) + send_scrape(data_ref(b, r)); + break; + } + } +} + +void Ctransaction::send_connect(data_ref r) +{ + if (read_int(8, &r[uti_connection_id], r.end()) != 0x41727101980ll) + return; + const int cb_d = 2 << 10; + char d[cb_d]; + write_int(4, d + uto_action, uta_connect); + write_int(4, d + uto_transaction_id, read_int(4, &r[uti_transaction_id], r.end())); + write_int(8, d + utoc_connection_id, connection_id()); + send(data_ref(d, utoc_size)); +} + +void Ctransaction::send_announce(data_ref r) +{ + if (read_int(8, &r[uti_connection_id], r.end()) != connection_id()) + return; + if (!srv_config().m_anonymous_announce) + { + send_error(r, "access denied"); + return; + } + Ctracker_input ti; + ti.m_downloaded = read_int(8, &r[utia_downloaded], r.end()); + ti.m_event = static_cast(read_int(4, &r[utia_event], r.end())); + ti.m_info_hash.assign(reinterpret_cast(&r[utia_info_hash]), 20); + ti.m_ipa = read_int(4, &r[utia_ipa], r.end()) && is_private_ipa(m_a.sin_addr.s_addr) + ? htonl(read_int(4, &r[utia_ipa], r.end())) + : m_a.sin_addr.s_addr; + ti.m_left = read_int(8, &r[utia_left], r.end()); + memcpy(ti.m_peer_id.data(), &r[utia_peer_id], 20); + ti.m_port = htons(read_int(2, &r[utia_port], r.end())); + ti.m_uploaded = read_int(8, &r[utia_uploaded], r.end()); + std::string error = srv_insert_peer(ti, true, NULL); + if (!error.empty()) + { + send_error(r, error); + return; + } + auto torrent = find_torrent(ti.m_info_hash); + if (!torrent) + return; + char d[2 << 10]; + write_int(4, d + uto_action, uta_announce); + write_int(4, d + uto_transaction_id, read_int(4, &r[uti_transaction_id], r.end())); + write_int(4, d + utoa_interval, srv_config().m_announce_interval); + write_int(4, d + utoa_leechers, torrent->leechers); + write_int(4, d + utoa_seeders, torrent->seeders); + mutable_str_ref peers(d + utoa_size, 300); + torrent->select_peers(peers, ti); + send(data_ref(d, peers.begin())); +} + +void Ctransaction::send_scrape(data_ref r) +{ + if (read_int(8, &r[uti_connection_id], r.end()) != connection_id()) + return; + if (!srv_config().m_anonymous_scrape) + { + send_error(r, "access denied"); + return; + } + const int cb_d = 2 << 10; + char d[cb_d]; + write_int(4, d + uto_action, uta_scrape); + write_int(4, d + uto_transaction_id, read_int(4, &r[uti_transaction_id], r.end())); + char* w = d + utos_size; + for (r.advance_begin(utis_size); r.size() >= 20 && w + 12 <= d + cb_d; r.advance_begin(20)) + { + if (auto t = find_torrent(r.substr(0, 20).s())) + { + w = write_int(4, w, t->seeders); + w = write_int(4, w, t->completed); + w = write_int(4, w, t->leechers); + } + else + { + w = write_int(4, w, 0); + w = write_int(4, w, 0); + w = write_int(4, w, 0); + } + } + srv_stats().scraped_udp++; + send(data_ref(d, w)); +} + +void Ctransaction::send_error(data_ref r, const std::string& msg) +{ + char d[2 << 10]; + write_int(4, d + uto_action, uta_error); + write_int(4, d + uto_transaction_id, read_int(4, &r[uti_transaction_id], r.end())); + memcpy(d + utoe_size, msg); + send(data_ref(d, utoe_size + msg.size())); +} + +void Ctransaction::send(data_ref b) +{ + if (m_s.sendto(b, reinterpret_cast(&m_a), sizeof(sockaddr_in)) != b.size()) + std::cerr << "send failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; + srv_stats().sent_udp++; +} diff --git a/Tracker/.svn/text-base/transaction.h.svn-base b/Tracker/.svn/text-base/transaction.h.svn-base new file mode 100644 index 0000000..18b52cd --- /dev/null +++ b/Tracker/.svn/text-base/transaction.h.svn-base @@ -0,0 +1,17 @@ +#pragma once + +class Ctransaction +{ +public: + long long connection_id() const; + void recv(); + void send(data_ref); + void send_announce(data_ref); + void send_connect(data_ref); + void send_scrape(data_ref); + void send_error(data_ref, const std::string& msg); + Ctransaction(const Csocket&); +private: + const Csocket& m_s; + sockaddr_in m_a; +}; diff --git a/Tracker/.svn/text-base/xbt_tracker.conf.default.svn-base b/Tracker/.svn/text-base/xbt_tracker.conf.default.svn-base new file mode 100644 index 0000000..3d38662 --- /dev/null +++ b/Tracker/.svn/text-base/xbt_tracker.conf.default.svn-base @@ -0,0 +1,4 @@ +mysql_host = localhost +mysql_user = xbt +mysql_password = +mysql_database = xbt diff --git a/Tracker/.svn/text-base/xbt_tracker.sql.svn-base b/Tracker/.svn/text-base/xbt_tracker.sql.svn-base new file mode 100644 index 0000000..e836da8 --- /dev/null +++ b/Tracker/.svn/text-base/xbt_tracker.sql.svn-base @@ -0,0 +1,89 @@ +-- create table if not exists xbt_announce_log +-- ( + -- id int not null auto_increment, + -- ipa int unsigned not null, + -- port int not null, + -- event int not null, + -- info_hash binary(20) not null, + -- peer_id binary(20) not null, + -- downloaded bigint unsigned not null, + -- left0 bigint unsigned not null, + -- uploaded bigint unsigned not null, + -- uid int not null, + -- mtime int not null, + -- primary key (id) +-- ) engine = myisam; + +create table if not exists xbt_config +( + name varchar(255) not null, + value varchar(255) not null +); + +-- create table if not exists xbt_deny_from_clients +-- ( + -- peer_id char(20) not null +-- ); + +-- create table if not exists xbt_deny_from_hosts +-- ( + -- begin int unsigned not null, + -- end int unsigned not null +-- ); + +create table if not exists xbt_files +( + fid int not null auto_increment, + info_hash binary(20) not null, + leechers int not null default 0, + seeders int not null default 0, + completed int not null default 0, + flags int not null default 0, + mtime int not null, + ctime int not null, + primary key (fid), + unique key (info_hash) +); + +create table if not exists xbt_files_users +( + fid int not null, + uid int not null, + active tinyint not null, + announced int not null, + completed int not null, + downloaded bigint unsigned not null, + `left` bigint unsigned not null, + uploaded bigint unsigned not null, + mtime int not null, + unique key (fid, uid), + key (uid) +); + +-- create table if not exists xbt_scrape_log +-- ( + -- id int not null auto_increment, + -- ipa int unsigned not null, + -- uid int not null, + -- mtime int not null, + -- primary key (id) +-- ) engine = myisam; + +create table if not exists xbt_users +( + uid int not null auto_increment, + -- can_leech tinyint not null default 1, + -- wait_time int not null default 0, + -- peers_limit int not null default 0, + -- torrent_pass char(32) not null, + torrent_pass_version int not null default 0, + downloaded bigint unsigned not null default 0, + uploaded bigint unsigned not null default 0, + primary key (uid) +); + +-- alter table xbt_files_users add down_rate int unsigned not null; +-- alter table xbt_files_users add up_rate int unsigned not null; + +-- alter table xbt_files_users add foreign key (fid) references xbt_files (fid) on delete cascade; +-- alter table xbt_files_users add foreign key (uid) references xbt_users (uid) on delete cascade; diff --git a/Tracker/CMakeLists.txt b/Tracker/CMakeLists.txt new file mode 100644 index 0000000..1b249e6 --- /dev/null +++ b/Tracker/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 2.4) +project(xbt-tracker) +set(CMAKE_BUILD_TYPE release) +# set(CMAKE_CXX_COMPILER clang++) +set(CMAKE_CXX_FLAGS -std=c++11) +set(CPACK_GENERATOR DEB) +set(CPACK_PACKAGE_CONTACT "Olaf van der Spek ") +set(CPACK_STRIP_FILES true) +include_directories(. ../misc) +include(CheckIncludeFileCXX) +check_include_file_cxx(sys/epoll.h HAVE_SYS_EPOLL) +if(HAVE_SYS_EPOLL) + add_definitions(-DEPOLL) +endif() +# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -Wall -Wextra") +# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wno-parentheses -Wno-sign-compare") +add_executable( + xbt_tracker + ../misc/bt_misc.cpp + ../misc/database.cpp + ../misc/sha1.cpp + ../misc/socket.cpp + ../misc/sql_query.cpp + ../misc/xcc_z.cpp + config.cpp + connection.cpp + epoll.cpp + server.cpp + tracker_input.cpp + transaction.cpp + "XBT Tracker.cpp" +) +target_link_libraries(xbt_tracker mysqlclient z) +install(TARGETS xbt_tracker DESTINATION bin) +include(CPack) diff --git a/Tracker/COPYING b/Tracker/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/Tracker/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Tracker/XBT Tracker.cpp b/Tracker/XBT Tracker.cpp new file mode 100644 index 0000000..b31b0c9 --- /dev/null +++ b/Tracker/XBT Tracker.cpp @@ -0,0 +1,140 @@ +#include "stdafx.h" +#include +#include "config.h" +#include "server.h" + +std::string g_conf_file = "xbt_tracker.conf"; +const char* g_service_name = "XBT Tracker"; + +int main1() +{ + srand(static_cast(time(NULL))); + Cconfig config; + if (config.load(g_conf_file)) +#ifdef WIN32 + { + char b[MAX_PATH]; + *b = 0; + GetModuleFileName(NULL, b, MAX_PATH); + if (*b) + strrchr(b, '\\')[1] = 0; + strcat(b, "xbt_tracker.conf"); + if (config.load(b)) + std::cerr << "Unable to read " << g_conf_file << std::endl; + else + g_conf_file = b; + } +#else + std::cerr << "Unable to read " << g_conf_file << std::endl; +#endif + try + { + if (config.m_mysql_host != "-") + srv_database().open(config.m_mysql_host, config.m_mysql_user, config.m_mysql_password, config.m_mysql_database, true); + } + catch (bad_query& e) + { + std::cerr << e.what() << std::endl; + return 1; + } + if (!config.m_query_log.empty()) + { + static std::ofstream os(config.m_query_log.c_str()); + srv_database().set_query_log(&os); + } + return srv_run(config.m_mysql_table_prefix, config.m_mysql_host != "-", g_conf_file); +} + +#ifdef WIN32 +static SERVICE_STATUS g_service_status; +static SERVICE_STATUS_HANDLE gh_service_status; + +void WINAPI nt_service_handler(DWORD op) +{ + switch (op) + { + case SERVICE_CONTROL_STOP: + g_service_status.dwCurrentState = SERVICE_STOP_PENDING; + SetServiceStatus(gh_service_status, &g_service_status); + srv_term(); + break; + } + SetServiceStatus(gh_service_status, &g_service_status); +} + +void WINAPI nt_service_main(DWORD argc, LPTSTR* argv) +{ + g_service_status.dwCheckPoint = 0; + g_service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP; + g_service_status.dwCurrentState = SERVICE_START_PENDING; + g_service_status.dwServiceSpecificExitCode = 0; + g_service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + g_service_status.dwWaitHint = 0; + g_service_status.dwWin32ExitCode = NO_ERROR; + if (!(gh_service_status = RegisterServiceCtrlHandler(g_service_name, nt_service_handler))) + return; + SetServiceStatus(gh_service_status, &g_service_status); + g_service_status.dwCurrentState = SERVICE_RUNNING; + SetServiceStatus(gh_service_status, &g_service_status); + main1(); + g_service_status.dwCurrentState = SERVICE_STOPPED; + SetServiceStatus(gh_service_status, &g_service_status); +} +#endif + +int main(int argc, char* argv[]) +{ +#ifdef WIN32 + if (argc >= 2) + { + if (!strcmp(argv[1], "--install")) + { + if (nt_service_install(g_service_name)) + { + std::cerr << "Failed to install service " << g_service_name << "." << std::endl; + return 1; + } + std::cout << "Service " << g_service_name << " has been installed." << std::endl; + return 0; + } + else if (!strcmp(argv[1], "--uninstall")) + { + if (nt_service_uninstall(g_service_name)) + { + std::cerr << "Failed to uninstall service " << g_service_name << "." << std::endl; + return 1; + } + std::cout << "Service " << g_service_name << " has been uninstalled." << std::endl; + return 0; + } + else if (!strcmp(argv[1], "--conf_file") && argc >= 3) + g_conf_file = argv[2]; + else + return 1; + } +#ifdef NDEBUG + SERVICE_TABLE_ENTRY st[] = + { + { "", nt_service_main }, + { NULL, NULL } + }; + if (StartServiceCtrlDispatcher(st)) + return 0; + if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED + && GetLastError() != ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) + return 1; +#endif +#else + if (argc >= 2) + { + if (!strcmp(argv[1], "--conf_file") && argc >= 3) + g_conf_file = argv[2]; + else + { + std::cerr << " --conf_file arg (=xbt_tracker.conf)" << std::endl; + return 1; + } + } +#endif + return main1(); +} diff --git a/Tracker/XBT Tracker.nsi b/Tracker/XBT Tracker.nsi new file mode 100644 index 0000000..cb5c4cf --- /dev/null +++ b/Tracker/XBT Tracker.nsi @@ -0,0 +1,46 @@ +!define VERSION "0.3.0" + +Name "XBT Tracker ${VERSION}" +Outfile "XBT_Tracker-${VERSION}.exe" +InstallDir "$PROGRAMFILES\XBT\Tracker" +InstallDirRegKey HKLM "Software\XBT\Tracker" "InstallDir" +XPStyle on +Page directory +Page instfiles +UninstPage uninstConfirm +UninstPage instfiles + +Section "Install" + SetShellVarContext all + SetOutPath "$INSTDIR" + + Delete "$INSTDIR\XBT Tracker.exe" + Delete "$INSTDIR\XBT Tracker Old.exe" + Rename "$INSTDIR\XBT Tracker.exe" "$INSTDIR\XBT Tracker Old.exe" + File "release\XBT Tracker.exe" + File xbt_tracker.conf.default + File xbt_tracker.sql + SetOverwrite off + File /oname=xbt_tracker.conf xbt_tracker.conf.default + SetOutPath "$INSTDIR\htdocs" + File htdocs\* + Exec "$INSTDIR\XBT Tracker.exe --install" + WriteUninstaller "$INSTDIR\Uninstall.exe" + CreateShortCut "$SMPROGRAMS\XBT Tracker.lnk" "$INSTDIR\XBT Tracker.exe" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\XBT Tracker" "DisplayName" "XBT Tracker ${VERSION}" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\XBT Tracker" "UninstallString" '"$INSTDIR\Uninstall.exe"' + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\XBT Tracker" "NoModify" 1 + WriteRegStr HKLM "Software\XBT\Tracker" "InstallDir" "$INSTDIR" +SectionEnd + +Section "Uninstall" + SetShellVarContext all + ExecWait 'net stop "XBT Tracker"' + ExecWait "$INSTDIR\XBT Tracker.exe --uninstall" + Delete "$SMPROGRAMS\XBT Tracker.lnk" + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\XBT Tracker" + DeleteRegKey HKLM "Software\XBT\Tracker" + DeleteRegKey /ifempty HKLM "Software\XBT" + RMDir /r "$PROGRAMFILES\XBT\Tracker" + RMDir "$PROGRAMFILES\XBT" +SectionEnd diff --git a/Tracker/XBT Tracker.rc b/Tracker/XBT Tracker.rc new file mode 100644 index 0000000..c7dafe6 --- /dev/null +++ b/Tracker/XBT Tracker.rc @@ -0,0 +1,3 @@ +#include "resource.h" + +IDR_MAINFRAME ICON "res\\XBT Tracker.ico" diff --git a/Tracker/XBT Tracker.sln b/Tracker/XBT Tracker.sln new file mode 100644 index 0000000..2a4ad44 --- /dev/null +++ b/Tracker/XBT Tracker.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "XBT Tracker", "XBT Tracker.vcxproj", "{73C570AC-E7C3-451D-A5C6-7A2532F0121B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {73C570AC-E7C3-451D-A5C6-7A2532F0121B}.Debug|Win32.ActiveCfg = Debug|Win32 + {73C570AC-E7C3-451D-A5C6-7A2532F0121B}.Debug|Win32.Build.0 = Debug|Win32 + {73C570AC-E7C3-451D-A5C6-7A2532F0121B}.Release|Win32.ActiveCfg = Release|Win32 + {73C570AC-E7C3-451D-A5C6-7A2532F0121B}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Tracker/XBT Tracker.vcxproj b/Tracker/XBT Tracker.vcxproj new file mode 100644 index 0000000..06564ce --- /dev/null +++ b/Tracker/XBT Tracker.vcxproj @@ -0,0 +1,173 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {73C570AC-E7C3-451D-A5C6-7A2532F0121B} + XBT Tracker + $(VCTargetsPath11) + + + + Application + false + MultiByte + v120 + + + Application + false + MultiByte + v120 + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + + + + + + + + MaxSpeed + OnlyExplicitInline + true + ..\misc;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + true + Use + stdafx.h + Level3 + stdafx.h + + + NDEBUG;%(PreprocessorDefinitions) + 0x0413 + + + zlib.lib;%(AdditionalDependencies) + Console + UseLinkTimeCodeGeneration + false + + + MachineX86 + + + + + + + + + Disabled + ..\misc;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + Use + stdafx.h + Level1 + stdafx.h + + + _DEBUG;%(PreprocessorDefinitions) + 0x0413 + + + zlibd.lib;%(AdditionalDependencies) + + + true + Console + false + + + MachineX86 + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Tracker/client.h b/Tracker/client.h new file mode 100644 index 0000000..d24cca5 --- /dev/null +++ b/Tracker/client.h @@ -0,0 +1,18 @@ +#pragma once + +class Cserver; + +class Cclient +{ +public: + virtual void process_events(int) = 0; + + virtual ~Cclient() {} + + const Csocket& s() const + { + return m_s; + } +protected: + Csocket m_s; +}; diff --git a/Tracker/config.cpp b/Tracker/config.cpp new file mode 100644 index 0000000..f1df4e8 --- /dev/null +++ b/Tracker/config.cpp @@ -0,0 +1,118 @@ +#include "stdafx.h" +#include "config.h" + +Cconfig::Cconfig() +{ + fill_maps(NULL); +} + +Cconfig::Cconfig(const Cconfig& v) +{ + fill_maps(&v); +} + +const Cconfig& Cconfig::operator=(const Cconfig& v) +{ + fill_maps(&v); + return *this; +} + +void Cconfig::fill_maps(const Cconfig* v) +{ + { + t_attribute attributes[] = + { + { "auto_register", &m_auto_register, false }, + { "anonymous_announce", &m_anonymous_announce, true }, + { "anonymous_scrape", &m_anonymous_scrape, true }, + { "daemon", &m_daemon, true }, + { "debug", &m_debug, false }, + { "full_scrape", &m_full_scrape, false }, + { "gzip_scrape", &m_gzip_scrape, true }, + { "log_access", &m_log_access, false }, + { "log_announce", &m_log_announce, false }, + { "log_scrape", &m_log_scrape, false }, + { NULL, NULL, false } + }; + fill_map(attributes, v ? &v->m_attributes_bool : NULL, m_attributes_bool); + } + { + t_attribute attributes[] = + { + { "announce_interval", &m_announce_interval, 1800 }, + { "clean_up_interval", &m_clean_up_interval, 60 }, + { "read_config_interval", &m_read_config_interval, 60 }, + { "read_db_interval", &m_read_db_interval, 60 }, + { "scrape_interval", &m_scrape_interval, 0 }, + { "write_db_interval", &m_write_db_interval, 15 }, + { NULL, NULL, 0 } + }; + fill_map(attributes, v ? &v->m_attributes_int : NULL, m_attributes_int); + } + { + t_attribute attributes[] = + { + { "column_files_completed", &m_column_files_completed, "completed" }, + { "column_files_fid", &m_column_files_fid, "fid" }, + { "column_files_leechers", &m_column_files_leechers, "leechers" }, + { "column_files_seeders", &m_column_files_seeders, "seeders" }, + { "column_users_uid", &m_column_users_uid, "uid" }, + { "mysql_database", &m_mysql_database, "xbt" }, + { "mysql_host", &m_mysql_host, "" }, + { "mysql_password", &m_mysql_password, "" }, + { "mysql_table_prefix", &m_mysql_table_prefix, "xbt_" }, + { "mysql_user", &m_mysql_user, "" }, + { "offline_message", &m_offline_message, "" }, + { "pid_file", &m_pid_file, "" }, + { "query_log", &m_query_log, "" }, + { "redirect_url", &m_redirect_url, "" }, + { "table_announce_log", &m_table_announce_log, "" }, + { "table_files", &m_table_torrents, "" }, + { "table_files_users", &m_table_torrents_users, "" }, + { "table_scrape_log", &m_table_scrape_log, "" }, + { "table_users", &m_table_users, "" }, + { "torrent_pass_private_key", &m_torrent_pass_private_key, "" }, + { NULL, NULL, "" } + }; + fill_map(attributes, v ? &v->m_attributes_string : NULL, m_attributes_string); + } + if (v) + { + m_listen_ipas = v->m_listen_ipas; + m_listen_ports = v->m_listen_ports; + } +} + +int Cconfig::set(const std::string& name, const std::string& value) +{ + if (t_attribute* i = find_ptr(m_attributes_string, name)) + *i->value = value; + else if (name == "listen_ipa") + { + if (value != "*") + m_listen_ipas.insert(inet_addr(value.c_str())); + } + else + return set(name, atoi(value.c_str())); + return 0; +} + +int Cconfig::set(const std::string& name, int value) +{ + if (t_attribute* i = find_ptr(m_attributes_int, name)) + *i->value = value; + else if (name == "listen_port") + m_listen_ports.insert(value); + else + return set(name, static_cast(value)); + return 0; +} + +int Cconfig::set(const std::string& name, bool value) +{ + if (t_attribute* i = find_ptr(m_attributes_bool, name)) + *i->value = value; + else + return 1; + return 0; +} diff --git a/Tracker/config.h b/Tracker/config.h new file mode 100644 index 0000000..e59ece4 --- /dev/null +++ b/Tracker/config.h @@ -0,0 +1,55 @@ +#pragma once + +#include + +class Cconfig: public Cconfig_base +{ +public: + int set(const std::string& name, const std::string& value); + int set(const std::string& name, int value); + int set(const std::string& name, bool value); + Cconfig(); + Cconfig(const Cconfig&); + const Cconfig& operator=(const Cconfig&); + + bool m_anonymous_announce; + bool m_anonymous_scrape; + bool m_auto_register; + bool m_daemon; + bool m_debug; + bool m_full_scrape; + bool m_gzip_scrape; + bool m_log_access; + bool m_log_announce; + bool m_log_scrape; + int m_announce_interval; + int m_clean_up_interval; + int m_read_config_interval; + int m_read_db_interval; + int m_scrape_interval; + int m_write_db_interval; + std::string m_column_files_completed; + std::string m_column_files_fid; + std::string m_column_files_leechers; + std::string m_column_files_seeders; + std::string m_column_users_uid; + std::string m_mysql_database; + std::string m_mysql_host; + std::string m_mysql_password; + std::string m_mysql_table_prefix; + std::string m_mysql_user; + std::string m_offline_message; + std::string m_query_log; + std::string m_pid_file; + std::string m_redirect_url; + std::string m_table_announce_log; + std::string m_table_torrents; + std::string m_table_torrents_users; + std::string m_table_scrape_log; + std::string m_table_users; + std::string m_torrent_pass_private_key; + std::set m_listen_ipas; + std::set m_listen_ports; +private: + void fill_maps(const Cconfig*); +}; diff --git a/Tracker/connection.cpp b/Tracker/connection.cpp new file mode 100644 index 0000000..6ba4263 --- /dev/null +++ b/Tracker/connection.cpp @@ -0,0 +1,278 @@ +#include "stdafx.h" +#include "connection.h" + +#include "epoll.h" +#include "server.h" + +Cconnection::Cconnection(const Csocket& s, const sockaddr_in& a) +{ + m_s = s; + m_a = a; + m_ctime = srv_time(); + + m_state = 0; + m_w = m_read_b; +} + +int Cconnection::pre_select(fd_set* fd_read_set, fd_set* fd_write_set) +{ + FD_SET(m_s, fd_read_set); + if (!m_r.empty()) + FD_SET(m_s, fd_write_set); + return m_s; +} + +int Cconnection::post_select(fd_set* fd_read_set, fd_set* fd_write_set) +{ + return FD_ISSET(m_s, fd_read_set) && recv() + || FD_ISSET(m_s, fd_write_set) && send() + || srv_time() - m_ctime > 10 + || m_state == 5 && m_r.empty(); +} + +int Cconnection::recv() +{ + int r = m_s.recv(m_w); + if (!r) + { + m_state = 5; + return 0; + } + if (r == SOCKET_ERROR) + { + int e = WSAGetLastError(); + switch (e) + { + case WSAECONNABORTED: + case WSAECONNRESET: + return 1; + case WSAEWOULDBLOCK: + return 0; + } + std::cerr << "recv failed: " << Csocket::error2a(e) << std::endl; + return 1; + } + if (m_state == 5) + return 0; + const char* a = m_w.data(); + m_w.advance_begin(r); + int state; + do + { + state = m_state; + while (a < m_w.begin() && *a != '\n' && *a != '\r') + { + a++; + if (m_state) + m_state = 1; + } + if (a < m_w.begin()) + { + switch (m_state) + { + case 0: + read(std::string(&m_read_b.front(), a - &m_read_b.front())); + m_state = 1; + case 1: + case 3: + m_state += *a == '\n' ? 2 : 1; + break; + case 2: + case 4: + m_state++; + break; + } + a++; + } + } + while (state != m_state); + return 0; +} + +int Cconnection::send() +{ + if (m_r.empty()) + return 0; + int r = m_s.send(m_r); + if (r == SOCKET_ERROR) + { + int e = WSAGetLastError(); + switch (e) + { + case WSAECONNABORTED: + case WSAECONNRESET: + return 1; + case WSAEWOULDBLOCK: + return 0; + } + std::cerr << "send failed: " << Csocket::error2a(e) << std::endl; + return 1; + } + m_r.advance_begin(r); + if (m_r.empty()) + m_write_b.clear(); + return 0; +} + +void Cconnection::read(const std::string& v) +{ +#ifndef NDEBUG + std::cout << v << std::endl; +#endif + if (srv_config().m_log_access) + { + static std::ofstream f("xbt_tracker_raw.log"); + f << srv_time() << '\t' << inet_ntoa(m_a.sin_addr) << '\t' << ntohs(m_a.sin_port) << '\t' << v << std::endl; + } + Ctracker_input ti; + size_t e = v.find('?'); + if (e == std::string::npos) + e = v.size(); + else + { + size_t a = e + 1; + size_t b = v.find(' ', a); + if (b == std::string::npos) + return; + while (a < b) + { + size_t c = v.find('=', a); + if (c++ == std::string::npos) + break; + size_t d = v.find_first_of(" &", c); + if (d == std::string::npos) + break; + ti.set(v.substr(a, c - a - 1), uri_decode(v.substr(c, d - c))); + a = d + 1; + } + } + if (!ti.m_ipa || !is_private_ipa(m_a.sin_addr.s_addr)) + ti.m_ipa = m_a.sin_addr.s_addr; + str_ref torrent_pass; + size_t a = 4; + if (a < e && v[a] == '/') + { + a++; + if (a + 32 < e && v[a + 32] == '/') + { + torrent_pass.assign(&v[a], 32); + a += 33; + } + } + std::string h = "HTTP/1.0 200 OK\r\n"; + std::string s; + bool gzip = true; + switch (a < v.size() ? v[a] : 0) + { + case 'a': + if (ti.valid()) + { + gzip = false; + std::string error = srv_insert_peer(ti, false, find_user_by_torrent_pass(torrent_pass, ti.m_info_hash)); + s = error.empty() ? srv_select_peers(ti) : (boost::format("d14:failure reason%d:%se") % error.size() % error).str(); + } + break; + case 'd': + if (srv_config().m_debug) + { + h += "Content-Type: text/html; charset=us-ascii\r\n"; + s = srv_debug(ti); + } + break; + case 's': + if (v.size() >= 7 && v[6] == 't') + { + h += "Content-Type: text/html; charset=us-ascii\r\n"; + s = srv_statistics(); + } + else if (srv_config().m_full_scrape || !ti.m_info_hash.empty()) + { + gzip = srv_config().m_gzip_scrape && ti.m_info_hash.empty(); + s = srv_scrape(ti, find_user_by_torrent_pass(torrent_pass, ti.m_info_hash)); + } + break; + } + if (s.empty()) + { + if (!ti.m_info_hash.empty() || srv_config().m_redirect_url.empty()) + h = "HTTP/1.0 404 Not Found\r\n"; + else + { + h = "HTTP/1.0 302 Found\r\n" + "Location: " + srv_config().m_redirect_url + (ti.m_info_hash.empty() ? "" : "?info_hash=" + uri_encode(ti.m_info_hash)) + "\r\n"; + } + } + else if (gzip) + { + shared_data s2 = xcc_z::gzip(s); +#ifndef NDEBUG + static std::ofstream f("xbt_tracker_gzip.log"); + f << srv_time() << '\t' << v[5] << '\t' << s.size() << '\t' << s2.size() << std::endl; +#endif + if (s2.size() + 24 < s.size()) + { + h += "Content-Encoding: gzip\r\n"; + s = to_string(s2); + } + } + h += "\r\n"; +#ifdef WIN32 + m_write_b = shared_data(h.size() + s.size()); + memcpy(m_write_b.data(), h); + memcpy(m_write_b.data() + h.size(), s); + int r = m_s.send(m_write_b); +#else + std::array d; + d[0].iov_base = const_cast(h.data()); + d[0].iov_len = h.size(); + d[1].iov_base = const_cast(s.data()); + d[1].iov_len = s.size(); + msghdr m; + m.msg_name = NULL; + m.msg_namelen = 0; + m.msg_iov = const_cast(d.data()); + m.msg_iovlen = d.size(); + m.msg_control = NULL; + m.msg_controllen = 0; + m.msg_flags = 0; + int r = sendmsg(m_s, &m, MSG_NOSIGNAL); +#endif + if (r == SOCKET_ERROR) + { + if (WSAGetLastError() != WSAECONNRESET) + std::cerr << "send failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; + } + else if (r != h.size() + s.size()) + { +#ifndef WIN32 + if (r < h.size()) + { + m_write_b = shared_data(h.size() + s.size()); + memcpy(m_write_b.data(), h); + memcpy(m_write_b.data() + h.size(), s); + } + else + { + m_write_b = make_shared_data(s); + r -= h.size(); + } +#endif + m_r = m_write_b; + m_r.advance_begin(r); + } + if (m_r.empty()) + m_write_b.clear(); +} + +void Cconnection::process_events(int events) +{ + if (events & (EPOLLIN | EPOLLPRI | EPOLLERR | EPOLLHUP) && recv() + || events & EPOLLOUT && send() + || m_state == 5 && m_write_b.empty()) + m_s.close(); +} + +int Cconnection::run() +{ + return s() == INVALID_SOCKET || srv_time() - m_ctime > 10; +} diff --git a/Tracker/connection.h b/Tracker/connection.h new file mode 100644 index 0000000..861832c --- /dev/null +++ b/Tracker/connection.h @@ -0,0 +1,24 @@ +#pragma once + +#include "client.h" + +class Cconnection : public Cclient, boost::noncopyable +{ +public: + int run(); + void read(const std::string&); + int recv(); + int send(); + virtual void process_events(int); + int pre_select(fd_set* read, fd_set* write); + int post_select(fd_set* read, fd_set* write); + Cconnection(const Csocket&, const sockaddr_in&); +private: + sockaddr_in m_a; + time_t m_ctime; + int m_state; + std::array m_read_b; + shared_data m_write_b; + str_ref m_r; + mutable_str_ref m_w; +}; diff --git a/Tracker/epoll.cpp b/Tracker/epoll.cpp new file mode 100644 index 0000000..0aa26b4 --- /dev/null +++ b/Tracker/epoll.cpp @@ -0,0 +1,43 @@ +#include "epoll.h" + +#ifndef WIN32 +#include +#endif + +Cepoll::~Cepoll() +{ +#ifdef EPOLL + if (m_fd != -1) + close(m_fd); +#endif +} + +int Cepoll::create() +{ +#ifdef EPOLL + return m_fd = epoll_create(1); +#else + return 0; +#endif +} + +int Cepoll::ctl(int op, int fd, int events, void* p) +{ +#ifdef EPOLL + epoll_event e; + e.data.ptr = p; + e.events = events; + return epoll_ctl(m_fd, op, fd, &e); +#else + return 0; +#endif +} + +int Cepoll::wait(epoll_event* events, int maxevents, int timeout) +{ +#ifdef EPOLL + return epoll_wait(m_fd, events, maxevents, timeout); +#else + return 0; +#endif +} diff --git a/Tracker/epoll.h b/Tracker/epoll.h new file mode 100644 index 0000000..c3c9484 --- /dev/null +++ b/Tracker/epoll.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +#ifdef EPOLL +#include +#else +enum +{ + EPOLLIN = 1, + EPOLLOUT = 2, + EPOLLPRI = 4, + EPOLLERR = 8, + EPOLLHUP = 0x10, + EPOLLET = 0x20, + EPOLLONESHOT = 0x40, +}; + +enum +{ + EPOLL_CTL_ADD = 1, + EPOLL_CTL_MOD = 2, + EPOLL_CTL_DEL = 4, +}; + +typedef void epoll_event; +#endif + +class Cepoll: boost::noncopyable +{ +public: + int create(); + int ctl(int op, int fd, int events, void* p); + int wait(epoll_event* events, int maxevents, int timeout); + ~Cepoll(); +private: + int m_fd = -1; +}; diff --git a/Tracker/htdocs/.svn/all-wcprops b/Tracker/htdocs/.svn/all-wcprops new file mode 100644 index 0000000..b08afa1 --- /dev/null +++ b/Tracker/htdocs/.svn/all-wcprops @@ -0,0 +1,35 @@ +K 25 +svn:wc:ra_dav:version-url +V 43 +/svn/!svn/ver/2020/trunk/xbt/Tracker/htdocs +END +xbt.css +K 25 +svn:wc:ra_dav:version-url +V 51 +/svn/!svn/ver/2020/trunk/xbt/Tracker/htdocs/xbt.css +END +xbt_files.php +K 25 +svn:wc:ra_dav:version-url +V 57 +/svn/!svn/ver/2020/trunk/xbt/Tracker/htdocs/xbt_files.php +END +xbt_config.php +K 25 +svn:wc:ra_dav:version-url +V 58 +/svn/!svn/ver/2020/trunk/xbt/Tracker/htdocs/xbt_config.php +END +xbt_torrent_pass_version.php +K 25 +svn:wc:ra_dav:version-url +V 72 +/svn/!svn/ver/2020/trunk/xbt/Tracker/htdocs/xbt_torrent_pass_version.php +END +xbt_common.php +K 25 +svn:wc:ra_dav:version-url +V 58 +/svn/!svn/ver/2020/trunk/xbt/Tracker/htdocs/xbt_common.php +END diff --git a/Tracker/htdocs/.svn/entries b/Tracker/htdocs/.svn/entries new file mode 100644 index 0000000..fbf1652 --- /dev/null +++ b/Tracker/htdocs/.svn/entries @@ -0,0 +1,198 @@ +10 + +dir +2494 +http://xbt.googlecode.com/svn/trunk/xbt/Tracker/htdocs +http://xbt.googlecode.com/svn + + + +2010-04-15T16:24:59.778639Z +2020 +olafvdspek + + + + + + + + + + + + + + +a4ef9278-c6d2-effe-6dce-f9b4ebbbbc1a + +xbt.css +file + + + + +2015-07-14T06:50:49.252094Z +02cd778e52437b895070bd0ff18abd32 +2010-04-15T16:24:59.778639Z +2020 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +161 + +xbt_files.php +file + + + + +2015-07-14T06:50:49.252094Z +f4d7fc74fd5ca1f235cb25a280e8527c +2010-04-15T16:24:59.778639Z +2020 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +2352 + +xbt_config.php +file + + + + +2015-07-14T06:50:49.252094Z +8ed355e1aec257ed71809d3e1462d549 +2010-04-15T16:24:59.778639Z +2020 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +111 + +xbt_torrent_pass_version.php +file + + + + +2015-07-14T06:50:49.252094Z +5a7cc1dac254d471c69ca49ef0af6a7c +2010-04-15T16:24:59.778639Z +2020 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +867 + +xbt_common.php +file + + + + +2015-07-14T06:50:49.252094Z +cd1960a8b475c182007f0c886dfb3487 +2010-04-15T16:24:59.778639Z +2020 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +721 + diff --git a/Tracker/htdocs/.svn/prop-base/xbt.css.svn-base b/Tracker/htdocs/.svn/prop-base/xbt.css.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/htdocs/.svn/prop-base/xbt.css.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/htdocs/.svn/prop-base/xbt_common.php.svn-base b/Tracker/htdocs/.svn/prop-base/xbt_common.php.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/htdocs/.svn/prop-base/xbt_common.php.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/htdocs/.svn/prop-base/xbt_config.php.svn-base b/Tracker/htdocs/.svn/prop-base/xbt_config.php.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/htdocs/.svn/prop-base/xbt_config.php.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/htdocs/.svn/prop-base/xbt_files.php.svn-base b/Tracker/htdocs/.svn/prop-base/xbt_files.php.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/htdocs/.svn/prop-base/xbt_files.php.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/htdocs/.svn/prop-base/xbt_torrent_pass_version.php.svn-base b/Tracker/htdocs/.svn/prop-base/xbt_torrent_pass_version.php.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/Tracker/htdocs/.svn/prop-base/xbt_torrent_pass_version.php.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/Tracker/htdocs/.svn/text-base/xbt.css.svn-base b/Tracker/htdocs/.svn/text-base/xbt.css.svn-base new file mode 100644 index 0000000..fcdbf74 --- /dev/null +++ b/Tracker/htdocs/.svn/text-base/xbt.css.svn-base @@ -0,0 +1,5 @@ +img {border-style: none;} +table {border-collapse: collapse;} +td, th {border: 1px solid #000000;} +td {background-color: #cccccc;} +th {background-color: #ccccff;} diff --git a/Tracker/htdocs/.svn/text-base/xbt_common.php.svn-base b/Tracker/htdocs/.svn/text-base/xbt_common.php.svn-base new file mode 100644 index 0000000..3e9d3ba --- /dev/null +++ b/Tracker/htdocs/.svn/text-base/xbt_common.php.svn-base @@ -0,0 +1,31 @@ +', htmlspecialchars($query)); + $result = mysql_query($query); + if (!$result) + die(sprintf('%s
%s', htmlspecialchars(mysql_error()), htmlspecialchars($query))); + return $result; + } + + function db_query_first($query) + { + return mysql_fetch_assoc(db_query($query)); + } + + function db_query_first_field($query) + { + $row = mysql_fetch_array(db_query($query)); + return $row[0]; + } + + db_connect(); diff --git a/Tracker/htdocs/.svn/text-base/xbt_config.php.svn-base b/Tracker/htdocs/.svn/text-base/xbt_config.php.svn-base new file mode 100644 index 0000000..a1b7802 --- /dev/null +++ b/Tracker/htdocs/.svn/text-base/xbt_config.php.svn-base @@ -0,0 +1,5 @@ + + + +XBT Files +'); + printf('completed%d', $result['completed']); + printf('peers%d100 %%', $result['peers']); + if ($result['peers']) + { + printf('leechers%d%d %%', $result['leechers'], $result['leechers'] * 100 / $result['peers']); + printf('seeders%d%d %%', $result['seeders'], $result['seeders'] * 100 / $result['peers']); + } + printf('torrents%d', $result['torrents']); + printf('time%s', gmdate('Y-m-d H:i:s')); + echo(''); + echo('
'); + $results = db_query("select * from xbt_files where leechers or seeders order by ctime desc"); + echo(''); + echo(''); + echo(''); + printf('
fid'); + echo('info_hash'); + echo('leechers'); + echo('seeders'); + echo('completed'); + echo('modified'); + echo('created'); + while ($result = mysql_fetch_assoc($results)) + { + echo('
%d', $result['fid']); + printf('%s', bin2hex($result['info_hash'])); + echo(''); + if ($result['leechers']) + printf('%d', $result['leechers']); + echo(''); + if ($result['seeders']) + printf('%d', $result['seeders']); + echo(''); + if ($result['completed']) + printf('%d', $result['completed']); + printf('%s', gmdate('Y-m-d H:i:s', $result['mtime'])); + printf('%s', gmdate('Y-m-d H:i:s', $result['ctime'])); + } + echo('
'); +?> +
+
+ XBT project at SF + Valid CSS! + Valid HTML 4.01! +
diff --git a/Tracker/htdocs/.svn/text-base/xbt_torrent_pass_version.php.svn-base b/Tracker/htdocs/.svn/text-base/xbt_torrent_pass_version.php.svn-base new file mode 100644 index 0000000..dc87fe6 --- /dev/null +++ b/Tracker/htdocs/.svn/text-base/xbt_torrent_pass_version.php.svn-base @@ -0,0 +1,18 @@ + +XBT Torrent Pass Version +Torrent Pass: %08x%s', $uid, substr(sha1(sprintf('%s %d %d %s', $torrent_pass_private_key, $torrent_pass_version, $uid, pack('H*', $info_hash))), 0, 24)); +?> +
+

Info Hash: +

UID: +

+

diff --git a/Tracker/htdocs/xbt.css b/Tracker/htdocs/xbt.css new file mode 100644 index 0000000..fcdbf74 --- /dev/null +++ b/Tracker/htdocs/xbt.css @@ -0,0 +1,5 @@ +img {border-style: none;} +table {border-collapse: collapse;} +td, th {border: 1px solid #000000;} +td {background-color: #cccccc;} +th {background-color: #ccccff;} diff --git a/Tracker/htdocs/xbt_common.php b/Tracker/htdocs/xbt_common.php new file mode 100644 index 0000000..3e9d3ba --- /dev/null +++ b/Tracker/htdocs/xbt_common.php @@ -0,0 +1,31 @@ +', htmlspecialchars($query)); + $result = mysql_query($query); + if (!$result) + die(sprintf('%s
%s', htmlspecialchars(mysql_error()), htmlspecialchars($query))); + return $result; + } + + function db_query_first($query) + { + return mysql_fetch_assoc(db_query($query)); + } + + function db_query_first_field($query) + { + $row = mysql_fetch_array(db_query($query)); + return $row[0]; + } + + db_connect(); diff --git a/Tracker/htdocs/xbt_config.php b/Tracker/htdocs/xbt_config.php new file mode 100644 index 0000000..a1b7802 --- /dev/null +++ b/Tracker/htdocs/xbt_config.php @@ -0,0 +1,5 @@ + + + +XBT Files +'); + printf('completed%d', $result['completed']); + printf('peers%d100 %%', $result['peers']); + if ($result['peers']) + { + printf('leechers%d%d %%', $result['leechers'], $result['leechers'] * 100 / $result['peers']); + printf('seeders%d%d %%', $result['seeders'], $result['seeders'] * 100 / $result['peers']); + } + printf('torrents%d', $result['torrents']); + printf('time%s', gmdate('Y-m-d H:i:s')); + echo(''); + echo('
'); + $results = db_query("select * from xbt_files where leechers or seeders order by ctime desc"); + echo(''); + echo(''); + echo(''); + printf('
fid'); + echo('info_hash'); + echo('leechers'); + echo('seeders'); + echo('completed'); + echo('modified'); + echo('created'); + while ($result = mysql_fetch_assoc($results)) + { + echo('
%d', $result['fid']); + printf('%s', bin2hex($result['info_hash'])); + echo(''); + if ($result['leechers']) + printf('%d', $result['leechers']); + echo(''); + if ($result['seeders']) + printf('%d', $result['seeders']); + echo(''); + if ($result['completed']) + printf('%d', $result['completed']); + printf('%s', gmdate('Y-m-d H:i:s', $result['mtime'])); + printf('%s', gmdate('Y-m-d H:i:s', $result['ctime'])); + } + echo('
'); +?> +
+
+ XBT project at SF + Valid CSS! + Valid HTML 4.01! +
diff --git a/Tracker/htdocs/xbt_torrent_pass_version.php b/Tracker/htdocs/xbt_torrent_pass_version.php new file mode 100644 index 0000000..dc87fe6 --- /dev/null +++ b/Tracker/htdocs/xbt_torrent_pass_version.php @@ -0,0 +1,18 @@ + +XBT Torrent Pass Version +Torrent Pass: %08x%s', $uid, substr(sha1(sprintf('%s %d %d %s', $torrent_pass_private_key, $torrent_pass_version, $uid, pack('H*', $info_hash))), 0, 24)); +?> +
+

Info Hash: +

UID: +

+

diff --git a/Tracker/make.sh b/Tracker/make.sh new file mode 100644 index 0000000..d1f0af4 --- /dev/null +++ b/Tracker/make.sh @@ -0,0 +1,15 @@ +g++ $@ -DEPOLL -DNDEBUG -I ../misc -I . -O3 -o xbt_tracker -std=c++11 \ + ../misc/bt_misc.cpp \ + ../misc/database.cpp \ + ../misc/sha1.cpp \ + ../misc/socket.cpp \ + ../misc/sql_query.cpp \ + ../misc/xcc_z.cpp \ + config.cpp \ + connection.cpp \ + epoll.cpp \ + server.cpp \ + tracker_input.cpp \ + transaction.cpp \ + "XBT Tracker.cpp" \ + `mysql_config --libs` -lz && strip xbt_tracker diff --git a/Tracker/res/.svn/all-wcprops b/Tracker/res/.svn/all-wcprops new file mode 100644 index 0000000..c223c9f --- /dev/null +++ b/Tracker/res/.svn/all-wcprops @@ -0,0 +1,11 @@ +K 25 +svn:wc:ra_dav:version-url +V 40 +/svn/!svn/ver/1652/trunk/xbt/Tracker/res +END +XBT Tracker.ico +K 25 +svn:wc:ra_dav:version-url +V 58 +/svn/!svn/ver/1652/trunk/xbt/Tracker/res/XBT%20Tracker.ico +END diff --git a/Tracker/res/.svn/entries b/Tracker/res/.svn/entries new file mode 100644 index 0000000..10e07d4 --- /dev/null +++ b/Tracker/res/.svn/entries @@ -0,0 +1,62 @@ +10 + +dir +2494 +http://xbt.googlecode.com/svn/trunk/xbt/Tracker/res +http://xbt.googlecode.com/svn + + + +2006-05-09T18:43:07.053993Z +1652 +olafvdspek + + + + + + + + + + + + + + +a4ef9278-c6d2-effe-6dce-f9b4ebbbbc1a + +XBT Tracker.ico +file + + + + +2015-07-14T06:50:49.388094Z +3eaa8f29bfb81f46b06d05cf17d7c213 +2006-05-09T18:43:07.053993Z +1652 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +7406 + diff --git a/Tracker/res/.svn/prop-base/XBT Tracker.ico.svn-base b/Tracker/res/.svn/prop-base/XBT Tracker.ico.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/Tracker/res/.svn/prop-base/XBT Tracker.ico.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/Tracker/res/.svn/text-base/XBT Tracker.ico.svn-base b/Tracker/res/.svn/text-base/XBT Tracker.ico.svn-base new file mode 100644 index 0000000..0e2f5d1 Binary files /dev/null and b/Tracker/res/.svn/text-base/XBT Tracker.ico.svn-base differ diff --git a/Tracker/res/XBT Tracker.ico b/Tracker/res/XBT Tracker.ico new file mode 100644 index 0000000..0e2f5d1 Binary files /dev/null and b/Tracker/res/XBT Tracker.ico differ diff --git a/Tracker/resource.h b/Tracker/resource.h new file mode 100644 index 0000000..1a8193f --- /dev/null +++ b/Tracker/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by XBT Client.rc +// +#define IDR_MAINFRAME 128 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 149 +#define _APS_NEXT_COMMAND_VALUE 32849 +#define _APS_NEXT_CONTROL_VALUE 1091 +#define _APS_NEXT_SYMED_VALUE 105 +#endif +#endif diff --git a/Tracker/server.cpp b/Tracker/server.cpp new file mode 100644 index 0000000..932d92f --- /dev/null +++ b/Tracker/server.cpp @@ -0,0 +1,978 @@ +#include "stdafx.h" +#include "server.h" + +#include +#include "connection.h" +#include "epoll.h" +#include "transaction.h" + +namespace boost +{ + template + struct hash> + { + size_t operator()(const std::array& v) const + { + return boost::hash_range(v.begin(), v.end()); + } + }; +} + +static volatile bool g_sig_term = false; +boost::ptr_list m_connections; +boost::unordered_map, t_torrent> m_torrents; +boost::unordered_map m_users; +boost::unordered_map, t_user*> m_users_torrent_passes; +Cconfig m_config; +Cdatabase m_database; +Cepoll m_epoll; +Cstats m_stats; +std::string m_announce_log_buffer; +std::string m_conf_file; +std::string m_scrape_log_buffer; +std::string m_table_prefix; +std::string m_torrents_users_updates_buffer; +std::string m_users_updates_buffer; +time_t m_clean_up_time; +time_t m_read_config_time; +time_t m_read_db_torrents_time; +time_t m_read_db_users_time; +time_t m_time = time(NULL); +time_t m_write_db_torrents_time; +time_t m_write_db_users_time; +unsigned long long m_secret; +int m_fid_end = 0; +bool m_read_users_can_leech; +bool m_read_users_peers_limit; +bool m_read_users_torrent_pass; +bool m_read_users_wait_time; +bool m_use_sql; + +void accept(const Csocket&); + +static void async_query(const std::string& v) +{ + m_database.query_nothrow(v); +} + +static void sig_handler(int v) +{ + if (v == SIGTERM) + g_sig_term = true; +} + +class Ctcp_listen_socket : public Cclient +{ +public: + Ctcp_listen_socket(const Csocket& s) + { + m_s = s; + } + + virtual void process_events(int) + { + accept(m_s); + } +}; + +class Cudp_listen_socket : public Cclient +{ +public: + Cudp_listen_socket(const Csocket& s) + { + m_s = s; + } + + virtual void process_events(int events) + { + if (events & EPOLLIN) + Ctransaction(m_s).recv(); + } +}; + +const Cconfig& srv_config() +{ + return m_config; +} + +Cdatabase& srv_database() +{ + return m_database; +} + +const t_torrent* find_torrent(const std::string& id) +{ + return find_ptr(m_torrents, to_array(id)); +} + +t_user* find_user_by_uid(int v) +{ + return find_ptr(m_users, v); +} + +long long srv_secret() +{ + return m_secret; +} + +Cstats& srv_stats() +{ + return m_stats; +} + +time_t srv_time() +{ + return m_time; +} + +void read_config() +{ + if (m_use_sql) + { + try + { + Csql_result result = Csql_query(m_database, "select name, value from @config where value is not null").execute(); + Cconfig config; + while (Csql_row row = result.fetch_row()) + { + if (config.set(row[0].s(), row[1].s())) + std::cerr << "unknown config name: " << row[0].s() << std::endl; + } + config.load(m_conf_file); + if (config.m_torrent_pass_private_key.empty()) + { + config.m_torrent_pass_private_key = generate_random_string(27); + Csql_query(m_database, "insert into @config (name, value) values ('torrent_pass_private_key', ?)")(config.m_torrent_pass_private_key).execute(); + } + m_config = config; + m_database.set_name("completed", m_config.m_column_files_completed); + m_database.set_name("leechers", m_config.m_column_files_leechers); + m_database.set_name("seeders", m_config.m_column_files_seeders); + m_database.set_name("fid", m_config.m_column_files_fid); + m_database.set_name("uid", m_config.m_column_users_uid); + m_database.set_name("announce_log", m_config.m_table_announce_log.empty() ? m_table_prefix + "announce_log" : m_config.m_table_announce_log); + m_database.set_name("files", m_config.m_table_torrents.empty() ? m_table_prefix + "files" : m_config.m_table_torrents); + m_database.set_name("files_users", m_config.m_table_torrents_users.empty() ? m_table_prefix + "files_users" : m_config.m_table_torrents_users); + m_database.set_name("scrape_log", m_config.m_table_scrape_log.empty() ? m_table_prefix + "scrape_log" : m_config.m_table_scrape_log); + m_database.set_name("users", m_config.m_table_users.empty() ? m_table_prefix + "users" : m_config.m_table_users); + } + catch (bad_query&) + { + } + } + else + { + Cconfig config; + if (!config.load(m_conf_file)) + m_config = config; + } + if (m_config.m_listen_ipas.empty()) + m_config.m_listen_ipas.insert(htonl(INADDR_ANY)); + if (m_config.m_listen_ports.empty()) + m_config.m_listen_ports.insert(2710); + m_read_config_time = srv_time(); +} + +void read_db_torrents_sql() +{ + try + { + if (!m_config.m_auto_register) + { + Csql_result result = Csql_query(m_database, "select info_hash, @fid from @files where flags & 1").execute(); + while (Csql_row row = result.fetch_row()) + { + m_torrents.erase(to_array(row[0])); + Csql_query(m_database, "delete from @files where @fid = ?")(row[1]).execute(); + } + } + if (m_config.m_auto_register && !m_torrents.empty()) + return; + Csql_result result = Csql_query(m_database, "select info_hash, @completed, @fid, ctime from @files where @fid >= ?")(m_fid_end).execute(); + // m_torrents.reserve(m_torrents.size() + result.size()); + while (Csql_row row = result.fetch_row()) + { + m_fid_end = std::max(m_fid_end, row[2].i() + 1); + if (row[0].size() != 20 || find_torrent(row[0].s())) + continue; + t_torrent& file = m_torrents[to_array(row[0])]; + if (file.fid) + continue; + file.completed = row[1].i(); + file.dirty = false; + file.fid = row[2].i(); + file.ctime = row[3].i(); + } + } + catch (bad_query&) + { + } +} + +void read_db_torrents() +{ + m_read_db_torrents_time = srv_time(); + if (m_use_sql) + read_db_torrents_sql(); + else if (!m_config.m_auto_register) + { + std::set new_torrents; + std::ifstream is("xbt_torrents.txt"); + for (std::string s; getline(is, s); ) + { + s = hex_decode(s); + if (s.size() == 20) + new_torrents.insert(&m_torrents[to_array(s)]); + } + for (auto i = m_torrents.begin(); i != m_torrents.end(); ) + { + if (new_torrents.count(&i->second)) + i++; + else + m_torrents.erase(i++); + } + } +} + +void read_db_users() +{ + m_read_db_users_time = srv_time(); + if (!m_use_sql) + return; + try + { + Csql_query q(m_database, "select @uid"); + if (m_read_users_can_leech) + q += ", can_leech"; + if (m_read_users_peers_limit) + q += ", peers_limit"; + if (m_read_users_torrent_pass) + q += ", torrent_pass"; + q += ", torrent_pass_version"; + if (m_read_users_wait_time) + q += ", wait_time"; + q += " from @users"; + Csql_result result = q.execute(); + // m_users.reserve(result.size()); + for (auto& i : m_users) + i.second.marked = true; + m_users_torrent_passes.clear(); + while (Csql_row row = result.fetch_row()) + { + t_user& user = m_users[row[0].i()]; + user.marked = false; + int c = 0; + user.uid = row[c++].i(); + if (m_read_users_can_leech) + user.can_leech = row[c++].i(); + if (m_read_users_peers_limit) + user.peers_limit = row[c++].i(); + if (m_read_users_torrent_pass) + { + if (row[c].size() == 32) + m_users_torrent_passes[to_array(row[c])] = &user; + c++; + } + user.torrent_pass_version = row[c++].i(); + if (m_read_users_wait_time) + user.wait_time = row[c++].i(); + } + for (auto i = m_users.begin(); i != m_users.end(); ) + { + if (i->second.marked) + m_users.erase(i++); + else + i++; + } + } + catch (bad_query&) + { + } +} + +const std::string& db_name(const std::string& v) +{ + return m_database.name(v); +} + +void write_db_torrents() +{ + m_write_db_torrents_time = srv_time(); + if (!m_use_sql) + return; + try + { + std::string buffer; + while (1) + { + for (auto& i : m_torrents) + { + t_torrent& file = i.second; + if (!file.dirty) + continue; + if (!file.fid) + { + Csql_query(m_database, "insert into @files (info_hash, mtime, ctime) values (?, unix_timestamp(), unix_timestamp())")(i.first).execute(); + file.fid = m_database.insert_id(); + } + buffer += Csql_query(m_database, "(?,?,?,?),")(file.leechers)(file.seeders)(file.completed)(file.fid).read(); + file.dirty = false; + if (buffer.size() > 255 << 10) + break; + } + if (buffer.empty()) + break; + buffer.pop_back(); + async_query("insert into " + db_name("files") + " (" + db_name("leechers") + ", " + db_name("seeders") + ", " + db_name("completed") + ", " + db_name("fid") + ") values " + + buffer + + " on duplicate key update" + + " " + db_name("leechers") + " = values(" + db_name("leechers") + ")," + + " " + db_name("seeders") + " = values(" + db_name("seeders") + ")," + + " " + db_name("completed") + " = values(" + db_name("completed") + ")," + + " mtime = unix_timestamp()"); + buffer.clear(); + } + } + catch (bad_query&) + { + } + if (!m_announce_log_buffer.empty()) + { + m_announce_log_buffer.pop_back(); + async_query("insert delayed into " + db_name("announce_log") + " (ipa, port, event, info_hash, peer_id, downloaded, left0, uploaded, uid, mtime) values " + m_announce_log_buffer); + m_announce_log_buffer.erase(); + } + if (!m_scrape_log_buffer.empty()) + { + m_scrape_log_buffer.pop_back(); + async_query("insert delayed into " + db_name("scrape_log") + " (ipa, uid, mtime) values " + m_scrape_log_buffer); + m_scrape_log_buffer.erase(); + } +} + +void write_db_users() +{ + m_write_db_users_time = srv_time(); + if (!m_use_sql) + return; + if (!m_torrents_users_updates_buffer.empty()) + { + m_torrents_users_updates_buffer.pop_back(); + async_query("insert into " + db_name("files_users") + " (active, announced, completed, downloaded, `left`, uploaded, mtime, fid, uid) values " + + m_torrents_users_updates_buffer + + " on duplicate key update" + + " active = values(active)," + + " announced = announced + values(announced)," + + " completed = completed + values(completed)," + + " downloaded = downloaded + values(downloaded)," + + " `left` = values(`left`)," + + " uploaded = uploaded + values(uploaded)," + + " mtime = values(mtime)"); + m_torrents_users_updates_buffer.erase(); + } + async_query("update " + db_name("files_users") + " set active = 0 where mtime < unix_timestamp() - 60 * 60"); + if (!m_users_updates_buffer.empty()) + { + m_users_updates_buffer.pop_back(); + async_query("insert into " + db_name("users") + " (downloaded, uploaded, " + db_name("uid") + ") values " + + m_users_updates_buffer + + " on duplicate key update" + + " downloaded = downloaded + values(downloaded)," + + " uploaded = uploaded + values(uploaded)"); + m_users_updates_buffer.erase(); + } +} + +int test_sql() +{ + if (!m_use_sql) + return 0; + try + { + mysql_get_server_version(m_database); + if (m_config.m_log_announce) + Csql_query(m_database, "select id, ipa, port, event, info_hash, peer_id, downloaded, left0, uploaded, uid, mtime from @announce_log where 0").execute(); + Csql_query(m_database, "select name, value from @config where 0").execute(); + Csql_query(m_database, "select @fid, info_hash, @leechers, @seeders, flags, mtime, ctime from @files where 0").execute(); + Csql_query(m_database, "select fid, uid, active, announced, completed, downloaded, `left`, uploaded from @files_users where 0").execute(); + if (m_config.m_log_scrape) + Csql_query(m_database, "select id, ipa, uid, mtime from @scrape_log where 0").execute(); + Csql_query(m_database, "select @uid, torrent_pass_version, downloaded, uploaded from @users where 0").execute(); + Csql_query(m_database, "update @files set @leechers = 0, @seeders = 0").execute(); + // Csql_query(m_database, "update @files_users set active = 0").execute(); + m_read_users_can_leech = Csql_query(m_database, "show columns from @users like 'can_leech'").execute(); + m_read_users_peers_limit = Csql_query(m_database, "show columns from @users like 'peers_limit'").execute(); + m_read_users_torrent_pass = Csql_query(m_database, "show columns from @users like 'torrent_pass'").execute(); + m_read_users_wait_time = Csql_query(m_database, "show columns from @users like 'wait_time'").execute(); + return 0; + } + catch (bad_query&) + { + } + return 1; +} + +void clean_up(t_torrent& t, time_t time) +{ + for (auto i = t.peers.begin(); i != t.peers.end(); ) + { + if (i->second.mtime < time) + { + (i->second.left ? t.leechers : t.seeders)--; + t.peers.erase(i++); + t.dirty = true; + } + else + i++; + } +} + +void clean_up() +{ + for (auto& i : m_torrents) + clean_up(i.second, srv_time() - static_cast(1.5 * m_config.m_announce_interval)); + m_clean_up_time = srv_time(); +} + +int srv_run(const std::string& table_prefix, bool use_sql, const std::string& conf_file) +{ + for (int i = 0; i < 8; i++) + m_secret = m_secret << 8 ^ rand(); + m_conf_file = conf_file; + m_database.set_name("config", table_prefix + "config"); + m_table_prefix = table_prefix; + m_use_sql = use_sql; + + read_config(); + if (test_sql()) + return 1; + if (m_epoll.create() == -1) + { + std::cerr << "epoll_create failed" << std::endl; + return 1; + } + std::list lt; + std::list lu; + for (auto& j : m_config.m_listen_ipas) + { + for (auto& i : m_config.m_listen_ports) + { + Csocket l; + if (l.open(SOCK_STREAM) == INVALID_SOCKET) + std::cerr << "socket failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; + else if (l.setsockopt(SOL_SOCKET, SO_REUSEADDR, true), + l.bind(j, htons(i))) + std::cerr << "bind failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; + else if (l.listen()) + std::cerr << "listen failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; + else + { +#ifdef SO_ACCEPTFILTER + accept_filter_arg afa; + bzero(&afa, sizeof(afa)); + strcpy(afa.af_name, "httpready"); + if (l.setsockopt(SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa))) + std::cerr << "setsockopt failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; +#elif 0 // TCP_DEFER_ACCEPT + if (l.setsockopt(IPPROTO_TCP, TCP_DEFER_ACCEPT, 90)) + std::cerr << "setsockopt failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; +#endif + lt.push_back(Ctcp_listen_socket(l)); + if (!m_epoll.ctl(EPOLL_CTL_ADD, l, EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLERR | EPOLLHUP, <.back())) + continue; + } + return 1; + } + for (auto& i : m_config.m_listen_ports) + { + Csocket l; + if (l.open(SOCK_DGRAM) == INVALID_SOCKET) + std::cerr << "socket failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; + else if (l.setsockopt(SOL_SOCKET, SO_REUSEADDR, true), + l.bind(j, htons(i))) + std::cerr << "bind failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; + else + { + lu.push_back(Cudp_listen_socket(l)); + if (!m_epoll.ctl(EPOLL_CTL_ADD, l, EPOLLIN | EPOLLPRI | EPOLLERR | EPOLLHUP, &lu.back())) + continue; + } + return 1; + } + } + clean_up(); + read_db_torrents(); + read_db_users(); + write_db_torrents(); + write_db_users(); +#ifndef NDEBUG + // test_announce(); +#endif +#ifndef WIN32 + if (m_config.m_daemon) + { + if (daemon(true, false)) + std::cerr << "daemon failed" << std::endl; + std::ofstream(m_config.m_pid_file.c_str()) << getpid() << std::endl; + struct sigaction act; + act.sa_handler = sig_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + if (sigaction(SIGTERM, &act, NULL)) + std::cerr << "sigaction failed" << std::endl; + act.sa_handler = SIG_IGN; + if (sigaction(SIGPIPE, &act, NULL)) + std::cerr << "sigaction failed" << std::endl; + } +#endif +#ifdef EPOLL + std::array events; +#else + fd_set fd_read_set; + fd_set fd_write_set; + fd_set fd_except_set; +#endif + while (!g_sig_term) + { +#ifdef EPOLL + int r = m_epoll.wait(events.data(), events.size(), 5000); + if (r == -1) + std::cerr << "epoll_wait failed: " << errno << std::endl; + else + { + time_t prev_time = m_time; + m_time = ::time(NULL); + for (int i = 0; i < r; i++) + reinterpret_cast(events[i].data.ptr)->process_events(events[i].events); + if (m_time == prev_time) + continue; + for (auto i = m_connections.begin(); i != m_connections.end(); ) + { + if (i->run()) + i = m_connections.erase(i); + else + i++; + } + } +#else + FD_ZERO(&fd_read_set); + FD_ZERO(&fd_write_set); + FD_ZERO(&fd_except_set); + int n = 0; + for (auto& i : m_connections) + { + int z = i.pre_select(&fd_read_set, &fd_write_set); + n = std::max(n, z); + } + for (auto& i : lt) + { + FD_SET(i.s(), &fd_read_set); + n = std::max(n, i.s()); + } + for (auto& i : lu) + { + FD_SET(i.s(), &fd_read_set); + n = std::max(n, i.s()); + } + timeval tv; + tv.tv_sec = 5; + tv.tv_usec = 0; + if (select(n + 1, &fd_read_set, &fd_write_set, &fd_except_set, &tv) == SOCKET_ERROR) + std::cerr << "select failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; + else + { + m_time = ::time(NULL); + for (auto& i : lt) + { + if (FD_ISSET(i.s(), &fd_read_set)) + accept(i.s()); + } + for (auto& i : lu) + { + if (FD_ISSET(i.s(), &fd_read_set)) + Ctransaction(i.s()).recv(); + } + for (auto i = m_connections.begin(); i != m_connections.end(); ) + { + if (i->post_select(&fd_read_set, &fd_write_set)) + i = m_connections.erase(i); + else + i++; + } + } +#endif + if (srv_time() - m_read_config_time > m_config.m_read_config_interval) + read_config(); + else if (srv_time() - m_clean_up_time > m_config.m_clean_up_interval) + clean_up(); + else if (srv_time() - m_read_db_torrents_time > m_config.m_read_db_interval) + read_db_torrents(); + else if (srv_time() - m_read_db_users_time > m_config.m_read_db_interval) + read_db_users(); + else if (m_config.m_write_db_interval && srv_time() - m_write_db_torrents_time > m_config.m_write_db_interval) + write_db_torrents(); + else if (m_config.m_write_db_interval && srv_time() - m_write_db_users_time > m_config.m_write_db_interval) + write_db_users(); + } + write_db_torrents(); + write_db_users(); + unlink(m_config.m_pid_file.c_str()); + return 0; +} + +void accept(const Csocket& l) +{ + sockaddr_in a; + for (int i = 0; i < 10000; i++) + { + socklen_t cb_a = sizeof(sockaddr_in); +#ifdef SOCK_NONBLOCK + Csocket s = accept4(l, reinterpret_cast(&a), &cb_a, SOCK_NONBLOCK); +#else + Csocket s = ::accept(l, reinterpret_cast(&a), &cb_a); +#endif + if (s == SOCKET_ERROR) + { + if (WSAGetLastError() == WSAECONNABORTED) + continue; + if (WSAGetLastError() != WSAEWOULDBLOCK) + { + m_stats.accept_errors++; + std::cerr << "accept failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; + xbt_syslog("accept failed: " + Csocket::error2a(WSAGetLastError())); + } + break; + } + m_stats.accepted_tcp++; +#ifndef SOCK_NONBLOCK + if (s.blocking(false)) + std::cerr << "ioctlsocket failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; +#endif + std::unique_ptr connection(new Cconnection(s, a)); + connection->process_events(EPOLLIN); + if (connection->s() != INVALID_SOCKET) + { + m_stats.slow_tcp++; + m_connections.push_back(connection.release()); + m_epoll.ctl(EPOLL_CTL_ADD, m_connections.back().s(), EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLERR | EPOLLHUP | EPOLLET, &m_connections.back()); + } + } +} + +std::string srv_insert_peer(const Ctracker_input& v, bool udp, t_user* user) +{ + if (m_use_sql && m_config.m_log_announce) + { + m_announce_log_buffer += Csql_query(m_database, "(?,?,?,?,?,?,?,?,?,?),") + (ntohl(v.m_ipa)) + (ntohs(v.m_port)) + (v.m_event) + (v.m_info_hash) + (v.m_peer_id) + (v.m_downloaded) + (v.m_left) + (v.m_uploaded) + (user ? user->uid : 0) + (srv_time()) + .read(); + } + if (!m_config.m_offline_message.empty()) + return m_config.m_offline_message; + if (0) + return bts_banned_client; + if (!m_config.m_anonymous_announce && !user) + return bts_unregistered_torrent_pass; + if (!m_config.m_auto_register && !find_torrent(v.m_info_hash)) + return bts_unregistered_torrent; + if (v.m_left && user && !user->can_leech) + return bts_can_not_leech; + t_torrent& file = m_torrents[to_array(v.m_info_hash)]; + if (!file.ctime) + file.ctime = srv_time(); + if (v.m_left && user && user->wait_time && file.ctime + user->wait_time > srv_time()) + return bts_wait_time; + peer_key_c peer_key(v.m_ipa, user ? user->uid : 0); + t_peer* i = find_ptr(file.peers, peer_key); + if (i) + (i->left ? file.leechers : file.seeders)--; + else if (v.m_left && user && user->peers_limit) + { + int c = 0; + for (auto& j : file.peers) + c += j.second.left && j.second.uid == user->uid; + if (c >= user->peers_limit) + return bts_peers_limit_reached; + } + if (m_use_sql && user && file.fid) + { + long long downloaded = 0; + long long uploaded = 0; + if (i + && i->uid == user->uid + && boost::equals(i->peer_id, v.m_peer_id) + && v.m_downloaded >= i->downloaded + && v.m_uploaded >= i->uploaded) + { + downloaded = v.m_downloaded - i->downloaded; + uploaded = v.m_uploaded - i->uploaded; + } + m_torrents_users_updates_buffer += Csql_query(m_database, "(?,1,?,?,?,?,?,?,?),") + (v.m_event != Ctracker_input::e_stopped) + (v.m_event == Ctracker_input::e_completed) + (downloaded) + (v.m_left) + (uploaded) + (srv_time()) + (file.fid) + (user->uid) + .read(); + if (downloaded || uploaded) + m_users_updates_buffer += Csql_query(m_database, "(?,?,?),")(downloaded)(uploaded)(user->uid).read(); + if (m_torrents_users_updates_buffer.size() > 255 << 10) + write_db_users(); + } + if (v.m_event == Ctracker_input::e_stopped) + file.peers.erase(peer_key); + else + { + t_peer& peer = i ? *i : file.peers[peer_key]; + peer.downloaded = v.m_downloaded; + peer.left = v.m_left; + peer.peer_id = v.m_peer_id; + peer.port = v.m_port; + peer.uid = user ? user->uid : 0; + peer.uploaded = v.m_uploaded; + (peer.left ? file.leechers : file.seeders)++; + peer.mtime = srv_time(); + } + if (v.m_event == Ctracker_input::e_completed) + file.completed++; + (udp ? m_stats.announced_udp : m_stats.announced_http)++; + file.dirty = true; + return std::string(); +} + +void t_torrent::select_peers(mutable_str_ref& d, const Ctracker_input& ti) const +{ + if (ti.m_event == Ctracker_input::e_stopped) + return; + std::vector> candidates; + candidates.reserve(peers.size()); + for (auto& i : peers) + { + if (!ti.m_left && !i.second.left) + continue; + std::array v; + memcpy(&v[0], &i.first.host_, 4); + memcpy(&v[4], &i.second.port, 2); + candidates.push_back(v); + } + size_t c = d.size() / 6; + if (candidates.size() <= c) + { + memcpy(d.data(), candidates); + d.advance_begin(6 * candidates.size()); + return; + } + const char* d0 = d.begin(); + while (c--) + { + int i = rand() % candidates.size(); + memcpy(d.data(), candidates[i]); + d.advance_begin(6); + candidates[i] = candidates.back(); + candidates.pop_back(); + } +} + +std::string srv_select_peers(const Ctracker_input& ti) +{ + const t_torrent* f = find_torrent(ti.m_info_hash); + if (!f) + return std::string(); + std::array peers0; + mutable_str_ref peers = peers0; + f->select_peers(peers, ti); + peers.assign(peers0.data(), peers.data()); + return (boost::format("d8:completei%de10:incompletei%de8:intervali%de12:min intervali%de5:peers%d:%se") + % f->seeders % f->leechers % m_config.m_announce_interval % m_config.m_announce_interval % peers.size() % peers).str(); +} + +std::string srv_scrape(const Ctracker_input& ti, t_user* user) +{ + if (m_use_sql && m_config.m_log_scrape) + m_scrape_log_buffer += Csql_query(m_database, "(?,?,?),")(ntohl(ti.m_ipa))(user ? user->uid : 0)(srv_time()).read(); + if (!m_config.m_anonymous_scrape && !user) + return "d14:failure reason25:unregistered torrent passe"; + std::string d; + d += "d5:filesd"; + if (ti.m_info_hashes.empty()) + { + m_stats.scraped_full++; + d.reserve(90 * m_torrents.size()); + for (auto& i : m_torrents) + { + if (i.second.leechers || i.second.seeders) + d += (boost::format("20:%sd8:completei%de10:downloadedi%de10:incompletei%dee") % boost::make_iterator_range(i.first) % i.second.seeders % i.second.completed % i.second.leechers).str(); + } + } + else + { + m_stats.scraped_http++; + if (ti.m_info_hashes.size() > 1) + m_stats.scraped_multi++; + for (auto& j : ti.m_info_hashes) + { + if (const t_torrent* i = find_torrent(j)) + d += (boost::format("20:%sd8:completei%de10:downloadedi%de10:incompletei%dee") % j % i->seeders % i->completed % i->leechers).str(); + } + } + d += "e"; + if (m_config.m_scrape_interval) + d += (boost::format("5:flagsd20:min_request_intervali%dee") % m_config.m_scrape_interval).str(); + d += "e"; + return d; +} + +void debug(const t_torrent& t, std::ostream& os) +{ + for (auto& i : t.peers) + { + os << "" + Csocket::inet_ntoa(i.first.host_) + << "" << ntohs(i.second.port) + << "" << i.second.uid + << "" << i.second.left + << "" << srv_time() - i.second.mtime + << "" << hex_encode(i.second.peer_id); + } +} + +std::string srv_debug(const Ctracker_input& ti) +{ + std::ostringstream os; + os << "XBT Tracker"; + os << ""; + if (ti.m_info_hash.empty()) + { + for (auto& i : m_torrents) + { + if (!i.second.leechers && !i.second.seeders) + continue; + os << "
" << i.second.fid + << "" << hex_encode(i.first) << "" + << "" << (i.second.dirty ? '*' : ' ') + << "" << i.second.leechers + << "" << i.second.seeders; + } + } + else if (const t_torrent* i = find_torrent(ti.m_info_hash)) + debug(*i, os); + os << "
"; + return os.str(); +} + +std::string srv_statistics() +{ + std::ostringstream os; + os << "XBT Tracker"; + os << ""; + long long leechers = 0; + long long seeders = 0; + int torrents = 0; + for (auto& i : m_torrents) + { + leechers += i.second.leechers; + seeders += i.second.seeders; + torrents += i.second.leechers || i.second.seeders; + } + int peers = leechers + seeders; + time_t t = srv_time(); + time_t up_time = std::max(1, t - m_stats.start_time); + os << "" + << "
peers" << peers; + if (peers) + { + os << "
seeders" << seeders << "" << seeders * 100 / peers << " %" + << "
leechers" << leechers << "" << leechers * 100 / peers << " %"; + } + os << "
torrents" << torrents + << "
" + << "
accepted tcp" << m_stats.accepted_tcp << "" << m_stats.accepted_tcp / up_time << " /s" + << "
slow tcp" << m_stats.slow_tcp << "" << m_stats.slow_tcp / up_time << " /s" + << "
rejected tcp" << m_stats.rejected_tcp + << "
accept errors" << m_stats.accept_errors + << "
received udp" << m_stats.received_udp << "" << m_stats.received_udp / up_time << " /s" + << "
sent udp" << m_stats.sent_udp << "" << m_stats.sent_udp / up_time << " /s"; + if (m_stats.announced()) + { + os << "
announced" << m_stats.announced() << "" << m_stats.announced() * 100 / m_stats.accepted_tcp << " %" + << "
announced http " << m_stats.announced_http << "" << m_stats.announced_http * 100 / m_stats.announced() << " %" + << "
announced udp" << m_stats.announced_udp << "" << m_stats.announced_udp * 100 / m_stats.announced() << " %"; + } + os << "
scraped full" << m_stats.scraped_full; + os << "
scraped multi" << m_stats.scraped_multi; + if (m_stats.scraped()) + { + os << "
scraped" << m_stats.scraped() << "" << m_stats.scraped() * 100 / m_stats.accepted_tcp << " %" + << "
scraped http" << m_stats.scraped_http << "" << m_stats.scraped_http * 100 / m_stats.scraped() << " %" + << "
scraped udp" << m_stats.scraped_udp << "" << m_stats.scraped_udp * 100 / m_stats.scraped() << " %"; + } + os << "
" + << "
up time" << duration2a(up_time) + << "
" + << "
anonymous announce" << m_config.m_anonymous_announce + << "
anonymous scrape" << m_config.m_anonymous_scrape + << "
auto register" << m_config.m_auto_register + << "
full scrape" << m_config.m_full_scrape + << "
read config time" << t - m_read_config_time << " / " << m_config.m_read_config_interval + << "
clean up time" << t - m_clean_up_time << " / " << m_config.m_clean_up_interval + << "
read db files time" << t - m_read_db_torrents_time << " / " << m_config.m_read_db_interval; + if (m_use_sql) + { + os << "
read db users time" << t - m_read_db_users_time << " / " << m_config.m_read_db_interval + << "
write db files time" << t - m_write_db_torrents_time << " / " << m_config.m_write_db_interval + << "
write db users time" << t - m_write_db_users_time << " / " << m_config.m_write_db_interval; + } + os << "
"; + return os.str(); +} + +t_user* find_user_by_torrent_pass(str_ref v, str_ref info_hash) +{ + if (v.size() != 32) + return NULL; + if (t_user* user = find_user_by_uid(read_int(4, hex_decode(v.substr(0, 8))))) + { + if (Csha1((boost::format("%s %d %d %s") % m_config.m_torrent_pass_private_key % user->torrent_pass_version % user->uid % info_hash).str()).read().substr(0, 12) == hex_decode(v.substr(8, 24))) + return user; + } + return find_ptr2(m_users_torrent_passes, to_array(v)); +} + +void srv_term() +{ + g_sig_term = true; +} + +void test_announce() +{ + t_user* u = find_ptr(m_users, 1); + Ctracker_input i; + i.m_info_hash = "IHIHIHIHIHIHIHIHIHIH"; + memcpy(i.m_peer_id.data(), str_ref("PIPIPIPIPIPIPIPIPIPI")); + i.m_ipa = htonl(0x7f000063); + i.m_port = 54321; + std::cout << srv_insert_peer(i, false, u) << std::endl; + write_db_torrents(); + write_db_users(); + m_time++; + i.m_uploaded = 1 << 30; + i.m_downloaded = 1 << 20; + std::cout << srv_insert_peer(i, false, u) << std::endl; + write_db_torrents(); + write_db_users(); + m_time += 3600; + clean_up(); + write_db_torrents(); + write_db_users(); +} diff --git a/Tracker/server.h b/Tracker/server.h new file mode 100644 index 0000000..b82fa46 --- /dev/null +++ b/Tracker/server.h @@ -0,0 +1,129 @@ +#pragma once + +#include "config.h" +#include "tracker_input.h" + +class Cstats +{ +public: + long long announced() const + { + return announced_http + announced_udp; + } + + long long scraped() const + { + return scraped_http + scraped_udp; + } + + long long accept_errors = 0; + long long accepted_tcp = 0; + long long announced_http = 0; + long long announced_udp = 0; + long long received_udp = 0; + long long rejected_tcp = 0; + long long scraped_full = 0; + long long scraped_http = 0; + long long scraped_multi = 0; + long long scraped_udp = 0; + long long sent_udp = 0; + long long slow_tcp = 0; + time_t start_time = time(NULL); +}; + +class peer_key_c +{ +public: + peer_key_c(int host, int uid) + { + host_ = host; +#ifdef PEERS_KEY + uid_ = uid; +#else + (void)uid; +#endif + } + + bool operator==(peer_key_c v) const + { +#ifdef PEERS_KEY + return host_ == v.host_ && uid_ == v.uid_; +#else + return host_ == v.host_; +#endif + } + + bool operator<(peer_key_c v) const + { +#ifdef PEERS_KEY + return host_ < v.host_ || host_ == v.host_ && uid_ < v.uid_; +#else + return host_ < v.host_; +#endif + } + + friend std::size_t hash_value(const peer_key_c& v) + { + std::size_t seed = boost::hash_value(v.host_); +#ifdef PEERS_KEY + boost::hash_combine(seed, v.uid_); +#endif + return seed; + } + + int host_; +#ifdef PEERS_KEY + int uid_; +#endif +}; + +struct t_peer +{ + long long downloaded; + long long uploaded; + time_t mtime = 0; + int uid; + short port; + bool left; + std::array peer_id; +}; + +struct t_torrent +{ + void select_peers(mutable_str_ref& d, const Ctracker_input&) const; + + boost::unordered_map peers; + time_t ctime; + int completed = 0; + int fid = 0; + int leechers = 0; + int seeders = 0; + bool dirty = true; +}; + +struct t_user +{ + int uid; + int peers_limit = 0; + int torrent_pass_version = 0; + int wait_time = 0; + bool can_leech = true; + bool marked; +}; + +const t_torrent* find_torrent(const std::string& id); +t_user* find_user_by_torrent_pass(str_ref, str_ref info_hash); +t_user* find_user_by_uid(int v); +long long srv_secret(); +const Cconfig& srv_config(); +Cdatabase& srv_database(); +Cstats& srv_stats(); +time_t srv_time(); + +int srv_run(const std::string& table_prefix, bool use_sql, const std::string& conf_file); +void srv_term(); +std::string srv_debug(const Ctracker_input&); +std::string srv_insert_peer(const Ctracker_input&, bool udp, t_user*); +std::string srv_scrape(const Ctracker_input&, t_user*); +std::string srv_select_peers(const Ctracker_input&); +std::string srv_statistics(); diff --git a/Tracker/stdafx.cpp b/Tracker/stdafx.cpp new file mode 100644 index 0000000..fd4f341 --- /dev/null +++ b/Tracker/stdafx.cpp @@ -0,0 +1 @@ +#include "stdafx.h" diff --git a/Tracker/stdafx.h b/Tracker/stdafx.h new file mode 100644 index 0000000..5e63134 --- /dev/null +++ b/Tracker/stdafx.h @@ -0,0 +1,36 @@ +#pragma once + +#define FD_SETSIZE 1024 +#define NOMINMAX + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef unsigned char byte; diff --git a/Tracker/tracker_input.cpp b/Tracker/tracker_input.cpp new file mode 100644 index 0000000..ec8c836 --- /dev/null +++ b/Tracker/tracker_input.cpp @@ -0,0 +1,62 @@ +#include "stdafx.h" +#include "tracker_input.h" + +void Ctracker_input::set(const std::string& name, const std::string& value) +{ + if (name.empty()) + return; + switch (name[0]) + { + case 'd': + if (name == "downloaded") + m_downloaded = to_int(value); + break; + case 'e': + if (name == "event") + { + if (value == "completed") + m_event = e_completed; + else if (value == "started") + m_event = e_started; + else if (value == "stopped") + m_event = e_stopped; + else + m_event = e_none; + } + break; + case 'i': + if (name == "info_hash" && value.size() == 20) + { + m_info_hash = value; + m_info_hashes.push_back(value); + } + else if (name == "ip") + m_ipa = inet_addr(value.c_str()); + break; + case 'l': + if (name == "left") + m_left = to_int(value); + break; + case 'p': + if (name == "peer_id" && value.size() == 20) + memcpy(m_peer_id, value); + else if (name == "port") + m_port = htons(to_int(value)); + break; + case 'u': + if (name == "uploaded") + m_uploaded = to_int(value); + break; + } +} + +bool Ctracker_input::valid() const +{ + return m_downloaded >= 0 + && (m_event != e_completed || !m_left) + && m_info_hash.size() == 20 + && m_left >= -1 + && m_peer_id.size() == 20 + && m_port >= 0 + && m_uploaded >= 0; +} diff --git a/Tracker/tracker_input.h b/Tracker/tracker_input.h new file mode 100644 index 0000000..6375481 --- /dev/null +++ b/Tracker/tracker_input.h @@ -0,0 +1,36 @@ +#pragma once + +class Ctracker_input +{ +public: + void set(const std::string& name, const std::string& value); + bool valid() const; + + bool is_seeder() const + { + return !m_left; + } + + bool is_leecher() const + { + return m_left; + } + + enum t_event + { + e_none, + e_completed, + e_started, + e_stopped, + }; + + t_event m_event = e_none; + std::string m_info_hash; + std::vector m_info_hashes; + std::array m_peer_id; + long long m_downloaded = 0; + long long m_left = 0; + long long m_uploaded = 0; + int m_ipa = 0; + int m_port = 0; +}; diff --git a/Tracker/transaction.cpp b/Tracker/transaction.cpp new file mode 100644 index 0000000..88584cb --- /dev/null +++ b/Tracker/transaction.cpp @@ -0,0 +1,157 @@ +#include "stdafx.h" +#include "transaction.h" + +#include +#include "server.h" + +Ctransaction::Ctransaction(const Csocket& s) : + m_s(s) +{ +} + +long long Ctransaction::connection_id() const +{ + const int cb_s = 12; + char s[cb_s]; + write_int(8, s, srv_secret()); + write_int(4, s + 8, m_a.sin_addr.s_addr); + char d[20]; + Csha1(data_ref(s, cb_s)).read(d); + return read_int(8, d); +} + +void Ctransaction::recv() +{ + const int cb_b = 2 << 10; + char b[cb_b]; + for (int i = 0; i < 10000; i++) + { + socklen_t cb_a = sizeof(sockaddr_in); + int r = m_s.recvfrom(mutable_data_ref(b, cb_b), reinterpret_cast(&m_a), &cb_a); + if (r == SOCKET_ERROR) + { + if (WSAGetLastError() != WSAEWOULDBLOCK) + std::cerr << "recv failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; + return; + } + srv_stats().received_udp++; + if (r < uti_size) + return; + switch (read_int(4, b + uti_action, b + r)) + { + case uta_connect: + if (r >= utic_size) + send_connect(data_ref(b, r)); + break; + case uta_announce: + if (r >= utia_size) + send_announce(data_ref(b, r)); + break; + case uta_scrape: + if (r >= utis_size) + send_scrape(data_ref(b, r)); + break; + } + } +} + +void Ctransaction::send_connect(data_ref r) +{ + if (read_int(8, &r[uti_connection_id], r.end()) != 0x41727101980ll) + return; + const int cb_d = 2 << 10; + char d[cb_d]; + write_int(4, d + uto_action, uta_connect); + write_int(4, d + uto_transaction_id, read_int(4, &r[uti_transaction_id], r.end())); + write_int(8, d + utoc_connection_id, connection_id()); + send(data_ref(d, utoc_size)); +} + +void Ctransaction::send_announce(data_ref r) +{ + if (read_int(8, &r[uti_connection_id], r.end()) != connection_id()) + return; + if (!srv_config().m_anonymous_announce) + { + send_error(r, "access denied"); + return; + } + Ctracker_input ti; + ti.m_downloaded = read_int(8, &r[utia_downloaded], r.end()); + ti.m_event = static_cast(read_int(4, &r[utia_event], r.end())); + ti.m_info_hash.assign(reinterpret_cast(&r[utia_info_hash]), 20); + ti.m_ipa = read_int(4, &r[utia_ipa], r.end()) && is_private_ipa(m_a.sin_addr.s_addr) + ? htonl(read_int(4, &r[utia_ipa], r.end())) + : m_a.sin_addr.s_addr; + ti.m_left = read_int(8, &r[utia_left], r.end()); + memcpy(ti.m_peer_id.data(), &r[utia_peer_id], 20); + ti.m_port = htons(read_int(2, &r[utia_port], r.end())); + ti.m_uploaded = read_int(8, &r[utia_uploaded], r.end()); + std::string error = srv_insert_peer(ti, true, NULL); + if (!error.empty()) + { + send_error(r, error); + return; + } + auto torrent = find_torrent(ti.m_info_hash); + if (!torrent) + return; + char d[2 << 10]; + write_int(4, d + uto_action, uta_announce); + write_int(4, d + uto_transaction_id, read_int(4, &r[uti_transaction_id], r.end())); + write_int(4, d + utoa_interval, srv_config().m_announce_interval); + write_int(4, d + utoa_leechers, torrent->leechers); + write_int(4, d + utoa_seeders, torrent->seeders); + mutable_str_ref peers(d + utoa_size, 300); + torrent->select_peers(peers, ti); + send(data_ref(d, peers.begin())); +} + +void Ctransaction::send_scrape(data_ref r) +{ + if (read_int(8, &r[uti_connection_id], r.end()) != connection_id()) + return; + if (!srv_config().m_anonymous_scrape) + { + send_error(r, "access denied"); + return; + } + const int cb_d = 2 << 10; + char d[cb_d]; + write_int(4, d + uto_action, uta_scrape); + write_int(4, d + uto_transaction_id, read_int(4, &r[uti_transaction_id], r.end())); + char* w = d + utos_size; + for (r.advance_begin(utis_size); r.size() >= 20 && w + 12 <= d + cb_d; r.advance_begin(20)) + { + if (auto t = find_torrent(r.substr(0, 20).s())) + { + w = write_int(4, w, t->seeders); + w = write_int(4, w, t->completed); + w = write_int(4, w, t->leechers); + } + else + { + w = write_int(4, w, 0); + w = write_int(4, w, 0); + w = write_int(4, w, 0); + } + } + srv_stats().scraped_udp++; + send(data_ref(d, w)); +} + +void Ctransaction::send_error(data_ref r, const std::string& msg) +{ + char d[2 << 10]; + write_int(4, d + uto_action, uta_error); + write_int(4, d + uto_transaction_id, read_int(4, &r[uti_transaction_id], r.end())); + memcpy(d + utoe_size, msg); + send(data_ref(d, utoe_size + msg.size())); +} + +void Ctransaction::send(data_ref b) +{ + if (m_s.sendto(b, reinterpret_cast(&m_a), sizeof(sockaddr_in)) != b.size()) + std::cerr << "send failed: " << Csocket::error2a(WSAGetLastError()) << std::endl; + srv_stats().sent_udp++; +} diff --git a/Tracker/transaction.h b/Tracker/transaction.h new file mode 100644 index 0000000..18b52cd --- /dev/null +++ b/Tracker/transaction.h @@ -0,0 +1,17 @@ +#pragma once + +class Ctransaction +{ +public: + long long connection_id() const; + void recv(); + void send(data_ref); + void send_announce(data_ref); + void send_connect(data_ref); + void send_scrape(data_ref); + void send_error(data_ref, const std::string& msg); + Ctransaction(const Csocket&); +private: + const Csocket& m_s; + sockaddr_in m_a; +}; diff --git a/Tracker/xbt_tracker.conf.default b/Tracker/xbt_tracker.conf.default new file mode 100644 index 0000000..3d38662 --- /dev/null +++ b/Tracker/xbt_tracker.conf.default @@ -0,0 +1,4 @@ +mysql_host = localhost +mysql_user = xbt +mysql_password = +mysql_database = xbt diff --git a/Tracker/xbt_tracker.sql b/Tracker/xbt_tracker.sql new file mode 100644 index 0000000..e836da8 --- /dev/null +++ b/Tracker/xbt_tracker.sql @@ -0,0 +1,89 @@ +-- create table if not exists xbt_announce_log +-- ( + -- id int not null auto_increment, + -- ipa int unsigned not null, + -- port int not null, + -- event int not null, + -- info_hash binary(20) not null, + -- peer_id binary(20) not null, + -- downloaded bigint unsigned not null, + -- left0 bigint unsigned not null, + -- uploaded bigint unsigned not null, + -- uid int not null, + -- mtime int not null, + -- primary key (id) +-- ) engine = myisam; + +create table if not exists xbt_config +( + name varchar(255) not null, + value varchar(255) not null +); + +-- create table if not exists xbt_deny_from_clients +-- ( + -- peer_id char(20) not null +-- ); + +-- create table if not exists xbt_deny_from_hosts +-- ( + -- begin int unsigned not null, + -- end int unsigned not null +-- ); + +create table if not exists xbt_files +( + fid int not null auto_increment, + info_hash binary(20) not null, + leechers int not null default 0, + seeders int not null default 0, + completed int not null default 0, + flags int not null default 0, + mtime int not null, + ctime int not null, + primary key (fid), + unique key (info_hash) +); + +create table if not exists xbt_files_users +( + fid int not null, + uid int not null, + active tinyint not null, + announced int not null, + completed int not null, + downloaded bigint unsigned not null, + `left` bigint unsigned not null, + uploaded bigint unsigned not null, + mtime int not null, + unique key (fid, uid), + key (uid) +); + +-- create table if not exists xbt_scrape_log +-- ( + -- id int not null auto_increment, + -- ipa int unsigned not null, + -- uid int not null, + -- mtime int not null, + -- primary key (id) +-- ) engine = myisam; + +create table if not exists xbt_users +( + uid int not null auto_increment, + -- can_leech tinyint not null default 1, + -- wait_time int not null default 0, + -- peers_limit int not null default 0, + -- torrent_pass char(32) not null, + torrent_pass_version int not null default 0, + downloaded bigint unsigned not null default 0, + uploaded bigint unsigned not null default 0, + primary key (uid) +); + +-- alter table xbt_files_users add down_rate int unsigned not null; +-- alter table xbt_files_users add up_rate int unsigned not null; + +-- alter table xbt_files_users add foreign key (fid) references xbt_files (fid) on delete cascade; +-- alter table xbt_files_users add foreign key (uid) references xbt_users (uid) on delete cascade; diff --git a/misc/.svn/all-wcprops b/misc/.svn/all-wcprops new file mode 100644 index 0000000..7d087c3 --- /dev/null +++ b/misc/.svn/all-wcprops @@ -0,0 +1,197 @@ +K 25 +svn:wc:ra_dav:version-url +V 33 +/svn/!svn/ver/2494/trunk/xbt/misc +END +config_base.h +K 25 +svn:wc:ra_dav:version-url +V 47 +/svn/!svn/ver/2020/trunk/xbt/misc/config_base.h +END +bt_tracker_url.h +K 25 +svn:wc:ra_dav:version-url +V 50 +/svn/!svn/ver/2020/trunk/xbt/misc/bt_tracker_url.h +END +stream_int.h +K 25 +svn:wc:ra_dav:version-url +V 46 +/svn/!svn/ver/2449/trunk/xbt/misc/stream_int.h +END +bvalue.cpp +K 25 +svn:wc:ra_dav:version-url +V 44 +/svn/!svn/ver/2339/trunk/xbt/misc/bvalue.cpp +END +xif_key_r.cpp +K 25 +svn:wc:ra_dav:version-url +V 47 +/svn/!svn/ver/2371/trunk/xbt/misc/xif_key_r.cpp +END +xif_value.cpp +K 25 +svn:wc:ra_dav:version-url +V 47 +/svn/!svn/ver/2371/trunk/xbt/misc/xif_value.cpp +END +stream_reader.h +K 25 +svn:wc:ra_dav:version-url +V 49 +/svn/!svn/ver/2128/trunk/xbt/misc/stream_reader.h +END +bt_torrent.cpp +K 25 +svn:wc:ra_dav:version-url +V 48 +/svn/!svn/ver/2020/trunk/xbt/misc/bt_torrent.cpp +END +bt_strings.h +K 25 +svn:wc:ra_dav:version-url +V 46 +/svn/!svn/ver/2020/trunk/xbt/misc/bt_strings.h +END +sql_query.cpp +K 25 +svn:wc:ra_dav:version-url +V 47 +/svn/!svn/ver/2384/trunk/xbt/misc/sql_query.cpp +END +tf_misc.h +K 25 +svn:wc:ra_dav:version-url +V 43 +/svn/!svn/ver/2431/trunk/xbt/misc/tf_misc.h +END +virtual_binary.cpp +K 25 +svn:wc:ra_dav:version-url +V 52 +/svn/!svn/ver/2244/trunk/xbt/misc/virtual_binary.cpp +END +alerts.cpp +K 25 +svn:wc:ra_dav:version-url +V 44 +/svn/!svn/ver/2157/trunk/xbt/misc/alerts.cpp +END +stream_writer.h +K 25 +svn:wc:ra_dav:version-url +V 49 +/svn/!svn/ver/2244/trunk/xbt/misc/stream_writer.h +END +socket.h +K 25 +svn:wc:ra_dav:version-url +V 42 +/svn/!svn/ver/2443/trunk/xbt/misc/socket.h +END +xif_key.cpp +K 25 +svn:wc:ra_dav:version-url +V 45 +/svn/!svn/ver/2245/trunk/xbt/misc/xif_key.cpp +END +sha1.h +K 25 +svn:wc:ra_dav:version-url +V 40 +/svn/!svn/ver/2128/trunk/xbt/misc/sha1.h +END +bt_tracker_account.h +K 25 +svn:wc:ra_dav:version-url +V 54 +/svn/!svn/ver/2020/trunk/xbt/misc/bt_tracker_account.h +END +bt_tracker_url.cpp +K 25 +svn:wc:ra_dav:version-url +V 52 +/svn/!svn/ver/2020/trunk/xbt/misc/bt_tracker_url.cpp +END +database.cpp +K 25 +svn:wc:ra_dav:version-url +V 46 +/svn/!svn/ver/2439/trunk/xbt/misc/database.cpp +END +xcc_z.cpp +K 25 +svn:wc:ra_dav:version-url +V 43 +/svn/!svn/ver/2423/trunk/xbt/misc/xcc_z.cpp +END +stream_reader.cpp +K 25 +svn:wc:ra_dav:version-url +V 51 +/svn/!svn/ver/2020/trunk/xbt/misc/stream_reader.cpp +END +bt_misc.cpp +K 25 +svn:wc:ra_dav:version-url +V 45 +/svn/!svn/ver/2494/trunk/xbt/misc/bt_misc.cpp +END +bvalue.h +K 25 +svn:wc:ra_dav:version-url +V 42 +/svn/!svn/ver/2233/trunk/xbt/misc/bvalue.h +END +tf_misc.cpp +K 25 +svn:wc:ra_dav:version-url +V 45 +/svn/!svn/ver/2479/trunk/xbt/misc/tf_misc.cpp +END +stream_writer.cpp +K 25 +svn:wc:ra_dav:version-url +V 51 +/svn/!svn/ver/2020/trunk/xbt/misc/stream_writer.cpp +END +bt_torrent.h +K 25 +svn:wc:ra_dav:version-url +V 46 +/svn/!svn/ver/2020/trunk/xbt/misc/bt_torrent.h +END +socket.cpp +K 25 +svn:wc:ra_dav:version-url +V 44 +/svn/!svn/ver/2484/trunk/xbt/misc/socket.cpp +END +bt_tracker_account.cpp +K 25 +svn:wc:ra_dav:version-url +V 56 +/svn/!svn/ver/2020/trunk/xbt/misc/bt_tracker_account.cpp +END +sha1.cpp +K 25 +svn:wc:ra_dav:version-url +V 42 +/svn/!svn/ver/2186/trunk/xbt/misc/sha1.cpp +END +CMakeLists.txt +K 25 +svn:wc:ra_dav:version-url +V 48 +/svn/!svn/ver/2483/trunk/xbt/misc/CMakeLists.txt +END +alerts.h +K 25 +svn:wc:ra_dav:version-url +V 42 +/svn/!svn/ver/2020/trunk/xbt/misc/alerts.h +END diff --git a/misc/.svn/entries b/misc/.svn/entries new file mode 100644 index 0000000..4aa602b --- /dev/null +++ b/misc/.svn/entries @@ -0,0 +1,1122 @@ +10 + +dir +2494 +http://xbt.googlecode.com/svn/trunk/xbt/misc +http://xbt.googlecode.com/svn + + + +2015-06-12T15:14:35.736728Z +2494 +olafvdspek + + + + + + + + + + + + + + +a4ef9278-c6d2-effe-6dce-f9b4ebbbbc1a + +stream_writer.h +file + + + + +2015-07-14T06:50:41.464095Z +350d1d1187a3b4248d8799afcb969504 +2011-10-25T12:45:47.652726Z +2244 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +470 + +socket.h +file + + + + +2015-07-14T06:50:41.464095Z +e1582d32498567b627c835fa2a05853b +2014-10-25T13:46:59.509710Z +2443 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +2969 + +xif_key.cpp +file + + + + +2015-07-14T06:50:41.456095Z +c15fe5edb41ef970c06543bb70a02373 +2011-10-25T13:19:26.868307Z +2245 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +3607 + +sha1.h +file + + + + +2015-07-14T06:50:41.456095Z +feea6958a0acc7c7e11965c827f517df +2011-03-29T17:06:55.376009Z +2128 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +1857 + +bt_tracker_account.h +file + + + + +2015-07-14T06:50:41.456095Z +5350df70c6362d6bba099fc656135b32 +2010-04-15T16:24:59.778639Z +2020 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +722 + +bt_tracker_url.cpp +file + + + + +2015-07-14T06:50:41.456095Z +fddfbbf7d60e2049fd2bf97ab8579ed9 +2010-04-15T16:24:59.778639Z +2020 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +1297 + +database.cpp +file + + + + +2015-07-14T06:50:41.456095Z +fe2deefb9ea9faedb38f4dda33b5edfd +2014-08-29T23:07:52.143958Z +2439 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +2091 + +xcc_z.cpp +file + + + + +2015-07-14T06:50:41.456095Z +3cf2a4f23b8aa763ba8341378c1bf3c6 +2014-03-29T14:05:16.909554Z +2423 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +1646 + +windows +dir + +xbt +dir + +stream_reader.cpp +file + + + + +2015-07-14T06:50:41.456095Z +4b2b77d25b58164e0947f651c805bea9 +2010-04-15T16:24:59.778639Z +2020 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +48 + +bt_misc.cpp +file + + + + +2015-07-14T06:50:41.460095Z +2beb8a92c78d002d96cb2cd3eb1b2c6b +2015-06-12T15:14:35.736728Z +2494 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +7524 + +bvalue.h +file + + + + +2015-07-14T06:50:41.460095Z +62db9a7e94f9445706b0de54523e5dee +2011-10-18T18:10:23.294255Z +2233 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +1153 + +tf_misc.cpp +file + + + + +2015-07-14T06:50:41.460095Z +6a9b83a461480610afcb9e47dbacde43 +2015-04-02T14:04:30.162518Z +2479 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +6016 + +stream_writer.cpp +file + + + + +2015-07-14T06:50:41.460095Z +ca79a8d082f94737f58e10f2b25a0be9 +2010-04-15T16:24:59.778639Z +2020 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +47 + +bt_torrent.h +file + + + + +2015-07-14T06:50:41.460095Z +12af38b9592bf66588710b9628878e2a +2010-04-15T16:24:59.778639Z +2020 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +1027 + +socket.cpp +file + + + + +2015-07-14T06:50:41.460095Z +54d6374d66190c1576fe584df6c1c870 +2015-05-05T14:38:36.684267Z +2484 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +5780 + +CMakeLists.txt +file + + + + +2015-07-14T06:50:41.460095Z +efc70edda2c42f1f7098acb3d03f005d +2015-05-02T12:06:59.703938Z +2483 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +272 + +sha1.cpp +file + + + + +2015-07-14T06:50:41.460095Z +b0eae898d09defdfa38fddb8015f2185 +2011-09-25T16:04:24.208468Z +2186 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +10733 + +bt_tracker_account.cpp +file + + + + +2015-07-14T06:50:41.460095Z +e3828cecc2c4ee9550bc4c1dd8cb9fd3 +2010-04-15T16:24:59.778639Z +2020 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +1429 + +alerts.h +file + + + + +2015-07-14T06:50:41.460095Z +90b888f51cac8004385c18ae8e46745c +2010-04-15T16:24:59.778639Z +2020 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +1033 + +config_base.h +file + + + + +2015-07-14T06:50:41.460095Z +21f3f231788193917234fb8aa5ecea40 +2010-04-15T16:24:59.778639Z +2020 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +2325 + +bt_tracker_url.h +file + + + + +2015-07-14T06:50:41.460095Z +0419c40165637bea6636dbcecf228f90 +2010-04-15T16:24:59.778639Z +2020 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +317 + +stream_int.h +file + + + + +2015-07-14T06:50:41.460095Z +0a06ccd018cc330fa93345c48fd7fb9c +2014-11-03T13:57:06.199832Z +2449 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +1621 + +bvalue.cpp +file + + + + +2015-07-14T06:50:41.464095Z +0a18f6451165450def8e2883bfbf7b95 +2012-06-01T12:24:31.168150Z +2339 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +5984 + +xif_key_r.cpp +file + + + + +2015-07-14T06:50:41.464095Z +eab51737939957eaaee072a668d6ece3 +2012-11-10T16:39:45.863522Z +2371 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +1666 + +xif_value.cpp +file + + + + +2015-07-14T06:50:41.464095Z +104fe36356a988722084aa3c3e1f2fec +2012-11-10T16:39:45.863522Z +2371 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +2075 + +stream_reader.h +file + + + + +2015-07-14T06:50:41.464095Z +1cd3a5761da99b9e09a43ec9644defb5 +2011-03-29T17:06:55.376009Z +2128 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +814 + +bt_torrent.cpp +file + + + + +2015-07-14T06:50:41.464095Z +2542b24164e91e03d3db550bd5ba14a2 +2010-04-15T16:24:59.778639Z +2020 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +1867 + +bt_strings.h +file + + + + +2015-07-14T06:50:41.464095Z +18cfaedc79ab6579b42b246812607cf6 +2010-04-15T16:24:59.778639Z +2020 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +4245 + +sql_query.cpp +file + + + + +2015-07-14T06:50:41.464095Z +a9d632efe2111b0a6abd8f55995c5cf5 +2013-04-16T09:49:22.931379Z +2384 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +1975 + +tf_misc.h +file + + + + +2015-07-14T06:50:41.464095Z +256c4bebd1ba3f1d38730c723f321555 +2014-06-26T12:22:00.924804Z +2431 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +289 + +alerts.cpp +file + + + + +2015-07-14T06:50:41.464095Z +70b41c0aec501029f7a708c0e69c37b6 +2011-07-20T14:08:22.366166Z +2157 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +273 + +virtual_binary.cpp +file + + + + +2015-07-14T06:50:41.464095Z +ba12e79b78dc2d8226eab9497ad7d82b +2011-10-25T12:45:47.652726Z +2244 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +433 + diff --git a/misc/.svn/prop-base/CMakeLists.txt.svn-base b/misc/.svn/prop-base/CMakeLists.txt.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/CMakeLists.txt.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/alerts.cpp.svn-base b/misc/.svn/prop-base/alerts.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/alerts.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/alerts.h.svn-base b/misc/.svn/prop-base/alerts.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/alerts.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/bt_misc.cpp.svn-base b/misc/.svn/prop-base/bt_misc.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/bt_misc.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/bt_strings.h.svn-base b/misc/.svn/prop-base/bt_strings.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/bt_strings.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/bt_torrent.cpp.svn-base b/misc/.svn/prop-base/bt_torrent.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/bt_torrent.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/bt_torrent.h.svn-base b/misc/.svn/prop-base/bt_torrent.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/bt_torrent.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/bt_tracker_account.cpp.svn-base b/misc/.svn/prop-base/bt_tracker_account.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/bt_tracker_account.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/bt_tracker_account.h.svn-base b/misc/.svn/prop-base/bt_tracker_account.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/bt_tracker_account.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/bt_tracker_url.cpp.svn-base b/misc/.svn/prop-base/bt_tracker_url.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/bt_tracker_url.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/bt_tracker_url.h.svn-base b/misc/.svn/prop-base/bt_tracker_url.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/bt_tracker_url.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/bvalue.cpp.svn-base b/misc/.svn/prop-base/bvalue.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/bvalue.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/bvalue.h.svn-base b/misc/.svn/prop-base/bvalue.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/bvalue.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/config_base.h.svn-base b/misc/.svn/prop-base/config_base.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/config_base.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/database.cpp.svn-base b/misc/.svn/prop-base/database.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/database.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/sha1.cpp.svn-base b/misc/.svn/prop-base/sha1.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/sha1.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/sha1.h.svn-base b/misc/.svn/prop-base/sha1.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/sha1.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/socket.cpp.svn-base b/misc/.svn/prop-base/socket.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/socket.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/socket.h.svn-base b/misc/.svn/prop-base/socket.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/socket.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/sql_query.cpp.svn-base b/misc/.svn/prop-base/sql_query.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/sql_query.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/stream_int.h.svn-base b/misc/.svn/prop-base/stream_int.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/stream_int.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/stream_reader.cpp.svn-base b/misc/.svn/prop-base/stream_reader.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/stream_reader.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/stream_reader.h.svn-base b/misc/.svn/prop-base/stream_reader.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/stream_reader.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/stream_writer.cpp.svn-base b/misc/.svn/prop-base/stream_writer.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/stream_writer.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/stream_writer.h.svn-base b/misc/.svn/prop-base/stream_writer.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/stream_writer.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/tf_misc.cpp.svn-base b/misc/.svn/prop-base/tf_misc.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/tf_misc.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/tf_misc.h.svn-base b/misc/.svn/prop-base/tf_misc.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/tf_misc.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/virtual_binary.cpp.svn-base b/misc/.svn/prop-base/virtual_binary.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/virtual_binary.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/xcc_z.cpp.svn-base b/misc/.svn/prop-base/xcc_z.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/xcc_z.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/xif_key.cpp.svn-base b/misc/.svn/prop-base/xif_key.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/xif_key.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/xif_key_r.cpp.svn-base b/misc/.svn/prop-base/xif_key_r.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/xif_key_r.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/prop-base/xif_value.cpp.svn-base b/misc/.svn/prop-base/xif_value.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/.svn/prop-base/xif_value.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/.svn/text-base/CMakeLists.txt.svn-base b/misc/.svn/text-base/CMakeLists.txt.svn-base new file mode 100644 index 0000000..95f5bb6 --- /dev/null +++ b/misc/.svn/text-base/CMakeLists.txt.svn-base @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 2.4) +set(CMAKE_BUILD_TYPE release) +set(CMAKE_CXX_FLAGS -std=c++11) +include_directories(.) +add_library( + xbt + bt_misc.cpp + database.cpp + sql_query.cpp + xcc_z.cpp +) +target_link_libraries(xbt mysqlclient z) +install(TARGETS xbt DESTINATION lib) diff --git a/misc/.svn/text-base/alerts.cpp.svn-base b/misc/.svn/text-base/alerts.cpp.svn-base new file mode 100644 index 0000000..89ff7ba --- /dev/null +++ b/misc/.svn/text-base/alerts.cpp.svn-base @@ -0,0 +1,15 @@ +#include "stdafx.h" +#include "alerts.h" + +int Calert::pre_dump() const +{ + return m_message.size() + m_source.size() + 16; +} + +void Calert::dump(Cstream_writer& w) const +{ + w.write_int(4, m_time); + w.write_int(4, m_level); + w.write_data(m_message); + w.write_data(m_source); +} diff --git a/misc/.svn/text-base/alerts.h.svn-base b/misc/.svn/text-base/alerts.h.svn-base new file mode 100644 index 0000000..a97f175 --- /dev/null +++ b/misc/.svn/text-base/alerts.h.svn-base @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include "stream_writer.h" + +class Calert +{ +public: + enum t_level + { + emerg, + alert, + crit, + error, + warn, + notice, + info, + debug, + }; + + time_t time() const + { + return m_time; + } + + t_level level() const + { + return m_level; + } + + const std::string& message() const + { + return m_message; + } + + void message(const std::string& v) + { + m_message = v; + } + + Calert(t_level level, const std::string& message) + { + m_time = ::time(NULL); + m_level = level; + m_message = message; + } + + Calert(t_level level, const std::string& source, const std::string& message) + { + m_time = ::time(NULL); + m_level = level; + m_message = message; + m_source = source; + } + + int pre_dump() const; + void dump(Cstream_writer&) const; +private: + time_t m_time; + t_level m_level; + std::string m_message; + std::string m_source; +}; + +class Calerts: public std::list +{ +public: + void push_back(const value_type& v) + { + std::list::push_back(v); + while (size() > 250) + erase(begin()); + } +}; diff --git a/misc/.svn/text-base/bt_misc.cpp.svn-base b/misc/.svn/text-base/bt_misc.cpp.svn-base new file mode 100644 index 0000000..cb3e288 --- /dev/null +++ b/misc/.svn/text-base/bt_misc.cpp.svn-base @@ -0,0 +1,408 @@ +#include "xbt/bt_misc.h" + +#include +#include +#include +#include +#include +#include + +#ifdef WIN32 +#pragma comment(lib, "ws2_32") +#else +#include +#endif + +std::string escape_string(const std::string& v) +{ + std::string w; + w.reserve(v.length()); + for (char i : v) + { + if (isgraph(i & 0xff)) + w += i; + else + { + switch (i) + { + case '\0': + w += "\\0"; + break; + default: + w += "\\x" + hex_encode(2, i); + } + } + } + return w; +} + +std::string generate_random_string(int l) +{ + std::string v; + while (l--) + v += "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"[rand() % 62]; + return v; +} + +std::string get_env(const std::string& v) +{ + const char* p = getenv(v.c_str()); + return p ? p : ""; +} + +static int hex_decode(char v) +{ + if (v >= '0' && v <= '9') + return v - '0'; + if (v >= 'A' && v <= 'F') + return v - 'A' + 10; + if (v >= 'a' && v <= 'f') + return v - 'a' + 10; + return -1; +} + +std::string hex_decode(str_ref v) +{ + std::string r; + r.resize(v.size() >> 1); + for (size_t i = 0; i + 2 <= v.size(); i += 2) + { + int a = hex_decode(v[i]); + r[i >> 1] = a << 4 | hex_decode(v[i + 1]); + } + return r; +} + +std::string hex_encode(int l, int v) +{ + std::string r; + r.resize(l); + while (l--) + { + r[l] = "0123456789abcdef"[v & 0xf]; + v >>= 4; + } + return r; +} + +std::string n(long long v) +{ + return std::to_string(v); +} + +std::string hex_encode(data_ref v) +{ + std::string r; + r.reserve(v.size() << 1); + for (int i : v) + r += hex_encode(2, i); + return r; +} + +std::string js_encode(str_ref v) +{ + std::string r; + for (int i : v) + { + switch (i) + { + case '\"': + case '\'': + case '\\': + r += '\\'; + default: + r += i; + } + } + return r; +} + +std::string uri_decode(str_ref v) +{ + std::string r; + r.reserve(v.size()); + for (size_t i = 0; i < v.size(); i++) + { + char c = v[i]; + switch (c) + { + case '%': + { + if (i + 2 >= v.size()) + return std::string(); + int l = v[++i]; + r += hex_decode(l) << 4 | hex_decode(v[++i]); + break; + } + case '+': + r += ' '; + break; + default: + r += c; + } + } + return r; +} + +std::string uri_encode(str_ref v) +{ + std::string r; + r.reserve(v.size()); + for (char c : v) + { + if (isalpha(c & 0xff) || isdigit(c & 0xff)) + r += c; + else + { + switch (c) + { + case ' ': + r += '+'; + break; + case '-': + case ',': + case '.': + case '@': + case '_': + r += c; + break; + default: + r += "%" + hex_encode(2, c); + } + } + } + return r; +} + +bool is_private_ipa(int a) +{ + return (ntohl(a) & 0xff000000) == 0x0a000000 + || (ntohl(a) & 0xff000000) == 0x7f000000 + || (ntohl(a) & 0xfff00000) == 0xac100000 + || (ntohl(a) & 0xffff0000) == 0xc0a80000; +} + +std::string b2a(long long v, const char* postfix) +{ + char d[32]; + char* w = d; + if (v < 0) + { + v = -v; + *w++ = '-'; + } + int l = 0; + for (; v > 999999; l++) + v >>= 10; + if (v > 999) + { + l++; + int b = static_cast((v & 0x3ff) * 100 >> 10); + v >>= 10; + w += sprintf(w, "%d", static_cast(v)); + if (v < 10 && b % 10) + w += sprintf(w, ".%02d", b); + else if (v < 100 && b > 9) + w += sprintf(w, ".%d", b / 10); + } + else + w += sprintf(w, "%d", static_cast(v)); + const char* a[] = {"", " k", " m", " g", " t", " p", " e", " z", " y"}; + w += sprintf(w, "%s", a[l]); + if (postfix) + w += sprintf(w, "%s%s", l ? "" : " ", postfix); + return d; +} + +std::string n2a(long long v, const char* postfix) +{ + char d[32]; + char* w = d; + if (v < 0) + { + v = -v; + *w++ = '-'; + } + int l = 0; + for (; v > 999999; l++) + v /= 1000; + if (v > 999) + { + l++; + int b = static_cast(v % 1000 / 10); + v /= 1000; + w += sprintf(w, "%d", static_cast(v)); + if (v < 10 && b % 10) + w += sprintf(w, ".%02d", b); + else if (v < 100 && b > 9) + w += sprintf(w, ".%d", b / 10); + } + else + w += sprintf(w, "%d", static_cast(v)); + const char* a [] = { "", " k", " m", " g", " t", " p", " e", " z", " y" }; + w += sprintf(w, "%s", a[l]); + if (postfix) + w += sprintf(w, "%s%s", l ? "" : " ", postfix); + return d; +} + +static std::string peer_id2a(const std::string& name, const std::string& peer_id, int i) +{ + for (size_t j = i; j < peer_id.size(); j++) + { + if (!isalnum(peer_id[j])) + return name + peer_id.substr(i, j - i); + } + return name + peer_id.substr(i); +} + +std::string peer_id2a(const std::string& v) +{ + if (v.length() != 20) + return std::string(); + if (v[7] == '-') + { + switch (v[0]) + { + case '-': + if (v[1] == 'A' && v[2] == 'Z') + return peer_id2a("Azureus ", v, 3); + if (v[1] == 'B' && v[2] == 'C') + return peer_id2a("BitComet ", v, 3); + if (v[1] == 'U' && v[2] == 'T') + return peer_id2a("uTorrent ", v, 3); + if (v[1] == 'T' && v[2] == 'S') + return peer_id2a("TorrentStorm ", v, 3); + break; + case 'A': + return peer_id2a("ABC ", v, 1); + case 'M': + return peer_id2a("Mainline ", v, 1); + case 'S': + return peer_id2a("Shadow ", v, 1); + case 'T': + return peer_id2a("BitTornado ", v, 1); + case 'X': + if (v[1] == 'B' && v[2] == 'T') + return peer_id2a("XBT Client ", v, 3) + (v.find_first_not_of("0123456789ABCDEFGHIJKLMNOPQRSTUVWYXZabcdefghijklmnopqrstuvwyxz", 8) == std::string::npos ? "" : " (fake)"); + break; + } + } + switch (v[0]) + { + case '-': + if (v[1] == 'G' && v[2] == '3') + return "G3"; + break; + case 'S': + if (v[1] == 5 && v[2] == 7 && v[3] >= 0 && v[3] < 10) + return "Shadow 57" + n(v[3]); + break; + case 'e': + if (v[1] == 'x' && v[2] == 'b' && v[3] == 'c' && v[4] >= 0 && v[4] < 10 && v[5] >= 0 && v[5] < 100) + return "BitComet " + n(v[4]) + '.' + n(v[5] / 10) + n(v[5] % 10); + } + return "Unknown"; +} + +std::string duration2a(float v) +{ + char d[32]; + if (v > 31557600) + sprintf(d, "%.1f years", v / 31557600); + else if (v > 2629800) + sprintf(d, "%.1f months", v / 2629800); + else if (v > 604800) + sprintf(d, "%.1f weeks", v / 604800); + else if (v > 86400) + sprintf(d, "%.1f days", v / 86400); + else if (v > 3600) + sprintf(d, "%.1f hours", v / 3600); + else if (v > 60) + sprintf(d, "%.1f minutes", v / 60); + else + sprintf(d, "%.1f seconds", v); + return d; +} + +std::string time2a(time_t v) +{ + const tm* date = localtime(&v); + if (!date) + return std::string(); + char b[20]; + sprintf(b, "%04d-%02d-%02d %02d:%02d:%02d", date->tm_year + 1900, date->tm_mon + 1, date->tm_mday, date->tm_hour, date->tm_min, date->tm_sec); + return b; +} + +int merkle_tree_size(int v) +{ + int r = 0; + while (v > 1) + { + r += v++; + v >>= 1; + } + if (v == 1) + r++; + return r; +} + +std::string backward_slashes(std::string v) +{ + std::replace(v.begin(), v.end(), '/', '\\'); + return v; +} + +std::string forward_slashes(std::string v) +{ + std::replace(v.begin(), v.end(), '\\', '/'); + return v; +} + +std::string native_slashes(const std::string& v) +{ +#ifdef WIN32 + return backward_slashes(v); +#else + return forward_slashes(v); +#endif +} + +int hms2i(int h, int m, int s) +{ + return 60 * (h + 60 * m) + s; +} + +std::string xbt_version2a(int v) +{ + return n(v / 100) + "." + n(v / 10 % 10) + "." + n(v % 10); +} + +std::string mk_sname(std::string v) +{ + boost::erase_all(v, "-"); + boost::erase_all(v, "@"); + std::replace(v.begin(), v.end(), '0', 'o'); + std::replace(v.begin(), v.end(), '1', 'i'); + std::replace(v.begin(), v.end(), '3', 'e'); + std::replace(v.begin(), v.end(), '4', 'a'); + std::replace(v.begin(), v.end(), 'l', 'i'); + for (size_t i = 1; i < v.size(); ) + { + if (v[i] == v[i - 1]) + v.erase(i, 1); + else + i++; + } + return v; +} + +void xbt_syslog(const std::string& v) +{ +#ifdef WIN32 + std::cerr << v << std::endl; +#else + syslog(LOG_ERR, "%s", v.c_str()); +#endif +} diff --git a/misc/.svn/text-base/bt_strings.h.svn-base b/misc/.svn/text-base/bt_strings.h.svn-base new file mode 100644 index 0000000..af62954 --- /dev/null +++ b/misc/.svn/text-base/bt_strings.h.svn-base @@ -0,0 +1,113 @@ +#pragma once + +enum +{ + bti_choke , + bti_unchoke, + bti_interested, + bti_uninterested, + bti_have, + bti_bitfield, + bti_request, + bti_piece, + bti_cancel, + + bti_get_info, + bti_info, + bti_get_peers, + bti_peers, + + bti_extended = 20, + + bti_bvalue = 0x40, +}; + +enum +{ + bti_extended_handshake, + bti_extended_ut_pex, +}; + +enum +{ + bti_none, + bti_completed, + bti_started, + bti_stopped, +}; + +const std::string bts_action = "action"; +const std::string bts_admin_port = "admin port"; +const std::string bts_admin_user = "admin user"; +const std::string bts_admin_pass = "admin pass"; +const std::string bts_announce = "announce"; +const std::string bts_announce_list = "announce-list"; +const std::string bts_banned_client = "access denied, banned client"; +const std::string bts_can_not_leech = "access denied, leeching forbidden, you are only allowed to seed"; +const std::string bts_close_torrent = "close torrent"; +const std::string bts_complete = "complete"; +const std::string bts_complete_total = "complete total"; +const std::string bts_completed_at = "completed at"; +const std::string bts_completes_dir = "completes dir"; +const std::string bts_down_rate = "down rate"; +const std::string bts_downloaded = "downloaded"; +const std::string bts_erase_torrent = "erase torrent"; +const std::string bts_events = "events"; +const std::string bts_failure_reason = "failure reason"; +const std::string bts_files = "files"; +const std::string bts_flags = "flags"; +const std::string bts_get_options = "get options"; +const std::string bts_get_status = "get status"; +const std::string bts_hash = "hash"; +const std::string bts_incomplete = "incomplete"; +const std::string bts_incomplete_total = "incomplete total"; +const std::string bts_incompletes_dir = "incompletes dir"; +const std::string bts_info = "info"; +const std::string bts_interval = "interval"; +const std::string bts_ipa = "ip"; +const std::string bts_left = "left"; +const std::string bts_length = "length"; +const std::string bts_login = "login"; +const std::string bts_merkle_hash = "merkle hash"; +const std::string bts_message = "message"; +const std::string bts_min_interval = "min interval"; +const std::string bts_min_request_interval = "min_request_interval"; +const std::string bts_name = "name"; +const std::string bts_open_torrent = "open torrent"; +const std::string bts_pass = "pass"; +const std::string bts_path = "path"; +const std::string bts_peer_id = "peer id"; +const std::string bts_peer_limit = "peer limit"; +const std::string bts_peer_port = "peer port"; +const std::string bts_peers = "peers"; +const std::string bts_peers_limit_reached = "access denied, peers limit reached"; +const std::string bts_piece_length = "piece length"; +const std::string bts_pieces = "pieces"; +const std::string bts_private = "private"; +const std::string bts_port = "port"; +const std::string bts_priority = "priority"; +const std::string bts_seeding_ratio = "seeding ratio"; +const std::string bts_set_options = "set options"; +const std::string bts_set_priority = "set priority"; +const std::string bts_set_state = "set state"; +const std::string bts_size = "size"; +const std::string bts_started_at = "started at"; +const std::string bts_state = "state"; +const std::string bts_time = "time"; +const std::string bts_torrent = "torrent"; +const std::string bts_torrent_limit = "torrent limit"; +const std::string bts_torrents_dir = "torrents dir"; +const std::string bts_torrents_limit_reached = "access denied, torrents limit reached"; +const std::string bts_total_downloaded = "total downloaded"; +const std::string bts_total_uploaded = "total uploaded"; +const std::string bts_tracker_port = "tracker port"; +const std::string bts_unregistered_ipa = "unregistered IP address"; +const std::string bts_unregistered_torrent = "unregistered torrent"; +const std::string bts_unregistered_torrent_pass = "unregistered torrent pass"; +const std::string bts_unsupported_tracker_protocol = "unsupported tracker protocol, please upgrade your client"; +const std::string bts_up_rate = "up rate"; +const std::string bts_upload_rate = "upload rate"; +const std::string bts_upload_slots = "upload slots"; +const std::string bts_user_agent = "user agent"; +const std::string bts_version = "version"; +const std::string bts_wait_time = "access denied, wait time in effect"; diff --git a/misc/.svn/text-base/bt_torrent.cpp.svn-base b/misc/.svn/text-base/bt_torrent.cpp.svn-base new file mode 100644 index 0000000..efae0f6 --- /dev/null +++ b/misc/.svn/text-base/bt_torrent.cpp.svn-base @@ -0,0 +1,77 @@ +#include "stdafx.h" +#include "bt_torrent.h" + +#include "bt_strings.h" + +Cbt_torrent::Cbt_torrent() +{ +} + +Cbt_torrent::Cbt_torrent(const Cbvalue& v) +{ + write(v); +} + +int Cbt_torrent::write(const Cbvalue& v) +{ + m_announce = v[bts_announce].s(); + m_announces.clear(); + const Cbvalue::t_list& announces = v[bts_announce_list].l(); + for (Cbvalue::t_list::const_iterator i = announces.begin(); i != announces.end(); i++) + { + for (Cbvalue::t_list::const_iterator j = i->l().begin(); j != i->l().end(); j++) + m_announces.push_back(j->s()); + } + return write_info(v[bts_info]); +} + +int Cbt_torrent::write_info(const Cbvalue& v) +{ + m_files.clear(); + const Cbvalue::t_list& files = v[bts_files].l(); + for (Cbvalue::t_list::const_iterator i = files.begin(); i != files.end(); i++) + { + std::string name; + long long size = (*i)[bts_length].i(); + { + const Cbvalue::t_list& path = (*i)[bts_path].l(); + for (Cbvalue::t_list::const_iterator i = path.begin(); i != path.end(); i++) + { + if (i->s().empty() || i->s()[0] == '.' || i->s().find_first_of("\"*/:<>?\\|") != std::string::npos) + return 1; + name += '/' + i->s(); + } + } + if (name.empty()) + return 1; + m_files.push_back(Cfile(name, size)); + } + if (m_files.empty()) + m_files.push_back(Cfile("", v[bts_length].i())); + m_name = v[bts_name].s(); + m_piece_size = v[bts_piece_length].i(); + return 0; +} + +long long Cbt_torrent::size() const +{ + long long r = 0; + for (t_files::const_iterator i = m_files.begin(); i != m_files.end(); i++) + r += i->size(); + return r; +} + +bool Cbt_torrent::valid() const +{ + for (t_files::const_iterator i = m_files.begin(); i != m_files.end(); i++) + { + if (i->size() < 0) + return false; + } + return !files().empty() + && !name().empty() + && name()[0] != '.' + && name().find_first_of("\"*/:<>?\\|") == std::string::npos + && piece_size() >= 16 << 10 + && piece_size() <= 4 << 20; +} diff --git a/misc/.svn/text-base/bt_torrent.h.svn-base b/misc/.svn/text-base/bt_torrent.h.svn-base new file mode 100644 index 0000000..d808b9e --- /dev/null +++ b/misc/.svn/text-base/bt_torrent.h.svn-base @@ -0,0 +1,75 @@ +#pragma once + +#include "bvalue.h" + +class Cbt_torrent +{ +public: + class Cfile + { + public: + const std::string& name() const + { + return m_name; + } + + long long size() const + { + return m_size; + } + + Cfile() + { + } + + Cfile(const std::string& name, long long size) + { + m_name = name; + m_size = size; + } + private: + std::string m_name; + long long m_size; + }; + + typedef std::vector t_announces; + typedef std::vector t_files; + + long long size() const; + bool valid() const; + int write(const Cbvalue&); + int write_info(const Cbvalue&); + Cbt_torrent(); + Cbt_torrent(const Cbvalue&); + + const std::string& announce() const + { + return m_announce; + } + + const t_announces& announces() const + { + return m_announces; + } + + const t_files& files() const + { + return m_files; + } + + const std::string& name() const + { + return m_name; + } + + int piece_size() const + { + return m_piece_size; + } +private: + std::string m_announce; + t_announces m_announces; + t_files m_files; + std::string m_name; + int m_piece_size; +}; diff --git a/misc/.svn/text-base/bt_tracker_account.cpp.svn-base b/misc/.svn/text-base/bt_tracker_account.cpp.svn-base new file mode 100644 index 0000000..b812d4f --- /dev/null +++ b/misc/.svn/text-base/bt_tracker_account.cpp.svn-base @@ -0,0 +1,67 @@ +#include "stdafx.h" +#include "bt_tracker_account.h" + +#include "stream_reader.h" + +Cbt_tracker_account::Cbt_tracker_account() +{ +} + +Cbt_tracker_account::Cbt_tracker_account(const std::string& tracker, const std::string& user, const std::string& pass) +{ + m_tracker = tracker; + m_user = user; + m_pass = pass; +} + +int Cbt_tracker_account::pre_dump() const +{ + return tracker().size() + user().size() + pass().size() + 12; +} + +void Cbt_tracker_account::dump(Cstream_writer& w) const +{ + w.write_data(tracker()); + w.write_data(user()); + w.write_data(pass()); +} + +Cvirtual_binary Cbt_tracker_accounts::dump() const +{ + int cb_d = 4; + for (const_iterator i = begin(); i != end(); i++) + cb_d += i->pre_dump(); + Cvirtual_binary d; + Cstream_writer w(d.write_start(cb_d)); + w.write_int(4, size()); + for (const_iterator i = begin(); i != end(); i++) + i->dump(w); + assert(w.w() == d.end()); + return d; + +} + +const Cbt_tracker_account* Cbt_tracker_accounts::find(const std::string& v) const +{ + for (const_iterator i = begin(); i != end(); i++) + { + if (i->tracker() == v) + return &*i; + } + return NULL; +} + +void Cbt_tracker_accounts::load(const Cvirtual_binary& s) +{ + clear(); + if (s.size() < 4) + return; + Cstream_reader r(s); + for (int count = r.read_int(4); count--; ) + { + std::string tracker = r.read_string(); + std::string name = r.read_string(); + std::string pass = r.read_string(); + push_back(Cbt_tracker_account(tracker, name, pass)); + } +} diff --git a/misc/.svn/text-base/bt_tracker_account.h.svn-base b/misc/.svn/text-base/bt_tracker_account.h.svn-base new file mode 100644 index 0000000..ec197fc --- /dev/null +++ b/misc/.svn/text-base/bt_tracker_account.h.svn-base @@ -0,0 +1,39 @@ +#pragma once + +#include + +class Cbt_tracker_account +{ +public: + int pre_dump() const; + void dump(Cstream_writer&) const; + Cbt_tracker_account(); + Cbt_tracker_account(const std::string& tracker, const std::string& user, const std::string& pass); + + const std::string& tracker() const + { + return m_tracker; + } + + const std::string& user() const + { + return m_user; + } + + const std::string& pass() const + { + return m_pass; + } +private: + std::string m_tracker; + std::string m_user; + std::string m_pass; +}; + +class Cbt_tracker_accounts: public std::list +{ +public: + Cvirtual_binary dump() const; + const Cbt_tracker_account* find(const std::string&) const; + void load(const Cvirtual_binary&); +}; diff --git a/misc/.svn/text-base/bt_tracker_url.cpp.svn-base b/misc/.svn/text-base/bt_tracker_url.cpp.svn-base new file mode 100644 index 0000000..7630a36 --- /dev/null +++ b/misc/.svn/text-base/bt_tracker_url.cpp.svn-base @@ -0,0 +1,82 @@ +#include "stdafx.h" +#include "bt_tracker_url.h" + +#include + +Cbt_tracker_url::Cbt_tracker_url() +{ +} + +Cbt_tracker_url::Cbt_tracker_url(const std::string& v) +{ + write(v); +} + +void Cbt_tracker_url::clear() +{ + m_protocol = tp_unknown; + m_host.erase(); + m_port = 0; + m_path.erase(); +} + +bool Cbt_tracker_url::valid() const +{ + switch (m_protocol) + { + case tp_http: + if (m_path.empty() || m_path[0] != '/') + return false; + case tp_udp: + return !m_host.empty() + && m_port >= 0 && m_port < 0x10000; + } + return false; +} + +void Cbt_tracker_url::write(const std::string& v) +{ + clear(); + size_t a; + int protocol; + int port; + if (boost::istarts_with(v, "http://")) + { + a = 7; + protocol = tp_http; + port = 80; + } + else if (boost::istarts_with(v, "udp://")) + { + a = 6; + protocol = tp_udp; + port = 2710; + } + else + return; + size_t b = v.find_first_of("/:", a); + std::string host; + if (b == std::string::npos) + host = v.substr(a); + else + { + host = v.substr(a, b - a); + if (v[b] == '/') + m_path = v.substr(b); + else + { + b++; + a = v.find('/', b); + if (a == std::string::npos) + port = atoi(v.substr(b).c_str()); + else + { + port = atoi(v.substr(b, a - b).c_str()); + m_path = v.substr(a); + } + } + } + m_protocol = protocol; + m_host = host; + m_port = port; +} diff --git a/misc/.svn/text-base/bt_tracker_url.h.svn-base b/misc/.svn/text-base/bt_tracker_url.h.svn-base new file mode 100644 index 0000000..c100189 --- /dev/null +++ b/misc/.svn/text-base/bt_tracker_url.h.svn-base @@ -0,0 +1,25 @@ +#pragma once + +#include + +class Cbt_tracker_url +{ +public: + enum + { + tp_http, + tp_udp, + tp_unknown + }; + + void clear(); + bool valid() const; + void write(const std::string&); + Cbt_tracker_url(const std::string&); + Cbt_tracker_url(); + + int m_protocol; + std::string m_host; + int m_port; + std::string m_path; +}; diff --git a/misc/.svn/text-base/bvalue.cpp.svn-base b/misc/.svn/text-base/bvalue.cpp.svn-base new file mode 100644 index 0000000..8509748 --- /dev/null +++ b/misc/.svn/text-base/bvalue.cpp.svn-base @@ -0,0 +1,365 @@ +#include +#include "bvalue.h" + +#include +#include +#include +#include +#include + +Cbvalue::Cbvalue(long long v) +{ + m_value_type = vt_int; + m_int = v; +} + +Cbvalue::Cbvalue(t_value_type t) +{ + switch (m_value_type = t) + { + case vt_int: + break; + case vt_string: + m_string = new std::string; + break; + case vt_list: + m_list = new t_list; + break; + case vt_dictionary: + m_map = new t_map; + break; + default: + assert(false); + } +} + +Cbvalue::Cbvalue(const std::string& v) +{ + m_value_type = vt_string; + m_string = new std::string(v); +} + +Cbvalue::Cbvalue(const Cbvalue& v) +{ + switch (m_value_type = v.m_value_type) + { + case vt_int: + m_int = v.m_int; + break; + case vt_string: + m_string = new std::string(*v.m_string); + break; + case vt_list: + m_list = new t_list(*v.m_list); + break; + case vt_dictionary: + m_map = new t_map(*v.m_map); + break; + default: + assert(false); + } +} + +Cbvalue::Cbvalue(data_ref s) +{ + m_value_type = vt_int; + if (write(s)) + clear(); +} + +Cbvalue::~Cbvalue() +{ + clear(); +} + +const Cbvalue& Cbvalue::operator=(const Cbvalue& v) +{ + clear(); + m_value_type = v.m_value_type; + switch (v.m_value_type) + { + case vt_int: + m_int = v.m_int; + break; + case vt_string: + m_string = new std::string(*v.m_string); + break; + case vt_list: + m_list = new t_list(*v.m_list); + break; + case vt_dictionary: + m_map = new t_map(*v.m_map); + break; + default: + assert(false); + } + return *this; +} + +int Cbvalue::write(str_ref s) +{ + return write(s.data(), s.size()); +} + +int Cbvalue::write(const char* s, int cb_s) +{ + return write(s, s + cb_s); +} + +int Cbvalue::write(const char*& s, const char* s_end) +{ + clear(); + if (s >= s_end) + return 1; + switch (*s++) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + const char* a = s - 1; + while (s < s_end && *s != ':') + s++; + if (s++ >= s_end) + return 1; + int l = atoi(a); + if (s + l > s_end) + return 1; + m_value_type = vt_string; + m_string = new std::string(s, l); + s += l; + return 0; + } + case 'd': + { + m_value_type = vt_dictionary; + m_map = new t_map; + while (s < s_end && *s != 'e') + { + Cbvalue v; + Cbvalue w; + if (v.write(s, s_end) || v.m_value_type != vt_string) + return 1; + if (w.write(s, s_end)) + return 1; + (*m_map)[*v.m_string] = w; + } + if (s++ >= s_end) + return 1; + return 0; + } + break; + case 'i': + { + const char* a = s; + while (s < s_end && *s != 'e') + s++; + if (s++ >= s_end) + return 1; + m_value_type = vt_int; + m_int = atoll(a); + return 0; + } + case 'l': + { + m_value_type = vt_list; + m_list = new t_list; + while (s < s_end && *s != 'e') + { + Cbvalue v; + if (v.write(s, s_end)) + return 1; + m_list->push_back(v); + } + if (s++ >= s_end) + return 1; + return 0; + } + } + return 1; +} + +void Cbvalue::clear() +{ + switch (m_value_type) + { + case vt_int: + break; + case vt_string: + delete m_string; + break; + case vt_list: + delete m_list; + break; + case vt_dictionary: + delete m_map; + break; + default: + assert(false); + } + m_value_type = vt_int; +} + +const Cbvalue::t_map& Cbvalue::d() const +{ + static t_map z; + return m_value_type == vt_dictionary ? *m_map : z; +} + +bool Cbvalue::d_has(const std::string& v) const +{ + return m_value_type == vt_dictionary && m_map->count(v); +} + +const Cbvalue& Cbvalue::d(const std::string& v) const +{ + static Cbvalue z; + return m_value_type == vt_dictionary ? find_ref(*m_map, v, z) : z; +} + +const Cbvalue& Cbvalue::operator[](const std::string& v) const +{ + return d(v); +} + +long long Cbvalue::i() const +{ + return m_value_type == vt_int ? m_int : 0; +} + +const Cbvalue::t_list& Cbvalue::l() const +{ + static t_list z; + return m_value_type == vt_list ? *m_list : z; +} + +const std::string& Cbvalue::s() const +{ + static std::string z; + return m_value_type == vt_string ? *m_string : z; +} + +Cbvalue& Cbvalue::d(const std::string& v, const Cbvalue& w) +{ + if (m_value_type != vt_dictionary) + { + clear(); + m_value_type = vt_dictionary; + m_map = new t_map; + } + (*m_map)[v] = w; + return *this; +} + +Cbvalue& Cbvalue::l(const Cbvalue& v) +{ + if (m_value_type != vt_list) + { + clear(); + m_value_type = vt_list; + m_list = new t_list; + } + (*m_list).push_back(v); + return *this; +} + +int Cbvalue::pre_read() const +{ + switch (m_value_type) + { + case vt_int: + return n(m_int).size() + 2; + case vt_string: + return n(m_string->size()).size() + m_string->size() + 1; + case vt_list: + { + int v = 2; + BOOST_FOREACH(auto& i, *m_list) + v += i.pre_read(); + return v; + } + case vt_dictionary: + { + int v = 2; + BOOST_FOREACH(auto& i, *m_map) + v += n(i.first.size()).size() + i.first.size() + i.second.pre_read() + 1; + return v; + } + } + assert(false); + return 0; +} + +shared_data Cbvalue::read() const +{ + shared_data d(pre_read()); + BOOST_VERIFY(read(d.data()) == d.size()); + return d; +} + +int Cbvalue::read(void* d) const +{ + return read(reinterpret_cast(d)); +} + +int Cbvalue::read(char* d) const +{ + char* w = d; + switch (m_value_type) + { + case vt_int: +#ifdef WIN32 + sprintf(d, "i%I64d", m_int); +#else + sprintf(d, "i%lld", m_int); +#endif + w += strlen(d); + *w++ = 'e'; + return w - d; + case vt_string: +#ifdef WIN32 + sprintf(w, "%d:", m_string->size()); +#else + sprintf(w, "%zu:", m_string->size()); +#endif + w += n(m_string->size()).size() + 1; + memcpy(w, m_string->data(), m_string->size()); + w += m_string->size(); + return w - d; + case vt_list: + { + *w++ = 'l'; + BOOST_FOREACH(auto& i, *m_list) + w += i.read(w); + *w++ = 'e'; + return w - d; + } + case vt_dictionary: + { + *w++ = 'd'; + BOOST_FOREACH(auto& i, *m_map) + { +#ifdef WIN32 + sprintf(w, "%d:", i.first.size()); +#else + sprintf(w, "%zu:", i.first.size()); +#endif + w += n(i.first.size()).size() + 1; + memcpy(w, i.first.data(), i.first.size()); + w += i.first.size(); + w += i.second.read(w); + } + *w++ = 'e'; + return w - d; + } + } + assert(false); + return 0; +} diff --git a/misc/.svn/text-base/bvalue.h.svn-base b/misc/.svn/text-base/bvalue.h.svn-base new file mode 100644 index 0000000..8a38014 --- /dev/null +++ b/misc/.svn/text-base/bvalue.h.svn-base @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include +#include + +class Cbvalue +{ +public: + enum t_value_type + { + vt_int, + vt_string, + vt_list, + vt_dictionary, + }; + + typedef std::map t_map; + typedef std::vector t_list; + + void clear(); + const t_map& d() const; + const t_list& l() const; + long long i() const; + const std::string& s() const; + bool d_has(const std::string&) const; + Cbvalue& d(const std::string& v, const Cbvalue& w); + Cbvalue& l(const Cbvalue& v); + int pre_read() const; + int read(char* d) const; + int read(void* d) const; + shared_data read() const; + int write(const char* s, int cb_s); + int write(str_ref); + Cbvalue(long long v = 0); + Cbvalue(t_value_type t); + Cbvalue(const std::string& v); + Cbvalue(const Cbvalue&); + Cbvalue(data_ref); + const Cbvalue& operator=(const Cbvalue&); + const Cbvalue& operator[](const std::string&) const; + ~Cbvalue(); +private: + const Cbvalue& d(const std::string&) const; + + t_value_type m_value_type; + + union + { + long long m_int; + std::string* m_string; + t_list* m_list; + t_map* m_map; + }; + + int write(const char*& s, const char* s_end); +}; diff --git a/misc/.svn/text-base/config_base.h.svn-base b/misc/.svn/text-base/config_base.h.svn-base new file mode 100644 index 0000000..b063397 --- /dev/null +++ b/misc/.svn/text-base/config_base.h.svn-base @@ -0,0 +1,108 @@ +#pragma once + +#include +#include +#include +#include +#include + +class Cconfig_base +{ +public: + template + struct t_attribute + { + const char* key; + T* value; + T default_value; + }; + + template + class t_attributes: public std::map > + { + }; + + virtual int set(const std::string& name, const std::string& value) + { + t_attributes::iterator i = m_attributes_string.find(name); + if (i != m_attributes_string.end()) + *i->second.value = value; + else + return set(name, atoi(value.c_str())); + return 0; + } + + virtual int set(const std::string& name, int value) + { + t_attributes::iterator i = m_attributes_int.find(name); + if (i != m_attributes_int.end()) + *i->second.value = value; + else + return set(name, static_cast(value)); + return 0; + } + + virtual int set(const std::string& name, bool value) + { + t_attributes::iterator i = m_attributes_bool.find(name); + if (i != m_attributes_bool.end()) + *i->second.value = value; + else + return 1; + return 0; + } + + std::istream& load(std::istream& is) + { + for (std::string s; getline(is, s); ) + { + size_t i = s.find('='); + if (i != std::string::npos) + set(boost::trim_copy(s.substr(0, i)), boost::trim_copy(s.substr(i + 1))); + } + return is; + } + + int load(const std::string& file) + { + std::ifstream is(file.c_str()); + if (!is) + return 1; + load(is); + return !is.eof(); + } + + std::ostream& save(std::ostream& os) const + { + save_map(os, m_attributes_bool); + save_map(os, m_attributes_int); + save_map(os, m_attributes_string); + return os; + } + +protected: + t_attributes m_attributes_bool; + t_attributes m_attributes_int; + t_attributes m_attributes_string; + + template + void fill_map(t_attribute* attributes, const t_attributes* s, t_attributes& d) + { + for (t_attribute* i = attributes; i->key; i++) + { + *i->value = s ? *s->find(i->key)->second.value : i->default_value; + d[i->key] = *i; + } + } + + template + void save_map(std::ostream& os, const T& v) const + { + for (typename T::const_iterator i = v.begin(); i != v.end(); i++) + { + if (*i->second.value == i->second.default_value) + os << "# "; + os << i->first << " = " << *i->second.value << std::endl; + } + } +}; diff --git a/misc/.svn/text-base/database.cpp.svn-base b/misc/.svn/text-base/database.cpp.svn-base new file mode 100644 index 0000000..9f6b1c9 --- /dev/null +++ b/misc/.svn/text-base/database.cpp.svn-base @@ -0,0 +1,99 @@ +#include + +#include +#include + +#ifdef WIN32 +#pragma comment(lib, "libmysql") +#else +#include +#endif + +Cdatabase::Cdatabase() +{ + mysql_init(&m_handle); +} + +Cdatabase::~Cdatabase() +{ + close(); +} + +void Cdatabase::open(const std::string& host, const std::string& user, const std::string& password, const std::string& database, bool echo_errors) +{ + m_echo_errors = echo_errors; + if (!mysql_init(&m_handle) + || mysql_options(&m_handle, MYSQL_READ_DEFAULT_GROUP, "") + || !mysql_real_connect(&m_handle, host.c_str(), user.c_str(), password.empty() ? NULL : password.c_str(), database.c_str(), database == "sphinx" ? 9306 : 0, NULL, 0)) + throw bad_query(mysql_error(&m_handle)); + char a0 = true; + mysql_options(&m_handle, MYSQL_OPT_RECONNECT, &a0); +} + +int Cdatabase::query_nothrow(const std::string& q) +{ + if (m_query_log) + { + *m_query_log << q.substr(0, 999) << std::endl; + } + if (mysql_real_query(&m_handle, q.data(), q.size())) + { + if (m_echo_errors) + { + std::cerr << mysql_error(&m_handle) << std::endl + << q.substr(0, 239) << std::endl; + } +#ifndef WIN32 + syslog(LOG_ERR, "%s", mysql_error(&m_handle)); +#endif + return 1; + } + return 0; +} + +Csql_result Cdatabase::query(const std::string& q) +{ + if (query_nothrow(q)) + throw bad_query(mysql_error(&m_handle)); + MYSQL_RES* result = mysql_store_result(&m_handle); + if (!result && mysql_errno(&m_handle)) + throw bad_query(mysql_error(&m_handle)); + return Csql_result(result); +} + +void Cdatabase::close() +{ + mysql_close(&m_handle); +} + +int Cdatabase::affected_rows() +{ + return mysql_affected_rows(&m_handle); +} + +int Cdatabase::insert_id() +{ + return mysql_insert_id(&m_handle); +} + +int Cdatabase::select_db(const std::string& v) +{ + return mysql_select_db(&m_handle, v.c_str()); +} + +void Cdatabase::set_query_log(std::ostream* v) +{ + m_query_log = v; +} + +void Cdatabase::set_name(const std::string& a, std::string b) +{ + m_names[a] = std::move(b); +} + +const std::string& Cdatabase::name(const std::string& v) const +{ + const std::string* i = find_ptr(m_names, v); + assert(i); + return i ? *i : v; +} diff --git a/misc/.svn/text-base/sha1.cpp.svn-base b/misc/.svn/text-base/sha1.cpp.svn-base new file mode 100644 index 0000000..ccb02fa --- /dev/null +++ b/misc/.svn/text-base/sha1.cpp.svn-base @@ -0,0 +1,418 @@ +/* + * sha1.c + * + * Description: + * This file implements the Secure Hashing Algorithm 1 as + * defined in FIPS PUB 180-1 published April 17, 1995. + * + * The SHA-1, produces a 160-bit message digest for a given + * data stream. It should take about 2**n steps to find a + * message with the same digest as a given message and + * 2**(n/2) to find any two messages with the same digest, + * when n is the digest size in bits. Therefore, this + * algorithm can serve as a means of providing a + * "fingerprint" for a message. + * + * Portability Issues: + * SHA-1 is defined in terms of 32-bit "words". This code + * uses (included via "sha1.h" to define 32 and 8 + * bit unsigned integer types. If your C compiler does not + * support 32 bit unsigned integers, this code is not + * appropriate. + * + * Caveats: + * SHA-1 is designed to work with messages less than 2^64 bits + * long. Although SHA-1 allows a message digest to be generated + * for messages of any number of bits less than 2^64, this + * implementation only works with messages with a length that is + * a multiple of the size of an 8-bit character. + * + */ + +#include "sha1.h" + +/* + * Define the SHA1 circular left shift macro + */ +#define SHA1CircularShift(bits,word) \ + (((word) << (bits)) | ((word) >> (32-(bits)))) + +/* Local Function Prototyptes */ +void SHA1PadMessage(SHA1Context *); +void SHA1ProcessMessageBlock(SHA1Context *); + +/* + * SHA1Reset + * + * Description: + * This function will initialize the SHA1Context in preparation + * for computing a new SHA1 message digest. + * + * Parameters: + * context: [in/out] + * The context to reset. + * + * Returns: + * sha Error Code. + * + */ +int SHA1Reset(SHA1Context *context) +{ + if (!context) + { + return shaNull; + } + + context->Length_Low = 0; + context->Length_High = 0; + context->Message_Block_Index = 0; + + context->Intermediate_Hash[0] = 0x67452301; + context->Intermediate_Hash[1] = 0xEFCDAB89; + context->Intermediate_Hash[2] = 0x98BADCFE; + context->Intermediate_Hash[3] = 0x10325476; + context->Intermediate_Hash[4] = 0xC3D2E1F0; + + context->Computed = 0; + context->Corrupted = 0; + + return shaSuccess; +} + +/* + * SHA1Result + * + * Description: + * This function will return the 160-bit message digest into the + * Message_Digest array provided by the caller. + * NOTE: The first octet of hash is stored in the 0th element, + * the last octet of hash in the 19th element. + * + * Parameters: + * context: [in/out] + * The context to use to calculate the SHA-1 hash. + * Message_Digest: [out] + * Where the digest is returned. + * + * Returns: + * sha Error Code. + * + */ +int SHA1Result( SHA1Context *context, + uint8_t Message_Digest[SHA1HashSize]) +{ + int i; + + if (!context || !Message_Digest) + { + return shaNull; + } + + if (context->Corrupted) + { + return context->Corrupted; + } + + if (!context->Computed) + { + SHA1PadMessage(context); + for(i=0; i<64; ++i) + { + /* message may be sensitive, clear it out */ + context->Message_Block[i] = 0; + } + context->Length_Low = 0; /* and clear length */ + context->Length_High = 0; + context->Computed = 1; + + } + + for(i = 0; i < SHA1HashSize; ++i) + { + Message_Digest[i] = context->Intermediate_Hash[i>>2] + >> 8 * ( 3 - ( i & 0x03 ) ); + } + + return shaSuccess; +} + +/* + * SHA1Input + * + * Description: + * This function accepts an array of octets as the next portion + * of the message. + * + * Parameters: + * context: [in/out] + * The SHA context to update + * message_array: [in] + * An array of characters representing the next portion of + * the message. + * length: [in] + * The length of the message in message_array + * + * Returns: + * sha Error Code. + * + */ +int SHA1Input( SHA1Context *context, + const void *message_array0, + size_t length) +{ + const uint8_t *message_array = reinterpret_cast(message_array0); + if (!length) + { + return shaSuccess; + } + + if (!context || !message_array) + { + return shaNull; + } + + if (context->Computed) + { + context->Corrupted = shaStateError; + + return shaStateError; + } + + if (context->Corrupted) + { + return context->Corrupted; + } + while(length-- && !context->Corrupted) + { + context->Message_Block[context->Message_Block_Index++] = + (*message_array & 0xFF); + + context->Length_Low += 8; + if (context->Length_Low == 0) + { + context->Length_High++; + if (context->Length_High == 0) + { + /* Message is too long */ + context->Corrupted = 1; + } + } + + if (context->Message_Block_Index == 64) + { + SHA1ProcessMessageBlock(context); + } + + message_array++; + } + + return shaSuccess; +} + +/* + * SHA1ProcessMessageBlock + * + * Description: + * This function will process the next 512 bits of the message + * stored in the Message_Block array. + * + * Parameters: + * None. + * + * Returns: + * Nothing. + * + * Comments: + + * Many of the variable names in this code, especially the + * single character names, were used because those were the + * names used in the publication. + * + * + */ +void SHA1ProcessMessageBlock(SHA1Context *context) +{ + const uint32_t K[] = { /* Constants defined in SHA-1 */ + 0x5A827999, + 0x6ED9EBA1, + 0x8F1BBCDC, + 0xCA62C1D6 + }; + int t; /* Loop counter */ + uint32_t temp; /* Temporary word value */ + uint32_t W[80]; /* Word sequence */ + uint32_t A, B, C, D, E; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for(t = 0; t < 16; t++) + { + W[t] = context->Message_Block[t * 4] << 24; + W[t] |= context->Message_Block[t * 4 + 1] << 16; + W[t] |= context->Message_Block[t * 4 + 2] << 8; + W[t] |= context->Message_Block[t * 4 + 3]; + } + + for(t = 16; t < 80; t++) + { + W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + + A = context->Intermediate_Hash[0]; + B = context->Intermediate_Hash[1]; + C = context->Intermediate_Hash[2]; + D = context->Intermediate_Hash[3]; + E = context->Intermediate_Hash[4]; + + for(t = 0; t < 20; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + + B = A; + A = temp; + } + + for(t = 20; t < 40; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 40; t < 60; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 60; t < 80; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + context->Intermediate_Hash[0] += A; + context->Intermediate_Hash[1] += B; + context->Intermediate_Hash[2] += C; + context->Intermediate_Hash[3] += D; + context->Intermediate_Hash[4] += E; + + context->Message_Block_Index = 0; +} + +/* + * SHA1PadMessage + * + + * Description: + * According to the standard, the message must be padded to an even + * 512 bits. The first padding bit must be a '1'. The last 64 + * bits represent the length of the original message. All bits in + * between should be 0. This function will pad the message + * according to those rules by filling the Message_Block array + * accordingly. It will also call the ProcessMessageBlock function + * provided appropriately. When it returns, it can be assumed that + * the message digest has been computed. + * + * Parameters: + * context: [in/out] + * The context to pad + * ProcessMessageBlock: [in] + * The appropriate SHA*ProcessMessageBlock function + * Returns: + * Nothing. + * + */ + +void SHA1PadMessage(SHA1Context *context) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (context->Message_Block_Index > 55) + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 64) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + + SHA1ProcessMessageBlock(context); + + while(context->Message_Block_Index < 56) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + else + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 56) + { + + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + + /* + * Store the message length as the last 8 octets + */ + context->Message_Block[56] = context->Length_High >> 24; + context->Message_Block[57] = context->Length_High >> 16; + context->Message_Block[58] = context->Length_High >> 8; + context->Message_Block[59] = context->Length_High; + context->Message_Block[60] = context->Length_Low >> 24; + context->Message_Block[61] = context->Length_Low >> 16; + context->Message_Block[62] = context->Length_Low >> 8; + context->Message_Block[63] = context->Length_Low; + + SHA1ProcessMessageBlock(context); +} + +Csha1::Csha1() +{ + SHA1Reset(&m_context); +} + +Csha1::Csha1(data_ref s) +{ + SHA1Reset(&m_context); + write(s); +} + +void Csha1::read(void* d) +{ + SHA1Result(&m_context, reinterpret_cast(d)); +} + +std::string Csha1::read() +{ + char d[SHA1HashSize]; + read(d); + return std::string(d, SHA1HashSize); +} + +void Csha1::write(data_ref s) +{ + SHA1Input(&m_context, s.data(), s.size()); +} diff --git a/misc/.svn/text-base/sha1.h.svn-base b/misc/.svn/text-base/sha1.h.svn-base new file mode 100644 index 0000000..8a1ef4f --- /dev/null +++ b/misc/.svn/text-base/sha1.h.svn-base @@ -0,0 +1,79 @@ +/* + * sha1.h + * + * Description: + * This is the header file for code which implements the Secure + * Hashing Algorithm 1 as defined in FIPS PUB 180-1 published + * April 17, 1995. + * + * Many of the variable names in this code, especially the + * single character names, were used because those were the names + * used in the publication. + * + * Please read the file sha1.c for more information. + * + */ + +#ifndef _SHA1_H_ +#define _SHA1_H_ + +#include +#include +#include + +#ifndef _SHA_enum_ +#define _SHA_enum_ +enum +{ + shaSuccess = 0, + shaNull, /* Null pointer parameter */ + shaInputTooLong, /* input data too long */ + shaStateError /* called Input after Result */ +}; +#endif +#define SHA1HashSize 20 + +/* + * This structure will hold context information for the SHA-1 + * hashing operation + */ +typedef struct SHA1Context +{ + uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */ + + uint32_t Length_Low; /* Message length in bits */ + uint32_t Length_High; /* Message length in bits */ + + /* Index into message block array */ + int_least16_t Message_Block_Index; + uint8_t Message_Block[64]; /* 512-bit message blocks */ + + int Computed; /* Is the digest computed? */ + int Corrupted; /* Is the message digest corrupted? */ +} SHA1Context; + +/* + * Function Prototypes + */ + +int SHA1Reset( SHA1Context *); +int SHA1Input( SHA1Context *, + const void *, + size_t); +int SHA1Result( SHA1Context *, + uint8_t Message_Digest[SHA1HashSize]); + +class Csha1 +{ +public: + void read(void*); + std::string read(); + void write(data_ref); + Csha1(); + Csha1(data_ref); +private: + SHA1Context m_context; +}; + +#endif + diff --git a/misc/.svn/text-base/socket.cpp.svn-base b/misc/.svn/text-base/socket.cpp.svn-base new file mode 100644 index 0000000..1eb0509 --- /dev/null +++ b/misc/.svn/text-base/socket.cpp.svn-base @@ -0,0 +1,229 @@ +#include "socket.h" + +#include +#include + +#ifdef WIN32 +#pragma comment(lib, "ws2_32.lib") +#else +#include +#include +#include +#include +#include +#endif + +#ifndef INADDR_NONE +const int INADDR_NONE = -1; +#endif + +#ifndef MSG_NOSIGNAL +const int MSG_NOSIGNAL = 0; +#endif + +Csocket::Csocket(SOCKET s) +{ + if (s != INVALID_SOCKET) + m_source = std::make_shared(s); +} + +/* +int Csocket::accept(int& h, int& p) +{ + sockaddr_in a; + socklen_t cb_a = sizeof(sockaddr_in); + a.sin_family = AF_INET; + int r = ::accept(*this, reinterpret_cast(&a), &cb_a); + if (r == INVALID_SOCKET) + return r; + h = a.sin_addr.s_addr; + p = a.sin_port; + return r; +} +*/ + +int Csocket::bind(int h, int p) +{ + sockaddr_in a; + memset(&a, 0, sizeof(a)); + a.sin_family = AF_INET; + a.sin_addr.s_addr = h; + a.sin_port = p; + return ::bind(*this, reinterpret_cast(&a), sizeof(sockaddr_in)); +} + +int Csocket::blocking(bool v) +{ +#ifdef FIONBIO + unsigned long p = !v; + return ioctlsocket(*this, FIONBIO, &p); +#else + return fcntl(*this, F_SETFL, v ? 0 : O_NONBLOCK) == -1; +#endif +} + +void Csocket::close() +{ + m_source.reset(); +} + +int Csocket::connect(int h, int p) +{ + sockaddr_in a; + memset(&a, 0, sizeof(a)); + a.sin_family = AF_INET; + a.sin_addr.s_addr = h; + a.sin_port = p; + return ::connect(*this, reinterpret_cast(&a), sizeof(sockaddr_in)); +} + +int Csocket::listen() +{ + return ::listen(*this, SOMAXCONN); +} + +const Csocket& Csocket::open(int t, bool _blocking) +{ + start_up(); + *this = socket(AF_INET, t, 0); + if (*this != INVALID_SOCKET && !_blocking && blocking(false)) + close(); + return *this; +} + +int Csocket::recv(mutable_str_ref d) const +{ + return ::recv(*this, d.data(), d.size(), MSG_NOSIGNAL); +} + +int Csocket::recvfrom(mutable_str_ref d, sockaddr* a, socklen_t* cb_a) const +{ + return ::recvfrom(*this, d.data(), d.size(), MSG_NOSIGNAL, a, cb_a); +} + +int Csocket::send(str_ref s) const +{ + return ::send(*this, s.data(), s.size(), MSG_NOSIGNAL); +} + +int Csocket::sendto(str_ref s, const sockaddr* a, socklen_t cb_a) const +{ + return ::sendto(*this, s.data(), s.size(), MSG_NOSIGNAL, a, cb_a); +} + +int Csocket::getsockopt(int level, int name, void* v, socklen_t& cb_v) +{ + return ::getsockopt(*this, level, name, reinterpret_cast(v), &cb_v); +} + +int Csocket::getsockopt(int level, int name, int& v) +{ + socklen_t cb_v = sizeof(int); + return getsockopt(level, name, &v, cb_v); +} + +int Csocket::setsockopt(int level, int name, const void* v, int cb_v) +{ + return ::setsockopt(*this, level, name, reinterpret_cast(v), cb_v); +} + +int Csocket::setsockopt(int level, int name, int v) +{ + return setsockopt(level, name, &v, sizeof(int)); +} + +int Csocket::get_host(const std::string& name) +{ + hostent* e = gethostbyname(name.c_str()); + return e && e->h_addrtype == AF_INET && e->h_length == sizeof(in_addr) && e->h_addr_list ? *reinterpret_cast(*e->h_addr_list) : INADDR_NONE; +} + +std::string Csocket::error2a(int v) +{ + switch (v) + { + case WSAEACCES: return "EACCES"; + case WSAEADDRINUSE: return "EADDRINUSE"; + case WSAEADDRNOTAVAIL: return "EADDRNOTAVAIL"; + case WSAEAFNOSUPPORT: return "EAFNOSUPPORT"; + case WSAEALREADY: return "EALREADY"; + case WSAEBADF: return "EBADF"; + case WSAECONNABORTED: return "ECONNABORTED"; + case WSAECONNREFUSED: return "ECONNREFUSED"; + case WSAECONNRESET: return "ECONNRESET"; + case WSAEDESTADDRREQ: return "EDESTADDRREQ"; + case WSAEDQUOT: return "EDQUOT"; + case WSAEFAULT: return "EFAULT"; + case WSAEHOSTDOWN: return "EHOSTDOWN"; + case WSAEHOSTUNREACH: return "EHOSTUNREACH"; + case WSAEINPROGRESS: return "EINPROGRESS"; + case WSAEINTR: return "EINTR"; + case WSAEINVAL: return "EINVAL"; + case WSAEISCONN: return "EISCONN"; + case WSAELOOP: return "ELOOP"; + case WSAEMFILE: return "EMFILE"; + case WSAEMSGSIZE: return "EMSGSIZE"; + case WSAENAMETOOLONG: return "ENAMETOOLONG"; + case WSAENETDOWN: return "ENETDOWN"; + case WSAENETRESET: return "ENETRESET"; + case WSAENETUNREACH: return "ENETUNREACH"; + case WSAENOBUFS: return "ENOBUFS"; + case WSAENOPROTOOPT: return "ENOPROTOOPT"; + case WSAENOTCONN: return "ENOTCONN"; + case WSAENOTEMPTY: return "ENOTEMPTY"; + case WSAENOTSOCK: return "ENOTSOCK"; + case WSAEOPNOTSUPP: return "EOPNOTSUPP"; + case WSAEPFNOSUPPORT: return "EPFNOSUPPORT"; + case WSAEPROTONOSUPPORT: return "EPROTONOSUPPORT"; + case WSAEPROTOTYPE: return "EPROTOTYPE"; + case WSAEREMOTE: return "EREMOTE"; + case WSAESHUTDOWN: return "ESHUTDOWN"; + case WSAESOCKTNOSUPPORT: return "ESOCKTNOSUPPORT"; + case WSAESTALE: return "ESTALE"; + case WSAETIMEDOUT: return "ETIMEDOUT"; + case WSAETOOMANYREFS: return "ETOOMANYREFS"; + case WSAEUSERS: return "EUSERS"; + case WSAEWOULDBLOCK: return "EWOULDBLOCK"; +#ifdef WIN32 + case WSAECANCELLED: return "ECANCELLED"; + case WSAEDISCON: return "EDISCON"; + case WSAEINVALIDPROCTABLE: return "EINVALIDPROCTABLE"; + case WSAEINVALIDPROVIDER: return "EINVALIDPROVIDER"; + case WSAENOMORE: return "ENOMORE"; + case WSAEPROVIDERFAILEDINIT: return "EPROVIDERFAILEDINIT"; + case WSAEREFUSED: return "EREFUSED"; + case WSANOTINITIALISED: return "NOTINITIALISED"; + case WSASERVICE_NOT_FOUND: return "SERVICE_NOT_FOUND"; + case WSASYSCALLFAILURE: return "SYSCALLFAILURE"; + case WSASYSNOTREADY: return "SYSNOTREADY"; + case WSATYPE_NOT_FOUND: return "TYPE_NOT_FOUND"; + case WSAVERNOTSUPPORTED: return "VERNOTSUPPORTED"; + case WSA_E_CANCELLED: return "E_CANCELLED"; + case WSA_E_NO_MORE: return "E_NO_MORE"; +#endif + } + char b[12]; + sprintf(b, "%d", v); + return b; +} + +std::string Csocket::inet_ntoa(int v) +{ + in_addr a; + a.s_addr = v; + return ::inet_ntoa(a); +} + +int Csocket::start_up() +{ +#ifdef WIN32 + static bool done = false; + if (done) + return 0; + done = true; + WSADATA wsadata; + if (WSAStartup(MAKEWORD(2, 0), &wsadata)) + return 1; +#endif + return 0; +} diff --git a/misc/.svn/text-base/socket.h.svn-base b/misc/.svn/text-base/socket.h.svn-base new file mode 100644 index 0000000..dd917aa --- /dev/null +++ b/misc/.svn/text-base/socket.h.svn-base @@ -0,0 +1,124 @@ +#pragma once + +#include +#include +#include +#include + +#ifdef WIN32 +#include + +typedef int socklen_t; +#else +#include +#include +#include +#include +#include +#include + +#define closesocket close +#define ioctlsocket ioctl +#define WSAGetLastError() errno + +#define WSAEACCES EACCES +#define WSAEADDRINUSE EADDRINUSE +#define WSAEADDRNOTAVAIL EADDRNOTAVAIL +#define WSAEAFNOSUPPORT EAFNOSUPPORT +#define WSAEALREADY EALREADY +#define WSAEBADF EBADF +#define WSAECONNABORTED ECONNABORTED +#define WSAECONNREFUSED ECONNREFUSED +#define WSAECONNRESET ECONNRESET +#define WSAEDESTADDRREQ EDESTADDRREQ +#define WSAEDQUOT EDQUOT +#define WSAEFAULT EFAULT +#define WSAEHOSTDOWN EHOSTDOWN +#define WSAEHOSTUNREACH EHOSTUNREACH +#define WSAEINPROGRESS EINPROGRESS +#define WSAEINTR EINTR +#define WSAEINVAL EINVAL +#define WSAEISCONN EISCONN +#define WSAELOOP ELOOP +#define WSAEMFILE EMFILE +#define WSAEMSGSIZE EMSGSIZE +#define WSAENAMETOOLONG ENAMETOOLONG +#define WSAENETDOWN ENETDOWN +#define WSAENETRESET ENETRESET +#define WSAENETUNREACH ENETUNREACH +#define WSAENOBUFS ENOBUFS +#define WSAENOPROTOOPT ENOPROTOOPT +#define WSAENOTCONN ENOTCONN +#define WSAENOTEMPTY ENOTEMPTY +#define WSAENOTSOCK ENOTSOCK +#define WSAEOPNOTSUPP EOPNOTSUPP +#define WSAEPFNOSUPPORT EPFNOSUPPORT +#define WSAEPROTONOSUPPORT EPROTONOSUPPORT +#define WSAEPROTOTYPE EPROTOTYPE +#define WSAEREMOTE EREMOTE +#define WSAESHUTDOWN ESHUTDOWN +#define WSAESOCKTNOSUPPORT ESOCKTNOSUPPORT +#define WSAESTALE ESTALE +#define WSAETIMEDOUT ETIMEDOUT +#define WSAETOOMANYREFS ETOOMANYREFS +#define WSAEUSERS EUSERS +#define WSAEWOULDBLOCK EWOULDBLOCK + +typedef int SOCKET; + +const int INVALID_SOCKET = -1; +const int SOCKET_ERROR = -1; +#endif + +class Csocket_source : boost::noncopyable +{ +public: + Csocket_source(SOCKET s) + { + m_s = s; + } + + ~Csocket_source() + { + closesocket(m_s); + } + + operator SOCKET() const + { + return m_s; + } +private: + SOCKET m_s; +}; + +class Csocket +{ +public: + static std::string error2a(int v); + static int get_host(const std::string& name); + static std::string inet_ntoa(int h); + static int start_up(); + int accept(int& h, int& p); + int bind(int h, int p); + int blocking(bool v); + void close(); + int connect(int h, int p); + int getsockopt(int level, int name, void* v, socklen_t& cb_v); + int getsockopt(int level, int name, int& v); + int listen(); + const Csocket& open(int t, bool blocking = false); + int recv(mutable_str_ref) const; + int recvfrom(mutable_str_ref, sockaddr* a, socklen_t* cb_a) const; + int send(str_ref) const; + int sendto(str_ref, const sockaddr* a, socklen_t cb_a) const; + int setsockopt(int level, int name, const void* v, int cb_v); + int setsockopt(int level, int name, int v); + Csocket(SOCKET = INVALID_SOCKET); + + operator SOCKET() const + { + return m_source ? static_cast(*m_source) : INVALID_SOCKET; + } +private: + std::shared_ptr m_source; +}; diff --git a/misc/.svn/text-base/sql_query.cpp.svn-base b/misc/.svn/text-base/sql_query.cpp.svn-base new file mode 100644 index 0000000..a14fb36 --- /dev/null +++ b/misc/.svn/text-base/sql_query.cpp.svn-base @@ -0,0 +1,104 @@ +#include + +#include +#include +#include + +Csql_query::Csql_query(Cdatabase& database, std::string v) : + m_database(database), + m_in(std::move(v)) +{ +} + +Csql_result Csql_query::execute() const +{ + return m_database.query(read()); +} + +int Csql_query::execute_nothrow() const +{ + return m_database.query_nothrow(read()); +} + +std::string Csql_query::replace_names(const std::string& v) const +{ + std::string r; + for (size_t i = 0; ; ) + { + size_t j = v.find('@', i); + if (j == std::string::npos) + { + r.append(v, i, v.size() - i); + break; + } + r.append(v, i, j - i); + i = j + 1; + j = v.find_first_of(" ,", i); + if (j == std::string::npos) + j = v.size(); + r += m_database.name(v.substr(i, j - i)); + i = j; + } + return r; +} + +std::string Csql_query::read() const +{ + return m_out + replace_names(m_in); +} + +void Csql_query::operator=(std::string v) +{ + m_in = std::move(v); + m_out.clear(); +} + +void Csql_query::operator+=(const std::string& v) +{ + m_in += v; +} + +Csql_query& Csql_query::p_name(const std::string& v0) +{ + const std::string& v = m_database.name(v0); + std::vector r(2 * v.size() + 2); + r.resize(mysql_real_escape_string(m_database, &r[1], v.data(), v.size()) + 2); + r.front() = '`'; + r.back() = '`'; + p_raw(r); + return *this; +} + +Csql_query& Csql_query::p_raw(data_ref v) +{ + size_t i = m_in.find('?'); + assert(i != std::string::npos); + if (i == std::string::npos) + return *this; + m_out.append(replace_names(m_in.substr(0, i))); + m_in.erase(0, i + 1); + m_out.append(v.begin(), v.end()); + return *this; +} + +Csql_query& Csql_query::operator()(long long v) +{ + char b[21]; +#ifdef WIN32 + sprintf(b, "%I64d", v); +#else + sprintf(b, "%lld", v); +#endif + p_raw(data_ref(b)); + return *this; +} + +Csql_query& Csql_query::operator()(str_ref v) +{ + std::vector r(2 * v.size() + 2); + r.resize(mysql_real_escape_string(m_database, &r[1], v.data(), v.size()) + 2); + r.front() = '\''; + r.back() = '\''; + p_raw(r); + return *this; +} diff --git a/misc/.svn/text-base/stream_int.h.svn-base b/misc/.svn/text-base/stream_int.h.svn-base new file mode 100644 index 0000000..6169bc0 --- /dev/null +++ b/misc/.svn/text-base/stream_int.h.svn-base @@ -0,0 +1,83 @@ +#pragma once + +#include +#include + +inline float read_float(const void* r) +{ + float v; + memcpy(&v, r, sizeof(float)); + return v; +} + +inline float read_float(const void* r0, const void* /*s_end*/) +{ + return read_float(r0); +} + +template +static T write_float(T w0, float v) +{ + unsigned char* w = reinterpret_cast(w0); + memcpy(w, &v, sizeof(float)); + return w + sizeof(float); +} + +inline long long read_int(int cb, const void* r0) +{ + const unsigned char* r = reinterpret_cast(r0); + long long v = 0; + while (cb--) + v = v << 8 | *r++; + return v; +} + +inline long long read_int(size_t cb, data_ref s) +{ + return static_cast(s.size()) < cb ? 0 : read_int(cb, s.data()); +} + +inline long long read_int(int cb, const void* r, const void* s_end) +{ + return read_int(cb, data_ref(r, s_end)); +} + +template +T write_int(int cb, T w0, long long v) +{ + unsigned char* w = reinterpret_cast(w0); + w += cb; + for (int i = 0; i < cb; i++) + { + *--w = v & 0xff; + v >>= 8; + } + return reinterpret_cast(w + cb); +} + +inline long long read_int_le(int cb, const void* r0) +{ + const unsigned char* r = reinterpret_cast(r0); + r += cb; + long long v = 0; + while (cb--) + v = v << 8 | *--r; + return v; +} + +inline long long read_int_le(int cb, const void* r, const void* /*s_end*/) +{ + return read_int_le(cb, r); +} + +template +T write_int_le(int cb, T w0, long long v) +{ + unsigned char* w = reinterpret_cast(w0); + for (int i = 0; i < cb; i++) + { + *w++ = v & 0xff; + v >>= 8; + } + return reinterpret_cast(w); +} diff --git a/misc/.svn/text-base/stream_reader.cpp.svn-base b/misc/.svn/text-base/stream_reader.cpp.svn-base new file mode 100644 index 0000000..d0d6dfc --- /dev/null +++ b/misc/.svn/text-base/stream_reader.cpp.svn-base @@ -0,0 +1,3 @@ +#include "stdafx.h" +#include "stream_reader.h" + diff --git a/misc/.svn/text-base/stream_reader.h.svn-base b/misc/.svn/text-base/stream_reader.h.svn-base new file mode 100644 index 0000000..dd2e144 --- /dev/null +++ b/misc/.svn/text-base/stream_reader.h.svn-base @@ -0,0 +1,59 @@ +#pragma once + +#include +#include + +class Cstream_reader +{ +public: + const unsigned char* d() const + { + return m_d; + } + + const unsigned char* d_end() const + { + return m_d.end(); + } + + const unsigned char* r() const + { + return m_r; + } + + const unsigned char* read(int size) + { + m_r += size; + return m_r - size; + } + + long long read_int(int cb) + { + m_r += cb; + return ::read_int(cb, m_r - cb); + } + + Cvirtual_binary read_data() + { + int l = read_int(4); + return Cvirtual_binary(data_ref(read(l), l)); + } + + std::string read_string() + { + int l = read_int(4); + return std::string(reinterpret_cast(read(l)), l); + } + + Cstream_reader() + { + } + + Cstream_reader(const Cvirtual_binary& d) + { + m_r = m_d = d; + } +private: + Cvirtual_binary m_d; + const unsigned char* m_r; +}; diff --git a/misc/.svn/text-base/stream_writer.cpp.svn-base b/misc/.svn/text-base/stream_writer.cpp.svn-base new file mode 100644 index 0000000..496b9dc --- /dev/null +++ b/misc/.svn/text-base/stream_writer.cpp.svn-base @@ -0,0 +1,2 @@ +#include "stdafx.h" +#include "stream_writer.h" diff --git a/misc/.svn/text-base/stream_writer.h.svn-base b/misc/.svn/text-base/stream_writer.h.svn-base new file mode 100644 index 0000000..761c335 --- /dev/null +++ b/misc/.svn/text-base/stream_writer.h.svn-base @@ -0,0 +1,40 @@ +#pragma once + +#include + +class Cstream_writer +{ +public: + unsigned char* w() const + { + return m_w; + } + + unsigned char* write(int size) + { + m_w += size; + return m_w - size; + } + + void write_int(int cb, long long v) + { + m_w = ::write_int(cb, m_w, v); + } + + void write_data(data_ref v) + { + write_int(4, v.size()); + memcpy(write(v.size()), v); + } + + Cstream_writer() + { + } + + Cstream_writer(unsigned char* w) + { + m_w = w; + } +private: + unsigned char* m_w; +}; diff --git a/misc/.svn/text-base/tf_misc.cpp.svn-base b/misc/.svn/text-base/tf_misc.cpp.svn-base new file mode 100644 index 0000000..3e41a05 --- /dev/null +++ b/misc/.svn/text-base/tf_misc.cpp.svn-base @@ -0,0 +1,317 @@ +#include "tf_misc.h" + +#include +#include +#include + +static std::string web_encode(str_ref v) +{ + std::string d; + d.reserve(v.size() << 1); + while (v) + { + switch (v.front()) + { + case '"': + d += """; + break; + case '&': + d += "&"; + break; + case '<': + d += "<"; + break; + default: + d += v.front(); + } + v.pop_front(); + } + return d; +} + +static std::string web_link(str_ref title, str_ref link) +{ + return "" + web_encode(title.empty() ? link : title) + ""; +} + +std::string encode_field(str_ref v, bool add_br) +{ + std::string r; + r.reserve(v.size() << 1); + while (v) + { + if (boost::istarts_with(v, "ftp://") + || boost::istarts_with(v, "http://") + || boost::istarts_with(v, "https://") + || boost::istarts_with(v, "mailto:")) + { + size_t p = 0; + while (p < v.size() && !isspace(v[p] & 0xff) && v[p] != '\"' && v[p] != '<' && v[p] != '>' && v[p] != '[' && v[p] != ']') + p++; + if (v[p - 1] == '!' || v[p - 1] == ',' || v[p - 1] == '.' || v[p - 1] == '?') + p--; + if (v[p - 1] == ')') + p--; + str_ref url = v.substr(0, p); + if (boost::istarts_with(v, "ftp.")) + r += web_link(url, "ftp://" + url.s()); + else if (boost::istarts_with(v, "www.")) + r += web_link(url, "http://" + url.s()); + else + r += web_link(boost::istarts_with(v, "mailto:") ? url.substr(7) : url, url); + while (p--) + v.pop_front(); + } + else + { + switch (v.front()) + { + case '\n': + r += add_br ? "
" : " "; + break; + case '\r': + break; + case '&': + r += "&"; + break; + case '<': + r += "<"; + break; + default: + r += v.front(); + } + v.pop_front(); + } + } + return r; +} + +std::string encode_text(str_ref v, bool add_quote_class) +{ + std::string r; + r.reserve(v.size() << 1); + while (v) + { + str_ref line = read_until(v, '\n'); + r += add_quote_class && boost::istarts_with(line, "> ") ? "" + encode_field(line) + "" : encode_field(line); + r += "
"; + } + return r; +} + +std::string trim_field(const std::string& v) +{ + return boost::find_format_all_copy(boost::trim_copy(v), boost::token_finder(boost::is_space(), boost::token_compress_on), boost::const_formatter(" ")); +} + +std::string trim_text(const std::string& v) +{ + std::string r; + bool copy_white = false; + for (size_t i = 0; i < v.size(); ) + { + size_t p = v.find('\n', i); + if (p == std::string::npos) + p = v.size(); + std::string line = trim_field(v.substr(i, p - i)); + if (line.empty()) + copy_white = true; + else + { + if (copy_white) + { + if (!r.empty()) + r += '\n'; + copy_white = false; + } + r += line + '\n'; + } + i = p + 1; + } + return r; +} + +enum bb_t +{ + bb_literal, + bb_none, + bb_bold, + bb_bold_close, + bb_center, + bb_center_close, + bb_color, + bb_color_close, + bb_quote, + bb_quote_close, + bb_strike, + bb_strike_close, + bb_underline, + bb_underline_close, + bb_url, + bb_unknown, + bb_video, + bb_end, +}; + +static bool operator==(str_ref a, const char* b) +{ + return a.size() == strlen(b) && !memcmp(a.data(), b, a.size()); +} + +bb_t get_next(str_ref& s, str_ref& a0) +{ + if (!s) + return bb_end; + if (s.front() != '[') + { + auto a = std::find(s.begin(), s.end(), '['); + if (a == s.end()) + { + a0 = s; + s.clear(); + } + else + { + a0 = str_ref(s.begin(), a); + s.set_begin(a); + } + return bb_literal; + } + auto a = std::find(s.begin(), s.end(), ']'); + if (a == s.end()) + { + a0 = s; + s.clear(); + return bb_literal; + } + str_ref tag = { &s[1], a }; + s.set_begin(a + 1); + a0.clear(); + if (tag == "b") + return bb_bold; + if (tag == "/b") + return bb_bold_close; + if (tag == "center") + return bb_center; + if (tag == "/center") + return bb_center_close; + if (boost::starts_with(tag, "color=")) + { + a0 = tag.substr(6); + return bb_color; + } + if (tag == "/color") + return bb_color_close; + if (boost::starts_with(tag, "font=") || tag == "/font") + return bb_none; + if (tag == "i" || tag == "/i") + return bb_none; + if (tag == "img" || tag == "IMG" || tag == "/img" || tag == "/IMG") + return bb_none; + if (boost::starts_with(tag, "img=")) + { + a0 = tag.substr(4); + return bb_literal; + } + if (tag == "q" || tag == "quote") + return bb_quote; + if (boost::starts_with(tag, "quote=")) + { + a0 = tag.substr(6); + return bb_quote; + } + if (tag == "/q" || tag == "/quote") + return bb_quote_close; + if (tag == "s") + return bb_strike; + if (tag == "/s") + return bb_strike_close; + if (boost::starts_with(tag, "size=") || tag == "/size") + return bb_none; + if (tag == "u") + return bb_underline; + if (tag == "/u") + return bb_underline_close; + if (boost::starts_with(tag, "url=")) + { + a0 = tag.substr(4); + return bb_url; + } + if (tag == "/url") + return bb_none; + if (boost::starts_with(tag, "video=")) + { + a0 = tag.substr(6); + return bb_video; + } + a0 = tag; + return bb_unknown; +} + +std::string bbformat(str_ref s) +{ + std::string d; + str_ref a0; + while (1) + { + switch (get_next(s, a0)) + { + case bb_literal: + d += encode_field(a0, true); + break; + case bb_none: + break; + case bb_bold: + d += ""; + break; + case bb_bold_close: + d += ""; + break; + case bb_center: + d += "
"; + break; + case bb_center_close: + d += "
"; + break; + case bb_color: + d += ""; // escape a0 + break; + case bb_color_close: + d += ""; + break; + case bb_quote: + d += "
"; + if (a0) + d += "" + encode_field(a0) + " wrote:"; + break; + case bb_quote_close: + d += "
"; + break; + case bb_strike: + d += ""; + break; + case bb_strike_close: + d += ""; + break; + case bb_underline: + d += ""; + break; + case bb_underline_close: + d += ""; + break; + case bb_url: + d += encode_field(a0, true) + " "; + break; + case bb_video: + d += encode_field(a0, true) + " "; + break; + case bb_unknown: + d += "[" + encode_field(a0) + "]"; + break; + case bb_end: + return d; + default: + assert(false); + } + } +} diff --git a/misc/.svn/text-base/tf_misc.h.svn-base b/misc/.svn/text-base/tf_misc.h.svn-base new file mode 100644 index 0000000..d260227 --- /dev/null +++ b/misc/.svn/text-base/tf_misc.h.svn-base @@ -0,0 +1,10 @@ +#pragma once + +#include +#include + +std::string bbformat(str_ref); +std::string encode_field(str_ref, bool add_br = false); +std::string encode_text(str_ref, bool add_quote_class); +std::string trim_field(const std::string&); +std::string trim_text(const std::string&); diff --git a/misc/.svn/text-base/virtual_binary.cpp.svn-base b/misc/.svn/text-base/virtual_binary.cpp.svn-base new file mode 100644 index 0000000..faf47d8 --- /dev/null +++ b/misc/.svn/text-base/virtual_binary.cpp.svn-base @@ -0,0 +1,24 @@ +#include "xbt/virtual_binary.h" + +#include +#include +#include + +void Cvirtual_binary::assign(data_ref v) +{ + if (v.size()) + { + m_source = boost::make_shared(v.size()); + if (v.begin()) + memcpy(data_edit(), v.data(), v.size()); + } + else + m_source.reset(); +} + +unsigned char* Cvirtual_binary::write_start(size_t cb_d) +{ + if (size() != cb_d) + assign(cb_d); + return data_edit(); +} diff --git a/misc/.svn/text-base/xcc_z.cpp.svn-base b/misc/.svn/text-base/xcc_z.cpp.svn-base new file mode 100644 index 0000000..13e7481 --- /dev/null +++ b/misc/.svn/text-base/xcc_z.cpp.svn-base @@ -0,0 +1,70 @@ +#include "xbt/xcc_z.h" + +#include +#include +#include +#include "stream_int.h" + +shared_data xcc_z::gunzip(data_ref s) +{ + if (s.size() < 18) + return shared_data(); + shared_data d(read_int_le(4, s.end() - 4)); + z_stream stream; + stream.zalloc = NULL; + stream.zfree = NULL; + stream.opaque = NULL; + stream.next_in = const_cast(s.begin()) + 10; + stream.avail_in = s.size() - 18; + stream.next_out = d.data(); + stream.avail_out = d.size(); + return stream.next_out + && Z_OK == inflateInit2(&stream, -MAX_WBITS) + && Z_STREAM_END == inflate(&stream, Z_FINISH) + && Z_OK == inflateEnd(&stream) + ? d + : shared_data(); +} + +shared_data xcc_z::gzip(data_ref s) +{ + unsigned long cb_d = s.size() + (s.size() + 999) / 1000 + 12; + shared_data d(10 + cb_d + 8); + unsigned char* w = d.data(); + *w++ = 0x1f; + *w++ = 0x8b; + *w++ = Z_DEFLATED; + *w++ = 0; + *w++ = 0; + *w++ = 0; + *w++ = 0; + *w++ = 0; + *w++ = 0; + *w++ = 3; + { + z_stream stream; + stream.zalloc = NULL; + stream.zfree = NULL; + stream.opaque = NULL; + deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); + stream.next_in = const_cast(s.begin()); + stream.avail_in = s.size(); + stream.next_out = w; + stream.avail_out = cb_d; + deflate(&stream, Z_FINISH); + deflateEnd(&stream); + w = stream.next_out; + } + w = write_int_le(4, w, crc32(crc32(0, NULL, 0), s.data(), s.size())); + w = write_int_le(4, w, s.size()); + return d.substr(0, w - d.data()); +} + +/* +void xcc_z::gzip_out(data_ref s) +{ + gzFile f = gzdopen(fileno(stdout), "wb"); + gzwrite(f, s.data(), s.size()); + gzflush(f, Z_FINISH); +} +*/ diff --git a/misc/.svn/text-base/xif_key.cpp.svn-base b/misc/.svn/text-base/xif_key.cpp.svn-base new file mode 100644 index 0000000..2453213 --- /dev/null +++ b/misc/.svn/text-base/xif_key.cpp.svn-base @@ -0,0 +1,149 @@ +#include "stdafx.h" +#include "xif_key.h" + +#include +#include "stream_int.h" + +static int read_int(const byte*& r) +{ + r += 4; + return read_int_le(4, r - 4); +} + +void Cxif_key::load_old(const byte*& data) +{ + for (int count = read_int(data); count--; ) + { + Cxif_key& i = set_key(read_int(data)); + i.load_old(data); + } + for (int count = read_int(data); count--; ) + { + Cxif_value& i = set_value(read_int(data)); + i.load_old(data); + } +} + +void Cxif_key::load_new(const byte*& data) +{ + for (int count = read_int(data), id = 0; count--; ) + { + id += read_int(data); + open_key_write(id).load_new(data); + } + for (int count = read_int(data), id = 0; count--; ) + { + id += read_int(data); + open_value_write(id).load_new(data); + } +} + +int Cxif_key::get_size() const +{ + int size = 8; + BOOST_FOREACH(t_xif_key_map::const_reference i, m_keys) + size += 4 + i.second.get_size(); + BOOST_FOREACH(t_xif_value_map::const_reference i, m_values) + { + size += 9; + switch (i.second.get_type()) + { + case vt_bin32: + case vt_int32: + break; + default: + size += i.second.get_size(); + } + } + return size; +} + +void Cxif_key::save(byte*& data) const +{ + { + data = write_int_le(4, data, m_keys.size()); + int id = 0; + BOOST_FOREACH(t_xif_key_map::const_reference i, m_keys) + { + data = write_int_le(4, data, i.first - id); + id = i.first; + i.second.save(data); + } + } + { + data = write_int_le(4, data, m_values.size()); + int id = 0; + BOOST_FOREACH(t_xif_value_map::const_reference i, m_values) + { + data = write_int_le(4, data, i.first - id); + id = i.first; + i.second.save(data); + } + } +} + +int Cxif_key::load_key(const byte* data, size_t size) +{ + const byte* read_p = data; + const t_xif_header_fast& header = *reinterpret_cast(read_p); + if (size < sizeof(t_xif_header_old) + || header.id != file_id + || header.version != file_version_old && header.version != file_version_new && header.version != file_version_fast) + return 1; + int error = 0; + if (header.version == file_version_old) + { + read_p += sizeof(t_xif_header_old) - 4; + load_old(read_p); + error = size != read_p - data; + } + else + { + unsigned long cb_d = header.size_uncompressed; + if (cb_d) + { + shared_data d(cb_d); + if (header.version == file_version_new) + error = Z_OK != uncompress(d.data(), &cb_d, data + sizeof(t_xif_header_old), size - sizeof(t_xif_header_old)); + else + error = Z_OK != uncompress(d.data(), &cb_d, data + sizeof(t_xif_header_fast), header.size_compressed); + if (!error) + { + read_p = d.data(); + load_new(read_p); + error = read_p != d.end(); + if (header.version == file_version_fast && !error) + { + read_p = data + sizeof(t_xif_header_fast) + header.size_compressed; + error = size != read_p - data; + } + } + } + else + { + read_p = data + (header.version == file_version_fast ? sizeof(t_xif_header_fast) : sizeof(t_xif_header_old)); + load_new(read_p); + error = size != read_p - data; + } + } + return error; +} + +shared_data Cxif_key::vdata() const +{ + int size = get_size(); + shared_data s(size); + byte* w = s.data(); + save(w); + unsigned long cb_d = s.size() + (s.size() + 999) / 1000 + 12; + shared_data d(sizeof(t_xif_header_fast) + cb_d); + t_xif_header_fast& header = *reinterpret_cast(d.data()); + compress(d.data() + sizeof(t_xif_header_fast), &cb_d, s.data(), s.size()); + w = d.data() + sizeof(t_xif_header_fast) + cb_d; + header.id = file_id; + header.version = file_version_fast; + header.size_uncompressed = size; + header.size_compressed = cb_d; + header.size_external = 0; + return d.substr(0, sizeof(t_xif_header_fast) + cb_d); +} diff --git a/misc/.svn/text-base/xif_key_r.cpp.svn-base b/misc/.svn/text-base/xif_key_r.cpp.svn-base new file mode 100644 index 0000000..960938c --- /dev/null +++ b/misc/.svn/text-base/xif_key_r.cpp.svn-base @@ -0,0 +1,79 @@ +#include "stdafx.h" +#include "xbt/xif_key_r.h" + +#include +#include +#include +#include + +static int read_int(const byte*& r) +{ + r += 4; + return read_int_le(4, r - 4); +} + +int Cxif_key_r::import(data_ref s) +{ + const t_xif_header_fast& h = *reinterpret_cast(s.data()); + if (s.size() < sizeof(t_xif_header_fast) + 8 + || h.id != file_id + || h.version != file_version_fast) + return 1; + unsigned long cb_d = h.size_uncompressed; + if (cb_d) + { + shared_data d(cb_d); + if (Z_OK != uncompress(d.data(), &cb_d, &s[sizeof(t_xif_header_fast)], h.size_compressed)) + return 1; + load(d.data()); + } + else + { + load(&s[sizeof(t_xif_header_fast)]); + } + return 0; +} + +int Cxif_key_r::load(const byte* s) +{ + const byte* r = s; + { + int count = read_int(r); + int id = 0; + m_keys.reserve(count); + while (count--) + { + id += read_int(r); + m_keys.push_back(std::make_pair(id, Cxif_key_r())); + r += m_keys.rbegin()->second.load(r); + } + } + { + int count = read_int(r); + int id = 0; + m_values.reserve(count); + while (count--) + { + id += read_int(r); + m_values.push_back(std::make_pair(id, Cxif_value())); + m_values.rbegin()->second.load_new(r); + } + } + return r - s; +} + +const Cxif_key_r* Cxif_key_r::find_key(int id) const +{ + t_key_map::const_iterator i = keys().begin(); + while (i != keys().end() && i->first != id) + i++; + return i == keys().end() ? NULL : &i->second; +} + +const Cxif_value* Cxif_key_r::find_value(int id) const +{ + t_value_map::const_iterator i = values().begin(); + while (i != values().end() && i->first != id) + i++; + return i == values().end() ? NULL : &i->second; +} diff --git a/misc/.svn/text-base/xif_value.cpp.svn-base b/misc/.svn/text-base/xif_value.cpp.svn-base new file mode 100644 index 0000000..264a0e9 --- /dev/null +++ b/misc/.svn/text-base/xif_value.cpp.svn-base @@ -0,0 +1,127 @@ +#include "stdafx.h" +#include "xbt/xif_value.h" + +#include +#include "stream_int.h" + +static float read_float(const byte*& r) +{ + assert(sizeof(float) == 4); + float v; + memcpy(&v, r, 4); + r += 4; + return v; +} + +static int read_int(const byte*& r) +{ + r += 4; + return read_int_le(4, r - 4); +} + +t_vt Cxif_value::get_type() const +{ + if (m_type != vt_unknown) + return m_type; + const byte* data = m_data.data(); + if (!data) + return vt_binary; + int size = m_data.size(); + if (!data[size - 1]) + { + const byte* r = data; + int c = size - 1; + while (c--) + { + if (*r != 9 && *r < 0x20) + break; + r++; + } + if (c == -1) + return vt_string; + } + if (size == 4) + return vt_int32; + return vt_binary; +} + +void Cxif_value::load_old(const byte*& data) +{ + m_data.clear(); + int size = read_int(data); + if (size == 4) + memcpy(m_value, data, size); + m_data = make_shared_data(data, size); + data += size; + m_type = vt_unknown; + m_type = get_type(); +} + +void Cxif_value::load_new(const byte*& data) +{ + m_data.clear(); + m_type = static_cast(*data++); + switch (m_type) + { + case vt_bin32: + case vt_int32: + m_value_int = read_int(data); + break; + case vt_float: + m_value_float = read_float(data); + break; + case vt_external_binary: + m_data = shared_data(read_int(data)); + break; + default: + { + int size = read_int(data); + m_data = make_shared_data(data, size); + data += size; + } + } +} + +int Cxif_value::skip(const byte* s) +{ + const byte* r = s; + t_vt type = static_cast(*r++); + switch (type) + { + case vt_bin32: + case vt_int32: + read_int(r); + break; + case vt_float: + read_float(r); + break; + case vt_external_binary: + read_int(r); + break; + default: + r += read_int(r); + } + return r - s; +} + +void Cxif_value::save(byte*& data) const +{ + *data++ = m_type; + switch (m_type) + { + case vt_bin32: + case vt_int32: + data = write_int_le(4, data, get_int()); + break; + case vt_float: + data = write_float(data, get_float()); + break; + default: + { + int size = get_size(); + data = write_int_le(4, data, size); + memcpy(data, get_data(), size); + data += size; + } + } +} diff --git a/misc/CMakeLists.txt b/misc/CMakeLists.txt new file mode 100644 index 0000000..95f5bb6 --- /dev/null +++ b/misc/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 2.4) +set(CMAKE_BUILD_TYPE release) +set(CMAKE_CXX_FLAGS -std=c++11) +include_directories(.) +add_library( + xbt + bt_misc.cpp + database.cpp + sql_query.cpp + xcc_z.cpp +) +target_link_libraries(xbt mysqlclient z) +install(TARGETS xbt DESTINATION lib) diff --git a/misc/alerts.cpp b/misc/alerts.cpp new file mode 100644 index 0000000..89ff7ba --- /dev/null +++ b/misc/alerts.cpp @@ -0,0 +1,15 @@ +#include "stdafx.h" +#include "alerts.h" + +int Calert::pre_dump() const +{ + return m_message.size() + m_source.size() + 16; +} + +void Calert::dump(Cstream_writer& w) const +{ + w.write_int(4, m_time); + w.write_int(4, m_level); + w.write_data(m_message); + w.write_data(m_source); +} diff --git a/misc/alerts.h b/misc/alerts.h new file mode 100644 index 0000000..a97f175 --- /dev/null +++ b/misc/alerts.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include "stream_writer.h" + +class Calert +{ +public: + enum t_level + { + emerg, + alert, + crit, + error, + warn, + notice, + info, + debug, + }; + + time_t time() const + { + return m_time; + } + + t_level level() const + { + return m_level; + } + + const std::string& message() const + { + return m_message; + } + + void message(const std::string& v) + { + m_message = v; + } + + Calert(t_level level, const std::string& message) + { + m_time = ::time(NULL); + m_level = level; + m_message = message; + } + + Calert(t_level level, const std::string& source, const std::string& message) + { + m_time = ::time(NULL); + m_level = level; + m_message = message; + m_source = source; + } + + int pre_dump() const; + void dump(Cstream_writer&) const; +private: + time_t m_time; + t_level m_level; + std::string m_message; + std::string m_source; +}; + +class Calerts: public std::list +{ +public: + void push_back(const value_type& v) + { + std::list::push_back(v); + while (size() > 250) + erase(begin()); + } +}; diff --git a/misc/bt_misc.cpp b/misc/bt_misc.cpp new file mode 100644 index 0000000..cb3e288 --- /dev/null +++ b/misc/bt_misc.cpp @@ -0,0 +1,408 @@ +#include "xbt/bt_misc.h" + +#include +#include +#include +#include +#include +#include + +#ifdef WIN32 +#pragma comment(lib, "ws2_32") +#else +#include +#endif + +std::string escape_string(const std::string& v) +{ + std::string w; + w.reserve(v.length()); + for (char i : v) + { + if (isgraph(i & 0xff)) + w += i; + else + { + switch (i) + { + case '\0': + w += "\\0"; + break; + default: + w += "\\x" + hex_encode(2, i); + } + } + } + return w; +} + +std::string generate_random_string(int l) +{ + std::string v; + while (l--) + v += "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"[rand() % 62]; + return v; +} + +std::string get_env(const std::string& v) +{ + const char* p = getenv(v.c_str()); + return p ? p : ""; +} + +static int hex_decode(char v) +{ + if (v >= '0' && v <= '9') + return v - '0'; + if (v >= 'A' && v <= 'F') + return v - 'A' + 10; + if (v >= 'a' && v <= 'f') + return v - 'a' + 10; + return -1; +} + +std::string hex_decode(str_ref v) +{ + std::string r; + r.resize(v.size() >> 1); + for (size_t i = 0; i + 2 <= v.size(); i += 2) + { + int a = hex_decode(v[i]); + r[i >> 1] = a << 4 | hex_decode(v[i + 1]); + } + return r; +} + +std::string hex_encode(int l, int v) +{ + std::string r; + r.resize(l); + while (l--) + { + r[l] = "0123456789abcdef"[v & 0xf]; + v >>= 4; + } + return r; +} + +std::string n(long long v) +{ + return std::to_string(v); +} + +std::string hex_encode(data_ref v) +{ + std::string r; + r.reserve(v.size() << 1); + for (int i : v) + r += hex_encode(2, i); + return r; +} + +std::string js_encode(str_ref v) +{ + std::string r; + for (int i : v) + { + switch (i) + { + case '\"': + case '\'': + case '\\': + r += '\\'; + default: + r += i; + } + } + return r; +} + +std::string uri_decode(str_ref v) +{ + std::string r; + r.reserve(v.size()); + for (size_t i = 0; i < v.size(); i++) + { + char c = v[i]; + switch (c) + { + case '%': + { + if (i + 2 >= v.size()) + return std::string(); + int l = v[++i]; + r += hex_decode(l) << 4 | hex_decode(v[++i]); + break; + } + case '+': + r += ' '; + break; + default: + r += c; + } + } + return r; +} + +std::string uri_encode(str_ref v) +{ + std::string r; + r.reserve(v.size()); + for (char c : v) + { + if (isalpha(c & 0xff) || isdigit(c & 0xff)) + r += c; + else + { + switch (c) + { + case ' ': + r += '+'; + break; + case '-': + case ',': + case '.': + case '@': + case '_': + r += c; + break; + default: + r += "%" + hex_encode(2, c); + } + } + } + return r; +} + +bool is_private_ipa(int a) +{ + return (ntohl(a) & 0xff000000) == 0x0a000000 + || (ntohl(a) & 0xff000000) == 0x7f000000 + || (ntohl(a) & 0xfff00000) == 0xac100000 + || (ntohl(a) & 0xffff0000) == 0xc0a80000; +} + +std::string b2a(long long v, const char* postfix) +{ + char d[32]; + char* w = d; + if (v < 0) + { + v = -v; + *w++ = '-'; + } + int l = 0; + for (; v > 999999; l++) + v >>= 10; + if (v > 999) + { + l++; + int b = static_cast((v & 0x3ff) * 100 >> 10); + v >>= 10; + w += sprintf(w, "%d", static_cast(v)); + if (v < 10 && b % 10) + w += sprintf(w, ".%02d", b); + else if (v < 100 && b > 9) + w += sprintf(w, ".%d", b / 10); + } + else + w += sprintf(w, "%d", static_cast(v)); + const char* a[] = {"", " k", " m", " g", " t", " p", " e", " z", " y"}; + w += sprintf(w, "%s", a[l]); + if (postfix) + w += sprintf(w, "%s%s", l ? "" : " ", postfix); + return d; +} + +std::string n2a(long long v, const char* postfix) +{ + char d[32]; + char* w = d; + if (v < 0) + { + v = -v; + *w++ = '-'; + } + int l = 0; + for (; v > 999999; l++) + v /= 1000; + if (v > 999) + { + l++; + int b = static_cast(v % 1000 / 10); + v /= 1000; + w += sprintf(w, "%d", static_cast(v)); + if (v < 10 && b % 10) + w += sprintf(w, ".%02d", b); + else if (v < 100 && b > 9) + w += sprintf(w, ".%d", b / 10); + } + else + w += sprintf(w, "%d", static_cast(v)); + const char* a [] = { "", " k", " m", " g", " t", " p", " e", " z", " y" }; + w += sprintf(w, "%s", a[l]); + if (postfix) + w += sprintf(w, "%s%s", l ? "" : " ", postfix); + return d; +} + +static std::string peer_id2a(const std::string& name, const std::string& peer_id, int i) +{ + for (size_t j = i; j < peer_id.size(); j++) + { + if (!isalnum(peer_id[j])) + return name + peer_id.substr(i, j - i); + } + return name + peer_id.substr(i); +} + +std::string peer_id2a(const std::string& v) +{ + if (v.length() != 20) + return std::string(); + if (v[7] == '-') + { + switch (v[0]) + { + case '-': + if (v[1] == 'A' && v[2] == 'Z') + return peer_id2a("Azureus ", v, 3); + if (v[1] == 'B' && v[2] == 'C') + return peer_id2a("BitComet ", v, 3); + if (v[1] == 'U' && v[2] == 'T') + return peer_id2a("uTorrent ", v, 3); + if (v[1] == 'T' && v[2] == 'S') + return peer_id2a("TorrentStorm ", v, 3); + break; + case 'A': + return peer_id2a("ABC ", v, 1); + case 'M': + return peer_id2a("Mainline ", v, 1); + case 'S': + return peer_id2a("Shadow ", v, 1); + case 'T': + return peer_id2a("BitTornado ", v, 1); + case 'X': + if (v[1] == 'B' && v[2] == 'T') + return peer_id2a("XBT Client ", v, 3) + (v.find_first_not_of("0123456789ABCDEFGHIJKLMNOPQRSTUVWYXZabcdefghijklmnopqrstuvwyxz", 8) == std::string::npos ? "" : " (fake)"); + break; + } + } + switch (v[0]) + { + case '-': + if (v[1] == 'G' && v[2] == '3') + return "G3"; + break; + case 'S': + if (v[1] == 5 && v[2] == 7 && v[3] >= 0 && v[3] < 10) + return "Shadow 57" + n(v[3]); + break; + case 'e': + if (v[1] == 'x' && v[2] == 'b' && v[3] == 'c' && v[4] >= 0 && v[4] < 10 && v[5] >= 0 && v[5] < 100) + return "BitComet " + n(v[4]) + '.' + n(v[5] / 10) + n(v[5] % 10); + } + return "Unknown"; +} + +std::string duration2a(float v) +{ + char d[32]; + if (v > 31557600) + sprintf(d, "%.1f years", v / 31557600); + else if (v > 2629800) + sprintf(d, "%.1f months", v / 2629800); + else if (v > 604800) + sprintf(d, "%.1f weeks", v / 604800); + else if (v > 86400) + sprintf(d, "%.1f days", v / 86400); + else if (v > 3600) + sprintf(d, "%.1f hours", v / 3600); + else if (v > 60) + sprintf(d, "%.1f minutes", v / 60); + else + sprintf(d, "%.1f seconds", v); + return d; +} + +std::string time2a(time_t v) +{ + const tm* date = localtime(&v); + if (!date) + return std::string(); + char b[20]; + sprintf(b, "%04d-%02d-%02d %02d:%02d:%02d", date->tm_year + 1900, date->tm_mon + 1, date->tm_mday, date->tm_hour, date->tm_min, date->tm_sec); + return b; +} + +int merkle_tree_size(int v) +{ + int r = 0; + while (v > 1) + { + r += v++; + v >>= 1; + } + if (v == 1) + r++; + return r; +} + +std::string backward_slashes(std::string v) +{ + std::replace(v.begin(), v.end(), '/', '\\'); + return v; +} + +std::string forward_slashes(std::string v) +{ + std::replace(v.begin(), v.end(), '\\', '/'); + return v; +} + +std::string native_slashes(const std::string& v) +{ +#ifdef WIN32 + return backward_slashes(v); +#else + return forward_slashes(v); +#endif +} + +int hms2i(int h, int m, int s) +{ + return 60 * (h + 60 * m) + s; +} + +std::string xbt_version2a(int v) +{ + return n(v / 100) + "." + n(v / 10 % 10) + "." + n(v % 10); +} + +std::string mk_sname(std::string v) +{ + boost::erase_all(v, "-"); + boost::erase_all(v, "@"); + std::replace(v.begin(), v.end(), '0', 'o'); + std::replace(v.begin(), v.end(), '1', 'i'); + std::replace(v.begin(), v.end(), '3', 'e'); + std::replace(v.begin(), v.end(), '4', 'a'); + std::replace(v.begin(), v.end(), 'l', 'i'); + for (size_t i = 1; i < v.size(); ) + { + if (v[i] == v[i - 1]) + v.erase(i, 1); + else + i++; + } + return v; +} + +void xbt_syslog(const std::string& v) +{ +#ifdef WIN32 + std::cerr << v << std::endl; +#else + syslog(LOG_ERR, "%s", v.c_str()); +#endif +} diff --git a/misc/bt_strings.h b/misc/bt_strings.h new file mode 100644 index 0000000..af62954 --- /dev/null +++ b/misc/bt_strings.h @@ -0,0 +1,113 @@ +#pragma once + +enum +{ + bti_choke , + bti_unchoke, + bti_interested, + bti_uninterested, + bti_have, + bti_bitfield, + bti_request, + bti_piece, + bti_cancel, + + bti_get_info, + bti_info, + bti_get_peers, + bti_peers, + + bti_extended = 20, + + bti_bvalue = 0x40, +}; + +enum +{ + bti_extended_handshake, + bti_extended_ut_pex, +}; + +enum +{ + bti_none, + bti_completed, + bti_started, + bti_stopped, +}; + +const std::string bts_action = "action"; +const std::string bts_admin_port = "admin port"; +const std::string bts_admin_user = "admin user"; +const std::string bts_admin_pass = "admin pass"; +const std::string bts_announce = "announce"; +const std::string bts_announce_list = "announce-list"; +const std::string bts_banned_client = "access denied, banned client"; +const std::string bts_can_not_leech = "access denied, leeching forbidden, you are only allowed to seed"; +const std::string bts_close_torrent = "close torrent"; +const std::string bts_complete = "complete"; +const std::string bts_complete_total = "complete total"; +const std::string bts_completed_at = "completed at"; +const std::string bts_completes_dir = "completes dir"; +const std::string bts_down_rate = "down rate"; +const std::string bts_downloaded = "downloaded"; +const std::string bts_erase_torrent = "erase torrent"; +const std::string bts_events = "events"; +const std::string bts_failure_reason = "failure reason"; +const std::string bts_files = "files"; +const std::string bts_flags = "flags"; +const std::string bts_get_options = "get options"; +const std::string bts_get_status = "get status"; +const std::string bts_hash = "hash"; +const std::string bts_incomplete = "incomplete"; +const std::string bts_incomplete_total = "incomplete total"; +const std::string bts_incompletes_dir = "incompletes dir"; +const std::string bts_info = "info"; +const std::string bts_interval = "interval"; +const std::string bts_ipa = "ip"; +const std::string bts_left = "left"; +const std::string bts_length = "length"; +const std::string bts_login = "login"; +const std::string bts_merkle_hash = "merkle hash"; +const std::string bts_message = "message"; +const std::string bts_min_interval = "min interval"; +const std::string bts_min_request_interval = "min_request_interval"; +const std::string bts_name = "name"; +const std::string bts_open_torrent = "open torrent"; +const std::string bts_pass = "pass"; +const std::string bts_path = "path"; +const std::string bts_peer_id = "peer id"; +const std::string bts_peer_limit = "peer limit"; +const std::string bts_peer_port = "peer port"; +const std::string bts_peers = "peers"; +const std::string bts_peers_limit_reached = "access denied, peers limit reached"; +const std::string bts_piece_length = "piece length"; +const std::string bts_pieces = "pieces"; +const std::string bts_private = "private"; +const std::string bts_port = "port"; +const std::string bts_priority = "priority"; +const std::string bts_seeding_ratio = "seeding ratio"; +const std::string bts_set_options = "set options"; +const std::string bts_set_priority = "set priority"; +const std::string bts_set_state = "set state"; +const std::string bts_size = "size"; +const std::string bts_started_at = "started at"; +const std::string bts_state = "state"; +const std::string bts_time = "time"; +const std::string bts_torrent = "torrent"; +const std::string bts_torrent_limit = "torrent limit"; +const std::string bts_torrents_dir = "torrents dir"; +const std::string bts_torrents_limit_reached = "access denied, torrents limit reached"; +const std::string bts_total_downloaded = "total downloaded"; +const std::string bts_total_uploaded = "total uploaded"; +const std::string bts_tracker_port = "tracker port"; +const std::string bts_unregistered_ipa = "unregistered IP address"; +const std::string bts_unregistered_torrent = "unregistered torrent"; +const std::string bts_unregistered_torrent_pass = "unregistered torrent pass"; +const std::string bts_unsupported_tracker_protocol = "unsupported tracker protocol, please upgrade your client"; +const std::string bts_up_rate = "up rate"; +const std::string bts_upload_rate = "upload rate"; +const std::string bts_upload_slots = "upload slots"; +const std::string bts_user_agent = "user agent"; +const std::string bts_version = "version"; +const std::string bts_wait_time = "access denied, wait time in effect"; diff --git a/misc/bt_torrent.cpp b/misc/bt_torrent.cpp new file mode 100644 index 0000000..efae0f6 --- /dev/null +++ b/misc/bt_torrent.cpp @@ -0,0 +1,77 @@ +#include "stdafx.h" +#include "bt_torrent.h" + +#include "bt_strings.h" + +Cbt_torrent::Cbt_torrent() +{ +} + +Cbt_torrent::Cbt_torrent(const Cbvalue& v) +{ + write(v); +} + +int Cbt_torrent::write(const Cbvalue& v) +{ + m_announce = v[bts_announce].s(); + m_announces.clear(); + const Cbvalue::t_list& announces = v[bts_announce_list].l(); + for (Cbvalue::t_list::const_iterator i = announces.begin(); i != announces.end(); i++) + { + for (Cbvalue::t_list::const_iterator j = i->l().begin(); j != i->l().end(); j++) + m_announces.push_back(j->s()); + } + return write_info(v[bts_info]); +} + +int Cbt_torrent::write_info(const Cbvalue& v) +{ + m_files.clear(); + const Cbvalue::t_list& files = v[bts_files].l(); + for (Cbvalue::t_list::const_iterator i = files.begin(); i != files.end(); i++) + { + std::string name; + long long size = (*i)[bts_length].i(); + { + const Cbvalue::t_list& path = (*i)[bts_path].l(); + for (Cbvalue::t_list::const_iterator i = path.begin(); i != path.end(); i++) + { + if (i->s().empty() || i->s()[0] == '.' || i->s().find_first_of("\"*/:<>?\\|") != std::string::npos) + return 1; + name += '/' + i->s(); + } + } + if (name.empty()) + return 1; + m_files.push_back(Cfile(name, size)); + } + if (m_files.empty()) + m_files.push_back(Cfile("", v[bts_length].i())); + m_name = v[bts_name].s(); + m_piece_size = v[bts_piece_length].i(); + return 0; +} + +long long Cbt_torrent::size() const +{ + long long r = 0; + for (t_files::const_iterator i = m_files.begin(); i != m_files.end(); i++) + r += i->size(); + return r; +} + +bool Cbt_torrent::valid() const +{ + for (t_files::const_iterator i = m_files.begin(); i != m_files.end(); i++) + { + if (i->size() < 0) + return false; + } + return !files().empty() + && !name().empty() + && name()[0] != '.' + && name().find_first_of("\"*/:<>?\\|") == std::string::npos + && piece_size() >= 16 << 10 + && piece_size() <= 4 << 20; +} diff --git a/misc/bt_torrent.h b/misc/bt_torrent.h new file mode 100644 index 0000000..d808b9e --- /dev/null +++ b/misc/bt_torrent.h @@ -0,0 +1,75 @@ +#pragma once + +#include "bvalue.h" + +class Cbt_torrent +{ +public: + class Cfile + { + public: + const std::string& name() const + { + return m_name; + } + + long long size() const + { + return m_size; + } + + Cfile() + { + } + + Cfile(const std::string& name, long long size) + { + m_name = name; + m_size = size; + } + private: + std::string m_name; + long long m_size; + }; + + typedef std::vector t_announces; + typedef std::vector t_files; + + long long size() const; + bool valid() const; + int write(const Cbvalue&); + int write_info(const Cbvalue&); + Cbt_torrent(); + Cbt_torrent(const Cbvalue&); + + const std::string& announce() const + { + return m_announce; + } + + const t_announces& announces() const + { + return m_announces; + } + + const t_files& files() const + { + return m_files; + } + + const std::string& name() const + { + return m_name; + } + + int piece_size() const + { + return m_piece_size; + } +private: + std::string m_announce; + t_announces m_announces; + t_files m_files; + std::string m_name; + int m_piece_size; +}; diff --git a/misc/bt_tracker_account.cpp b/misc/bt_tracker_account.cpp new file mode 100644 index 0000000..b812d4f --- /dev/null +++ b/misc/bt_tracker_account.cpp @@ -0,0 +1,67 @@ +#include "stdafx.h" +#include "bt_tracker_account.h" + +#include "stream_reader.h" + +Cbt_tracker_account::Cbt_tracker_account() +{ +} + +Cbt_tracker_account::Cbt_tracker_account(const std::string& tracker, const std::string& user, const std::string& pass) +{ + m_tracker = tracker; + m_user = user; + m_pass = pass; +} + +int Cbt_tracker_account::pre_dump() const +{ + return tracker().size() + user().size() + pass().size() + 12; +} + +void Cbt_tracker_account::dump(Cstream_writer& w) const +{ + w.write_data(tracker()); + w.write_data(user()); + w.write_data(pass()); +} + +Cvirtual_binary Cbt_tracker_accounts::dump() const +{ + int cb_d = 4; + for (const_iterator i = begin(); i != end(); i++) + cb_d += i->pre_dump(); + Cvirtual_binary d; + Cstream_writer w(d.write_start(cb_d)); + w.write_int(4, size()); + for (const_iterator i = begin(); i != end(); i++) + i->dump(w); + assert(w.w() == d.end()); + return d; + +} + +const Cbt_tracker_account* Cbt_tracker_accounts::find(const std::string& v) const +{ + for (const_iterator i = begin(); i != end(); i++) + { + if (i->tracker() == v) + return &*i; + } + return NULL; +} + +void Cbt_tracker_accounts::load(const Cvirtual_binary& s) +{ + clear(); + if (s.size() < 4) + return; + Cstream_reader r(s); + for (int count = r.read_int(4); count--; ) + { + std::string tracker = r.read_string(); + std::string name = r.read_string(); + std::string pass = r.read_string(); + push_back(Cbt_tracker_account(tracker, name, pass)); + } +} diff --git a/misc/bt_tracker_account.h b/misc/bt_tracker_account.h new file mode 100644 index 0000000..ec197fc --- /dev/null +++ b/misc/bt_tracker_account.h @@ -0,0 +1,39 @@ +#pragma once + +#include + +class Cbt_tracker_account +{ +public: + int pre_dump() const; + void dump(Cstream_writer&) const; + Cbt_tracker_account(); + Cbt_tracker_account(const std::string& tracker, const std::string& user, const std::string& pass); + + const std::string& tracker() const + { + return m_tracker; + } + + const std::string& user() const + { + return m_user; + } + + const std::string& pass() const + { + return m_pass; + } +private: + std::string m_tracker; + std::string m_user; + std::string m_pass; +}; + +class Cbt_tracker_accounts: public std::list +{ +public: + Cvirtual_binary dump() const; + const Cbt_tracker_account* find(const std::string&) const; + void load(const Cvirtual_binary&); +}; diff --git a/misc/bt_tracker_url.cpp b/misc/bt_tracker_url.cpp new file mode 100644 index 0000000..7630a36 --- /dev/null +++ b/misc/bt_tracker_url.cpp @@ -0,0 +1,82 @@ +#include "stdafx.h" +#include "bt_tracker_url.h" + +#include + +Cbt_tracker_url::Cbt_tracker_url() +{ +} + +Cbt_tracker_url::Cbt_tracker_url(const std::string& v) +{ + write(v); +} + +void Cbt_tracker_url::clear() +{ + m_protocol = tp_unknown; + m_host.erase(); + m_port = 0; + m_path.erase(); +} + +bool Cbt_tracker_url::valid() const +{ + switch (m_protocol) + { + case tp_http: + if (m_path.empty() || m_path[0] != '/') + return false; + case tp_udp: + return !m_host.empty() + && m_port >= 0 && m_port < 0x10000; + } + return false; +} + +void Cbt_tracker_url::write(const std::string& v) +{ + clear(); + size_t a; + int protocol; + int port; + if (boost::istarts_with(v, "http://")) + { + a = 7; + protocol = tp_http; + port = 80; + } + else if (boost::istarts_with(v, "udp://")) + { + a = 6; + protocol = tp_udp; + port = 2710; + } + else + return; + size_t b = v.find_first_of("/:", a); + std::string host; + if (b == std::string::npos) + host = v.substr(a); + else + { + host = v.substr(a, b - a); + if (v[b] == '/') + m_path = v.substr(b); + else + { + b++; + a = v.find('/', b); + if (a == std::string::npos) + port = atoi(v.substr(b).c_str()); + else + { + port = atoi(v.substr(b, a - b).c_str()); + m_path = v.substr(a); + } + } + } + m_protocol = protocol; + m_host = host; + m_port = port; +} diff --git a/misc/bt_tracker_url.h b/misc/bt_tracker_url.h new file mode 100644 index 0000000..c100189 --- /dev/null +++ b/misc/bt_tracker_url.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +class Cbt_tracker_url +{ +public: + enum + { + tp_http, + tp_udp, + tp_unknown + }; + + void clear(); + bool valid() const; + void write(const std::string&); + Cbt_tracker_url(const std::string&); + Cbt_tracker_url(); + + int m_protocol; + std::string m_host; + int m_port; + std::string m_path; +}; diff --git a/misc/bvalue.cpp b/misc/bvalue.cpp new file mode 100644 index 0000000..8509748 --- /dev/null +++ b/misc/bvalue.cpp @@ -0,0 +1,365 @@ +#include +#include "bvalue.h" + +#include +#include +#include +#include +#include + +Cbvalue::Cbvalue(long long v) +{ + m_value_type = vt_int; + m_int = v; +} + +Cbvalue::Cbvalue(t_value_type t) +{ + switch (m_value_type = t) + { + case vt_int: + break; + case vt_string: + m_string = new std::string; + break; + case vt_list: + m_list = new t_list; + break; + case vt_dictionary: + m_map = new t_map; + break; + default: + assert(false); + } +} + +Cbvalue::Cbvalue(const std::string& v) +{ + m_value_type = vt_string; + m_string = new std::string(v); +} + +Cbvalue::Cbvalue(const Cbvalue& v) +{ + switch (m_value_type = v.m_value_type) + { + case vt_int: + m_int = v.m_int; + break; + case vt_string: + m_string = new std::string(*v.m_string); + break; + case vt_list: + m_list = new t_list(*v.m_list); + break; + case vt_dictionary: + m_map = new t_map(*v.m_map); + break; + default: + assert(false); + } +} + +Cbvalue::Cbvalue(data_ref s) +{ + m_value_type = vt_int; + if (write(s)) + clear(); +} + +Cbvalue::~Cbvalue() +{ + clear(); +} + +const Cbvalue& Cbvalue::operator=(const Cbvalue& v) +{ + clear(); + m_value_type = v.m_value_type; + switch (v.m_value_type) + { + case vt_int: + m_int = v.m_int; + break; + case vt_string: + m_string = new std::string(*v.m_string); + break; + case vt_list: + m_list = new t_list(*v.m_list); + break; + case vt_dictionary: + m_map = new t_map(*v.m_map); + break; + default: + assert(false); + } + return *this; +} + +int Cbvalue::write(str_ref s) +{ + return write(s.data(), s.size()); +} + +int Cbvalue::write(const char* s, int cb_s) +{ + return write(s, s + cb_s); +} + +int Cbvalue::write(const char*& s, const char* s_end) +{ + clear(); + if (s >= s_end) + return 1; + switch (*s++) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + const char* a = s - 1; + while (s < s_end && *s != ':') + s++; + if (s++ >= s_end) + return 1; + int l = atoi(a); + if (s + l > s_end) + return 1; + m_value_type = vt_string; + m_string = new std::string(s, l); + s += l; + return 0; + } + case 'd': + { + m_value_type = vt_dictionary; + m_map = new t_map; + while (s < s_end && *s != 'e') + { + Cbvalue v; + Cbvalue w; + if (v.write(s, s_end) || v.m_value_type != vt_string) + return 1; + if (w.write(s, s_end)) + return 1; + (*m_map)[*v.m_string] = w; + } + if (s++ >= s_end) + return 1; + return 0; + } + break; + case 'i': + { + const char* a = s; + while (s < s_end && *s != 'e') + s++; + if (s++ >= s_end) + return 1; + m_value_type = vt_int; + m_int = atoll(a); + return 0; + } + case 'l': + { + m_value_type = vt_list; + m_list = new t_list; + while (s < s_end && *s != 'e') + { + Cbvalue v; + if (v.write(s, s_end)) + return 1; + m_list->push_back(v); + } + if (s++ >= s_end) + return 1; + return 0; + } + } + return 1; +} + +void Cbvalue::clear() +{ + switch (m_value_type) + { + case vt_int: + break; + case vt_string: + delete m_string; + break; + case vt_list: + delete m_list; + break; + case vt_dictionary: + delete m_map; + break; + default: + assert(false); + } + m_value_type = vt_int; +} + +const Cbvalue::t_map& Cbvalue::d() const +{ + static t_map z; + return m_value_type == vt_dictionary ? *m_map : z; +} + +bool Cbvalue::d_has(const std::string& v) const +{ + return m_value_type == vt_dictionary && m_map->count(v); +} + +const Cbvalue& Cbvalue::d(const std::string& v) const +{ + static Cbvalue z; + return m_value_type == vt_dictionary ? find_ref(*m_map, v, z) : z; +} + +const Cbvalue& Cbvalue::operator[](const std::string& v) const +{ + return d(v); +} + +long long Cbvalue::i() const +{ + return m_value_type == vt_int ? m_int : 0; +} + +const Cbvalue::t_list& Cbvalue::l() const +{ + static t_list z; + return m_value_type == vt_list ? *m_list : z; +} + +const std::string& Cbvalue::s() const +{ + static std::string z; + return m_value_type == vt_string ? *m_string : z; +} + +Cbvalue& Cbvalue::d(const std::string& v, const Cbvalue& w) +{ + if (m_value_type != vt_dictionary) + { + clear(); + m_value_type = vt_dictionary; + m_map = new t_map; + } + (*m_map)[v] = w; + return *this; +} + +Cbvalue& Cbvalue::l(const Cbvalue& v) +{ + if (m_value_type != vt_list) + { + clear(); + m_value_type = vt_list; + m_list = new t_list; + } + (*m_list).push_back(v); + return *this; +} + +int Cbvalue::pre_read() const +{ + switch (m_value_type) + { + case vt_int: + return n(m_int).size() + 2; + case vt_string: + return n(m_string->size()).size() + m_string->size() + 1; + case vt_list: + { + int v = 2; + BOOST_FOREACH(auto& i, *m_list) + v += i.pre_read(); + return v; + } + case vt_dictionary: + { + int v = 2; + BOOST_FOREACH(auto& i, *m_map) + v += n(i.first.size()).size() + i.first.size() + i.second.pre_read() + 1; + return v; + } + } + assert(false); + return 0; +} + +shared_data Cbvalue::read() const +{ + shared_data d(pre_read()); + BOOST_VERIFY(read(d.data()) == d.size()); + return d; +} + +int Cbvalue::read(void* d) const +{ + return read(reinterpret_cast(d)); +} + +int Cbvalue::read(char* d) const +{ + char* w = d; + switch (m_value_type) + { + case vt_int: +#ifdef WIN32 + sprintf(d, "i%I64d", m_int); +#else + sprintf(d, "i%lld", m_int); +#endif + w += strlen(d); + *w++ = 'e'; + return w - d; + case vt_string: +#ifdef WIN32 + sprintf(w, "%d:", m_string->size()); +#else + sprintf(w, "%zu:", m_string->size()); +#endif + w += n(m_string->size()).size() + 1; + memcpy(w, m_string->data(), m_string->size()); + w += m_string->size(); + return w - d; + case vt_list: + { + *w++ = 'l'; + BOOST_FOREACH(auto& i, *m_list) + w += i.read(w); + *w++ = 'e'; + return w - d; + } + case vt_dictionary: + { + *w++ = 'd'; + BOOST_FOREACH(auto& i, *m_map) + { +#ifdef WIN32 + sprintf(w, "%d:", i.first.size()); +#else + sprintf(w, "%zu:", i.first.size()); +#endif + w += n(i.first.size()).size() + 1; + memcpy(w, i.first.data(), i.first.size()); + w += i.first.size(); + w += i.second.read(w); + } + *w++ = 'e'; + return w - d; + } + } + assert(false); + return 0; +} diff --git a/misc/bvalue.h b/misc/bvalue.h new file mode 100644 index 0000000..8a38014 --- /dev/null +++ b/misc/bvalue.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include +#include + +class Cbvalue +{ +public: + enum t_value_type + { + vt_int, + vt_string, + vt_list, + vt_dictionary, + }; + + typedef std::map t_map; + typedef std::vector t_list; + + void clear(); + const t_map& d() const; + const t_list& l() const; + long long i() const; + const std::string& s() const; + bool d_has(const std::string&) const; + Cbvalue& d(const std::string& v, const Cbvalue& w); + Cbvalue& l(const Cbvalue& v); + int pre_read() const; + int read(char* d) const; + int read(void* d) const; + shared_data read() const; + int write(const char* s, int cb_s); + int write(str_ref); + Cbvalue(long long v = 0); + Cbvalue(t_value_type t); + Cbvalue(const std::string& v); + Cbvalue(const Cbvalue&); + Cbvalue(data_ref); + const Cbvalue& operator=(const Cbvalue&); + const Cbvalue& operator[](const std::string&) const; + ~Cbvalue(); +private: + const Cbvalue& d(const std::string&) const; + + t_value_type m_value_type; + + union + { + long long m_int; + std::string* m_string; + t_list* m_list; + t_map* m_map; + }; + + int write(const char*& s, const char* s_end); +}; diff --git a/misc/config_base.h b/misc/config_base.h new file mode 100644 index 0000000..b063397 --- /dev/null +++ b/misc/config_base.h @@ -0,0 +1,108 @@ +#pragma once + +#include +#include +#include +#include +#include + +class Cconfig_base +{ +public: + template + struct t_attribute + { + const char* key; + T* value; + T default_value; + }; + + template + class t_attributes: public std::map > + { + }; + + virtual int set(const std::string& name, const std::string& value) + { + t_attributes::iterator i = m_attributes_string.find(name); + if (i != m_attributes_string.end()) + *i->second.value = value; + else + return set(name, atoi(value.c_str())); + return 0; + } + + virtual int set(const std::string& name, int value) + { + t_attributes::iterator i = m_attributes_int.find(name); + if (i != m_attributes_int.end()) + *i->second.value = value; + else + return set(name, static_cast(value)); + return 0; + } + + virtual int set(const std::string& name, bool value) + { + t_attributes::iterator i = m_attributes_bool.find(name); + if (i != m_attributes_bool.end()) + *i->second.value = value; + else + return 1; + return 0; + } + + std::istream& load(std::istream& is) + { + for (std::string s; getline(is, s); ) + { + size_t i = s.find('='); + if (i != std::string::npos) + set(boost::trim_copy(s.substr(0, i)), boost::trim_copy(s.substr(i + 1))); + } + return is; + } + + int load(const std::string& file) + { + std::ifstream is(file.c_str()); + if (!is) + return 1; + load(is); + return !is.eof(); + } + + std::ostream& save(std::ostream& os) const + { + save_map(os, m_attributes_bool); + save_map(os, m_attributes_int); + save_map(os, m_attributes_string); + return os; + } + +protected: + t_attributes m_attributes_bool; + t_attributes m_attributes_int; + t_attributes m_attributes_string; + + template + void fill_map(t_attribute* attributes, const t_attributes* s, t_attributes& d) + { + for (t_attribute* i = attributes; i->key; i++) + { + *i->value = s ? *s->find(i->key)->second.value : i->default_value; + d[i->key] = *i; + } + } + + template + void save_map(std::ostream& os, const T& v) const + { + for (typename T::const_iterator i = v.begin(); i != v.end(); i++) + { + if (*i->second.value == i->second.default_value) + os << "# "; + os << i->first << " = " << *i->second.value << std::endl; + } + } +}; diff --git a/misc/database.cpp b/misc/database.cpp new file mode 100644 index 0000000..9f6b1c9 --- /dev/null +++ b/misc/database.cpp @@ -0,0 +1,99 @@ +#include + +#include +#include + +#ifdef WIN32 +#pragma comment(lib, "libmysql") +#else +#include +#endif + +Cdatabase::Cdatabase() +{ + mysql_init(&m_handle); +} + +Cdatabase::~Cdatabase() +{ + close(); +} + +void Cdatabase::open(const std::string& host, const std::string& user, const std::string& password, const std::string& database, bool echo_errors) +{ + m_echo_errors = echo_errors; + if (!mysql_init(&m_handle) + || mysql_options(&m_handle, MYSQL_READ_DEFAULT_GROUP, "") + || !mysql_real_connect(&m_handle, host.c_str(), user.c_str(), password.empty() ? NULL : password.c_str(), database.c_str(), database == "sphinx" ? 9306 : 0, NULL, 0)) + throw bad_query(mysql_error(&m_handle)); + char a0 = true; + mysql_options(&m_handle, MYSQL_OPT_RECONNECT, &a0); +} + +int Cdatabase::query_nothrow(const std::string& q) +{ + if (m_query_log) + { + *m_query_log << q.substr(0, 999) << std::endl; + } + if (mysql_real_query(&m_handle, q.data(), q.size())) + { + if (m_echo_errors) + { + std::cerr << mysql_error(&m_handle) << std::endl + << q.substr(0, 239) << std::endl; + } +#ifndef WIN32 + syslog(LOG_ERR, "%s", mysql_error(&m_handle)); +#endif + return 1; + } + return 0; +} + +Csql_result Cdatabase::query(const std::string& q) +{ + if (query_nothrow(q)) + throw bad_query(mysql_error(&m_handle)); + MYSQL_RES* result = mysql_store_result(&m_handle); + if (!result && mysql_errno(&m_handle)) + throw bad_query(mysql_error(&m_handle)); + return Csql_result(result); +} + +void Cdatabase::close() +{ + mysql_close(&m_handle); +} + +int Cdatabase::affected_rows() +{ + return mysql_affected_rows(&m_handle); +} + +int Cdatabase::insert_id() +{ + return mysql_insert_id(&m_handle); +} + +int Cdatabase::select_db(const std::string& v) +{ + return mysql_select_db(&m_handle, v.c_str()); +} + +void Cdatabase::set_query_log(std::ostream* v) +{ + m_query_log = v; +} + +void Cdatabase::set_name(const std::string& a, std::string b) +{ + m_names[a] = std::move(b); +} + +const std::string& Cdatabase::name(const std::string& v) const +{ + const std::string* i = find_ptr(m_names, v); + assert(i); + return i ? *i : v; +} diff --git a/misc/sha1.cpp b/misc/sha1.cpp new file mode 100644 index 0000000..ccb02fa --- /dev/null +++ b/misc/sha1.cpp @@ -0,0 +1,418 @@ +/* + * sha1.c + * + * Description: + * This file implements the Secure Hashing Algorithm 1 as + * defined in FIPS PUB 180-1 published April 17, 1995. + * + * The SHA-1, produces a 160-bit message digest for a given + * data stream. It should take about 2**n steps to find a + * message with the same digest as a given message and + * 2**(n/2) to find any two messages with the same digest, + * when n is the digest size in bits. Therefore, this + * algorithm can serve as a means of providing a + * "fingerprint" for a message. + * + * Portability Issues: + * SHA-1 is defined in terms of 32-bit "words". This code + * uses (included via "sha1.h" to define 32 and 8 + * bit unsigned integer types. If your C compiler does not + * support 32 bit unsigned integers, this code is not + * appropriate. + * + * Caveats: + * SHA-1 is designed to work with messages less than 2^64 bits + * long. Although SHA-1 allows a message digest to be generated + * for messages of any number of bits less than 2^64, this + * implementation only works with messages with a length that is + * a multiple of the size of an 8-bit character. + * + */ + +#include "sha1.h" + +/* + * Define the SHA1 circular left shift macro + */ +#define SHA1CircularShift(bits,word) \ + (((word) << (bits)) | ((word) >> (32-(bits)))) + +/* Local Function Prototyptes */ +void SHA1PadMessage(SHA1Context *); +void SHA1ProcessMessageBlock(SHA1Context *); + +/* + * SHA1Reset + * + * Description: + * This function will initialize the SHA1Context in preparation + * for computing a new SHA1 message digest. + * + * Parameters: + * context: [in/out] + * The context to reset. + * + * Returns: + * sha Error Code. + * + */ +int SHA1Reset(SHA1Context *context) +{ + if (!context) + { + return shaNull; + } + + context->Length_Low = 0; + context->Length_High = 0; + context->Message_Block_Index = 0; + + context->Intermediate_Hash[0] = 0x67452301; + context->Intermediate_Hash[1] = 0xEFCDAB89; + context->Intermediate_Hash[2] = 0x98BADCFE; + context->Intermediate_Hash[3] = 0x10325476; + context->Intermediate_Hash[4] = 0xC3D2E1F0; + + context->Computed = 0; + context->Corrupted = 0; + + return shaSuccess; +} + +/* + * SHA1Result + * + * Description: + * This function will return the 160-bit message digest into the + * Message_Digest array provided by the caller. + * NOTE: The first octet of hash is stored in the 0th element, + * the last octet of hash in the 19th element. + * + * Parameters: + * context: [in/out] + * The context to use to calculate the SHA-1 hash. + * Message_Digest: [out] + * Where the digest is returned. + * + * Returns: + * sha Error Code. + * + */ +int SHA1Result( SHA1Context *context, + uint8_t Message_Digest[SHA1HashSize]) +{ + int i; + + if (!context || !Message_Digest) + { + return shaNull; + } + + if (context->Corrupted) + { + return context->Corrupted; + } + + if (!context->Computed) + { + SHA1PadMessage(context); + for(i=0; i<64; ++i) + { + /* message may be sensitive, clear it out */ + context->Message_Block[i] = 0; + } + context->Length_Low = 0; /* and clear length */ + context->Length_High = 0; + context->Computed = 1; + + } + + for(i = 0; i < SHA1HashSize; ++i) + { + Message_Digest[i] = context->Intermediate_Hash[i>>2] + >> 8 * ( 3 - ( i & 0x03 ) ); + } + + return shaSuccess; +} + +/* + * SHA1Input + * + * Description: + * This function accepts an array of octets as the next portion + * of the message. + * + * Parameters: + * context: [in/out] + * The SHA context to update + * message_array: [in] + * An array of characters representing the next portion of + * the message. + * length: [in] + * The length of the message in message_array + * + * Returns: + * sha Error Code. + * + */ +int SHA1Input( SHA1Context *context, + const void *message_array0, + size_t length) +{ + const uint8_t *message_array = reinterpret_cast(message_array0); + if (!length) + { + return shaSuccess; + } + + if (!context || !message_array) + { + return shaNull; + } + + if (context->Computed) + { + context->Corrupted = shaStateError; + + return shaStateError; + } + + if (context->Corrupted) + { + return context->Corrupted; + } + while(length-- && !context->Corrupted) + { + context->Message_Block[context->Message_Block_Index++] = + (*message_array & 0xFF); + + context->Length_Low += 8; + if (context->Length_Low == 0) + { + context->Length_High++; + if (context->Length_High == 0) + { + /* Message is too long */ + context->Corrupted = 1; + } + } + + if (context->Message_Block_Index == 64) + { + SHA1ProcessMessageBlock(context); + } + + message_array++; + } + + return shaSuccess; +} + +/* + * SHA1ProcessMessageBlock + * + * Description: + * This function will process the next 512 bits of the message + * stored in the Message_Block array. + * + * Parameters: + * None. + * + * Returns: + * Nothing. + * + * Comments: + + * Many of the variable names in this code, especially the + * single character names, were used because those were the + * names used in the publication. + * + * + */ +void SHA1ProcessMessageBlock(SHA1Context *context) +{ + const uint32_t K[] = { /* Constants defined in SHA-1 */ + 0x5A827999, + 0x6ED9EBA1, + 0x8F1BBCDC, + 0xCA62C1D6 + }; + int t; /* Loop counter */ + uint32_t temp; /* Temporary word value */ + uint32_t W[80]; /* Word sequence */ + uint32_t A, B, C, D, E; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for(t = 0; t < 16; t++) + { + W[t] = context->Message_Block[t * 4] << 24; + W[t] |= context->Message_Block[t * 4 + 1] << 16; + W[t] |= context->Message_Block[t * 4 + 2] << 8; + W[t] |= context->Message_Block[t * 4 + 3]; + } + + for(t = 16; t < 80; t++) + { + W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + + A = context->Intermediate_Hash[0]; + B = context->Intermediate_Hash[1]; + C = context->Intermediate_Hash[2]; + D = context->Intermediate_Hash[3]; + E = context->Intermediate_Hash[4]; + + for(t = 0; t < 20; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + + B = A; + A = temp; + } + + for(t = 20; t < 40; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 40; t < 60; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 60; t < 80; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + context->Intermediate_Hash[0] += A; + context->Intermediate_Hash[1] += B; + context->Intermediate_Hash[2] += C; + context->Intermediate_Hash[3] += D; + context->Intermediate_Hash[4] += E; + + context->Message_Block_Index = 0; +} + +/* + * SHA1PadMessage + * + + * Description: + * According to the standard, the message must be padded to an even + * 512 bits. The first padding bit must be a '1'. The last 64 + * bits represent the length of the original message. All bits in + * between should be 0. This function will pad the message + * according to those rules by filling the Message_Block array + * accordingly. It will also call the ProcessMessageBlock function + * provided appropriately. When it returns, it can be assumed that + * the message digest has been computed. + * + * Parameters: + * context: [in/out] + * The context to pad + * ProcessMessageBlock: [in] + * The appropriate SHA*ProcessMessageBlock function + * Returns: + * Nothing. + * + */ + +void SHA1PadMessage(SHA1Context *context) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (context->Message_Block_Index > 55) + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 64) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + + SHA1ProcessMessageBlock(context); + + while(context->Message_Block_Index < 56) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + else + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 56) + { + + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + + /* + * Store the message length as the last 8 octets + */ + context->Message_Block[56] = context->Length_High >> 24; + context->Message_Block[57] = context->Length_High >> 16; + context->Message_Block[58] = context->Length_High >> 8; + context->Message_Block[59] = context->Length_High; + context->Message_Block[60] = context->Length_Low >> 24; + context->Message_Block[61] = context->Length_Low >> 16; + context->Message_Block[62] = context->Length_Low >> 8; + context->Message_Block[63] = context->Length_Low; + + SHA1ProcessMessageBlock(context); +} + +Csha1::Csha1() +{ + SHA1Reset(&m_context); +} + +Csha1::Csha1(data_ref s) +{ + SHA1Reset(&m_context); + write(s); +} + +void Csha1::read(void* d) +{ + SHA1Result(&m_context, reinterpret_cast(d)); +} + +std::string Csha1::read() +{ + char d[SHA1HashSize]; + read(d); + return std::string(d, SHA1HashSize); +} + +void Csha1::write(data_ref s) +{ + SHA1Input(&m_context, s.data(), s.size()); +} diff --git a/misc/sha1.h b/misc/sha1.h new file mode 100644 index 0000000..8a1ef4f --- /dev/null +++ b/misc/sha1.h @@ -0,0 +1,79 @@ +/* + * sha1.h + * + * Description: + * This is the header file for code which implements the Secure + * Hashing Algorithm 1 as defined in FIPS PUB 180-1 published + * April 17, 1995. + * + * Many of the variable names in this code, especially the + * single character names, were used because those were the names + * used in the publication. + * + * Please read the file sha1.c for more information. + * + */ + +#ifndef _SHA1_H_ +#define _SHA1_H_ + +#include +#include +#include + +#ifndef _SHA_enum_ +#define _SHA_enum_ +enum +{ + shaSuccess = 0, + shaNull, /* Null pointer parameter */ + shaInputTooLong, /* input data too long */ + shaStateError /* called Input after Result */ +}; +#endif +#define SHA1HashSize 20 + +/* + * This structure will hold context information for the SHA-1 + * hashing operation + */ +typedef struct SHA1Context +{ + uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */ + + uint32_t Length_Low; /* Message length in bits */ + uint32_t Length_High; /* Message length in bits */ + + /* Index into message block array */ + int_least16_t Message_Block_Index; + uint8_t Message_Block[64]; /* 512-bit message blocks */ + + int Computed; /* Is the digest computed? */ + int Corrupted; /* Is the message digest corrupted? */ +} SHA1Context; + +/* + * Function Prototypes + */ + +int SHA1Reset( SHA1Context *); +int SHA1Input( SHA1Context *, + const void *, + size_t); +int SHA1Result( SHA1Context *, + uint8_t Message_Digest[SHA1HashSize]); + +class Csha1 +{ +public: + void read(void*); + std::string read(); + void write(data_ref); + Csha1(); + Csha1(data_ref); +private: + SHA1Context m_context; +}; + +#endif + diff --git a/misc/socket.cpp b/misc/socket.cpp new file mode 100644 index 0000000..1eb0509 --- /dev/null +++ b/misc/socket.cpp @@ -0,0 +1,229 @@ +#include "socket.h" + +#include +#include + +#ifdef WIN32 +#pragma comment(lib, "ws2_32.lib") +#else +#include +#include +#include +#include +#include +#endif + +#ifndef INADDR_NONE +const int INADDR_NONE = -1; +#endif + +#ifndef MSG_NOSIGNAL +const int MSG_NOSIGNAL = 0; +#endif + +Csocket::Csocket(SOCKET s) +{ + if (s != INVALID_SOCKET) + m_source = std::make_shared(s); +} + +/* +int Csocket::accept(int& h, int& p) +{ + sockaddr_in a; + socklen_t cb_a = sizeof(sockaddr_in); + a.sin_family = AF_INET; + int r = ::accept(*this, reinterpret_cast(&a), &cb_a); + if (r == INVALID_SOCKET) + return r; + h = a.sin_addr.s_addr; + p = a.sin_port; + return r; +} +*/ + +int Csocket::bind(int h, int p) +{ + sockaddr_in a; + memset(&a, 0, sizeof(a)); + a.sin_family = AF_INET; + a.sin_addr.s_addr = h; + a.sin_port = p; + return ::bind(*this, reinterpret_cast(&a), sizeof(sockaddr_in)); +} + +int Csocket::blocking(bool v) +{ +#ifdef FIONBIO + unsigned long p = !v; + return ioctlsocket(*this, FIONBIO, &p); +#else + return fcntl(*this, F_SETFL, v ? 0 : O_NONBLOCK) == -1; +#endif +} + +void Csocket::close() +{ + m_source.reset(); +} + +int Csocket::connect(int h, int p) +{ + sockaddr_in a; + memset(&a, 0, sizeof(a)); + a.sin_family = AF_INET; + a.sin_addr.s_addr = h; + a.sin_port = p; + return ::connect(*this, reinterpret_cast(&a), sizeof(sockaddr_in)); +} + +int Csocket::listen() +{ + return ::listen(*this, SOMAXCONN); +} + +const Csocket& Csocket::open(int t, bool _blocking) +{ + start_up(); + *this = socket(AF_INET, t, 0); + if (*this != INVALID_SOCKET && !_blocking && blocking(false)) + close(); + return *this; +} + +int Csocket::recv(mutable_str_ref d) const +{ + return ::recv(*this, d.data(), d.size(), MSG_NOSIGNAL); +} + +int Csocket::recvfrom(mutable_str_ref d, sockaddr* a, socklen_t* cb_a) const +{ + return ::recvfrom(*this, d.data(), d.size(), MSG_NOSIGNAL, a, cb_a); +} + +int Csocket::send(str_ref s) const +{ + return ::send(*this, s.data(), s.size(), MSG_NOSIGNAL); +} + +int Csocket::sendto(str_ref s, const sockaddr* a, socklen_t cb_a) const +{ + return ::sendto(*this, s.data(), s.size(), MSG_NOSIGNAL, a, cb_a); +} + +int Csocket::getsockopt(int level, int name, void* v, socklen_t& cb_v) +{ + return ::getsockopt(*this, level, name, reinterpret_cast(v), &cb_v); +} + +int Csocket::getsockopt(int level, int name, int& v) +{ + socklen_t cb_v = sizeof(int); + return getsockopt(level, name, &v, cb_v); +} + +int Csocket::setsockopt(int level, int name, const void* v, int cb_v) +{ + return ::setsockopt(*this, level, name, reinterpret_cast(v), cb_v); +} + +int Csocket::setsockopt(int level, int name, int v) +{ + return setsockopt(level, name, &v, sizeof(int)); +} + +int Csocket::get_host(const std::string& name) +{ + hostent* e = gethostbyname(name.c_str()); + return e && e->h_addrtype == AF_INET && e->h_length == sizeof(in_addr) && e->h_addr_list ? *reinterpret_cast(*e->h_addr_list) : INADDR_NONE; +} + +std::string Csocket::error2a(int v) +{ + switch (v) + { + case WSAEACCES: return "EACCES"; + case WSAEADDRINUSE: return "EADDRINUSE"; + case WSAEADDRNOTAVAIL: return "EADDRNOTAVAIL"; + case WSAEAFNOSUPPORT: return "EAFNOSUPPORT"; + case WSAEALREADY: return "EALREADY"; + case WSAEBADF: return "EBADF"; + case WSAECONNABORTED: return "ECONNABORTED"; + case WSAECONNREFUSED: return "ECONNREFUSED"; + case WSAECONNRESET: return "ECONNRESET"; + case WSAEDESTADDRREQ: return "EDESTADDRREQ"; + case WSAEDQUOT: return "EDQUOT"; + case WSAEFAULT: return "EFAULT"; + case WSAEHOSTDOWN: return "EHOSTDOWN"; + case WSAEHOSTUNREACH: return "EHOSTUNREACH"; + case WSAEINPROGRESS: return "EINPROGRESS"; + case WSAEINTR: return "EINTR"; + case WSAEINVAL: return "EINVAL"; + case WSAEISCONN: return "EISCONN"; + case WSAELOOP: return "ELOOP"; + case WSAEMFILE: return "EMFILE"; + case WSAEMSGSIZE: return "EMSGSIZE"; + case WSAENAMETOOLONG: return "ENAMETOOLONG"; + case WSAENETDOWN: return "ENETDOWN"; + case WSAENETRESET: return "ENETRESET"; + case WSAENETUNREACH: return "ENETUNREACH"; + case WSAENOBUFS: return "ENOBUFS"; + case WSAENOPROTOOPT: return "ENOPROTOOPT"; + case WSAENOTCONN: return "ENOTCONN"; + case WSAENOTEMPTY: return "ENOTEMPTY"; + case WSAENOTSOCK: return "ENOTSOCK"; + case WSAEOPNOTSUPP: return "EOPNOTSUPP"; + case WSAEPFNOSUPPORT: return "EPFNOSUPPORT"; + case WSAEPROTONOSUPPORT: return "EPROTONOSUPPORT"; + case WSAEPROTOTYPE: return "EPROTOTYPE"; + case WSAEREMOTE: return "EREMOTE"; + case WSAESHUTDOWN: return "ESHUTDOWN"; + case WSAESOCKTNOSUPPORT: return "ESOCKTNOSUPPORT"; + case WSAESTALE: return "ESTALE"; + case WSAETIMEDOUT: return "ETIMEDOUT"; + case WSAETOOMANYREFS: return "ETOOMANYREFS"; + case WSAEUSERS: return "EUSERS"; + case WSAEWOULDBLOCK: return "EWOULDBLOCK"; +#ifdef WIN32 + case WSAECANCELLED: return "ECANCELLED"; + case WSAEDISCON: return "EDISCON"; + case WSAEINVALIDPROCTABLE: return "EINVALIDPROCTABLE"; + case WSAEINVALIDPROVIDER: return "EINVALIDPROVIDER"; + case WSAENOMORE: return "ENOMORE"; + case WSAEPROVIDERFAILEDINIT: return "EPROVIDERFAILEDINIT"; + case WSAEREFUSED: return "EREFUSED"; + case WSANOTINITIALISED: return "NOTINITIALISED"; + case WSASERVICE_NOT_FOUND: return "SERVICE_NOT_FOUND"; + case WSASYSCALLFAILURE: return "SYSCALLFAILURE"; + case WSASYSNOTREADY: return "SYSNOTREADY"; + case WSATYPE_NOT_FOUND: return "TYPE_NOT_FOUND"; + case WSAVERNOTSUPPORTED: return "VERNOTSUPPORTED"; + case WSA_E_CANCELLED: return "E_CANCELLED"; + case WSA_E_NO_MORE: return "E_NO_MORE"; +#endif + } + char b[12]; + sprintf(b, "%d", v); + return b; +} + +std::string Csocket::inet_ntoa(int v) +{ + in_addr a; + a.s_addr = v; + return ::inet_ntoa(a); +} + +int Csocket::start_up() +{ +#ifdef WIN32 + static bool done = false; + if (done) + return 0; + done = true; + WSADATA wsadata; + if (WSAStartup(MAKEWORD(2, 0), &wsadata)) + return 1; +#endif + return 0; +} diff --git a/misc/socket.h b/misc/socket.h new file mode 100644 index 0000000..dd917aa --- /dev/null +++ b/misc/socket.h @@ -0,0 +1,124 @@ +#pragma once + +#include +#include +#include +#include + +#ifdef WIN32 +#include + +typedef int socklen_t; +#else +#include +#include +#include +#include +#include +#include + +#define closesocket close +#define ioctlsocket ioctl +#define WSAGetLastError() errno + +#define WSAEACCES EACCES +#define WSAEADDRINUSE EADDRINUSE +#define WSAEADDRNOTAVAIL EADDRNOTAVAIL +#define WSAEAFNOSUPPORT EAFNOSUPPORT +#define WSAEALREADY EALREADY +#define WSAEBADF EBADF +#define WSAECONNABORTED ECONNABORTED +#define WSAECONNREFUSED ECONNREFUSED +#define WSAECONNRESET ECONNRESET +#define WSAEDESTADDRREQ EDESTADDRREQ +#define WSAEDQUOT EDQUOT +#define WSAEFAULT EFAULT +#define WSAEHOSTDOWN EHOSTDOWN +#define WSAEHOSTUNREACH EHOSTUNREACH +#define WSAEINPROGRESS EINPROGRESS +#define WSAEINTR EINTR +#define WSAEINVAL EINVAL +#define WSAEISCONN EISCONN +#define WSAELOOP ELOOP +#define WSAEMFILE EMFILE +#define WSAEMSGSIZE EMSGSIZE +#define WSAENAMETOOLONG ENAMETOOLONG +#define WSAENETDOWN ENETDOWN +#define WSAENETRESET ENETRESET +#define WSAENETUNREACH ENETUNREACH +#define WSAENOBUFS ENOBUFS +#define WSAENOPROTOOPT ENOPROTOOPT +#define WSAENOTCONN ENOTCONN +#define WSAENOTEMPTY ENOTEMPTY +#define WSAENOTSOCK ENOTSOCK +#define WSAEOPNOTSUPP EOPNOTSUPP +#define WSAEPFNOSUPPORT EPFNOSUPPORT +#define WSAEPROTONOSUPPORT EPROTONOSUPPORT +#define WSAEPROTOTYPE EPROTOTYPE +#define WSAEREMOTE EREMOTE +#define WSAESHUTDOWN ESHUTDOWN +#define WSAESOCKTNOSUPPORT ESOCKTNOSUPPORT +#define WSAESTALE ESTALE +#define WSAETIMEDOUT ETIMEDOUT +#define WSAETOOMANYREFS ETOOMANYREFS +#define WSAEUSERS EUSERS +#define WSAEWOULDBLOCK EWOULDBLOCK + +typedef int SOCKET; + +const int INVALID_SOCKET = -1; +const int SOCKET_ERROR = -1; +#endif + +class Csocket_source : boost::noncopyable +{ +public: + Csocket_source(SOCKET s) + { + m_s = s; + } + + ~Csocket_source() + { + closesocket(m_s); + } + + operator SOCKET() const + { + return m_s; + } +private: + SOCKET m_s; +}; + +class Csocket +{ +public: + static std::string error2a(int v); + static int get_host(const std::string& name); + static std::string inet_ntoa(int h); + static int start_up(); + int accept(int& h, int& p); + int bind(int h, int p); + int blocking(bool v); + void close(); + int connect(int h, int p); + int getsockopt(int level, int name, void* v, socklen_t& cb_v); + int getsockopt(int level, int name, int& v); + int listen(); + const Csocket& open(int t, bool blocking = false); + int recv(mutable_str_ref) const; + int recvfrom(mutable_str_ref, sockaddr* a, socklen_t* cb_a) const; + int send(str_ref) const; + int sendto(str_ref, const sockaddr* a, socklen_t cb_a) const; + int setsockopt(int level, int name, const void* v, int cb_v); + int setsockopt(int level, int name, int v); + Csocket(SOCKET = INVALID_SOCKET); + + operator SOCKET() const + { + return m_source ? static_cast(*m_source) : INVALID_SOCKET; + } +private: + std::shared_ptr m_source; +}; diff --git a/misc/sql_query.cpp b/misc/sql_query.cpp new file mode 100644 index 0000000..a14fb36 --- /dev/null +++ b/misc/sql_query.cpp @@ -0,0 +1,104 @@ +#include + +#include +#include +#include + +Csql_query::Csql_query(Cdatabase& database, std::string v) : + m_database(database), + m_in(std::move(v)) +{ +} + +Csql_result Csql_query::execute() const +{ + return m_database.query(read()); +} + +int Csql_query::execute_nothrow() const +{ + return m_database.query_nothrow(read()); +} + +std::string Csql_query::replace_names(const std::string& v) const +{ + std::string r; + for (size_t i = 0; ; ) + { + size_t j = v.find('@', i); + if (j == std::string::npos) + { + r.append(v, i, v.size() - i); + break; + } + r.append(v, i, j - i); + i = j + 1; + j = v.find_first_of(" ,", i); + if (j == std::string::npos) + j = v.size(); + r += m_database.name(v.substr(i, j - i)); + i = j; + } + return r; +} + +std::string Csql_query::read() const +{ + return m_out + replace_names(m_in); +} + +void Csql_query::operator=(std::string v) +{ + m_in = std::move(v); + m_out.clear(); +} + +void Csql_query::operator+=(const std::string& v) +{ + m_in += v; +} + +Csql_query& Csql_query::p_name(const std::string& v0) +{ + const std::string& v = m_database.name(v0); + std::vector r(2 * v.size() + 2); + r.resize(mysql_real_escape_string(m_database, &r[1], v.data(), v.size()) + 2); + r.front() = '`'; + r.back() = '`'; + p_raw(r); + return *this; +} + +Csql_query& Csql_query::p_raw(data_ref v) +{ + size_t i = m_in.find('?'); + assert(i != std::string::npos); + if (i == std::string::npos) + return *this; + m_out.append(replace_names(m_in.substr(0, i))); + m_in.erase(0, i + 1); + m_out.append(v.begin(), v.end()); + return *this; +} + +Csql_query& Csql_query::operator()(long long v) +{ + char b[21]; +#ifdef WIN32 + sprintf(b, "%I64d", v); +#else + sprintf(b, "%lld", v); +#endif + p_raw(data_ref(b)); + return *this; +} + +Csql_query& Csql_query::operator()(str_ref v) +{ + std::vector r(2 * v.size() + 2); + r.resize(mysql_real_escape_string(m_database, &r[1], v.data(), v.size()) + 2); + r.front() = '\''; + r.back() = '\''; + p_raw(r); + return *this; +} diff --git a/misc/stream_int.h b/misc/stream_int.h new file mode 100644 index 0000000..6169bc0 --- /dev/null +++ b/misc/stream_int.h @@ -0,0 +1,83 @@ +#pragma once + +#include +#include + +inline float read_float(const void* r) +{ + float v; + memcpy(&v, r, sizeof(float)); + return v; +} + +inline float read_float(const void* r0, const void* /*s_end*/) +{ + return read_float(r0); +} + +template +static T write_float(T w0, float v) +{ + unsigned char* w = reinterpret_cast(w0); + memcpy(w, &v, sizeof(float)); + return w + sizeof(float); +} + +inline long long read_int(int cb, const void* r0) +{ + const unsigned char* r = reinterpret_cast(r0); + long long v = 0; + while (cb--) + v = v << 8 | *r++; + return v; +} + +inline long long read_int(size_t cb, data_ref s) +{ + return static_cast(s.size()) < cb ? 0 : read_int(cb, s.data()); +} + +inline long long read_int(int cb, const void* r, const void* s_end) +{ + return read_int(cb, data_ref(r, s_end)); +} + +template +T write_int(int cb, T w0, long long v) +{ + unsigned char* w = reinterpret_cast(w0); + w += cb; + for (int i = 0; i < cb; i++) + { + *--w = v & 0xff; + v >>= 8; + } + return reinterpret_cast(w + cb); +} + +inline long long read_int_le(int cb, const void* r0) +{ + const unsigned char* r = reinterpret_cast(r0); + r += cb; + long long v = 0; + while (cb--) + v = v << 8 | *--r; + return v; +} + +inline long long read_int_le(int cb, const void* r, const void* /*s_end*/) +{ + return read_int_le(cb, r); +} + +template +T write_int_le(int cb, T w0, long long v) +{ + unsigned char* w = reinterpret_cast(w0); + for (int i = 0; i < cb; i++) + { + *w++ = v & 0xff; + v >>= 8; + } + return reinterpret_cast(w); +} diff --git a/misc/stream_reader.cpp b/misc/stream_reader.cpp new file mode 100644 index 0000000..d0d6dfc --- /dev/null +++ b/misc/stream_reader.cpp @@ -0,0 +1,3 @@ +#include "stdafx.h" +#include "stream_reader.h" + diff --git a/misc/stream_reader.h b/misc/stream_reader.h new file mode 100644 index 0000000..dd2e144 --- /dev/null +++ b/misc/stream_reader.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include + +class Cstream_reader +{ +public: + const unsigned char* d() const + { + return m_d; + } + + const unsigned char* d_end() const + { + return m_d.end(); + } + + const unsigned char* r() const + { + return m_r; + } + + const unsigned char* read(int size) + { + m_r += size; + return m_r - size; + } + + long long read_int(int cb) + { + m_r += cb; + return ::read_int(cb, m_r - cb); + } + + Cvirtual_binary read_data() + { + int l = read_int(4); + return Cvirtual_binary(data_ref(read(l), l)); + } + + std::string read_string() + { + int l = read_int(4); + return std::string(reinterpret_cast(read(l)), l); + } + + Cstream_reader() + { + } + + Cstream_reader(const Cvirtual_binary& d) + { + m_r = m_d = d; + } +private: + Cvirtual_binary m_d; + const unsigned char* m_r; +}; diff --git a/misc/stream_writer.cpp b/misc/stream_writer.cpp new file mode 100644 index 0000000..496b9dc --- /dev/null +++ b/misc/stream_writer.cpp @@ -0,0 +1,2 @@ +#include "stdafx.h" +#include "stream_writer.h" diff --git a/misc/stream_writer.h b/misc/stream_writer.h new file mode 100644 index 0000000..761c335 --- /dev/null +++ b/misc/stream_writer.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +class Cstream_writer +{ +public: + unsigned char* w() const + { + return m_w; + } + + unsigned char* write(int size) + { + m_w += size; + return m_w - size; + } + + void write_int(int cb, long long v) + { + m_w = ::write_int(cb, m_w, v); + } + + void write_data(data_ref v) + { + write_int(4, v.size()); + memcpy(write(v.size()), v); + } + + Cstream_writer() + { + } + + Cstream_writer(unsigned char* w) + { + m_w = w; + } +private: + unsigned char* m_w; +}; diff --git a/misc/tf_misc.cpp b/misc/tf_misc.cpp new file mode 100644 index 0000000..3e41a05 --- /dev/null +++ b/misc/tf_misc.cpp @@ -0,0 +1,317 @@ +#include "tf_misc.h" + +#include +#include +#include + +static std::string web_encode(str_ref v) +{ + std::string d; + d.reserve(v.size() << 1); + while (v) + { + switch (v.front()) + { + case '"': + d += """; + break; + case '&': + d += "&"; + break; + case '<': + d += "<"; + break; + default: + d += v.front(); + } + v.pop_front(); + } + return d; +} + +static std::string web_link(str_ref title, str_ref link) +{ + return "" + web_encode(title.empty() ? link : title) + ""; +} + +std::string encode_field(str_ref v, bool add_br) +{ + std::string r; + r.reserve(v.size() << 1); + while (v) + { + if (boost::istarts_with(v, "ftp://") + || boost::istarts_with(v, "http://") + || boost::istarts_with(v, "https://") + || boost::istarts_with(v, "mailto:")) + { + size_t p = 0; + while (p < v.size() && !isspace(v[p] & 0xff) && v[p] != '\"' && v[p] != '<' && v[p] != '>' && v[p] != '[' && v[p] != ']') + p++; + if (v[p - 1] == '!' || v[p - 1] == ',' || v[p - 1] == '.' || v[p - 1] == '?') + p--; + if (v[p - 1] == ')') + p--; + str_ref url = v.substr(0, p); + if (boost::istarts_with(v, "ftp.")) + r += web_link(url, "ftp://" + url.s()); + else if (boost::istarts_with(v, "www.")) + r += web_link(url, "http://" + url.s()); + else + r += web_link(boost::istarts_with(v, "mailto:") ? url.substr(7) : url, url); + while (p--) + v.pop_front(); + } + else + { + switch (v.front()) + { + case '\n': + r += add_br ? "
" : " "; + break; + case '\r': + break; + case '&': + r += "&"; + break; + case '<': + r += "<"; + break; + default: + r += v.front(); + } + v.pop_front(); + } + } + return r; +} + +std::string encode_text(str_ref v, bool add_quote_class) +{ + std::string r; + r.reserve(v.size() << 1); + while (v) + { + str_ref line = read_until(v, '\n'); + r += add_quote_class && boost::istarts_with(line, "> ") ? "" + encode_field(line) + "" : encode_field(line); + r += "
"; + } + return r; +} + +std::string trim_field(const std::string& v) +{ + return boost::find_format_all_copy(boost::trim_copy(v), boost::token_finder(boost::is_space(), boost::token_compress_on), boost::const_formatter(" ")); +} + +std::string trim_text(const std::string& v) +{ + std::string r; + bool copy_white = false; + for (size_t i = 0; i < v.size(); ) + { + size_t p = v.find('\n', i); + if (p == std::string::npos) + p = v.size(); + std::string line = trim_field(v.substr(i, p - i)); + if (line.empty()) + copy_white = true; + else + { + if (copy_white) + { + if (!r.empty()) + r += '\n'; + copy_white = false; + } + r += line + '\n'; + } + i = p + 1; + } + return r; +} + +enum bb_t +{ + bb_literal, + bb_none, + bb_bold, + bb_bold_close, + bb_center, + bb_center_close, + bb_color, + bb_color_close, + bb_quote, + bb_quote_close, + bb_strike, + bb_strike_close, + bb_underline, + bb_underline_close, + bb_url, + bb_unknown, + bb_video, + bb_end, +}; + +static bool operator==(str_ref a, const char* b) +{ + return a.size() == strlen(b) && !memcmp(a.data(), b, a.size()); +} + +bb_t get_next(str_ref& s, str_ref& a0) +{ + if (!s) + return bb_end; + if (s.front() != '[') + { + auto a = std::find(s.begin(), s.end(), '['); + if (a == s.end()) + { + a0 = s; + s.clear(); + } + else + { + a0 = str_ref(s.begin(), a); + s.set_begin(a); + } + return bb_literal; + } + auto a = std::find(s.begin(), s.end(), ']'); + if (a == s.end()) + { + a0 = s; + s.clear(); + return bb_literal; + } + str_ref tag = { &s[1], a }; + s.set_begin(a + 1); + a0.clear(); + if (tag == "b") + return bb_bold; + if (tag == "/b") + return bb_bold_close; + if (tag == "center") + return bb_center; + if (tag == "/center") + return bb_center_close; + if (boost::starts_with(tag, "color=")) + { + a0 = tag.substr(6); + return bb_color; + } + if (tag == "/color") + return bb_color_close; + if (boost::starts_with(tag, "font=") || tag == "/font") + return bb_none; + if (tag == "i" || tag == "/i") + return bb_none; + if (tag == "img" || tag == "IMG" || tag == "/img" || tag == "/IMG") + return bb_none; + if (boost::starts_with(tag, "img=")) + { + a0 = tag.substr(4); + return bb_literal; + } + if (tag == "q" || tag == "quote") + return bb_quote; + if (boost::starts_with(tag, "quote=")) + { + a0 = tag.substr(6); + return bb_quote; + } + if (tag == "/q" || tag == "/quote") + return bb_quote_close; + if (tag == "s") + return bb_strike; + if (tag == "/s") + return bb_strike_close; + if (boost::starts_with(tag, "size=") || tag == "/size") + return bb_none; + if (tag == "u") + return bb_underline; + if (tag == "/u") + return bb_underline_close; + if (boost::starts_with(tag, "url=")) + { + a0 = tag.substr(4); + return bb_url; + } + if (tag == "/url") + return bb_none; + if (boost::starts_with(tag, "video=")) + { + a0 = tag.substr(6); + return bb_video; + } + a0 = tag; + return bb_unknown; +} + +std::string bbformat(str_ref s) +{ + std::string d; + str_ref a0; + while (1) + { + switch (get_next(s, a0)) + { + case bb_literal: + d += encode_field(a0, true); + break; + case bb_none: + break; + case bb_bold: + d += ""; + break; + case bb_bold_close: + d += ""; + break; + case bb_center: + d += "
"; + break; + case bb_center_close: + d += "
"; + break; + case bb_color: + d += ""; // escape a0 + break; + case bb_color_close: + d += ""; + break; + case bb_quote: + d += "
"; + if (a0) + d += "" + encode_field(a0) + " wrote:"; + break; + case bb_quote_close: + d += "
"; + break; + case bb_strike: + d += ""; + break; + case bb_strike_close: + d += ""; + break; + case bb_underline: + d += ""; + break; + case bb_underline_close: + d += ""; + break; + case bb_url: + d += encode_field(a0, true) + " "; + break; + case bb_video: + d += encode_field(a0, true) + " "; + break; + case bb_unknown: + d += "[" + encode_field(a0) + "]"; + break; + case bb_end: + return d; + default: + assert(false); + } + } +} diff --git a/misc/tf_misc.h b/misc/tf_misc.h new file mode 100644 index 0000000..d260227 --- /dev/null +++ b/misc/tf_misc.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include + +std::string bbformat(str_ref); +std::string encode_field(str_ref, bool add_br = false); +std::string encode_text(str_ref, bool add_quote_class); +std::string trim_field(const std::string&); +std::string trim_text(const std::string&); diff --git a/misc/virtual_binary.cpp b/misc/virtual_binary.cpp new file mode 100644 index 0000000..faf47d8 --- /dev/null +++ b/misc/virtual_binary.cpp @@ -0,0 +1,24 @@ +#include "xbt/virtual_binary.h" + +#include +#include +#include + +void Cvirtual_binary::assign(data_ref v) +{ + if (v.size()) + { + m_source = boost::make_shared(v.size()); + if (v.begin()) + memcpy(data_edit(), v.data(), v.size()); + } + else + m_source.reset(); +} + +unsigned char* Cvirtual_binary::write_start(size_t cb_d) +{ + if (size() != cb_d) + assign(cb_d); + return data_edit(); +} diff --git a/misc/windows/.svn/all-wcprops b/misc/windows/.svn/all-wcprops new file mode 100644 index 0000000..50dacf0 --- /dev/null +++ b/misc/windows/.svn/all-wcprops @@ -0,0 +1,41 @@ +K 25 +svn:wc:ra_dav:version-url +V 41 +/svn/!svn/ver/2355/trunk/xbt/misc/windows +END +browse_for_directory.h +K 25 +svn:wc:ra_dav:version-url +V 64 +/svn/!svn/ver/2020/trunk/xbt/misc/windows/browse_for_directory.h +END +nt_service.cpp +K 25 +svn:wc:ra_dav:version-url +V 56 +/svn/!svn/ver/2355/trunk/xbt/misc/windows/nt_service.cpp +END +nt_service.h +K 25 +svn:wc:ra_dav:version-url +V 54 +/svn/!svn/ver/2020/trunk/xbt/misc/windows/nt_service.h +END +ETSLayout.cpp +K 25 +svn:wc:ra_dav:version-url +V 55 +/svn/!svn/ver/2020/trunk/xbt/misc/windows/ETSLayout.cpp +END +browse_for_directory.cpp +K 25 +svn:wc:ra_dav:version-url +V 66 +/svn/!svn/ver/2020/trunk/xbt/misc/windows/browse_for_directory.cpp +END +ETSLayout.h +K 25 +svn:wc:ra_dav:version-url +V 53 +/svn/!svn/ver/2020/trunk/xbt/misc/windows/ETSLayout.h +END diff --git a/misc/windows/.svn/entries b/misc/windows/.svn/entries new file mode 100644 index 0000000..ce5d4ea --- /dev/null +++ b/misc/windows/.svn/entries @@ -0,0 +1,232 @@ +10 + +dir +2494 +http://xbt.googlecode.com/svn/trunk/xbt/misc/windows +http://xbt.googlecode.com/svn + + + +2012-07-06T12:26:16.237752Z +2355 +olafvdspek + + + + + + + + + + + + + + +a4ef9278-c6d2-effe-6dce-f9b4ebbbbc1a + +ETSLayout.cpp +file + + + + +2015-07-14T06:50:40.848095Z +881d6312289a1e1bb362db623ee2a456 +2010-04-15T16:24:59.778639Z +2020 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +83652 + +browse_for_directory.cpp +file + + + + +2015-07-14T06:50:40.848095Z +718b50ff98e882afb1f6cd69ad7f8e2c +2010-04-15T16:24:59.778639Z +2020 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +844 + +ETSLayout.h +file + + + + +2015-07-14T06:50:40.848095Z +edd57d288c38db7938fa1ac2b93fe161 +2010-04-15T16:24:59.778639Z +2020 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +26966 + +browse_for_directory.h +file + + + + +2015-07-14T06:50:40.848095Z +a5c486f9156fce7d7b1373269f49ac38 +2010-04-15T16:24:59.778639Z +2020 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +96 + +nt_service.cpp +file + + + + +2015-07-14T06:50:40.848095Z +85ff1f532081a66acf2c9b065ae91846 +2012-07-06T12:26:16.237752Z +2355 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +1214 + +nt_service.h +file + + + + +2015-07-14T06:50:40.848095Z +3aa8ca2bce397aabe640275486b17fe0 +2010-04-15T16:24:59.778639Z +2020 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +100 + diff --git a/misc/windows/.svn/prop-base/ETSLayout.cpp.svn-base b/misc/windows/.svn/prop-base/ETSLayout.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/windows/.svn/prop-base/ETSLayout.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/windows/.svn/prop-base/ETSLayout.h.svn-base b/misc/windows/.svn/prop-base/ETSLayout.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/windows/.svn/prop-base/ETSLayout.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/windows/.svn/prop-base/browse_for_directory.cpp.svn-base b/misc/windows/.svn/prop-base/browse_for_directory.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/windows/.svn/prop-base/browse_for_directory.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/windows/.svn/prop-base/browse_for_directory.h.svn-base b/misc/windows/.svn/prop-base/browse_for_directory.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/windows/.svn/prop-base/browse_for_directory.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/windows/.svn/prop-base/nt_service.cpp.svn-base b/misc/windows/.svn/prop-base/nt_service.cpp.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/windows/.svn/prop-base/nt_service.cpp.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/windows/.svn/prop-base/nt_service.h.svn-base b/misc/windows/.svn/prop-base/nt_service.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/windows/.svn/prop-base/nt_service.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/windows/.svn/text-base/ETSLayout.cpp.svn-base b/misc/windows/.svn/text-base/ETSLayout.cpp.svn-base new file mode 100644 index 0000000..e68bdad --- /dev/null +++ b/misc/windows/.svn/text-base/ETSLayout.cpp.svn-base @@ -0,0 +1,3058 @@ +//////////////////////////////////////////// +// ___ ____ _________________ // +// / _/_ _// _______________/ // +// / _/ / / / / ___ ___ ____ // +// /__/ /_/ / / / // _/_ _/ // +// _________/ / / / // _/ / / // +// (c) 1998-2000_/ /___//_/ /_/ // +// // +//////////////////////////////////////////// +// all rights reserved // +//////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// ETSLayoutDialog +// +// A class for smart layouting of Dialogs and such +// +// USAGE: See LayoutMgr.html +// +// AUTHOR: Erwin Tratar +// +// DISCLAIMER: +// +// This Sourcecode and all accompaning material is ©1998-1999 Erwin Tratar. +// All rights reserved. +// +// The source code may be used in compiled form in any way you desire +// (including usage in commercial applications), providing that your +// application adds essential code (i.e. it is not only a wrapper) to the +// functionality found here +// +// Redistribution of the sourcecode itself, publication in any media or +// inclusion in a library requires the authors expressed written consent. +// You may not sale this code for profit. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. USE IT +// AT YOUR OWN RISK! THE AUTHOR ACCEPTS NO LIABILITY FOR ANY DAMAGE/LOSS OF +// BUSINESS THAT THIS PRODUCT MAY CAUSE. +// +// +// HISTORY: +// 1998/05/1 Initial Release +// 1998/05/13 Added ability to have a Pane with a control +// 1998/05/13 Added better support for TabControls +// 1998/05/14 automatically set Icon to IDR_MAINFRAME +// 1998/05/19 no flicker on restoring position in OnInitialUpdate +// Changed procedure for load/save, see constructor +// 1998/10/02 Added support for Maximum (tracking) size +// 1998/10/02 Much improved handling regarding RELATIVE/GREEDY +// /w critical minimum size +// 1998/10/02 turn on/off gripper at lower right corner +// 1998/10/05 Support for user defined minimum size for items +// (was hardcoded 5 before) +// 1998/10/07 Fix for FormViews +// 1998/10/31 Support for SECDialogBar/CDialogBar +// 1998/10/31 simplified interface +// 1998/10/31 Advanced positioning options +// 1998/10/31 Added paneNull for empty Pane (former: NULL) +// 1998/11/20 Swapped ETSLayoutDialog constructor parameters +// 1998/11/20 Added Pane::addItemSpaceBetween +// [Leo Zelevinsky] +// 1998/11/24 Added fixup for greedy panes +// 1998/11/24 addItemSpaceBetween now subtracts 2*nDefaultBorder +// 1998/11/24 addGrowing() added as a shortcut for a paneNull +// 1998/11/24 simplified interface: no more PaneBase:: / Pane:: +// needed +// 1998/11/24 added FILL_* Modes +// 1998/11/24 improved maximum size handling for greedy panes +// 1998/11/25 Fixup of greedy panes caused infinite loop in some +// cases +// 1999/01/07 addItemSpaceLike() added +// 1999/04/03 Fixed ETSLayoutFormView memory leak +// 1999/04/07 Fixed ALIGN_xCENTER +// 1999/04/08 New simple stream-interface added +// 1999/04/09 Added support for an empty Status-Bar for resizing +// instead of a gripper in the lower right corner +// [Andreas Kapust] +// 1999/04/11 New code for much less flickering, OnEraseBkgnd() +// overidden for this task +// 1999/05/12 Split Layout code into understandable pieces and adding +// a lot of comments +// 1999/06/20 ABSOLUTE_X + ALIGN_FILL_X expands item if there is any +// left space (after all Abs/Rel/Greedy processing is done) +// 1999/10/06 Changed Load() and Save() to use WINDOWPLACEMENT +// [Keith Bussell] +// 1999/11/18 Added possibility to add panes of the same orientation +// to another pane. This merges both panes in one big +// pane with the same orientation +// 1999/11/18 Added support for BCGDialogBar (only with BCG > 4.52!) +// 1999/11/25 Addes support for PropertyPages/Sheets. Uses some code +// of a code submission from Anreas Kapust +// 1999/11/25 Renamed classes to ETSLayoutXXX +// 1999/11/25 Use CreateRoot() and Root() instead of m_pRootPane in +// derived class. +// 1999/11/26 Added autopointer support. No need to use normal pointers +// when defining layout anymore. Changed m_pRootPane to +// m_RootPane +// 1999/11/26 Bug in Fixup Greedy II with multiple GREEDY panes and one +// of them min/max limited +// 1999/11/28 Fixed PaneTab::getConstrainVert() for ABSOLUTE_VERT +// 1999/11/28 Fixed itemFixed() +// 1999/11/28 Changed DWORD modeResize Arguments to layModeResize for +// better type safety. Added typesafe operator| +// 1999/12/04 Don't reposition window in UpdateLayout if it's a child +// (as a child Dialog or PropertyPage) +// 1999/12/04 Erase Backgroung with GCL_HBRBACKGROUND (if available) +// 1999/12/04 itemSpaceXXX() adds a NORESIZE item instead of ABSOLUTE_XXX +// this will fix unwanted growing in secondary direction +// +// Version: 1.0 [1999/12/04] Initial Article on CodeProject +// +// 1999/12/10 Erase Backgroung within TabCtrl was 'fixed' badly. Reverted to +// old working code +// 2000/02/02 When the Dialog is child of a View the class works correctly +// now [Didier BULTIAUW] +// 2000/02/15 Combo-Boxes were not working correctly (in all modes!) +// 2000/02/17 aligned SpinButton Controls (with buddy) now handled +// automatically +// !! do not add such a control to the layout !! it is always +// reattached to its buddy. +// 2000/02/17 changed some cotrol class names to the defined constants +// +// Version: 1.1 [2000/02/17] +// +// 2000/02/25 Fixed auto alignment of SpinButton Controls to only affect +// visible ones +// 2000/02/27 Put all the classes into the namespace 'ETSLayout' +// 2000/03/07 Fixed growing Dialog after minimizing and restoring +// 2000/05/22 Whole Statusbar (Gripper) is not excluded anymore in EraseBkgnd() +// instead only the triangular Gripper is excluded +// 2000/05/31 Fix for PropertySheets with PSH_WIZARDHASFINISH [Thömmi] +// 2000/05/31 Fix for UpDown-Controls with EditCtrl Buddy in PropertyPages. +// These were not repositioned every time the page is being show +// until the first resize +// 2000/07/28 Problems with resizing ActiveX Controls fixed [Micheal Chapman] +// 2000/07/28 Some strings were not properly wrapped with _T() +// 2000/08/03 Check for BS_GROUPBOX was not correct as BS_GROUPBOX is more than one Bit +// 2000/08/03 New override AddMainArea added to ETSLayoutPropertySheet in order to +// have a hook for additional controls in a PropertySheet (besides the Tab) +// 2000/08/03 New override AddButtons added to ETSLayoutPropertySheet in order to +// have a hook for additional controls in the bottem pane of a PropertySheet +// 2000/08/03 Removed the need for DECLARE_LAYOUT +// +// Version: 1.2 [2000/08/05] + +#define OEMRESOURCE +#include + +#include "stdafx.h" +#include "ETSLayout.h" + +using namespace ETSLayout; +#pragma warning(disable: 4097 4610 4510 4100) + + +#ifndef OBM_SIZE +#define OBM_SIZE 32766 +// taken from WinresRc.h +// if not used for any reason +#endif + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +static UINT auIDStatusBar[] = +{ + ID_SEPARATOR +}; + +const int ERASE_GROUP_BORDER = 10; +const int FIXUP_CUTOFF = 5; +const int TAB_SPACE = 5; + +// the _NULL-Pane +CWnd* ETSLayoutMgr::paneNull = 0; + +void ETSLayoutMgr::Layout(CRect& rcClient) +{ + if(rcClient.Height() && rcClient.Width() && m_RootPane.IsValid()) \ + m_RootPane->resizeTo(rcClient); \ +} + + +ETSLayoutMgr::CPane ETSLayoutMgr::pane( layOrientation orientation, ETSLayoutMgr::layResizeMode modeResize /*=GREEDY*/, + int sizeBorder /*=nDefaultBorder*/, int sizeExtraBorder /*=0*/, + int sizeSecondary /*=0*/) +{ + Pane* pPane = new Pane ( this, orientation, sizeBorder, sizeExtraBorder ); + pPane->m_sizeSecondary = sizeSecondary; + pPane->m_modeResize = modeResize; + + return CPane(pPane); +} + +ETSLayoutMgr::CPane ETSLayoutMgr::paneTab( CTabCtrl* pTab, layOrientation orientation, + ETSLayoutMgr::layResizeMode modeResize /*=GREEDY*/, int sizeBorder /*=nDefaultBorder*/, + int sizeExtraBorder /*=0*/, int sizeSecondary /*=0*/) +{ + Pane* pPane = new PaneTab ( pTab, this, orientation, sizeBorder, sizeExtraBorder ); + pPane->m_sizeSecondary = sizeSecondary; + pPane->m_modeResize = modeResize; + + return CPane(pPane); +} + + +ETSLayoutMgr::CPane ETSLayoutMgr::paneCtrl( CWnd* pCtrl, layOrientation orientation, + ETSLayoutMgr::layResizeMode modeResize /*=GREEDY*/, int sizeBorder /*=nDefaultBorder*/, + int sizeExtraBorder /*=0*/, int sizeTopExtra /*=0*/, + int sizeSecondary /*=0*/) +{ + Pane* pPane = new PaneCtrl ( pCtrl, this, orientation, sizeBorder, sizeExtraBorder, sizeTopExtra ); + pPane->m_sizeSecondary = sizeSecondary; + pPane->m_modeResize = modeResize; + + return CPane(pPane); +} + +ETSLayoutMgr::CPane ETSLayoutMgr::paneCtrl( UINT nID, layOrientation orientation, ETSLayoutMgr::layResizeMode modeResize /*=GREEDY*/, + int sizeBorder /*=nDefaultBorder*/, int sizeExtraBorder /*=0*/, + int sizeTopExtra /*=0*/, int sizeSecondary /*=0*/) +{ + Pane* pPane = new PaneCtrl ( nID, this, orientation, sizeBorder, sizeExtraBorder, sizeTopExtra ); + pPane->m_sizeSecondary = sizeSecondary; + pPane->m_modeResize = modeResize; + + return CPane(pPane); +} + + +ETSLayoutMgr::CPaneBase ETSLayoutMgr::item(UINT nID, ETSLayoutMgr::layResizeMode modeResize /*=GREEDY*/, int sizeX /*=0*/, int sizeY /*=0*/, + int sizeXMin /*=-1*/, int sizeYMin /*=-1*/) +{ + return new PaneItem( nID, this, modeResize, sizeX, sizeY, sizeXMin, sizeYMin); +} + +ETSLayoutMgr::CPaneBase ETSLayoutMgr::item(CWnd* pWnd, ETSLayoutMgr::layResizeMode modeResize /*=GREEDY*/, + int sizeX /*=0*/, int sizeY /*=0*/, int sizeXMin /*=-1*/, + int sizeYMin /*=-1*/) +{ + return new PaneItem( pWnd, this, modeResize, sizeX, sizeY, sizeXMin, sizeYMin); +} + +ETSLayoutMgr::CPaneBase ETSLayoutMgr::itemFixed(layOrientation orientation, int sizePrimary) +{ + CPaneBase p = new PaneItem(paneNull, this, NORESIZE, (orientation==HORIZONTAL)?sizePrimary:0, (orientation==VERTICAL)?sizePrimary:0); + return p; +} + +ETSLayoutMgr::CPaneBase ETSLayoutMgr::itemGrowing(layOrientation orientation) +{ + return new PaneItem(paneNull, this, (orientation==HORIZONTAL)?ABSOLUTE_VERT:ABSOLUTE_HORZ, 0, 0, -nDefaultBorder, -nDefaultBorder); +} + +ETSLayoutMgr::CPaneBase ETSLayoutMgr::itemSpaceBetween( layOrientation orientation, CWnd* pWndFirst, CWnd* pWndSecond ) +{ + if( orientation == HORIZONTAL ) { + // I'm interested in horizontal spacing + + CRect rLeft, rRight; + pWndFirst->GetWindowRect(&rLeft); + pWndSecond->GetWindowRect(&rRight); + + int sizeX = rRight.left - rLeft.right; + + if( sizeX < 0 ) { + // compare top to top + sizeX = rRight.left - rLeft.left; + } + else { + sizeX -= 2*nDefaultBorder; + } + + return new PaneItem(paneNull, this, NORESIZE, sizeX, 0); + } + else { + // I'm interested in vertical spacing + CRect rTop, rBot; + pWndFirst->GetWindowRect(&rTop); + pWndSecond->GetWindowRect(&rBot); + + int sizeY = rBot.top - rTop.bottom; + + if( sizeY < 0 ) { + // compare top to top + sizeY = sizeY = rBot.top - rTop.top; + } + else { + sizeY -= 2*nDefaultBorder; + } + + return new PaneItem(paneNull, this, NORESIZE, 0, sizeY); + } +} + + +ETSLayoutMgr::CPaneBase ETSLayoutMgr::itemSpaceBetween( layOrientation orientation, UINT nIDFirst, UINT nIDSecond ) +{ + CWnd *pFirst = GetWnd()->GetDlgItem(nIDFirst); + CWnd *pSecond = GetWnd()->GetDlgItem(nIDSecond); + + ASSERT( pFirst && pSecond ); + + return itemSpaceBetween( orientation, pFirst, pSecond ); +} + + +ETSLayoutMgr::CPaneBase ETSLayoutMgr::itemSpaceLike( layOrientation orientation, CWnd* pWnd ) +{ + CRect rRect; + pWnd->GetWindowRect(&rRect); + + if( orientation == HORIZONTAL ) { + // I'm interested in horizontal spacing + return new PaneItem(paneNull, this, NORESIZE, rRect.Width(), 0); + } + else { + // I'm interested in vertical spacing + return new PaneItem(paneNull, this, NORESIZE, 0, rRect.Height() ); + } + +} + + +ETSLayoutMgr::CPaneBase ETSLayoutMgr::itemSpaceLike( layOrientation orientation, UINT nID ) +{ + CWnd *pWnd = GetWnd()->GetDlgItem(nID); + ASSERT( pWnd ); + + return itemSpaceLike( orientation, pWnd ); +} + + + +ETSLayoutMgr::~ETSLayoutMgr() +{ +} + +void ETSLayoutMgr::UpdateLayout() +{ + if(!m_RootPane) + return; + + // Check constraints + CRect rcClient = GetRect(); + + if( m_pWnd->IsKindOf( RUNTIME_CLASS( CDialog ) ) && !(m_pWnd->GetStyle()&WS_CHILD) ) { + CRect rcWindow; + m_pWnd->GetWindowRect(rcWindow); + + // Added by Didier BULTIAUW + CWnd* parentWnd = m_pWnd->GetParent(); + if( (parentWnd != 0) && parentWnd->IsKindOf(RUNTIME_CLASS(CView)) ) + { + CRect rcParent; + parentWnd->GetWindowRect(rcParent); + rcWindow.OffsetRect(-rcParent.left,-rcParent.top); + } + // end add + + CRect rcBorder = rcWindow; + rcBorder -= rcClient; + + // Min and Max info + int minWidth = m_RootPane->getMinConstrainHorz() + rcBorder.Width() + 2*m_sizeRootBorders.cx; + int minHeight = m_RootPane->getMinConstrainVert() + rcBorder.Height() + 2*m_sizeRootBorders.cy; + int maxWidth = m_RootPane->getMaxConstrainHorz(); + if(maxWidth != -1) { + maxWidth += rcBorder.Width() + 2*m_sizeRootBorders.cx; + maxWidth = max(maxWidth, minWidth); + } + int maxHeight = m_RootPane->getMaxConstrainVert(); + if(maxHeight != -1) { + maxHeight += rcBorder.Height() + 2*m_sizeRootBorders.cy; + maxHeight = max(maxHeight, minHeight); + } + + if(rcWindow.Width() < minWidth) + rcWindow.right = rcWindow.left + minWidth; + if(rcWindow.Height() < minHeight) + rcWindow.bottom = rcWindow.top + minHeight; + + if(maxWidth != -1 && rcWindow.Width() > maxWidth) + rcWindow.right = rcWindow.left + maxWidth; + if(maxHeight != -1 && rcWindow.Height() > maxHeight) + rcWindow.bottom = rcWindow.top + maxHeight; + + m_pWnd->MoveWindow(rcWindow); + } + // Do the Layout + rcClient = GetRect(); + + // Add a Border around the rootPane + rcClient.top += m_sizeRootBorders.cy; + rcClient.bottom -= m_sizeRootBorders.cy; + rcClient.left += m_sizeRootBorders.cx; + rcClient.right -= m_sizeRootBorders.cx; + + if(GetWnd()->IsWindowVisible()) { + // Avoid ugly artifacts + //GetWnd()->SetRedraw(FALSE); + Layout(rcClient); + //GetWnd()->SetRedraw(TRUE); + } + else + Layout(rcClient); + + // Take special care of SpinButtons (Up-Down Controls) with Buddy set, enumerate + // all childs: + CWnd* pWndChild = GetWnd()->GetWindow(GW_CHILD); + TCHAR szClassName[ MAX_PATH ]; + while(pWndChild) + { + ::GetClassName( pWndChild->GetSafeHwnd(), szClassName, MAX_PATH ); + DWORD dwStyle = pWndChild->GetStyle(); + + // is it a SpinButton? + if( _tcscmp(szClassName, UPDOWN_CLASS)==0 && ::IsWindowVisible(pWndChild->GetSafeHwnd()) ) { + HWND hwndBuddy = (HWND)::SendMessage( pWndChild->GetSafeHwnd(), UDM_GETBUDDY, 0, 0); + if( hwndBuddy != 0 && (dwStyle&(UDS_ALIGNRIGHT|UDS_ALIGNLEFT)) != 0 ) + { + // reset Buddy + ::SendMessage( pWndChild->GetSafeHwnd(), UDM_SETBUDDY, (WPARAM)hwndBuddy, 0); + } + } + + + pWndChild = pWndChild->GetWindow(GW_HWNDNEXT); + } + + + GetWnd()->Invalidate(); +} + + +bool ETSLayoutMgr::Save(LPCTSTR lpstrRegKey) +{ + CRect rcWnd; + + if(IsWindow(GetWnd()->m_hWnd)) + { + WINDOWPLACEMENT wp; + if(GetWnd()->GetWindowPlacement(&wp)) + { + // Make sure we don't pop up + // minimized the next time + if(wp.showCmd != SW_SHOWMAXIMIZED) + wp.showCmd = SW_SHOWNORMAL; + + AfxGetApp()->WriteProfileBinary(lpstrRegKey, + _T("WindowPlacement"), + reinterpret_cast(&wp), sizeof(wp)); + } + } + return true; +} + +bool ETSLayoutMgr::Load(LPCTSTR lpstrRegKey) +{ + LPBYTE pbtData = 0; + UINT nSize = 0; + if(AfxGetApp()->GetProfileBinary(lpstrRegKey, + _T("WindowPlacement"), &pbtData, &nSize)) + { + WINDOWPLACEMENT* pwp = + reinterpret_cast(pbtData); + + ASSERT(nSize == sizeof(WINDOWPLACEMENT)); + if(nSize == sizeof(WINDOWPLACEMENT)) + GetWnd()->SetWindowPlacement(reinterpret_cast(pbtData)); + + delete [] pbtData; + } + return true; +} + + +void ETSLayoutMgr::EraseBkgnd(CDC* pDC) +{ + CRect rcClient; + GetWnd()->GetClientRect( rcClient ); + + CRgn rgn; + rgn.CreateRectRgnIndirect(rcClient); + TRACE("CreateRgn (%d,%d,%d,%d)\n", rcClient.left, rcClient.top, rcClient.right, rcClient.bottom ); + + CRgn rgnRect; + rgnRect.CreateRectRgn(0,0,0,0); + + CRect rcChild; + CWnd* pWndChild = GetWnd()->GetWindow( GW_CHILD ); + + TCHAR szClassName[ MAX_PATH ]; + + pDC->SelectClipRgn(NULL); + + while( pWndChild ) { + + pWndChild->GetWindowRect(rcChild); + GetWnd()->ScreenToClient( rcChild ); + + rgnRect.SetRectRgn( rcChild ); + + ::GetClassName( pWndChild->GetSafeHwnd(), szClassName, MAX_PATH ); + DWORD dwStyle = pWndChild->GetStyle(); + + // doesn't make sense for hidden children + if( dwStyle & WS_VISIBLE ) { + + // Fix: BS_GROUPBOX is more than one Bit, extend check to (dwStyle & BS_GROUPBOX)==BS_GROUPBOX [ET] + if( _tcscmp(szClassName,_T("Button"))==0 && (dwStyle & BS_GROUPBOX)==BS_GROUPBOX ) { + // it is a group-box, ignore completely + } + else if( _tcscmp(szClassName,WC_TABCONTROL )==0 ) { + // ignore Tab-Control's inside rect + static_cast(pWndChild)->AdjustRect(FALSE,rcChild); + + CRgn rgnContent; + rgnContent.CreateRectRgnIndirect(rcChild); + + rgnRect.CombineRgn( &rgnRect, &rgnContent, RGN_DIFF ); + rgn.CombineRgn( &rgn, &rgnRect, RGN_DIFF ); + } + else if( _tcscmp(szClassName,STATUSCLASSNAME)==0 ) { + + CPoint ptTriangleGrip[3]; + ptTriangleGrip[0] = CPoint(rcChild.right,rcChild.top); + ptTriangleGrip[1] = CPoint(rcChild.right,rcChild.bottom); + ptTriangleGrip[2] = CPoint(rcChild.right-rcChild.Height(),rcChild.bottom); + + CRgn rgnGripper; + rgnGripper.CreatePolygonRgn(ptTriangleGrip,3, WINDING); + + rgn.CombineRgn( &rgn, &rgnGripper, RGN_DIFF ); + + } + else { + rgn.CombineRgn( &rgn, &rgnRect, RGN_DIFF ); + } + } + + pWndChild = pWndChild->GetNextWindow(); + } + + + HBRUSH hBrBack = (HBRUSH) ::GetClassLong(GetWnd()->GetSafeHwnd(), GCL_HBRBACKGROUND) ; + if( hBrBack == 0 ) + hBrBack = ::GetSysColorBrush(COLOR_BTNFACE); + + pDC->FillRgn( &rgn, + CBrush::FromHandle( hBrBack ) + ); + +} + +///////////////////////////////////////////////////////////////////////////// +// ETSLayoutMgr::PaneItem implementation + + +ETSLayoutMgr::PaneItem::PaneItem(CWnd* pWnd, ETSLayoutMgr* pMgr, ETSLayoutMgr::layResizeMode modeResize/*=GREEDY*/ + , int sizeX/*=0*/, int sizeY/*=0*/ + , int sizeXMin/*=-1*/, int sizeYMin/*=-1*/ ) : PaneBase( pMgr ) +{ + m_modeResize = modeResize; + m_hwndCtrl = pWnd->GetSafeHwnd(); + + m_sizeX = 0; + m_sizeY = 0; + + m_bComboSpecial = false; + + m_sizeXMin = sizeXMin; + m_sizeYMin = sizeYMin; + + if(!m_hwndCtrl) { // only Dummy! + m_sizeX = sizeX; + m_sizeY = sizeY; + } + else { + CRect rcControl; + ::GetWindowRect(m_hwndCtrl, &rcControl); + + if(sizeX == 0) { + m_sizeX = rcControl.Width(); + } + else { + m_sizeX = sizeX; + } + if( m_sizeXMin == -1 ) { + // do not make smaller than current size + m_sizeXMin = rcControl.Width(); + } + + if(sizeY == 0) { + m_sizeY = rcControl.Height(); + } + else { + m_sizeY = sizeY; + } + if( m_sizeYMin == -1 ) { + // do not make smaller than current size + m_sizeYMin = rcControl.Height(); + } + + TCHAR szClassName[ MAX_PATH ]; + ::GetClassName( m_hwndCtrl, szClassName, MAX_PATH ); + + // special treatment for combo-boxes + if( _tcscmp(szClassName,_T("ComboBox"))==0 || _tcscmp(szClassName,WC_COMBOBOXEX)==0) { + m_bComboSpecial = true; + } + } +} + +ETSLayoutMgr::PaneItem::PaneItem( UINT nID, ETSLayoutMgr* pMgr, ETSLayoutMgr::layResizeMode modeResize/*=GREEDY*/ + , int sizeX/*=0*/, int sizeY/*=0*/ + , int sizeXMin/*=-1*/, int sizeYMin/*=-1*/ ) : PaneBase( pMgr ) +{ + CWnd* pWnd = pMgr->GetWnd()->GetDlgItem(nID); + m_hwndCtrl = pWnd->GetSafeHwnd(); + + m_sizeX = 0; + m_sizeY = 0; + + m_bComboSpecial = false; + + m_modeResize = modeResize; + + m_sizeXMin = sizeXMin; + m_sizeYMin = sizeYMin; + + if(!m_hwndCtrl) { // only Dummy! + m_sizeX = sizeX; + m_sizeY = sizeY; + } + else { + CRect rcControl; + ::GetWindowRect(m_hwndCtrl, &rcControl); + + if(sizeX == 0) { + m_sizeX = rcControl.Width(); + } + else { + m_sizeX = sizeX; + } + if( m_sizeXMin == -1 ) { + // do not make smaller than current size + m_sizeXMin = rcControl.Width(); + } + + if(sizeY == 0) { + m_sizeY = rcControl.Height(); + } + else { + m_sizeY = sizeY; + } + if( m_sizeYMin == -1 ) { + // do not make smaller than current size + m_sizeYMin = rcControl.Height(); + } + + TCHAR szClassName[ MAX_PATH ]; + ::GetClassName( m_hwndCtrl, szClassName, MAX_PATH ); + + // special treatment for combo-boxes + if( _tcscmp(szClassName,_T("ComboBox"))==0 || _tcscmp(szClassName,WC_COMBOBOXEX)==0) { + m_bComboSpecial = true; + } + } +} + +int ETSLayoutMgr::PaneItem::getConstrainHorz(int sizeParent) +{ + if( m_modeResize & ABSOLUTE_HORZ) { + return m_sizeX; + } + if(m_modeResize & RELATIVE_HORZ) { + return (sizeParent * m_sizeX) / 100; + } + return -1; +} + +int ETSLayoutMgr::PaneItem::getConstrainVert(int sizeParent) +{ + if(m_modeResize & ABSOLUTE_VERT) { + return m_sizeY; + } + if(m_modeResize & RELATIVE_VERT) { + return (sizeParent * m_sizeY) / 100; + } + return -1; +} + +int ETSLayoutMgr::PaneItem::getMinConstrainHorz() +{ + if(m_modeResize & ABSOLUTE_HORZ) { + return m_sizeX; + } + return max(nMinConstrain,m_sizeXMin); +} + +int ETSLayoutMgr::PaneItem::getMinConstrainVert() +{ + if(m_modeResize & ABSOLUTE_VERT) { + return m_sizeY; + } + return max(nMinConstrain,m_sizeYMin); +} + +int ETSLayoutMgr::PaneItem::getMaxConstrainHorz() +{ + if(m_modeResize & ABSOLUTE_HORZ) { + return m_sizeX; + } + return -1; +} + +int ETSLayoutMgr::PaneItem::getMaxConstrainVert() +{ + if(m_modeResize & ABSOLUTE_VERT) { + return m_sizeY; + } + return -1; +} + +bool ETSLayoutMgr::PaneItem::resizeTo(CRect& rcNewArea) +{ + if(m_hwndCtrl) { + + CRect rcWnd; + ::GetWindowRect( m_hwndCtrl, rcWnd ); + + if( !(m_modeResize & ALIGN_FILL_HORZ) && m_modeResize & ABSOLUTE_HORZ ) { + + + if( (m_modeResize & ALIGN_HCENTER) == ALIGN_HCENTER ) { + rcNewArea.OffsetRect( (rcNewArea.Width() - rcWnd.Width())/2, 0 ); + } + else if( m_modeResize & ALIGN_RIGHT ) { + rcNewArea.OffsetRect( rcNewArea.Width() - rcWnd.Width(), 0 ); + } + + rcNewArea.right = rcNewArea.left + rcWnd.Width(); + } + if( !(m_modeResize & ALIGN_FILL_VERT) && m_modeResize & ABSOLUTE_VERT ) { + + + if( (m_modeResize & ALIGN_VCENTER) == ALIGN_VCENTER ) { + rcNewArea.OffsetRect( 0, (rcNewArea.Height()-rcWnd.Height())/2 ); + } + else if( m_modeResize & ALIGN_BOTTOM ) { + rcNewArea.OffsetRect( 0, rcNewArea.Height() - rcWnd.Height()); + } + + rcNewArea.bottom = rcNewArea.top + rcWnd.Height(); + + } + + DWORD dwStyle = ::GetWindowLong( m_hwndCtrl, GWL_STYLE ); + + // special treatment for combo-boxes + if( m_bComboSpecial && (dwStyle & CBS_DROPDOWN) ) { + // keep height (though only fully visible when dropped down) + rcNewArea.bottom = rcNewArea.top + rcWnd.Height(); + } + + // FIX: ::MoveWindow would case problems with some ActiveX Controls [Micheal Chapman] + CWnd* pTempWnd = CWnd::FromHandle( m_hwndCtrl ); + pTempWnd->MoveWindow( rcNewArea.left, rcNewArea.top, rcNewArea.Width(), rcNewArea.Height() ); + + if( m_bComboSpecial && !(dwStyle & CBS_DROPDOWN) && !(dwStyle & CBS_NOINTEGRALHEIGHT) ) { + + // Keep CB Size = Edit + LB ( if not CBS_NOINTEGRALHEIGHT) + + ::GetWindowRect( m_hwndCtrl, rcWnd ); + + CRect rcListBox; + HWND hwndListBox = ::GetDlgItem(m_hwndCtrl, 1000); // ListBox of CB + if( hwndListBox != 0 ) + { + ::GetWindowRect( hwndListBox, rcListBox ); + rcWnd.bottom = rcListBox.bottom; + + rcNewArea.bottom = rcNewArea.top + rcWnd.Height(); + + // FIX: ::MoveWindow would case problems with some ActiveX Controls [Micheal Chapman] + CWnd* pTempWnd = CWnd::FromHandle( m_hwndCtrl ); + pTempWnd->MoveWindow( rcNewArea.left, rcNewArea.top, rcNewArea.Width(), rcNewArea.Height(), true ); + } + } + + ::RedrawWindow(m_hwndCtrl,0,0, RDW_INVALIDATE | RDW_UPDATENOW ); + + } + return true; +} + + +///////////////////////////////////////////////////////////////////////////// +// ETSLayoutMgr::PaneTab implementation + + +ETSLayoutMgr::PaneTab::PaneTab( CTabCtrl* pTab, ETSLayoutMgr* pMgr, layOrientation orientation, int sizeBorder /*= nDefaultBorder*/, int sizeExtraBorder /*= 0*/ ) +: ETSLayoutMgr::Pane(pMgr, orientation, sizeBorder, sizeExtraBorder) +{ + ASSERT(pTab); + m_pTab = pTab; +} + +int ETSLayoutMgr::PaneTab::getConstrainHorz(int sizeParent) +{ + CRect rcTab; + m_pTab->AdjustRect(TRUE, &rcTab); + + if(rcTab.Width() > sizeParent) + return rcTab.Width(); + + return Pane::getConstrainHorz(sizeParent /*- rcTab.Width()*/); +} + +int ETSLayoutMgr::PaneTab::getConstrainVert(int sizeParent) +{ + CRect rcTab; + m_pTab->AdjustRect(TRUE, &rcTab); + + if( m_modeResize & ABSOLUTE_VERT ) { + return m_sizeSecondary + rcTab.Height(); + } + + if(rcTab.Height() > sizeParent) + return rcTab.Height(); + + return Pane::getConstrainVert(sizeParent /*- rcTab.Height()*/); +} + +int ETSLayoutMgr::PaneTab::getMinConstrainHorz() +{ + CRect rcTab(0,0,0,0); + m_pTab->AdjustRect(TRUE, &rcTab); + + return Pane::getMinConstrainHorz() + rcTab.Width() ; +} + +int ETSLayoutMgr::PaneTab::getMinConstrainVert() +{ + CRect rcTab(0,0,0,0); + m_pTab->AdjustRect(TRUE, &rcTab); + + return Pane::getMinConstrainVert() + rcTab.Height(); +} + +int ETSLayoutMgr::PaneTab::getMaxConstrainHorz() +{ + CRect rcTab(0,0,0,0); + m_pTab->AdjustRect(TRUE, &rcTab); + + int paneMax = Pane::getMaxConstrainHorz(); + return (paneMax != -1) ? paneMax + rcTab.Width() : -1; +} + +int ETSLayoutMgr::PaneTab::getMaxConstrainVert() +{ + CRect rcTab(0,0,0,0); + m_pTab->AdjustRect(TRUE, &rcTab); + + int paneMax = Pane::getMaxConstrainVert(); + return (paneMax != -1) ? paneMax + rcTab.Height() : -1; +} + +bool ETSLayoutMgr::PaneTab::resizeTo(CRect& rcNewArea) +{ + m_pTab->MoveWindow(rcNewArea); + m_pTab->AdjustRect(FALSE,rcNewArea); + + return Pane::resizeTo(rcNewArea); +} + +///////////////////////////////////////////////////////////////////////////// +// ETSLayoutMgr::PaneCtrl implementation + + +ETSLayoutMgr::PaneCtrl::PaneCtrl( CWnd* pCtrl, ETSLayoutMgr* pMgr, layOrientation orientation, int sizeBorder /*= nDefaultBorder*/, int sizeExtraBorder /*= 0*/, int sizeTopExtra /*= 0*/ ) +: ETSLayoutMgr::Pane(pMgr, orientation, sizeBorder, sizeExtraBorder) +{ + m_sizeTopExtra = sizeTopExtra; + + ASSERT(pCtrl); + m_hwndCtrl = pCtrl->GetSafeHwnd(); +} + +ETSLayoutMgr::PaneCtrl::PaneCtrl( UINT nID, ETSLayoutMgr* pMgr, layOrientation orientation, int sizeBorder /*= nDefaultBorder*/, int sizeExtraBorder /*= 0*/, int sizeTopExtra /*= 0*/ ) +: ETSLayoutMgr::Pane(pMgr, orientation, sizeBorder, sizeExtraBorder) +{ + m_sizeTopExtra = sizeTopExtra; + + m_hwndCtrl = ::GetDlgItem(pMgr->GetWnd()->GetSafeHwnd(), nID); + ASSERT(m_hwndCtrl); +} + +int ETSLayoutMgr::PaneCtrl::getConstrainHorz(int sizeParent) +{ + return Pane::getConstrainHorz(sizeParent) ; +} + +int ETSLayoutMgr::PaneCtrl::getConstrainVert(int sizeParent) +{ + return Pane::getConstrainVert(sizeParent); +} + +int ETSLayoutMgr::PaneCtrl::getMinConstrainHorz() +{ + return Pane::getMinConstrainHorz(); +} + +int ETSLayoutMgr::PaneCtrl::getMinConstrainVert() +{ + return Pane::getMinConstrainVert() + m_sizeTopExtra; +} + +int ETSLayoutMgr::PaneCtrl::getMaxConstrainHorz() +{ + int paneMax = Pane::getMaxConstrainHorz(); + return ( paneMax == -1) ? -1 : paneMax ; +} + +int ETSLayoutMgr::PaneCtrl::getMaxConstrainVert() +{ + int paneMax = Pane::getMaxConstrainVert(); + return ( paneMax == -1) ? -1 : paneMax + m_sizeTopExtra; +} + +bool ETSLayoutMgr::PaneCtrl::resizeTo(CRect& rcNewArea) +{ + // FIX: ::MoveWindow would case problems with some ActiveX Controls [Micheal Chapman] + CWnd* pTempWnd = CWnd::FromHandle( m_hwndCtrl ); + pTempWnd->MoveWindow( rcNewArea.left, rcNewArea.top, rcNewArea.Width(), rcNewArea.Height(), true ); + + ::RedrawWindow(m_hwndCtrl,0,0, RDW_INVALIDATE | RDW_UPDATENOW |RDW_ERASE); + rcNewArea.top += m_sizeTopExtra; + return Pane::resizeTo(rcNewArea); +} + +///////////////////////////////////////////////////////////////////////////// +// ETSLayoutMgr::Pane implementation + +ETSLayoutMgr::Pane::Pane( ETSLayoutMgr* pMgr, layOrientation orientation, int sizeBorder /* = nDefaultBorder */, int sizeExtraBorder /*= 0*/) +: PaneBase(pMgr) +{ + m_Orientation = orientation; + m_sizeBorder = sizeBorder; + m_sizeSecondary = 0; + m_modeResize = 0; + m_sizeExtraBorder= sizeExtraBorder; +} + + +ETSLayoutMgr::Pane::~Pane() +{ +} + + +bool ETSLayoutMgr::Pane::addItem( CWnd* pWnd, ETSLayoutMgr::layResizeMode modeResize /*=GREEDY*/, int sizeX /*=0*/, int sizeY /*=0*/, int sizeXMin /*=0*/, int sizeYMin /*=0*/) +{ + CPaneBase pItem = new PaneItem( pWnd, m_pMgr, modeResize, sizeX, sizeY, sizeXMin, sizeYMin); + return addPane( pItem ); +} + +bool ETSLayoutMgr::Pane::addItem( UINT nID, ETSLayoutMgr::layResizeMode modeResize /*=GREEDY*/, int sizeX /*=0*/, int sizeY /*=0*/, int sizeXMin /*=0*/, int sizeYMin /*=0*/) +{ + CPaneBase pItem = new PaneItem( nID, m_pMgr, modeResize, sizeX, sizeY, sizeXMin, sizeYMin); + return addPane( pItem ); +} + +bool ETSLayoutMgr::Pane::addItemFixed(int size) +{ + CPaneBase pNewItem = m_pMgr->itemFixed(m_Orientation, size); + return addPane( pNewItem ); +} + +bool ETSLayoutMgr::Pane::addItemGrowing() +{ + CPaneBase pNewItem = m_pMgr->itemGrowing(m_Orientation); + return addPane( pNewItem ); +} + +bool ETSLayoutMgr::Pane::addItemSpaceBetween( CWnd* pWndFirst, CWnd* pWndSecond ) +{ + CPaneBase pNewItem = m_pMgr->itemSpaceBetween(m_Orientation, pWndFirst, pWndSecond); + return addPane( pNewItem ); +} + +bool ETSLayoutMgr::Pane::addItemSpaceBetween( UINT nIDFirst, UINT nIDSecond ) +{ + CPaneBase pNewItem = m_pMgr->itemSpaceBetween(m_Orientation, nIDFirst, nIDSecond); + return addPane( pNewItem ); +} + +bool ETSLayoutMgr::Pane::addItemSpaceLike( CWnd* pWnd ) +{ + CPaneBase pNewItem = m_pMgr->itemSpaceLike(m_Orientation, pWnd); + return addPane( pNewItem ); +} + +bool ETSLayoutMgr::Pane::addItemSpaceLike( UINT nID ) +{ + CPaneBase pNewItem = m_pMgr->itemSpaceLike(m_Orientation, nID); + return addPane( pNewItem ); +} + +bool ETSLayoutMgr::Pane::addPane( CPane pSubpane, ETSLayoutMgr::layResizeMode modeResize, int sizeSecondary /* = 0 */) +{ + if( pSubpane->getOrientation() == m_Orientation) + { + // wrap in subpane of opposite orientation + CPane pPaneWrap = new Pane(m_pMgr, m_Orientation==HORIZONTAL?VERTICAL:HORIZONTAL,0,0); + pPaneWrap->addPane( pSubpane ); + + addPane( pPaneWrap, modeResize, sizeSecondary ); + } + else + { + pSubpane->m_modeResize = modeResize; + + if(m_Orientation==HORIZONTAL && (modeResize & ABSOLUTE_HORZ) ) { + if(sizeSecondary == 0) { + pSubpane->m_sizeSecondary = pSubpane->getMinConstrainHorz(); + } + } + else if(m_Orientation==HORIZONTAL && (modeResize & RELATIVE_HORZ) ) { + pSubpane->m_sizeSecondary = sizeSecondary; + } + else if(m_Orientation==VERTICAL && (modeResize & ABSOLUTE_VERT) ) { + if(sizeSecondary == 0) { + pSubpane->m_sizeSecondary = pSubpane->getMinConstrainVert(); + } + } + else if(m_Orientation==VERTICAL && (modeResize & RELATIVE_VERT) ) { + pSubpane->m_sizeSecondary = sizeSecondary; + } + + m_paneItems.Add(pSubpane); + } + + return true; +} + +bool ETSLayoutMgr::Pane::addPane( CPaneBase pItem ) +{ + m_paneItems.Add(pItem); + return true; +} + +int ETSLayoutMgr::Pane::getConstrainHorz(int sizeParent) +{ + ASSERT( m_Orientation == VERTICAL); + + if( m_modeResize & RELATIVE_HORZ ) { + return (sizeParent * m_sizeSecondary) / 100; + } + else if( m_modeResize & ABSOLUTE_HORZ ){ + return m_sizeSecondary; + } + else + return 0; +} + + +int ETSLayoutMgr::Pane::getConstrainVert(int sizeParent) +{ + ASSERT( m_Orientation == HORIZONTAL); + + if( m_modeResize & RELATIVE_VERT ) { + return (sizeParent * m_sizeSecondary) / 100; + } + else if( m_modeResize & ABSOLUTE_VERT ) { + return m_sizeSecondary; + } + else { + return 0; + } +} + +int ETSLayoutMgr::Pane::getMaxConstrainHorz() +{ + if(m_Orientation == HORIZONTAL) { + int nMaxConstr = -1; + for(int i=0; igetMaxConstrainHorz(); + if(nConstrain == -1) + return -1; + + nMaxConstr += nConstrain; + } + return (nMaxConstr == -1) ? -1 : nMaxConstr + (m_paneItems.GetUpperBound()*m_sizeBorder) + 2*m_sizeExtraBorder; + } + else if( m_modeResize & ABSOLUTE_HORZ && m_sizeSecondary!=0) { + return m_sizeSecondary; // + 2*m_sizeExtraBorder; + } + else { + int nMaxConstr = -1; + for(int i=0; igetMaxConstrainHorz(); + + if( nConstrain == -1) + return -1; + else + nMaxConstr = max(nMaxConstr, nConstrain); + + } + return (nMaxConstr == -1) ? -1 : nMaxConstr + 2*m_sizeExtraBorder; + } +} + +int ETSLayoutMgr::Pane::getMaxConstrainVert() +{ + if(m_Orientation == VERTICAL) { + int nMaxConstr = -1; + for(int i=0; igetMaxConstrainVert(); + if(nConstrain == -1) + return -1; + + nMaxConstr += nConstrain; + } + return (nMaxConstr == -1) ? -1 : nMaxConstr + (m_paneItems.GetUpperBound()*m_sizeBorder) + 2*m_sizeExtraBorder; + } + else if( m_modeResize & ABSOLUTE_VERT && m_sizeSecondary!=0) { + return m_sizeSecondary; // + 2*m_sizeExtraBorder; + } + else { + int nMaxConstr = -1; + for(int i=0; igetMaxConstrainVert(); + + if( nConstrain == -1) + return -1; + else + nMaxConstr = max(nMaxConstr, nConstrain); + + } + return (nMaxConstr == -1) ? -1 : nMaxConstr + 2*m_sizeExtraBorder; + } +} + +int ETSLayoutMgr::Pane::getMinConstrainHorz() +{ + if(m_Orientation == HORIZONTAL) { + int nMaxConstr = 0; + for(int i=0; igetMinConstrainHorz()); + } + return nMaxConstr + (m_paneItems.GetUpperBound()*m_sizeBorder) + 2*m_sizeExtraBorder; + } + else if( m_modeResize & ABSOLUTE_HORZ && m_sizeSecondary!=0) { + return m_sizeSecondary; // + 2*m_sizeExtraBorder; + } + else { + int nMaxConstr = 0; + for(int i=0; igetMinConstrainHorz(); + nMaxConstr = max(nMaxConstr, nConstrain); + } + return nMaxConstr + 2*m_sizeExtraBorder; + } +} + +int ETSLayoutMgr::Pane::getMinConstrainVert() +{ + if(m_Orientation == VERTICAL) { + int nMaxConstr = 0; + for(int i=0; igetMinConstrainVert()); + } + return nMaxConstr + (m_paneItems.GetUpperBound()*m_sizeBorder) + 2*m_sizeExtraBorder; + } + else if( m_modeResize & ABSOLUTE_VERT && m_sizeSecondary!=0) { + return m_sizeSecondary; // + 2*m_sizeExtraBorder; + } + else { + int nMaxConstr = 0; + for(int i=0; igetMinConstrainVert(); + nMaxConstr = max(nMaxConstr, nConstrain); + } + return nMaxConstr + 2*m_sizeExtraBorder; + } +} + + +int ETSLayoutMgr::Pane::resizeToAbsolute(int& availSpace, CArray& sizePrimary, + CArray& sizeMin, CArray& sizeMax) +{ + // count all greedy items as returnvalue + int nGreedy = 0; + + // first, subtract all absoulute items from available space + for(int i=0; imodeResize() & ABSOLUTE_HORZ) { + availSpace -= (sizePrimary[i] = pItem->getConstrainHorz(0)); + } + + // count Greedy items for later + if(!(pItem->modeResize() & ABSOLUTE_HORZ) && !(pItem->modeResize() & RELATIVE_HORZ)) { + nGreedy++; + } + + sizeMin[i] = pItem->getMinConstrainHorz(); + sizeMax[i] = pItem->getMaxConstrainHorz(); + } + else { + + // for absolute items subtract their size from available space + if(pItem->modeResize() & ABSOLUTE_VERT) { + availSpace -= (sizePrimary[i] = pItem->getConstrainVert(0)); + } + + // count Greedy items for later + if(!(pItem->modeResize() & ABSOLUTE_VERT) && !(pItem->modeResize() & RELATIVE_VERT)) { + nGreedy++; + } + + sizeMin[i] = pItem->getMinConstrainVert(); + sizeMax[i] = pItem->getMaxConstrainVert(); + } + + } + + // Must not be negative !! + availSpace = max(availSpace, 0); + + return nGreedy; +} + +bool ETSLayoutMgr::Pane::resizeToRelative(int& availSpace, CArray& sizePrimary, + CArray& sizeMin, CArray& sizeMax) +{ + // Then all relative items as percentage of left space (as of now after + // all absolute items are subtracted + + int availRel = availSpace; // At the beginning all of remaining space is available. We want all + // operation to be relative to the left space at this moment, so we + // save this amount here. Then we safly can lower availSpace + + int relDiff = 0; // The cumulated difference between first proposed size and + // eventual maximum/minimum size. This amount has to be + // saved in some other place (i.e. where relativ items/subpane + // are not limited by min/max + + int relLeft = 0; // The cumulated amout of space that can be saved by + // shrinking the items/panes up to the minimum + + int relCount = 0; // Actually allocated item/subpane's cumulated primary sizes + // of non-limited items/subpanes (these can be modified in fixup) + // needed for equally distribution of differences amoung non-limited + // relative items/subpanes + + for(int i=0; imodeResize() & RELATIVE_HORZ) + || + (m_Orientation==VERTICAL && pItem->modeResize() & RELATIVE_VERT) ) + { + // minimum item/subpane size in primary direction (pixels) + int nSizeRelMin = sizeMin[i]; + + // maximum item/subpane size in primary direction (pixels) + int nSizeRelMax = sizeMax[i]; + + // Relative size in primary direction (pixels) + int nSizeRel = (m_Orientation==HORIZONTAL) + ? + (pItem->getConstrainHorz(availRel)) + : + (pItem->getConstrainVert(availRel)); + + if( nSizeRel < nSizeRelMin) { + // The item/pane is shrinked too small! + // We will grow it to it's minimum-size. In order not to modify + // this item later when fixing up set the size to the negative + // minimum size + sizePrimary[i] = -nSizeRelMin; + + // As we grew one item/subpane we have to shrink another one. + // We keep count on how much space we needed to grow the item + // to it's minimum size + relDiff += ( nSizeRelMin - nSizeRel ); + } + else if( nSizeRelMax != -1 && nSizeRel > nSizeRelMax) { + // if there's a maximum size (nSizeRelMax != -1) and our item/subpane + // is to be resized over that amount correct it. In order not to modify + // this item later when fixing up set the size to the negative + // maximum size + sizePrimary[i] = -nSizeRelMax; + + // As we shrinked one item/subpane we have to grow another one. + // We keep count on how much space we needed to grow the item + // to it's maximum size. + relDiff += ( nSizeRelMax - nSizeRel ); + } + else { + // this is the normal case: neither are we minimum limited nor maximum + // limited + + // As this item/subpane is larger that it's minimum we could later (if + // necessary for fixup) shrink it for the difference amount of pixels + relLeft += ( nSizeRel - nSizeRelMin ); + + // Set the primary size of this item/pane. Can later be modified by fixup + sizePrimary[i] = nSizeRel; + + // Add this item/subpane's primary size to the count of already allocated + // cumulated size of non-limited items/subpanes (these can be modified in fixup) + relCount += nSizeRel; + } + + // decrease available space by used space in this step + availSpace -= nSizeRel; + } + } + + // We now have the situation that some items/subpanes had to be adjusted for cumulated + // relDiff pixels (positive value means more space taken than indicated by percentage of + // left space). On the other hand we have some items/subpanes which were not limited (in + // their current dimensions) but could be if necessary up to relLeft pixels. + if(relLeft < relDiff && availSpace >= (relDiff-relLeft) ){ + + // If it's not possible to shrink other (relative) panes in order to distribute the + // difference because the left for shrinking (relLeft) is too small we need to aquire + // more space from the globally left space (if available at all) + availSpace -= (relDiff-relLeft); + relDiff = relLeft; + } + + // At this point we should have some space left (at least not be negative with the leftover + // space) and on the other hand there's enough space for the limit-difference to be distributed +// ASSERT( availSpace >= 0 && relLeft >= relDiff); + + // Fixup Relative: + // Distribute (if anecessary) relDiff on other (not limited) relative items/subpanes + // (if available - if not later just grow the limited panes) + while( relDiff != 0 && relCount >= 0 ) { + + // in every iteration there must be some space distributed (of the difference) or it could + // come to endless looping. Save the amount of space actually distributed in this iteration + int relDist = 0; + + for(int i=0; imodeResize() & RELATIVE_HORZ) && sizePrimary[i] > 0) + || + (m_Orientation==VERTICAL && (pItem->modeResize() & RELATIVE_VERT) && sizePrimary[i] > 0) ) + { + // keep a flag for termination of this iteration + bool bLast = false; + + // the difference should be distributed amoung all non-limited items/subpanes equally. + // nDiff is the amount for the current item/subpane + int nDiff = (relDiff * sizePrimary[i]) / relCount; + + // if it's a too small value just add it to the current pane and break iteration + if( abs(relDiff) <= FIXUP_CUTOFF ) { + // take it all in this step + nDiff = relDiff; + + // set break flag + bLast = true; + } + + // calculate the new size for the current item/subpane + int nNewSize = sizePrimary[i] - nDiff; + + if( nNewSize < sizeMin[i] ) { + // oh, we are limited here. Revise our plan: + + // Not all of the space could be saved, add the actually possible space + // to the sum + relDist += ( sizePrimary[i] - sizeMin[i] ); + + // set it to the minimum possible size + sizePrimary[i] = -sizeMin[i]; + + // as this item/subpane is now limited it's occupied space doesn't count + // for relCount anymore + relCount-= ( sizePrimary[i] ); + } + else { + // account the difference of the sizes in relDist and set new size + relDist += ( sizePrimary[i] - nNewSize ); + sizePrimary[i] = nNewSize; + + // if it's the last one break now + if(bLast) + break; + } + } + } + // Distributed some relDiff-space in every iteration +// ASSERT(relDist != 0); + relDiff -= relDist; + + if( relDist == 0 ) + break; + } + + { + // Fixup Relative: invert all negative (limited) sized to correct value + for(int i=0; imodeResize() & RELATIVE_HORZ) && sizePrimary[i] < 0) + || + (m_Orientation==VERTICAL && (pItem->modeResize() & RELATIVE_VERT) && sizePrimary[i] < 0) ) + { + sizePrimary[i] *= -1; + } + } + } + + return true; +} + +bool ETSLayoutMgr::Pane::resizeToGreedy(int& availSpace, int nGreedy, CArray& sizePrimary, + CArray& sizeMin, CArray& sizeMax) +{ + // Now resize all Greedy items/subpanes equally among the remaining space + int greedyDiff = 0; // The cumulated difference between first proposed size and + // eventual maximum/minimum size. This amount has to be + // saved in some other place (i.e. where items/subpane + // are not limited by min/max + + int greedyLeft = 0; // The cumulated amount of space that can be saved by + // shrinking the items/panes up to the minimum + + int greedyCount = 0; // Actually allocated item/subpane's cumulated primary sizes + // of non-limited items/subpanes (these can be modified in fixup) + // needed for equally distribution of differences amoung non-limited + // items/subpanes + + for(int i=0; imodeResize()&ABSOLUTE_HORZ) + && !(pItem->modeResize()&RELATIVE_HORZ) + ) + || + (m_Orientation==VERTICAL + && !(pItem->modeResize()&ABSOLUTE_VERT) + && !(pItem->modeResize()&RELATIVE_VERT) + ) + ) + { + + // All greedy items get an equal portion of the left space + int nSize = availSpace / nGreedy; + + // minimum item/subpane size in primary direction (pixels) + int nSizeMin = sizeMin[i]; + + // maximum item/subpane size in primary direction (pixels) + int nSizeMax = sizeMax[i]; + + + // the last gets the all of the remaining space + if( nGreedy == 1 ) + nSize = availSpace; + + if( nSize < nSizeMin) { + // The item/pane is shrinked too small! + // We will grow it to it's minimum-size. In order not to modify + // this item later when fixing up set the size to the negative + // minimum size + sizePrimary[i] = -nSizeMin; + + // As we grew one item/subpane we have to shrink another one. + // We keep count on how much space we needed to grow the item + // to it's minimum size + greedyDiff += ( nSizeMin - nSize ); + } + else if( nSizeMax != -1 && nSize > nSizeMax) { + // if there's a maximum size (nSizeRelMax != -1) and our item/subpane + // is to be resized over that amount correct it. In order not to modify + // this item later when fixing up set the size to the negative + // maximum size + sizePrimary[i] = -nSizeMax; + + // As we shrinked one item/subpane we have to grow another one. + // We keep count on how much space we needed to grow the item + // to it's maximum size. + greedyDiff += ( nSizeMax - nSize ); + } + else { + + // this is the normal case: neither are we minimum limited nor maximum + // limited + + // As this item/subpane is larger that it's minimum we could later (if + // necessary for fixup) shrink it for the difference amount of pixels + greedyLeft += ( nSize - nSizeMin ); + + // Set the primary size of this item/pane. Can later be modified by fixup + sizePrimary[i] = nSize; + + // Add this item/subpane's primary size to the count of already allocated + // cumulated size of non-limited items/subpanes (these can be modified in fixup) + greedyCount += nSize; + } + + // decrease available space by used space in this step + availSpace -= nSize; + + // one greedy item/subpane complete + --nGreedy; + } + } + + + // Fixup Greedy I + // Distribute (if anecessary) greedyDiff on other (not limited) greedy items/subpanes + // (if available - if not later just grow the limited panes) + + // at least on not limited item present + bool bAtLeastOne = true; + + while( bAtLeastOne && greedyDiff != 0 && greedyCount > 0) { + + // in every iteration there must be some space distributed (of the difference) or it could + // come to endless looping. Save the amount of space actually distributed in this iteration + int greedyDist = 0; + + // at least on not limited item present + bAtLeastOne = false; + + for(int i=0; imodeResize()&ABSOLUTE_HORZ) + && !(pItem->modeResize()&RELATIVE_HORZ) + && sizePrimary[i] > 0 + ) + || + (m_Orientation==VERTICAL + && !(pItem->modeResize()&ABSOLUTE_VERT) + && !(pItem->modeResize()&RELATIVE_VERT) + && sizePrimary[i] > 0 + ) + ) + { + // keep a flag for termination of this iteration + bool bLast = false; + + // the difference should be distributed among all non-limited items/subpanes equally. + // nDiff is the amount for the current item/subpane + int nDiff = (greedyDiff * sizePrimary[i]) / greedyCount; + + // if it's a too small value just add it to the current pane and break iteration + if( abs(greedyDiff) <= FIXUP_CUTOFF || nDiff == 0) { + // take it all in this step + nDiff = greedyDiff; + + // set break flag + bLast = true; + } + + // calculate the new size for the current item/subpane + int nNewSize = sizePrimary[i] - nDiff; + + if( nNewSize < sizeMin[i] ) { + // oh, we are limited here. Revise our plan: + + if( sizePrimary[i] != sizeMin[i] ) + bAtLeastOne = true; + + // Not all of the space could be saved, add the actually possible space + // to the sum + greedyDist += ( sizePrimary[i] - sizeMin[i] ); + + // set it to the minimum possible size + sizePrimary[i] = sizeMin[i]; + + // as this item/subpane is now limited its occupied space doesn't count + // for relCount anymore + greedyCount -= ( sizePrimary[i] ); + } + else { + // yes, there is one + bAtLeastOne = true; + + // account the difference of the sizes in relDist and set new size + greedyDist += ( sizePrimary[i] - nNewSize ); + sizePrimary[i] = nNewSize; + + // if it's the last one break now + if(bLast) + break; + } + } + } + // Distributed some greedyDiff-space in every iteration + ASSERT(!bAtLeastOne || greedyDist != 0 || greedyCount<=0); + greedyDiff -= greedyDist; + } + + + // Fixup Greedy II + if( greedyDiff < 0 ) { + // still difference, some space left + + // are there any items which are minimum-limited where we can give more space? + for(int i=0; imodeResize()&ABSOLUTE_HORZ) + && !(pItem->modeResize()&RELATIVE_HORZ) + ) + || + (m_Orientation==VERTICAL + && !(pItem->modeResize()&ABSOLUTE_VERT) + && !(pItem->modeResize()&RELATIVE_VERT) + ) + ) + { + if( sizePrimary[i] == -sizeMin[i] ) { + // fill this one up as much as possible + if( sizeMax[i] == -1) { + // all fits in + sizePrimary[i] += greedyDiff; + greedyDiff = 0; + } + else { + sizePrimary[i] += -min( -greedyDiff, sizeMax[i]-sizeMin[i]); + greedyDiff -= -min( -greedyDiff, sizeMax[i]-sizeMin[i]); + } + } + } + } + } + + { + // Fixup Greedy III: invert all negative (limited) sized to correct value + for(int i=0; imodeResize() & ABSOLUTE_HORZ) + && !(pItem->modeResize() & RELATIVE_HORZ) + && sizePrimary[i] < 0 + && sizeMin[i] >= 0 + ) + || + (m_Orientation==VERTICAL + && !(pItem->modeResize() & ABSOLUTE_VERT) + && !(pItem->modeResize() & RELATIVE_VERT) + && sizePrimary[i] < 0 + && sizeMin[i] >= 0 + ) + ) + { + if(sizePrimary[i] < 0) + sizePrimary[i] *= -1; + } + } + } + + return true; +} + + +bool ETSLayoutMgr::Pane::resizeTo(CRect& rcNewArea) +{ + // There must be some items or subpanes + ASSERT(m_paneItems.GetSize()); + + // This Array holds the size in primary direction for each item/subpane + CArray sizePrimary; + sizePrimary.SetSize(m_paneItems.GetSize()); + + // This Array holds information about the minimum size in primary direction + CArray sizeMin; + sizeMin.SetSize(m_paneItems.GetSize()); + + // This Array holds information about the maximum size in primary direction + CArray sizeMax; + sizeMax.SetSize(m_paneItems.GetSize()); + + + // How much space is actually available, subtract all borders between items + int availSpace = (m_Orientation == HORIZONTAL ? rcNewArea.Width() : rcNewArea.Height() ) - (m_paneItems.GetUpperBound()*m_sizeBorder); + + // If there is some Extra border (on top/bottem resp. left/right) subtract it too + availSpace -= 2*m_sizeExtraBorder; + + // Add the extra Border to top/bottem resp. left/right + if(m_Orientation == HORIZONTAL) { + rcNewArea.top += m_sizeExtraBorder; + rcNewArea.bottom -= m_sizeExtraBorder; + } + else { + rcNewArea.left += m_sizeExtraBorder; + rcNewArea.right -= m_sizeExtraBorder; + } + + // Counts the number of greedy items/subpanes + int nGreedy = resizeToAbsolute(availSpace, sizePrimary, sizeMin, sizeMax ); + + if(nGreedy == -1) + return false; + + if(! resizeToRelative(availSpace, sizePrimary, sizeMin, sizeMax ) ) + return false; + + if(! resizeToGreedy(availSpace, nGreedy, sizePrimary, sizeMin, sizeMax ) ) + return false; + + + // If there is any left space and there are ALIGN_FILL_* Items to assign it + // equally among them + if( availSpace > 0 ) { + // Count possible Items + int nFillItems = 0; + + for(int i=0; imodeResize() & ABSOLUTE_HORZ ) + && (pItem->modeResize() & ALIGN_FILL_HORZ) + + || + + (pItem->modeResize() & ABSOLUTE_VERT ) + && (pItem->modeResize() & ALIGN_FILL_VERT) + ) + { + ++nFillItems; + } + } + + if( nFillItems > 0 ) { + // okay, there are nFillItems, make them all availSpace/nFillItems bigger + for(int i=0; imodeResize() & ABSOLUTE_HORZ ) + && (pItem->modeResize() & ALIGN_FILL_HORZ) + + || + + (pItem->modeResize() & ABSOLUTE_VERT ) + && (pItem->modeResize() & ALIGN_FILL_VERT) + ) + { + + if( nFillItems == 1 ) { + // the last one gets all the rest + sizePrimary[i] += availSpace; + availSpace = 0; + --nFillItems; + } + else { + sizePrimary[i] += availSpace/nFillItems; + availSpace -= availSpace/nFillItems; + --nFillItems; + } + + } + } + } + + } + + // Now reposition all items: + + // starting offset + int nOffset = (m_Orientation==HORIZONTAL ? rcNewArea.left : rcNewArea.top ) + m_sizeExtraBorder; + for(int i=0; iresizeTo( rcPane ); + + // go to the next position (old pos + size + border) + ASSERT(sizePrimary[i] >= 0); + nOffset += m_sizeBorder + sizePrimary[i]; + } + + + return true; +} + + +///////////////////////////////////////////////////////////////////////////// +// ETSLayoutDialog dialog + +#pragma warning(disable: 4355) +ETSLayoutDialog::ETSLayoutDialog(UINT nID, CWnd* pParent /*=NULL*/, LPCTSTR strName /*=NULL*/, bool bGripper /*=true*/) + : CBaseDialog(nID, pParent), ETSLayoutMgr( this ) +{ + //{{AFX_DATA_INIT(ETSLayoutDialog) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT + m_bGripper = bGripper; + + if(strName) + m_strRegStore = strName; +} +#pragma warning(default: 4355) + +BEGIN_MESSAGE_MAP(ETSLayoutDialog, CBaseDialog) + //{{AFX_MSG_MAP(ETSLayoutDialog) + ON_WM_SIZE() + ON_WM_GETMINMAXINFO() + ON_WM_ERASEBKGND() + ON_WM_DESTROY() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +///////////////////////////////////////////////////////////////////////////// +// ETSLayoutDialog message handlers + +BOOL ETSLayoutDialog::OnEraseBkgnd(CDC* pDC) +{ + EraseBkgnd(pDC); + return true; +} + +void ETSLayoutDialog::OnSize(UINT nType, int cx, int cy) +{ + CBaseDialog::OnSize(nType, cx, cy); + + if( abs(cx) + abs(cy) > 0) + { + // Reposition Size Marker + // Re-Layout all controls + UpdateLayout(); + RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0); + } + +} + +void ETSLayoutDialog::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) +{ + if(m_RootPane.IsValid()) { + + CRect rcClient = GetRect(); + if( rcClient.Height() > 0 || rcClient.Width() > 0 ) + { + + CRect rcWnd; + GetWindowRect(rcWnd); + + // How much do Window and Client differ + int nDiffHorz = rcWnd.Width() - rcClient.Width(); + int nDiffVert = rcWnd.Height() - rcClient.Height(); + + // Take into account that there is a border around the rootPane + lpMMI->ptMinTrackSize = CPoint(m_RootPane->getMinConstrainHorz() + nDiffHorz + 2*m_sizeRootBorders.cx, + m_RootPane->getMinConstrainVert() + nDiffVert + 2*m_sizeRootBorders.cy); + + int maxWidth = m_RootPane->getMaxConstrainHorz(); + int maxHeight = m_RootPane->getMaxConstrainVert(); + + if( maxWidth != -1 ) { + lpMMI->ptMaxTrackSize.x = maxWidth + nDiffHorz + 2*m_sizeRootBorders.cx; + lpMMI->ptMaxSize.x = maxWidth + nDiffHorz + 2*m_sizeRootBorders.cx; + } + + if( maxHeight != -1 ) { + lpMMI->ptMaxTrackSize.y = maxHeight + nDiffVert + 2*m_sizeRootBorders.cy; + lpMMI->ptMaxSize.y = maxHeight + nDiffVert + 2*m_sizeRootBorders.cy; + } + } + } +} + + +CRect ETSLayoutDialog::GetRect() +{ + CRect r; + GetClientRect(r); + + if( m_bGripper ) + { + if( ::IsWindow(m_StatusBar.GetSafeHwnd()) ) + { + CRect rcSizeIcon; + m_StatusBar.GetWindowRect( rcSizeIcon); + r.bottom -= (rcSizeIcon.Height() - m_sizeRootBorders.cy - 5); + } + } + + return r; +} + + +BOOL ETSLayoutDialog::OnInitDialog() +{ + CBaseDialog::OnInitDialog(); + + // Ensure that the dialog is resizable + this->ModifyStyle(0, WS_THICKFRAME); + + if(!m_strRegStore.IsEmpty()) { + Load(m_strRegStore); + } + +#ifdef _AUTO_SET_ICON + POSITION pos = AfxGetApp()->GetFirstDocTemplatePosition(); + if(pos) { + + class ETSPseudoDocTemplate : public CDocTemplate + { + friend class ETSLayoutDialog; + }; + + ETSPseudoDocTemplate* pDocT = (ETSPseudoDocTemplate*) AfxGetApp()->GetNextDocTemplate(pos); + SetIcon( AfxGetApp()->LoadIcon(pDocT->m_nIDResource) ,FALSE); + } +#endif + + // Sizing icon + if(m_bGripper) + { + if(m_StatusBar.Create(m_pWnd)) + { + m_StatusBar.SetIndicators(auIDStatusBar, sizeof(auIDStatusBar) / sizeof(UINT)); + m_StatusBar.SetWindowText(_T("")); + m_StatusBar.SetPaneStyle( 0, SBPS_STRETCH | SBPS_NOBORDERS ); + m_pWnd -> RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0); + } + else + AfxMessageBox(_T("Error - Statusbar")); + + } + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void ETSLayoutDialog::OnDestroy() +{ + // Store size/position + if(!m_strRegStore.IsEmpty()) { + Save(m_strRegStore); + } + + // manually delete layout definition if object is reused + m_RootPane = 0; + + CBaseDialog::OnDestroy(); +} + +///////////////////////////////////////////////////////////////////////////// +// ETSLayoutDialog dialog + +#pragma warning(disable: 4355) +#ifdef CS_HELP +ETSLayoutDialogBar::ETSLayoutDialogBar(UINT nID ) + : CBaseDialogBar( nID ), ETSLayoutMgr( this ) +#else +ETSLayoutDialogBar::ETSLayoutDialogBar() + : ETSLayoutMgr( this ) +#endif +{ + //{{AFX_DATA_INIT(ETSLayoutDialogBar) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT + m_bInitialized = false; + setRootBorders(0,0); +} +#pragma warning(default: 4355) + +BEGIN_MESSAGE_MAP(ETSLayoutDialogBar, CBaseDialogBar) + //{{AFX_MSG_MAP(ETSLayoutDialogBar) + ON_WM_SIZE() + ON_WM_GETMINMAXINFO() + ON_WM_DESTROY() + ON_WM_ERASEBKGND() + ON_MESSAGE(WM_INITDIALOG, OnInitDialog) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +///////////////////////////////////////////////////////////////////////////// +// ETSLayoutDialogBar message handlers + +LRESULT ETSLayoutDialogBar::OnInitDialog(WPARAM, LPARAM) +{ + Default(); + Initialize(); + return TRUE; +} + +void ETSLayoutDialogBar::UpdateLayout() +{ + ETSLayoutMgr::UpdateLayout(); + + if(m_RootPane.IsValid()) { + CRect rcClient = GetRect(); + + CRect rcWnd; + GetWindowRect(rcWnd); + + // How much do Window and Client differ + CSize sizeDiff( rcWnd.Width() - rcClient.Width(), rcWnd.Height() - rcClient.Height()); + + // Take into account that there is a border around the rootPane +// m_szMin = CSize(m_RootPane->getMinConstrainHorz() + sizeDiff.cx + 2*m_sizeRootBorders.cx, +// m_RootPane->getMinConstrainVert() + sizeDiff.cy + 2*m_sizeRootBorders.cy); + } +} + +CSize ETSLayoutDialogBar::CalcDynamicLayout(int nLength, DWORD dwMode) +{ + CSize sizeRet = CBaseDialogBar::CalcDynamicLayout(nLength, dwMode); + + CSize sizeMin = sizeRet; + CSize sizeMax = sizeRet; + + if(m_RootPane.IsValid()) { + CRect rcClient = GetRect(); + + CRect rcWnd; + GetWindowRect(rcWnd); + + // How much do Window and Client differ + CSize sizeDiff( rcWnd.Width() - rcClient.Width(), rcWnd.Height() - rcClient.Height()); + + // Take into account that there is a border around the rootPane +// sizeMin = CSize(m_RootPane->getMinConstrainHorz() + sizeDiff.cx + 2*m_sizeRootBorders.cx, +// m_RootPane->getMinConstrainVert() + sizeDiff.cy + 2*m_sizeRootBorders.cy); + + + int maxWidth = m_RootPane->getMaxConstrainHorz(); + int maxHeight = m_RootPane->getMaxConstrainVert(); + + if( maxWidth != -1 ) { + sizeMax.cx = maxWidth + sizeDiff.cy + 2*m_sizeRootBorders.cx; + } + + if( maxHeight != -1 ) { + sizeMax.cy = maxHeight + sizeDiff.cy + 2*m_sizeRootBorders.cy; + } + } + + if( IsFloating() || !(dwMode&LM_HORZ)) + { + sizeRet.cx = min( sizeRet.cx, sizeMax.cx ); + } + if( IsFloating() || (dwMode&LM_HORZ)) + { + sizeRet.cy = min( sizeRet.cy, sizeMax.cy ); + } + + sizeRet.cx = max( sizeRet.cx, sizeMin.cx ); + sizeRet.cy = max( sizeRet.cy, sizeMin.cy ); + + return sizeRet; +} + +BOOL ETSLayoutDialogBar::OnEraseBkgnd(CDC* pDC) +{ + EraseBkgnd(pDC); + return true; +} + + +void ETSLayoutDialogBar::OnSize(UINT nType, int cx, int cy) +{ + CBaseDialogBar::OnSize(nType, cx, cy); + + if( abs(cx) + abs(cy) > 0) + { + // Re-Layout all controls + UpdateLayout(); + } + RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0); + +} + + +CRect ETSLayoutDialogBar::GetRect() +{ + CRect r; + GetClientRect(r); + + if( IsFloating() ) + r.DeflateRect(4,4); + + return r; +} + + +void ETSLayoutDialogBar::OnDestroy() +{ + // Store size/position on your own! + CBaseDialogBar::OnDestroy(); +} + + + +///////////////////////////////////////////////////////////////////////////// +// ETSLayoutFormView dialog + +IMPLEMENT_DYNAMIC(ETSLayoutFormView, CFormView) + +#pragma warning(disable: 4355) +ETSLayoutFormView::ETSLayoutFormView(UINT nID, LPCTSTR strName /*=NULL*/) + : CBaseFormView(nID), ETSLayoutMgr( this ) +{ + if(strName) + m_strRegStore = strName; +} +#pragma warning(default: 4355) + +BEGIN_MESSAGE_MAP(ETSLayoutFormView, CBaseFormView) + //{{AFX_MSG_MAP(ETSLayoutFormView) + ON_WM_SIZE() + ON_WM_GETMINMAXINFO() + ON_WM_ERASEBKGND() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +///////////////////////////////////////////////////////////////////////////// +// ETSLayoutFormView message handlers + +BOOL ETSLayoutFormView::OnEraseBkgnd(CDC* pDC) +{ + EraseBkgnd(pDC); + return true; +} + + +void ETSLayoutFormView::OnSize(UINT nType, int cx, int cy) +{ +// CBaseFormView::OnSize(nType, cx, cy); + SetScrollSizes(MM_TEXT, CSize(cx,cy)); + if( abs(cx) + abs(cy) > 0) { + // Re-Layout all controls + UpdateLayout(); + } +// MoveWindow(0,0,cx,cy); +} + +/* +void ETSLayoutFormView::UpdateLayout() +{ + ETSLayoutMgr::UpdateLayout(); + + if(m_RootPane.IsValid()) { + // Force MainFrame to re-layout + CFrameWnd* pFrame = static_cast(GetParent()); + if(pFrame) { + + CRect rcWnd; + pFrame->GetWindowRect(rcWnd); + pFrame->MoveWindow(rcWnd); + pFrame->RecalcLayout(); + + } + return; + } +} +*/ + +void ETSLayoutFormView::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) +{ + // To use this you'll have to modify your CMainFrame: + // + // 1) Add a handler for WM_GETMINMAXINFO() + // 2) Let this handler be: + // void CMainFrame::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) + // { + // CFrameWnd::OnGetMinMaxInfo(lpMMI); + // + // if( GetActiveView() && GetActiveView()->IsKindOf( RUNTIME_CLASS(ETSLayoutFormView) ) ) { + // GetActiveView()->SendMessage( WM_GETMINMAXINFO, 0, (LPARAM) lpMMI ); + // } + // } + // 3) Add "#include "dialogmgr.h" to MainFrm.cpp + + if(m_RootPane.IsValid()) { + CRect rcClient = GetRect(); + + CRect rcWnd; + GetParent()->GetWindowRect(rcWnd); + + // How much do Window and Client differ + rcWnd-=rcClient; + + // Take into account that there is a border around the rootPane + lpMMI->ptMinTrackSize = CPoint(m_RootPane->getMinConstrainHorz() + rcWnd.Width() + 2*m_sizeRootBorders.cx, + m_RootPane->getMinConstrainVert() + rcWnd.Height() + 2*m_sizeRootBorders.cy); + + int maxWidth = m_RootPane->getMaxConstrainHorz(); + int maxHeight = m_RootPane->getMaxConstrainVert(); + + if( maxWidth != -1 ) { + lpMMI->ptMaxTrackSize.x = maxWidth + rcWnd.Width()+ 2*m_sizeRootBorders.cx; + lpMMI->ptMaxSize.x = maxWidth + rcWnd.Width()+ 2*m_sizeRootBorders.cx; + } + + if( maxHeight != -1 ) { + lpMMI->ptMaxTrackSize.y = maxHeight + rcWnd.Height() + 2*m_sizeRootBorders.cy; + lpMMI->ptMaxSize.y = maxHeight + rcWnd.Height() + 2*m_sizeRootBorders.cy; + } + } +} + +ETSLayoutFormView::~ETSLayoutFormView() +{ + // Cleanup +} + + +///////////////////////////////////////////////////////////////////////////// +// ETSLayoutPropertyPage + +#ifdef CS_HELP + IMPLEMENT_DYNCREATE(ETSLayoutPropertyPage, ETSCSHelpPropPage) +#else + IMPLEMENT_DYNCREATE(ETSLayoutPropertyPage, CPropertyPage) +#endif + +#pragma warning(disable: 4355) +ETSLayoutPropertyPage::ETSLayoutPropertyPage( ) : ETSLayoutMgr( this ) +{ + m_bLockMove = false; + m_bResetBuddyOnNextTimeVisible = true; +} + +ETSLayoutPropertyPage::ETSLayoutPropertyPage( UINT nIDTemplate, UINT nIDCaption /*= 0*/ ) + : CBasePropertyPage(nIDTemplate, nIDCaption), ETSLayoutMgr( this ) +{ + m_bLockMove = false; + m_bResetBuddyOnNextTimeVisible = true; +} + +ETSLayoutPropertyPage::ETSLayoutPropertyPage( LPCTSTR lpszTemplateName, UINT nIDCaption /*= 0*/ ) + : CBasePropertyPage(lpszTemplateName, nIDCaption), ETSLayoutMgr( this ) +{ + m_bLockMove = false; + m_bResetBuddyOnNextTimeVisible = true; +} +#pragma warning(default: 4355) + +ETSLayoutPropertyPage::~ETSLayoutPropertyPage() +{ +} + + +BEGIN_MESSAGE_MAP(ETSLayoutPropertyPage, CBasePropertyPage) + //{{AFX_MSG_MAP(ETSLayoutPropertyPage) + ON_WM_SIZE() + ON_WM_GETMINMAXINFO() + ON_WM_ERASEBKGND() + ON_WM_WINDOWPOSCHANGING() + ON_WM_DESTROY() + ON_WM_WINDOWPOSCHANGED() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +///////////////////////////////////////////////////////////////////////////// +// Behandlungsroutinen für Nachrichten ETSLayoutPropertyPage + + + +void ETSLayoutPropertyPage::OnWindowPosChanged(WINDOWPOS FAR* lpwndpos) +{ + CBasePropertyPage::OnWindowPosChanged(lpwndpos); + + // This code is needed in order to reset the buddy after this page has + // been activated. At least on Win2k this is not done thru normal resizing, + // as the page is not visible when first layouted. And without the page + // being visible it's not possible to tell if the attached buddy is visible + // or not (at least I don't know any way to do so) + + if( ::IsWindowVisible( GetWnd()->GetSafeHwnd() ) ) + { + if( m_bResetBuddyOnNextTimeVisible ) + { + // Take special care of SpinButtons (Up-Down Controls) with Buddy set, enumerate + // all childs: + CWnd* pWndChild = GetWnd()->GetWindow(GW_CHILD); + TCHAR szClassName[ MAX_PATH ]; + while(pWndChild) + { + ::GetClassName( pWndChild->GetSafeHwnd(), szClassName, MAX_PATH ); + DWORD dwStyle = pWndChild->GetStyle(); + + // is it a SpinButton? + if( _tcscmp(szClassName, UPDOWN_CLASS)==0 && ::IsWindowVisible(pWndChild->GetSafeHwnd()) ) { + HWND hwndBuddy = (HWND)::SendMessage( pWndChild->GetSafeHwnd(), UDM_GETBUDDY, 0, 0); + if( hwndBuddy != 0 && (dwStyle&(UDS_ALIGNRIGHT|UDS_ALIGNLEFT)) != 0 ) + { + // reset Buddy + ::SendMessage( pWndChild->GetSafeHwnd(), UDM_SETBUDDY, (WPARAM)hwndBuddy, 0); + } + } + + + pWndChild = pWndChild->GetWindow(GW_HWNDNEXT); + } + + m_bResetBuddyOnNextTimeVisible = false; + } + } + else + { + // has been hidden again + m_bResetBuddyOnNextTimeVisible = true; + } +} + +void ETSLayoutPropertyPage::OnWindowPosChanging( WINDOWPOS* lpwndpos ) +{ + // In WizardMode the System calls SetWindowPos with the + // original size at every activation. This could cause + // some flicker in certain circumstances. Therefore we lock + // moving the page and unlock it only if _we_ move the page + if( m_bLockMove) + { + lpwndpos->flags |= SWP_NOMOVE | SWP_NOSIZE; + } + CBasePropertyPage::OnWindowPosChanging( lpwndpos ); +} + +BOOL ETSLayoutPropertyPage::OnEraseBkgnd(CDC* pDC) +{ + EraseBkgnd(pDC); + return true; +} + +void ETSLayoutPropertyPage::OnDestroy() +{ + // manually delete layout definition if object is reused + m_RootPane = 0; + + CBasePropertyPage::OnDestroy(); +} + +void ETSLayoutPropertyPage::OnSize(UINT nType, int cx, int cy) +{ + CBasePropertyPage::OnSize(nType, cx, cy); + + if( abs(cx) + abs(cy) > 0) + { + // Re-Layout all controls + UpdateLayout(); + } +} + +void ETSLayoutPropertyPage::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) +{ + if(m_RootPane.IsValid()) { + CRect rcClient = GetRect(); + + CRect rcWnd; + GetWindowRect(rcWnd); + + // How much do Window and Client differ + int nDiffHorz = rcWnd.Width() - rcClient.Width(); + int nDiffVert = rcWnd.Height() - rcClient.Height(); + + // Take into account that there is a border around the rootPane + lpMMI->ptMinTrackSize = CPoint(m_RootPane->getMinConstrainHorz() + nDiffHorz + 2*m_sizeRootBorders.cx, + m_RootPane->getMinConstrainVert() + nDiffVert + 2*m_sizeRootBorders.cy); + + int maxWidth = m_RootPane->getMaxConstrainHorz(); + int maxHeight = m_RootPane->getMaxConstrainVert(); + + if( maxWidth != -1 ) { + lpMMI->ptMaxTrackSize.x = maxWidth + nDiffHorz + 2*m_sizeRootBorders.cx; + lpMMI->ptMaxSize.x = maxWidth + nDiffHorz + 2*m_sizeRootBorders.cx; + } + + if( maxHeight != -1 ) { + lpMMI->ptMaxTrackSize.y = maxHeight + nDiffVert + 2*m_sizeRootBorders.cy; + lpMMI->ptMaxSize.y = maxHeight + nDiffVert + 2*m_sizeRootBorders.cy; + } + } +} + + +CRect ETSLayoutPropertyPage::GetRect() +{ + CRect r; + GetClientRect(r); + return r; +} + + +BOOL ETSLayoutPropertyPage::OnInitDialog() +{ + CBasePropertyPage::OnInitDialog(); + UpdateLayout(); + + ETSLayoutPropertySheet* pSheet = (ETSLayoutPropertySheet*) GetParent(); + + ASSERT_KINDOF( ETSLayoutPropertySheet, pSheet); + if(pSheet) + { + if(pSheet->IsWizard()) + { + m_bLockMove = true; + } + } + + return TRUE; +} + +BOOL ETSLayoutPropertyPage::OnSetActive() +{ + ETSLayoutPropertySheet* pSheet = (ETSLayoutPropertySheet*) GetParent(); + + ASSERT_KINDOF( ETSLayoutPropertySheet, pSheet); + if(pSheet) + { + if(pSheet->IsWizard()) + { + // In WizardMode the System calls SetWindowPos with the + // original size on Page Activation. This will position the + // page at the correct position + m_bLockMove = false; + MoveWindow(pSheet->m_rcPage); + m_bLockMove = true; + } + } + + UpdateLayout(); + + return CBasePropertyPage::OnSetActive(); +} + +///////////////////////////////////////////////////////////////////////////// +// ETSLayoutPropertySheet + +IMPLEMENT_DYNAMIC(ETSLayoutPropertySheet, CPropertySheet) + +#pragma warning(disable: 4355) +ETSLayoutPropertySheet::ETSLayoutPropertySheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage, + LPCTSTR strName /*=NULL*/, bool bGripper/*=true*/) + : CPropertySheet(nIDCaption, pParentWnd, iSelectPage), ETSLayoutMgr( this ) +{ + Init(strName, bGripper); +} + +ETSLayoutPropertySheet::ETSLayoutPropertySheet(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage, + LPCTSTR strName /*=NULL*/, bool bGripper/*=true*/) + : CPropertySheet(pszCaption, pParentWnd, iSelectPage), ETSLayoutMgr( this ) +{ + Init(strName, bGripper); +} +#pragma warning(default: 4355) + +void ETSLayoutPropertySheet::Init(LPCTSTR strName, bool bGripper) +{ + m_bGripper = bGripper; + if(strName) + m_strRegStore = strName; + + m_bAutoDestroy = false; + m_bAutoDestroyPages = false; + m_bModelessButtons = false; +} + +ETSLayoutPropertySheet::~ETSLayoutPropertySheet() +{ +} + + +BEGIN_MESSAGE_MAP(ETSLayoutPropertySheet, CPropertySheet) + //{{AFX_MSG_MAP(ETSLayoutPropertySheet) + ON_WM_CREATE() + ON_WM_SIZE() + ON_WM_GETMINMAXINFO() + ON_WM_DESTROY() + ON_WM_ERASEBKGND() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// Behandlungsroutinen für Nachrichten ETSLayoutPropertySheet + +BOOL ETSLayoutPropertySheet::OnEraseBkgnd(CDC* pDC) +{ + EraseBkgnd(pDC); + return true; +} + + +int ETSLayoutPropertySheet::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + if (CPropertySheet::OnCreate(lpCreateStruct) == -1) + return -1; + + ModifyStyle(0,WS_THICKFRAME| WS_SYSMENU); + return 0; +} + + +void ETSLayoutPropertySheet::Resize(int cx, int cy) +{ + if( abs(cx) + abs(cy) > 0 && m_RootPane.IsValid() ) + { + UpdateLayout(); + + // Fix for PSH_WIZARDHASFINISH [Thömmi] + if (IsWizard() && !(m_psh.dwFlags & PSH_WIZARDHASFINISH) ) + { + // manual reposition of the FINISH button + // can not be done with normaly layouting because it + // shares position with the NEXT button + CWnd *pWndFinish; + pWndFinish=GetDlgItem(ID_WIZFINISH); + + if(pWndFinish) + { + CRect rcWnd; + GetDlgItem(ID_WIZNEXT)->GetWindowRect(&rcWnd); + ScreenToClient(&rcWnd); + pWndFinish->MoveWindow(rcWnd); + pWndFinish->RedrawWindow(0,0, RDW_INVALIDATE | RDW_UPDATENOW ); + } + } + + // reposition Gripper + if(m_bGripper) + RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0); + + CPropertyPage* pPage = (CPropertyPage*)GetActivePage(); + + if(pPage) + { + CRect rcWnd; + GetTabControl()->GetWindowRect(&rcWnd); + ScreenToClient(&rcWnd); + + if(!IsWizard()) { + // get inside of tab + GetTabControl()->AdjustRect(FALSE, &rcWnd); + } + else + { + rcWnd.bottom += 5; + } + + // we need this size in WizardMode in order to + // reposition newly activated page correctly + m_rcPage = rcWnd; + + if( IsWizard() && pPage->IsKindOf(RUNTIME_CLASS(ETSLayoutPropertyPage)) ) + { + ETSLayoutPropertyPage* pEtsPage = reinterpret_cast(pPage); + + pEtsPage->m_bLockMove = false; + pEtsPage->MoveWindow(m_rcPage); + pEtsPage->m_bLockMove = true; + } + else + { + pPage->MoveWindow(m_rcPage); + } + + } + + if(IsWindowVisible()) + { + RedrawWindow(0,0, RDW_INVALIDATE|RDW_UPDATENOW ); + + if(!IsWizard()) + GetTabControl()->RedrawWindow(0,0, RDW_INVALIDATE|RDW_UPDATENOW ); + } + } +} + +void ETSLayoutPropertySheet::OnSize(UINT nType, int cx, int cy) +{ + CPropertySheet::OnSize(nType, cx, cy); + Resize(cx,cy); +} + +// IDs of all PropertySheet controls +long _PropertySheetIDs[] = +{ + ID_WIZBACK, + ID_WIZNEXT, + ID_WIZFINISH, + IDOK, + IDCANCEL, + ID_APPLY_NOW, + IDHELP +}; + +void ETSLayoutPropertySheet::AddMainArea(CPane paneRoot, CPaneBase itemTab) +{ + // the default is: Whole main Area is covered by the TabCtrl + paneRoot << itemTab; +} + +void ETSLayoutPropertySheet::AddButtons(CPane paneBottom) +{ + // first item greedy to keep others right + paneBottom->addItem (paneNull, GREEDY); + + + // add all Controls to the layouting + bool bFirst = true; + for(int i = 0; i < (sizeof(_PropertySheetIDs) / sizeof(long)) ; i++) + { + // Prevent movement of finish button, if it is not shown explicitly [Thömmi] + if( IsWizard() + && _PropertySheetIDs[i] == ID_WIZFINISH + && !(m_psh.dwFlags & PSH_WIZARDHASFINISH) ) + { + continue; + } + + CWnd* pWnd = GetDlgItem(_PropertySheetIDs[i]); + + if(pWnd) + { + + if(!(m_psh.dwFlags & PSH_HASHELP) && _PropertySheetIDs[i] == IDHELP) + { + // don't insert + continue; + } + + if((m_psh.dwFlags & PSH_NOAPPLYNOW) && _PropertySheetIDs[i] == ID_APPLY_NOW) + { + // don't insert + continue; + } + + // space before first one and between BACK & NEXT + if( IsWizard() ) + { + if( !bFirst && !(_PropertySheetIDs[i]==ID_WIZNEXT) ) + { + paneBottom->addItem(paneNull, NORESIZE,12,0,0,0); + } + } + + pWnd->ShowWindow(true); + paneBottom->addItem(_PropertySheetIDs[i], NORESIZE); + bFirst = false; + } + } + +} + +BOOL ETSLayoutPropertySheet::OnInitDialog() +{ + BOOL bRet = CPropertySheet::OnInitDialog(); + + ASSERT(!m_RootPane); + + // Save initial rect + GetWindowRect(&m_rcStart); + + CPropertyPage* pPage = CPropertySheet::GetActivePage(); + ASSERT(pPage); + + CRect rcPage; + pPage->GetClientRect(&rcPage); + + CreateRoot(VERTICAL); + ASSERT(m_RootPane.IsValid()); + + // Add Tabcontrol to root pane + m_ItemTab = item( GetTabControl(), GREEDY, 0, 0, 0, 0); + AddMainArea(m_RootPane, m_ItemTab); + + // Tabcontrol is invisible in WizardMode + if(IsWizard()) + { + GetTabControl()->ShowWindow(false); + } + + // add horizontal line in WizardMode + if(IsWizard() && GetDlgItem(ID_WIZFINISH+1)) + { + m_RootPane << item(ID_WIZFINISH+1, ABSOLUTE_VERT, 0, 0, 0, 0); + } + + if( IsWizard() || !m_bModeless || m_bModelessButtons ) + { + // No spaces in WizardMode in order to keep BACK & NEXT together + CPane bottomPane = pane(HORIZONTAL, ABSOLUTE_VERT, IsWizard() ? 0 : 5); + + AddButtons(bottomPane); + // add bottom (button) pane if any controls were added + if(bottomPane->m_paneItems.GetSize() > 0) { + m_RootPane << bottomPane; + } + } + + + + // some Space between Buttons und Gripper + if(m_bGripper) + { + m_RootPane->addItem(paneNull, ABSOLUTE_VERT,0,2); + + if(m_StatusBar.Create(m_pWnd)) + { + m_StatusBar.SetIndicators(auIDStatusBar, + sizeof(auIDStatusBar) / sizeof(UINT)); + m_StatusBar.SetWindowText(_T("")); + RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0); + } + else + { + AfxMessageBox(_T("Error - Statusbar")); + } + } + + if(!m_strRegStore.IsEmpty()) + { + Load(m_strRegStore); + } + + Resize(1,1); // Fix. for 95/98/NT difference + + CRect rcWnd; + GetWindowRect( & rcWnd ); + MoveWindow( rcWnd ); + + return bRet; +} + + +void ETSLayoutPropertySheet::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) +{ + if(m_RootPane.IsValid() && GetTabControl() != 0 ) + { + CRect rcWnd; + GetWindowRect(rcWnd); + + CRect rcClient = GetRect(); + rcWnd-=rcClient; + + // ask for MinMax of all pages + CSize sizePageMax(0,0); + CSize sizePageMin(0,0); + for( int nPage=0; nPageGetSafeHwnd()) ) + { + pPage->SendMessage(WM_GETMINMAXINFO, 0, (LPARAM) &mmi); + + if(mmi.ptMaxTrackSize.x != 0) + { + sizePageMax.cx = min(sizePageMax.cx, mmi.ptMaxTrackSize.x); + } + if(mmi.ptMaxTrackSize.y != 0) + { + sizePageMax.cy = min(sizePageMax.cy, mmi.ptMaxTrackSize.y); + } + if(mmi.ptMinTrackSize.x != 0) + { + sizePageMin.cx = max(sizePageMin.cx, mmi.ptMinTrackSize.x); + } + if(mmi.ptMinTrackSize.y != 0) + { + sizePageMin.cy = max(sizePageMin.cy, mmi.ptMinTrackSize.y); + } + } + } + } + static_cast( m_ItemTab.GetPaneBase() )->m_sizeXMin = sizePageMin.cx; + static_cast( m_ItemTab.GetPaneBase() )->m_sizeYMin = sizePageMin.cy; + + // calculate the needed size of the tabctrl in non-wizard-mode + CRect rcItem(0,0,0,0); + if(!IsWizard()) + { + GetTabControl()->AdjustRect( TRUE, rcItem ); + } + + lpMMI->ptMinTrackSize.x = m_RootPane->getMinConstrainHorz() + rcWnd.Width() + 2*m_sizeRootBorders.cx + + rcItem.Width(); + + lpMMI->ptMinTrackSize.y = m_RootPane->getMinConstrainVert() + rcWnd.Height() + 2*m_sizeRootBorders.cy + + rcItem.Height(); + + // never smaller than inital size! + lpMMI->ptMinTrackSize.x = max(lpMMI->ptMinTrackSize.x, m_rcStart.Width() ); + lpMMI->ptMinTrackSize.y = max(lpMMI->ptMinTrackSize.y, m_rcStart.Height() ); + + // Rest like ETSLayoutMgr + + int maxWidth = m_RootPane->getMaxConstrainHorz(); + int maxHeight = m_RootPane->getMaxConstrainVert(); + + if( maxWidth != -1 ) + { + lpMMI->ptMaxSize.x = sizePageMax.cx + rcWnd.Width()+ 2*m_sizeRootBorders.cx + rcItem.Width() ; + } + + if( maxHeight != -1 ) + { + lpMMI->ptMaxSize.y = sizePageMax.cy + rcWnd.Height() + 2*m_sizeRootBorders.cy + rcItem.Width() ; + } + + lpMMI->ptMaxTrackSize = lpMMI->ptMaxSize; + + } +} + + +void ETSLayoutPropertySheet::OnDestroy() +{ + // Store size/position + if(!m_strRegStore.IsEmpty()) + { + Save(m_strRegStore); + } + m_RootPane = 0; + + CPropertySheet::OnDestroy(); +} + +void ETSLayoutPropertySheet::PostNcDestroy() +{ + if(m_bAutoDestroyPages) + { + // walk all pages and destry them + for( int nPage=0; nPageRelease(); +} + +void ETSLayoutMgr::CPaneBase::operator=( PaneBase* pPane ) +{ + if(m_pPaneHolder) + { + m_pPaneHolder->Release(); + m_pPaneHolder = 0; + } + + if( pPane != 0 ) + m_pPaneHolder = new PaneHolder( pPane ); +} + +void ETSLayoutMgr::CPaneBase::operator=( const CPaneBase& other ) +{ + ASSERT( other.m_pPaneHolder ); + + if(m_pPaneHolder) + { + m_pPaneHolder->Release(); + m_pPaneHolder = 0; + } + + other.m_pPaneHolder->AddRef(); + m_pPaneHolder = other.m_pPaneHolder; +} + +ETSLayoutMgr::PaneBase* ETSLayoutMgr::CPaneBase::operator->() const +{ + ASSERT(m_pPaneHolder); + + if(!m_pPaneHolder) + return 0; + + return (m_pPaneHolder->m_pPane); +} + + + +ETSLayoutMgr::CPane::CPane( ) +{ +} + +ETSLayoutMgr::CPane::CPane( Pane* pPane ) : ETSLayoutMgr::CPaneBase( static_cast(pPane) ) +{ +} + +ETSLayoutMgr::CPane::CPane( const CPane& other ) +{ + operator=(other); +} + +ETSLayoutMgr::CPane::~CPane() +{ +} + +void ETSLayoutMgr::CPane::operator=( Pane* pPane ) +{ + CPaneBase::operator=(pPane); +} + +void ETSLayoutMgr::CPane::operator=( const ETSLayoutMgr::CPane& other ) +{ + ASSERT( other.m_pPaneHolder ); + + if(m_pPaneHolder) + { + m_pPaneHolder->Release(); + m_pPaneHolder = 0; + } + + other.m_pPaneHolder->AddRef(); + m_pPaneHolder = other.m_pPaneHolder; +} + +ETSLayoutMgr::Pane* ETSLayoutMgr::CPane::operator->() const +{ + ASSERT(m_pPaneHolder); + + if(!m_pPaneHolder) + return 0; + + return reinterpret_cast(m_pPaneHolder->m_pPane); +} + +ETSLayoutMgr::CPaneBase ETSLayoutMgr::CPane::ConvertBase() const +{ + ASSERT(m_pPaneHolder); + return CPaneBase( m_pPaneHolder->m_pPane ); +} + +ETSLayoutMgr::CPane& ETSLayoutMgr::CPane::operator<< ( const ETSLayoutMgr::CPane pPane ) +{ + GetPane()->addPane( pPane, (ETSLayoutMgr::layResizeMode)pPane->m_modeResize, pPane->m_sizeSecondary); + return (*this); +} + +ETSLayoutMgr::CPane& ETSLayoutMgr::CPane::operator<< ( const ETSLayoutMgr::CPaneBase pItem ) +{ + GetPane()->addPane( pItem ); + return (*this); +} diff --git a/misc/windows/.svn/text-base/ETSLayout.h.svn-base b/misc/windows/.svn/text-base/ETSLayout.h.svn-base new file mode 100644 index 0000000..0e641a7 --- /dev/null +++ b/misc/windows/.svn/text-base/ETSLayout.h.svn-base @@ -0,0 +1,964 @@ +//////////////////////////////////////////// +// ___ ____ _________________ // +// / _/_ _// _______________/ // +// / _/ / / / / ___ ___ ____ // +// /__/ /_/ / / / // _/_ _/ // +// _________/ / / / // _/ / / // +// (c) 1998-2000_/ /___//_/ /_/ // +// // +//////////////////////////////////////////// +// all rights reserved // +//////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// ETSLayoutDialog +// +// A class for smart layouting of Dialogs and such +// +// USAGE: See LayoutMgr.html +// +// AUTHOR: Erwin Tratar +// +// DISCLAIMER: +// +// This Sourcecode and all accompaning material is ©1998-1999 Erwin Tratar. +// All rights reserved. +// +// The source code may be used in compiled form in any way you desire +// (including usage in commercial applications), providing that your +// application adds essential code (i.e. it is not only a wrapper) to the +// functionality found here +// +// Redistribution of the sourcecode itself, publication in any media or +// inclusion in a library requires the authors expressed written consent. +// You may not sale this code for profit. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. USE IT +// AT YOUR OWN RISK! THE AUTHOR ACCEPTS NO LIABILITY FOR ANY DAMAGE/LOSS OF +// BUSINESS THAT THIS PRODUCT MAY CAUSE. + + +#if !defined(ETS_LAYOUTMGR_INCLUDED_) +#define ETS_LAYOUTMGR_INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// DialogMgr.h : header file +// + +namespace ETSLayout +{ + +#ifdef CS_HELP + typedef ETSCSHelpDialog CBaseDialog; + typedef ETSCSHelpFormView CBaseFormView; + typedef ETSCSHelpDlgBar CBaseDialogBar; + typedef ETSCSHelpPropPage CBasePropertyPage; +#else + typedef CDialog CBaseDialog; + typedef CFormView CBaseFormView; + typedef CDialogBar CBaseDialogBar; + typedef CPropertyPage CBasePropertyPage; +#endif +} + +// Support for CBCGDialogBar instead of CDialogBar available: +// you just have to change the typedef to CBaseDialogBar + +#ifndef ETSGUI_EXT_CLASS +#define ETSGUI_EXT_CLASS +#endif + +#include + +// Support for CBCGDialogBar instead of CDialogBar + +/** + * Controls whether the Icon is automatically set to IDR_MAINFRAME + */ +#define _AUTO_SET_ICON + +/** + * Forward class declarations + */ +class ETSLayoutDialog; +class ETSLayoutDialogBar; +class ETSLayoutFormView; +class ETSLayoutMgr; +class ETSLayoutPropertyPage; +class ETSLayoutPropertySheet; + + +/** + * These are NOOPs now + */ +#define DECLARE_LAYOUT() +#define IMPLEMENT_LAYOUT() + +/** + * This is the default border size between the panes. You + * may override it in Pane constructor, but it is the + * fixed border around the root pane + */ +const int nDefaultBorder = 5; + +/** + * The minimum size for not ABSOLUTE_XXX items + */ +const int nMinConstrain = 5; + +class ETSGUI_EXT_CLASS ETSLayoutMgr +{ +public: + + enum layResizeMode { + GREEDY = 0, // Will eat up as much as it can + ABSOLUTE_HORZ = 1 << 0, // Horizontal size is absolute + RELATIVE_HORZ = 1 << 1, // Horizontal size in percent + ABSOLUTE_VERT = 1 << 2, // Vertical size is absolute + RELATIVE_VERT = 1 << 3, // Vertical size in percent + + NORESIZE = ABSOLUTE_HORZ | ABSOLUTE_VERT, + + SIZE_MASK = NORESIZE, + + ALIGN_LEFT = 1 << 4, // following only for NORESIZE + ALIGN_RIGHT = 1 << 5, + ALIGN_TOP = 1 << 6, + ALIGN_BOTTOM = 1 << 7, + + ALIGN_HCENTER = ALIGN_LEFT | ALIGN_RIGHT, + ALIGN_VCENTER = ALIGN_TOP | ALIGN_BOTTOM, + + ALIGN_CENTER = ALIGN_HCENTER | ALIGN_VCENTER, + + ALIGN_FILL_HORZ = 1 << 8, + ALIGN_FILL_VERT = 1 << 9, + ALIGN_FILL = ALIGN_FILL_HORZ | ALIGN_FILL_VERT, + +/* TRACKER_LEFT = 1 << 10, // not yet. May allow tracking of borders + TRACKER_RIGHT = 1 << 11, // between items in the future + TRACKER_TOP = 1 << 12, + TRACKER_BOTTOM = 1 << 13, +*/ + }; + + enum layOrientation { + HORIZONTAL, + VERTICAL + }; + + /** + * This is the base class for all kind of panes. + */ + class ETSGUI_EXT_CLASS PaneBase { + friend class ETSLayoutMgr; + friend class CPaneBase; + friend class CPane; + + public: + + /** + * Informs the caller how much of the given space this pane would + * like to receive in horizontal direction + */ + virtual int getConstrainHorz(int sizeParent) = 0; + + + /** + * Informs the caller how much of the given space this pane would + * like to receive in vertical direction + */ + virtual int getConstrainVert(int sizeParent) = 0; + + /** + * Informs the caller how much of the given space this pane + * minimally need. This would be an absolute Value if + * the mode contains ABSOLUTE_HORZ or an explicit minimum + * value, else nMinConstrain + */ + virtual int getMinConstrainHorz() = 0; + /** + * Informs the caller if there is an restriction for maximum + * space this pane needs. Return -1 for unrestricted (GREEDY + * or RELATIVE) + */ + virtual int getMaxConstrainHorz() = 0; + + /** + * Informs the caller how much of the given space this pane + * minimally need. This would be an absolute Value if + * the mode contains ABSOLUTE_VERT or an explicit minimum + * value, else nMinConstrain + */ + virtual int getMinConstrainVert() = 0; + + /** + * Informs the caller if there is an restriction for maximum + * space this pane needs. Return -1 for unrestricted (GREEDY + * or RELATIVE) + */ + virtual int getMaxConstrainVert() = 0; + + /** + * This will do the actual resize operation after the + * caller computed a new area for this pane + */ + virtual bool resizeTo(CRect& rcNewArea) = 0; + + /** + * Constructor needed pointer to LayoutManager + */ + PaneBase( ETSLayoutMgr* pMgr ) { m_pMgr = pMgr; }; + + /** + * Virtual destructor needed in Container operations + */ + virtual ~PaneBase() {}; + + /** + * Returs the Resize Mode of this pane + */ + DWORD modeResize() { return m_modeResize; }; + + protected: + /** + * How this Item will be resized, a combination of the flags above + */ + DWORD m_modeResize; + + /** + * A pointer to the holding LayoutManager derivate + */ + ETSLayoutMgr* m_pMgr; + }; + + /** + * CPaneBase represents an autopointer to a PaneBase. Use this and you won't have to worry + * about cleaning up any Panes. Also this autopointer lets you return Pane objects + * from function without using pointers (at least you won't see them :) ) + */ + struct ETSGUI_EXT_CLASS PaneHolder + { + PaneHolder(PaneBase* pPane ); + ~PaneHolder(); + + void AddRef(); + void Release(); + + PaneBase* m_pPane; + long m_nRefCount; + }; + + class ETSGUI_EXT_CLASS CPaneBase + { + protected: + PaneHolder* m_pPaneHolder; + + public: + // Standardconstructor + CPaneBase( ); + CPaneBase( PaneBase* pPane ); + CPaneBase( const CPaneBase& other ); + + ~CPaneBase(); + + void operator=( PaneBase* pPane ); + void operator=( const CPaneBase& other ); + PaneBase* operator->() const; + PaneBase* GetPaneBase() { return operator->(); } + + bool IsValid() { return (m_pPaneHolder != 0); } + bool operator !() { return (m_pPaneHolder == 0); } + + }; + + class Pane; + class ETSGUI_EXT_CLASS CPane : public CPaneBase + { + public: + // Standardconstructor + CPane( ); + CPane( Pane* pPane ); + CPane( const CPane& other ); + + ~CPane(); + + void operator=( Pane* pPane ); + void operator=( const CPane& other ); + Pane* operator->() const; + Pane* GetPane() { return operator->(); } + + CPaneBase ConvertBase() const; + + CPane& operator<< ( const CPane pPane ); + CPane& operator<< ( const CPaneBase pItem ); + }; + + + + /** + * PaneItem represents a single control + */ + class ETSGUI_EXT_CLASS PaneItem : public PaneBase { + friend class ETSLayoutMgr; + friend class Pane; + protected: + /** + * Creates a new PaneItem from an Control. If sizeX or sizeY are 0 + * and modeResize is ABSOLUTE will copy the current dimensions of + * the control to m_sizeX/Y. So the appearance does not change + * from the Dialog Editor + */ + PaneItem( CWnd* pWnd, ETSLayoutMgr* pMgr, layResizeMode modeResize = GREEDY, int sizeX=0, int sizeY=0, int sizeXMin=0, int sizeYMin=0); + + /** + * If your control is not mapped you can name it by its ChildID. Pass + * the pMgr to receive the CWnd* of nID. + * The rest as stated above + */ + PaneItem( UINT nID, ETSLayoutMgr* pMgr, layResizeMode modeResize = GREEDY, int sizeX=0, int sizeY=0, int sizeXMin=0, int sizeYMin=0); + + + public: + /** + * see PaneBase + */ + virtual int getConstrainHorz(int sizeParent); + virtual int getConstrainVert(int sizeParent); + virtual int getMinConstrainHorz(); + virtual int getMinConstrainVert(); + virtual int getMaxConstrainHorz(); + virtual int getMaxConstrainVert(); + virtual bool resizeTo(CRect& rcNewArea); + + bool isDummy() { return (m_hwndCtrl == 0); } + + protected: + friend class ETSLayoutPropertySheet; + + /** + * The horizontal size of the control (see m_modeResize) + */ + int m_sizeX; + int m_sizeXMin; + + /** + * The vertical size of the control (see m_modeResize) + */ + int m_sizeY; + int m_sizeYMin; + + /** + * Child Control pointer + */ + HWND m_hwndCtrl; + + /** + * Combo box needs special treatment + */ + bool m_bComboSpecial; + }; + + + /** + * This class encapsulates a Subpane (and indeed the root Pane too) + * it is a container of PaneBase* which it will recursivly resize + */ + class ETSGUI_EXT_CLASS Pane : public PaneBase { + friend class ETSLayoutMgr; + friend class CPaneBase; + friend class CPane; + friend class ETSLayoutPropertySheet; + + protected: + /** + * Tell the pane in which direction it is positioned. A HORIZONTAL pane + * arranges it's subpanes from left to right, a VERTICAL from top to bottom + */ + Pane( ETSLayoutMgr* pMgr, layOrientation orientation, int sizeBorder = nDefaultBorder, int sizeExtraBorder = 0 ); + + public: + /** + * If your control is not mapped you can name it by its ChildID. Pass + * the pMgr to receive the CWnd* of nID. + * The rest as stated above + */ + bool addItem( UINT nID, layResizeMode modeResize = GREEDY, int sizeX=0, int sizeY=0, int sizeXMin=-1, int sizeYMin=-1); + + /** + * Creates a new PaneItem from an Control. If sizeX or sizeY are 0 + * and modeResize is ABSOLUTE will copy the current dimensions of + * the control to m_sizeX/Y. So the appearance does not change + * from the Dialog Editor + */ + bool addItem( CWnd* pWnd, layResizeMode modeResize = GREEDY, int sizeX=0, int sizeY=0, int sizeXMin=-1, int sizeYMin=-1); + + + /** + * Add a whitespace Item (paneNull) of variable size with + * a minimum size of 0 + */ + bool addItemGrowing(); + + /** + * Add a whitespace Item (paneNull) with fixed size + */ + bool addItemFixed(int size); + + /** + * Add a whitespace Item (paneNull) of fixed size based on the + * current layout (as in the dialog template). Based on the layout + * of the pane vertical or horizontal spacing is considered + * + * First argument is the left (top) item for a HORIZONTAL (VERTICAL) pane + */ + bool addItemSpaceBetween( CWnd* pWndFirst, CWnd* pWndSecond ); + bool addItemSpaceBetween( UINT nIDFirst, UINT nIDSecond ); + + + /** + * Add a whitespace Item (paneNull) of fixed size based on the + * size of another item + */ + bool addItemSpaceLike( CWnd* pWnd ); + bool addItemSpaceLike( UINT nID ); + + + /** + * Add an item to the pane, appending at the end. This may be either obtained + * by a call to ETSLayoutMgr::item() or one of the ETSLayoutMgr::paneXXX() calls + */ + bool addPane( CPaneBase pItem ); + bool addPane( CPane pSubpane, layResizeMode modeResize, int sizeSecondary /* = 0 */); + + virtual int getConstrainHorz(int sizeParent); + virtual int getConstrainVert(int sizeParent); + virtual int getMinConstrainHorz(); + virtual int getMinConstrainVert(); + virtual int getMaxConstrainHorz(); + virtual int getMaxConstrainVert(); + virtual bool resizeTo(CRect& rcNewArea); + + /** + * The destructor takes care of destroying all Subpanes and items + */ + virtual ~Pane(); + + /** + * Access to the orientation of this pane + */ + layOrientation getOrientation() { return m_Orientation; }; + + + protected: + + int resizeToAbsolute(int& availSpace, CArray& sizePrimary, + CArray& sizeMin, CArray& sizeMax); + + bool resizeToRelative(int& availSpace, CArray& sizePrimary, + CArray& sizeMin, CArray& sizeMax); + + bool resizeToGreedy( int& availSpace, int nGreedy, CArray& sizePrimary, + CArray& sizeMin, CArray& sizeMax); + + /** + * The orientation of the pane. Keep in mind that all subpanes + * must have the complementary orientation, i.e. a VERTICAL + * pane must have all HORIZONTAL SubPanes (or normal Items + * of course) + */ + layOrientation m_Orientation; + + /** + * This array holds the pointers to the Items/SubPanes + */ + CArray m_paneItems; + + /** + * The secondary constrain + */ + int m_sizeSecondary; + + /** + * Size of gap between childs + */ + int m_sizeBorder; + int m_sizeExtraBorder; + }; + + + /** + * This class encapsulates a Subpane which is a Tab + * it will use calls to AdjustRect to position it's + * childs + */ + class ETSGUI_EXT_CLASS PaneTab : public Pane + { + friend class ETSLayoutMgr; + + protected: + /** + * Tell the pane in which direction it is positioned. A HORIZONTAL pane + * arranges it's subpanes from left to right, a VERTICAL from top to bottom + */ + PaneTab( CTabCtrl* pTab, ETSLayoutMgr* pMgr, layOrientation orientation, int sizeBorder = nDefaultBorder, int sizeExtraBorder = 0 ); + + public: + virtual int getConstrainHorz(int sizeParent); + virtual int getConstrainVert(int sizeParent); + virtual int getMinConstrainHorz(); + virtual int getMinConstrainVert(); + virtual int getMaxConstrainHorz(); + virtual int getMaxConstrainVert(); + virtual bool resizeTo(CRect& rcNewArea); + + private: + CTabCtrl* m_pTab; + }; + + /** + * This class encapsulates a Subpane which is a Static + * it will use calls to AdjustRect to position it's + * childs + */ + class ETSGUI_EXT_CLASS PaneCtrl : public Pane + { + friend class ETSLayoutMgr; + protected: + /** + * Tell the pane in which direction it is positioned. A HORIZONTAL pane + * arranges it's subpanes from left to right, a VERTICAL from top to bottom + */ + PaneCtrl( CWnd* pCtrl, ETSLayoutMgr* pMgr, layOrientation orientation, int sizeBorder = nDefaultBorder, int sizeExtraBorder = 0, int sizeTopExtra = 0); + PaneCtrl( UINT nID, ETSLayoutMgr* pMgr, layOrientation orientation, int sizeBorder = nDefaultBorder, int sizeExtraBorder = 0, int sizeTopExtra = 0 ); + + public: + + virtual int getConstrainHorz(int sizeParent); + virtual int getConstrainVert(int sizeParent); + virtual int getMinConstrainHorz(); + virtual int getMinConstrainVert(); + virtual int getMaxConstrainHorz(); + virtual int getMaxConstrainVert(); + virtual bool resizeTo(CRect& rcNewArea); + + private: + HWND m_hwndCtrl; + int m_sizeTopExtra; + }; + + + + + ETSLayoutMgr(CWnd* pWnd) { m_pWnd = pWnd; m_sizeRootBorders = CSize(5,5); }; + virtual ~ETSLayoutMgr(); + + virtual CRect GetRect() { CRect r; m_pWnd->GetClientRect(r); return r; }; + CWnd* m_pWnd; + CWnd* GetWnd() { return m_pWnd; }; + void setRootBorders(int cx, int cy) { m_sizeRootBorders = CSize(cx,cy); }; + + /** + * Pass this for a pseudo Pane with no content + */ + static CWnd* paneNull; + + /** + * Loads the current position and size from the registry using a supplied + * key. Will be loaded with AfxGetApp()->WriteProfileXXX(). You may + * specify a subfolder (e.g. Load( _T("MyDialog\\Layout") ); ). Will + * load the following keys: + * + * - lpstrRegKey+"SizeX"; + * - lpstrRegKey+"SizeY"; + * - lpstrRegKey+"PosX"; + * - lpstrRegKey+"PosY"; + * + * Is automatically called during OnActivate() if key specified in + * constructor. + */ + bool Load(LPCTSTR lpstrRegKey); + + /** + * Store the current position and size to the registry using a supplied + * key. Will be stored with AfxGetApp()->WriteProfileXXX(). You may + * specify a subfolder (e.g. Save( _T("MyDialog\\Layout") ); ). Will + * create the following keys: + * + * - lpstrRegKey+"SizeX"; + * - lpstrRegKey+"SizeY"; + * - lpstrRegKey+"PosX"; + * - lpstrRegKey+"PosY"; + * + * Is automatically called during DestroyWindow() if key specified in + * constructor. + */ + bool Save(LPCTSTR lpstrRegKey); + + /** + * Updates the layout after you specify the new + * layout + */ + virtual void UpdateLayout(); + virtual void UpdateLayout(CPane p) { + if(m_RootPane.IsValid()) + { + // free old root + m_RootPane = 0; + } + m_RootPane = p; + UpdateLayout(); + } + + /** + * Does the actual Layout, called from OnSize() + * Default implementation does nothing, use + * IMPLEMENT_LAYOUT in your derived class (see above) + */ + virtual void Layout(CRect& rcClient); + + + /** + * Erasing only the these parts of the client area where + * there is no child window. Extra-code for group-boxes + * included! + */ + void EraseBkgnd(CDC* pDC); + + /** + * Helperfunctions for the stream-interface. For usage see sample Application + * and/or documentation. + */ + + /** + * Create a new Pane. You may specify the resize + * mode for both directions. If you add modes for the secondary direction + * (i.e. *_VERT for a HORIZONTAL pane) then sizeSecondary is used as it's + * size. If you do not specify sizeSecondary and the mode is ABSOLUTE_VERT + * it will be computed as the maximum Height of all SubPanes (the same is + * true for VERTICAL panes and subpanes with *_HORZ) + */ + CPane pane( layOrientation orientation, layResizeMode modeResize = GREEDY, int sizeBorder = nDefaultBorder, int sizeExtraBorder = 0, int sizeSecondary = 0); + + /** + * Create one of the special control panes. Parameter are like pane(). For + * additional information see documentation + */ + CPane paneTab( CTabCtrl* pTab, layOrientation orientation, layResizeMode modeResize = GREEDY, int sizeBorder = nDefaultBorder, int sizeExtraBorder = 0, int sizeSecondary = 0); + CPane paneCtrl( UINT nID, layOrientation orientation, layResizeMode modeResize = GREEDY, int sizeBorder = nDefaultBorder, int sizeExtraBorder = 0, int sizeTopExtra = 0, int sizeSecondary = 0); + CPane paneCtrl( CWnd* pCtrl, layOrientation orientation, layResizeMode modeResize = GREEDY, int sizeBorder = nDefaultBorder, int sizeExtraBorder = 0, int sizeTopExtra = 0, int sizeSecondary = 0); + + /** + * Creates a new PaneItem for an Control. If sizeX or sizeY are 0 + * and modeResize is ABSOLUTE will copy the current dimensions of + * the control to m_sizeX/Y. So the appearance does not change + * from the Dialog Editor. size*Min = -1 means: do not make smaller + * than in Dialog Template. + */ + CPaneBase item(UINT nID, layResizeMode modeResize = GREEDY, int sizeX =0, int sizeY =0, int sizeXMin =-1, int sizeYMin =-1); + CPaneBase item(CWnd* pWnd, layResizeMode modeResize = GREEDY, int sizeX =0, int sizeY =0, int sizeXMin =-1, int sizeYMin =-1); + + + /** + * Add a whitespace Item (paneNull) of variable size with + * a minimum size of 0 + */ + CPaneBase itemGrowing(layOrientation orientation); + + /** + * Add a whitespace Item (paneNull) with fixed size + */ + CPaneBase itemFixed(layOrientation orientation, int sizePrimary); + + /** + * Add a whitespace Item (paneNull) of fixed size based on the + * current layout (as in the dialog template). Based on the layout + * of the pane vertical or horizontal spacing is considered + * + * First argument is the left (top) item for a HORIZONTAL (VERTICAL) pane + */ + CPaneBase itemSpaceBetween( layOrientation orientation, CWnd* pWndFirst, CWnd* pWndSecond ); + CPaneBase itemSpaceBetween( layOrientation orientation, UINT nIDFirst, UINT nIDSecond ); + + /** + * Add a whitespace Item (paneNull) of fixed size based on the + * size of another item + */ + CPaneBase itemSpaceLike( layOrientation orientation, CWnd* pWnd ); + CPaneBase itemSpaceLike( layOrientation orientation, UINT nID ); + +protected: + /** + * This holds the root pane. Fill in InitDialog() + */ + CPane m_RootPane; + + /** + * Create a root pane + */ + CPane CreateRoot(layOrientation orientation, int sizeBorder = nDefaultBorder, int sizeExtraBorder = 0 ) + { + if(m_RootPane.IsValid()) + { + // free old root + m_RootPane = 0; + } + m_RootPane = new Pane( this, orientation, sizeBorder, sizeExtraBorder); + return m_RootPane; + } + + /** + * Key in Registry where to store Size + */ + CString m_strRegStore; + + /** + * Borders around root + */ + CSize m_sizeRootBorders; +}; + +inline ETSLayoutMgr::layResizeMode operator|(const ETSLayoutMgr::layResizeMode m1, + const ETSLayoutMgr::layResizeMode m2) + { return (ETSLayoutMgr::layResizeMode)( (DWORD)m1|(DWORD)m2); } + + +/** + * Base class for the Layout function. Derive your own class + * from this or derive it from CDialog and modify _all_ + * references to CDialog to ETSLayoutDialog + */ +class ETSGUI_EXT_CLASS ETSLayoutDialog : public ETSLayout::CBaseDialog, protected ETSLayoutMgr +{ +// Construction +public: + ETSLayoutDialog(UINT nID, CWnd* pParent = NULL, LPCTSTR strName = NULL, bool bGripper = true); // standard constructor + +// Dialog Data + //{{AFX_DATA(ETSLayoutDialog) + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(ETSLayoutDialog) + //}}AFX_VIRTUAL + +// Implementation +protected: + // Generated message map functions + //{{AFX_MSG(ETSLayoutDialog) + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI); + afx_msg BOOL OnEraseBkgnd(CDC* pDC); + virtual BOOL OnInitDialog(); + afx_msg void OnDestroy(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + + virtual CRect GetRect(); + + bool m_bGripper; + CStatusBar m_StatusBar; +}; + + +/** + * Base class for the Layout function. Derive your own class + * from this or derive it from CDialog and modify _all_ + * references to CFormView to ETSLayoutFormView + */ +class ETSGUI_EXT_CLASS ETSLayoutFormView : public ETSLayout::CBaseFormView, public ETSLayoutMgr +{ +// Construction + DECLARE_DYNAMIC(ETSLayoutFormView) +public: + ETSLayoutFormView(UINT nID, LPCTSTR strName = NULL); // standard constructor + virtual ~ETSLayoutFormView(); + +// virtual void UpdateLayout(); + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(ETSLayoutDialog) + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(ETSLayoutDialog) + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg BOOL OnEraseBkgnd(CDC* pDC); + afx_msg void OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + + +/** + * Base class for the Layout function. Derive your own class + * from this or derive it from CBCGDialogBar/CDialogBar and + * modify _all_ references to CBCGDialogBar/CDialogBar to + * ETSLayoutDialogBar + */ +class ETSGUI_EXT_CLASS ETSLayoutDialogBar : public ETSLayout::CBaseDialogBar, protected ETSLayoutMgr +{ +// Construction +public: +#ifdef CS_HELP + ETSLayoutDialogBar(UINT nID); +#else + ETSLayoutDialogBar(); +#endif + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(ETSLayoutDialogBar) + virtual CSize CalcDynamicLayout(int nLength, DWORD dwMode); + //}}AFX_VIRTUAL + + /** + * Override this to define Layout + */ + virtual BOOL Initialize() { return false; }; + virtual void UpdateLayout(); + +// Implementation +protected: + // Generated message map functions + //{{AFX_MSG(ETSLayoutDialogBar) + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnDestroy(); + afx_msg BOOL OnEraseBkgnd(CDC* pDC); + //}}AFX_MSG + LRESULT OnInitDialog(WPARAM, LPARAM); + DECLARE_MESSAGE_MAP() + + virtual CRect GetRect(); + bool m_bInitialized; +}; + + + +/************************************************** + ** ! the code is only tested for modal sheets ! ** + **************************************************/ + + +/** + * Resizable PropertySheet. Use this class standalone + * or as your base class (instead CProptertySheet) + */ +class ETSGUI_EXT_CLASS ETSLayoutPropertySheet : public CPropertySheet, protected ETSLayoutMgr +{ + DECLARE_DYNAMIC(ETSLayoutPropertySheet) + +// Construction +public: + ETSLayoutPropertySheet(UINT nIDCaption, CWnd *pParentWnd = NULL, UINT iSelectPage = 0, LPCTSTR strName=NULL, bool bGripper=true); + ETSLayoutPropertySheet(LPCTSTR pszCaption, CWnd *pParentWnd = NULL, UINT iSelectPage = 0, LPCTSTR strName=NULL, bool bGripper=true); + +// Operationen +public: + void SetAutoDestroy() { m_bAutoDestroy = true; } + void SetAutoDestroyPages() { m_bAutoDestroyPages = true; } + void ModelessWithButtons() { m_bModelessButtons = true; } +// Overrides + virtual void AddMainArea(CPane paneRoot, CPaneBase itemTab); + virtual void AddButtons(CPane paneBottom); + + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(ETSLayoutPropertySheet) + public: + virtual BOOL OnInitDialog(); + virtual void PostNcDestroy(); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~ETSLayoutPropertySheet(); + + // Generated message map functions +protected: + //{{AFX_MSG(ETSLayoutPropertySheet) + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI); + afx_msg void OnDestroy(); + afx_msg BOOL OnEraseBkgnd(CDC* pDC); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + + void Resize(int cx, int cy); + +friend class ETSLayoutPropertyPage; + + void Init(LPCTSTR strName, bool bGripper); + CRect m_rcStart; + CRect m_rcPage; + bool m_bGripper; + CStatusBar m_StatusBar; + CPaneBase m_ItemTab; + bool m_bAutoDestroy; + bool m_bAutoDestroyPages; + bool m_bModelessButtons; +}; + +/** + * Base class for the Layout function. Derive your own class + * from this or derive it from CPropertyPage and + * modify _all_ references to CPropertyPage to + * ETSLayoutPropertyPage + */ +class ETSGUI_EXT_CLASS ETSLayoutPropertyPage : public ETSLayout::CBasePropertyPage, protected ETSLayoutMgr +{ +friend class ETSLayoutPropertySheet; + + DECLARE_DYNCREATE(ETSLayoutPropertyPage) + +// Konstruktion +public: + ETSLayoutPropertyPage( ); + ETSLayoutPropertyPage( UINT nIDTemplate, UINT nIDCaption = 0 ); + ETSLayoutPropertyPage( LPCTSTR lpszTemplateName, UINT nIDCaption = 0 ); + + ~ETSLayoutPropertyPage(); + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(ETSLayoutPropertyPage) + public: + virtual BOOL OnSetActive(); + //}}AFX_VIRTUAL + +// Implementation +protected: + // Generated message map functions + //{{AFX_MSG(ETSLayoutPropertyPage) + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI); + virtual BOOL OnInitDialog(); + afx_msg BOOL OnEraseBkgnd(CDC* pDC); + afx_msg void OnWindowPosChanging( WINDOWPOS* lpwndpos ); + afx_msg void OnDestroy(); + afx_msg void OnWindowPosChanged(WINDOWPOS FAR* lpwndpos); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + + virtual CRect GetRect(); + bool m_bLockMove; + bool m_bResetBuddyOnNextTimeVisible; +}; + + + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(ETS_LAYOUTMGR_INCLUDED_) diff --git a/misc/windows/.svn/text-base/browse_for_directory.cpp.svn-base b/misc/windows/.svn/text-base/browse_for_directory.cpp.svn-base new file mode 100644 index 0000000..9f69549 --- /dev/null +++ b/misc/windows/.svn/text-base/browse_for_directory.cpp.svn-base @@ -0,0 +1,33 @@ +#include "stdafx.h" +#include "browse_for_directory.h" + +int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData) +{ + if (uMsg == BFFM_INITIALIZED) + SendMessage(hwnd, BFFM_SETSELECTION, true, lpData); + return 0; +} + +int browse_for_directory(HWND hWnd, const std::string& title, std::string& directory) +{ + BROWSEINFO bi; + ZeroMemory(&bi, sizeof(BROWSEINFO)); + bi.hwndOwner = hWnd; + bi.lpszTitle = title.c_str(); + bi.ulFlags = BIF_NEWDIALOGSTYLE | BIF_RETURNONLYFSDIRS; + bi.lpfn = BrowseCallbackProc; + bi.lParam = reinterpret_cast(directory.c_str()); + ITEMIDLIST* idl = SHBrowseForFolder(&bi); + if (!idl) + return 1; + char path[MAX_PATH]; + if (!SHGetPathFromIDList(idl, path)) + *path = 0; + LPMALLOC lpm; + if (SHGetMalloc(&lpm) == NOERROR) + lpm->Free(idl); + if (!*path) + return 1; + directory = path; + return 0; +} \ No newline at end of file diff --git a/misc/windows/.svn/text-base/browse_for_directory.h.svn-base b/misc/windows/.svn/text-base/browse_for_directory.h.svn-base new file mode 100644 index 0000000..73f51b8 --- /dev/null +++ b/misc/windows/.svn/text-base/browse_for_directory.h.svn-base @@ -0,0 +1,3 @@ +#pragma once + +int browse_for_directory(HWND, const std::string& title, std::string& directory); diff --git a/misc/windows/.svn/text-base/nt_service.cpp.svn-base b/misc/windows/.svn/text-base/nt_service.cpp.svn-base new file mode 100644 index 0000000..94fbb85 --- /dev/null +++ b/misc/windows/.svn/text-base/nt_service.cpp.svn-base @@ -0,0 +1,66 @@ +#include "nt_service.h" + +#include + +int nt_service_install(const char* name) +{ + SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + if (!scm) + return 1; + char file_name[MAX_PATH]; + GetModuleFileName(NULL, file_name, MAX_PATH); + SC_HANDLE service = CreateService(scm, + name, + name, + SERVICE_ALL_ACCESS, + SERVICE_WIN32_OWN_PROCESS, + SERVICE_AUTO_START, + SERVICE_ERROR_NORMAL, + file_name, + NULL, + NULL, + NULL, + "NT AUTHORITY\\LocalService", + NULL); + if (!service) + { + service = CreateService(scm, + name, + name, + SERVICE_ALL_ACCESS, + SERVICE_WIN32_OWN_PROCESS, + SERVICE_AUTO_START, + SERVICE_ERROR_NORMAL, + file_name, + NULL, + NULL, + NULL, + NULL, + NULL); + } + if (!service) + { + CloseServiceHandle(scm); + return 1; + } + CloseServiceHandle(service); + CloseServiceHandle(scm); + return 0; +} + +int nt_service_uninstall(const char* name) +{ + SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + if (!scm) + return 1; + int result = 1; + SC_HANDLE service = OpenService(scm, name, DELETE); + if (service) + { + if (DeleteService(service)) + result = 0; + CloseServiceHandle(service); + } + CloseServiceHandle(scm); + return result; +} diff --git a/misc/windows/.svn/text-base/nt_service.h.svn-base b/misc/windows/.svn/text-base/nt_service.h.svn-base new file mode 100644 index 0000000..17f21d5 --- /dev/null +++ b/misc/windows/.svn/text-base/nt_service.h.svn-base @@ -0,0 +1,4 @@ +#pragma once + +int nt_service_install(const char* name); +int nt_service_uninstall(const char* name); diff --git a/misc/windows/ETSLayout.cpp b/misc/windows/ETSLayout.cpp new file mode 100644 index 0000000..e68bdad --- /dev/null +++ b/misc/windows/ETSLayout.cpp @@ -0,0 +1,3058 @@ +//////////////////////////////////////////// +// ___ ____ _________________ // +// / _/_ _// _______________/ // +// / _/ / / / / ___ ___ ____ // +// /__/ /_/ / / / // _/_ _/ // +// _________/ / / / // _/ / / // +// (c) 1998-2000_/ /___//_/ /_/ // +// // +//////////////////////////////////////////// +// all rights reserved // +//////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// ETSLayoutDialog +// +// A class for smart layouting of Dialogs and such +// +// USAGE: See LayoutMgr.html +// +// AUTHOR: Erwin Tratar +// +// DISCLAIMER: +// +// This Sourcecode and all accompaning material is ©1998-1999 Erwin Tratar. +// All rights reserved. +// +// The source code may be used in compiled form in any way you desire +// (including usage in commercial applications), providing that your +// application adds essential code (i.e. it is not only a wrapper) to the +// functionality found here +// +// Redistribution of the sourcecode itself, publication in any media or +// inclusion in a library requires the authors expressed written consent. +// You may not sale this code for profit. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. USE IT +// AT YOUR OWN RISK! THE AUTHOR ACCEPTS NO LIABILITY FOR ANY DAMAGE/LOSS OF +// BUSINESS THAT THIS PRODUCT MAY CAUSE. +// +// +// HISTORY: +// 1998/05/1 Initial Release +// 1998/05/13 Added ability to have a Pane with a control +// 1998/05/13 Added better support for TabControls +// 1998/05/14 automatically set Icon to IDR_MAINFRAME +// 1998/05/19 no flicker on restoring position in OnInitialUpdate +// Changed procedure for load/save, see constructor +// 1998/10/02 Added support for Maximum (tracking) size +// 1998/10/02 Much improved handling regarding RELATIVE/GREEDY +// /w critical minimum size +// 1998/10/02 turn on/off gripper at lower right corner +// 1998/10/05 Support for user defined minimum size for items +// (was hardcoded 5 before) +// 1998/10/07 Fix for FormViews +// 1998/10/31 Support for SECDialogBar/CDialogBar +// 1998/10/31 simplified interface +// 1998/10/31 Advanced positioning options +// 1998/10/31 Added paneNull for empty Pane (former: NULL) +// 1998/11/20 Swapped ETSLayoutDialog constructor parameters +// 1998/11/20 Added Pane::addItemSpaceBetween +// [Leo Zelevinsky] +// 1998/11/24 Added fixup for greedy panes +// 1998/11/24 addItemSpaceBetween now subtracts 2*nDefaultBorder +// 1998/11/24 addGrowing() added as a shortcut for a paneNull +// 1998/11/24 simplified interface: no more PaneBase:: / Pane:: +// needed +// 1998/11/24 added FILL_* Modes +// 1998/11/24 improved maximum size handling for greedy panes +// 1998/11/25 Fixup of greedy panes caused infinite loop in some +// cases +// 1999/01/07 addItemSpaceLike() added +// 1999/04/03 Fixed ETSLayoutFormView memory leak +// 1999/04/07 Fixed ALIGN_xCENTER +// 1999/04/08 New simple stream-interface added +// 1999/04/09 Added support for an empty Status-Bar for resizing +// instead of a gripper in the lower right corner +// [Andreas Kapust] +// 1999/04/11 New code for much less flickering, OnEraseBkgnd() +// overidden for this task +// 1999/05/12 Split Layout code into understandable pieces and adding +// a lot of comments +// 1999/06/20 ABSOLUTE_X + ALIGN_FILL_X expands item if there is any +// left space (after all Abs/Rel/Greedy processing is done) +// 1999/10/06 Changed Load() and Save() to use WINDOWPLACEMENT +// [Keith Bussell] +// 1999/11/18 Added possibility to add panes of the same orientation +// to another pane. This merges both panes in one big +// pane with the same orientation +// 1999/11/18 Added support for BCGDialogBar (only with BCG > 4.52!) +// 1999/11/25 Addes support for PropertyPages/Sheets. Uses some code +// of a code submission from Anreas Kapust +// 1999/11/25 Renamed classes to ETSLayoutXXX +// 1999/11/25 Use CreateRoot() and Root() instead of m_pRootPane in +// derived class. +// 1999/11/26 Added autopointer support. No need to use normal pointers +// when defining layout anymore. Changed m_pRootPane to +// m_RootPane +// 1999/11/26 Bug in Fixup Greedy II with multiple GREEDY panes and one +// of them min/max limited +// 1999/11/28 Fixed PaneTab::getConstrainVert() for ABSOLUTE_VERT +// 1999/11/28 Fixed itemFixed() +// 1999/11/28 Changed DWORD modeResize Arguments to layModeResize for +// better type safety. Added typesafe operator| +// 1999/12/04 Don't reposition window in UpdateLayout if it's a child +// (as a child Dialog or PropertyPage) +// 1999/12/04 Erase Backgroung with GCL_HBRBACKGROUND (if available) +// 1999/12/04 itemSpaceXXX() adds a NORESIZE item instead of ABSOLUTE_XXX +// this will fix unwanted growing in secondary direction +// +// Version: 1.0 [1999/12/04] Initial Article on CodeProject +// +// 1999/12/10 Erase Backgroung within TabCtrl was 'fixed' badly. Reverted to +// old working code +// 2000/02/02 When the Dialog is child of a View the class works correctly +// now [Didier BULTIAUW] +// 2000/02/15 Combo-Boxes were not working correctly (in all modes!) +// 2000/02/17 aligned SpinButton Controls (with buddy) now handled +// automatically +// !! do not add such a control to the layout !! it is always +// reattached to its buddy. +// 2000/02/17 changed some cotrol class names to the defined constants +// +// Version: 1.1 [2000/02/17] +// +// 2000/02/25 Fixed auto alignment of SpinButton Controls to only affect +// visible ones +// 2000/02/27 Put all the classes into the namespace 'ETSLayout' +// 2000/03/07 Fixed growing Dialog after minimizing and restoring +// 2000/05/22 Whole Statusbar (Gripper) is not excluded anymore in EraseBkgnd() +// instead only the triangular Gripper is excluded +// 2000/05/31 Fix for PropertySheets with PSH_WIZARDHASFINISH [Thömmi] +// 2000/05/31 Fix for UpDown-Controls with EditCtrl Buddy in PropertyPages. +// These were not repositioned every time the page is being show +// until the first resize +// 2000/07/28 Problems with resizing ActiveX Controls fixed [Micheal Chapman] +// 2000/07/28 Some strings were not properly wrapped with _T() +// 2000/08/03 Check for BS_GROUPBOX was not correct as BS_GROUPBOX is more than one Bit +// 2000/08/03 New override AddMainArea added to ETSLayoutPropertySheet in order to +// have a hook for additional controls in a PropertySheet (besides the Tab) +// 2000/08/03 New override AddButtons added to ETSLayoutPropertySheet in order to +// have a hook for additional controls in the bottem pane of a PropertySheet +// 2000/08/03 Removed the need for DECLARE_LAYOUT +// +// Version: 1.2 [2000/08/05] + +#define OEMRESOURCE +#include + +#include "stdafx.h" +#include "ETSLayout.h" + +using namespace ETSLayout; +#pragma warning(disable: 4097 4610 4510 4100) + + +#ifndef OBM_SIZE +#define OBM_SIZE 32766 +// taken from WinresRc.h +// if not used for any reason +#endif + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +static UINT auIDStatusBar[] = +{ + ID_SEPARATOR +}; + +const int ERASE_GROUP_BORDER = 10; +const int FIXUP_CUTOFF = 5; +const int TAB_SPACE = 5; + +// the _NULL-Pane +CWnd* ETSLayoutMgr::paneNull = 0; + +void ETSLayoutMgr::Layout(CRect& rcClient) +{ + if(rcClient.Height() && rcClient.Width() && m_RootPane.IsValid()) \ + m_RootPane->resizeTo(rcClient); \ +} + + +ETSLayoutMgr::CPane ETSLayoutMgr::pane( layOrientation orientation, ETSLayoutMgr::layResizeMode modeResize /*=GREEDY*/, + int sizeBorder /*=nDefaultBorder*/, int sizeExtraBorder /*=0*/, + int sizeSecondary /*=0*/) +{ + Pane* pPane = new Pane ( this, orientation, sizeBorder, sizeExtraBorder ); + pPane->m_sizeSecondary = sizeSecondary; + pPane->m_modeResize = modeResize; + + return CPane(pPane); +} + +ETSLayoutMgr::CPane ETSLayoutMgr::paneTab( CTabCtrl* pTab, layOrientation orientation, + ETSLayoutMgr::layResizeMode modeResize /*=GREEDY*/, int sizeBorder /*=nDefaultBorder*/, + int sizeExtraBorder /*=0*/, int sizeSecondary /*=0*/) +{ + Pane* pPane = new PaneTab ( pTab, this, orientation, sizeBorder, sizeExtraBorder ); + pPane->m_sizeSecondary = sizeSecondary; + pPane->m_modeResize = modeResize; + + return CPane(pPane); +} + + +ETSLayoutMgr::CPane ETSLayoutMgr::paneCtrl( CWnd* pCtrl, layOrientation orientation, + ETSLayoutMgr::layResizeMode modeResize /*=GREEDY*/, int sizeBorder /*=nDefaultBorder*/, + int sizeExtraBorder /*=0*/, int sizeTopExtra /*=0*/, + int sizeSecondary /*=0*/) +{ + Pane* pPane = new PaneCtrl ( pCtrl, this, orientation, sizeBorder, sizeExtraBorder, sizeTopExtra ); + pPane->m_sizeSecondary = sizeSecondary; + pPane->m_modeResize = modeResize; + + return CPane(pPane); +} + +ETSLayoutMgr::CPane ETSLayoutMgr::paneCtrl( UINT nID, layOrientation orientation, ETSLayoutMgr::layResizeMode modeResize /*=GREEDY*/, + int sizeBorder /*=nDefaultBorder*/, int sizeExtraBorder /*=0*/, + int sizeTopExtra /*=0*/, int sizeSecondary /*=0*/) +{ + Pane* pPane = new PaneCtrl ( nID, this, orientation, sizeBorder, sizeExtraBorder, sizeTopExtra ); + pPane->m_sizeSecondary = sizeSecondary; + pPane->m_modeResize = modeResize; + + return CPane(pPane); +} + + +ETSLayoutMgr::CPaneBase ETSLayoutMgr::item(UINT nID, ETSLayoutMgr::layResizeMode modeResize /*=GREEDY*/, int sizeX /*=0*/, int sizeY /*=0*/, + int sizeXMin /*=-1*/, int sizeYMin /*=-1*/) +{ + return new PaneItem( nID, this, modeResize, sizeX, sizeY, sizeXMin, sizeYMin); +} + +ETSLayoutMgr::CPaneBase ETSLayoutMgr::item(CWnd* pWnd, ETSLayoutMgr::layResizeMode modeResize /*=GREEDY*/, + int sizeX /*=0*/, int sizeY /*=0*/, int sizeXMin /*=-1*/, + int sizeYMin /*=-1*/) +{ + return new PaneItem( pWnd, this, modeResize, sizeX, sizeY, sizeXMin, sizeYMin); +} + +ETSLayoutMgr::CPaneBase ETSLayoutMgr::itemFixed(layOrientation orientation, int sizePrimary) +{ + CPaneBase p = new PaneItem(paneNull, this, NORESIZE, (orientation==HORIZONTAL)?sizePrimary:0, (orientation==VERTICAL)?sizePrimary:0); + return p; +} + +ETSLayoutMgr::CPaneBase ETSLayoutMgr::itemGrowing(layOrientation orientation) +{ + return new PaneItem(paneNull, this, (orientation==HORIZONTAL)?ABSOLUTE_VERT:ABSOLUTE_HORZ, 0, 0, -nDefaultBorder, -nDefaultBorder); +} + +ETSLayoutMgr::CPaneBase ETSLayoutMgr::itemSpaceBetween( layOrientation orientation, CWnd* pWndFirst, CWnd* pWndSecond ) +{ + if( orientation == HORIZONTAL ) { + // I'm interested in horizontal spacing + + CRect rLeft, rRight; + pWndFirst->GetWindowRect(&rLeft); + pWndSecond->GetWindowRect(&rRight); + + int sizeX = rRight.left - rLeft.right; + + if( sizeX < 0 ) { + // compare top to top + sizeX = rRight.left - rLeft.left; + } + else { + sizeX -= 2*nDefaultBorder; + } + + return new PaneItem(paneNull, this, NORESIZE, sizeX, 0); + } + else { + // I'm interested in vertical spacing + CRect rTop, rBot; + pWndFirst->GetWindowRect(&rTop); + pWndSecond->GetWindowRect(&rBot); + + int sizeY = rBot.top - rTop.bottom; + + if( sizeY < 0 ) { + // compare top to top + sizeY = sizeY = rBot.top - rTop.top; + } + else { + sizeY -= 2*nDefaultBorder; + } + + return new PaneItem(paneNull, this, NORESIZE, 0, sizeY); + } +} + + +ETSLayoutMgr::CPaneBase ETSLayoutMgr::itemSpaceBetween( layOrientation orientation, UINT nIDFirst, UINT nIDSecond ) +{ + CWnd *pFirst = GetWnd()->GetDlgItem(nIDFirst); + CWnd *pSecond = GetWnd()->GetDlgItem(nIDSecond); + + ASSERT( pFirst && pSecond ); + + return itemSpaceBetween( orientation, pFirst, pSecond ); +} + + +ETSLayoutMgr::CPaneBase ETSLayoutMgr::itemSpaceLike( layOrientation orientation, CWnd* pWnd ) +{ + CRect rRect; + pWnd->GetWindowRect(&rRect); + + if( orientation == HORIZONTAL ) { + // I'm interested in horizontal spacing + return new PaneItem(paneNull, this, NORESIZE, rRect.Width(), 0); + } + else { + // I'm interested in vertical spacing + return new PaneItem(paneNull, this, NORESIZE, 0, rRect.Height() ); + } + +} + + +ETSLayoutMgr::CPaneBase ETSLayoutMgr::itemSpaceLike( layOrientation orientation, UINT nID ) +{ + CWnd *pWnd = GetWnd()->GetDlgItem(nID); + ASSERT( pWnd ); + + return itemSpaceLike( orientation, pWnd ); +} + + + +ETSLayoutMgr::~ETSLayoutMgr() +{ +} + +void ETSLayoutMgr::UpdateLayout() +{ + if(!m_RootPane) + return; + + // Check constraints + CRect rcClient = GetRect(); + + if( m_pWnd->IsKindOf( RUNTIME_CLASS( CDialog ) ) && !(m_pWnd->GetStyle()&WS_CHILD) ) { + CRect rcWindow; + m_pWnd->GetWindowRect(rcWindow); + + // Added by Didier BULTIAUW + CWnd* parentWnd = m_pWnd->GetParent(); + if( (parentWnd != 0) && parentWnd->IsKindOf(RUNTIME_CLASS(CView)) ) + { + CRect rcParent; + parentWnd->GetWindowRect(rcParent); + rcWindow.OffsetRect(-rcParent.left,-rcParent.top); + } + // end add + + CRect rcBorder = rcWindow; + rcBorder -= rcClient; + + // Min and Max info + int minWidth = m_RootPane->getMinConstrainHorz() + rcBorder.Width() + 2*m_sizeRootBorders.cx; + int minHeight = m_RootPane->getMinConstrainVert() + rcBorder.Height() + 2*m_sizeRootBorders.cy; + int maxWidth = m_RootPane->getMaxConstrainHorz(); + if(maxWidth != -1) { + maxWidth += rcBorder.Width() + 2*m_sizeRootBorders.cx; + maxWidth = max(maxWidth, minWidth); + } + int maxHeight = m_RootPane->getMaxConstrainVert(); + if(maxHeight != -1) { + maxHeight += rcBorder.Height() + 2*m_sizeRootBorders.cy; + maxHeight = max(maxHeight, minHeight); + } + + if(rcWindow.Width() < minWidth) + rcWindow.right = rcWindow.left + minWidth; + if(rcWindow.Height() < minHeight) + rcWindow.bottom = rcWindow.top + minHeight; + + if(maxWidth != -1 && rcWindow.Width() > maxWidth) + rcWindow.right = rcWindow.left + maxWidth; + if(maxHeight != -1 && rcWindow.Height() > maxHeight) + rcWindow.bottom = rcWindow.top + maxHeight; + + m_pWnd->MoveWindow(rcWindow); + } + // Do the Layout + rcClient = GetRect(); + + // Add a Border around the rootPane + rcClient.top += m_sizeRootBorders.cy; + rcClient.bottom -= m_sizeRootBorders.cy; + rcClient.left += m_sizeRootBorders.cx; + rcClient.right -= m_sizeRootBorders.cx; + + if(GetWnd()->IsWindowVisible()) { + // Avoid ugly artifacts + //GetWnd()->SetRedraw(FALSE); + Layout(rcClient); + //GetWnd()->SetRedraw(TRUE); + } + else + Layout(rcClient); + + // Take special care of SpinButtons (Up-Down Controls) with Buddy set, enumerate + // all childs: + CWnd* pWndChild = GetWnd()->GetWindow(GW_CHILD); + TCHAR szClassName[ MAX_PATH ]; + while(pWndChild) + { + ::GetClassName( pWndChild->GetSafeHwnd(), szClassName, MAX_PATH ); + DWORD dwStyle = pWndChild->GetStyle(); + + // is it a SpinButton? + if( _tcscmp(szClassName, UPDOWN_CLASS)==0 && ::IsWindowVisible(pWndChild->GetSafeHwnd()) ) { + HWND hwndBuddy = (HWND)::SendMessage( pWndChild->GetSafeHwnd(), UDM_GETBUDDY, 0, 0); + if( hwndBuddy != 0 && (dwStyle&(UDS_ALIGNRIGHT|UDS_ALIGNLEFT)) != 0 ) + { + // reset Buddy + ::SendMessage( pWndChild->GetSafeHwnd(), UDM_SETBUDDY, (WPARAM)hwndBuddy, 0); + } + } + + + pWndChild = pWndChild->GetWindow(GW_HWNDNEXT); + } + + + GetWnd()->Invalidate(); +} + + +bool ETSLayoutMgr::Save(LPCTSTR lpstrRegKey) +{ + CRect rcWnd; + + if(IsWindow(GetWnd()->m_hWnd)) + { + WINDOWPLACEMENT wp; + if(GetWnd()->GetWindowPlacement(&wp)) + { + // Make sure we don't pop up + // minimized the next time + if(wp.showCmd != SW_SHOWMAXIMIZED) + wp.showCmd = SW_SHOWNORMAL; + + AfxGetApp()->WriteProfileBinary(lpstrRegKey, + _T("WindowPlacement"), + reinterpret_cast(&wp), sizeof(wp)); + } + } + return true; +} + +bool ETSLayoutMgr::Load(LPCTSTR lpstrRegKey) +{ + LPBYTE pbtData = 0; + UINT nSize = 0; + if(AfxGetApp()->GetProfileBinary(lpstrRegKey, + _T("WindowPlacement"), &pbtData, &nSize)) + { + WINDOWPLACEMENT* pwp = + reinterpret_cast(pbtData); + + ASSERT(nSize == sizeof(WINDOWPLACEMENT)); + if(nSize == sizeof(WINDOWPLACEMENT)) + GetWnd()->SetWindowPlacement(reinterpret_cast(pbtData)); + + delete [] pbtData; + } + return true; +} + + +void ETSLayoutMgr::EraseBkgnd(CDC* pDC) +{ + CRect rcClient; + GetWnd()->GetClientRect( rcClient ); + + CRgn rgn; + rgn.CreateRectRgnIndirect(rcClient); + TRACE("CreateRgn (%d,%d,%d,%d)\n", rcClient.left, rcClient.top, rcClient.right, rcClient.bottom ); + + CRgn rgnRect; + rgnRect.CreateRectRgn(0,0,0,0); + + CRect rcChild; + CWnd* pWndChild = GetWnd()->GetWindow( GW_CHILD ); + + TCHAR szClassName[ MAX_PATH ]; + + pDC->SelectClipRgn(NULL); + + while( pWndChild ) { + + pWndChild->GetWindowRect(rcChild); + GetWnd()->ScreenToClient( rcChild ); + + rgnRect.SetRectRgn( rcChild ); + + ::GetClassName( pWndChild->GetSafeHwnd(), szClassName, MAX_PATH ); + DWORD dwStyle = pWndChild->GetStyle(); + + // doesn't make sense for hidden children + if( dwStyle & WS_VISIBLE ) { + + // Fix: BS_GROUPBOX is more than one Bit, extend check to (dwStyle & BS_GROUPBOX)==BS_GROUPBOX [ET] + if( _tcscmp(szClassName,_T("Button"))==0 && (dwStyle & BS_GROUPBOX)==BS_GROUPBOX ) { + // it is a group-box, ignore completely + } + else if( _tcscmp(szClassName,WC_TABCONTROL )==0 ) { + // ignore Tab-Control's inside rect + static_cast(pWndChild)->AdjustRect(FALSE,rcChild); + + CRgn rgnContent; + rgnContent.CreateRectRgnIndirect(rcChild); + + rgnRect.CombineRgn( &rgnRect, &rgnContent, RGN_DIFF ); + rgn.CombineRgn( &rgn, &rgnRect, RGN_DIFF ); + } + else if( _tcscmp(szClassName,STATUSCLASSNAME)==0 ) { + + CPoint ptTriangleGrip[3]; + ptTriangleGrip[0] = CPoint(rcChild.right,rcChild.top); + ptTriangleGrip[1] = CPoint(rcChild.right,rcChild.bottom); + ptTriangleGrip[2] = CPoint(rcChild.right-rcChild.Height(),rcChild.bottom); + + CRgn rgnGripper; + rgnGripper.CreatePolygonRgn(ptTriangleGrip,3, WINDING); + + rgn.CombineRgn( &rgn, &rgnGripper, RGN_DIFF ); + + } + else { + rgn.CombineRgn( &rgn, &rgnRect, RGN_DIFF ); + } + } + + pWndChild = pWndChild->GetNextWindow(); + } + + + HBRUSH hBrBack = (HBRUSH) ::GetClassLong(GetWnd()->GetSafeHwnd(), GCL_HBRBACKGROUND) ; + if( hBrBack == 0 ) + hBrBack = ::GetSysColorBrush(COLOR_BTNFACE); + + pDC->FillRgn( &rgn, + CBrush::FromHandle( hBrBack ) + ); + +} + +///////////////////////////////////////////////////////////////////////////// +// ETSLayoutMgr::PaneItem implementation + + +ETSLayoutMgr::PaneItem::PaneItem(CWnd* pWnd, ETSLayoutMgr* pMgr, ETSLayoutMgr::layResizeMode modeResize/*=GREEDY*/ + , int sizeX/*=0*/, int sizeY/*=0*/ + , int sizeXMin/*=-1*/, int sizeYMin/*=-1*/ ) : PaneBase( pMgr ) +{ + m_modeResize = modeResize; + m_hwndCtrl = pWnd->GetSafeHwnd(); + + m_sizeX = 0; + m_sizeY = 0; + + m_bComboSpecial = false; + + m_sizeXMin = sizeXMin; + m_sizeYMin = sizeYMin; + + if(!m_hwndCtrl) { // only Dummy! + m_sizeX = sizeX; + m_sizeY = sizeY; + } + else { + CRect rcControl; + ::GetWindowRect(m_hwndCtrl, &rcControl); + + if(sizeX == 0) { + m_sizeX = rcControl.Width(); + } + else { + m_sizeX = sizeX; + } + if( m_sizeXMin == -1 ) { + // do not make smaller than current size + m_sizeXMin = rcControl.Width(); + } + + if(sizeY == 0) { + m_sizeY = rcControl.Height(); + } + else { + m_sizeY = sizeY; + } + if( m_sizeYMin == -1 ) { + // do not make smaller than current size + m_sizeYMin = rcControl.Height(); + } + + TCHAR szClassName[ MAX_PATH ]; + ::GetClassName( m_hwndCtrl, szClassName, MAX_PATH ); + + // special treatment for combo-boxes + if( _tcscmp(szClassName,_T("ComboBox"))==0 || _tcscmp(szClassName,WC_COMBOBOXEX)==0) { + m_bComboSpecial = true; + } + } +} + +ETSLayoutMgr::PaneItem::PaneItem( UINT nID, ETSLayoutMgr* pMgr, ETSLayoutMgr::layResizeMode modeResize/*=GREEDY*/ + , int sizeX/*=0*/, int sizeY/*=0*/ + , int sizeXMin/*=-1*/, int sizeYMin/*=-1*/ ) : PaneBase( pMgr ) +{ + CWnd* pWnd = pMgr->GetWnd()->GetDlgItem(nID); + m_hwndCtrl = pWnd->GetSafeHwnd(); + + m_sizeX = 0; + m_sizeY = 0; + + m_bComboSpecial = false; + + m_modeResize = modeResize; + + m_sizeXMin = sizeXMin; + m_sizeYMin = sizeYMin; + + if(!m_hwndCtrl) { // only Dummy! + m_sizeX = sizeX; + m_sizeY = sizeY; + } + else { + CRect rcControl; + ::GetWindowRect(m_hwndCtrl, &rcControl); + + if(sizeX == 0) { + m_sizeX = rcControl.Width(); + } + else { + m_sizeX = sizeX; + } + if( m_sizeXMin == -1 ) { + // do not make smaller than current size + m_sizeXMin = rcControl.Width(); + } + + if(sizeY == 0) { + m_sizeY = rcControl.Height(); + } + else { + m_sizeY = sizeY; + } + if( m_sizeYMin == -1 ) { + // do not make smaller than current size + m_sizeYMin = rcControl.Height(); + } + + TCHAR szClassName[ MAX_PATH ]; + ::GetClassName( m_hwndCtrl, szClassName, MAX_PATH ); + + // special treatment for combo-boxes + if( _tcscmp(szClassName,_T("ComboBox"))==0 || _tcscmp(szClassName,WC_COMBOBOXEX)==0) { + m_bComboSpecial = true; + } + } +} + +int ETSLayoutMgr::PaneItem::getConstrainHorz(int sizeParent) +{ + if( m_modeResize & ABSOLUTE_HORZ) { + return m_sizeX; + } + if(m_modeResize & RELATIVE_HORZ) { + return (sizeParent * m_sizeX) / 100; + } + return -1; +} + +int ETSLayoutMgr::PaneItem::getConstrainVert(int sizeParent) +{ + if(m_modeResize & ABSOLUTE_VERT) { + return m_sizeY; + } + if(m_modeResize & RELATIVE_VERT) { + return (sizeParent * m_sizeY) / 100; + } + return -1; +} + +int ETSLayoutMgr::PaneItem::getMinConstrainHorz() +{ + if(m_modeResize & ABSOLUTE_HORZ) { + return m_sizeX; + } + return max(nMinConstrain,m_sizeXMin); +} + +int ETSLayoutMgr::PaneItem::getMinConstrainVert() +{ + if(m_modeResize & ABSOLUTE_VERT) { + return m_sizeY; + } + return max(nMinConstrain,m_sizeYMin); +} + +int ETSLayoutMgr::PaneItem::getMaxConstrainHorz() +{ + if(m_modeResize & ABSOLUTE_HORZ) { + return m_sizeX; + } + return -1; +} + +int ETSLayoutMgr::PaneItem::getMaxConstrainVert() +{ + if(m_modeResize & ABSOLUTE_VERT) { + return m_sizeY; + } + return -1; +} + +bool ETSLayoutMgr::PaneItem::resizeTo(CRect& rcNewArea) +{ + if(m_hwndCtrl) { + + CRect rcWnd; + ::GetWindowRect( m_hwndCtrl, rcWnd ); + + if( !(m_modeResize & ALIGN_FILL_HORZ) && m_modeResize & ABSOLUTE_HORZ ) { + + + if( (m_modeResize & ALIGN_HCENTER) == ALIGN_HCENTER ) { + rcNewArea.OffsetRect( (rcNewArea.Width() - rcWnd.Width())/2, 0 ); + } + else if( m_modeResize & ALIGN_RIGHT ) { + rcNewArea.OffsetRect( rcNewArea.Width() - rcWnd.Width(), 0 ); + } + + rcNewArea.right = rcNewArea.left + rcWnd.Width(); + } + if( !(m_modeResize & ALIGN_FILL_VERT) && m_modeResize & ABSOLUTE_VERT ) { + + + if( (m_modeResize & ALIGN_VCENTER) == ALIGN_VCENTER ) { + rcNewArea.OffsetRect( 0, (rcNewArea.Height()-rcWnd.Height())/2 ); + } + else if( m_modeResize & ALIGN_BOTTOM ) { + rcNewArea.OffsetRect( 0, rcNewArea.Height() - rcWnd.Height()); + } + + rcNewArea.bottom = rcNewArea.top + rcWnd.Height(); + + } + + DWORD dwStyle = ::GetWindowLong( m_hwndCtrl, GWL_STYLE ); + + // special treatment for combo-boxes + if( m_bComboSpecial && (dwStyle & CBS_DROPDOWN) ) { + // keep height (though only fully visible when dropped down) + rcNewArea.bottom = rcNewArea.top + rcWnd.Height(); + } + + // FIX: ::MoveWindow would case problems with some ActiveX Controls [Micheal Chapman] + CWnd* pTempWnd = CWnd::FromHandle( m_hwndCtrl ); + pTempWnd->MoveWindow( rcNewArea.left, rcNewArea.top, rcNewArea.Width(), rcNewArea.Height() ); + + if( m_bComboSpecial && !(dwStyle & CBS_DROPDOWN) && !(dwStyle & CBS_NOINTEGRALHEIGHT) ) { + + // Keep CB Size = Edit + LB ( if not CBS_NOINTEGRALHEIGHT) + + ::GetWindowRect( m_hwndCtrl, rcWnd ); + + CRect rcListBox; + HWND hwndListBox = ::GetDlgItem(m_hwndCtrl, 1000); // ListBox of CB + if( hwndListBox != 0 ) + { + ::GetWindowRect( hwndListBox, rcListBox ); + rcWnd.bottom = rcListBox.bottom; + + rcNewArea.bottom = rcNewArea.top + rcWnd.Height(); + + // FIX: ::MoveWindow would case problems with some ActiveX Controls [Micheal Chapman] + CWnd* pTempWnd = CWnd::FromHandle( m_hwndCtrl ); + pTempWnd->MoveWindow( rcNewArea.left, rcNewArea.top, rcNewArea.Width(), rcNewArea.Height(), true ); + } + } + + ::RedrawWindow(m_hwndCtrl,0,0, RDW_INVALIDATE | RDW_UPDATENOW ); + + } + return true; +} + + +///////////////////////////////////////////////////////////////////////////// +// ETSLayoutMgr::PaneTab implementation + + +ETSLayoutMgr::PaneTab::PaneTab( CTabCtrl* pTab, ETSLayoutMgr* pMgr, layOrientation orientation, int sizeBorder /*= nDefaultBorder*/, int sizeExtraBorder /*= 0*/ ) +: ETSLayoutMgr::Pane(pMgr, orientation, sizeBorder, sizeExtraBorder) +{ + ASSERT(pTab); + m_pTab = pTab; +} + +int ETSLayoutMgr::PaneTab::getConstrainHorz(int sizeParent) +{ + CRect rcTab; + m_pTab->AdjustRect(TRUE, &rcTab); + + if(rcTab.Width() > sizeParent) + return rcTab.Width(); + + return Pane::getConstrainHorz(sizeParent /*- rcTab.Width()*/); +} + +int ETSLayoutMgr::PaneTab::getConstrainVert(int sizeParent) +{ + CRect rcTab; + m_pTab->AdjustRect(TRUE, &rcTab); + + if( m_modeResize & ABSOLUTE_VERT ) { + return m_sizeSecondary + rcTab.Height(); + } + + if(rcTab.Height() > sizeParent) + return rcTab.Height(); + + return Pane::getConstrainVert(sizeParent /*- rcTab.Height()*/); +} + +int ETSLayoutMgr::PaneTab::getMinConstrainHorz() +{ + CRect rcTab(0,0,0,0); + m_pTab->AdjustRect(TRUE, &rcTab); + + return Pane::getMinConstrainHorz() + rcTab.Width() ; +} + +int ETSLayoutMgr::PaneTab::getMinConstrainVert() +{ + CRect rcTab(0,0,0,0); + m_pTab->AdjustRect(TRUE, &rcTab); + + return Pane::getMinConstrainVert() + rcTab.Height(); +} + +int ETSLayoutMgr::PaneTab::getMaxConstrainHorz() +{ + CRect rcTab(0,0,0,0); + m_pTab->AdjustRect(TRUE, &rcTab); + + int paneMax = Pane::getMaxConstrainHorz(); + return (paneMax != -1) ? paneMax + rcTab.Width() : -1; +} + +int ETSLayoutMgr::PaneTab::getMaxConstrainVert() +{ + CRect rcTab(0,0,0,0); + m_pTab->AdjustRect(TRUE, &rcTab); + + int paneMax = Pane::getMaxConstrainVert(); + return (paneMax != -1) ? paneMax + rcTab.Height() : -1; +} + +bool ETSLayoutMgr::PaneTab::resizeTo(CRect& rcNewArea) +{ + m_pTab->MoveWindow(rcNewArea); + m_pTab->AdjustRect(FALSE,rcNewArea); + + return Pane::resizeTo(rcNewArea); +} + +///////////////////////////////////////////////////////////////////////////// +// ETSLayoutMgr::PaneCtrl implementation + + +ETSLayoutMgr::PaneCtrl::PaneCtrl( CWnd* pCtrl, ETSLayoutMgr* pMgr, layOrientation orientation, int sizeBorder /*= nDefaultBorder*/, int sizeExtraBorder /*= 0*/, int sizeTopExtra /*= 0*/ ) +: ETSLayoutMgr::Pane(pMgr, orientation, sizeBorder, sizeExtraBorder) +{ + m_sizeTopExtra = sizeTopExtra; + + ASSERT(pCtrl); + m_hwndCtrl = pCtrl->GetSafeHwnd(); +} + +ETSLayoutMgr::PaneCtrl::PaneCtrl( UINT nID, ETSLayoutMgr* pMgr, layOrientation orientation, int sizeBorder /*= nDefaultBorder*/, int sizeExtraBorder /*= 0*/, int sizeTopExtra /*= 0*/ ) +: ETSLayoutMgr::Pane(pMgr, orientation, sizeBorder, sizeExtraBorder) +{ + m_sizeTopExtra = sizeTopExtra; + + m_hwndCtrl = ::GetDlgItem(pMgr->GetWnd()->GetSafeHwnd(), nID); + ASSERT(m_hwndCtrl); +} + +int ETSLayoutMgr::PaneCtrl::getConstrainHorz(int sizeParent) +{ + return Pane::getConstrainHorz(sizeParent) ; +} + +int ETSLayoutMgr::PaneCtrl::getConstrainVert(int sizeParent) +{ + return Pane::getConstrainVert(sizeParent); +} + +int ETSLayoutMgr::PaneCtrl::getMinConstrainHorz() +{ + return Pane::getMinConstrainHorz(); +} + +int ETSLayoutMgr::PaneCtrl::getMinConstrainVert() +{ + return Pane::getMinConstrainVert() + m_sizeTopExtra; +} + +int ETSLayoutMgr::PaneCtrl::getMaxConstrainHorz() +{ + int paneMax = Pane::getMaxConstrainHorz(); + return ( paneMax == -1) ? -1 : paneMax ; +} + +int ETSLayoutMgr::PaneCtrl::getMaxConstrainVert() +{ + int paneMax = Pane::getMaxConstrainVert(); + return ( paneMax == -1) ? -1 : paneMax + m_sizeTopExtra; +} + +bool ETSLayoutMgr::PaneCtrl::resizeTo(CRect& rcNewArea) +{ + // FIX: ::MoveWindow would case problems with some ActiveX Controls [Micheal Chapman] + CWnd* pTempWnd = CWnd::FromHandle( m_hwndCtrl ); + pTempWnd->MoveWindow( rcNewArea.left, rcNewArea.top, rcNewArea.Width(), rcNewArea.Height(), true ); + + ::RedrawWindow(m_hwndCtrl,0,0, RDW_INVALIDATE | RDW_UPDATENOW |RDW_ERASE); + rcNewArea.top += m_sizeTopExtra; + return Pane::resizeTo(rcNewArea); +} + +///////////////////////////////////////////////////////////////////////////// +// ETSLayoutMgr::Pane implementation + +ETSLayoutMgr::Pane::Pane( ETSLayoutMgr* pMgr, layOrientation orientation, int sizeBorder /* = nDefaultBorder */, int sizeExtraBorder /*= 0*/) +: PaneBase(pMgr) +{ + m_Orientation = orientation; + m_sizeBorder = sizeBorder; + m_sizeSecondary = 0; + m_modeResize = 0; + m_sizeExtraBorder= sizeExtraBorder; +} + + +ETSLayoutMgr::Pane::~Pane() +{ +} + + +bool ETSLayoutMgr::Pane::addItem( CWnd* pWnd, ETSLayoutMgr::layResizeMode modeResize /*=GREEDY*/, int sizeX /*=0*/, int sizeY /*=0*/, int sizeXMin /*=0*/, int sizeYMin /*=0*/) +{ + CPaneBase pItem = new PaneItem( pWnd, m_pMgr, modeResize, sizeX, sizeY, sizeXMin, sizeYMin); + return addPane( pItem ); +} + +bool ETSLayoutMgr::Pane::addItem( UINT nID, ETSLayoutMgr::layResizeMode modeResize /*=GREEDY*/, int sizeX /*=0*/, int sizeY /*=0*/, int sizeXMin /*=0*/, int sizeYMin /*=0*/) +{ + CPaneBase pItem = new PaneItem( nID, m_pMgr, modeResize, sizeX, sizeY, sizeXMin, sizeYMin); + return addPane( pItem ); +} + +bool ETSLayoutMgr::Pane::addItemFixed(int size) +{ + CPaneBase pNewItem = m_pMgr->itemFixed(m_Orientation, size); + return addPane( pNewItem ); +} + +bool ETSLayoutMgr::Pane::addItemGrowing() +{ + CPaneBase pNewItem = m_pMgr->itemGrowing(m_Orientation); + return addPane( pNewItem ); +} + +bool ETSLayoutMgr::Pane::addItemSpaceBetween( CWnd* pWndFirst, CWnd* pWndSecond ) +{ + CPaneBase pNewItem = m_pMgr->itemSpaceBetween(m_Orientation, pWndFirst, pWndSecond); + return addPane( pNewItem ); +} + +bool ETSLayoutMgr::Pane::addItemSpaceBetween( UINT nIDFirst, UINT nIDSecond ) +{ + CPaneBase pNewItem = m_pMgr->itemSpaceBetween(m_Orientation, nIDFirst, nIDSecond); + return addPane( pNewItem ); +} + +bool ETSLayoutMgr::Pane::addItemSpaceLike( CWnd* pWnd ) +{ + CPaneBase pNewItem = m_pMgr->itemSpaceLike(m_Orientation, pWnd); + return addPane( pNewItem ); +} + +bool ETSLayoutMgr::Pane::addItemSpaceLike( UINT nID ) +{ + CPaneBase pNewItem = m_pMgr->itemSpaceLike(m_Orientation, nID); + return addPane( pNewItem ); +} + +bool ETSLayoutMgr::Pane::addPane( CPane pSubpane, ETSLayoutMgr::layResizeMode modeResize, int sizeSecondary /* = 0 */) +{ + if( pSubpane->getOrientation() == m_Orientation) + { + // wrap in subpane of opposite orientation + CPane pPaneWrap = new Pane(m_pMgr, m_Orientation==HORIZONTAL?VERTICAL:HORIZONTAL,0,0); + pPaneWrap->addPane( pSubpane ); + + addPane( pPaneWrap, modeResize, sizeSecondary ); + } + else + { + pSubpane->m_modeResize = modeResize; + + if(m_Orientation==HORIZONTAL && (modeResize & ABSOLUTE_HORZ) ) { + if(sizeSecondary == 0) { + pSubpane->m_sizeSecondary = pSubpane->getMinConstrainHorz(); + } + } + else if(m_Orientation==HORIZONTAL && (modeResize & RELATIVE_HORZ) ) { + pSubpane->m_sizeSecondary = sizeSecondary; + } + else if(m_Orientation==VERTICAL && (modeResize & ABSOLUTE_VERT) ) { + if(sizeSecondary == 0) { + pSubpane->m_sizeSecondary = pSubpane->getMinConstrainVert(); + } + } + else if(m_Orientation==VERTICAL && (modeResize & RELATIVE_VERT) ) { + pSubpane->m_sizeSecondary = sizeSecondary; + } + + m_paneItems.Add(pSubpane); + } + + return true; +} + +bool ETSLayoutMgr::Pane::addPane( CPaneBase pItem ) +{ + m_paneItems.Add(pItem); + return true; +} + +int ETSLayoutMgr::Pane::getConstrainHorz(int sizeParent) +{ + ASSERT( m_Orientation == VERTICAL); + + if( m_modeResize & RELATIVE_HORZ ) { + return (sizeParent * m_sizeSecondary) / 100; + } + else if( m_modeResize & ABSOLUTE_HORZ ){ + return m_sizeSecondary; + } + else + return 0; +} + + +int ETSLayoutMgr::Pane::getConstrainVert(int sizeParent) +{ + ASSERT( m_Orientation == HORIZONTAL); + + if( m_modeResize & RELATIVE_VERT ) { + return (sizeParent * m_sizeSecondary) / 100; + } + else if( m_modeResize & ABSOLUTE_VERT ) { + return m_sizeSecondary; + } + else { + return 0; + } +} + +int ETSLayoutMgr::Pane::getMaxConstrainHorz() +{ + if(m_Orientation == HORIZONTAL) { + int nMaxConstr = -1; + for(int i=0; igetMaxConstrainHorz(); + if(nConstrain == -1) + return -1; + + nMaxConstr += nConstrain; + } + return (nMaxConstr == -1) ? -1 : nMaxConstr + (m_paneItems.GetUpperBound()*m_sizeBorder) + 2*m_sizeExtraBorder; + } + else if( m_modeResize & ABSOLUTE_HORZ && m_sizeSecondary!=0) { + return m_sizeSecondary; // + 2*m_sizeExtraBorder; + } + else { + int nMaxConstr = -1; + for(int i=0; igetMaxConstrainHorz(); + + if( nConstrain == -1) + return -1; + else + nMaxConstr = max(nMaxConstr, nConstrain); + + } + return (nMaxConstr == -1) ? -1 : nMaxConstr + 2*m_sizeExtraBorder; + } +} + +int ETSLayoutMgr::Pane::getMaxConstrainVert() +{ + if(m_Orientation == VERTICAL) { + int nMaxConstr = -1; + for(int i=0; igetMaxConstrainVert(); + if(nConstrain == -1) + return -1; + + nMaxConstr += nConstrain; + } + return (nMaxConstr == -1) ? -1 : nMaxConstr + (m_paneItems.GetUpperBound()*m_sizeBorder) + 2*m_sizeExtraBorder; + } + else if( m_modeResize & ABSOLUTE_VERT && m_sizeSecondary!=0) { + return m_sizeSecondary; // + 2*m_sizeExtraBorder; + } + else { + int nMaxConstr = -1; + for(int i=0; igetMaxConstrainVert(); + + if( nConstrain == -1) + return -1; + else + nMaxConstr = max(nMaxConstr, nConstrain); + + } + return (nMaxConstr == -1) ? -1 : nMaxConstr + 2*m_sizeExtraBorder; + } +} + +int ETSLayoutMgr::Pane::getMinConstrainHorz() +{ + if(m_Orientation == HORIZONTAL) { + int nMaxConstr = 0; + for(int i=0; igetMinConstrainHorz()); + } + return nMaxConstr + (m_paneItems.GetUpperBound()*m_sizeBorder) + 2*m_sizeExtraBorder; + } + else if( m_modeResize & ABSOLUTE_HORZ && m_sizeSecondary!=0) { + return m_sizeSecondary; // + 2*m_sizeExtraBorder; + } + else { + int nMaxConstr = 0; + for(int i=0; igetMinConstrainHorz(); + nMaxConstr = max(nMaxConstr, nConstrain); + } + return nMaxConstr + 2*m_sizeExtraBorder; + } +} + +int ETSLayoutMgr::Pane::getMinConstrainVert() +{ + if(m_Orientation == VERTICAL) { + int nMaxConstr = 0; + for(int i=0; igetMinConstrainVert()); + } + return nMaxConstr + (m_paneItems.GetUpperBound()*m_sizeBorder) + 2*m_sizeExtraBorder; + } + else if( m_modeResize & ABSOLUTE_VERT && m_sizeSecondary!=0) { + return m_sizeSecondary; // + 2*m_sizeExtraBorder; + } + else { + int nMaxConstr = 0; + for(int i=0; igetMinConstrainVert(); + nMaxConstr = max(nMaxConstr, nConstrain); + } + return nMaxConstr + 2*m_sizeExtraBorder; + } +} + + +int ETSLayoutMgr::Pane::resizeToAbsolute(int& availSpace, CArray& sizePrimary, + CArray& sizeMin, CArray& sizeMax) +{ + // count all greedy items as returnvalue + int nGreedy = 0; + + // first, subtract all absoulute items from available space + for(int i=0; imodeResize() & ABSOLUTE_HORZ) { + availSpace -= (sizePrimary[i] = pItem->getConstrainHorz(0)); + } + + // count Greedy items for later + if(!(pItem->modeResize() & ABSOLUTE_HORZ) && !(pItem->modeResize() & RELATIVE_HORZ)) { + nGreedy++; + } + + sizeMin[i] = pItem->getMinConstrainHorz(); + sizeMax[i] = pItem->getMaxConstrainHorz(); + } + else { + + // for absolute items subtract their size from available space + if(pItem->modeResize() & ABSOLUTE_VERT) { + availSpace -= (sizePrimary[i] = pItem->getConstrainVert(0)); + } + + // count Greedy items for later + if(!(pItem->modeResize() & ABSOLUTE_VERT) && !(pItem->modeResize() & RELATIVE_VERT)) { + nGreedy++; + } + + sizeMin[i] = pItem->getMinConstrainVert(); + sizeMax[i] = pItem->getMaxConstrainVert(); + } + + } + + // Must not be negative !! + availSpace = max(availSpace, 0); + + return nGreedy; +} + +bool ETSLayoutMgr::Pane::resizeToRelative(int& availSpace, CArray& sizePrimary, + CArray& sizeMin, CArray& sizeMax) +{ + // Then all relative items as percentage of left space (as of now after + // all absolute items are subtracted + + int availRel = availSpace; // At the beginning all of remaining space is available. We want all + // operation to be relative to the left space at this moment, so we + // save this amount here. Then we safly can lower availSpace + + int relDiff = 0; // The cumulated difference between first proposed size and + // eventual maximum/minimum size. This amount has to be + // saved in some other place (i.e. where relativ items/subpane + // are not limited by min/max + + int relLeft = 0; // The cumulated amout of space that can be saved by + // shrinking the items/panes up to the minimum + + int relCount = 0; // Actually allocated item/subpane's cumulated primary sizes + // of non-limited items/subpanes (these can be modified in fixup) + // needed for equally distribution of differences amoung non-limited + // relative items/subpanes + + for(int i=0; imodeResize() & RELATIVE_HORZ) + || + (m_Orientation==VERTICAL && pItem->modeResize() & RELATIVE_VERT) ) + { + // minimum item/subpane size in primary direction (pixels) + int nSizeRelMin = sizeMin[i]; + + // maximum item/subpane size in primary direction (pixels) + int nSizeRelMax = sizeMax[i]; + + // Relative size in primary direction (pixels) + int nSizeRel = (m_Orientation==HORIZONTAL) + ? + (pItem->getConstrainHorz(availRel)) + : + (pItem->getConstrainVert(availRel)); + + if( nSizeRel < nSizeRelMin) { + // The item/pane is shrinked too small! + // We will grow it to it's minimum-size. In order not to modify + // this item later when fixing up set the size to the negative + // minimum size + sizePrimary[i] = -nSizeRelMin; + + // As we grew one item/subpane we have to shrink another one. + // We keep count on how much space we needed to grow the item + // to it's minimum size + relDiff += ( nSizeRelMin - nSizeRel ); + } + else if( nSizeRelMax != -1 && nSizeRel > nSizeRelMax) { + // if there's a maximum size (nSizeRelMax != -1) and our item/subpane + // is to be resized over that amount correct it. In order not to modify + // this item later when fixing up set the size to the negative + // maximum size + sizePrimary[i] = -nSizeRelMax; + + // As we shrinked one item/subpane we have to grow another one. + // We keep count on how much space we needed to grow the item + // to it's maximum size. + relDiff += ( nSizeRelMax - nSizeRel ); + } + else { + // this is the normal case: neither are we minimum limited nor maximum + // limited + + // As this item/subpane is larger that it's minimum we could later (if + // necessary for fixup) shrink it for the difference amount of pixels + relLeft += ( nSizeRel - nSizeRelMin ); + + // Set the primary size of this item/pane. Can later be modified by fixup + sizePrimary[i] = nSizeRel; + + // Add this item/subpane's primary size to the count of already allocated + // cumulated size of non-limited items/subpanes (these can be modified in fixup) + relCount += nSizeRel; + } + + // decrease available space by used space in this step + availSpace -= nSizeRel; + } + } + + // We now have the situation that some items/subpanes had to be adjusted for cumulated + // relDiff pixels (positive value means more space taken than indicated by percentage of + // left space). On the other hand we have some items/subpanes which were not limited (in + // their current dimensions) but could be if necessary up to relLeft pixels. + if(relLeft < relDiff && availSpace >= (relDiff-relLeft) ){ + + // If it's not possible to shrink other (relative) panes in order to distribute the + // difference because the left for shrinking (relLeft) is too small we need to aquire + // more space from the globally left space (if available at all) + availSpace -= (relDiff-relLeft); + relDiff = relLeft; + } + + // At this point we should have some space left (at least not be negative with the leftover + // space) and on the other hand there's enough space for the limit-difference to be distributed +// ASSERT( availSpace >= 0 && relLeft >= relDiff); + + // Fixup Relative: + // Distribute (if anecessary) relDiff on other (not limited) relative items/subpanes + // (if available - if not later just grow the limited panes) + while( relDiff != 0 && relCount >= 0 ) { + + // in every iteration there must be some space distributed (of the difference) or it could + // come to endless looping. Save the amount of space actually distributed in this iteration + int relDist = 0; + + for(int i=0; imodeResize() & RELATIVE_HORZ) && sizePrimary[i] > 0) + || + (m_Orientation==VERTICAL && (pItem->modeResize() & RELATIVE_VERT) && sizePrimary[i] > 0) ) + { + // keep a flag for termination of this iteration + bool bLast = false; + + // the difference should be distributed amoung all non-limited items/subpanes equally. + // nDiff is the amount for the current item/subpane + int nDiff = (relDiff * sizePrimary[i]) / relCount; + + // if it's a too small value just add it to the current pane and break iteration + if( abs(relDiff) <= FIXUP_CUTOFF ) { + // take it all in this step + nDiff = relDiff; + + // set break flag + bLast = true; + } + + // calculate the new size for the current item/subpane + int nNewSize = sizePrimary[i] - nDiff; + + if( nNewSize < sizeMin[i] ) { + // oh, we are limited here. Revise our plan: + + // Not all of the space could be saved, add the actually possible space + // to the sum + relDist += ( sizePrimary[i] - sizeMin[i] ); + + // set it to the minimum possible size + sizePrimary[i] = -sizeMin[i]; + + // as this item/subpane is now limited it's occupied space doesn't count + // for relCount anymore + relCount-= ( sizePrimary[i] ); + } + else { + // account the difference of the sizes in relDist and set new size + relDist += ( sizePrimary[i] - nNewSize ); + sizePrimary[i] = nNewSize; + + // if it's the last one break now + if(bLast) + break; + } + } + } + // Distributed some relDiff-space in every iteration +// ASSERT(relDist != 0); + relDiff -= relDist; + + if( relDist == 0 ) + break; + } + + { + // Fixup Relative: invert all negative (limited) sized to correct value + for(int i=0; imodeResize() & RELATIVE_HORZ) && sizePrimary[i] < 0) + || + (m_Orientation==VERTICAL && (pItem->modeResize() & RELATIVE_VERT) && sizePrimary[i] < 0) ) + { + sizePrimary[i] *= -1; + } + } + } + + return true; +} + +bool ETSLayoutMgr::Pane::resizeToGreedy(int& availSpace, int nGreedy, CArray& sizePrimary, + CArray& sizeMin, CArray& sizeMax) +{ + // Now resize all Greedy items/subpanes equally among the remaining space + int greedyDiff = 0; // The cumulated difference between first proposed size and + // eventual maximum/minimum size. This amount has to be + // saved in some other place (i.e. where items/subpane + // are not limited by min/max + + int greedyLeft = 0; // The cumulated amount of space that can be saved by + // shrinking the items/panes up to the minimum + + int greedyCount = 0; // Actually allocated item/subpane's cumulated primary sizes + // of non-limited items/subpanes (these can be modified in fixup) + // needed for equally distribution of differences amoung non-limited + // items/subpanes + + for(int i=0; imodeResize()&ABSOLUTE_HORZ) + && !(pItem->modeResize()&RELATIVE_HORZ) + ) + || + (m_Orientation==VERTICAL + && !(pItem->modeResize()&ABSOLUTE_VERT) + && !(pItem->modeResize()&RELATIVE_VERT) + ) + ) + { + + // All greedy items get an equal portion of the left space + int nSize = availSpace / nGreedy; + + // minimum item/subpane size in primary direction (pixels) + int nSizeMin = sizeMin[i]; + + // maximum item/subpane size in primary direction (pixels) + int nSizeMax = sizeMax[i]; + + + // the last gets the all of the remaining space + if( nGreedy == 1 ) + nSize = availSpace; + + if( nSize < nSizeMin) { + // The item/pane is shrinked too small! + // We will grow it to it's minimum-size. In order not to modify + // this item later when fixing up set the size to the negative + // minimum size + sizePrimary[i] = -nSizeMin; + + // As we grew one item/subpane we have to shrink another one. + // We keep count on how much space we needed to grow the item + // to it's minimum size + greedyDiff += ( nSizeMin - nSize ); + } + else if( nSizeMax != -1 && nSize > nSizeMax) { + // if there's a maximum size (nSizeRelMax != -1) and our item/subpane + // is to be resized over that amount correct it. In order not to modify + // this item later when fixing up set the size to the negative + // maximum size + sizePrimary[i] = -nSizeMax; + + // As we shrinked one item/subpane we have to grow another one. + // We keep count on how much space we needed to grow the item + // to it's maximum size. + greedyDiff += ( nSizeMax - nSize ); + } + else { + + // this is the normal case: neither are we minimum limited nor maximum + // limited + + // As this item/subpane is larger that it's minimum we could later (if + // necessary for fixup) shrink it for the difference amount of pixels + greedyLeft += ( nSize - nSizeMin ); + + // Set the primary size of this item/pane. Can later be modified by fixup + sizePrimary[i] = nSize; + + // Add this item/subpane's primary size to the count of already allocated + // cumulated size of non-limited items/subpanes (these can be modified in fixup) + greedyCount += nSize; + } + + // decrease available space by used space in this step + availSpace -= nSize; + + // one greedy item/subpane complete + --nGreedy; + } + } + + + // Fixup Greedy I + // Distribute (if anecessary) greedyDiff on other (not limited) greedy items/subpanes + // (if available - if not later just grow the limited panes) + + // at least on not limited item present + bool bAtLeastOne = true; + + while( bAtLeastOne && greedyDiff != 0 && greedyCount > 0) { + + // in every iteration there must be some space distributed (of the difference) or it could + // come to endless looping. Save the amount of space actually distributed in this iteration + int greedyDist = 0; + + // at least on not limited item present + bAtLeastOne = false; + + for(int i=0; imodeResize()&ABSOLUTE_HORZ) + && !(pItem->modeResize()&RELATIVE_HORZ) + && sizePrimary[i] > 0 + ) + || + (m_Orientation==VERTICAL + && !(pItem->modeResize()&ABSOLUTE_VERT) + && !(pItem->modeResize()&RELATIVE_VERT) + && sizePrimary[i] > 0 + ) + ) + { + // keep a flag for termination of this iteration + bool bLast = false; + + // the difference should be distributed among all non-limited items/subpanes equally. + // nDiff is the amount for the current item/subpane + int nDiff = (greedyDiff * sizePrimary[i]) / greedyCount; + + // if it's a too small value just add it to the current pane and break iteration + if( abs(greedyDiff) <= FIXUP_CUTOFF || nDiff == 0) { + // take it all in this step + nDiff = greedyDiff; + + // set break flag + bLast = true; + } + + // calculate the new size for the current item/subpane + int nNewSize = sizePrimary[i] - nDiff; + + if( nNewSize < sizeMin[i] ) { + // oh, we are limited here. Revise our plan: + + if( sizePrimary[i] != sizeMin[i] ) + bAtLeastOne = true; + + // Not all of the space could be saved, add the actually possible space + // to the sum + greedyDist += ( sizePrimary[i] - sizeMin[i] ); + + // set it to the minimum possible size + sizePrimary[i] = sizeMin[i]; + + // as this item/subpane is now limited its occupied space doesn't count + // for relCount anymore + greedyCount -= ( sizePrimary[i] ); + } + else { + // yes, there is one + bAtLeastOne = true; + + // account the difference of the sizes in relDist and set new size + greedyDist += ( sizePrimary[i] - nNewSize ); + sizePrimary[i] = nNewSize; + + // if it's the last one break now + if(bLast) + break; + } + } + } + // Distributed some greedyDiff-space in every iteration + ASSERT(!bAtLeastOne || greedyDist != 0 || greedyCount<=0); + greedyDiff -= greedyDist; + } + + + // Fixup Greedy II + if( greedyDiff < 0 ) { + // still difference, some space left + + // are there any items which are minimum-limited where we can give more space? + for(int i=0; imodeResize()&ABSOLUTE_HORZ) + && !(pItem->modeResize()&RELATIVE_HORZ) + ) + || + (m_Orientation==VERTICAL + && !(pItem->modeResize()&ABSOLUTE_VERT) + && !(pItem->modeResize()&RELATIVE_VERT) + ) + ) + { + if( sizePrimary[i] == -sizeMin[i] ) { + // fill this one up as much as possible + if( sizeMax[i] == -1) { + // all fits in + sizePrimary[i] += greedyDiff; + greedyDiff = 0; + } + else { + sizePrimary[i] += -min( -greedyDiff, sizeMax[i]-sizeMin[i]); + greedyDiff -= -min( -greedyDiff, sizeMax[i]-sizeMin[i]); + } + } + } + } + } + + { + // Fixup Greedy III: invert all negative (limited) sized to correct value + for(int i=0; imodeResize() & ABSOLUTE_HORZ) + && !(pItem->modeResize() & RELATIVE_HORZ) + && sizePrimary[i] < 0 + && sizeMin[i] >= 0 + ) + || + (m_Orientation==VERTICAL + && !(pItem->modeResize() & ABSOLUTE_VERT) + && !(pItem->modeResize() & RELATIVE_VERT) + && sizePrimary[i] < 0 + && sizeMin[i] >= 0 + ) + ) + { + if(sizePrimary[i] < 0) + sizePrimary[i] *= -1; + } + } + } + + return true; +} + + +bool ETSLayoutMgr::Pane::resizeTo(CRect& rcNewArea) +{ + // There must be some items or subpanes + ASSERT(m_paneItems.GetSize()); + + // This Array holds the size in primary direction for each item/subpane + CArray sizePrimary; + sizePrimary.SetSize(m_paneItems.GetSize()); + + // This Array holds information about the minimum size in primary direction + CArray sizeMin; + sizeMin.SetSize(m_paneItems.GetSize()); + + // This Array holds information about the maximum size in primary direction + CArray sizeMax; + sizeMax.SetSize(m_paneItems.GetSize()); + + + // How much space is actually available, subtract all borders between items + int availSpace = (m_Orientation == HORIZONTAL ? rcNewArea.Width() : rcNewArea.Height() ) - (m_paneItems.GetUpperBound()*m_sizeBorder); + + // If there is some Extra border (on top/bottem resp. left/right) subtract it too + availSpace -= 2*m_sizeExtraBorder; + + // Add the extra Border to top/bottem resp. left/right + if(m_Orientation == HORIZONTAL) { + rcNewArea.top += m_sizeExtraBorder; + rcNewArea.bottom -= m_sizeExtraBorder; + } + else { + rcNewArea.left += m_sizeExtraBorder; + rcNewArea.right -= m_sizeExtraBorder; + } + + // Counts the number of greedy items/subpanes + int nGreedy = resizeToAbsolute(availSpace, sizePrimary, sizeMin, sizeMax ); + + if(nGreedy == -1) + return false; + + if(! resizeToRelative(availSpace, sizePrimary, sizeMin, sizeMax ) ) + return false; + + if(! resizeToGreedy(availSpace, nGreedy, sizePrimary, sizeMin, sizeMax ) ) + return false; + + + // If there is any left space and there are ALIGN_FILL_* Items to assign it + // equally among them + if( availSpace > 0 ) { + // Count possible Items + int nFillItems = 0; + + for(int i=0; imodeResize() & ABSOLUTE_HORZ ) + && (pItem->modeResize() & ALIGN_FILL_HORZ) + + || + + (pItem->modeResize() & ABSOLUTE_VERT ) + && (pItem->modeResize() & ALIGN_FILL_VERT) + ) + { + ++nFillItems; + } + } + + if( nFillItems > 0 ) { + // okay, there are nFillItems, make them all availSpace/nFillItems bigger + for(int i=0; imodeResize() & ABSOLUTE_HORZ ) + && (pItem->modeResize() & ALIGN_FILL_HORZ) + + || + + (pItem->modeResize() & ABSOLUTE_VERT ) + && (pItem->modeResize() & ALIGN_FILL_VERT) + ) + { + + if( nFillItems == 1 ) { + // the last one gets all the rest + sizePrimary[i] += availSpace; + availSpace = 0; + --nFillItems; + } + else { + sizePrimary[i] += availSpace/nFillItems; + availSpace -= availSpace/nFillItems; + --nFillItems; + } + + } + } + } + + } + + // Now reposition all items: + + // starting offset + int nOffset = (m_Orientation==HORIZONTAL ? rcNewArea.left : rcNewArea.top ) + m_sizeExtraBorder; + for(int i=0; iresizeTo( rcPane ); + + // go to the next position (old pos + size + border) + ASSERT(sizePrimary[i] >= 0); + nOffset += m_sizeBorder + sizePrimary[i]; + } + + + return true; +} + + +///////////////////////////////////////////////////////////////////////////// +// ETSLayoutDialog dialog + +#pragma warning(disable: 4355) +ETSLayoutDialog::ETSLayoutDialog(UINT nID, CWnd* pParent /*=NULL*/, LPCTSTR strName /*=NULL*/, bool bGripper /*=true*/) + : CBaseDialog(nID, pParent), ETSLayoutMgr( this ) +{ + //{{AFX_DATA_INIT(ETSLayoutDialog) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT + m_bGripper = bGripper; + + if(strName) + m_strRegStore = strName; +} +#pragma warning(default: 4355) + +BEGIN_MESSAGE_MAP(ETSLayoutDialog, CBaseDialog) + //{{AFX_MSG_MAP(ETSLayoutDialog) + ON_WM_SIZE() + ON_WM_GETMINMAXINFO() + ON_WM_ERASEBKGND() + ON_WM_DESTROY() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +///////////////////////////////////////////////////////////////////////////// +// ETSLayoutDialog message handlers + +BOOL ETSLayoutDialog::OnEraseBkgnd(CDC* pDC) +{ + EraseBkgnd(pDC); + return true; +} + +void ETSLayoutDialog::OnSize(UINT nType, int cx, int cy) +{ + CBaseDialog::OnSize(nType, cx, cy); + + if( abs(cx) + abs(cy) > 0) + { + // Reposition Size Marker + // Re-Layout all controls + UpdateLayout(); + RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0); + } + +} + +void ETSLayoutDialog::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) +{ + if(m_RootPane.IsValid()) { + + CRect rcClient = GetRect(); + if( rcClient.Height() > 0 || rcClient.Width() > 0 ) + { + + CRect rcWnd; + GetWindowRect(rcWnd); + + // How much do Window and Client differ + int nDiffHorz = rcWnd.Width() - rcClient.Width(); + int nDiffVert = rcWnd.Height() - rcClient.Height(); + + // Take into account that there is a border around the rootPane + lpMMI->ptMinTrackSize = CPoint(m_RootPane->getMinConstrainHorz() + nDiffHorz + 2*m_sizeRootBorders.cx, + m_RootPane->getMinConstrainVert() + nDiffVert + 2*m_sizeRootBorders.cy); + + int maxWidth = m_RootPane->getMaxConstrainHorz(); + int maxHeight = m_RootPane->getMaxConstrainVert(); + + if( maxWidth != -1 ) { + lpMMI->ptMaxTrackSize.x = maxWidth + nDiffHorz + 2*m_sizeRootBorders.cx; + lpMMI->ptMaxSize.x = maxWidth + nDiffHorz + 2*m_sizeRootBorders.cx; + } + + if( maxHeight != -1 ) { + lpMMI->ptMaxTrackSize.y = maxHeight + nDiffVert + 2*m_sizeRootBorders.cy; + lpMMI->ptMaxSize.y = maxHeight + nDiffVert + 2*m_sizeRootBorders.cy; + } + } + } +} + + +CRect ETSLayoutDialog::GetRect() +{ + CRect r; + GetClientRect(r); + + if( m_bGripper ) + { + if( ::IsWindow(m_StatusBar.GetSafeHwnd()) ) + { + CRect rcSizeIcon; + m_StatusBar.GetWindowRect( rcSizeIcon); + r.bottom -= (rcSizeIcon.Height() - m_sizeRootBorders.cy - 5); + } + } + + return r; +} + + +BOOL ETSLayoutDialog::OnInitDialog() +{ + CBaseDialog::OnInitDialog(); + + // Ensure that the dialog is resizable + this->ModifyStyle(0, WS_THICKFRAME); + + if(!m_strRegStore.IsEmpty()) { + Load(m_strRegStore); + } + +#ifdef _AUTO_SET_ICON + POSITION pos = AfxGetApp()->GetFirstDocTemplatePosition(); + if(pos) { + + class ETSPseudoDocTemplate : public CDocTemplate + { + friend class ETSLayoutDialog; + }; + + ETSPseudoDocTemplate* pDocT = (ETSPseudoDocTemplate*) AfxGetApp()->GetNextDocTemplate(pos); + SetIcon( AfxGetApp()->LoadIcon(pDocT->m_nIDResource) ,FALSE); + } +#endif + + // Sizing icon + if(m_bGripper) + { + if(m_StatusBar.Create(m_pWnd)) + { + m_StatusBar.SetIndicators(auIDStatusBar, sizeof(auIDStatusBar) / sizeof(UINT)); + m_StatusBar.SetWindowText(_T("")); + m_StatusBar.SetPaneStyle( 0, SBPS_STRETCH | SBPS_NOBORDERS ); + m_pWnd -> RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0); + } + else + AfxMessageBox(_T("Error - Statusbar")); + + } + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void ETSLayoutDialog::OnDestroy() +{ + // Store size/position + if(!m_strRegStore.IsEmpty()) { + Save(m_strRegStore); + } + + // manually delete layout definition if object is reused + m_RootPane = 0; + + CBaseDialog::OnDestroy(); +} + +///////////////////////////////////////////////////////////////////////////// +// ETSLayoutDialog dialog + +#pragma warning(disable: 4355) +#ifdef CS_HELP +ETSLayoutDialogBar::ETSLayoutDialogBar(UINT nID ) + : CBaseDialogBar( nID ), ETSLayoutMgr( this ) +#else +ETSLayoutDialogBar::ETSLayoutDialogBar() + : ETSLayoutMgr( this ) +#endif +{ + //{{AFX_DATA_INIT(ETSLayoutDialogBar) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT + m_bInitialized = false; + setRootBorders(0,0); +} +#pragma warning(default: 4355) + +BEGIN_MESSAGE_MAP(ETSLayoutDialogBar, CBaseDialogBar) + //{{AFX_MSG_MAP(ETSLayoutDialogBar) + ON_WM_SIZE() + ON_WM_GETMINMAXINFO() + ON_WM_DESTROY() + ON_WM_ERASEBKGND() + ON_MESSAGE(WM_INITDIALOG, OnInitDialog) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +///////////////////////////////////////////////////////////////////////////// +// ETSLayoutDialogBar message handlers + +LRESULT ETSLayoutDialogBar::OnInitDialog(WPARAM, LPARAM) +{ + Default(); + Initialize(); + return TRUE; +} + +void ETSLayoutDialogBar::UpdateLayout() +{ + ETSLayoutMgr::UpdateLayout(); + + if(m_RootPane.IsValid()) { + CRect rcClient = GetRect(); + + CRect rcWnd; + GetWindowRect(rcWnd); + + // How much do Window and Client differ + CSize sizeDiff( rcWnd.Width() - rcClient.Width(), rcWnd.Height() - rcClient.Height()); + + // Take into account that there is a border around the rootPane +// m_szMin = CSize(m_RootPane->getMinConstrainHorz() + sizeDiff.cx + 2*m_sizeRootBorders.cx, +// m_RootPane->getMinConstrainVert() + sizeDiff.cy + 2*m_sizeRootBorders.cy); + } +} + +CSize ETSLayoutDialogBar::CalcDynamicLayout(int nLength, DWORD dwMode) +{ + CSize sizeRet = CBaseDialogBar::CalcDynamicLayout(nLength, dwMode); + + CSize sizeMin = sizeRet; + CSize sizeMax = sizeRet; + + if(m_RootPane.IsValid()) { + CRect rcClient = GetRect(); + + CRect rcWnd; + GetWindowRect(rcWnd); + + // How much do Window and Client differ + CSize sizeDiff( rcWnd.Width() - rcClient.Width(), rcWnd.Height() - rcClient.Height()); + + // Take into account that there is a border around the rootPane +// sizeMin = CSize(m_RootPane->getMinConstrainHorz() + sizeDiff.cx + 2*m_sizeRootBorders.cx, +// m_RootPane->getMinConstrainVert() + sizeDiff.cy + 2*m_sizeRootBorders.cy); + + + int maxWidth = m_RootPane->getMaxConstrainHorz(); + int maxHeight = m_RootPane->getMaxConstrainVert(); + + if( maxWidth != -1 ) { + sizeMax.cx = maxWidth + sizeDiff.cy + 2*m_sizeRootBorders.cx; + } + + if( maxHeight != -1 ) { + sizeMax.cy = maxHeight + sizeDiff.cy + 2*m_sizeRootBorders.cy; + } + } + + if( IsFloating() || !(dwMode&LM_HORZ)) + { + sizeRet.cx = min( sizeRet.cx, sizeMax.cx ); + } + if( IsFloating() || (dwMode&LM_HORZ)) + { + sizeRet.cy = min( sizeRet.cy, sizeMax.cy ); + } + + sizeRet.cx = max( sizeRet.cx, sizeMin.cx ); + sizeRet.cy = max( sizeRet.cy, sizeMin.cy ); + + return sizeRet; +} + +BOOL ETSLayoutDialogBar::OnEraseBkgnd(CDC* pDC) +{ + EraseBkgnd(pDC); + return true; +} + + +void ETSLayoutDialogBar::OnSize(UINT nType, int cx, int cy) +{ + CBaseDialogBar::OnSize(nType, cx, cy); + + if( abs(cx) + abs(cy) > 0) + { + // Re-Layout all controls + UpdateLayout(); + } + RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0); + +} + + +CRect ETSLayoutDialogBar::GetRect() +{ + CRect r; + GetClientRect(r); + + if( IsFloating() ) + r.DeflateRect(4,4); + + return r; +} + + +void ETSLayoutDialogBar::OnDestroy() +{ + // Store size/position on your own! + CBaseDialogBar::OnDestroy(); +} + + + +///////////////////////////////////////////////////////////////////////////// +// ETSLayoutFormView dialog + +IMPLEMENT_DYNAMIC(ETSLayoutFormView, CFormView) + +#pragma warning(disable: 4355) +ETSLayoutFormView::ETSLayoutFormView(UINT nID, LPCTSTR strName /*=NULL*/) + : CBaseFormView(nID), ETSLayoutMgr( this ) +{ + if(strName) + m_strRegStore = strName; +} +#pragma warning(default: 4355) + +BEGIN_MESSAGE_MAP(ETSLayoutFormView, CBaseFormView) + //{{AFX_MSG_MAP(ETSLayoutFormView) + ON_WM_SIZE() + ON_WM_GETMINMAXINFO() + ON_WM_ERASEBKGND() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +///////////////////////////////////////////////////////////////////////////// +// ETSLayoutFormView message handlers + +BOOL ETSLayoutFormView::OnEraseBkgnd(CDC* pDC) +{ + EraseBkgnd(pDC); + return true; +} + + +void ETSLayoutFormView::OnSize(UINT nType, int cx, int cy) +{ +// CBaseFormView::OnSize(nType, cx, cy); + SetScrollSizes(MM_TEXT, CSize(cx,cy)); + if( abs(cx) + abs(cy) > 0) { + // Re-Layout all controls + UpdateLayout(); + } +// MoveWindow(0,0,cx,cy); +} + +/* +void ETSLayoutFormView::UpdateLayout() +{ + ETSLayoutMgr::UpdateLayout(); + + if(m_RootPane.IsValid()) { + // Force MainFrame to re-layout + CFrameWnd* pFrame = static_cast(GetParent()); + if(pFrame) { + + CRect rcWnd; + pFrame->GetWindowRect(rcWnd); + pFrame->MoveWindow(rcWnd); + pFrame->RecalcLayout(); + + } + return; + } +} +*/ + +void ETSLayoutFormView::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) +{ + // To use this you'll have to modify your CMainFrame: + // + // 1) Add a handler for WM_GETMINMAXINFO() + // 2) Let this handler be: + // void CMainFrame::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) + // { + // CFrameWnd::OnGetMinMaxInfo(lpMMI); + // + // if( GetActiveView() && GetActiveView()->IsKindOf( RUNTIME_CLASS(ETSLayoutFormView) ) ) { + // GetActiveView()->SendMessage( WM_GETMINMAXINFO, 0, (LPARAM) lpMMI ); + // } + // } + // 3) Add "#include "dialogmgr.h" to MainFrm.cpp + + if(m_RootPane.IsValid()) { + CRect rcClient = GetRect(); + + CRect rcWnd; + GetParent()->GetWindowRect(rcWnd); + + // How much do Window and Client differ + rcWnd-=rcClient; + + // Take into account that there is a border around the rootPane + lpMMI->ptMinTrackSize = CPoint(m_RootPane->getMinConstrainHorz() + rcWnd.Width() + 2*m_sizeRootBorders.cx, + m_RootPane->getMinConstrainVert() + rcWnd.Height() + 2*m_sizeRootBorders.cy); + + int maxWidth = m_RootPane->getMaxConstrainHorz(); + int maxHeight = m_RootPane->getMaxConstrainVert(); + + if( maxWidth != -1 ) { + lpMMI->ptMaxTrackSize.x = maxWidth + rcWnd.Width()+ 2*m_sizeRootBorders.cx; + lpMMI->ptMaxSize.x = maxWidth + rcWnd.Width()+ 2*m_sizeRootBorders.cx; + } + + if( maxHeight != -1 ) { + lpMMI->ptMaxTrackSize.y = maxHeight + rcWnd.Height() + 2*m_sizeRootBorders.cy; + lpMMI->ptMaxSize.y = maxHeight + rcWnd.Height() + 2*m_sizeRootBorders.cy; + } + } +} + +ETSLayoutFormView::~ETSLayoutFormView() +{ + // Cleanup +} + + +///////////////////////////////////////////////////////////////////////////// +// ETSLayoutPropertyPage + +#ifdef CS_HELP + IMPLEMENT_DYNCREATE(ETSLayoutPropertyPage, ETSCSHelpPropPage) +#else + IMPLEMENT_DYNCREATE(ETSLayoutPropertyPage, CPropertyPage) +#endif + +#pragma warning(disable: 4355) +ETSLayoutPropertyPage::ETSLayoutPropertyPage( ) : ETSLayoutMgr( this ) +{ + m_bLockMove = false; + m_bResetBuddyOnNextTimeVisible = true; +} + +ETSLayoutPropertyPage::ETSLayoutPropertyPage( UINT nIDTemplate, UINT nIDCaption /*= 0*/ ) + : CBasePropertyPage(nIDTemplate, nIDCaption), ETSLayoutMgr( this ) +{ + m_bLockMove = false; + m_bResetBuddyOnNextTimeVisible = true; +} + +ETSLayoutPropertyPage::ETSLayoutPropertyPage( LPCTSTR lpszTemplateName, UINT nIDCaption /*= 0*/ ) + : CBasePropertyPage(lpszTemplateName, nIDCaption), ETSLayoutMgr( this ) +{ + m_bLockMove = false; + m_bResetBuddyOnNextTimeVisible = true; +} +#pragma warning(default: 4355) + +ETSLayoutPropertyPage::~ETSLayoutPropertyPage() +{ +} + + +BEGIN_MESSAGE_MAP(ETSLayoutPropertyPage, CBasePropertyPage) + //{{AFX_MSG_MAP(ETSLayoutPropertyPage) + ON_WM_SIZE() + ON_WM_GETMINMAXINFO() + ON_WM_ERASEBKGND() + ON_WM_WINDOWPOSCHANGING() + ON_WM_DESTROY() + ON_WM_WINDOWPOSCHANGED() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +///////////////////////////////////////////////////////////////////////////// +// Behandlungsroutinen für Nachrichten ETSLayoutPropertyPage + + + +void ETSLayoutPropertyPage::OnWindowPosChanged(WINDOWPOS FAR* lpwndpos) +{ + CBasePropertyPage::OnWindowPosChanged(lpwndpos); + + // This code is needed in order to reset the buddy after this page has + // been activated. At least on Win2k this is not done thru normal resizing, + // as the page is not visible when first layouted. And without the page + // being visible it's not possible to tell if the attached buddy is visible + // or not (at least I don't know any way to do so) + + if( ::IsWindowVisible( GetWnd()->GetSafeHwnd() ) ) + { + if( m_bResetBuddyOnNextTimeVisible ) + { + // Take special care of SpinButtons (Up-Down Controls) with Buddy set, enumerate + // all childs: + CWnd* pWndChild = GetWnd()->GetWindow(GW_CHILD); + TCHAR szClassName[ MAX_PATH ]; + while(pWndChild) + { + ::GetClassName( pWndChild->GetSafeHwnd(), szClassName, MAX_PATH ); + DWORD dwStyle = pWndChild->GetStyle(); + + // is it a SpinButton? + if( _tcscmp(szClassName, UPDOWN_CLASS)==0 && ::IsWindowVisible(pWndChild->GetSafeHwnd()) ) { + HWND hwndBuddy = (HWND)::SendMessage( pWndChild->GetSafeHwnd(), UDM_GETBUDDY, 0, 0); + if( hwndBuddy != 0 && (dwStyle&(UDS_ALIGNRIGHT|UDS_ALIGNLEFT)) != 0 ) + { + // reset Buddy + ::SendMessage( pWndChild->GetSafeHwnd(), UDM_SETBUDDY, (WPARAM)hwndBuddy, 0); + } + } + + + pWndChild = pWndChild->GetWindow(GW_HWNDNEXT); + } + + m_bResetBuddyOnNextTimeVisible = false; + } + } + else + { + // has been hidden again + m_bResetBuddyOnNextTimeVisible = true; + } +} + +void ETSLayoutPropertyPage::OnWindowPosChanging( WINDOWPOS* lpwndpos ) +{ + // In WizardMode the System calls SetWindowPos with the + // original size at every activation. This could cause + // some flicker in certain circumstances. Therefore we lock + // moving the page and unlock it only if _we_ move the page + if( m_bLockMove) + { + lpwndpos->flags |= SWP_NOMOVE | SWP_NOSIZE; + } + CBasePropertyPage::OnWindowPosChanging( lpwndpos ); +} + +BOOL ETSLayoutPropertyPage::OnEraseBkgnd(CDC* pDC) +{ + EraseBkgnd(pDC); + return true; +} + +void ETSLayoutPropertyPage::OnDestroy() +{ + // manually delete layout definition if object is reused + m_RootPane = 0; + + CBasePropertyPage::OnDestroy(); +} + +void ETSLayoutPropertyPage::OnSize(UINT nType, int cx, int cy) +{ + CBasePropertyPage::OnSize(nType, cx, cy); + + if( abs(cx) + abs(cy) > 0) + { + // Re-Layout all controls + UpdateLayout(); + } +} + +void ETSLayoutPropertyPage::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) +{ + if(m_RootPane.IsValid()) { + CRect rcClient = GetRect(); + + CRect rcWnd; + GetWindowRect(rcWnd); + + // How much do Window and Client differ + int nDiffHorz = rcWnd.Width() - rcClient.Width(); + int nDiffVert = rcWnd.Height() - rcClient.Height(); + + // Take into account that there is a border around the rootPane + lpMMI->ptMinTrackSize = CPoint(m_RootPane->getMinConstrainHorz() + nDiffHorz + 2*m_sizeRootBorders.cx, + m_RootPane->getMinConstrainVert() + nDiffVert + 2*m_sizeRootBorders.cy); + + int maxWidth = m_RootPane->getMaxConstrainHorz(); + int maxHeight = m_RootPane->getMaxConstrainVert(); + + if( maxWidth != -1 ) { + lpMMI->ptMaxTrackSize.x = maxWidth + nDiffHorz + 2*m_sizeRootBorders.cx; + lpMMI->ptMaxSize.x = maxWidth + nDiffHorz + 2*m_sizeRootBorders.cx; + } + + if( maxHeight != -1 ) { + lpMMI->ptMaxTrackSize.y = maxHeight + nDiffVert + 2*m_sizeRootBorders.cy; + lpMMI->ptMaxSize.y = maxHeight + nDiffVert + 2*m_sizeRootBorders.cy; + } + } +} + + +CRect ETSLayoutPropertyPage::GetRect() +{ + CRect r; + GetClientRect(r); + return r; +} + + +BOOL ETSLayoutPropertyPage::OnInitDialog() +{ + CBasePropertyPage::OnInitDialog(); + UpdateLayout(); + + ETSLayoutPropertySheet* pSheet = (ETSLayoutPropertySheet*) GetParent(); + + ASSERT_KINDOF( ETSLayoutPropertySheet, pSheet); + if(pSheet) + { + if(pSheet->IsWizard()) + { + m_bLockMove = true; + } + } + + return TRUE; +} + +BOOL ETSLayoutPropertyPage::OnSetActive() +{ + ETSLayoutPropertySheet* pSheet = (ETSLayoutPropertySheet*) GetParent(); + + ASSERT_KINDOF( ETSLayoutPropertySheet, pSheet); + if(pSheet) + { + if(pSheet->IsWizard()) + { + // In WizardMode the System calls SetWindowPos with the + // original size on Page Activation. This will position the + // page at the correct position + m_bLockMove = false; + MoveWindow(pSheet->m_rcPage); + m_bLockMove = true; + } + } + + UpdateLayout(); + + return CBasePropertyPage::OnSetActive(); +} + +///////////////////////////////////////////////////////////////////////////// +// ETSLayoutPropertySheet + +IMPLEMENT_DYNAMIC(ETSLayoutPropertySheet, CPropertySheet) + +#pragma warning(disable: 4355) +ETSLayoutPropertySheet::ETSLayoutPropertySheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage, + LPCTSTR strName /*=NULL*/, bool bGripper/*=true*/) + : CPropertySheet(nIDCaption, pParentWnd, iSelectPage), ETSLayoutMgr( this ) +{ + Init(strName, bGripper); +} + +ETSLayoutPropertySheet::ETSLayoutPropertySheet(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage, + LPCTSTR strName /*=NULL*/, bool bGripper/*=true*/) + : CPropertySheet(pszCaption, pParentWnd, iSelectPage), ETSLayoutMgr( this ) +{ + Init(strName, bGripper); +} +#pragma warning(default: 4355) + +void ETSLayoutPropertySheet::Init(LPCTSTR strName, bool bGripper) +{ + m_bGripper = bGripper; + if(strName) + m_strRegStore = strName; + + m_bAutoDestroy = false; + m_bAutoDestroyPages = false; + m_bModelessButtons = false; +} + +ETSLayoutPropertySheet::~ETSLayoutPropertySheet() +{ +} + + +BEGIN_MESSAGE_MAP(ETSLayoutPropertySheet, CPropertySheet) + //{{AFX_MSG_MAP(ETSLayoutPropertySheet) + ON_WM_CREATE() + ON_WM_SIZE() + ON_WM_GETMINMAXINFO() + ON_WM_DESTROY() + ON_WM_ERASEBKGND() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// Behandlungsroutinen für Nachrichten ETSLayoutPropertySheet + +BOOL ETSLayoutPropertySheet::OnEraseBkgnd(CDC* pDC) +{ + EraseBkgnd(pDC); + return true; +} + + +int ETSLayoutPropertySheet::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + if (CPropertySheet::OnCreate(lpCreateStruct) == -1) + return -1; + + ModifyStyle(0,WS_THICKFRAME| WS_SYSMENU); + return 0; +} + + +void ETSLayoutPropertySheet::Resize(int cx, int cy) +{ + if( abs(cx) + abs(cy) > 0 && m_RootPane.IsValid() ) + { + UpdateLayout(); + + // Fix for PSH_WIZARDHASFINISH [Thömmi] + if (IsWizard() && !(m_psh.dwFlags & PSH_WIZARDHASFINISH) ) + { + // manual reposition of the FINISH button + // can not be done with normaly layouting because it + // shares position with the NEXT button + CWnd *pWndFinish; + pWndFinish=GetDlgItem(ID_WIZFINISH); + + if(pWndFinish) + { + CRect rcWnd; + GetDlgItem(ID_WIZNEXT)->GetWindowRect(&rcWnd); + ScreenToClient(&rcWnd); + pWndFinish->MoveWindow(rcWnd); + pWndFinish->RedrawWindow(0,0, RDW_INVALIDATE | RDW_UPDATENOW ); + } + } + + // reposition Gripper + if(m_bGripper) + RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0); + + CPropertyPage* pPage = (CPropertyPage*)GetActivePage(); + + if(pPage) + { + CRect rcWnd; + GetTabControl()->GetWindowRect(&rcWnd); + ScreenToClient(&rcWnd); + + if(!IsWizard()) { + // get inside of tab + GetTabControl()->AdjustRect(FALSE, &rcWnd); + } + else + { + rcWnd.bottom += 5; + } + + // we need this size in WizardMode in order to + // reposition newly activated page correctly + m_rcPage = rcWnd; + + if( IsWizard() && pPage->IsKindOf(RUNTIME_CLASS(ETSLayoutPropertyPage)) ) + { + ETSLayoutPropertyPage* pEtsPage = reinterpret_cast(pPage); + + pEtsPage->m_bLockMove = false; + pEtsPage->MoveWindow(m_rcPage); + pEtsPage->m_bLockMove = true; + } + else + { + pPage->MoveWindow(m_rcPage); + } + + } + + if(IsWindowVisible()) + { + RedrawWindow(0,0, RDW_INVALIDATE|RDW_UPDATENOW ); + + if(!IsWizard()) + GetTabControl()->RedrawWindow(0,0, RDW_INVALIDATE|RDW_UPDATENOW ); + } + } +} + +void ETSLayoutPropertySheet::OnSize(UINT nType, int cx, int cy) +{ + CPropertySheet::OnSize(nType, cx, cy); + Resize(cx,cy); +} + +// IDs of all PropertySheet controls +long _PropertySheetIDs[] = +{ + ID_WIZBACK, + ID_WIZNEXT, + ID_WIZFINISH, + IDOK, + IDCANCEL, + ID_APPLY_NOW, + IDHELP +}; + +void ETSLayoutPropertySheet::AddMainArea(CPane paneRoot, CPaneBase itemTab) +{ + // the default is: Whole main Area is covered by the TabCtrl + paneRoot << itemTab; +} + +void ETSLayoutPropertySheet::AddButtons(CPane paneBottom) +{ + // first item greedy to keep others right + paneBottom->addItem (paneNull, GREEDY); + + + // add all Controls to the layouting + bool bFirst = true; + for(int i = 0; i < (sizeof(_PropertySheetIDs) / sizeof(long)) ; i++) + { + // Prevent movement of finish button, if it is not shown explicitly [Thömmi] + if( IsWizard() + && _PropertySheetIDs[i] == ID_WIZFINISH + && !(m_psh.dwFlags & PSH_WIZARDHASFINISH) ) + { + continue; + } + + CWnd* pWnd = GetDlgItem(_PropertySheetIDs[i]); + + if(pWnd) + { + + if(!(m_psh.dwFlags & PSH_HASHELP) && _PropertySheetIDs[i] == IDHELP) + { + // don't insert + continue; + } + + if((m_psh.dwFlags & PSH_NOAPPLYNOW) && _PropertySheetIDs[i] == ID_APPLY_NOW) + { + // don't insert + continue; + } + + // space before first one and between BACK & NEXT + if( IsWizard() ) + { + if( !bFirst && !(_PropertySheetIDs[i]==ID_WIZNEXT) ) + { + paneBottom->addItem(paneNull, NORESIZE,12,0,0,0); + } + } + + pWnd->ShowWindow(true); + paneBottom->addItem(_PropertySheetIDs[i], NORESIZE); + bFirst = false; + } + } + +} + +BOOL ETSLayoutPropertySheet::OnInitDialog() +{ + BOOL bRet = CPropertySheet::OnInitDialog(); + + ASSERT(!m_RootPane); + + // Save initial rect + GetWindowRect(&m_rcStart); + + CPropertyPage* pPage = CPropertySheet::GetActivePage(); + ASSERT(pPage); + + CRect rcPage; + pPage->GetClientRect(&rcPage); + + CreateRoot(VERTICAL); + ASSERT(m_RootPane.IsValid()); + + // Add Tabcontrol to root pane + m_ItemTab = item( GetTabControl(), GREEDY, 0, 0, 0, 0); + AddMainArea(m_RootPane, m_ItemTab); + + // Tabcontrol is invisible in WizardMode + if(IsWizard()) + { + GetTabControl()->ShowWindow(false); + } + + // add horizontal line in WizardMode + if(IsWizard() && GetDlgItem(ID_WIZFINISH+1)) + { + m_RootPane << item(ID_WIZFINISH+1, ABSOLUTE_VERT, 0, 0, 0, 0); + } + + if( IsWizard() || !m_bModeless || m_bModelessButtons ) + { + // No spaces in WizardMode in order to keep BACK & NEXT together + CPane bottomPane = pane(HORIZONTAL, ABSOLUTE_VERT, IsWizard() ? 0 : 5); + + AddButtons(bottomPane); + // add bottom (button) pane if any controls were added + if(bottomPane->m_paneItems.GetSize() > 0) { + m_RootPane << bottomPane; + } + } + + + + // some Space between Buttons und Gripper + if(m_bGripper) + { + m_RootPane->addItem(paneNull, ABSOLUTE_VERT,0,2); + + if(m_StatusBar.Create(m_pWnd)) + { + m_StatusBar.SetIndicators(auIDStatusBar, + sizeof(auIDStatusBar) / sizeof(UINT)); + m_StatusBar.SetWindowText(_T("")); + RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0); + } + else + { + AfxMessageBox(_T("Error - Statusbar")); + } + } + + if(!m_strRegStore.IsEmpty()) + { + Load(m_strRegStore); + } + + Resize(1,1); // Fix. for 95/98/NT difference + + CRect rcWnd; + GetWindowRect( & rcWnd ); + MoveWindow( rcWnd ); + + return bRet; +} + + +void ETSLayoutPropertySheet::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) +{ + if(m_RootPane.IsValid() && GetTabControl() != 0 ) + { + CRect rcWnd; + GetWindowRect(rcWnd); + + CRect rcClient = GetRect(); + rcWnd-=rcClient; + + // ask for MinMax of all pages + CSize sizePageMax(0,0); + CSize sizePageMin(0,0); + for( int nPage=0; nPageGetSafeHwnd()) ) + { + pPage->SendMessage(WM_GETMINMAXINFO, 0, (LPARAM) &mmi); + + if(mmi.ptMaxTrackSize.x != 0) + { + sizePageMax.cx = min(sizePageMax.cx, mmi.ptMaxTrackSize.x); + } + if(mmi.ptMaxTrackSize.y != 0) + { + sizePageMax.cy = min(sizePageMax.cy, mmi.ptMaxTrackSize.y); + } + if(mmi.ptMinTrackSize.x != 0) + { + sizePageMin.cx = max(sizePageMin.cx, mmi.ptMinTrackSize.x); + } + if(mmi.ptMinTrackSize.y != 0) + { + sizePageMin.cy = max(sizePageMin.cy, mmi.ptMinTrackSize.y); + } + } + } + } + static_cast( m_ItemTab.GetPaneBase() )->m_sizeXMin = sizePageMin.cx; + static_cast( m_ItemTab.GetPaneBase() )->m_sizeYMin = sizePageMin.cy; + + // calculate the needed size of the tabctrl in non-wizard-mode + CRect rcItem(0,0,0,0); + if(!IsWizard()) + { + GetTabControl()->AdjustRect( TRUE, rcItem ); + } + + lpMMI->ptMinTrackSize.x = m_RootPane->getMinConstrainHorz() + rcWnd.Width() + 2*m_sizeRootBorders.cx + + rcItem.Width(); + + lpMMI->ptMinTrackSize.y = m_RootPane->getMinConstrainVert() + rcWnd.Height() + 2*m_sizeRootBorders.cy + + rcItem.Height(); + + // never smaller than inital size! + lpMMI->ptMinTrackSize.x = max(lpMMI->ptMinTrackSize.x, m_rcStart.Width() ); + lpMMI->ptMinTrackSize.y = max(lpMMI->ptMinTrackSize.y, m_rcStart.Height() ); + + // Rest like ETSLayoutMgr + + int maxWidth = m_RootPane->getMaxConstrainHorz(); + int maxHeight = m_RootPane->getMaxConstrainVert(); + + if( maxWidth != -1 ) + { + lpMMI->ptMaxSize.x = sizePageMax.cx + rcWnd.Width()+ 2*m_sizeRootBorders.cx + rcItem.Width() ; + } + + if( maxHeight != -1 ) + { + lpMMI->ptMaxSize.y = sizePageMax.cy + rcWnd.Height() + 2*m_sizeRootBorders.cy + rcItem.Width() ; + } + + lpMMI->ptMaxTrackSize = lpMMI->ptMaxSize; + + } +} + + +void ETSLayoutPropertySheet::OnDestroy() +{ + // Store size/position + if(!m_strRegStore.IsEmpty()) + { + Save(m_strRegStore); + } + m_RootPane = 0; + + CPropertySheet::OnDestroy(); +} + +void ETSLayoutPropertySheet::PostNcDestroy() +{ + if(m_bAutoDestroyPages) + { + // walk all pages and destry them + for( int nPage=0; nPageRelease(); +} + +void ETSLayoutMgr::CPaneBase::operator=( PaneBase* pPane ) +{ + if(m_pPaneHolder) + { + m_pPaneHolder->Release(); + m_pPaneHolder = 0; + } + + if( pPane != 0 ) + m_pPaneHolder = new PaneHolder( pPane ); +} + +void ETSLayoutMgr::CPaneBase::operator=( const CPaneBase& other ) +{ + ASSERT( other.m_pPaneHolder ); + + if(m_pPaneHolder) + { + m_pPaneHolder->Release(); + m_pPaneHolder = 0; + } + + other.m_pPaneHolder->AddRef(); + m_pPaneHolder = other.m_pPaneHolder; +} + +ETSLayoutMgr::PaneBase* ETSLayoutMgr::CPaneBase::operator->() const +{ + ASSERT(m_pPaneHolder); + + if(!m_pPaneHolder) + return 0; + + return (m_pPaneHolder->m_pPane); +} + + + +ETSLayoutMgr::CPane::CPane( ) +{ +} + +ETSLayoutMgr::CPane::CPane( Pane* pPane ) : ETSLayoutMgr::CPaneBase( static_cast(pPane) ) +{ +} + +ETSLayoutMgr::CPane::CPane( const CPane& other ) +{ + operator=(other); +} + +ETSLayoutMgr::CPane::~CPane() +{ +} + +void ETSLayoutMgr::CPane::operator=( Pane* pPane ) +{ + CPaneBase::operator=(pPane); +} + +void ETSLayoutMgr::CPane::operator=( const ETSLayoutMgr::CPane& other ) +{ + ASSERT( other.m_pPaneHolder ); + + if(m_pPaneHolder) + { + m_pPaneHolder->Release(); + m_pPaneHolder = 0; + } + + other.m_pPaneHolder->AddRef(); + m_pPaneHolder = other.m_pPaneHolder; +} + +ETSLayoutMgr::Pane* ETSLayoutMgr::CPane::operator->() const +{ + ASSERT(m_pPaneHolder); + + if(!m_pPaneHolder) + return 0; + + return reinterpret_cast(m_pPaneHolder->m_pPane); +} + +ETSLayoutMgr::CPaneBase ETSLayoutMgr::CPane::ConvertBase() const +{ + ASSERT(m_pPaneHolder); + return CPaneBase( m_pPaneHolder->m_pPane ); +} + +ETSLayoutMgr::CPane& ETSLayoutMgr::CPane::operator<< ( const ETSLayoutMgr::CPane pPane ) +{ + GetPane()->addPane( pPane, (ETSLayoutMgr::layResizeMode)pPane->m_modeResize, pPane->m_sizeSecondary); + return (*this); +} + +ETSLayoutMgr::CPane& ETSLayoutMgr::CPane::operator<< ( const ETSLayoutMgr::CPaneBase pItem ) +{ + GetPane()->addPane( pItem ); + return (*this); +} diff --git a/misc/windows/ETSLayout.h b/misc/windows/ETSLayout.h new file mode 100644 index 0000000..0e641a7 --- /dev/null +++ b/misc/windows/ETSLayout.h @@ -0,0 +1,964 @@ +//////////////////////////////////////////// +// ___ ____ _________________ // +// / _/_ _// _______________/ // +// / _/ / / / / ___ ___ ____ // +// /__/ /_/ / / / // _/_ _/ // +// _________/ / / / // _/ / / // +// (c) 1998-2000_/ /___//_/ /_/ // +// // +//////////////////////////////////////////// +// all rights reserved // +//////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// ETSLayoutDialog +// +// A class for smart layouting of Dialogs and such +// +// USAGE: See LayoutMgr.html +// +// AUTHOR: Erwin Tratar +// +// DISCLAIMER: +// +// This Sourcecode and all accompaning material is ©1998-1999 Erwin Tratar. +// All rights reserved. +// +// The source code may be used in compiled form in any way you desire +// (including usage in commercial applications), providing that your +// application adds essential code (i.e. it is not only a wrapper) to the +// functionality found here +// +// Redistribution of the sourcecode itself, publication in any media or +// inclusion in a library requires the authors expressed written consent. +// You may not sale this code for profit. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. USE IT +// AT YOUR OWN RISK! THE AUTHOR ACCEPTS NO LIABILITY FOR ANY DAMAGE/LOSS OF +// BUSINESS THAT THIS PRODUCT MAY CAUSE. + + +#if !defined(ETS_LAYOUTMGR_INCLUDED_) +#define ETS_LAYOUTMGR_INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// DialogMgr.h : header file +// + +namespace ETSLayout +{ + +#ifdef CS_HELP + typedef ETSCSHelpDialog CBaseDialog; + typedef ETSCSHelpFormView CBaseFormView; + typedef ETSCSHelpDlgBar CBaseDialogBar; + typedef ETSCSHelpPropPage CBasePropertyPage; +#else + typedef CDialog CBaseDialog; + typedef CFormView CBaseFormView; + typedef CDialogBar CBaseDialogBar; + typedef CPropertyPage CBasePropertyPage; +#endif +} + +// Support for CBCGDialogBar instead of CDialogBar available: +// you just have to change the typedef to CBaseDialogBar + +#ifndef ETSGUI_EXT_CLASS +#define ETSGUI_EXT_CLASS +#endif + +#include + +// Support for CBCGDialogBar instead of CDialogBar + +/** + * Controls whether the Icon is automatically set to IDR_MAINFRAME + */ +#define _AUTO_SET_ICON + +/** + * Forward class declarations + */ +class ETSLayoutDialog; +class ETSLayoutDialogBar; +class ETSLayoutFormView; +class ETSLayoutMgr; +class ETSLayoutPropertyPage; +class ETSLayoutPropertySheet; + + +/** + * These are NOOPs now + */ +#define DECLARE_LAYOUT() +#define IMPLEMENT_LAYOUT() + +/** + * This is the default border size between the panes. You + * may override it in Pane constructor, but it is the + * fixed border around the root pane + */ +const int nDefaultBorder = 5; + +/** + * The minimum size for not ABSOLUTE_XXX items + */ +const int nMinConstrain = 5; + +class ETSGUI_EXT_CLASS ETSLayoutMgr +{ +public: + + enum layResizeMode { + GREEDY = 0, // Will eat up as much as it can + ABSOLUTE_HORZ = 1 << 0, // Horizontal size is absolute + RELATIVE_HORZ = 1 << 1, // Horizontal size in percent + ABSOLUTE_VERT = 1 << 2, // Vertical size is absolute + RELATIVE_VERT = 1 << 3, // Vertical size in percent + + NORESIZE = ABSOLUTE_HORZ | ABSOLUTE_VERT, + + SIZE_MASK = NORESIZE, + + ALIGN_LEFT = 1 << 4, // following only for NORESIZE + ALIGN_RIGHT = 1 << 5, + ALIGN_TOP = 1 << 6, + ALIGN_BOTTOM = 1 << 7, + + ALIGN_HCENTER = ALIGN_LEFT | ALIGN_RIGHT, + ALIGN_VCENTER = ALIGN_TOP | ALIGN_BOTTOM, + + ALIGN_CENTER = ALIGN_HCENTER | ALIGN_VCENTER, + + ALIGN_FILL_HORZ = 1 << 8, + ALIGN_FILL_VERT = 1 << 9, + ALIGN_FILL = ALIGN_FILL_HORZ | ALIGN_FILL_VERT, + +/* TRACKER_LEFT = 1 << 10, // not yet. May allow tracking of borders + TRACKER_RIGHT = 1 << 11, // between items in the future + TRACKER_TOP = 1 << 12, + TRACKER_BOTTOM = 1 << 13, +*/ + }; + + enum layOrientation { + HORIZONTAL, + VERTICAL + }; + + /** + * This is the base class for all kind of panes. + */ + class ETSGUI_EXT_CLASS PaneBase { + friend class ETSLayoutMgr; + friend class CPaneBase; + friend class CPane; + + public: + + /** + * Informs the caller how much of the given space this pane would + * like to receive in horizontal direction + */ + virtual int getConstrainHorz(int sizeParent) = 0; + + + /** + * Informs the caller how much of the given space this pane would + * like to receive in vertical direction + */ + virtual int getConstrainVert(int sizeParent) = 0; + + /** + * Informs the caller how much of the given space this pane + * minimally need. This would be an absolute Value if + * the mode contains ABSOLUTE_HORZ or an explicit minimum + * value, else nMinConstrain + */ + virtual int getMinConstrainHorz() = 0; + /** + * Informs the caller if there is an restriction for maximum + * space this pane needs. Return -1 for unrestricted (GREEDY + * or RELATIVE) + */ + virtual int getMaxConstrainHorz() = 0; + + /** + * Informs the caller how much of the given space this pane + * minimally need. This would be an absolute Value if + * the mode contains ABSOLUTE_VERT or an explicit minimum + * value, else nMinConstrain + */ + virtual int getMinConstrainVert() = 0; + + /** + * Informs the caller if there is an restriction for maximum + * space this pane needs. Return -1 for unrestricted (GREEDY + * or RELATIVE) + */ + virtual int getMaxConstrainVert() = 0; + + /** + * This will do the actual resize operation after the + * caller computed a new area for this pane + */ + virtual bool resizeTo(CRect& rcNewArea) = 0; + + /** + * Constructor needed pointer to LayoutManager + */ + PaneBase( ETSLayoutMgr* pMgr ) { m_pMgr = pMgr; }; + + /** + * Virtual destructor needed in Container operations + */ + virtual ~PaneBase() {}; + + /** + * Returs the Resize Mode of this pane + */ + DWORD modeResize() { return m_modeResize; }; + + protected: + /** + * How this Item will be resized, a combination of the flags above + */ + DWORD m_modeResize; + + /** + * A pointer to the holding LayoutManager derivate + */ + ETSLayoutMgr* m_pMgr; + }; + + /** + * CPaneBase represents an autopointer to a PaneBase. Use this and you won't have to worry + * about cleaning up any Panes. Also this autopointer lets you return Pane objects + * from function without using pointers (at least you won't see them :) ) + */ + struct ETSGUI_EXT_CLASS PaneHolder + { + PaneHolder(PaneBase* pPane ); + ~PaneHolder(); + + void AddRef(); + void Release(); + + PaneBase* m_pPane; + long m_nRefCount; + }; + + class ETSGUI_EXT_CLASS CPaneBase + { + protected: + PaneHolder* m_pPaneHolder; + + public: + // Standardconstructor + CPaneBase( ); + CPaneBase( PaneBase* pPane ); + CPaneBase( const CPaneBase& other ); + + ~CPaneBase(); + + void operator=( PaneBase* pPane ); + void operator=( const CPaneBase& other ); + PaneBase* operator->() const; + PaneBase* GetPaneBase() { return operator->(); } + + bool IsValid() { return (m_pPaneHolder != 0); } + bool operator !() { return (m_pPaneHolder == 0); } + + }; + + class Pane; + class ETSGUI_EXT_CLASS CPane : public CPaneBase + { + public: + // Standardconstructor + CPane( ); + CPane( Pane* pPane ); + CPane( const CPane& other ); + + ~CPane(); + + void operator=( Pane* pPane ); + void operator=( const CPane& other ); + Pane* operator->() const; + Pane* GetPane() { return operator->(); } + + CPaneBase ConvertBase() const; + + CPane& operator<< ( const CPane pPane ); + CPane& operator<< ( const CPaneBase pItem ); + }; + + + + /** + * PaneItem represents a single control + */ + class ETSGUI_EXT_CLASS PaneItem : public PaneBase { + friend class ETSLayoutMgr; + friend class Pane; + protected: + /** + * Creates a new PaneItem from an Control. If sizeX or sizeY are 0 + * and modeResize is ABSOLUTE will copy the current dimensions of + * the control to m_sizeX/Y. So the appearance does not change + * from the Dialog Editor + */ + PaneItem( CWnd* pWnd, ETSLayoutMgr* pMgr, layResizeMode modeResize = GREEDY, int sizeX=0, int sizeY=0, int sizeXMin=0, int sizeYMin=0); + + /** + * If your control is not mapped you can name it by its ChildID. Pass + * the pMgr to receive the CWnd* of nID. + * The rest as stated above + */ + PaneItem( UINT nID, ETSLayoutMgr* pMgr, layResizeMode modeResize = GREEDY, int sizeX=0, int sizeY=0, int sizeXMin=0, int sizeYMin=0); + + + public: + /** + * see PaneBase + */ + virtual int getConstrainHorz(int sizeParent); + virtual int getConstrainVert(int sizeParent); + virtual int getMinConstrainHorz(); + virtual int getMinConstrainVert(); + virtual int getMaxConstrainHorz(); + virtual int getMaxConstrainVert(); + virtual bool resizeTo(CRect& rcNewArea); + + bool isDummy() { return (m_hwndCtrl == 0); } + + protected: + friend class ETSLayoutPropertySheet; + + /** + * The horizontal size of the control (see m_modeResize) + */ + int m_sizeX; + int m_sizeXMin; + + /** + * The vertical size of the control (see m_modeResize) + */ + int m_sizeY; + int m_sizeYMin; + + /** + * Child Control pointer + */ + HWND m_hwndCtrl; + + /** + * Combo box needs special treatment + */ + bool m_bComboSpecial; + }; + + + /** + * This class encapsulates a Subpane (and indeed the root Pane too) + * it is a container of PaneBase* which it will recursivly resize + */ + class ETSGUI_EXT_CLASS Pane : public PaneBase { + friend class ETSLayoutMgr; + friend class CPaneBase; + friend class CPane; + friend class ETSLayoutPropertySheet; + + protected: + /** + * Tell the pane in which direction it is positioned. A HORIZONTAL pane + * arranges it's subpanes from left to right, a VERTICAL from top to bottom + */ + Pane( ETSLayoutMgr* pMgr, layOrientation orientation, int sizeBorder = nDefaultBorder, int sizeExtraBorder = 0 ); + + public: + /** + * If your control is not mapped you can name it by its ChildID. Pass + * the pMgr to receive the CWnd* of nID. + * The rest as stated above + */ + bool addItem( UINT nID, layResizeMode modeResize = GREEDY, int sizeX=0, int sizeY=0, int sizeXMin=-1, int sizeYMin=-1); + + /** + * Creates a new PaneItem from an Control. If sizeX or sizeY are 0 + * and modeResize is ABSOLUTE will copy the current dimensions of + * the control to m_sizeX/Y. So the appearance does not change + * from the Dialog Editor + */ + bool addItem( CWnd* pWnd, layResizeMode modeResize = GREEDY, int sizeX=0, int sizeY=0, int sizeXMin=-1, int sizeYMin=-1); + + + /** + * Add a whitespace Item (paneNull) of variable size with + * a minimum size of 0 + */ + bool addItemGrowing(); + + /** + * Add a whitespace Item (paneNull) with fixed size + */ + bool addItemFixed(int size); + + /** + * Add a whitespace Item (paneNull) of fixed size based on the + * current layout (as in the dialog template). Based on the layout + * of the pane vertical or horizontal spacing is considered + * + * First argument is the left (top) item for a HORIZONTAL (VERTICAL) pane + */ + bool addItemSpaceBetween( CWnd* pWndFirst, CWnd* pWndSecond ); + bool addItemSpaceBetween( UINT nIDFirst, UINT nIDSecond ); + + + /** + * Add a whitespace Item (paneNull) of fixed size based on the + * size of another item + */ + bool addItemSpaceLike( CWnd* pWnd ); + bool addItemSpaceLike( UINT nID ); + + + /** + * Add an item to the pane, appending at the end. This may be either obtained + * by a call to ETSLayoutMgr::item() or one of the ETSLayoutMgr::paneXXX() calls + */ + bool addPane( CPaneBase pItem ); + bool addPane( CPane pSubpane, layResizeMode modeResize, int sizeSecondary /* = 0 */); + + virtual int getConstrainHorz(int sizeParent); + virtual int getConstrainVert(int sizeParent); + virtual int getMinConstrainHorz(); + virtual int getMinConstrainVert(); + virtual int getMaxConstrainHorz(); + virtual int getMaxConstrainVert(); + virtual bool resizeTo(CRect& rcNewArea); + + /** + * The destructor takes care of destroying all Subpanes and items + */ + virtual ~Pane(); + + /** + * Access to the orientation of this pane + */ + layOrientation getOrientation() { return m_Orientation; }; + + + protected: + + int resizeToAbsolute(int& availSpace, CArray& sizePrimary, + CArray& sizeMin, CArray& sizeMax); + + bool resizeToRelative(int& availSpace, CArray& sizePrimary, + CArray& sizeMin, CArray& sizeMax); + + bool resizeToGreedy( int& availSpace, int nGreedy, CArray& sizePrimary, + CArray& sizeMin, CArray& sizeMax); + + /** + * The orientation of the pane. Keep in mind that all subpanes + * must have the complementary orientation, i.e. a VERTICAL + * pane must have all HORIZONTAL SubPanes (or normal Items + * of course) + */ + layOrientation m_Orientation; + + /** + * This array holds the pointers to the Items/SubPanes + */ + CArray m_paneItems; + + /** + * The secondary constrain + */ + int m_sizeSecondary; + + /** + * Size of gap between childs + */ + int m_sizeBorder; + int m_sizeExtraBorder; + }; + + + /** + * This class encapsulates a Subpane which is a Tab + * it will use calls to AdjustRect to position it's + * childs + */ + class ETSGUI_EXT_CLASS PaneTab : public Pane + { + friend class ETSLayoutMgr; + + protected: + /** + * Tell the pane in which direction it is positioned. A HORIZONTAL pane + * arranges it's subpanes from left to right, a VERTICAL from top to bottom + */ + PaneTab( CTabCtrl* pTab, ETSLayoutMgr* pMgr, layOrientation orientation, int sizeBorder = nDefaultBorder, int sizeExtraBorder = 0 ); + + public: + virtual int getConstrainHorz(int sizeParent); + virtual int getConstrainVert(int sizeParent); + virtual int getMinConstrainHorz(); + virtual int getMinConstrainVert(); + virtual int getMaxConstrainHorz(); + virtual int getMaxConstrainVert(); + virtual bool resizeTo(CRect& rcNewArea); + + private: + CTabCtrl* m_pTab; + }; + + /** + * This class encapsulates a Subpane which is a Static + * it will use calls to AdjustRect to position it's + * childs + */ + class ETSGUI_EXT_CLASS PaneCtrl : public Pane + { + friend class ETSLayoutMgr; + protected: + /** + * Tell the pane in which direction it is positioned. A HORIZONTAL pane + * arranges it's subpanes from left to right, a VERTICAL from top to bottom + */ + PaneCtrl( CWnd* pCtrl, ETSLayoutMgr* pMgr, layOrientation orientation, int sizeBorder = nDefaultBorder, int sizeExtraBorder = 0, int sizeTopExtra = 0); + PaneCtrl( UINT nID, ETSLayoutMgr* pMgr, layOrientation orientation, int sizeBorder = nDefaultBorder, int sizeExtraBorder = 0, int sizeTopExtra = 0 ); + + public: + + virtual int getConstrainHorz(int sizeParent); + virtual int getConstrainVert(int sizeParent); + virtual int getMinConstrainHorz(); + virtual int getMinConstrainVert(); + virtual int getMaxConstrainHorz(); + virtual int getMaxConstrainVert(); + virtual bool resizeTo(CRect& rcNewArea); + + private: + HWND m_hwndCtrl; + int m_sizeTopExtra; + }; + + + + + ETSLayoutMgr(CWnd* pWnd) { m_pWnd = pWnd; m_sizeRootBorders = CSize(5,5); }; + virtual ~ETSLayoutMgr(); + + virtual CRect GetRect() { CRect r; m_pWnd->GetClientRect(r); return r; }; + CWnd* m_pWnd; + CWnd* GetWnd() { return m_pWnd; }; + void setRootBorders(int cx, int cy) { m_sizeRootBorders = CSize(cx,cy); }; + + /** + * Pass this for a pseudo Pane with no content + */ + static CWnd* paneNull; + + /** + * Loads the current position and size from the registry using a supplied + * key. Will be loaded with AfxGetApp()->WriteProfileXXX(). You may + * specify a subfolder (e.g. Load( _T("MyDialog\\Layout") ); ). Will + * load the following keys: + * + * - lpstrRegKey+"SizeX"; + * - lpstrRegKey+"SizeY"; + * - lpstrRegKey+"PosX"; + * - lpstrRegKey+"PosY"; + * + * Is automatically called during OnActivate() if key specified in + * constructor. + */ + bool Load(LPCTSTR lpstrRegKey); + + /** + * Store the current position and size to the registry using a supplied + * key. Will be stored with AfxGetApp()->WriteProfileXXX(). You may + * specify a subfolder (e.g. Save( _T("MyDialog\\Layout") ); ). Will + * create the following keys: + * + * - lpstrRegKey+"SizeX"; + * - lpstrRegKey+"SizeY"; + * - lpstrRegKey+"PosX"; + * - lpstrRegKey+"PosY"; + * + * Is automatically called during DestroyWindow() if key specified in + * constructor. + */ + bool Save(LPCTSTR lpstrRegKey); + + /** + * Updates the layout after you specify the new + * layout + */ + virtual void UpdateLayout(); + virtual void UpdateLayout(CPane p) { + if(m_RootPane.IsValid()) + { + // free old root + m_RootPane = 0; + } + m_RootPane = p; + UpdateLayout(); + } + + /** + * Does the actual Layout, called from OnSize() + * Default implementation does nothing, use + * IMPLEMENT_LAYOUT in your derived class (see above) + */ + virtual void Layout(CRect& rcClient); + + + /** + * Erasing only the these parts of the client area where + * there is no child window. Extra-code for group-boxes + * included! + */ + void EraseBkgnd(CDC* pDC); + + /** + * Helperfunctions for the stream-interface. For usage see sample Application + * and/or documentation. + */ + + /** + * Create a new Pane. You may specify the resize + * mode for both directions. If you add modes for the secondary direction + * (i.e. *_VERT for a HORIZONTAL pane) then sizeSecondary is used as it's + * size. If you do not specify sizeSecondary and the mode is ABSOLUTE_VERT + * it will be computed as the maximum Height of all SubPanes (the same is + * true for VERTICAL panes and subpanes with *_HORZ) + */ + CPane pane( layOrientation orientation, layResizeMode modeResize = GREEDY, int sizeBorder = nDefaultBorder, int sizeExtraBorder = 0, int sizeSecondary = 0); + + /** + * Create one of the special control panes. Parameter are like pane(). For + * additional information see documentation + */ + CPane paneTab( CTabCtrl* pTab, layOrientation orientation, layResizeMode modeResize = GREEDY, int sizeBorder = nDefaultBorder, int sizeExtraBorder = 0, int sizeSecondary = 0); + CPane paneCtrl( UINT nID, layOrientation orientation, layResizeMode modeResize = GREEDY, int sizeBorder = nDefaultBorder, int sizeExtraBorder = 0, int sizeTopExtra = 0, int sizeSecondary = 0); + CPane paneCtrl( CWnd* pCtrl, layOrientation orientation, layResizeMode modeResize = GREEDY, int sizeBorder = nDefaultBorder, int sizeExtraBorder = 0, int sizeTopExtra = 0, int sizeSecondary = 0); + + /** + * Creates a new PaneItem for an Control. If sizeX or sizeY are 0 + * and modeResize is ABSOLUTE will copy the current dimensions of + * the control to m_sizeX/Y. So the appearance does not change + * from the Dialog Editor. size*Min = -1 means: do not make smaller + * than in Dialog Template. + */ + CPaneBase item(UINT nID, layResizeMode modeResize = GREEDY, int sizeX =0, int sizeY =0, int sizeXMin =-1, int sizeYMin =-1); + CPaneBase item(CWnd* pWnd, layResizeMode modeResize = GREEDY, int sizeX =0, int sizeY =0, int sizeXMin =-1, int sizeYMin =-1); + + + /** + * Add a whitespace Item (paneNull) of variable size with + * a minimum size of 0 + */ + CPaneBase itemGrowing(layOrientation orientation); + + /** + * Add a whitespace Item (paneNull) with fixed size + */ + CPaneBase itemFixed(layOrientation orientation, int sizePrimary); + + /** + * Add a whitespace Item (paneNull) of fixed size based on the + * current layout (as in the dialog template). Based on the layout + * of the pane vertical or horizontal spacing is considered + * + * First argument is the left (top) item for a HORIZONTAL (VERTICAL) pane + */ + CPaneBase itemSpaceBetween( layOrientation orientation, CWnd* pWndFirst, CWnd* pWndSecond ); + CPaneBase itemSpaceBetween( layOrientation orientation, UINT nIDFirst, UINT nIDSecond ); + + /** + * Add a whitespace Item (paneNull) of fixed size based on the + * size of another item + */ + CPaneBase itemSpaceLike( layOrientation orientation, CWnd* pWnd ); + CPaneBase itemSpaceLike( layOrientation orientation, UINT nID ); + +protected: + /** + * This holds the root pane. Fill in InitDialog() + */ + CPane m_RootPane; + + /** + * Create a root pane + */ + CPane CreateRoot(layOrientation orientation, int sizeBorder = nDefaultBorder, int sizeExtraBorder = 0 ) + { + if(m_RootPane.IsValid()) + { + // free old root + m_RootPane = 0; + } + m_RootPane = new Pane( this, orientation, sizeBorder, sizeExtraBorder); + return m_RootPane; + } + + /** + * Key in Registry where to store Size + */ + CString m_strRegStore; + + /** + * Borders around root + */ + CSize m_sizeRootBorders; +}; + +inline ETSLayoutMgr::layResizeMode operator|(const ETSLayoutMgr::layResizeMode m1, + const ETSLayoutMgr::layResizeMode m2) + { return (ETSLayoutMgr::layResizeMode)( (DWORD)m1|(DWORD)m2); } + + +/** + * Base class for the Layout function. Derive your own class + * from this or derive it from CDialog and modify _all_ + * references to CDialog to ETSLayoutDialog + */ +class ETSGUI_EXT_CLASS ETSLayoutDialog : public ETSLayout::CBaseDialog, protected ETSLayoutMgr +{ +// Construction +public: + ETSLayoutDialog(UINT nID, CWnd* pParent = NULL, LPCTSTR strName = NULL, bool bGripper = true); // standard constructor + +// Dialog Data + //{{AFX_DATA(ETSLayoutDialog) + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(ETSLayoutDialog) + //}}AFX_VIRTUAL + +// Implementation +protected: + // Generated message map functions + //{{AFX_MSG(ETSLayoutDialog) + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI); + afx_msg BOOL OnEraseBkgnd(CDC* pDC); + virtual BOOL OnInitDialog(); + afx_msg void OnDestroy(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + + virtual CRect GetRect(); + + bool m_bGripper; + CStatusBar m_StatusBar; +}; + + +/** + * Base class for the Layout function. Derive your own class + * from this or derive it from CDialog and modify _all_ + * references to CFormView to ETSLayoutFormView + */ +class ETSGUI_EXT_CLASS ETSLayoutFormView : public ETSLayout::CBaseFormView, public ETSLayoutMgr +{ +// Construction + DECLARE_DYNAMIC(ETSLayoutFormView) +public: + ETSLayoutFormView(UINT nID, LPCTSTR strName = NULL); // standard constructor + virtual ~ETSLayoutFormView(); + +// virtual void UpdateLayout(); + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(ETSLayoutDialog) + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(ETSLayoutDialog) + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg BOOL OnEraseBkgnd(CDC* pDC); + afx_msg void OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + + +/** + * Base class for the Layout function. Derive your own class + * from this or derive it from CBCGDialogBar/CDialogBar and + * modify _all_ references to CBCGDialogBar/CDialogBar to + * ETSLayoutDialogBar + */ +class ETSGUI_EXT_CLASS ETSLayoutDialogBar : public ETSLayout::CBaseDialogBar, protected ETSLayoutMgr +{ +// Construction +public: +#ifdef CS_HELP + ETSLayoutDialogBar(UINT nID); +#else + ETSLayoutDialogBar(); +#endif + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(ETSLayoutDialogBar) + virtual CSize CalcDynamicLayout(int nLength, DWORD dwMode); + //}}AFX_VIRTUAL + + /** + * Override this to define Layout + */ + virtual BOOL Initialize() { return false; }; + virtual void UpdateLayout(); + +// Implementation +protected: + // Generated message map functions + //{{AFX_MSG(ETSLayoutDialogBar) + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnDestroy(); + afx_msg BOOL OnEraseBkgnd(CDC* pDC); + //}}AFX_MSG + LRESULT OnInitDialog(WPARAM, LPARAM); + DECLARE_MESSAGE_MAP() + + virtual CRect GetRect(); + bool m_bInitialized; +}; + + + +/************************************************** + ** ! the code is only tested for modal sheets ! ** + **************************************************/ + + +/** + * Resizable PropertySheet. Use this class standalone + * or as your base class (instead CProptertySheet) + */ +class ETSGUI_EXT_CLASS ETSLayoutPropertySheet : public CPropertySheet, protected ETSLayoutMgr +{ + DECLARE_DYNAMIC(ETSLayoutPropertySheet) + +// Construction +public: + ETSLayoutPropertySheet(UINT nIDCaption, CWnd *pParentWnd = NULL, UINT iSelectPage = 0, LPCTSTR strName=NULL, bool bGripper=true); + ETSLayoutPropertySheet(LPCTSTR pszCaption, CWnd *pParentWnd = NULL, UINT iSelectPage = 0, LPCTSTR strName=NULL, bool bGripper=true); + +// Operationen +public: + void SetAutoDestroy() { m_bAutoDestroy = true; } + void SetAutoDestroyPages() { m_bAutoDestroyPages = true; } + void ModelessWithButtons() { m_bModelessButtons = true; } +// Overrides + virtual void AddMainArea(CPane paneRoot, CPaneBase itemTab); + virtual void AddButtons(CPane paneBottom); + + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(ETSLayoutPropertySheet) + public: + virtual BOOL OnInitDialog(); + virtual void PostNcDestroy(); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~ETSLayoutPropertySheet(); + + // Generated message map functions +protected: + //{{AFX_MSG(ETSLayoutPropertySheet) + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI); + afx_msg void OnDestroy(); + afx_msg BOOL OnEraseBkgnd(CDC* pDC); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + + void Resize(int cx, int cy); + +friend class ETSLayoutPropertyPage; + + void Init(LPCTSTR strName, bool bGripper); + CRect m_rcStart; + CRect m_rcPage; + bool m_bGripper; + CStatusBar m_StatusBar; + CPaneBase m_ItemTab; + bool m_bAutoDestroy; + bool m_bAutoDestroyPages; + bool m_bModelessButtons; +}; + +/** + * Base class for the Layout function. Derive your own class + * from this or derive it from CPropertyPage and + * modify _all_ references to CPropertyPage to + * ETSLayoutPropertyPage + */ +class ETSGUI_EXT_CLASS ETSLayoutPropertyPage : public ETSLayout::CBasePropertyPage, protected ETSLayoutMgr +{ +friend class ETSLayoutPropertySheet; + + DECLARE_DYNCREATE(ETSLayoutPropertyPage) + +// Konstruktion +public: + ETSLayoutPropertyPage( ); + ETSLayoutPropertyPage( UINT nIDTemplate, UINT nIDCaption = 0 ); + ETSLayoutPropertyPage( LPCTSTR lpszTemplateName, UINT nIDCaption = 0 ); + + ~ETSLayoutPropertyPage(); + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(ETSLayoutPropertyPage) + public: + virtual BOOL OnSetActive(); + //}}AFX_VIRTUAL + +// Implementation +protected: + // Generated message map functions + //{{AFX_MSG(ETSLayoutPropertyPage) + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI); + virtual BOOL OnInitDialog(); + afx_msg BOOL OnEraseBkgnd(CDC* pDC); + afx_msg void OnWindowPosChanging( WINDOWPOS* lpwndpos ); + afx_msg void OnDestroy(); + afx_msg void OnWindowPosChanged(WINDOWPOS FAR* lpwndpos); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + + virtual CRect GetRect(); + bool m_bLockMove; + bool m_bResetBuddyOnNextTimeVisible; +}; + + + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(ETS_LAYOUTMGR_INCLUDED_) diff --git a/misc/windows/browse_for_directory.cpp b/misc/windows/browse_for_directory.cpp new file mode 100644 index 0000000..9f69549 --- /dev/null +++ b/misc/windows/browse_for_directory.cpp @@ -0,0 +1,33 @@ +#include "stdafx.h" +#include "browse_for_directory.h" + +int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData) +{ + if (uMsg == BFFM_INITIALIZED) + SendMessage(hwnd, BFFM_SETSELECTION, true, lpData); + return 0; +} + +int browse_for_directory(HWND hWnd, const std::string& title, std::string& directory) +{ + BROWSEINFO bi; + ZeroMemory(&bi, sizeof(BROWSEINFO)); + bi.hwndOwner = hWnd; + bi.lpszTitle = title.c_str(); + bi.ulFlags = BIF_NEWDIALOGSTYLE | BIF_RETURNONLYFSDIRS; + bi.lpfn = BrowseCallbackProc; + bi.lParam = reinterpret_cast(directory.c_str()); + ITEMIDLIST* idl = SHBrowseForFolder(&bi); + if (!idl) + return 1; + char path[MAX_PATH]; + if (!SHGetPathFromIDList(idl, path)) + *path = 0; + LPMALLOC lpm; + if (SHGetMalloc(&lpm) == NOERROR) + lpm->Free(idl); + if (!*path) + return 1; + directory = path; + return 0; +} \ No newline at end of file diff --git a/misc/windows/browse_for_directory.h b/misc/windows/browse_for_directory.h new file mode 100644 index 0000000..73f51b8 --- /dev/null +++ b/misc/windows/browse_for_directory.h @@ -0,0 +1,3 @@ +#pragma once + +int browse_for_directory(HWND, const std::string& title, std::string& directory); diff --git a/misc/windows/nt_service.cpp b/misc/windows/nt_service.cpp new file mode 100644 index 0000000..94fbb85 --- /dev/null +++ b/misc/windows/nt_service.cpp @@ -0,0 +1,66 @@ +#include "nt_service.h" + +#include + +int nt_service_install(const char* name) +{ + SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + if (!scm) + return 1; + char file_name[MAX_PATH]; + GetModuleFileName(NULL, file_name, MAX_PATH); + SC_HANDLE service = CreateService(scm, + name, + name, + SERVICE_ALL_ACCESS, + SERVICE_WIN32_OWN_PROCESS, + SERVICE_AUTO_START, + SERVICE_ERROR_NORMAL, + file_name, + NULL, + NULL, + NULL, + "NT AUTHORITY\\LocalService", + NULL); + if (!service) + { + service = CreateService(scm, + name, + name, + SERVICE_ALL_ACCESS, + SERVICE_WIN32_OWN_PROCESS, + SERVICE_AUTO_START, + SERVICE_ERROR_NORMAL, + file_name, + NULL, + NULL, + NULL, + NULL, + NULL); + } + if (!service) + { + CloseServiceHandle(scm); + return 1; + } + CloseServiceHandle(service); + CloseServiceHandle(scm); + return 0; +} + +int nt_service_uninstall(const char* name) +{ + SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + if (!scm) + return 1; + int result = 1; + SC_HANDLE service = OpenService(scm, name, DELETE); + if (service) + { + if (DeleteService(service)) + result = 0; + CloseServiceHandle(service); + } + CloseServiceHandle(scm); + return result; +} diff --git a/misc/windows/nt_service.h b/misc/windows/nt_service.h new file mode 100644 index 0000000..17f21d5 --- /dev/null +++ b/misc/windows/nt_service.h @@ -0,0 +1,4 @@ +#pragma once + +int nt_service_install(const char* name); +int nt_service_uninstall(const char* name); diff --git a/misc/xbt/.svn/all-wcprops b/misc/xbt/.svn/all-wcprops new file mode 100644 index 0000000..c7a4ec2 --- /dev/null +++ b/misc/xbt/.svn/all-wcprops @@ -0,0 +1,101 @@ +K 25 +svn:wc:ra_dav:version-url +V 37 +/svn/!svn/ver/2494/trunk/xbt/misc/xbt +END +sql_result.h +K 25 +svn:wc:ra_dav:version-url +V 50 +/svn/!svn/ver/2424/trunk/xbt/misc/xbt/sql_result.h +END +bstream.h +K 25 +svn:wc:ra_dav:version-url +V 47 +/svn/!svn/ver/2485/trunk/xbt/misc/xbt/bstream.h +END +xif_key.h +K 25 +svn:wc:ra_dav:version-url +V 47 +/svn/!svn/ver/2371/trunk/xbt/misc/xbt/xif_key.h +END +shared_data.h +K 25 +svn:wc:ra_dav:version-url +V 51 +/svn/!svn/ver/2461/trunk/xbt/misc/xbt/shared_data.h +END +database.h +K 25 +svn:wc:ra_dav:version-url +V 48 +/svn/!svn/ver/2439/trunk/xbt/misc/xbt/database.h +END +data_ref.h +K 25 +svn:wc:ra_dav:version-url +V 48 +/svn/!svn/ver/2453/trunk/xbt/misc/xbt/data_ref.h +END +find_ptr.h +K 25 +svn:wc:ra_dav:version-url +V 48 +/svn/!svn/ver/2486/trunk/xbt/misc/xbt/find_ptr.h +END +xcc_z.h +K 25 +svn:wc:ra_dav:version-url +V 45 +/svn/!svn/ver/2233/trunk/xbt/misc/xbt/xcc_z.h +END +xif_key_r.h +K 25 +svn:wc:ra_dav:version-url +V 49 +/svn/!svn/ver/2246/trunk/xbt/misc/xbt/xif_key_r.h +END +bt_misc.h +K 25 +svn:wc:ra_dav:version-url +V 47 +/svn/!svn/ver/2494/trunk/xbt/misc/xbt/bt_misc.h +END +to_array.h +K 25 +svn:wc:ra_dav:version-url +V 48 +/svn/!svn/ver/2287/trunk/xbt/misc/xbt/to_array.h +END +cfile.h +K 25 +svn:wc:ra_dav:version-url +V 45 +/svn/!svn/ver/2466/trunk/xbt/misc/xbt/cfile.h +END +xif_value.h +K 25 +svn:wc:ra_dav:version-url +V 49 +/svn/!svn/ver/2246/trunk/xbt/misc/xbt/xif_value.h +END +profiler.h +K 25 +svn:wc:ra_dav:version-url +V 48 +/svn/!svn/ver/2493/trunk/xbt/misc/xbt/profiler.h +END +sql_query.h +K 25 +svn:wc:ra_dav:version-url +V 49 +/svn/!svn/ver/2390/trunk/xbt/misc/xbt/sql_query.h +END +virtual_binary.h +K 25 +svn:wc:ra_dav:version-url +V 54 +/svn/!svn/ver/2244/trunk/xbt/misc/xbt/virtual_binary.h +END diff --git a/misc/xbt/.svn/entries b/misc/xbt/.svn/entries new file mode 100644 index 0000000..e2bf8c5 --- /dev/null +++ b/misc/xbt/.svn/entries @@ -0,0 +1,572 @@ +10 + +dir +2494 +http://xbt.googlecode.com/svn/trunk/xbt/misc/xbt +http://xbt.googlecode.com/svn + + + +2015-06-12T15:14:35.736728Z +2494 +olafvdspek + + + + + + + + + + + + + + +a4ef9278-c6d2-effe-6dce-f9b4ebbbbc1a + +shared_data.h +file + + + + +2015-07-14T06:50:41.176095Z +a234eba8cd48a2a7abc1eac8070bbd05 +2014-12-05T11:11:05.957956Z +2461 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +2158 + +database.h +file + + + + +2015-07-14T06:50:41.176095Z +aa9532c1e2a594f176c455be6572368f +2014-08-29T23:07:52.143958Z +2439 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +897 + +data_ref.h +file + + + + +2015-07-14T06:50:41.180095Z +c4e7e41ed8a68c7ea4c7c811098b171f +2014-11-19T15:34:48.522505Z +2453 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +3891 + +find_ptr.h +file + + + + +2015-07-14T06:50:41.176095Z +ad5f8be9da4301837cd2b6a77456a11a +2015-05-16T14:22:54.914248Z +2486 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +1914 + +xcc_z.h +file + + + + +2015-07-14T06:50:41.180095Z +e09a159d1d178117d0bac4139352654e +2011-10-18T18:10:23.294255Z +2233 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +150 + +xif_key_r.h +file + + + + +2015-07-14T06:50:41.180095Z +31ffb70e00c24108205eebf0aeed780a +2011-10-25T13:20:31.559824Z +2246 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +1593 + +bt_misc.h +file + + + + +2015-07-14T06:50:41.180095Z +e1fbae5f7285d67ee4d0ea86c8ae6fdd +2015-06-12T15:14:35.736728Z +2494 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +1995 + +to_array.h +file + + + + +2015-07-14T06:50:41.180095Z +701243b7fbe7fca279534ca6e3db7787 +2012-04-28T00:29:31.234683Z +2287 +olafvdspek + + + + + + + + + + + + + + + + + + + + + +238 + +cfile.h +file + + + + +2015-07-14T06:50:41.180095Z +80fc752b31ea762ebcaad1bdfbf73c9e +2014-12-05T12:40:46.040155Z +2466 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +1730 + +xif_value.h +file + + + + +2015-07-14T06:50:41.180095Z +dbd020428d54e73485282e3174c44768 +2011-10-25T13:20:31.559824Z +2246 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +1856 + +profiler.h +file + + + + +2015-07-14T06:50:41.180095Z +c8247ea8eafebc301bf3169d95fccd2d +2015-06-07T16:10:04.946949Z +2493 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +598 + +sql_query.h +file + + + + +2015-07-14T06:50:41.180095Z +d117465254912266d6b17ef8f5016f92 +2013-05-18T16:56:50.494148Z +2390 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +697 + +virtual_binary.h +file + + + + +2015-07-14T06:50:41.176095Z +5eb28e72440b3309b72709aec1847122 +2011-10-25T12:45:47.652726Z +2244 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +2064 + +sql_result.h +file + + + + +2015-07-14T06:50:41.176095Z +e955602feb70fa897c6944a18f786ce5 +2014-03-29T16:17:41.066529Z +2424 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +1810 + +bstream.h +file + + + + +2015-07-14T06:50:41.176095Z +639e65300eaddd82d4c5676d68c8a545 +2015-05-07T16:03:55.534677Z +2485 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +1454 + +xif_key.h +file + + + + +2015-07-14T06:50:41.176095Z +30b76e4793745e61b6db06c9e2af2a5c +2012-11-10T16:39:45.863522Z +2371 +olafvdspek +has-props + + + + + + + + + + + + + + + + + + + + +4300 + diff --git a/misc/xbt/.svn/prop-base/bstream.h.svn-base b/misc/xbt/.svn/prop-base/bstream.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/xbt/.svn/prop-base/bstream.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/xbt/.svn/prop-base/bt_misc.h.svn-base b/misc/xbt/.svn/prop-base/bt_misc.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/xbt/.svn/prop-base/bt_misc.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/xbt/.svn/prop-base/cfile.h.svn-base b/misc/xbt/.svn/prop-base/cfile.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/xbt/.svn/prop-base/cfile.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/xbt/.svn/prop-base/data_ref.h.svn-base b/misc/xbt/.svn/prop-base/data_ref.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/xbt/.svn/prop-base/data_ref.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/xbt/.svn/prop-base/database.h.svn-base b/misc/xbt/.svn/prop-base/database.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/xbt/.svn/prop-base/database.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/xbt/.svn/prop-base/find_ptr.h.svn-base b/misc/xbt/.svn/prop-base/find_ptr.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/xbt/.svn/prop-base/find_ptr.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/xbt/.svn/prop-base/profiler.h.svn-base b/misc/xbt/.svn/prop-base/profiler.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/xbt/.svn/prop-base/profiler.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/xbt/.svn/prop-base/shared_data.h.svn-base b/misc/xbt/.svn/prop-base/shared_data.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/xbt/.svn/prop-base/shared_data.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/xbt/.svn/prop-base/sql_query.h.svn-base b/misc/xbt/.svn/prop-base/sql_query.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/xbt/.svn/prop-base/sql_query.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/xbt/.svn/prop-base/sql_result.h.svn-base b/misc/xbt/.svn/prop-base/sql_result.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/xbt/.svn/prop-base/sql_result.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/xbt/.svn/prop-base/virtual_binary.h.svn-base b/misc/xbt/.svn/prop-base/virtual_binary.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/xbt/.svn/prop-base/virtual_binary.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/xbt/.svn/prop-base/xcc_z.h.svn-base b/misc/xbt/.svn/prop-base/xcc_z.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/xbt/.svn/prop-base/xcc_z.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/xbt/.svn/prop-base/xif_key.h.svn-base b/misc/xbt/.svn/prop-base/xif_key.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/xbt/.svn/prop-base/xif_key.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/xbt/.svn/prop-base/xif_key_r.h.svn-base b/misc/xbt/.svn/prop-base/xif_key_r.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/xbt/.svn/prop-base/xif_key_r.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/xbt/.svn/prop-base/xif_value.h.svn-base b/misc/xbt/.svn/prop-base/xif_value.h.svn-base new file mode 100644 index 0000000..abd5821 --- /dev/null +++ b/misc/xbt/.svn/prop-base/xif_value.h.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:eol-style +V 2 +LF +END diff --git a/misc/xbt/.svn/text-base/bstream.h.svn-base b/misc/xbt/.svn/text-base/bstream.h.svn-base new file mode 100644 index 0000000..78c3d64 --- /dev/null +++ b/misc/xbt/.svn/text-base/bstream.h.svn-base @@ -0,0 +1,116 @@ +#pragma once + +#include +#include + +class bstream_handle +{ +public: + typedef int handle_type; + + bstream_handle() = default; + + explicit bstream_handle(handle_type f) : f_(f) + { + } + + handle_type get() const + { + return f_; + } + + bool is_open() const + { + return get() != -1; + } + + explicit operator bool() const + { + return is_open(); + } + + void reset(handle_type f = -1) + { + f_ = f; + } + + ptrdiff_t read(void* d, size_t cb_d) + { + return ::read(get(), d, cb_d); + } + + ptrdiff_t write(const void* d, size_t cb_d) + { + return ::write(get(), d, cb_d); + } +private: + handle_type f_ = -1; +}; + +class bstream +{ +public: + typedef int handle_type; + + bstream() = default; + + bstream(bstream&& v) : f_(v.release()) + { + } + + explicit bstream(handle_type f) : f_(f) + { + } + + explicit bstream(const char* name, int mode) : f_(open(name, mode)) + { + } + + explicit bstream(const std::string& name, int mode) : f_(open(name.c_str(), mode)) + { + } + + ~bstream() + { + close(); + } + + handle_type release() + { + handle_type f = f_.get(); + f_.reset(); + return f; + } + + handle_type get() const + { + return f_.get(); + } + + bool is_open() const + { + return f_.is_open(); + } + + explicit operator bool() const + { + return is_open(); + } + + ptrdiff_t read(void* d, size_t cb_d) + { + return f_.read(d, cb_d); + } + + ptrdiff_t write(const void* d, size_t cb_d) + { + return f_.write(d, cb_d); + } + + int close() + { + return is_open() ? ::close(release()) : 0; + } +private: + bstream_handle f_; +}; diff --git a/misc/xbt/.svn/text-base/bt_misc.h.svn-base b/misc/xbt/.svn/text-base/bt_misc.h.svn-base new file mode 100644 index 0000000..5d973d1 --- /dev/null +++ b/misc/xbt/.svn/text-base/bt_misc.h.svn-base @@ -0,0 +1,98 @@ +#pragma once + +#include +#include + +std::string b2a(long long v, const char* postfix = NULL); +std::string backward_slashes(std::string); +std::string duration2a(float); +std::string escape_string(const std::string&); +std::string forward_slashes(std::string); +std::string generate_random_string(int); +std::string get_env(const std::string&); +int hms2i(int h, int m, int s); +bool is_private_ipa(int a); +int merkle_tree_size(int v); +std::string mk_sname(std::string); +std::string n(long long); +std::string n2a(long long v, const char* postfix = NULL); +std::string native_slashes(const std::string&); +std::string hex_decode(str_ref); +std::string hex_encode(int l, int v); +std::string hex_encode(data_ref); +std::string js_encode(str_ref); +std::string peer_id2a(const std::string&); +std::string time2a(time_t); +std::string uri_decode(str_ref); +std::string uri_encode(str_ref); +void xbt_syslog(const std::string&); +std::string xbt_version2a(int); + +inline long long htonll(long long v) +{ + const unsigned char* a = reinterpret_cast(&v); + long long b = a[0] << 24 | a[1] << 16 | a[2] << 8 | a[3]; + return b << 32 | static_cast(a[4]) << 24 | a[5] << 16 | a[6] << 8 | a[7]; +} + +inline long long ntohll(long long v) +{ + return htonll(v); +} + +enum +{ + hs_name_size = 0, + hs_name = 1, + hs_reserved = 20, + hs_info_hash = 28, + hs_size = 48, +}; + +enum +{ + uta_connect, + uta_announce, + uta_scrape, + uta_error, +}; + +enum +{ + uti_connection_id = 0, + uti_action = 8, + uti_transaction_id = 12, + uti_size = 16, + + utic_size = 16, + + utia_info_hash = 16, + utia_peer_id = 36, + utia_downloaded = 56, + utia_left = 64, + utia_uploaded = 72, + utia_event = 80, + utia_ipa = 84, + utia_key = 88, + utia_num_want = 92, + utia_port = 96, + utia_size = 98, + + utis_size = 16, + + uto_action = 0, + uto_transaction_id = 4, + uto_size = 8, + + utoc_connection_id = 8, + utoc_size = 16, + + utoa_interval = 8, + utoa_leechers = 12, + utoa_seeders = 16, + utoa_size = 20, + + utos_size = 8, + + utoe_size = 8, +}; diff --git a/misc/xbt/.svn/text-base/cfile.h.svn-base b/misc/xbt/.svn/text-base/cfile.h.svn-base new file mode 100644 index 0000000..961e67c --- /dev/null +++ b/misc/xbt/.svn/text-base/cfile.h.svn-base @@ -0,0 +1,132 @@ +#pragma once + +#include +#include +#include + +inline size_t read(FILE* f, void* d, size_t cb_d) +{ + return fread(d, 1, cb_d, f); +} + +inline size_t write(FILE* f, const void* d, size_t cb_d) +{ + return fwrite(d, 1, cb_d, f); +} + +class cfile_handle +{ +public: + typedef FILE* handle_type; + + cfile_handle() = default; + + explicit cfile_handle(handle_type f) : f_(f) + { + } + + handle_type get() const + { + return f_; + } + + bool is_open() const + { + return !!get(); + } + + explicit operator bool() const + { + return is_open(); + } + + void reset(handle_type f = NULL) + { + f_ = f; + } + + size_t read(void* d, size_t cb_d) + { + return fread(d, 1, cb_d, get()); + } + + size_t write(const void* d, size_t cb_d) + { + return fwrite(d, 1, cb_d, get()); + } +private: + handle_type f_ = NULL; +}; + +class cfile : boost::noncopyable +{ +public: + typedef FILE* handle_type; + + cfile() = default; + + cfile(cfile&& v) : f_(v.release()) + { + } + + explicit cfile(handle_type f) : f_(f) + { + } + + explicit cfile(const char* name, const char* mode) : f_(fopen(name, mode)) + { + } + + explicit cfile(const std::string& name, const char* mode) : f_(fopen(name.c_str(), mode)) + { + } + + ~cfile() + { + close(); + } + + handle_type release() + { + handle_type f = f_.get(); + f_.reset(); + return f; + } + + handle_type get() const + { + return f_.get(); + } + + operator handle_type() const + { + return get(); + } + + bool is_open() const + { + return f_.is_open(); + } + + explicit operator bool() const + { + return is_open(); + } + + size_t read(void* d, size_t cb_d) + { + return f_.read(d, cb_d); + } + + size_t write(const void* d, size_t cb_d) + { + return f_.write(d, cb_d); + } + + int close() + { + return is_open() ? fclose(release()) : 0; + } +private: + cfile_handle f_; +}; diff --git a/misc/xbt/.svn/text-base/data_ref.h.svn-base b/misc/xbt/.svn/text-base/data_ref.h.svn-base new file mode 100644 index 0000000..e4a7edc --- /dev/null +++ b/misc/xbt/.svn/text-base/data_ref.h.svn-base @@ -0,0 +1,225 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +template +class data_ref_base : public boost::iterator_range +{ +public: + data_ref_base() + { + clear(); + } + + template + data_ref_base(const V& v, typename boost::enable_if >::type* = 0) + { + if (v.end() != v.begin()) + assign(&*v.begin(), v.end() - v.begin() + &*v.begin()); + else + clear(); + } + + template + data_ref_base(V& v, typename boost::enable_if >::type* = 0) + { + if (v.end() != v.begin()) + assign(&*v.begin(), v.end() - v.begin() + &*v.begin()); + else + clear(); + } + + explicit data_ref_base(const char* v) + { + if (v) + assign(v, strlen(v)); + else + clear(); + } + + data_ref_base(U begin, U end) + { + assign(begin, end); + } + + data_ref_base(U begin, size_t size) + { + assign(begin, size); + } + + void assign(U begin, U end) + { + static_cast(*this) = base_t(reinterpret_cast(begin), reinterpret_cast(end)); + } + + void assign(U begin, size_t size) + { + assign(begin, reinterpret_cast(begin) + size); + } + + void set_begin(U v) + { + assign(v, base_t::end()); + } + + void set_end(U v) + { + assign(base_t::begin(), v); + } + + void clear() + { + assign(T(NULL), T(NULL)); + } + + T data() const + { + return base_t::begin(); + } + + template + data_ref_base find0(V v) const + { + data_ref_base t = *this; + while (!t.empty() && t.front() != v) + t.pop_front(); + return t; + } + + float f() const + { + return to_float(*this); + } + + long long i() const + { + return to_int(*this); + } + + const std::string s() const + { + return std::string(reinterpret_cast(data()), base_t::size()); + } + + data_ref_base substr(size_t pos) + { + return data_ref_base(base_t::begin() + pos, base_t::size() - pos); + } + + data_ref_base substr(size_t pos, size_t sz) + { + return data_ref_base(base_t::begin() + pos, sz); + } +private: + typedef boost::iterator_range base_t; +}; + +typedef data_ref_base data_ref; +typedef data_ref_base mutable_data_ref; +typedef data_ref_base str_ref; +typedef data_ref_base mutable_str_ref; + +bool operator==(str_ref a, const char* b); + +inline size_t memcpy(void* d, data_ref s) +{ + memcpy(d, s.data(), s.size()); + return s.size(); +} + +inline size_t memcpy(mutable_data_ref d, data_ref s) +{ + assert(d.size() >= s.size()); + memcpy(d.data(), s.data(), s.size()); + return s.size(); +} + +inline int eat(str_ref& s, char v) +{ + if (!s || s.front() != v) + return 1; + s.pop_front(); + return 0; +} + +inline str_ref read_until(str_ref& is, char sep) +{ + const char* a = is.begin(); + const char* b = std::find(is.begin(), is.end(), sep); + is.set_begin(b == is.end() ? b : b + 1); + return str_ref(a, b); +} + +inline float to_float(data_ref v) +{ + if (v.empty()) + return 0; + try + { + return boost::lexical_cast(v); + } + catch (boost::bad_lexical_cast&) + { + } + return 0; +} + +template +int try_parse(T& d, str_ref s) +{ + if (!s) + return 1; + bool neg = !eat(s, '-'); + d = 0; + for (; s; s.pop_front()) + { + char c = s.front(); + if (c < '0' || c > '9') + return 1; + d = neg ? 10 * d - (c - '0') : 10 * d + (c - '0'); + } + return 0; +} + +template +T parse(str_ref s) +{ + T d; + return try_parse(d, s) ? 0 : d; +} + +template +void parse(T& d, str_ref s) +{ + if (try_parse(d, s)) + d = 0; +} + +inline long long to_int(str_ref v) +{ + return parse(v); + if (v.empty()) + return 0; + if (!*v.end()) + return atoll(v.data()); + try + { + return boost::lexical_cast(v); + } + catch (boost::bad_lexical_cast&) + { + } + return 0; +} + +inline const std::string to_string(str_ref v) +{ + return std::string(v.data(), v.size()); +} diff --git a/misc/xbt/.svn/text-base/database.h.svn-base b/misc/xbt/.svn/text-base/database.h.svn-base new file mode 100644 index 0000000..bc6fb65 --- /dev/null +++ b/misc/xbt/.svn/text-base/database.h.svn-base @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include "sql_result.h" + +class bad_query : public std::runtime_error +{ +public: + bad_query(const std::string& s) : runtime_error(s) + { + } +}; + +class Cdatabase : boost::noncopyable +{ +public: + void open(const std::string& host, const std::string& user, const std::string& password, const std::string& database, bool echo_errors = false); + const std::string& name(const std::string&) const; + Csql_result query(const std::string&); + int query_nothrow(const std::string&); + void set_name(const std::string&, std::string); + void set_query_log(std::ostream*); + int affected_rows(); + int insert_id(); + int select_db(const std::string&); + void close(); + Cdatabase(); + ~Cdatabase(); + + operator MYSQL*() + { + return &m_handle; + } +private: + bool m_echo_errors; + MYSQL m_handle; + std::map m_names; + std::ostream* m_query_log = NULL; +}; diff --git a/misc/xbt/.svn/text-base/find_ptr.h.svn-base b/misc/xbt/.svn/text-base/find_ptr.h.svn-base new file mode 100644 index 0000000..638a7b5 --- /dev/null +++ b/misc/xbt/.svn/text-base/find_ptr.h.svn-base @@ -0,0 +1,68 @@ +#pragma once + +template +const typename T::value_type::second_type* find_ptr(const T& c, const U& v) +{ + typename T::const_iterator i = c.find(v); + return i == c.end() ? NULL : &i->second; +} + +template +typename T::value_type::second_type* find_ptr(T& c, const U& v) +{ + typename T::iterator i = c.find(v); + return i == c.end() ? NULL : &i->second; +} + +template +const typename T::value_type::second_type& find_ptr2(const T& c, const U& v) +{ + static typename T::value_type::second_type z = typename T::value_type::second_type(); + typename T::const_iterator i = c.find(v); + return i == c.end() ? z : i->second; +} + +template +typename T::value_type::second_type& find_ptr2(T& c, const U& v) +{ + static typename T::value_type::second_type z = typename T::value_type::second_type(); + typename T::iterator i = c.find(v); + return i == c.end() ? z : i->second; +} + +template +const typename T::value_type* find_ptr0(const T& c, const U& v) +{ + typename T::const_iterator i = c.find(v); + return i == c.end() ? NULL : &*i; +} + +template +typename T::value_type::second_type& find_ref(T& c, const U& v) +{ + typename T::iterator i = c.find(v); + assert(i != c.end()); + return i->second; +} + +template +const typename T::value_type::second_type& find_ref(const T& c, const U& v) +{ + typename T::const_iterator i = c.find(v); + assert(i != c.end()); + return i->second; +} + +template +const typename T::value_type::second_type& find_ref(const T& c, const U& v, const typename T::value_type::second_type& z) +{ + typename T::const_iterator i = c.find(v); + return i == c.end() ? z : i->second; +} + +template +typename T::value_type::second_type& find_ref(T& c, const U& v, typename T::value_type::second_type& z) +{ + typename T::iterator i = c.find(v); + return i == c.end() ? z : i->second; +} diff --git a/misc/xbt/.svn/text-base/profiler.h.svn-base b/misc/xbt/.svn/text-base/profiler.h.svn-base new file mode 100644 index 0000000..6b949d1 --- /dev/null +++ b/misc/xbt/.svn/text-base/profiler.h.svn-base @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include + +class xbt_profiler +{ +public: + xbt_profiler(int limit, const std::string& text) + { + limit_ = limit; + t0_ = boost::posix_time::microsec_clock::universal_time(); + text_ = text; + } + + ~xbt_profiler() + { + auto ms = (boost::posix_time::microsec_clock::universal_time() - t0_).total_milliseconds(); + if (ms < limit_) + return; + std::stringstream s; + s << ms << " ms: " << text_; + xbt_syslog(s.str()); + } +private: + int limit_; + boost::posix_time::ptime t0_; + std::string text_; +}; diff --git a/misc/xbt/.svn/text-base/shared_data.h.svn-base b/misc/xbt/.svn/text-base/shared_data.h.svn-base new file mode 100644 index 0000000..cbdbc36 --- /dev/null +++ b/misc/xbt/.svn/text-base/shared_data.h.svn-base @@ -0,0 +1,112 @@ +#pragma once + +#include +#include +// #include +// #include +#include +#include +#include +#include +#include +#include +#include + +template +class shared_array2 : public boost::iterator_range +{ +public: + shared_array2() + { + } + + explicit shared_array2(size_t sz) + { + if (!sz) + return; + std::shared_ptr n(new T[sz], boost::checked_array_deleter()); + static_cast(*this) = base_t(n.get(), n.get() + sz); + n_ = n; + } + + template + shared_array2(const shared_array2& v) : // , typename boost::enable_if>::type* = 0) : + base_t(v.data(), v.data() + v.size()), + n_(v.n()) + { + } + + shared_array2(T* b, T* e, std::shared_ptr const& n) : + base_t(b, e), + n_(n) + { + } + + shared_array2(T* b, size_t sz, std::shared_ptr const& n) : + base_t(b, b + sz), + n_(n) + { + } + + void clear() + { + *this = shared_array2(); + } + + T* data() const + { + return base_t::begin(); + } + + std::shared_ptr const& n() const + { + return n_; + } + + shared_array2 substr(size_t ofs, size_t sz) const + { + return shared_array2(data() + ofs, sz, n()); + } +private: + typedef boost::iterator_range base_t; + + std::shared_ptr n_; +}; + +typedef shared_array2 shared_data; + +inline shared_data make_shared_data(data_ref v) +{ + shared_data d(v.size()); + memcpy(d.data(), v); + return d; +} + +inline shared_data make_shared_data(const void* d, size_t sz) +{ + return make_shared_data(data_ref(d, sz)); +} + +inline shared_data file_get(FILE* f) +{ + if (!f) + return shared_data(); + fseek(f, 0, SEEK_SET); + struct stat b; + if (fstat(fileno(f), &b)) + return shared_data(); + shared_data d(b.st_size); + return read(f, d.data(), b.st_size) == b.st_size ? d : shared_data(); +} + +inline shared_data file_get(const std::string& fname) +{ + cfile f(fname, "rb"); + return file_get(f); +} + +inline int file_put(const std::string& fname, data_ref v) +{ + cfile f(fname, "wb"); + return !f || f.write(v.data(), v.size()) != v.size(); +} diff --git a/misc/xbt/.svn/text-base/sql_query.h.svn-base b/misc/xbt/.svn/text-base/sql_query.h.svn-base new file mode 100644 index 0000000..29821fb --- /dev/null +++ b/misc/xbt/.svn/text-base/sql_query.h.svn-base @@ -0,0 +1,39 @@ +#pragma once + +#include + +class Cdatabase; +class Csql_result; + +class Csql_query +{ +public: + Csql_result execute() const; + int execute_nothrow() const; + std::string read() const; + void operator=(std::string); + void operator+=(const std::string&); + Csql_query& p_name(const std::string&); + Csql_query& p_raw(data_ref); + Csql_query& operator()(long long); + Csql_query& operator()(str_ref); + Csql_query(Cdatabase&, std::string = ""); + +#if 1 + Csql_query& p(long long v) + { + return (*this)(v); + } + + Csql_query& p(str_ref v) + { + return (*this)(v); + } +#endif +private: + std::string replace_names(const std::string&) const; + + Cdatabase& m_database; + std::string m_in; + std::string m_out; +}; diff --git a/misc/xbt/.svn/text-base/sql_result.h.svn-base b/misc/xbt/.svn/text-base/sql_result.h.svn-base new file mode 100644 index 0000000..79f8176 --- /dev/null +++ b/misc/xbt/.svn/text-base/sql_result.h.svn-base @@ -0,0 +1,103 @@ +#pragma once + +#include +#include +#include +#ifdef _MSC_VER +#include +#endif +#include + +class Csql_row +{ +public: + Csql_row() = default; + + Csql_row(MYSQL_ROW data, unsigned long* sizes, std::shared_ptr source) + { + m_data = data; + m_sizes = sizes; + m_source = std::move(source); + } + + operator const void*() const + { + return m_data; + } + + str_ref operator[](size_t i) const + { + return m_data ? str_ref(m_data[i], m_sizes[i]) : str_ref(); + } +private: + MYSQL_ROW m_data; + unsigned long* m_sizes; + std::shared_ptr m_source; +}; + +class Csql_result +{ +public: + class iterator + { + public: + iterator() = default; + iterator(Csql_result& v) : res_(&v), row_(mysql_fetch_row(res_->h())) { } + bool operator!=(iterator) { return row_; } + Csql_row operator*() { return Csql_row(row_, mysql_fetch_lengths(res_->h()), res_->m_source); } + void operator++() { row_ = mysql_fetch_row(res_->h()); } + private: + Csql_result* res_ = NULL; + MYSQL_ROW row_; + }; + + Csql_row fetch_row() const + { + MYSQL_ROW data = mysql_fetch_row(h()); + return Csql_row(data, mysql_fetch_lengths(h()), m_source); + } + + str_ref fetch_value() const + { + return fetch_row()[0]; + } + + long long fetch_int() const + { + return fetch_value().i(); + } + + Csql_result(MYSQL_RES* h) : m_source(h, mysql_free_result) + { + } + + operator const void*() const + { + return size() ? this : NULL; + } + + int c_fields() const + { + return mysql_num_fields(h()); + } + + int size() const + { + return mysql_num_rows(h()); + } + + void data_seek(int i) + { + mysql_data_seek(h(), i); + } + + iterator begin() { return iterator(*this); } + iterator end() { return iterator(); } +private: + MYSQL_RES* h() const + { + return m_source.get(); + } + + std::shared_ptr m_source; +}; diff --git a/misc/xbt/.svn/text-base/to_array.h.svn-base b/misc/xbt/.svn/text-base/to_array.h.svn-base new file mode 100644 index 0000000..bdf2989 --- /dev/null +++ b/misc/xbt/.svn/text-base/to_array.h.svn-base @@ -0,0 +1,13 @@ +#pragma once + +template +std::array to_array(const V& v) +{ + assert(v.size() == N); + std::array d; + if (v.size() == d.size()) + std::copy(v.begin(), v.end(), d.data()); + else + d.fill(0); + return d; +} diff --git a/misc/xbt/.svn/text-base/virtual_binary.h.svn-base b/misc/xbt/.svn/text-base/virtual_binary.h.svn-base new file mode 100644 index 0000000..f91e5bd --- /dev/null +++ b/misc/xbt/.svn/text-base/virtual_binary.h.svn-base @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +class Cvirtual_binary_source: boost::noncopyable +{ +public: + Cvirtual_binary_source(size_t v) + { + m_range.assign(new char[v], v); + } + + ~Cvirtual_binary_source() + { + delete[] m_range.begin(); + } + + unsigned char* begin() const + { + return m_range.begin(); + } + + unsigned char* end() const + { + return m_range.end(); + } + + void resize(size_t v) + { + assert(v <= m_range.size()); + m_range.assign(m_range.begin(), v); + } +private: + mutable_data_ref m_range; +}; + +class Cvirtual_binary +{ +public: + void assign(data_ref); + unsigned char* write_start(size_t cb_d); + + Cvirtual_binary() + { + } + + explicit Cvirtual_binary(size_t v) + { + assign(v); + } + + explicit Cvirtual_binary(data_ref v) + { + assign(v); + } + + void assign(size_t v) + { + assign(data_ref(NULL, v)); + } + + const unsigned char* begin() const + { + return range().begin(); + } + + unsigned char* mutable_begin() + { + return mutable_range().begin(); + } + + const unsigned char* data() const + { + return range().begin(); + } + + unsigned char* data_edit() + { + return mutable_range().begin(); + } + + const unsigned char* end() const + { + return range().end(); + } + + unsigned char* mutable_end() + { + return mutable_range().end(); + } + + data_ref range() const + { + return m_source ? *m_source : data_ref(); + } + + mutable_data_ref mutable_range() + { + if (!m_source) + return mutable_data_ref(); + if (!m_source.unique()) + assign(range()); + return *m_source; + } + + void clear() + { + m_source.reset(); + } + + bool empty() const + { + return range().empty(); + } + + size_t size() const + { + return range().size(); + } + + void resize(size_t v) + { + if (!m_source) + write_start(v); + mutable_range(); + m_source->resize(v); + } + + operator const unsigned char*() const + { + return data(); + } + + operator data_ref() const + { + return range(); + } + + operator mutable_data_ref() + { + return mutable_range(); + } +private: + boost::shared_ptr m_source; +}; diff --git a/misc/xbt/.svn/text-base/xcc_z.h.svn-base b/misc/xbt/.svn/text-base/xcc_z.h.svn-base new file mode 100644 index 0000000..43a7200 --- /dev/null +++ b/misc/xbt/.svn/text-base/xcc_z.h.svn-base @@ -0,0 +1,10 @@ +#pragma once + +#include + +namespace xcc_z +{ + shared_data gunzip(data_ref); + shared_data gzip(data_ref); + void gzip_out(data_ref); +} diff --git a/misc/xbt/.svn/text-base/xif_key.h.svn-base b/misc/xbt/.svn/text-base/xif_key.h.svn-base new file mode 100644 index 0000000..ee7b5a3 --- /dev/null +++ b/misc/xbt/.svn/text-base/xif_key.h.svn-base @@ -0,0 +1,249 @@ +#pragma once + +#include +#include +#include +#include + +const static int file_id = 0x1a464958; // *reinterpret_cast("XIF\x1a"); +const static int file_version_old = 0; +const static int file_version_new = 1; +const static int file_version_fast = 2; + +struct t_xif_header_old +{ + int id; + int version; + int size_uncompressed; +}; + +struct t_xif_header_fast +{ + int id; + int version; + int size_uncompressed; + int size_compressed; + int size_external; +}; + +class Cxif_key; + +typedef std::map t_xif_key_map; +typedef std::map t_xif_value_map; + +class Cxif_key +{ +public: + Cxif_key(): + m_keys(*new t_xif_key_map) + { + } + + ~Cxif_key() + { + delete &m_keys; + } + + Cxif_key(const Cxif_key& v): + m_keys(*new t_xif_key_map) + { + m_keys = v.m_keys; + m_values = v.m_values; + } + + explicit Cxif_key(const shared_data& v): + m_keys(*new t_xif_key_map) + { + load_key(v); + } + + const Cxif_key& operator=(const Cxif_key& v) + { + m_keys = v.m_keys; + m_values = v.m_values; + return *this; + } + + Cxif_key& set_key(int id) + { + m_keys[id] = Cxif_key(); + return m_keys[id]; + } + + const Cxif_key& open_key_read(int id) const + { + return m_keys.find(id)->second; + } + + Cxif_key& open_key_edit(int id) + { + return m_keys[id]; + } + + Cxif_key& open_key_write() + { + return open_key_write(m_keys.empty() ? 0 : m_keys.rbegin()->first + 1); + } + + Cxif_key& open_key_write(int id) + { + m_keys[id] = Cxif_key(); + return m_keys[id]; + } + + const Cxif_value& open_value_read(int id) const + { + return m_values.find(id)->second; + } + + Cxif_value& open_value_edit(int id) + { + return m_values[id]; + } + + Cxif_value& open_value_write(int id) + { + m_values[id] = Cxif_value(); + return m_values[id]; + } + + Cxif_value& set_value(int id) + { + m_values[id] = Cxif_value(); + return m_values[id]; + } + + void set_value_bin(int id, int v) + { + m_values[id] = Cxif_value(vt_bin32, v); + } + + void set_value_binary(int id, const shared_data& v) + { + m_values[id] = Cxif_value(v); + } + + void set_value_float(int id, float v) + { + m_values[id] = Cxif_value(v); + } + + void set_value_int(int id, int v) + { + m_values[id] = Cxif_value(vt_int32, v); + } + + void set_value_string(int id, const std::string& v) + { + m_values[id] = Cxif_value(v); + } + + void set_value_int64(int id, long long v) + { + set_value_binary(id, make_shared_data(&v, 8)); + } + + const Cxif_key& get_key(int id) const + { + static Cxif_key z; + t_xif_key_map::iterator i = m_keys.find(id); + return i == m_keys.end() ? z : i->second; + } + + const Cxif_value& get_value(int id) const + { + static Cxif_value z; + t_xif_value_map::const_iterator i = m_values.find(id); + return i == m_values.end() ? z : i->second; + } + + float get_value_float(int id) const + { + return get_value(id).get_float(); + } + + float get_value_float(int id, float v) const + { + return get_value(id).get_float(v); + } + + int get_value_int(int id) const + { + return get_value(id).get_int(); + } + + int get_value_int(int id, int v) const + { + return get_value(id).get_int(v); + } + + long long get_value_int64(int id) const + { + return *reinterpret_cast(get_value(id).get_data()); + } + + std::string get_value_string(int id) const + { + return get_value(id).get_string(); + } + + std::string get_value_string(int id, const std::string& v) const + { + return get_value(id).get_string(v); + } + + bool exists_key(int id) const + { + return m_keys.find(id) != m_keys.end(); + } + + bool exists_value(int id) const + { + return m_values.find(id) != m_values.end(); + } + + int c_keys() const + { + return m_keys.size(); + } + + int c_values() const + { + return m_values.size(); + } + + int load_key(data_ref data) + { + return load_key(data.data(), data.size()); + } + + void delete_key(int id) + { + m_keys.erase(id); + } + + void delete_value(int id) + { + m_values.erase(id); + } + + void clear() + { + m_keys.clear(); + m_values.clear(); + } + + void dump(std::ostream& os, bool show_ratio, int depth = 0, shared_data* t = NULL) const; + void dump_ratio(std::ostream& os, shared_data* t) const; + shared_data export_bz() const; + int load_key(const byte* data, size_t size); + shared_data vdata() const; + + t_xif_key_map& m_keys; + t_xif_value_map m_values; +private: + int get_size() const; + void load_old(const byte*& data); + void load_new(const byte*& data); + void save(byte*& data) const; +}; diff --git a/misc/xbt/.svn/text-base/xif_key_r.h.svn-base b/misc/xbt/.svn/text-base/xif_key_r.h.svn-base new file mode 100644 index 0000000..c2400c0 --- /dev/null +++ b/misc/xbt/.svn/text-base/xif_key_r.h.svn-base @@ -0,0 +1,97 @@ +#pragma once + +#include +#include "xif_value.h" + +class Cxif_key_r +{ +public: + typedef std::vector > t_key_map; + typedef std::vector > t_value_map; + + const Cxif_key_r& get_key(int id) const + { + return *find_key(id); + } + + const Cxif_value& get_value(int id) const + { + static Cxif_value z; + const Cxif_value* i = find_value(id); + return i ? *i : z; + } + + float get_value_float(int id) const + { + return get_value(id).get_float(); + } + + float get_value_float(int id, float v) const + { + return get_value(id).get_float(v); + } + + int get_value_int(int id) const + { + return get_value(id).get_int(); + } + + int get_value_int(int id, int v) const + { + return get_value(id).get_int(v); + } + + long long get_value_int64(int id) const + { + return *reinterpret_cast(get_value(id).get_data()); + } + + std::string get_value_string(int id) const + { + return get_value(id).get_string(); + } + + std::string get_value_string(int id, const std::string& v) const + { + return get_value(id).get_string(v); + } + + const t_key_map& keys() const + { + return m_keys; + } + + const t_value_map& values() const + { + return m_values; + } + + int c_keys() const + { + return keys().size(); + } + + int c_values() const + { + return values().size(); + } + + bool has_key(int id) const + { + return find_key(id); + } + + bool has_value(int id) const + { + return find_value(id); + } + + const Cxif_key_r* find_key(int id) const; + const Cxif_value* find_value(int id) const; + int import(data_ref); +private: + int load(const byte* s); + + t_key_map m_keys; + t_value_map m_values; +}; diff --git a/misc/xbt/.svn/text-base/xif_value.h.svn-base b/misc/xbt/.svn/text-base/xif_value.h.svn-base new file mode 100644 index 0000000..033e951 --- /dev/null +++ b/misc/xbt/.svn/text-base/xif_value.h.svn-base @@ -0,0 +1,113 @@ +#pragma once + +#include +#include + +enum t_vt {vt_bin32, vt_binary, vt_int32, vt_string, vt_external_binary, vt_float, vt_unknown}; + +class Cxif_value +{ +public: + Cxif_value() + { + m_type = vt_unknown; + } + + Cxif_value(float v) + { + m_type = vt_float; + m_value_float = v; + } + + Cxif_value(t_vt type, int v) + { + m_type = type; + m_value_int = v; + } + + Cxif_value(const shared_data& v) + { + m_type = vt_binary; + m_data = v; + } + + Cxif_value(const std::string& v) + { + m_type = vt_string; + m_data = make_shared_data(v.c_str(), v.size() + 1); + } + + shared_data get_vdata() const + { + assert(!idata()); + return m_data; + } + + const byte* get_data() const + { + return idata() ? m_value : m_data.data(); + } + + int get_size() const + { + return idata() ? 4 : m_data.size(); + } + + float get_float() const + { + assert(get_size() == 4); + return m_value_float; + } + + float get_float(float v) const + { + return get_size() ? get_float() : v; + } + + int get_int() const + { + assert(get_size() == 4); + return m_value_int; + } + + int get_int(int v) const + { + return get_size() ? get_int() : v; + } + + std::string get_string() const + { + assert(get_size()); + return reinterpret_cast(get_data()); + } + + std::string get_string(const std::string& v) const + { + return get_size() ? get_string() : v; + } + + bool idata() const + { + // internal data? + return get_type() == vt_bin32 || get_type() == vt_float || get_type() == vt_int32; + } + + void dump(std::ostream& os, int depth = 0) const; + t_vt get_type() const; + void load_old(const byte*& data); + void load_new(const byte*& data); + void load_external(const byte*& data); + void save(byte*& data) const; + static int skip(const byte* s); + void external_save(byte*& data) const; +private: + shared_data m_data; + t_vt m_type; + + union + { + byte m_value[4]; + float m_value_float; + int m_value_int; + }; +}; diff --git a/misc/xbt/bstream.h b/misc/xbt/bstream.h new file mode 100644 index 0000000..78c3d64 --- /dev/null +++ b/misc/xbt/bstream.h @@ -0,0 +1,116 @@ +#pragma once + +#include +#include + +class bstream_handle +{ +public: + typedef int handle_type; + + bstream_handle() = default; + + explicit bstream_handle(handle_type f) : f_(f) + { + } + + handle_type get() const + { + return f_; + } + + bool is_open() const + { + return get() != -1; + } + + explicit operator bool() const + { + return is_open(); + } + + void reset(handle_type f = -1) + { + f_ = f; + } + + ptrdiff_t read(void* d, size_t cb_d) + { + return ::read(get(), d, cb_d); + } + + ptrdiff_t write(const void* d, size_t cb_d) + { + return ::write(get(), d, cb_d); + } +private: + handle_type f_ = -1; +}; + +class bstream +{ +public: + typedef int handle_type; + + bstream() = default; + + bstream(bstream&& v) : f_(v.release()) + { + } + + explicit bstream(handle_type f) : f_(f) + { + } + + explicit bstream(const char* name, int mode) : f_(open(name, mode)) + { + } + + explicit bstream(const std::string& name, int mode) : f_(open(name.c_str(), mode)) + { + } + + ~bstream() + { + close(); + } + + handle_type release() + { + handle_type f = f_.get(); + f_.reset(); + return f; + } + + handle_type get() const + { + return f_.get(); + } + + bool is_open() const + { + return f_.is_open(); + } + + explicit operator bool() const + { + return is_open(); + } + + ptrdiff_t read(void* d, size_t cb_d) + { + return f_.read(d, cb_d); + } + + ptrdiff_t write(const void* d, size_t cb_d) + { + return f_.write(d, cb_d); + } + + int close() + { + return is_open() ? ::close(release()) : 0; + } +private: + bstream_handle f_; +}; diff --git a/misc/xbt/bt_misc.h b/misc/xbt/bt_misc.h new file mode 100644 index 0000000..5d973d1 --- /dev/null +++ b/misc/xbt/bt_misc.h @@ -0,0 +1,98 @@ +#pragma once + +#include +#include + +std::string b2a(long long v, const char* postfix = NULL); +std::string backward_slashes(std::string); +std::string duration2a(float); +std::string escape_string(const std::string&); +std::string forward_slashes(std::string); +std::string generate_random_string(int); +std::string get_env(const std::string&); +int hms2i(int h, int m, int s); +bool is_private_ipa(int a); +int merkle_tree_size(int v); +std::string mk_sname(std::string); +std::string n(long long); +std::string n2a(long long v, const char* postfix = NULL); +std::string native_slashes(const std::string&); +std::string hex_decode(str_ref); +std::string hex_encode(int l, int v); +std::string hex_encode(data_ref); +std::string js_encode(str_ref); +std::string peer_id2a(const std::string&); +std::string time2a(time_t); +std::string uri_decode(str_ref); +std::string uri_encode(str_ref); +void xbt_syslog(const std::string&); +std::string xbt_version2a(int); + +inline long long htonll(long long v) +{ + const unsigned char* a = reinterpret_cast(&v); + long long b = a[0] << 24 | a[1] << 16 | a[2] << 8 | a[3]; + return b << 32 | static_cast(a[4]) << 24 | a[5] << 16 | a[6] << 8 | a[7]; +} + +inline long long ntohll(long long v) +{ + return htonll(v); +} + +enum +{ + hs_name_size = 0, + hs_name = 1, + hs_reserved = 20, + hs_info_hash = 28, + hs_size = 48, +}; + +enum +{ + uta_connect, + uta_announce, + uta_scrape, + uta_error, +}; + +enum +{ + uti_connection_id = 0, + uti_action = 8, + uti_transaction_id = 12, + uti_size = 16, + + utic_size = 16, + + utia_info_hash = 16, + utia_peer_id = 36, + utia_downloaded = 56, + utia_left = 64, + utia_uploaded = 72, + utia_event = 80, + utia_ipa = 84, + utia_key = 88, + utia_num_want = 92, + utia_port = 96, + utia_size = 98, + + utis_size = 16, + + uto_action = 0, + uto_transaction_id = 4, + uto_size = 8, + + utoc_connection_id = 8, + utoc_size = 16, + + utoa_interval = 8, + utoa_leechers = 12, + utoa_seeders = 16, + utoa_size = 20, + + utos_size = 8, + + utoe_size = 8, +}; diff --git a/misc/xbt/cfile.h b/misc/xbt/cfile.h new file mode 100644 index 0000000..961e67c --- /dev/null +++ b/misc/xbt/cfile.h @@ -0,0 +1,132 @@ +#pragma once + +#include +#include +#include + +inline size_t read(FILE* f, void* d, size_t cb_d) +{ + return fread(d, 1, cb_d, f); +} + +inline size_t write(FILE* f, const void* d, size_t cb_d) +{ + return fwrite(d, 1, cb_d, f); +} + +class cfile_handle +{ +public: + typedef FILE* handle_type; + + cfile_handle() = default; + + explicit cfile_handle(handle_type f) : f_(f) + { + } + + handle_type get() const + { + return f_; + } + + bool is_open() const + { + return !!get(); + } + + explicit operator bool() const + { + return is_open(); + } + + void reset(handle_type f = NULL) + { + f_ = f; + } + + size_t read(void* d, size_t cb_d) + { + return fread(d, 1, cb_d, get()); + } + + size_t write(const void* d, size_t cb_d) + { + return fwrite(d, 1, cb_d, get()); + } +private: + handle_type f_ = NULL; +}; + +class cfile : boost::noncopyable +{ +public: + typedef FILE* handle_type; + + cfile() = default; + + cfile(cfile&& v) : f_(v.release()) + { + } + + explicit cfile(handle_type f) : f_(f) + { + } + + explicit cfile(const char* name, const char* mode) : f_(fopen(name, mode)) + { + } + + explicit cfile(const std::string& name, const char* mode) : f_(fopen(name.c_str(), mode)) + { + } + + ~cfile() + { + close(); + } + + handle_type release() + { + handle_type f = f_.get(); + f_.reset(); + return f; + } + + handle_type get() const + { + return f_.get(); + } + + operator handle_type() const + { + return get(); + } + + bool is_open() const + { + return f_.is_open(); + } + + explicit operator bool() const + { + return is_open(); + } + + size_t read(void* d, size_t cb_d) + { + return f_.read(d, cb_d); + } + + size_t write(const void* d, size_t cb_d) + { + return f_.write(d, cb_d); + } + + int close() + { + return is_open() ? fclose(release()) : 0; + } +private: + cfile_handle f_; +}; diff --git a/misc/xbt/data_ref.h b/misc/xbt/data_ref.h new file mode 100644 index 0000000..e4a7edc --- /dev/null +++ b/misc/xbt/data_ref.h @@ -0,0 +1,225 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +template +class data_ref_base : public boost::iterator_range +{ +public: + data_ref_base() + { + clear(); + } + + template + data_ref_base(const V& v, typename boost::enable_if >::type* = 0) + { + if (v.end() != v.begin()) + assign(&*v.begin(), v.end() - v.begin() + &*v.begin()); + else + clear(); + } + + template + data_ref_base(V& v, typename boost::enable_if >::type* = 0) + { + if (v.end() != v.begin()) + assign(&*v.begin(), v.end() - v.begin() + &*v.begin()); + else + clear(); + } + + explicit data_ref_base(const char* v) + { + if (v) + assign(v, strlen(v)); + else + clear(); + } + + data_ref_base(U begin, U end) + { + assign(begin, end); + } + + data_ref_base(U begin, size_t size) + { + assign(begin, size); + } + + void assign(U begin, U end) + { + static_cast(*this) = base_t(reinterpret_cast(begin), reinterpret_cast(end)); + } + + void assign(U begin, size_t size) + { + assign(begin, reinterpret_cast(begin) + size); + } + + void set_begin(U v) + { + assign(v, base_t::end()); + } + + void set_end(U v) + { + assign(base_t::begin(), v); + } + + void clear() + { + assign(T(NULL), T(NULL)); + } + + T data() const + { + return base_t::begin(); + } + + template + data_ref_base find0(V v) const + { + data_ref_base t = *this; + while (!t.empty() && t.front() != v) + t.pop_front(); + return t; + } + + float f() const + { + return to_float(*this); + } + + long long i() const + { + return to_int(*this); + } + + const std::string s() const + { + return std::string(reinterpret_cast(data()), base_t::size()); + } + + data_ref_base substr(size_t pos) + { + return data_ref_base(base_t::begin() + pos, base_t::size() - pos); + } + + data_ref_base substr(size_t pos, size_t sz) + { + return data_ref_base(base_t::begin() + pos, sz); + } +private: + typedef boost::iterator_range base_t; +}; + +typedef data_ref_base data_ref; +typedef data_ref_base mutable_data_ref; +typedef data_ref_base str_ref; +typedef data_ref_base mutable_str_ref; + +bool operator==(str_ref a, const char* b); + +inline size_t memcpy(void* d, data_ref s) +{ + memcpy(d, s.data(), s.size()); + return s.size(); +} + +inline size_t memcpy(mutable_data_ref d, data_ref s) +{ + assert(d.size() >= s.size()); + memcpy(d.data(), s.data(), s.size()); + return s.size(); +} + +inline int eat(str_ref& s, char v) +{ + if (!s || s.front() != v) + return 1; + s.pop_front(); + return 0; +} + +inline str_ref read_until(str_ref& is, char sep) +{ + const char* a = is.begin(); + const char* b = std::find(is.begin(), is.end(), sep); + is.set_begin(b == is.end() ? b : b + 1); + return str_ref(a, b); +} + +inline float to_float(data_ref v) +{ + if (v.empty()) + return 0; + try + { + return boost::lexical_cast(v); + } + catch (boost::bad_lexical_cast&) + { + } + return 0; +} + +template +int try_parse(T& d, str_ref s) +{ + if (!s) + return 1; + bool neg = !eat(s, '-'); + d = 0; + for (; s; s.pop_front()) + { + char c = s.front(); + if (c < '0' || c > '9') + return 1; + d = neg ? 10 * d - (c - '0') : 10 * d + (c - '0'); + } + return 0; +} + +template +T parse(str_ref s) +{ + T d; + return try_parse(d, s) ? 0 : d; +} + +template +void parse(T& d, str_ref s) +{ + if (try_parse(d, s)) + d = 0; +} + +inline long long to_int(str_ref v) +{ + return parse(v); + if (v.empty()) + return 0; + if (!*v.end()) + return atoll(v.data()); + try + { + return boost::lexical_cast(v); + } + catch (boost::bad_lexical_cast&) + { + } + return 0; +} + +inline const std::string to_string(str_ref v) +{ + return std::string(v.data(), v.size()); +} diff --git a/misc/xbt/database.h b/misc/xbt/database.h new file mode 100644 index 0000000..bc6fb65 --- /dev/null +++ b/misc/xbt/database.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include "sql_result.h" + +class bad_query : public std::runtime_error +{ +public: + bad_query(const std::string& s) : runtime_error(s) + { + } +}; + +class Cdatabase : boost::noncopyable +{ +public: + void open(const std::string& host, const std::string& user, const std::string& password, const std::string& database, bool echo_errors = false); + const std::string& name(const std::string&) const; + Csql_result query(const std::string&); + int query_nothrow(const std::string&); + void set_name(const std::string&, std::string); + void set_query_log(std::ostream*); + int affected_rows(); + int insert_id(); + int select_db(const std::string&); + void close(); + Cdatabase(); + ~Cdatabase(); + + operator MYSQL*() + { + return &m_handle; + } +private: + bool m_echo_errors; + MYSQL m_handle; + std::map m_names; + std::ostream* m_query_log = NULL; +}; diff --git a/misc/xbt/find_ptr.h b/misc/xbt/find_ptr.h new file mode 100644 index 0000000..638a7b5 --- /dev/null +++ b/misc/xbt/find_ptr.h @@ -0,0 +1,68 @@ +#pragma once + +template +const typename T::value_type::second_type* find_ptr(const T& c, const U& v) +{ + typename T::const_iterator i = c.find(v); + return i == c.end() ? NULL : &i->second; +} + +template +typename T::value_type::second_type* find_ptr(T& c, const U& v) +{ + typename T::iterator i = c.find(v); + return i == c.end() ? NULL : &i->second; +} + +template +const typename T::value_type::second_type& find_ptr2(const T& c, const U& v) +{ + static typename T::value_type::second_type z = typename T::value_type::second_type(); + typename T::const_iterator i = c.find(v); + return i == c.end() ? z : i->second; +} + +template +typename T::value_type::second_type& find_ptr2(T& c, const U& v) +{ + static typename T::value_type::second_type z = typename T::value_type::second_type(); + typename T::iterator i = c.find(v); + return i == c.end() ? z : i->second; +} + +template +const typename T::value_type* find_ptr0(const T& c, const U& v) +{ + typename T::const_iterator i = c.find(v); + return i == c.end() ? NULL : &*i; +} + +template +typename T::value_type::second_type& find_ref(T& c, const U& v) +{ + typename T::iterator i = c.find(v); + assert(i != c.end()); + return i->second; +} + +template +const typename T::value_type::second_type& find_ref(const T& c, const U& v) +{ + typename T::const_iterator i = c.find(v); + assert(i != c.end()); + return i->second; +} + +template +const typename T::value_type::second_type& find_ref(const T& c, const U& v, const typename T::value_type::second_type& z) +{ + typename T::const_iterator i = c.find(v); + return i == c.end() ? z : i->second; +} + +template +typename T::value_type::second_type& find_ref(T& c, const U& v, typename T::value_type::second_type& z) +{ + typename T::iterator i = c.find(v); + return i == c.end() ? z : i->second; +} diff --git a/misc/xbt/profiler.h b/misc/xbt/profiler.h new file mode 100644 index 0000000..6b949d1 --- /dev/null +++ b/misc/xbt/profiler.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include + +class xbt_profiler +{ +public: + xbt_profiler(int limit, const std::string& text) + { + limit_ = limit; + t0_ = boost::posix_time::microsec_clock::universal_time(); + text_ = text; + } + + ~xbt_profiler() + { + auto ms = (boost::posix_time::microsec_clock::universal_time() - t0_).total_milliseconds(); + if (ms < limit_) + return; + std::stringstream s; + s << ms << " ms: " << text_; + xbt_syslog(s.str()); + } +private: + int limit_; + boost::posix_time::ptime t0_; + std::string text_; +}; diff --git a/misc/xbt/shared_data.h b/misc/xbt/shared_data.h new file mode 100644 index 0000000..cbdbc36 --- /dev/null +++ b/misc/xbt/shared_data.h @@ -0,0 +1,112 @@ +#pragma once + +#include +#include +// #include +// #include +#include +#include +#include +#include +#include +#include +#include + +template +class shared_array2 : public boost::iterator_range +{ +public: + shared_array2() + { + } + + explicit shared_array2(size_t sz) + { + if (!sz) + return; + std::shared_ptr n(new T[sz], boost::checked_array_deleter()); + static_cast(*this) = base_t(n.get(), n.get() + sz); + n_ = n; + } + + template + shared_array2(const shared_array2& v) : // , typename boost::enable_if>::type* = 0) : + base_t(v.data(), v.data() + v.size()), + n_(v.n()) + { + } + + shared_array2(T* b, T* e, std::shared_ptr const& n) : + base_t(b, e), + n_(n) + { + } + + shared_array2(T* b, size_t sz, std::shared_ptr const& n) : + base_t(b, b + sz), + n_(n) + { + } + + void clear() + { + *this = shared_array2(); + } + + T* data() const + { + return base_t::begin(); + } + + std::shared_ptr const& n() const + { + return n_; + } + + shared_array2 substr(size_t ofs, size_t sz) const + { + return shared_array2(data() + ofs, sz, n()); + } +private: + typedef boost::iterator_range base_t; + + std::shared_ptr n_; +}; + +typedef shared_array2 shared_data; + +inline shared_data make_shared_data(data_ref v) +{ + shared_data d(v.size()); + memcpy(d.data(), v); + return d; +} + +inline shared_data make_shared_data(const void* d, size_t sz) +{ + return make_shared_data(data_ref(d, sz)); +} + +inline shared_data file_get(FILE* f) +{ + if (!f) + return shared_data(); + fseek(f, 0, SEEK_SET); + struct stat b; + if (fstat(fileno(f), &b)) + return shared_data(); + shared_data d(b.st_size); + return read(f, d.data(), b.st_size) == b.st_size ? d : shared_data(); +} + +inline shared_data file_get(const std::string& fname) +{ + cfile f(fname, "rb"); + return file_get(f); +} + +inline int file_put(const std::string& fname, data_ref v) +{ + cfile f(fname, "wb"); + return !f || f.write(v.data(), v.size()) != v.size(); +} diff --git a/misc/xbt/sql_query.h b/misc/xbt/sql_query.h new file mode 100644 index 0000000..29821fb --- /dev/null +++ b/misc/xbt/sql_query.h @@ -0,0 +1,39 @@ +#pragma once + +#include + +class Cdatabase; +class Csql_result; + +class Csql_query +{ +public: + Csql_result execute() const; + int execute_nothrow() const; + std::string read() const; + void operator=(std::string); + void operator+=(const std::string&); + Csql_query& p_name(const std::string&); + Csql_query& p_raw(data_ref); + Csql_query& operator()(long long); + Csql_query& operator()(str_ref); + Csql_query(Cdatabase&, std::string = ""); + +#if 1 + Csql_query& p(long long v) + { + return (*this)(v); + } + + Csql_query& p(str_ref v) + { + return (*this)(v); + } +#endif +private: + std::string replace_names(const std::string&) const; + + Cdatabase& m_database; + std::string m_in; + std::string m_out; +}; diff --git a/misc/xbt/sql_result.h b/misc/xbt/sql_result.h new file mode 100644 index 0000000..79f8176 --- /dev/null +++ b/misc/xbt/sql_result.h @@ -0,0 +1,103 @@ +#pragma once + +#include +#include +#include +#ifdef _MSC_VER +#include +#endif +#include + +class Csql_row +{ +public: + Csql_row() = default; + + Csql_row(MYSQL_ROW data, unsigned long* sizes, std::shared_ptr source) + { + m_data = data; + m_sizes = sizes; + m_source = std::move(source); + } + + operator const void*() const + { + return m_data; + } + + str_ref operator[](size_t i) const + { + return m_data ? str_ref(m_data[i], m_sizes[i]) : str_ref(); + } +private: + MYSQL_ROW m_data; + unsigned long* m_sizes; + std::shared_ptr m_source; +}; + +class Csql_result +{ +public: + class iterator + { + public: + iterator() = default; + iterator(Csql_result& v) : res_(&v), row_(mysql_fetch_row(res_->h())) { } + bool operator!=(iterator) { return row_; } + Csql_row operator*() { return Csql_row(row_, mysql_fetch_lengths(res_->h()), res_->m_source); } + void operator++() { row_ = mysql_fetch_row(res_->h()); } + private: + Csql_result* res_ = NULL; + MYSQL_ROW row_; + }; + + Csql_row fetch_row() const + { + MYSQL_ROW data = mysql_fetch_row(h()); + return Csql_row(data, mysql_fetch_lengths(h()), m_source); + } + + str_ref fetch_value() const + { + return fetch_row()[0]; + } + + long long fetch_int() const + { + return fetch_value().i(); + } + + Csql_result(MYSQL_RES* h) : m_source(h, mysql_free_result) + { + } + + operator const void*() const + { + return size() ? this : NULL; + } + + int c_fields() const + { + return mysql_num_fields(h()); + } + + int size() const + { + return mysql_num_rows(h()); + } + + void data_seek(int i) + { + mysql_data_seek(h(), i); + } + + iterator begin() { return iterator(*this); } + iterator end() { return iterator(); } +private: + MYSQL_RES* h() const + { + return m_source.get(); + } + + std::shared_ptr m_source; +}; diff --git a/misc/xbt/to_array.h b/misc/xbt/to_array.h new file mode 100644 index 0000000..bdf2989 --- /dev/null +++ b/misc/xbt/to_array.h @@ -0,0 +1,13 @@ +#pragma once + +template +std::array to_array(const V& v) +{ + assert(v.size() == N); + std::array d; + if (v.size() == d.size()) + std::copy(v.begin(), v.end(), d.data()); + else + d.fill(0); + return d; +} diff --git a/misc/xbt/virtual_binary.h b/misc/xbt/virtual_binary.h new file mode 100644 index 0000000..f91e5bd --- /dev/null +++ b/misc/xbt/virtual_binary.h @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +class Cvirtual_binary_source: boost::noncopyable +{ +public: + Cvirtual_binary_source(size_t v) + { + m_range.assign(new char[v], v); + } + + ~Cvirtual_binary_source() + { + delete[] m_range.begin(); + } + + unsigned char* begin() const + { + return m_range.begin(); + } + + unsigned char* end() const + { + return m_range.end(); + } + + void resize(size_t v) + { + assert(v <= m_range.size()); + m_range.assign(m_range.begin(), v); + } +private: + mutable_data_ref m_range; +}; + +class Cvirtual_binary +{ +public: + void assign(data_ref); + unsigned char* write_start(size_t cb_d); + + Cvirtual_binary() + { + } + + explicit Cvirtual_binary(size_t v) + { + assign(v); + } + + explicit Cvirtual_binary(data_ref v) + { + assign(v); + } + + void assign(size_t v) + { + assign(data_ref(NULL, v)); + } + + const unsigned char* begin() const + { + return range().begin(); + } + + unsigned char* mutable_begin() + { + return mutable_range().begin(); + } + + const unsigned char* data() const + { + return range().begin(); + } + + unsigned char* data_edit() + { + return mutable_range().begin(); + } + + const unsigned char* end() const + { + return range().end(); + } + + unsigned char* mutable_end() + { + return mutable_range().end(); + } + + data_ref range() const + { + return m_source ? *m_source : data_ref(); + } + + mutable_data_ref mutable_range() + { + if (!m_source) + return mutable_data_ref(); + if (!m_source.unique()) + assign(range()); + return *m_source; + } + + void clear() + { + m_source.reset(); + } + + bool empty() const + { + return range().empty(); + } + + size_t size() const + { + return range().size(); + } + + void resize(size_t v) + { + if (!m_source) + write_start(v); + mutable_range(); + m_source->resize(v); + } + + operator const unsigned char*() const + { + return data(); + } + + operator data_ref() const + { + return range(); + } + + operator mutable_data_ref() + { + return mutable_range(); + } +private: + boost::shared_ptr m_source; +}; diff --git a/misc/xbt/xcc_z.h b/misc/xbt/xcc_z.h new file mode 100644 index 0000000..43a7200 --- /dev/null +++ b/misc/xbt/xcc_z.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +namespace xcc_z +{ + shared_data gunzip(data_ref); + shared_data gzip(data_ref); + void gzip_out(data_ref); +} diff --git a/misc/xbt/xif_key.h b/misc/xbt/xif_key.h new file mode 100644 index 0000000..ee7b5a3 --- /dev/null +++ b/misc/xbt/xif_key.h @@ -0,0 +1,249 @@ +#pragma once + +#include +#include +#include +#include + +const static int file_id = 0x1a464958; // *reinterpret_cast("XIF\x1a"); +const static int file_version_old = 0; +const static int file_version_new = 1; +const static int file_version_fast = 2; + +struct t_xif_header_old +{ + int id; + int version; + int size_uncompressed; +}; + +struct t_xif_header_fast +{ + int id; + int version; + int size_uncompressed; + int size_compressed; + int size_external; +}; + +class Cxif_key; + +typedef std::map t_xif_key_map; +typedef std::map t_xif_value_map; + +class Cxif_key +{ +public: + Cxif_key(): + m_keys(*new t_xif_key_map) + { + } + + ~Cxif_key() + { + delete &m_keys; + } + + Cxif_key(const Cxif_key& v): + m_keys(*new t_xif_key_map) + { + m_keys = v.m_keys; + m_values = v.m_values; + } + + explicit Cxif_key(const shared_data& v): + m_keys(*new t_xif_key_map) + { + load_key(v); + } + + const Cxif_key& operator=(const Cxif_key& v) + { + m_keys = v.m_keys; + m_values = v.m_values; + return *this; + } + + Cxif_key& set_key(int id) + { + m_keys[id] = Cxif_key(); + return m_keys[id]; + } + + const Cxif_key& open_key_read(int id) const + { + return m_keys.find(id)->second; + } + + Cxif_key& open_key_edit(int id) + { + return m_keys[id]; + } + + Cxif_key& open_key_write() + { + return open_key_write(m_keys.empty() ? 0 : m_keys.rbegin()->first + 1); + } + + Cxif_key& open_key_write(int id) + { + m_keys[id] = Cxif_key(); + return m_keys[id]; + } + + const Cxif_value& open_value_read(int id) const + { + return m_values.find(id)->second; + } + + Cxif_value& open_value_edit(int id) + { + return m_values[id]; + } + + Cxif_value& open_value_write(int id) + { + m_values[id] = Cxif_value(); + return m_values[id]; + } + + Cxif_value& set_value(int id) + { + m_values[id] = Cxif_value(); + return m_values[id]; + } + + void set_value_bin(int id, int v) + { + m_values[id] = Cxif_value(vt_bin32, v); + } + + void set_value_binary(int id, const shared_data& v) + { + m_values[id] = Cxif_value(v); + } + + void set_value_float(int id, float v) + { + m_values[id] = Cxif_value(v); + } + + void set_value_int(int id, int v) + { + m_values[id] = Cxif_value(vt_int32, v); + } + + void set_value_string(int id, const std::string& v) + { + m_values[id] = Cxif_value(v); + } + + void set_value_int64(int id, long long v) + { + set_value_binary(id, make_shared_data(&v, 8)); + } + + const Cxif_key& get_key(int id) const + { + static Cxif_key z; + t_xif_key_map::iterator i = m_keys.find(id); + return i == m_keys.end() ? z : i->second; + } + + const Cxif_value& get_value(int id) const + { + static Cxif_value z; + t_xif_value_map::const_iterator i = m_values.find(id); + return i == m_values.end() ? z : i->second; + } + + float get_value_float(int id) const + { + return get_value(id).get_float(); + } + + float get_value_float(int id, float v) const + { + return get_value(id).get_float(v); + } + + int get_value_int(int id) const + { + return get_value(id).get_int(); + } + + int get_value_int(int id, int v) const + { + return get_value(id).get_int(v); + } + + long long get_value_int64(int id) const + { + return *reinterpret_cast(get_value(id).get_data()); + } + + std::string get_value_string(int id) const + { + return get_value(id).get_string(); + } + + std::string get_value_string(int id, const std::string& v) const + { + return get_value(id).get_string(v); + } + + bool exists_key(int id) const + { + return m_keys.find(id) != m_keys.end(); + } + + bool exists_value(int id) const + { + return m_values.find(id) != m_values.end(); + } + + int c_keys() const + { + return m_keys.size(); + } + + int c_values() const + { + return m_values.size(); + } + + int load_key(data_ref data) + { + return load_key(data.data(), data.size()); + } + + void delete_key(int id) + { + m_keys.erase(id); + } + + void delete_value(int id) + { + m_values.erase(id); + } + + void clear() + { + m_keys.clear(); + m_values.clear(); + } + + void dump(std::ostream& os, bool show_ratio, int depth = 0, shared_data* t = NULL) const; + void dump_ratio(std::ostream& os, shared_data* t) const; + shared_data export_bz() const; + int load_key(const byte* data, size_t size); + shared_data vdata() const; + + t_xif_key_map& m_keys; + t_xif_value_map m_values; +private: + int get_size() const; + void load_old(const byte*& data); + void load_new(const byte*& data); + void save(byte*& data) const; +}; diff --git a/misc/xbt/xif_key_r.h b/misc/xbt/xif_key_r.h new file mode 100644 index 0000000..c2400c0 --- /dev/null +++ b/misc/xbt/xif_key_r.h @@ -0,0 +1,97 @@ +#pragma once + +#include +#include "xif_value.h" + +class Cxif_key_r +{ +public: + typedef std::vector > t_key_map; + typedef std::vector > t_value_map; + + const Cxif_key_r& get_key(int id) const + { + return *find_key(id); + } + + const Cxif_value& get_value(int id) const + { + static Cxif_value z; + const Cxif_value* i = find_value(id); + return i ? *i : z; + } + + float get_value_float(int id) const + { + return get_value(id).get_float(); + } + + float get_value_float(int id, float v) const + { + return get_value(id).get_float(v); + } + + int get_value_int(int id) const + { + return get_value(id).get_int(); + } + + int get_value_int(int id, int v) const + { + return get_value(id).get_int(v); + } + + long long get_value_int64(int id) const + { + return *reinterpret_cast(get_value(id).get_data()); + } + + std::string get_value_string(int id) const + { + return get_value(id).get_string(); + } + + std::string get_value_string(int id, const std::string& v) const + { + return get_value(id).get_string(v); + } + + const t_key_map& keys() const + { + return m_keys; + } + + const t_value_map& values() const + { + return m_values; + } + + int c_keys() const + { + return keys().size(); + } + + int c_values() const + { + return values().size(); + } + + bool has_key(int id) const + { + return find_key(id); + } + + bool has_value(int id) const + { + return find_value(id); + } + + const Cxif_key_r* find_key(int id) const; + const Cxif_value* find_value(int id) const; + int import(data_ref); +private: + int load(const byte* s); + + t_key_map m_keys; + t_value_map m_values; +}; diff --git a/misc/xbt/xif_value.h b/misc/xbt/xif_value.h new file mode 100644 index 0000000..033e951 --- /dev/null +++ b/misc/xbt/xif_value.h @@ -0,0 +1,113 @@ +#pragma once + +#include +#include + +enum t_vt {vt_bin32, vt_binary, vt_int32, vt_string, vt_external_binary, vt_float, vt_unknown}; + +class Cxif_value +{ +public: + Cxif_value() + { + m_type = vt_unknown; + } + + Cxif_value(float v) + { + m_type = vt_float; + m_value_float = v; + } + + Cxif_value(t_vt type, int v) + { + m_type = type; + m_value_int = v; + } + + Cxif_value(const shared_data& v) + { + m_type = vt_binary; + m_data = v; + } + + Cxif_value(const std::string& v) + { + m_type = vt_string; + m_data = make_shared_data(v.c_str(), v.size() + 1); + } + + shared_data get_vdata() const + { + assert(!idata()); + return m_data; + } + + const byte* get_data() const + { + return idata() ? m_value : m_data.data(); + } + + int get_size() const + { + return idata() ? 4 : m_data.size(); + } + + float get_float() const + { + assert(get_size() == 4); + return m_value_float; + } + + float get_float(float v) const + { + return get_size() ? get_float() : v; + } + + int get_int() const + { + assert(get_size() == 4); + return m_value_int; + } + + int get_int(int v) const + { + return get_size() ? get_int() : v; + } + + std::string get_string() const + { + assert(get_size()); + return reinterpret_cast(get_data()); + } + + std::string get_string(const std::string& v) const + { + return get_size() ? get_string() : v; + } + + bool idata() const + { + // internal data? + return get_type() == vt_bin32 || get_type() == vt_float || get_type() == vt_int32; + } + + void dump(std::ostream& os, int depth = 0) const; + t_vt get_type() const; + void load_old(const byte*& data); + void load_new(const byte*& data); + void load_external(const byte*& data); + void save(byte*& data) const; + static int skip(const byte* s); + void external_save(byte*& data) const; +private: + shared_data m_data; + t_vt m_type; + + union + { + byte m_value[4]; + float m_value_float; + int m_value_int; + }; +}; diff --git a/misc/xcc_z.cpp b/misc/xcc_z.cpp new file mode 100644 index 0000000..13e7481 --- /dev/null +++ b/misc/xcc_z.cpp @@ -0,0 +1,70 @@ +#include "xbt/xcc_z.h" + +#include +#include +#include +#include "stream_int.h" + +shared_data xcc_z::gunzip(data_ref s) +{ + if (s.size() < 18) + return shared_data(); + shared_data d(read_int_le(4, s.end() - 4)); + z_stream stream; + stream.zalloc = NULL; + stream.zfree = NULL; + stream.opaque = NULL; + stream.next_in = const_cast(s.begin()) + 10; + stream.avail_in = s.size() - 18; + stream.next_out = d.data(); + stream.avail_out = d.size(); + return stream.next_out + && Z_OK == inflateInit2(&stream, -MAX_WBITS) + && Z_STREAM_END == inflate(&stream, Z_FINISH) + && Z_OK == inflateEnd(&stream) + ? d + : shared_data(); +} + +shared_data xcc_z::gzip(data_ref s) +{ + unsigned long cb_d = s.size() + (s.size() + 999) / 1000 + 12; + shared_data d(10 + cb_d + 8); + unsigned char* w = d.data(); + *w++ = 0x1f; + *w++ = 0x8b; + *w++ = Z_DEFLATED; + *w++ = 0; + *w++ = 0; + *w++ = 0; + *w++ = 0; + *w++ = 0; + *w++ = 0; + *w++ = 3; + { + z_stream stream; + stream.zalloc = NULL; + stream.zfree = NULL; + stream.opaque = NULL; + deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); + stream.next_in = const_cast(s.begin()); + stream.avail_in = s.size(); + stream.next_out = w; + stream.avail_out = cb_d; + deflate(&stream, Z_FINISH); + deflateEnd(&stream); + w = stream.next_out; + } + w = write_int_le(4, w, crc32(crc32(0, NULL, 0), s.data(), s.size())); + w = write_int_le(4, w, s.size()); + return d.substr(0, w - d.data()); +} + +/* +void xcc_z::gzip_out(data_ref s) +{ + gzFile f = gzdopen(fileno(stdout), "wb"); + gzwrite(f, s.data(), s.size()); + gzflush(f, Z_FINISH); +} +*/ diff --git a/misc/xif_key.cpp b/misc/xif_key.cpp new file mode 100644 index 0000000..2453213 --- /dev/null +++ b/misc/xif_key.cpp @@ -0,0 +1,149 @@ +#include "stdafx.h" +#include "xif_key.h" + +#include +#include "stream_int.h" + +static int read_int(const byte*& r) +{ + r += 4; + return read_int_le(4, r - 4); +} + +void Cxif_key::load_old(const byte*& data) +{ + for (int count = read_int(data); count--; ) + { + Cxif_key& i = set_key(read_int(data)); + i.load_old(data); + } + for (int count = read_int(data); count--; ) + { + Cxif_value& i = set_value(read_int(data)); + i.load_old(data); + } +} + +void Cxif_key::load_new(const byte*& data) +{ + for (int count = read_int(data), id = 0; count--; ) + { + id += read_int(data); + open_key_write(id).load_new(data); + } + for (int count = read_int(data), id = 0; count--; ) + { + id += read_int(data); + open_value_write(id).load_new(data); + } +} + +int Cxif_key::get_size() const +{ + int size = 8; + BOOST_FOREACH(t_xif_key_map::const_reference i, m_keys) + size += 4 + i.second.get_size(); + BOOST_FOREACH(t_xif_value_map::const_reference i, m_values) + { + size += 9; + switch (i.second.get_type()) + { + case vt_bin32: + case vt_int32: + break; + default: + size += i.second.get_size(); + } + } + return size; +} + +void Cxif_key::save(byte*& data) const +{ + { + data = write_int_le(4, data, m_keys.size()); + int id = 0; + BOOST_FOREACH(t_xif_key_map::const_reference i, m_keys) + { + data = write_int_le(4, data, i.first - id); + id = i.first; + i.second.save(data); + } + } + { + data = write_int_le(4, data, m_values.size()); + int id = 0; + BOOST_FOREACH(t_xif_value_map::const_reference i, m_values) + { + data = write_int_le(4, data, i.first - id); + id = i.first; + i.second.save(data); + } + } +} + +int Cxif_key::load_key(const byte* data, size_t size) +{ + const byte* read_p = data; + const t_xif_header_fast& header = *reinterpret_cast(read_p); + if (size < sizeof(t_xif_header_old) + || header.id != file_id + || header.version != file_version_old && header.version != file_version_new && header.version != file_version_fast) + return 1; + int error = 0; + if (header.version == file_version_old) + { + read_p += sizeof(t_xif_header_old) - 4; + load_old(read_p); + error = size != read_p - data; + } + else + { + unsigned long cb_d = header.size_uncompressed; + if (cb_d) + { + shared_data d(cb_d); + if (header.version == file_version_new) + error = Z_OK != uncompress(d.data(), &cb_d, data + sizeof(t_xif_header_old), size - sizeof(t_xif_header_old)); + else + error = Z_OK != uncompress(d.data(), &cb_d, data + sizeof(t_xif_header_fast), header.size_compressed); + if (!error) + { + read_p = d.data(); + load_new(read_p); + error = read_p != d.end(); + if (header.version == file_version_fast && !error) + { + read_p = data + sizeof(t_xif_header_fast) + header.size_compressed; + error = size != read_p - data; + } + } + } + else + { + read_p = data + (header.version == file_version_fast ? sizeof(t_xif_header_fast) : sizeof(t_xif_header_old)); + load_new(read_p); + error = size != read_p - data; + } + } + return error; +} + +shared_data Cxif_key::vdata() const +{ + int size = get_size(); + shared_data s(size); + byte* w = s.data(); + save(w); + unsigned long cb_d = s.size() + (s.size() + 999) / 1000 + 12; + shared_data d(sizeof(t_xif_header_fast) + cb_d); + t_xif_header_fast& header = *reinterpret_cast(d.data()); + compress(d.data() + sizeof(t_xif_header_fast), &cb_d, s.data(), s.size()); + w = d.data() + sizeof(t_xif_header_fast) + cb_d; + header.id = file_id; + header.version = file_version_fast; + header.size_uncompressed = size; + header.size_compressed = cb_d; + header.size_external = 0; + return d.substr(0, sizeof(t_xif_header_fast) + cb_d); +} diff --git a/misc/xif_key_r.cpp b/misc/xif_key_r.cpp new file mode 100644 index 0000000..960938c --- /dev/null +++ b/misc/xif_key_r.cpp @@ -0,0 +1,79 @@ +#include "stdafx.h" +#include "xbt/xif_key_r.h" + +#include +#include +#include +#include + +static int read_int(const byte*& r) +{ + r += 4; + return read_int_le(4, r - 4); +} + +int Cxif_key_r::import(data_ref s) +{ + const t_xif_header_fast& h = *reinterpret_cast(s.data()); + if (s.size() < sizeof(t_xif_header_fast) + 8 + || h.id != file_id + || h.version != file_version_fast) + return 1; + unsigned long cb_d = h.size_uncompressed; + if (cb_d) + { + shared_data d(cb_d); + if (Z_OK != uncompress(d.data(), &cb_d, &s[sizeof(t_xif_header_fast)], h.size_compressed)) + return 1; + load(d.data()); + } + else + { + load(&s[sizeof(t_xif_header_fast)]); + } + return 0; +} + +int Cxif_key_r::load(const byte* s) +{ + const byte* r = s; + { + int count = read_int(r); + int id = 0; + m_keys.reserve(count); + while (count--) + { + id += read_int(r); + m_keys.push_back(std::make_pair(id, Cxif_key_r())); + r += m_keys.rbegin()->second.load(r); + } + } + { + int count = read_int(r); + int id = 0; + m_values.reserve(count); + while (count--) + { + id += read_int(r); + m_values.push_back(std::make_pair(id, Cxif_value())); + m_values.rbegin()->second.load_new(r); + } + } + return r - s; +} + +const Cxif_key_r* Cxif_key_r::find_key(int id) const +{ + t_key_map::const_iterator i = keys().begin(); + while (i != keys().end() && i->first != id) + i++; + return i == keys().end() ? NULL : &i->second; +} + +const Cxif_value* Cxif_key_r::find_value(int id) const +{ + t_value_map::const_iterator i = values().begin(); + while (i != values().end() && i->first != id) + i++; + return i == values().end() ? NULL : &i->second; +} diff --git a/misc/xif_value.cpp b/misc/xif_value.cpp new file mode 100644 index 0000000..264a0e9 --- /dev/null +++ b/misc/xif_value.cpp @@ -0,0 +1,127 @@ +#include "stdafx.h" +#include "xbt/xif_value.h" + +#include +#include "stream_int.h" + +static float read_float(const byte*& r) +{ + assert(sizeof(float) == 4); + float v; + memcpy(&v, r, 4); + r += 4; + return v; +} + +static int read_int(const byte*& r) +{ + r += 4; + return read_int_le(4, r - 4); +} + +t_vt Cxif_value::get_type() const +{ + if (m_type != vt_unknown) + return m_type; + const byte* data = m_data.data(); + if (!data) + return vt_binary; + int size = m_data.size(); + if (!data[size - 1]) + { + const byte* r = data; + int c = size - 1; + while (c--) + { + if (*r != 9 && *r < 0x20) + break; + r++; + } + if (c == -1) + return vt_string; + } + if (size == 4) + return vt_int32; + return vt_binary; +} + +void Cxif_value::load_old(const byte*& data) +{ + m_data.clear(); + int size = read_int(data); + if (size == 4) + memcpy(m_value, data, size); + m_data = make_shared_data(data, size); + data += size; + m_type = vt_unknown; + m_type = get_type(); +} + +void Cxif_value::load_new(const byte*& data) +{ + m_data.clear(); + m_type = static_cast(*data++); + switch (m_type) + { + case vt_bin32: + case vt_int32: + m_value_int = read_int(data); + break; + case vt_float: + m_value_float = read_float(data); + break; + case vt_external_binary: + m_data = shared_data(read_int(data)); + break; + default: + { + int size = read_int(data); + m_data = make_shared_data(data, size); + data += size; + } + } +} + +int Cxif_value::skip(const byte* s) +{ + const byte* r = s; + t_vt type = static_cast(*r++); + switch (type) + { + case vt_bin32: + case vt_int32: + read_int(r); + break; + case vt_float: + read_float(r); + break; + case vt_external_binary: + read_int(r); + break; + default: + r += read_int(r); + } + return r - s; +} + +void Cxif_value::save(byte*& data) const +{ + *data++ = m_type; + switch (m_type) + { + case vt_bin32: + case vt_int32: + data = write_int_le(4, data, get_int()); + break; + case vt_float: + data = write_float(data, get_float()); + break; + default: + { + int size = get_size(); + data = write_int_le(4, data, size); + memcpy(data, get_data(), size); + data += size; + } + } +}