├── .gitignore ├── .travis.yml ├── .travis ├── bitlbee.sh └── obs.sh ├── AUTHORS ├── COPYING ├── ChangeLog ├── Makefile.am ├── NEWS ├── README ├── README.md ├── autogen.sh ├── configure.ac ├── debian ├── changelog ├── compat ├── control ├── copyright └── rules └── steam ├── Makefile.am ├── steam-api.c ├── steam-api.h ├── steam-crypt.c ├── steam-crypt.h ├── steam-glib.h ├── steam-http.c ├── steam-http.h ├── steam-id.h ├── steam-json.c ├── steam-json.h ├── steam-user.c ├── steam-user.h ├── steam-util.c ├── steam-util.h ├── steam.c └── steam.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.la 2 | *.lo 3 | *.o 4 | *.tar.* 5 | .deps 6 | .libs 7 | aclocal.m4 8 | autom4te.cache 9 | build-aux 10 | config.log 11 | config.status 12 | configure 13 | debian 14 | INSTALL 15 | libtool 16 | libtool.m4 17 | lt*.m4 18 | Makefile 19 | Makefile.in 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | notifications: 4 | email: false 5 | 6 | os: 7 | - linux 8 | 9 | compiler: 10 | - gcc 11 | 12 | env: 13 | global: 14 | - secure: "SoxDnL6a7OVhPX6hcjzwQjzW1+gGfJ0H36/BRzJvBKarZI4P9q2cArSpwWK4IGWLTWAzuKHb37uoZflY0xpbcwc0eiG6OtBiOTlL4Zbj6kc5XTbywAw/THRrRAE+6Y3xQ5UhygOFEffVj83cfDM9YuWobSO8Xx9mU6rraGCSVpk=" 15 | - secure: "kZVvfKPjdoRzln1KMEglvgMFljd90YPtNDwQNpyu2aQ6zv6pkGUbtIni3kz8gDeirJ6eMQprbnH9n8olVh34bDIi+VC9wrneT58LQ83V6YFKicA34fHcUgXLiol7Cd5kDgEb/WuLXUnY3/p0znewmp98M2rZy5INAhE46vvdH0M=" 16 | - secure: "Xz4TLsK/LCjMzIPjl2JHsROdjLAgwfJQhxxnvLAceF4NI0qU8MRTu/QObPzzU+TnqLBOqFJNwB5EDnTnbUyB9SiEFlOu1YkskVTkmQFA/KcZxNwww/U7O/ecUWfxsaaBgkwXh9fLXm4gJN/KLUZrzkjZGpUryjMqWJRFTL9JRyA=" 17 | - COVERITY_SCAN_PROJECT_NAME=jgeboski/bitlbee-steam 18 | - COVERITY_SCAN_NOTIFICATION_EMAIL=nope 19 | - COVERITY_SCAN_BUILD_COMMAND=make 20 | - COVERITY_SCAN_BRANCH_PATTERN=coverity_scan 21 | - MY_DEPLOY_BRANCH=master 22 | 23 | before_install: 24 | - echo "deb http://download.opensuse.org/repositories/openSUSE:/Tools/xUbuntu_$(lsb_release -rs) ./" 25 | | sudo tee /etc/apt/sources.list.d/suse.list 26 | - curl -s "http://download.opensuse.org/repositories/openSUSE:/Tools/xUbuntu_$(lsb_release -rs)/Release.key" 27 | | sudo apt-key add - 28 | - sudo apt-get update -qq 29 | - sudo apt-get install -qq 30 | --no-install-recommends 31 | clang 32 | osc 33 | 34 | install: 35 | - .travis/bitlbee.sh 36 | 37 | script: 38 | - CFLAGS="-Werror" ./autogen.sh --enable-warnings 39 | - make all clean 40 | - scan-build -k --status-bugs make all clean 41 | 42 | after_success: 43 | - curl -s "https://scan.coverity.com/scripts/travisci_build_coverity_scan.sh" | bash || true 44 | - .travis/obs.sh 45 | -------------------------------------------------------------------------------- /.travis/bitlbee.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | git clone https://github.com/bitlbee/bitlbee /tmp/bitlbee 5 | cd /tmp/bitlbee 6 | 7 | ./configure \ 8 | --events=glib \ 9 | --ssl=gnutls \ 10 | --doc=0 \ 11 | --jabber=0 \ 12 | --msn=0 \ 13 | --oscar=0 \ 14 | --twitter=0 \ 15 | --yahoo=0 16 | 17 | make 18 | sudo make install install-dev 19 | -------------------------------------------------------------------------------- /.travis/obs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ "${TRAVIS_PULL_REQUEST}" == "false" -a \ 4 | "${TRAVIS_BRANCH}" == "${MY_DEPLOY_BRANCH}" \ 5 | ] || exit 6 | set -e 7 | 8 | FULLVERS="$(date +%Y%m%d)~$(git rev-parse --short=7 HEAD)~${TRAVIS_BUILD_NUMBER}" 9 | FULLDATE=$(date -R) 10 | REPONAME=$(basename "${TRAVIS_REPO_SLUG}") 11 | 12 | git reset -q --hard 13 | git clean -dfqx 14 | 15 | sed -ri \ 16 | -e "18 s/^(\s+).*(,)\$/\1\[${FULLVERS}\]\2/" \ 17 | -e "s|^PKG_CHECK_MODULES\(\[BITLBEE\].*|plugindir=/usr/lib/bitlbee|" \ 18 | configure.ac 19 | sed -ri \ 20 | -e "s/bitlbee-dev \([^\(\)]+\),?\s*//" \ 21 | debian/control 22 | 23 | cat < debian/changelog 24 | ${REPONAME} (${FULLVERS}) UNRELEASED; urgency=medium 25 | 26 | * Updated to ${FULLVERS}. 27 | 28 | -- Travis CI ${FULLDATE} 29 | EOF 30 | 31 | cat < ~/.oscrc 32 | [general] 33 | apiurl = https://api.opensuse.org 34 | [https://api.opensuse.org] 35 | user = ${OBSUSER} 36 | pass = ${OBSPASS} 37 | EOF 38 | 39 | mkdir -p m4 40 | cp /usr/local/include/bitlbee/*.h steam 41 | osc checkout "home:${OBSUSER}" "${REPONAME}" -o /tmp/obs 42 | 43 | ( 44 | cd /tmp/obs 45 | rm -f *.{dsc,tar.gz} 46 | dpkg-source -I -b "${TRAVIS_BUILD_DIR}" 47 | 48 | osc addremove -r 49 | osc commit -m "Updated to ${FULLVERS}" 50 | ) 51 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | James Geboski 2 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | bitlbee-steam-1.4.2 (2016-07-29): 2 | - Added support for bitlbee's new plugin information 3 | - Fixed certain requests failing due to improper cookies 4 | - Fixed improper debian dependencies 5 | - Fixed OTR being disabled 6 | - Improved the README documentation 7 | - Various code refactoring and improvements 8 | 9 | bitlbee-steam-1.4.1 (2016-01-27): 10 | - Fixed the missing steam-glib.h in the distribution tarball 11 | 12 | bitlbee-steam-1.4.0 (2016-01-15): 13 | - Added cleaner debugging utilities 14 | - Added Coverity support 15 | - Added support for the NOCONFIGURE variable to the autogen.sh script 16 | - Added the --enable-warnings option to the configure script 17 | - Dropped the required GLib version to 2.28.0 18 | - Fixed segfault when fetching aliases 19 | - Fixed broken message queuing 20 | - Fixed failing Steam Community requests 21 | - Fixed libgcrypt not being properly initialized 22 | - Fixed the game state not taking precedence over the away state 23 | - Implemented self message support 24 | - Implemented support for SteamGuard mobile 2FA 25 | - Make Travis CI build against bitlbee's master branch 26 | - Migrated to the newer OAuth chat history API 27 | - Migrated OBS packaging to Travis CI 28 | - Removed the --enable-debug option from the configure script 29 | - Various code refactoring and restyling 30 | 31 | bitlbee-steam-1.3.1 (2015-08-18): 32 | - Fixed gcrypt memory leak 33 | - Fixed OAuth data failing to parse 34 | - Reimplemented marking message as read 35 | 36 | bitlbee-steam-1.3.0 (2015-08-12): 37 | - Added debian support 38 | - Added information to the README for "Limited User Accounts" 39 | - Fixed failing logins with valid credentials 40 | - Fixed harmless libtool warnings 41 | - Fixed the usage of AC_REQUIRE() in configure.ac 42 | - Markdownified the README 43 | - Migrated to the newer chat history API (fixes failing logins) 44 | - Use BEE_USER_SPECIAL for denoting the game play state 45 | - Use the recommended build-aux directly for configure.ac 46 | 47 | bitlbee-steam-1.2.0 (2015-03-05): 48 | - Added cleaner debugging output 49 | - Added compile-time warnings for string security 50 | - Added "Last Online" field to "Status" field in info display 51 | - Added setting of user states: Online, Away, and Snooze 52 | - Added Travis CI support 53 | - Fixed authentication requests failing 54 | - Fixed pkg-config not supporting the plugindir variable 55 | - Fixed several issues in autogen.sh 56 | - Fixed tarballs generated by 'make dist' 57 | - Migrated to libgcrypt from internal crypto implementation 58 | - Refactored several things for better control flow and readability 59 | - Removed runtime check for missing macros in configure 60 | - Required bitlbee version bumped to 3.2.2 61 | 62 | bitlbee-steam-1.1.1 (2014-08-25): 63 | - Fixed information requests not populating 64 | - Fixed potential NULL pointer dereference with user statuses 65 | - Link all libraries which are required 66 | - Refactored the XML entity unescaper 67 | 68 | bitlbee-steam-1.1.0 (2014-07-27): 69 | - Added denotation of non-Steam games 70 | - Added internal documentation of the plugin 71 | - Added last online time to the information output 72 | - Added previous nicknames to the information output 73 | - Added user states to the information output 74 | - Fixed custom profile URLs causing request failures 75 | - Fixed missing sessionid after authentication 76 | - Fixed several control flow, overflow, and memory issues 77 | - Fixed storage issues of HTTP cookies, headers, and parameters 78 | - Fixed XML entities not being replaced with their characters 79 | - Refactored several things for better control flow and readability 80 | - Reorganized the information output 81 | - Required GLib version bumped to 2.32.0 82 | 83 | bitlbee-steam-1.0.1 (2014-04-27): 84 | - Added --enable-minimal-flags option to autoconf 85 | - Fixed a segfault when setting `show_playing` 86 | - Fixed channel modes not being forced and overridden 87 | - Fixed freeing functions not gracefully handling NULL pointers 88 | - Fixed the handling of unsupported poll messages 89 | - Fixed UI mode switching to mobile upon hidden relogons 90 | - Improved the parsing and storage of SteamIDs/CommunityIDs 91 | - Reverted the ignoring of CFLAGS when debugging is enabled 92 | 93 | bitlbee-steam-1.0.0 (2014-03-03): 94 | - Initial release 95 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | ACLOCAL_AMFLAGS = -Im4 2 | EXTRA_DIST = autogen.sh 3 | SUBDIRS = steam 4 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitlbee/bitlbee-steam/49d46c82c283f90b8ecab1e9d169ca61fd8b96da/NEWS -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | The Steam protocol plugin for bitlbee. This plugin uses the Steam Mobile 2 | API allowing it to run alongside the main Steam client. It is worth 3 | noting that the Steam Mobile API is HTTP based, which does lead to mild 4 | latency. 5 | 6 | ## Package Repositories 7 | 8 | There are package repositories available for various distributions. 9 | 10 | See: https://jgeboski.github.io 11 | 12 | ## Building and Installing 13 | 14 | Make sure bitlbee and its headers have been installed. If bitlbee came 15 | from the distribution's repository, it will most likely need the 16 | development package, usually bitlbee-dev. 17 | 18 | If bitlbee was built by hand (or alike via a script), ensure the make 19 | target `install-dev` is invoked. This target is not called by default, 20 | and will install the headers that are needed. 21 | 22 | Do *not* use the source tree headers unless you know what you are 23 | doing. This can lead to mismatched header versions, which often times 24 | will lead to bad things. 25 | 26 | $ git clone https://github.com/jgeboski/bitlbee-steam.git 27 | $ cd bitlbee-steam 28 | 29 | With a "global" (or system) bitlbee installation: 30 | 31 | $ ./autogen.sh 32 | $ make 33 | $ make install 34 | 35 | Or with a "local" bitlbee installation (location: $HOME/bitlbee): 36 | 37 | $ export BITLBEE_CFLAGS="-I$HOME/bitlbee/include/bitlbee" 38 | $ export BITLBEE_LIBS="" 39 | $ ./autogen.sh --libdir=$HOME/bitlbee/lib 40 | $ make 41 | $ make install 42 | 43 | ## Usage 44 | 45 | Before continuing, please note, any account which is a "Limited User 46 | Account" will not work with this plugin. These limited accounts have 47 | many features negated from them, which are required for this plugin 48 | to function correctly as a chat client. Please, do not file issues in 49 | the tracker if your account is a limited account. 50 | 51 | See: https://support.steampowered.com/kb_article.php?ref=3330-IAGK-7663 52 | 53 | Getting started: 54 | 55 | > account add steam 56 | > account steam on 57 | 58 | Authenticating with SteamGuard: 59 | 60 | > account steam set authcode 61 | 62 | Captcha interaction may be required: 63 | 64 | > account steam set captcha 65 | 66 | Output game play statues to the account channel(s): 67 | 68 | > account steam set game_status true 69 | 70 | ## Common Issues 71 | 72 | Below is a list of common issues and their respective fixes. 73 | 74 | ### Expired token or session 75 | 76 | When the token or session identifier has expired, the user may see 77 | errors thrown by the plugin. Sometimes these errors are one time, but 78 | if they repeatedly and reliably happen, then a new token and session 79 | identifier need to be obtained. 80 | 81 | ``` 82 | Login error: HTTP: 401 Unauthorized 83 | ``` 84 | 85 | Fixing this issue is done by forcing the plugin to re-authenticate: 86 | 87 | ``` 88 | account steam off 89 | account steam set -del token 90 | account steam on 91 | ``` 92 | 93 | The account can also be deleted and re-added, but this will cause all 94 | other local metadata to be lost, such as local buddy aliases. 95 | 96 | ## Debugging 97 | 98 | One of the two supported environment variables can be defined to enable 99 | debugging output. This can be used in unison with debuggers such as 100 | GDB, which should enable easier tracing of bugs. 101 | 102 | When posting to the issue tracker, please ensure any sensitive 103 | information has been stripped. 104 | 105 | For bitlbee and the plugin: 106 | 107 | $ export BITLBEE_DEBUG=1 108 | OR 109 | $ BITLBEE_DEBUG=1 gdb ... 110 | 111 | For just the plugin: 112 | 113 | $ export BITLBEE_DEBUG_STEAM=1 114 | OR 115 | $ BITLBEE_DEBUG_STEAM=1 gdb ... 116 | 117 | Obtaining a GDB backtrace: 118 | 119 | $ gdb \ 120 | -ex 'handle SIGPIPE nostop noprint pass' \ 121 | -ex 'break g_log' -ex run -ex bt \ 122 | --args /usr/sbin/bitlbee -Dnvc /etc/bitlbee/bitlbee.conf 123 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | README -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | test -z "$srcdir" && srcdir=$(dirname "$0") 4 | test -z "$srcdir" && srcdir=. 5 | 6 | cwd=$(pwd) 7 | cd "$srcdir" 8 | 9 | mkdir -p m4 10 | autoreconf --verbose --force --install || exit $? 11 | 12 | cd "$cwd" 13 | test -z "$NOCONFIGURE" && "$srcdir/configure" $@ 14 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # Copyright 2012-2016 James Geboski 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 2 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | AC_INIT( 17 | [bitlbee-steam], 18 | [1.4.2], 19 | [https://github.com/jgeboski/bitlbee-steam/issues], 20 | [bitlbee-steam], 21 | [https://github.com/jgeboski/bitlbee-steam], 22 | [] 23 | ) 24 | 25 | AC_CONFIG_AUX_DIR([build-aux]) 26 | AC_CONFIG_MACRO_DIR([m4]) 27 | AM_INIT_AUTOMAKE([no-define]) 28 | 29 | AC_PROG_CC 30 | AM_PROG_CC_C_O 31 | 32 | AC_DISABLE_STATIC 33 | AC_PROG_LIBTOOL 34 | 35 | m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) 36 | m4_ifdef([AC_PROG_CC_C99], [AC_PROG_CC_C99]) 37 | 38 | # Define PKG_CHECK_VAR() for pkg-config < 0.28 39 | m4_define_default( 40 | [PKG_CHECK_VAR], 41 | [AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config]) 42 | AS_IF([test -z "$$1"], [$1=`$PKG_CONFIG --variable="$3" "$2"`]) 43 | AS_IF([test -n "$$1"], [$4], [$5])] 44 | ) 45 | 46 | AC_ARG_ENABLE( 47 | [warnings], 48 | [AS_HELP_STRING( 49 | [--enable-warnings], 50 | [Enable additional compile-time (GCC) warnings] 51 | )], 52 | [WARNINGS="$enableval"], 53 | [WARNINGS="no"] 54 | ) 55 | 56 | AS_IF( 57 | [test "x$WARNINGS" = "xyes"], 58 | [CFLAGS="$CFLAGS -Wall -Wextra \ 59 | -Waggregate-return \ 60 | -Wdeclaration-after-statement \ 61 | -Wfloat-equal \ 62 | -Wformat \ 63 | -Winit-self \ 64 | -Wmissing-declarations \ 65 | -Wmissing-prototypes \ 66 | -Wno-unused-parameter \ 67 | -Wpointer-arith"] 68 | ) 69 | 70 | AC_ARG_WITH( 71 | [plugindir], 72 | [AS_HELP_STRING( 73 | [--with-plugindir], 74 | [BitlBee plugin directory] 75 | )], 76 | [plugindir="$withval"] 77 | ) 78 | 79 | AM_PATH_LIBGCRYPT([1.5.0]) 80 | PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.28.0]) 81 | PKG_CHECK_MODULES([BITLBEE], [bitlbee >= 3.4]) 82 | 83 | AS_IF( 84 | [test -z "$plugindir"], 85 | [PKG_CHECK_VAR( 86 | [BITLBEE_PLUGINDIR], 87 | [bitlbee], 88 | [plugindir], 89 | [plugindir="$BITLBEE_PLUGINDIR"], 90 | [plugindir="$libdir/bitlbee"] 91 | )] 92 | ) 93 | 94 | AC_CONFIG_FILES([Makefile steam/Makefile]) 95 | AC_SUBST([plugindir]) 96 | AC_OUTPUT 97 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | bitlbee-steam (0.0.0-1) UNRELEASED; urgency=medium 2 | 3 | * Initial debian support. 4 | 5 | -- jgeboski Thu, 01 Jan 1970 00:00:00 +0000 6 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: bitlbee-steam 2 | Maintainer: jgeboski 3 | Section: misc 4 | Priority: optional 5 | Standards-Version: 3.9.6 6 | Build-Depends: bitlbee-dev (>= 3.4), debhelper (>= 9), dh-autoreconf, libgcrypt11-dev (>= 1.5.0) | libgcrypt20-dev, libglib2.0-dev (>= 2.28) 7 | Homepage: https://github.com/jgeboski/bitlbee-steam 8 | 9 | Package: bitlbee-steam 10 | Architecture: any 11 | Section: misc 12 | Priority: optional 13 | Depends: ${shlibs:Depends}, ${misc:Depends}, bitlbee (>= 3.4) | bitlbee-libpurple (>= 3.4), libgcrypt11 (>= 1.5.0) | libgcrypt20, libglib2.0-0 (>= 2.28) 14 | Homepage: https://github.com/jgeboski/bitlbee-steam 15 | Description: Steam protocol plugin for BitlBee 16 | BitlBee Steam implements the Steam API into bitlbee. This plugin uses 17 | the Steam Mobile API allowing it to run alongside the main Steam 18 | client. It is worth noting that the Steam Mobile API is HTTP based, 19 | which does lead to mild latency. 20 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: bitlbee-steam 3 | Source: https://github.com/jgeboski/bitlbee-steam 4 | 5 | Files: * 6 | Copyright: Copyright 2012-2016 James Geboski 7 | License: GPL-2+ 8 | This program is free software; you can redistribute it 9 | and/or modify it under the terms of the GNU General Public 10 | License as published by the Free Software Foundation; either 11 | version 2 of the License, or (at your option) any later 12 | version. 13 | . 14 | This program is distributed in the hope that it will be 15 | useful, but WITHOUT ANY WARRANTY; without even the implied 16 | warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 17 | PURPOSE. See the GNU General Public License for more 18 | details. 19 | . 20 | You should have received a copy of the GNU General Public 21 | License along with this package; if not, write to the Free 22 | Software Foundation, Inc., 51 Franklin St, Fifth Floor, 23 | Boston, MA 02110-1301 USA 24 | . 25 | On Debian systems, the full text of the GNU General Public 26 | License version 2 can be found in the file 27 | `/usr/share/common-licenses/GPL-2'. 28 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | override_dh_auto_install: 4 | dh_auto_install 5 | find debian -name '*.la' -print -delete 6 | 7 | %: 8 | dh $@ --with autoreconf 9 | -------------------------------------------------------------------------------- /steam/Makefile.am: -------------------------------------------------------------------------------- 1 | libdir = $(plugindir) 2 | lib_LTLIBRARIES = steam.la 3 | 4 | steam_la_CFLAGS = \ 5 | $(BITLBEE_CFLAGS) \ 6 | $(GLIB_CFLAGS) \ 7 | $(LIBGCRYPT_CFLAGS) 8 | 9 | steam_la_LDFLAGS = \ 10 | $(BITLBEE_LIBS) \ 11 | $(GLIB_LIBS) \ 12 | $(LIBGCRYPT_LIBS) 13 | 14 | steam_la_SOURCES = \ 15 | steam.c \ 16 | steam.h \ 17 | steam-api.c \ 18 | steam-api.h \ 19 | steam-crypt.c \ 20 | steam-crypt.h \ 21 | steam-glib.h \ 22 | steam-http.c \ 23 | steam-http.h \ 24 | steam-id.h \ 25 | steam-json.c \ 26 | steam-json.h \ 27 | steam-user.c \ 28 | steam-user.h \ 29 | steam-util.c \ 30 | steam-util.h 31 | 32 | # Build the library as a module 33 | steam_la_LDFLAGS += -module -avoid-version 34 | -------------------------------------------------------------------------------- /steam/steam-api.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2016 James Geboski 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include 19 | #include 20 | 21 | #include "steam-api.h" 22 | #include "steam-crypt.h" 23 | #include "steam-http.h" 24 | #include "steam-json.h" 25 | #include "steam-util.h" 26 | 27 | GQuark 28 | steam_api_error_quark(void) 29 | { 30 | static GQuark q; 31 | 32 | if (G_UNLIKELY(q == 0)) { 33 | q = g_quark_from_static_string("steam-api-error-quark"); 34 | } 35 | 36 | return q; 37 | } 38 | 39 | SteamApi * 40 | steam_api_new(void) 41 | { 42 | SteamApi *api; 43 | 44 | api = g_new0(SteamApi, 1); 45 | api->info = steam_user_info_new(0); 46 | api->http = steam_http_new(STEAM_API_AGENT); 47 | api->msgs = g_queue_new(); 48 | 49 | return api; 50 | } 51 | 52 | void 53 | steam_api_free_auth(SteamApi *api) 54 | { 55 | if (G_UNLIKELY(api == NULL)) { 56 | return; 57 | } 58 | 59 | g_free(api->pktime); 60 | g_free(api->pkexp); 61 | g_free(api->pkmod); 62 | g_free(api->esid); 63 | g_free(api->cgid); 64 | 65 | api->pktime = NULL; 66 | api->pkexp = NULL; 67 | api->pkmod = NULL; 68 | api->esid = NULL; 69 | api->cgid = NULL; 70 | } 71 | 72 | void 73 | steam_api_free(SteamApi *api) 74 | { 75 | if (G_UNLIKELY(api == NULL)) { 76 | return; 77 | } 78 | 79 | g_queue_free_full(api->msgs, (GDestroyNotify) steam_api_req_free); 80 | 81 | steam_http_free(api->http); 82 | steam_user_info_free(api->info); 83 | steam_api_free_auth(api); 84 | 85 | g_free(api->sessid); 86 | g_free(api->token); 87 | g_free(api->umqid); 88 | g_free(api); 89 | } 90 | 91 | gchar * 92 | steam_api_captcha_url(const gchar *cgid) 93 | { 94 | g_return_val_if_fail(cgid != NULL, NULL); 95 | 96 | return g_strdup_printf("https://%s%s?gid=%s", STEAM_COM_HOST, 97 | STEAM_COM_PATH_CAPTCHA, cgid); 98 | } 99 | 100 | void 101 | steam_api_rehash(SteamApi *api) 102 | { 103 | gchar *str; 104 | 105 | g_return_if_fail(api != NULL); 106 | 107 | if (api->umqid == NULL) { 108 | api->umqid = g_strdup_printf("%" G_GUINT32_FORMAT, g_random_int()); 109 | } 110 | 111 | if ((api->info->id != 0) && (api->token != NULL)) { 112 | str = g_strdup_printf("%" STEAM_ID_FORMAT "||oauth:%s", 113 | api->info->id, api->token); 114 | 115 | steam_http_cookies_set(api->http, 116 | STEAM_HTTP_PAIR("steamLogin", str), 117 | NULL 118 | ); 119 | 120 | g_free(str); 121 | } 122 | 123 | steam_http_cookies_set(api->http, 124 | STEAM_HTTP_PAIR("forceMobile", "1"), 125 | STEAM_HTTP_PAIR("mobileClient", STEAM_API_CLIENT), 126 | STEAM_HTTP_PAIR("mobileClientVersion", STEAM_API_CLIENT_VERSION), 127 | STEAM_HTTP_PAIR("sessionid", api->sessid), 128 | NULL 129 | ); 130 | } 131 | 132 | static void 133 | steam_api_json_error(SteamApiReq *req, const json_value *json) 134 | { 135 | const gchar *str; 136 | gboolean bool; 137 | gint64 in; 138 | guint errc; 139 | 140 | if (steam_json_str_chk(json, "error", &str)) { 141 | if (g_ascii_strcasecmp(str, "OK") == 0) { 142 | return; 143 | } 144 | 145 | if (g_ascii_strcasecmp(str, "Timeout") == 0) { 146 | return; 147 | } 148 | 149 | if (g_ascii_strcasecmp(str, "Not Logged On") == 0) { 150 | req->api->online = FALSE; 151 | errc = STEAM_API_ERROR_EXPRIED; 152 | str = "Session expired"; 153 | } else { 154 | errc = STEAM_API_ERROR_GENERAL; 155 | } 156 | 157 | g_set_error(&req->err, STEAM_API_ERROR, errc, "%s", str); 158 | return; 159 | } 160 | 161 | if (steam_json_bool_chk(json, "success", &bool) && !bool) { 162 | if (steam_json_bool_chk(json, "captcha_needed", &bool) && bool) { 163 | return; 164 | } 165 | 166 | if (steam_json_bool_chk(json, "emailauth_needed", &bool) && bool) { 167 | return; 168 | } 169 | 170 | if (steam_json_bool_chk(json, "requires_twofactor", &bool) && bool) { 171 | return; 172 | } 173 | 174 | if (!steam_json_str_chk(json, "message", &str)) { 175 | str = "Unknown error"; 176 | } 177 | 178 | g_set_error(&req->err, STEAM_API_ERROR, STEAM_API_ERROR_UNKNOWN, 179 | "%s", str); 180 | return; 181 | } 182 | 183 | if (steam_json_int_chk(json, "sectimeout", &in) && 184 | (in < STEAM_API_TIMEOUT)) 185 | { 186 | g_set_error(&req->err, STEAM_API_ERROR, STEAM_API_ERROR_GENERAL, 187 | "Timeout of %" G_GINT64_FORMAT " too low", in); 188 | return; 189 | } 190 | } 191 | 192 | static void 193 | steam_api_json_user_info(SteamUserInfo *info, const json_value *json) 194 | { 195 | const gchar *str; 196 | const gchar *tmp; 197 | gint64 in; 198 | 199 | if (steam_json_str_chk(json, "gameextrainfo", &str)) { 200 | g_free(info->game); 201 | 202 | if (steam_json_str_chk(json, "gameid", &tmp)) { 203 | info->game = g_strdup(str); 204 | } else { 205 | info->game = g_strdup_printf("Non-Steam: %s", str); 206 | } 207 | } 208 | 209 | if (steam_json_str_chk(json, "gameserverip", &str)) { 210 | g_free(info->server); 211 | info->server = g_strdup(str); 212 | } 213 | 214 | if (steam_json_str_chk(json, "personaname", &str)) { 215 | g_free(info->nick); 216 | info->nick = g_strdup(str); 217 | } 218 | 219 | if (steam_json_str_chk(json, "profileurl", &str)) { 220 | g_free(info->profile); 221 | info->profile = g_strdup(str); 222 | } 223 | 224 | if (steam_json_str_chk(json, "realname", &str)) { 225 | g_free(info->fullname); 226 | info->fullname = g_strdup(str); 227 | } 228 | 229 | if (steam_json_int_chk(json, "lastlogoff", &in)) { 230 | info->ltime = in; 231 | } 232 | 233 | if (steam_json_int_chk(json, "personastate", &in)) { 234 | info->state = in; 235 | } 236 | 237 | if (steam_json_int_chk(json, "personastateflags", &in)) { 238 | info->flags = in; 239 | } 240 | } 241 | 242 | SteamApiReq * 243 | steam_api_req_new(SteamApi *api, SteamApiFunc func, gpointer data) 244 | { 245 | SteamApiReq *req; 246 | 247 | g_return_val_if_fail(api != NULL, NULL); 248 | 249 | req = g_new0(SteamApiReq, 1); 250 | req->api = api; 251 | req->func = func; 252 | req->data = data; 253 | req->msgs = g_queue_new(); 254 | req->infs = g_queue_new(); 255 | req->infr = g_queue_new(); 256 | 257 | return req; 258 | } 259 | 260 | SteamApiReq * 261 | steam_api_req_fwd(SteamApiReq *req) 262 | { 263 | SteamApiReq *deq; 264 | 265 | g_return_val_if_fail(req != NULL, NULL); 266 | 267 | deq = g_memdup(req, sizeof *req); 268 | deq->flags = 0; 269 | deq->req = NULL; 270 | deq->punc = NULL; 271 | 272 | req->err = NULL; 273 | req->func = NULL; 274 | req->data = NULL; 275 | req->msgs = g_queue_new(); 276 | req->infs = g_queue_new(); 277 | req->infr = g_queue_new(); 278 | 279 | return deq; 280 | } 281 | 282 | void 283 | steam_api_req_free(SteamApiReq *req) 284 | { 285 | GHashTable *tbl; 286 | GList *l; 287 | GList *n; 288 | SteamUserMsg *msg; 289 | 290 | if (G_UNLIKELY(req == NULL)) { 291 | return; 292 | } 293 | 294 | tbl = g_hash_table_new(g_direct_hash, g_direct_equal); 295 | 296 | for (l = req->msgs->head; l != NULL; l = l->next) { 297 | msg = l->data; 298 | g_hash_table_replace(tbl, msg->info, msg->info); 299 | } 300 | 301 | for (l = req->infs->head; l != NULL; l = n) { 302 | n = l->next; 303 | 304 | if (g_hash_table_lookup_extended(tbl, l->data, NULL, NULL)) { 305 | g_queue_delete_link(req->infs, l); 306 | } 307 | } 308 | 309 | g_queue_free_full(req->infs, (GDestroyNotify) steam_user_info_free); 310 | g_queue_free_full(req->msgs, (GDestroyNotify) steam_user_msg_free); 311 | 312 | g_queue_free(req->infr); 313 | g_hash_table_destroy(tbl); 314 | 315 | if (req->err != NULL) { 316 | g_error_free(req->err); 317 | } 318 | 319 | g_free(req); 320 | } 321 | 322 | static void 323 | steam_api_req_cb(SteamHttpReq *heq, gpointer data) 324 | { 325 | json_value *json = NULL; 326 | SteamApiReq *req = data; 327 | 328 | req->req = heq; 329 | 330 | if (G_LIKELY(req->err == NULL)) { 331 | if (heq->err != NULL) { 332 | g_propagate_error(&req->err, heq->err); 333 | heq->err = NULL; 334 | } 335 | 336 | if (!(req->flags & STEAM_API_REQ_FLAG_NOJSON) && (req->err == NULL)) { 337 | json = steam_json_new(heq->body, heq->body_size, &req->err); 338 | 339 | if (req->err == NULL) { 340 | steam_api_json_error(req, json); 341 | } 342 | } 343 | 344 | if ((req->punc != NULL) && (req->err == NULL)) { 345 | req->punc(req, json); 346 | } 347 | 348 | if (json != NULL) { 349 | json_value_free(json); 350 | } 351 | } 352 | 353 | if (req->func != NULL) { 354 | g_queue_remove(req->infs, req->api->info); 355 | req->func(req, req->data); 356 | } 357 | 358 | steam_api_req_free(req); 359 | } 360 | 361 | void 362 | steam_api_req_init(SteamApiReq *req, const gchar *host, const gchar *path) 363 | { 364 | SteamApi *api = req->api; 365 | SteamHttpReq *heq; 366 | 367 | g_return_if_fail(req != NULL); 368 | g_return_if_fail(api != NULL); 369 | g_return_if_fail(host != NULL); 370 | g_return_if_fail(path != NULL); 371 | 372 | heq = steam_http_req_new(api->http, host, 443, path, steam_api_req_cb, req); 373 | heq->flags = STEAM_HTTP_REQ_FLAG_SSL; 374 | req->req = heq; 375 | } 376 | 377 | static void 378 | steam_api_cb_user_info_req(SteamApiReq *req, const json_value *json) 379 | { 380 | req = steam_api_req_fwd(req); 381 | steam_api_req_user_info(req); 382 | } 383 | 384 | static void 385 | steam_api_cb_auth_finish(SteamApiReq *req, const json_value *json) 386 | { 387 | const gchar *str; 388 | 389 | steam_http_cookies_parse_req(req->api->http, req->req); 390 | str = steam_http_cookies_get(req->api->http, "sessionid"); 391 | 392 | if (str == NULL) { 393 | g_set_error(&req->err, STEAM_API_ERROR, STEAM_API_ERROR_GENERAL, 394 | "Failed to obtain sessionid"); 395 | return; 396 | } 397 | 398 | g_free(req->api->sessid); 399 | req->api->sessid = g_strdup(str); 400 | } 401 | 402 | static void 403 | steam_api_cb_auth_rdir(SteamApiReq *req, const json_value *json) 404 | { 405 | req = steam_api_req_fwd(req); 406 | req->punc = steam_api_cb_auth_finish; 407 | steam_api_req_init(req, STEAM_COM_HOST, "/"); 408 | 409 | req->flags |= STEAM_API_REQ_FLAG_NOJSON; 410 | steam_http_req_send(req->req); 411 | } 412 | 413 | static void 414 | steam_api_cb_auth(SteamApiReq *req, const json_value *json) 415 | { 416 | const gchar *str; 417 | gboolean bln; 418 | gchar *val; 419 | guint errc; 420 | guint i; 421 | json_value *jp; 422 | json_value *jv; 423 | 424 | if (steam_json_bool_chk(json, "success", &bln) && !bln) { 425 | if (steam_json_bool_chk(json, "requires_twofactor", &bln) && bln) { 426 | req->api->autht = STEAM_API_AUTH_TYPE_MOBILE; 427 | errc = STEAM_API_ERROR_STEAMGUARD; 428 | } else if (steam_json_bool_chk(json, "emailauth_needed", &bln) && bln) { 429 | req->api->autht = STEAM_API_AUTH_TYPE_EMAIL; 430 | errc = STEAM_API_ERROR_STEAMGUARD; 431 | str = steam_json_str(json, "emailsteamid"); 432 | 433 | g_free(req->api->esid); 434 | req->api->esid = g_strdup(str); 435 | } else if (steam_json_bool_chk(json, "captcha_needed", &bln) && bln) { 436 | errc = STEAM_API_ERROR_CAPTCHA; 437 | str = steam_json_str(json, "captcha_gid"); 438 | 439 | g_free(req->api->cgid); 440 | req->api->cgid = g_strdup(str); 441 | } else { 442 | errc = STEAM_API_ERROR_UNKNOWN; 443 | } 444 | 445 | if (errc == STEAM_API_ERROR_STEAMGUARD) { 446 | str = "SteamGuard authentication code required"; 447 | } else { 448 | str = steam_json_str(json, "message"); 449 | } 450 | 451 | g_set_error(&req->err, STEAM_API_ERROR, errc, "%s", str); 452 | return; 453 | } 454 | 455 | if (!steam_json_val_chk(json, "oauth", json_string, &jv)) { 456 | g_set_error(&req->err, STEAM_API_ERROR, STEAM_API_ERROR_GENERAL, 457 | "Failed to obtain OAuth data"); 458 | return; 459 | } 460 | 461 | jp = steam_json_new(jv->u.string.ptr, jv->u.string.length, &req->err); 462 | 463 | if ((jp == NULL) || (req->err != NULL)) { 464 | return; 465 | } 466 | 467 | if (steam_json_str_chk(jp, "oauth_token", &str)) { 468 | g_free(req->api->token); 469 | req->api->token = g_strdup(str); 470 | } 471 | 472 | req = steam_api_req_fwd(req); 473 | req->punc = steam_api_cb_auth_rdir; 474 | steam_api_req_init(req, STEAM_COM_HOST, STEAM_COM_PATH_AUTH_RDIR); 475 | 476 | for (i = 0; i < jp->u.object.length; i++) { 477 | str = jp->u.object.values[i].name; 478 | jv = jp->u.object.values[i].value; 479 | val = steam_json_valstr(jv); 480 | 481 | steam_http_req_params_set(req->req, STEAM_HTTP_PAIR(str, val), NULL); 482 | g_free(val); 483 | } 484 | 485 | req->flags |= STEAM_API_REQ_FLAG_NOJSON; 486 | req->req->flags |= STEAM_HTTP_REQ_FLAG_POST; 487 | steam_http_req_send(req->req); 488 | json_value_free(jp); 489 | } 490 | 491 | void 492 | steam_api_req_auth(SteamApiReq *req, const gchar *user, const gchar *pass, 493 | const gchar *authcode, const gchar *captcha) 494 | { 495 | gchar *ms; 496 | gchar *pswd; 497 | GTimeVal tv; 498 | 499 | g_return_if_fail(req != NULL); 500 | g_return_if_fail(user != NULL); 501 | g_return_if_fail(pass != NULL); 502 | 503 | pswd = steam_crypt_rsa_enc_str(req->api->pkmod, req->api->pkexp, pass); 504 | 505 | if (pswd == NULL) { 506 | g_set_error(&req->err, STEAM_API_ERROR, STEAM_API_ERROR_GENERAL, 507 | "Failed to encrypt password"); 508 | 509 | if (req->func != NULL) { 510 | req->func(req, req->data); 511 | } 512 | 513 | steam_api_req_free(req); 514 | return; 515 | } 516 | 517 | req->punc = steam_api_cb_auth; 518 | steam_api_req_init(req, STEAM_COM_HOST, STEAM_COM_PATH_AUTH); 519 | 520 | g_get_current_time(&tv); 521 | ms = g_strdup_printf("%ld", (tv.tv_usec / 1000)); 522 | 523 | switch (req->api->autht) { 524 | case STEAM_API_AUTH_TYPE_EMAIL: 525 | steam_http_req_params_set(req->req, 526 | STEAM_HTTP_PAIR("emailauth", authcode), 527 | STEAM_HTTP_PAIR("emailsteamid", req->api->esid), 528 | NULL 529 | ); 530 | break; 531 | 532 | case STEAM_API_AUTH_TYPE_MOBILE: 533 | steam_http_req_params_set(req->req, 534 | STEAM_HTTP_PAIR("twofactorcode", authcode), 535 | NULL 536 | ); 537 | break; 538 | 539 | default: 540 | break; 541 | } 542 | 543 | steam_http_req_params_set(req->req, 544 | STEAM_HTTP_PAIR("username", user), 545 | STEAM_HTTP_PAIR("password", pswd), 546 | STEAM_HTTP_PAIR("captchagid", req->api->cgid), 547 | STEAM_HTTP_PAIR("captcha_text", captcha), 548 | STEAM_HTTP_PAIR("rsatimestamp", req->api->pktime), 549 | STEAM_HTTP_PAIR("loginfriendlyname", PACKAGE), 550 | STEAM_HTTP_PAIR("oauth_client_id", STEAM_API_CLIENT_ID), 551 | STEAM_HTTP_PAIR("donotcache", ms), 552 | STEAM_HTTP_PAIR("remember_login", "true"), 553 | STEAM_HTTP_PAIR("oauth_scope", "read_profile write_profile " 554 | "read_client write_client"), 555 | NULL 556 | ); 557 | 558 | req->req->flags |= STEAM_HTTP_REQ_FLAG_POST; 559 | steam_http_req_send(req->req); 560 | 561 | g_free(pswd); 562 | g_free(ms); 563 | } 564 | 565 | static void 566 | steam_api_cb_friends(SteamApiReq *req, const json_value *json) 567 | { 568 | const gchar *str; 569 | guint i; 570 | json_value *je; 571 | json_value *jv; 572 | SteamUserInfo *info; 573 | SteamUserRel rel; 574 | 575 | if (!steam_json_array_chk(json, "friends", &jv)) { 576 | return; 577 | } 578 | 579 | for (i = 0; i < jv->u.array.length; i++) { 580 | je = jv->u.array.values[i]; 581 | 582 | if (!steam_json_str_chk(je, "relationship", &str)) { 583 | continue; 584 | } 585 | 586 | if (g_ascii_strcasecmp(str, "friend") == 0) { 587 | rel = STEAM_USER_REL_FRIEND; 588 | } else if (g_ascii_strcasecmp(str, "ignoredfriend") == 0) { 589 | rel = STEAM_USER_REL_IGNORE; 590 | } else { 591 | continue; 592 | } 593 | 594 | if (!steam_json_str_chk(je, "steamid", &str)) { 595 | continue; 596 | } 597 | 598 | info = steam_user_info_new(STEAM_ID_NEW_STR(str)); 599 | info->rel = rel; 600 | g_queue_push_tail(req->infs, info); 601 | } 602 | 603 | req = steam_api_req_fwd(req); 604 | steam_api_req_msg_info(req); 605 | } 606 | 607 | void 608 | steam_api_req_friends(SteamApiReq *req) 609 | { 610 | gchar sid[STEAM_ID_STRMAX]; 611 | 612 | g_return_if_fail(req != NULL); 613 | 614 | req->punc = steam_api_cb_friends; 615 | steam_api_req_init(req, STEAM_API_HOST, STEAM_API_PATH_FRIENDS); 616 | STEAM_ID_STR(req->api->info->id, sid); 617 | 618 | steam_http_req_params_set(req->req, 619 | STEAM_HTTP_PAIR("access_token", req->api->token), 620 | STEAM_HTTP_PAIR("steamid", sid), 621 | STEAM_HTTP_PAIR("relationship", "friend,ignoredfriend"), 622 | NULL 623 | ); 624 | 625 | steam_http_req_send(req->req); 626 | } 627 | 628 | static void 629 | steam_api_cb_key(SteamApiReq *req, const json_value *json) 630 | { 631 | const gchar *str; 632 | 633 | if (steam_json_str_chk(json, "publickey_mod", &str)) { 634 | g_free(req->api->pkmod); 635 | req->api->pkmod = g_strdup(str); 636 | } 637 | 638 | if (steam_json_str_chk(json, "publickey_exp", &str)) { 639 | g_free(req->api->pkexp); 640 | req->api->pkexp = g_strdup(str); 641 | } 642 | 643 | if (steam_json_str_chk(json, "timestamp", &str)) { 644 | g_free(req->api->pktime); 645 | req->api->pktime = g_strdup(str); 646 | } 647 | } 648 | 649 | void 650 | steam_api_req_key(SteamApiReq *req, const gchar *user) 651 | { 652 | gchar *ms; 653 | GTimeVal tv; 654 | 655 | g_return_if_fail(req != NULL); 656 | g_return_if_fail(user != NULL); 657 | 658 | req->punc = steam_api_cb_key; 659 | steam_api_req_init(req, STEAM_COM_HOST, STEAM_COM_PATH_KEY); 660 | 661 | g_get_current_time(&tv); 662 | ms = g_strdup_printf("%ld", (tv.tv_usec / 1000)); 663 | 664 | steam_http_req_params_set(req->req, 665 | STEAM_HTTP_PAIR("username", user), 666 | STEAM_HTTP_PAIR("donotcache", ms), 667 | NULL 668 | ); 669 | 670 | req->req->flags |= STEAM_HTTP_REQ_FLAG_POST; 671 | steam_http_req_send(req->req); 672 | g_free(ms); 673 | } 674 | 675 | void 676 | steam_api_req_logoff(SteamApiReq *req) 677 | { 678 | g_return_if_fail(req != NULL); 679 | steam_api_req_init(req, STEAM_API_HOST, STEAM_API_PATH_LOGOFF); 680 | 681 | steam_http_req_params_set(req->req, 682 | STEAM_HTTP_PAIR("access_token", req->api->token), 683 | STEAM_HTTP_PAIR("umqid", req->api->umqid), 684 | NULL 685 | ); 686 | 687 | req->req->flags |= STEAM_HTTP_REQ_FLAG_POST; 688 | steam_http_req_send(req->req); 689 | } 690 | 691 | static void 692 | steam_api_cb_logon(SteamApiReq *req, const json_value *json) 693 | { 694 | const gchar *str; 695 | 696 | if (steam_json_str_chk(json, "steamid", &str)) { 697 | req->api->info->id = STEAM_ID_NEW_STR(str); 698 | g_queue_push_tail(req->infs, req->api->info); 699 | steam_api_rehash(req->api); 700 | } 701 | 702 | if (steam_json_str_chk(json, "umqid", &str)) { 703 | g_free(req->api->umqid); 704 | req->api->umqid = g_strdup(str); 705 | steam_api_rehash(req->api); 706 | } 707 | 708 | req->api->lmid = steam_json_int(json, "message"); 709 | req->api->time = steam_json_int(json, "utc_timestamp"); 710 | 711 | /* Ensure the #SteamApi online state */ 712 | req->api->online = TRUE; 713 | 714 | /* If this is a relogon, process queued messages */ 715 | if (!g_queue_is_empty(req->api->msgs)) { 716 | req = g_queue_pop_head(req->api->msgs); 717 | steam_http_req_send(req->req); 718 | } 719 | 720 | steam_api_cb_user_info_req(req, json); 721 | } 722 | 723 | void 724 | steam_api_req_logon(SteamApiReq *req) 725 | { 726 | g_return_if_fail(req != NULL); 727 | 728 | req->punc = steam_api_cb_logon; 729 | steam_api_req_init(req, STEAM_API_HOST, STEAM_API_PATH_LOGON); 730 | 731 | steam_http_req_params_set(req->req, 732 | STEAM_HTTP_PAIR("access_token", req->api->token), 733 | STEAM_HTTP_PAIR("umqid", req->api->umqid), 734 | STEAM_HTTP_PAIR("ui_mode", "web"), 735 | NULL 736 | ); 737 | 738 | req->req->flags |= STEAM_HTTP_REQ_FLAG_POST; 739 | steam_http_req_send(req->req); 740 | } 741 | 742 | static void 743 | steam_api_cb_msg(SteamApiReq *req, const json_value *json) 744 | { 745 | /* Pop the successful message request */ 746 | g_queue_pop_head(req->api->msgs); 747 | 748 | if (!g_queue_is_empty(req->api->msgs)) { 749 | req = g_queue_peek_head(req->api->msgs); 750 | steam_http_req_send(req->req); 751 | } 752 | } 753 | 754 | void 755 | steam_api_req_msg(SteamApiReq *req, const SteamUserMsg *msg) 756 | { 757 | const gchar *type; 758 | gboolean empty; 759 | gchar sid[STEAM_ID_STRMAX]; 760 | 761 | g_return_if_fail(req != NULL); 762 | g_return_if_fail(msg != NULL); 763 | 764 | req->punc = steam_api_cb_msg; 765 | steam_api_req_init(req, STEAM_API_HOST, STEAM_API_PATH_MESSAGE); 766 | 767 | STEAM_ID_STR(msg->info->id, sid); 768 | type = steam_user_msg_type_str(msg->type); 769 | 770 | steam_http_req_params_set(req->req, 771 | STEAM_HTTP_PAIR("access_token", req->api->token), 772 | STEAM_HTTP_PAIR("umqid", req->api->umqid), 773 | STEAM_HTTP_PAIR("steamid_dst", sid), 774 | STEAM_HTTP_PAIR("type", type), 775 | NULL 776 | ); 777 | 778 | switch (msg->type) { 779 | case STEAM_USER_MSG_TYPE_SAYTEXT: 780 | case STEAM_USER_MSG_TYPE_EMOTE: 781 | steam_http_req_params_set(req->req, 782 | STEAM_HTTP_PAIR("text", msg->text), 783 | NULL 784 | ); 785 | break; 786 | 787 | case STEAM_USER_MSG_TYPE_TYPING: 788 | break; 789 | 790 | default: 791 | steam_http_req_free(req->req); 792 | return; 793 | } 794 | 795 | req->req->flags |= STEAM_HTTP_REQ_FLAG_POST; 796 | empty = g_queue_is_empty(req->api->msgs); 797 | g_queue_push_tail(req->api->msgs, req); 798 | 799 | if (empty && req->api->online) { 800 | steam_http_req_send(req->req); 801 | } 802 | } 803 | 804 | static void 805 | steam_api_cb_msg_info(SteamApiReq *req, const json_value *json) 806 | { 807 | GHashTable *ght; 808 | gint64 in; 809 | GList *l; 810 | guint i; 811 | json_value *je; 812 | json_value *jv; 813 | SteamId id; 814 | SteamUserInfo *info; 815 | 816 | if (!steam_json_val_chk(json, "response", json_object, &jv) || 817 | !steam_json_array_chk(jv, "message_sessions", &jv)) 818 | { 819 | steam_api_cb_user_info_req(req, json); 820 | return; 821 | } 822 | 823 | ght = g_hash_table_new(steam_id_hash, steam_id_equal); 824 | 825 | for (l = req->infs->head; l != NULL; l = l->next) { 826 | info = l->data; 827 | g_hash_table_replace(ght, &info->id, info); 828 | } 829 | 830 | for (i = 0; i < jv->u.array.length; i++) { 831 | je = jv->u.array.values[i]; 832 | 833 | if (!steam_json_int_chk(je, "accountid_friend", &in)) { 834 | continue; 835 | } 836 | 837 | id = STEAM_ID_NEW(STEAM_ID_UNIV_PUBLIC, STEAM_ID_TYPE_INDIVIDUAL, 838 | STEAM_ID_INST_DESKTOP, in); 839 | 840 | info = g_hash_table_lookup(ght, &id); 841 | 842 | if (G_UNLIKELY(info == NULL)) { 843 | continue; 844 | } 845 | 846 | if (steam_json_int_chk(je, "last_view", &in)) { 847 | info->vtime = in; 848 | } 849 | 850 | if (steam_json_int_chk(je, "unread_message_count", &in)) { 851 | info->unread = in; 852 | } 853 | } 854 | 855 | g_hash_table_destroy(ght); 856 | steam_api_cb_user_info_req(req, json); 857 | } 858 | 859 | void 860 | steam_api_req_msg_info(SteamApiReq *req) 861 | { 862 | g_return_if_fail(req != NULL); 863 | 864 | if (req->infs == NULL) { 865 | if (req->func != NULL) { 866 | req->func(req, req->data); 867 | } 868 | 869 | steam_api_req_free(req); 870 | return; 871 | } 872 | 873 | req->punc = steam_api_cb_msg_info; 874 | steam_api_req_init(req, STEAM_API_HOST, STEAM_API_PATH_MESSAGE_INFO); 875 | 876 | steam_http_req_params_set(req->req, 877 | STEAM_HTTP_PAIR("access_token", req->api->token), 878 | NULL 879 | ); 880 | 881 | steam_http_req_send(req->req); 882 | } 883 | 884 | static void 885 | steam_api_cb_msgs(SteamApiReq *req, const json_value *json) 886 | { 887 | const gchar *str; 888 | gint32 aid; 889 | gint64 in; 890 | guint i; 891 | json_value *je; 892 | json_value *jv; 893 | SteamId id; 894 | SteamUserMsg *msg = NULL; 895 | 896 | if (!steam_json_val_chk(json, "response", json_object, &jv) || 897 | !steam_json_array_chk(jv, "messages", &jv)) 898 | { 899 | return; 900 | } 901 | 902 | aid = STEAM_ID_ACCID(req->api->info->id); 903 | 904 | for (i = 0; i < jv->u.array.length; i++) { 905 | je = jv->u.array.values[i]; 906 | 907 | if (!steam_json_int_chk(je, "accountid", &in) || (in == aid)) { 908 | continue; 909 | } 910 | 911 | id = STEAM_ID_NEW(STEAM_ID_UNIV_PUBLIC, STEAM_ID_TYPE_INDIVIDUAL, 912 | STEAM_ID_INST_DESKTOP, in); 913 | 914 | msg = steam_user_msg_new(id); 915 | msg->type = STEAM_USER_MSG_TYPE_SAYTEXT; 916 | msg->time = steam_json_int(je, "timestamp"); 917 | 918 | str = steam_json_str(je, "message"); 919 | msg->text = g_strdup(str); 920 | 921 | /* Messages are send backwards */ 922 | g_queue_push_head(req->msgs, msg); 923 | g_queue_push_tail(req->infs, msg->info); 924 | } 925 | 926 | if (msg != NULL) { 927 | req = steam_api_req_fwd(req); 928 | steam_api_req_msgs_read(req, msg->info->id); 929 | } 930 | } 931 | 932 | void 933 | steam_api_req_msgs(SteamApiReq *req, SteamId id, gint64 since) 934 | { 935 | gchar sid1[STEAM_ID_STRMAX]; 936 | gchar sid2[STEAM_ID_STRMAX]; 937 | gchar *stime; 938 | 939 | g_return_if_fail(req != NULL); 940 | req->punc = steam_api_cb_msgs; 941 | steam_api_req_init(req, STEAM_API_HOST, STEAM_API_PATH_MESSAGES); 942 | 943 | STEAM_ID_STR(id, sid1); 944 | STEAM_ID_STR(req->api->info->id, sid2); 945 | stime = g_strdup_printf("%" G_GINT64_FORMAT, since); 946 | 947 | steam_http_req_params_set(req->req, 948 | STEAM_HTTP_PAIR("access_token", req->api->token), 949 | STEAM_HTTP_PAIR("steamid1", sid1), 950 | STEAM_HTTP_PAIR("steamid2", sid2), 951 | STEAM_HTTP_PAIR("rtime32_start_time", stime), 952 | NULL 953 | ); 954 | 955 | steam_http_req_send(req->req); 956 | g_free(stime); 957 | } 958 | 959 | void 960 | steam_api_req_msgs_read(SteamApiReq *req, SteamId id) 961 | { 962 | gchar sid[STEAM_ID_STRMAX]; 963 | 964 | g_return_if_fail(req != NULL); 965 | 966 | req->punc = steam_api_cb_user_info_req; 967 | steam_api_req_init(req, STEAM_API_HOST, STEAM_API_PATH_MESSAGES_READ); 968 | STEAM_ID_STR(id, sid); 969 | 970 | steam_http_req_params_set(req->req, 971 | STEAM_HTTP_PAIR("access_token", req->api->token), 972 | STEAM_HTTP_PAIR("steamid_friend", sid), 973 | NULL 974 | ); 975 | 976 | req->flags |= STEAM_API_REQ_FLAG_NOJSON; 977 | req->req->flags |= STEAM_HTTP_REQ_FLAG_POST; 978 | steam_http_req_send(req->req); 979 | } 980 | 981 | static void 982 | steam_api_cb_poll(SteamApiReq *req, const json_value *json) 983 | { 984 | const gchar *str; 985 | gboolean selfie = FALSE; 986 | gint64 in; 987 | guint i; 988 | json_value *je; 989 | json_value *jv; 990 | SteamId id; 991 | SteamUserMsg *msg; 992 | 993 | if (!steam_json_int_chk(json, "messagelast", &in) || 994 | (in == req->api->lmid)) 995 | { 996 | return; 997 | } 998 | 999 | req->api->lmid = in; 1000 | 1001 | if (!steam_json_array_chk(json, "messages", &jv)) { 1002 | return; 1003 | } 1004 | 1005 | for (i = 0; i < jv->u.array.length; i++) { 1006 | je = jv->u.array.values[i]; 1007 | 1008 | if (!steam_json_str_chk(je, "steamid_from", &str)) { 1009 | continue; 1010 | } 1011 | 1012 | id = STEAM_ID_NEW_STR(str); 1013 | 1014 | if (id == req->api->info->id) { 1015 | selfie = TRUE; 1016 | continue; 1017 | } 1018 | 1019 | /* For now, only handle individuals */ 1020 | if (STEAM_ID_TYPE(id) != STEAM_ID_TYPE_INDIVIDUAL) { 1021 | continue; 1022 | } 1023 | 1024 | msg = steam_user_msg_new(id); 1025 | str = steam_json_str(je, "type"); 1026 | 1027 | msg->type = steam_user_msg_type_from_str(str); 1028 | msg->time = steam_json_int(je, "utc_timestamp"); 1029 | 1030 | switch (msg->type) { 1031 | case STEAM_USER_MSG_TYPE_MY_SAYTEXT: 1032 | case STEAM_USER_MSG_TYPE_MY_EMOTE: 1033 | case STEAM_USER_MSG_TYPE_SAYTEXT: 1034 | case STEAM_USER_MSG_TYPE_EMOTE: 1035 | str = steam_json_str(je, "text"); 1036 | msg->text = g_strdup(str); 1037 | break; 1038 | 1039 | case STEAM_USER_MSG_TYPE_RELATIONSHIP: 1040 | msg->info->act = steam_json_int(je, "persona_state"); 1041 | break; 1042 | 1043 | case STEAM_USER_MSG_TYPE_STATE: 1044 | case STEAM_USER_MSG_TYPE_TYPING: 1045 | case STEAM_USER_MSG_TYPE_LEFT_CONV: 1046 | break; 1047 | 1048 | default: 1049 | steam_user_msg_free(msg); 1050 | continue; 1051 | } 1052 | 1053 | g_queue_push_tail(req->msgs, msg); 1054 | g_queue_push_tail(req->infs, msg->info); 1055 | } 1056 | 1057 | if (selfie) { 1058 | g_queue_push_tail(req->infs, req->api->info); 1059 | } 1060 | 1061 | steam_api_cb_user_info_req(req, json); 1062 | } 1063 | 1064 | void 1065 | steam_api_req_poll(SteamApiReq *req) 1066 | { 1067 | const gchar *idle; 1068 | gchar *lmid; 1069 | gchar *tout; 1070 | 1071 | static const SteamUtilEnum enums[] = { 1072 | {STEAM_USER_STATE_AWAY, G_STRINGIFY(STEAM_API_IDLEOUT_AWAY)}, 1073 | {STEAM_USER_STATE_SNOOZE, G_STRINGIFY(STEAM_API_IDLEOUT_SNOOZE)}, 1074 | STEAM_UTIL_ENUM_NULL 1075 | }; 1076 | 1077 | g_return_if_fail(req != NULL); 1078 | 1079 | idle = steam_util_enum_ptr(enums, "0", req->api->info->state); 1080 | lmid = g_strdup_printf("%" G_GINT64_FORMAT, req->api->lmid); 1081 | tout = g_strdup_printf("%" G_GINT32_FORMAT, STEAM_API_TIMEOUT); 1082 | 1083 | req->punc = steam_api_cb_poll; 1084 | steam_api_req_init(req, STEAM_API_HOST, STEAM_API_PATH_POLL); 1085 | 1086 | steam_http_req_headers_set(req->req, 1087 | STEAM_HTTP_PAIR("Connection", "Keep-Alive"), 1088 | NULL 1089 | ); 1090 | 1091 | steam_http_req_params_set(req->req, 1092 | STEAM_HTTP_PAIR("access_token", req->api->token), 1093 | STEAM_HTTP_PAIR("umqid", req->api->umqid), 1094 | STEAM_HTTP_PAIR("message", lmid), 1095 | STEAM_HTTP_PAIR("sectimeout", tout), 1096 | STEAM_HTTP_PAIR("secidletime", idle), 1097 | NULL 1098 | ); 1099 | 1100 | req->req->timeout = (STEAM_API_TIMEOUT + 5) * 1000; 1101 | req->req->flags |= STEAM_HTTP_REQ_FLAG_POST; 1102 | steam_http_req_send(req->req); 1103 | 1104 | g_free(tout); 1105 | g_free(lmid); 1106 | } 1107 | 1108 | void 1109 | steam_api_req_user_accept(SteamApiReq *req, SteamId id, 1110 | SteamApiAcceptType type) 1111 | { 1112 | const gchar *sct; 1113 | gchar sid[STEAM_ID_STRMAX]; 1114 | gchar *srl; 1115 | SteamUserInfo *info; 1116 | url_t url; 1117 | 1118 | static const SteamUtilEnum enums[] = { 1119 | {STEAM_API_ACCEPT_TYPE_DEFAULT, "accept"}, 1120 | {STEAM_API_ACCEPT_TYPE_BLOCK, "block"}, 1121 | {STEAM_API_ACCEPT_TYPE_IGNORE, "ignore"}, 1122 | STEAM_UTIL_ENUM_NULL 1123 | }; 1124 | 1125 | g_return_if_fail(req != NULL); 1126 | 1127 | sct = steam_util_enum_ptr(enums, NULL, type); 1128 | srl = steam_http_uri_join(req->api->info->profile, "home_process", NULL); 1129 | url_set(&url, srl); 1130 | 1131 | STEAM_ID_STR(id, sid); 1132 | info = steam_user_info_new(id); 1133 | g_queue_push_head(req->infs, info); 1134 | 1135 | req->punc = steam_api_cb_user_info_req; 1136 | steam_api_req_init(req, url.host, url.file); 1137 | 1138 | steam_http_req_params_set(req->req, 1139 | STEAM_HTTP_PAIR("sessionID", req->api->sessid), 1140 | STEAM_HTTP_PAIR("id", sid), 1141 | STEAM_HTTP_PAIR("perform", sct), 1142 | STEAM_HTTP_PAIR("action", "approvePending"), 1143 | STEAM_HTTP_PAIR("itype", "friend"), 1144 | STEAM_HTTP_PAIR("json", "1"), 1145 | STEAM_HTTP_PAIR("xml", "0"), 1146 | NULL 1147 | ); 1148 | 1149 | req->req->flags |= STEAM_HTTP_REQ_FLAG_POST; 1150 | steam_http_req_send(req->req); 1151 | 1152 | g_free(srl); 1153 | } 1154 | 1155 | static void 1156 | steam_api_cb_user_add(SteamApiReq *req, const json_value *json) 1157 | { 1158 | gint64 in; 1159 | 1160 | if (!steam_json_int_chk(json, "success", &in) || (in == 0)) { 1161 | g_set_error(&req->err, STEAM_API_ERROR, STEAM_API_ERROR_GENERAL, 1162 | "Failed to add friend"); 1163 | return; 1164 | } 1165 | 1166 | steam_api_cb_user_info_req(req, json); 1167 | } 1168 | 1169 | void 1170 | steam_api_req_user_add(SteamApiReq *req, SteamId id) 1171 | { 1172 | gchar sid[STEAM_ID_STRMAX]; 1173 | SteamUserInfo *info; 1174 | 1175 | g_return_if_fail(req != NULL); 1176 | 1177 | STEAM_ID_STR(id, sid); 1178 | info = steam_user_info_new(id); 1179 | g_queue_push_head(req->infs, info); 1180 | 1181 | req->punc = steam_api_cb_user_add; 1182 | steam_api_req_init(req, STEAM_COM_HOST, STEAM_COM_PATH_FRIEND_ADD); 1183 | 1184 | steam_http_req_params_set(req->req, 1185 | STEAM_HTTP_PAIR("sessionID", req->api->sessid), 1186 | STEAM_HTTP_PAIR("steamid", sid), 1187 | STEAM_HTTP_PAIR("accept_invite", "0"), 1188 | NULL 1189 | ); 1190 | 1191 | req->req->flags |= STEAM_HTTP_REQ_FLAG_POST; 1192 | steam_http_req_send(req->req); 1193 | } 1194 | 1195 | void 1196 | steam_api_req_user_ignore(SteamApiReq *req, SteamId id, gboolean ignore) 1197 | { 1198 | const gchar *act; 1199 | gchar *srl; 1200 | gchar *user; 1201 | SteamUserInfo *info; 1202 | url_t url; 1203 | 1204 | g_return_if_fail(req != NULL); 1205 | 1206 | act = ignore ? "ignore" : "unignore"; 1207 | user = g_strdup_printf("friends[%" STEAM_ID_FORMAT "]", id); 1208 | srl = steam_http_uri_join(req->api->info->profile, "friends", NULL); 1209 | url_set(&url, srl); 1210 | 1211 | info = steam_user_info_new(id); 1212 | g_queue_push_head(req->infs, info); 1213 | 1214 | req->punc = steam_api_cb_user_info_req; 1215 | steam_api_req_init(req, url.host, url.file); 1216 | 1217 | steam_http_req_params_set(req->req, 1218 | STEAM_HTTP_PAIR("sessionID", req->api->sessid), 1219 | STEAM_HTTP_PAIR("action", act), 1220 | STEAM_HTTP_PAIR(user, "1"), 1221 | NULL 1222 | ); 1223 | 1224 | req->flags |= STEAM_API_REQ_FLAG_NOJSON; 1225 | req->req->flags |= STEAM_HTTP_REQ_FLAG_POST; 1226 | steam_http_req_send(req->req); 1227 | 1228 | g_free(srl); 1229 | g_free(user); 1230 | } 1231 | 1232 | static void 1233 | steam_api_cb_user_info(SteamApiReq *req, const json_value *json) 1234 | { 1235 | const gchar *str; 1236 | GHashTable *ght; 1237 | GList *l; 1238 | GList *n; 1239 | gpointer key; 1240 | guint i; 1241 | json_value *je; 1242 | json_value *jv; 1243 | SteamId id; 1244 | SteamUserInfo *info; 1245 | 1246 | if ((!steam_json_array_chk(json, "players", &jv) || 1247 | (jv->u.array.length < 1)) && (req->infs != NULL)) 1248 | { 1249 | g_set_error(&req->err, STEAM_API_ERROR, STEAM_API_ERROR_GENERAL, 1250 | "Failed to retrieve requested friend summaries"); 1251 | return; 1252 | } 1253 | 1254 | ght = g_hash_table_new_full(steam_id_hash, steam_id_equal, g_free, NULL); 1255 | 1256 | for (i = 0; i < jv->u.array.length; i++) { 1257 | je = jv->u.array.values[i]; 1258 | 1259 | if (steam_json_str_chk(je, "steamid", &str)) { 1260 | id = STEAM_ID_NEW_STR(str); 1261 | key = g_memdup(&id, sizeof id); 1262 | g_hash_table_replace(ght, key, je); 1263 | } 1264 | } 1265 | 1266 | for (l = req->infr->head; l != NULL; l = n) { 1267 | info = l->data; 1268 | n = l->next; 1269 | je = g_hash_table_lookup(ght, &info->id); 1270 | 1271 | if (je != NULL) { 1272 | steam_api_json_user_info(info, je); 1273 | g_queue_delete_link(req->infr, l); 1274 | } 1275 | } 1276 | 1277 | if (!g_queue_is_empty(req->infr)) { 1278 | req = steam_api_req_fwd(req); 1279 | steam_api_req_user_info(req); 1280 | } 1281 | 1282 | g_hash_table_destroy(ght); 1283 | } 1284 | 1285 | void 1286 | steam_api_req_user_info(SteamApiReq *req) 1287 | { 1288 | GHashTable *ght; 1289 | GList *l; 1290 | GList *n; 1291 | gsize i; 1292 | GString *gstr; 1293 | SteamId *id; 1294 | SteamUserInfo *info; 1295 | 1296 | g_return_if_fail(req != NULL); 1297 | 1298 | if (G_UNLIKELY(g_queue_is_empty(req->infs))) { 1299 | if (req->func != NULL) { 1300 | req->func(req, req->data); 1301 | } 1302 | 1303 | steam_api_req_free(req); 1304 | return; 1305 | } 1306 | 1307 | if (g_queue_is_empty(req->infr)) { 1308 | g_queue_free(req->infr); 1309 | req->infr = g_queue_copy(req->infs); 1310 | } 1311 | 1312 | ght = g_hash_table_new(g_int64_hash, g_int64_equal); 1313 | gstr = g_string_new(NULL); 1314 | 1315 | for (l = req->infr->head, i = 0; l != NULL; l = n) { 1316 | info = l->data; 1317 | n = l->next; 1318 | id = &info->id; 1319 | 1320 | if (!g_hash_table_lookup_extended(ght, id, NULL, NULL)) { 1321 | g_hash_table_replace(ght, id, id); 1322 | g_string_append_printf(gstr, "%" STEAM_ID_FORMAT ",", info->id); 1323 | 1324 | if ((++i % 100) == 0) { 1325 | break; 1326 | } 1327 | } 1328 | } 1329 | 1330 | /* Remove trailing comma */ 1331 | gstr->str[gstr->len - 1] = 0; 1332 | 1333 | req->punc = steam_api_cb_user_info; 1334 | steam_api_req_init(req, STEAM_API_HOST, STEAM_API_PATH_SUMMARIES); 1335 | 1336 | steam_http_req_params_set(req->req, 1337 | STEAM_HTTP_PAIR("access_token", req->api->token), 1338 | STEAM_HTTP_PAIR("steamids", gstr->str), 1339 | NULL 1340 | ); 1341 | 1342 | steam_http_req_send(req->req); 1343 | g_string_free(gstr, TRUE); 1344 | g_hash_table_destroy(ght); 1345 | } 1346 | 1347 | static void 1348 | steam_api_cb_user_info_nicks(SteamApiReq *req, const json_value *json) 1349 | { 1350 | const gchar *str; 1351 | guint i; 1352 | json_value *je; 1353 | SteamUserInfo *info; 1354 | 1355 | info = g_queue_pop_head(req->infr); 1356 | 1357 | for (i = 0; i < json->u.array.length; i++) { 1358 | je = json->u.array.values[i]; 1359 | 1360 | if (!steam_json_str_chk(je, "newname", &str)) { 1361 | continue; 1362 | } 1363 | 1364 | if (g_strcmp0(str, info->nick) != 0) { 1365 | info->nicks = g_slist_prepend(info->nicks, g_strdup(str)); 1366 | } 1367 | } 1368 | 1369 | info->nicks = g_slist_reverse(info->nicks); 1370 | 1371 | if (!g_queue_is_empty(req->infr)) { 1372 | req = steam_api_req_fwd(req); 1373 | steam_api_req_user_info_nicks(req); 1374 | } 1375 | } 1376 | 1377 | void 1378 | steam_api_req_user_info_nicks(SteamApiReq *req) 1379 | { 1380 | gchar *srl; 1381 | SteamUserInfo *info; 1382 | url_t url; 1383 | 1384 | g_return_if_fail(req != NULL); 1385 | 1386 | if (G_UNLIKELY(g_queue_is_empty(req->infs))) { 1387 | if (req->func != NULL) 1388 | req->func(req, req->data); 1389 | 1390 | steam_api_req_free(req); 1391 | return; 1392 | } 1393 | 1394 | if (g_queue_is_empty(req->infr)) { 1395 | g_queue_free(req->infr); 1396 | req->infr = g_queue_copy(req->infs); 1397 | } 1398 | 1399 | info = g_queue_peek_head(req->infr); 1400 | 1401 | if (G_UNLIKELY(info->profile == NULL)) { 1402 | if (req->func != NULL) { 1403 | req->func(req, req->data); 1404 | } 1405 | 1406 | steam_api_req_free(req); 1407 | return; 1408 | } 1409 | 1410 | srl = steam_http_uri_join(info->profile, "ajaxaliases", NULL); 1411 | url_set(&url, srl); 1412 | 1413 | req->punc = steam_api_cb_user_info_nicks; 1414 | steam_api_req_init(req, url.host, url.file); 1415 | 1416 | req->req->flags |= STEAM_HTTP_REQ_FLAG_POST; 1417 | steam_http_req_send(req->req); 1418 | g_free(srl); 1419 | } 1420 | 1421 | static void 1422 | steam_api_cb_user_remove(SteamApiReq *req, const json_value *json) 1423 | { 1424 | if ((req->req->body_size < 1) || !bool2int(req->req->body)) { 1425 | g_set_error(&req->err, STEAM_API_ERROR, STEAM_API_ERROR_GENERAL, 1426 | "Failed to remove user"); 1427 | return; 1428 | } 1429 | 1430 | steam_api_cb_user_info_req(req, json); 1431 | } 1432 | 1433 | void 1434 | steam_api_req_user_remove(SteamApiReq *req, SteamId id) 1435 | { 1436 | gchar sid[STEAM_ID_STRMAX]; 1437 | SteamUserInfo *info; 1438 | 1439 | g_return_if_fail(req != NULL); 1440 | 1441 | STEAM_ID_STR(id, sid); 1442 | info = steam_user_info_new(id); 1443 | g_queue_push_head(req->infs, info); 1444 | 1445 | req->punc = steam_api_cb_user_remove; 1446 | steam_api_req_init(req, STEAM_COM_HOST, STEAM_COM_PATH_FRIEND_REMOVE); 1447 | 1448 | steam_http_req_params_set(req->req, 1449 | STEAM_HTTP_PAIR("sessionID", req->api->sessid), 1450 | STEAM_HTTP_PAIR("steamid", sid), 1451 | NULL 1452 | ); 1453 | 1454 | req->flags |= STEAM_API_REQ_FLAG_NOJSON; 1455 | req->req->flags |= STEAM_HTTP_REQ_FLAG_POST; 1456 | steam_http_req_send(req->req); 1457 | } 1458 | 1459 | static void 1460 | steam_api_cb_user_search(SteamApiReq *req, const json_value *json) 1461 | { 1462 | const gchar *str; 1463 | guint i; 1464 | json_value *je; 1465 | json_value *jv; 1466 | SteamUserInfo *info; 1467 | 1468 | if (!steam_json_array_chk(json, "results", &jv)) { 1469 | return; 1470 | } 1471 | 1472 | for (i = 0; i < jv->u.array.length; i++) { 1473 | je = jv->u.array.values[i]; 1474 | 1475 | if (!steam_json_str_chk(je, "type", &str) || 1476 | (g_strcmp0(str, "user") != 0)) 1477 | { 1478 | continue; 1479 | } 1480 | 1481 | if (!steam_json_str_chk(je, "steamid", &str)) { 1482 | continue; 1483 | } 1484 | 1485 | info = steam_user_info_new(STEAM_ID_NEW_STR(str)); 1486 | str = steam_json_str(je, "matchingtext"); 1487 | info->nick = g_strdup(str); 1488 | g_queue_push_tail(req->infs, info); 1489 | } 1490 | 1491 | steam_api_cb_user_info_req(req, json); 1492 | } 1493 | 1494 | void 1495 | steam_api_req_user_search(SteamApiReq *req, const gchar *name, guint count) 1496 | { 1497 | gchar *snt; 1498 | gchar *str; 1499 | 1500 | g_return_if_fail(req != NULL); 1501 | 1502 | req->punc = steam_api_cb_user_search; 1503 | steam_api_req_init(req, STEAM_API_HOST, STEAM_API_PATH_FRIEND_SEARCH); 1504 | 1505 | snt = g_strdup_printf("%u", count); 1506 | str = g_strdup_printf("\"%s\"", name); 1507 | 1508 | steam_http_req_params_set(req->req, 1509 | STEAM_HTTP_PAIR("access_token", req->api->token), 1510 | STEAM_HTTP_PAIR("keywords", str), 1511 | STEAM_HTTP_PAIR("count", snt), 1512 | STEAM_HTTP_PAIR("offset", "0"), 1513 | STEAM_HTTP_PAIR("fields", "all"), 1514 | STEAM_HTTP_PAIR("targets", "users"), 1515 | NULL 1516 | ); 1517 | 1518 | steam_http_req_send(req->req); 1519 | 1520 | g_free(snt); 1521 | g_free(str); 1522 | } 1523 | -------------------------------------------------------------------------------- /steam/steam-api.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2016 James Geboski 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef _STEAM_API_H_ 19 | #define _STEAM_API_H_ 20 | 21 | /** 22 | * SECTION:api 23 | * @section_id: steam-api 24 | * @short_description: steam-api.h 25 | * @title: Steam API 26 | * 27 | * The API for interacting with the Steam Web protocol. 28 | */ 29 | 30 | #include "steam-glib.h" 31 | #include "steam-http.h" 32 | #include "steam-json.h" 33 | #include "steam-user.h" 34 | 35 | /** 36 | * STEAM_API_HOST: 37 | * 38 | * The HTTP host for the Steam API. 39 | */ 40 | #define STEAM_API_HOST "api.steampowered.com" 41 | 42 | /** 43 | * STEAM_COM_HOST: 44 | * 45 | * The HTTP host for the Steam Community. 46 | */ 47 | #define STEAM_COM_HOST "steamcommunity.com" 48 | 49 | /** 50 | * STEAM_API_AGENT: 51 | * 52 | * The HTTP user-agent for the Steam requests. 53 | */ 54 | #define STEAM_API_AGENT "Steam App / " PACKAGE " / " PACKAGE_VERSION 55 | 56 | /** 57 | * STEAM_API_CLIENT: 58 | * 59 | * The Steam client. 60 | */ 61 | #define STEAM_API_CLIENT "android" 62 | 63 | /** 64 | * STEAM_API_CLIENT_ID: 65 | * 66 | * The Steam client identifier. 67 | */ 68 | #define STEAM_API_CLIENT_ID "DE45CD61" 69 | 70 | /** 71 | * STEAM_API_CLIENT_VERSION: 72 | * 73 | * The Steam client version. 74 | */ 75 | #define STEAM_API_CLIENT_VERSION "3472020 (2.1.6)" 76 | 77 | /** 78 | * STEAM_API_TIMEOUT: 79 | * 80 | * The timeout (in seconds) of a poll request. This value should not 81 | * exceed `25`. Higher values result in frequent HTTP 500 responses. 82 | */ 83 | #define STEAM_API_TIMEOUT 25 84 | 85 | /** 86 | * STEAM_API_IDLEOUT_AWAY: 87 | * 88 | * The idle time (in seconds) required for being "away." 89 | */ 90 | #define STEAM_API_IDLEOUT_AWAY 600 91 | 92 | /** 93 | * STEAM_API_IDLEOUT_SNOOZE: 94 | * 95 | * The idle time (in seconds) required for "snoozing." 96 | */ 97 | #define STEAM_API_IDLEOUT_SNOOZE 8000 98 | 99 | /** 100 | * STEAM_API_PATH_FRIEND_SEARCH: 101 | * 102 | * The Steam API path for the friend search request. 103 | */ 104 | #define STEAM_API_PATH_FRIEND_SEARCH "/ISteamUserOAuth/Search/v0001" 105 | 106 | /** 107 | * STEAM_API_PATH_FRIENDS: 108 | * 109 | * The Steam API path for the friends request. 110 | */ 111 | #define STEAM_API_PATH_FRIENDS "/ISteamUserOAuth/GetFriendList/v0001" 112 | 113 | /** 114 | * STEAM_API_PATH_LOGON: 115 | * 116 | * The Steam API path for the logon request. 117 | */ 118 | #define STEAM_API_PATH_LOGON "/ISteamWebUserPresenceOAuth/Logon/v0001" 119 | 120 | /** 121 | * STEAM_API_PATH_LOGOFF: 122 | * 123 | * The Steam API path for the logoff request. 124 | */ 125 | #define STEAM_API_PATH_LOGOFF "/ISteamWebUserPresenceOAuth/Logoff/v0001" 126 | 127 | /** 128 | * STEAM_API_PATH_MESSAGE: 129 | * 130 | * The Steam API path for the message request. 131 | */ 132 | #define STEAM_API_PATH_MESSAGE "/ISteamWebUserPresenceOAuth/Message/v0001" 133 | 134 | /** 135 | * STEAM_API_PATH_MESSAGE_INFO: 136 | * 137 | * The Steam API path for the message information request. 138 | */ 139 | #define STEAM_API_PATH_MESSAGE_INFO "/IFriendMessagesService/GetActiveMessageSessions/v0001" 140 | 141 | /** 142 | * STEAM_API_PATH_MESSAGES: 143 | * 144 | * The Steam API path for the messages request. 145 | */ 146 | #define STEAM_API_PATH_MESSAGES "/IFriendMessagesService/GetRecentMessages/v0001" 147 | 148 | /** 149 | * STEAM_API_PATH_MESSAGES_READ: 150 | * 151 | * The Steam API path for the messages read request. 152 | */ 153 | #define STEAM_API_PATH_MESSAGES_READ "/IFriendMessagesService/MarkOfflineMessagesRead/v0001" 154 | 155 | /** 156 | * STEAM_API_PATH_POLL: 157 | * 158 | * The Steam API path for the poll request. 159 | */ 160 | #define STEAM_API_PATH_POLL "/ISteamWebUserPresenceOAuth/Poll/v0001" 161 | 162 | /** 163 | * STEAM_API_PATH_SUMMARIES: 164 | * 165 | * The Steam API path for the summaries request. 166 | */ 167 | #define STEAM_API_PATH_SUMMARIES "/ISteamUserOAuth/GetUserSummaries/v0001" 168 | 169 | /** 170 | * STEAM_COM_PATH_AUTH: 171 | * 172 | * The Steam Community path for the authentication request. 173 | */ 174 | #define STEAM_COM_PATH_AUTH "/mobilelogin/dologin/" 175 | 176 | /** 177 | * STEAM_COM_PATH_AUTH: 178 | * 179 | * The Steam Community path for the authentication request. 180 | */ 181 | #define STEAM_COM_PATH_AUTH_RDIR "/mobileloginsucceeded/" 182 | 183 | /** 184 | * STEAM_COM_PATH_CAPTCHA: 185 | * 186 | * The Steam Community path for the captcha image. 187 | */ 188 | #define STEAM_COM_PATH_CAPTCHA "/public/captcha.php" 189 | 190 | /** 191 | * STEAM_COM_PATH_CHATLOG: 192 | * 193 | * The Steam Community path for the chat log request. 194 | */ 195 | #define STEAM_COM_PATH_CHATLOG "/chat/chatlog/" 196 | 197 | /** 198 | * STEAM_COM_PATH_FRIEND_ADD: 199 | * 200 | * The Steam Community path for the friend add request. 201 | */ 202 | #define STEAM_COM_PATH_FRIEND_ADD "/actions/AddFriendAjax/" 203 | 204 | /** 205 | * STEAM_COM_PATH_FRIEND_BLOCK: 206 | * 207 | * The Steam Community path for the friend block request. 208 | */ 209 | #define STEAM_COM_PATH_FRIEND_BLOCK "/actions/BlockUserAjax/" 210 | 211 | /** 212 | * STEAM_COM_PATH_FRIEND_REMOVE: 213 | * 214 | * The Steam Community path for the friend remove request. 215 | */ 216 | #define STEAM_COM_PATH_FRIEND_REMOVE "/actions/RemoveFriendAjax/" 217 | 218 | /** 219 | * STEAM_COM_PATH_KEY: 220 | * 221 | * The Steam Community path for the public key request. 222 | */ 223 | #define STEAM_COM_PATH_KEY "/mobilelogin/getrsakey/" 224 | 225 | /** 226 | * STEAM_API_ERROR: 227 | * 228 | * The #GQuark of the domain of API errors. 229 | */ 230 | #define STEAM_API_ERROR steam_api_error_quark() 231 | 232 | typedef struct _SteamApi SteamApi; 233 | typedef struct _SteamApiReq SteamApiReq; 234 | 235 | /** 236 | * SteamApiFunc: 237 | * @req: The #SteamApiReq. 238 | * @data: The user defined data or #NULL. 239 | * 240 | * The callback for #SteamApiReq operations. 241 | */ 242 | typedef void (*SteamApiFunc) (SteamApiReq *req, gpointer data); 243 | 244 | /** 245 | * SteamApiParser: 246 | * @req: The #SteamApiReq. 247 | * @json: The #json_value or #NULL. 248 | * 249 | * The callback for parser based #SteamApiReq operations. 250 | */ 251 | typedef void (*SteamApiParser) (SteamApiReq *req, const json_value *json); 252 | 253 | /** 254 | * SteamApiAuthType: 255 | * @STEAM_API_AUTH_TYPE_EMAIL: SteamGuard via email. 256 | * @STEAM_API_AUTH_TYPE_MOBILE: SteamGuard via mobile. 257 | * 258 | * The authentication types. 259 | */ 260 | typedef enum 261 | { 262 | STEAM_API_AUTH_TYPE_EMAIL, 263 | STEAM_API_AUTH_TYPE_MOBILE 264 | } SteamApiAuthType; 265 | 266 | /** 267 | * SteamApiError: 268 | * @STEAM_API_ERROR_CAPTCHA: Captcha required. 269 | * @STEAM_API_ERROR_EXPRIED: Session expired. 270 | * @STEAM_API_ERROR_GENERAL: General failure. 271 | * @STEAM_API_ERROR_PARSER: Parsing failure. 272 | * @STEAM_API_ERROR_STEAMGUARD: SteamGuard required. 273 | * @STEAM_API_ERROR_UNKNOWN: Unknown failure. 274 | * 275 | * The error codes for the #STEAM_API_ERROR domain. 276 | */ 277 | typedef enum 278 | { 279 | STEAM_API_ERROR_CAPTCHA, 280 | STEAM_API_ERROR_EXPRIED, 281 | STEAM_API_ERROR_GENERAL, 282 | STEAM_API_ERROR_PARSER, 283 | STEAM_API_ERROR_STEAMGUARD, 284 | STEAM_API_ERROR_UNKNOWN 285 | } SteamApiError; 286 | 287 | /** 288 | * SteamApiReqFlags: 289 | * @STEAM_API_REQ_FLAG_NOJSON: Skip JSON parsing. 290 | * 291 | * The #SteamApiReq flags. 292 | */ 293 | typedef enum 294 | { 295 | STEAM_API_REQ_FLAG_NOJSON = 1 << 0 296 | } SteamApiReqFlags; 297 | 298 | /** 299 | * SteamApiAcceptType: 300 | * @STEAM_API_ACCEPT_TYPE_DEFAULT: Accept the request. 301 | * @STEAM_API_ACCEPT_TYPE_BLOCK: Block the other user. 302 | * @STEAM_API_ACCEPT_TYPE_IGNORE: Ignore the request. 303 | * 304 | * The friend acceptance types. 305 | */ 306 | typedef enum 307 | { 308 | STEAM_API_ACCEPT_TYPE_DEFAULT = 0, 309 | STEAM_API_ACCEPT_TYPE_BLOCK, 310 | STEAM_API_ACCEPT_TYPE_IGNORE 311 | } SteamApiAcceptType; 312 | 313 | /** 314 | * SteamApi: 315 | * @info: The #SteamUserInfo of the user. 316 | * @http: The #SteamHttp for API requests. 317 | * @msgs: The #GQueue of message based #SteamApiReq. 318 | * @online: The online state of the user. 319 | * @idle: The idle time of the user. 320 | * @umqid: The unique device identifier. 321 | * @token: The session token (mobile requests). 322 | * @sessid: The session identifier (community requests). 323 | * @lmid: The last message identifier. 324 | * @time: The logon timestamp (UTC). 325 | * @autht: The #SteamApiAuthType. 326 | * @cgid: The captcha GID (authentication). 327 | * @esid: The email SteamID (authentication). 328 | * @pkmod: The PKCS (RSA) modulus (authentication). 329 | * @pkexp: The PKCS (RSA) exponent (authentication). 330 | * @pktime: The PKCS (RSA) key time (authentication). 331 | * 332 | * Represents a Steam connection. 333 | */ 334 | struct _SteamApi 335 | { 336 | SteamUserInfo *info; 337 | SteamHttp *http; 338 | GQueue *msgs; 339 | gboolean online; 340 | guint32 idle; 341 | 342 | gchar *umqid; 343 | gchar *token; 344 | gchar *sessid; 345 | gint64 lmid; 346 | gint64 time; 347 | 348 | SteamApiAuthType autht; 349 | gchar *cgid; 350 | gchar *esid; 351 | gchar *pkmod; 352 | gchar *pkexp; 353 | gchar *pktime; 354 | }; 355 | 356 | /** 357 | * SteamApiReq: 358 | * @api: The #SteamApi. 359 | * @flags: The #SteamApiReqFlags. 360 | * @req: The #SteamHttpReq. 361 | * @err: The #GError or #NULL. 362 | * @msgs: The #GQueue of #SteamApiMsg. 363 | * @infs: The #GQueue of #SteamUserInfo. 364 | * @infr: The #GQueue of #SteamUserInfo remaining. 365 | * @func: The #SteamApiFunc or #NULL. 366 | * @data: The user define data or #NULL. 367 | * @punc: The #SteamApiParser or #NULL. 368 | * 369 | * Represents a Steam request. 370 | */ 371 | struct _SteamApiReq 372 | { 373 | SteamApi *api; 374 | SteamApiReqFlags flags; 375 | SteamHttpReq *req; 376 | 377 | GError *err; 378 | GQueue *msgs; 379 | GQueue *infs; 380 | GQueue *infr; 381 | 382 | SteamApiFunc func; 383 | gpointer data; 384 | SteamApiParser punc; 385 | }; 386 | 387 | /** 388 | * steam_api_error_quark: 389 | * 390 | * Gets the #GQuark of the domain of API errors. 391 | * 392 | * Returns: The #GQuark of the domain. 393 | */ 394 | GQuark 395 | steam_api_error_quark(void); 396 | 397 | /** 398 | * steam_api_new: 399 | * 400 | * Creates a new #SteamApi. The returned #SteamApi should be freed with 401 | * #steam_api_free() when no longer needed. 402 | * 403 | * Returns: The #SteamApi. 404 | */ 405 | SteamApi * 406 | steam_api_new(void); 407 | 408 | /** 409 | * steam_api_free_auth: 410 | * @api: The #SteamApi. 411 | * 412 | * Frees all memory used by the #SteamApi for authentication. 413 | */ 414 | void 415 | steam_api_free_auth(SteamApi *api); 416 | 417 | /** 418 | * steam_api_free: 419 | * @api: The #SteamApi. 420 | * 421 | * Frees all memory used by the #SteamApi. 422 | */ 423 | void 424 | steam_api_free(SteamApi *api); 425 | 426 | /** 427 | * steam_api_captcha_url: 428 | * @cgid: The captcha GID. 429 | * 430 | * Gets the captcha URL for the captcha GID. The returned string should 431 | * be freed with #g_free() when no longer needed. 432 | * 433 | * Returns: The captcha URL or #NULL on error. 434 | */ 435 | gchar * 436 | steam_api_captcha_url(const gchar *cgid); 437 | 438 | /** 439 | * steam_api_rehash: 440 | * @api: The #SteamApi. 441 | * 442 | * Rehashes and updates internal data of the #SteamApi. This should be 443 | * called whenever properties are modified. 444 | */ 445 | void 446 | steam_api_rehash(SteamApi *api); 447 | 448 | /** 449 | * steam_api_req_new: 450 | * @api: The #SteamApi. 451 | * @func: The #SteamApiFunc or #NULL. 452 | * @data: The user defined data or #NULL. 453 | * 454 | * Creates a new #SteamApiReq. The returned #SteamApiReq should be 455 | * freed with #steam_api_req_free() when no longer needed. 456 | * 457 | * Returns: The #SteamApiReq. 458 | */ 459 | SteamApiReq * 460 | steam_api_req_new(SteamApi *api, SteamApiFunc func, gpointer data); 461 | 462 | /** 463 | * steam_api_req_fwd: 464 | * @req: The #SteamApiReq. 465 | * 466 | * Creates a new forwarded #SteamApiReq. This NULLs the err, func, 467 | * data, msgs, infs, and infr data fields in the source #SteamApiReq, 468 | * and forwards them to the return #SteamApiReq. The returned 469 | * #SteamApiReq should be free with #steam_api_req_free() when no 470 | * longer needed. 471 | * 472 | * Returns: The #SteamApiReq. 473 | */ 474 | SteamApiReq * 475 | steam_api_req_fwd(SteamApiReq *req); 476 | 477 | /** 478 | * steam_api_req_free: 479 | * @req: The #SteamApiReq. 480 | * 481 | * Frees all memory used by the #SteamApiReq. 482 | */ 483 | void 484 | steam_api_req_free(SteamApiReq *req); 485 | 486 | /** 487 | * steam_api_req_init: 488 | * @req: The #SteamApiReq. 489 | * @host: The request hostname. 490 | * @path: The request pathname. 491 | * 492 | * Initializes a new SSL based #SteamHttpReq for the #SteamApiReq. 493 | */ 494 | void 495 | steam_api_req_init(SteamApiReq *req, const gchar *host, const gchar *path); 496 | 497 | /** 498 | * steam_api_req_auth: 499 | * @req: The #SteamApiReq. 500 | * @user: The username. 501 | * @pass: The password. 502 | * @authcode: The authorization code (Steam Guard) or #NULL. 503 | * @captcha: The captcha code or #NULL. 504 | * 505 | * Sends na authorization request. This is typically called twice to 506 | * complete the authorization process. First, the user is authenticated 507 | * partially, and then the Steam Guard code is requested. Then, with the 508 | * Steam Guard code, the authentication process can be completed. 509 | */ 510 | void 511 | steam_api_req_auth(SteamApiReq *req, const gchar *user, const gchar *pass, 512 | const gchar *authcode, const gchar *captcha); 513 | 514 | /** 515 | * steam_api_req_friends: 516 | * @req: The #SteamApiReq. 517 | * 518 | * Sends a friend list request. This returns the entire list of friends 519 | * for the #SteamApi user, including ignored friends. 520 | */ 521 | void 522 | steam_api_req_friends(SteamApiReq *req); 523 | 524 | /** 525 | * steam_api_req_key: 526 | * @req: The #SteamApiReq. 527 | * @user: The username. 528 | * 529 | * Sends a key request. The PKCS key is used to encrypt the password 530 | * before it is sent during the authentication phase. 531 | */ 532 | void 533 | steam_api_req_key(SteamApiReq *req, const gchar *user); 534 | 535 | /** 536 | * steam_api_req_logoff: 537 | * @req: The #SteamApiReq. 538 | * 539 | * Sends a logoff request. This simply logs the #SteamApi user off. 540 | */ 541 | void 542 | steam_api_req_logoff(SteamApiReq *req); 543 | 544 | /** 545 | * steam_api_req_logon: 546 | * @req: The #SteamApiReq. 547 | * 548 | * Sends a logon request. This simply logs the #SteamApi user on. The 549 | * #SteamApi user must be authenticated via #steam_api_req_auth() 550 | * before they can logon. 551 | */ 552 | void 553 | steam_api_req_logon(SteamApiReq *req); 554 | 555 | /** 556 | * steam_api_req_msg: 557 | * @req: The #SteamApiReq. 558 | * @msg: The #SteamUserMsg. 559 | * 560 | * Sends a message request. This sends a #SteamUserMsg to a Steam user. 561 | */ 562 | void 563 | steam_api_req_msg(SteamApiReq *req, const SteamUserMsg *msg); 564 | 565 | /** 566 | * steam_api_req_msg_info: 567 | * @req: The #SteamApiReq. 568 | * 569 | * Sends a message information request. This retrieves the last know 570 | * message info of the #SteamUserInfos. 571 | */ 572 | void 573 | steam_api_req_msg_info(SteamApiReq *req); 574 | 575 | /** 576 | * steam_api_req_msgs: 577 | * @req: The #SteamApiReq. 578 | * @id: The #SteamId. 579 | * @since: The since timestamp. 580 | * 581 | * Sends a message log request. 582 | */ 583 | void 584 | steam_api_req_msgs(SteamApiReq *req, SteamId id, gint64 since); 585 | 586 | /** 587 | * steam_api_req_msgs_read: 588 | * @req: The #SteamApiReq. 589 | * @id: The #SteamId. 590 | * 591 | * Sends a messages read request. 592 | */ 593 | void 594 | steam_api_req_msgs_read(SteamApiReq *req, SteamId id); 595 | 596 | /** 597 | * steam_api_req_poll: 598 | * @req: The #SteamApiReq. 599 | * 600 | * Sends a poll request. This retrieves new messages from Steam. In 601 | * addition, this keeps the #SteamApi session active, and must be 602 | * called every 30 seconds. 603 | */ 604 | void 605 | steam_api_req_poll(SteamApiReq *req); 606 | 607 | /** 608 | * steam_api_req_user_accept: 609 | * @req: The #SteamApiReq. 610 | * @id: The #SteamId. 611 | * @type: The #SteamApiAcceptType. 612 | * 613 | * Sends a friend accept request. If someone has requested friendship 614 | * with the #SteamApi user, this will accept the friendship request. 615 | */ 616 | void 617 | steam_api_req_user_accept(SteamApiReq *req, SteamId id, 618 | SteamApiAcceptType type); 619 | 620 | /** 621 | * steam_api_req_user_add: 622 | * @req: The #SteamApiReq. 623 | * @id: The #SteamId. 624 | * 625 | * Sends a friend add request. This will request the friendship of 626 | * another Steam user. The Steam user is not really a friend until 627 | * they accept the request on their end. 628 | */ 629 | void 630 | steam_api_req_user_add(SteamApiReq *req, SteamId id); 631 | 632 | /** 633 | * steam_api_req_user_ignore: 634 | * @req: The #SteamApiReq. 635 | * @id: The #SteamId. 636 | * @ignore: #TRUE to ignore, otherwise #FALSE. 637 | * 638 | * Sends a friend ignore request. This will either ignore or unignore 639 | * a Steam user from the #SteamApi user. 640 | */ 641 | void 642 | steam_api_req_user_ignore(SteamApiReq *req, SteamId id, gboolean ignore); 643 | 644 | /** 645 | * steam_api_req_user_info: 646 | * @req: The #SteamApiReq. 647 | * 648 | * Sends a user information request. This retrieves the user information 649 | * for all users in the #SteamApiReq->infos list. 650 | */ 651 | void 652 | steam_api_req_user_info(SteamApiReq *req); 653 | 654 | /** 655 | * steam_api_req_user_info_nicks: 656 | * @req: The #SteamApiReq. 657 | * 658 | * Sends a user nickname information request. This retrieves the user 659 | * nicname information for all users in the #SteamApiReq->infos list. 660 | */ 661 | void 662 | steam_api_req_user_info_nicks(SteamApiReq *req); 663 | 664 | /** 665 | * steam_api_req_user_remove: 666 | * @req: The #SteamApiReq. 667 | * @id: The #SteamId. 668 | * 669 | * Sends a friend remove request. This will remove a Steam friend from 670 | * the friend list of the #SteamApi user. This does not block the user, 671 | * see: #steam_api_req_user_ignore(). 672 | */ 673 | void 674 | steam_api_req_user_remove(SteamApiReq *req, SteamId id); 675 | 676 | /** 677 | * steam_api_req_user_search: 678 | * @req: The #SteamApiReq. 679 | * @name: The username. 680 | * @count: The amount of search results. 681 | * 682 | * Sends a user search request. This searches for Steam users based on 683 | * a search term. This is very useful when attempting to add Steam 684 | * users by their name via #steam_api_req_user_add(). 685 | */ 686 | void 687 | steam_api_req_user_search(SteamApiReq *req, const gchar *name, guint count); 688 | 689 | #endif /* _STEAM_API_H_ */ 690 | -------------------------------------------------------------------------------- /steam/steam-crypt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2016 James Geboski 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include 19 | #include 20 | 21 | #include "steam-crypt.h" 22 | #include "steam-util.h" 23 | 24 | GByteArray * 25 | steam_crypt_rsa_enc(const GByteArray *mod, const GByteArray *exp, 26 | const GByteArray *bytes) 27 | { 28 | GByteArray *ret = NULL; 29 | gcry_error_t res; 30 | gcry_mpi_t dmpi = NULL; 31 | gcry_mpi_t empi = NULL; 32 | gcry_mpi_t mmpi = NULL; 33 | gcry_sexp_t cata = NULL; 34 | gcry_sexp_t data = NULL; 35 | gcry_sexp_t kata = NULL; 36 | gsize size; 37 | 38 | g_return_val_if_fail(mod != NULL, NULL); 39 | g_return_val_if_fail(exp != NULL, NULL); 40 | g_return_val_if_fail(bytes != NULL, NULL); 41 | 42 | res = gcry_mpi_scan(&mmpi, GCRYMPI_FMT_USG, mod->data, 43 | mod->len, NULL); 44 | res |= gcry_mpi_scan(&empi, GCRYMPI_FMT_USG, exp->data, 45 | exp->len, NULL); 46 | res |= gcry_mpi_scan(&dmpi, GCRYMPI_FMT_USG, bytes->data, 47 | bytes->len, NULL); 48 | 49 | if (G_UNLIKELY(res != 0)) { 50 | goto finish; 51 | } 52 | 53 | res = gcry_sexp_build(&kata, NULL, "(public-key(rsa(n %m)(e %m)))", 54 | mmpi, empi); 55 | res |= gcry_sexp_build(&data, NULL, "(data(flags pkcs1)(value %m))", 56 | dmpi); 57 | 58 | if (G_UNLIKELY(res != 0)) { 59 | goto finish; 60 | } 61 | 62 | res = gcry_pk_encrypt(&cata, data, kata); 63 | 64 | if (G_UNLIKELY(res != 0)) { 65 | goto finish; 66 | } 67 | 68 | gcry_sexp_release(data); 69 | data = gcry_sexp_find_token(cata, "a", 0); 70 | 71 | if (G_UNLIKELY(data == NULL)) { 72 | g_warn_if_reached(); 73 | goto finish; 74 | } 75 | 76 | gcry_mpi_release(dmpi); 77 | dmpi = gcry_sexp_nth_mpi(data, 1, GCRYMPI_FMT_USG); 78 | 79 | if (G_UNLIKELY(dmpi == NULL)) { 80 | g_warn_if_reached(); 81 | goto finish; 82 | } 83 | 84 | ret = g_byte_array_new(); 85 | g_byte_array_set_size(ret, mod->len); 86 | gcry_mpi_print(GCRYMPI_FMT_USG, ret->data, ret->len, &size, dmpi); 87 | 88 | g_warn_if_fail(size <= mod->len); 89 | g_byte_array_set_size(ret, size); 90 | 91 | finish: 92 | gcry_sexp_release(cata); 93 | gcry_sexp_release(data); 94 | gcry_sexp_release(kata); 95 | 96 | gcry_mpi_release(dmpi); 97 | gcry_mpi_release(empi); 98 | gcry_mpi_release(mmpi); 99 | 100 | return ret; 101 | } 102 | 103 | gchar * 104 | steam_crypt_rsa_enc_str(const gchar *mod, const gchar *exp, const gchar *str) 105 | { 106 | GByteArray *bytes; 107 | GByteArray *enc; 108 | GByteArray *eytes; 109 | GByteArray *mytes; 110 | gchar *ret; 111 | 112 | g_return_val_if_fail(mod != NULL, NULL); 113 | g_return_val_if_fail(exp != NULL, NULL); 114 | g_return_val_if_fail(str != NULL, NULL); 115 | 116 | mytes = steam_util_str_hex2bytes(mod); 117 | 118 | if (G_UNLIKELY(mytes == NULL)) { 119 | return NULL; 120 | } 121 | 122 | eytes = steam_util_str_hex2bytes(exp); 123 | 124 | if (G_UNLIKELY(eytes == NULL)) { 125 | g_byte_array_free(mytes, TRUE); 126 | return NULL; 127 | } 128 | 129 | bytes = g_byte_array_new(); 130 | g_byte_array_append(bytes, (guint8 *) str, strlen(str)); 131 | enc = steam_crypt_rsa_enc(mytes, eytes, bytes); 132 | 133 | g_byte_array_free(bytes, TRUE); 134 | g_byte_array_free(eytes, TRUE); 135 | g_byte_array_free(mytes, TRUE); 136 | 137 | if (G_UNLIKELY(enc == NULL)) { 138 | return NULL; 139 | } 140 | 141 | ret = g_base64_encode(enc->data, enc->len); 142 | g_byte_array_free(enc, TRUE); 143 | return ret; 144 | } 145 | -------------------------------------------------------------------------------- /steam/steam-crypt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2016 James Geboski 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef _STEAM_CRYPT_H_ 19 | #define _STEAM_CRYPT_H_ 20 | 21 | /** 22 | * SECTION:crypt 23 | * @section_id: steam-crypt 24 | * @short_description: steam-crypt.h 25 | * @title: Cryptography Utilities 26 | * 27 | * The cryptography utilities. 28 | */ 29 | 30 | #include "steam-glib.h" 31 | 32 | /** 33 | * steam_crypt_rsa_enc: 34 | * @mod: The modulus. 35 | * @exp: The exponent. 36 | * @bytes: The #GByteArray. 37 | * 38 | * Encrypts the #GByteArray via an RSA public key modules and exponent. 39 | * The returned #GByteArray should be freed with #g_byte_array_free() 40 | * when no longer needed. 41 | * 42 | * Returns: The encrypted #GByteArray or #NULL on error. 43 | */ 44 | GByteArray * 45 | steam_crypt_rsa_enc(const GByteArray *mod, const GByteArray *exp, 46 | const GByteArray *bytes); 47 | 48 | /** 49 | * steam_crypt_rsa_enc_str: 50 | * @mod: The hexadecimal modulus string. 51 | * @exp: The hexadecimal exponent string. 52 | * @str: The string. 53 | * 54 | * Encrypts the string via an RSA public key modulus and exponent. The 55 | * modulus and exponent must be valid hexadecimal strings. The return 56 | * string is encoded with base64 encoding. The returned string should 57 | * be freed with #g_free() when no longer needed. 58 | * 59 | * Returns: The base64 encoded string or #NULL on error. 60 | */ 61 | gchar * 62 | steam_crypt_rsa_enc_str(const gchar *mod, const gchar *exp, const gchar *str); 63 | 64 | #endif /* _STEAM_CRYPT_H_ */ 65 | -------------------------------------------------------------------------------- /steam/steam-glib.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2016 James Geboski 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef _STEAM_GLIB_H_ 19 | #define _STEAM_GLIB_H_ 20 | 21 | #include 22 | #include 23 | 24 | #if !GLIB_CHECK_VERSION(2, 32, 0) 25 | static inline void 26 | g_queue_free_full(GQueue *queue, GDestroyNotify free_func) 27 | { 28 | g_queue_foreach(queue, (GFunc) free_func, NULL); 29 | g_queue_free(queue); 30 | } 31 | #endif /* 2.32.0 */ 32 | 33 | #endif /* _STEAM_GLIB_H_ */ 34 | -------------------------------------------------------------------------------- /steam/steam-http.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2016 James Geboski 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include 19 | #include 20 | 21 | #include "steam-http.h" 22 | #include "steam-util.h" 23 | 24 | GQuark 25 | steam_http_error_quark(void) 26 | { 27 | static GQuark q; 28 | 29 | if (G_UNLIKELY(q == 0)) { 30 | q = g_quark_from_static_string("steam-http-error-quark"); 31 | } 32 | 33 | return q; 34 | } 35 | 36 | SteamHttp * 37 | steam_http_new(const gchar *agent) 38 | { 39 | SteamHttp *http; 40 | 41 | http = g_new0(SteamHttp, 1); 42 | http->agent = g_strdup(agent); 43 | http->reqs = g_hash_table_new(g_direct_hash, g_direct_equal); 44 | http->cookies = g_hash_table_new_full(g_str_hash, 45 | (GEqualFunc) steam_util_str_iequal, 46 | g_free, g_free); 47 | return http; 48 | } 49 | 50 | void 51 | steam_http_free_reqs(SteamHttp *http) 52 | { 53 | GHashTableIter iter; 54 | gpointer key; 55 | 56 | if (G_UNLIKELY(http == NULL)) { 57 | return; 58 | } 59 | 60 | g_hash_table_iter_init(&iter, http->reqs); 61 | 62 | while (g_hash_table_iter_next(&iter, &key, NULL)) { 63 | g_hash_table_iter_remove(&iter); 64 | steam_http_req_free(key); 65 | } 66 | } 67 | 68 | void 69 | steam_http_free(SteamHttp *http) 70 | { 71 | if (G_UNLIKELY(http == NULL)) { 72 | return; 73 | } 74 | 75 | steam_http_free_reqs(http); 76 | g_hash_table_destroy(http->reqs); 77 | g_hash_table_destroy(http->cookies); 78 | 79 | g_free(http->agent); 80 | g_free(http); 81 | } 82 | 83 | static void 84 | steam_http_tree_ins(GHashTable *table, const SteamHttpPair *pair, va_list ap) 85 | { 86 | const SteamHttpPair *p; 87 | gchar *key; 88 | gchar *val; 89 | 90 | for (p = pair; p != NULL; ) { 91 | if (p->key == NULL) { 92 | continue; 93 | } 94 | 95 | key = g_strdup(p->key); 96 | val = g_strdup(p->val); 97 | 98 | g_hash_table_replace(table, key, val); 99 | p = va_arg(ap, const SteamHttpPair*); 100 | } 101 | } 102 | 103 | const gchar * 104 | steam_http_cookies_get(SteamHttp *http, const gchar *name) 105 | { 106 | g_return_val_if_fail(http != NULL, NULL); 107 | 108 | return g_hash_table_lookup(http->cookies, name); 109 | } 110 | 111 | void 112 | steam_http_cookies_set(SteamHttp *http, const SteamHttpPair *pair, ...) 113 | { 114 | va_list ap; 115 | 116 | g_return_if_fail(http != NULL); 117 | 118 | va_start(ap, pair); 119 | steam_http_tree_ins(http->cookies, pair, ap); 120 | va_end(ap); 121 | } 122 | 123 | void 124 | steam_http_cookies_parse_req(SteamHttp *http, const SteamHttpReq *req) 125 | { 126 | gchar **hdrs; 127 | gchar **kv; 128 | gchar *str; 129 | gsize i; 130 | gsize j; 131 | 132 | g_return_if_fail(http != NULL); 133 | g_return_if_fail(req != NULL); 134 | 135 | if (req->request == NULL) { 136 | return; 137 | } 138 | 139 | hdrs = g_strsplit(req->request->reply_headers, "\r\n", 0); 140 | 141 | for (i = 0; hdrs[i] != NULL; i++) { 142 | if (g_ascii_strncasecmp(hdrs[i], "Set-Cookie", 10) != 0) { 143 | continue; 144 | } 145 | 146 | str = strchr(hdrs[i], ';'); 147 | 148 | if (str != NULL) { 149 | str[0] = 0; 150 | } 151 | 152 | str = strchr(hdrs[i], ':'); 153 | 154 | if (str == NULL) { 155 | continue; 156 | } 157 | 158 | str = g_strstrip(++str); 159 | kv = g_strsplit(str, "=", 2); 160 | 161 | for (j = 0; kv[j] != NULL; j++) { 162 | str = steam_http_uri_unescape(kv[j]); 163 | g_free(kv[j]); 164 | kv[j] = str; 165 | } 166 | 167 | if (g_strv_length(kv) > 1) { 168 | steam_http_cookies_set(http, STEAM_HTTP_PAIR(kv[0], kv[1]), NULL); 169 | } 170 | 171 | g_strfreev(kv); 172 | } 173 | 174 | g_strfreev(hdrs); 175 | } 176 | 177 | void 178 | steam_http_cookies_parse_str(SteamHttp *http, const gchar *data) 179 | { 180 | gchar **ckis; 181 | gchar **kv; 182 | gchar *str; 183 | gsize i; 184 | gsize j; 185 | 186 | g_return_if_fail(http != NULL); 187 | g_return_if_fail(data != NULL); 188 | 189 | ckis = g_strsplit(data, ";", 0); 190 | 191 | for (i = 0; ckis[i] != NULL; i++) { 192 | str = g_strstrip(ckis[i]); 193 | kv = g_strsplit(str, "=", 2); 194 | 195 | for (j = 0; kv[j] != NULL; j++) { 196 | str = steam_http_uri_unescape(kv[j]); 197 | g_free(kv[j]); 198 | kv[j] = str; 199 | } 200 | 201 | if (g_strv_length(kv) > 1) { 202 | steam_http_cookies_set(http, STEAM_HTTP_PAIR(kv[0], kv[1]), NULL); 203 | } 204 | 205 | g_strfreev(kv); 206 | } 207 | 208 | g_strfreev(ckis); 209 | } 210 | 211 | gchar * 212 | steam_http_cookies_str(SteamHttp *http) 213 | { 214 | gchar *key; 215 | gchar *str; 216 | gchar *val; 217 | GHashTableIter iter; 218 | GString *gstr; 219 | 220 | g_return_val_if_fail(http != NULL, NULL); 221 | 222 | gstr = g_string_new(NULL); 223 | g_hash_table_iter_init(&iter, http->cookies); 224 | 225 | while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val)) { 226 | if (val == NULL) { 227 | val = ""; 228 | } 229 | 230 | key = steam_http_uri_escape(key); 231 | val = steam_http_uri_escape(val); 232 | 233 | str = (gstr->len > 0) ? "; " : ""; 234 | g_string_append_printf(gstr, "%s%s=%s", str, key, val); 235 | 236 | g_free(key); 237 | g_free(val); 238 | } 239 | 240 | str = g_strdup(gstr->str); 241 | g_string_free(gstr, TRUE); 242 | return str; 243 | } 244 | 245 | SteamHttpReq * 246 | steam_http_req_new(SteamHttp *http, const gchar *host, gint port, 247 | const gchar *path, SteamHttpFunc func, gpointer data) 248 | { 249 | SteamHttpReq *req; 250 | 251 | req = g_new0(SteamHttpReq, 1); 252 | req->http = http; 253 | req->host = g_strdup(host); 254 | req->port = port; 255 | req->path = g_strdup(path); 256 | req->func = func; 257 | req->data = data; 258 | 259 | req->headers = g_hash_table_new_full(g_str_hash, 260 | (GEqualFunc) steam_util_str_iequal, 261 | g_free, g_free); 262 | req->params = g_hash_table_new_full(g_str_hash, 263 | (GEqualFunc) steam_util_str_iequal, 264 | g_free, g_free); 265 | 266 | steam_http_req_headers_set(req, 267 | STEAM_HTTP_PAIR("User-Agent", http->agent), 268 | STEAM_HTTP_PAIR("Host", host), 269 | STEAM_HTTP_PAIR("Accept", "*/*"), 270 | STEAM_HTTP_PAIR("Connection", "Close"), 271 | NULL 272 | ); 273 | 274 | return req; 275 | } 276 | 277 | static void 278 | steam_http_req_close_nuller(struct http_request *request) 279 | { 280 | 281 | } 282 | 283 | static void 284 | steam_http_req_close(SteamHttpReq *req, gboolean callback) 285 | { 286 | g_return_if_fail(req != NULL); 287 | b_event_remove(req->toid); 288 | 289 | if ((req->err == NULL) && (req->scode == 0)) { 290 | g_set_error(&req->err, STEAM_HTTP_ERROR, STEAM_HTTP_ERROR_CLOSED, 291 | "Request closed"); 292 | } 293 | 294 | if (callback && (req->func != NULL)) { 295 | req->func(req, req->data); 296 | } 297 | 298 | if (req->request != NULL) { 299 | /* Prevent more than one call to request->func() */ 300 | req->request->func = steam_http_req_close_nuller; 301 | req->request->data = NULL; 302 | 303 | if (!(req->request->flags & STEAM_HTTP_CLIENT_FREED)) { 304 | http_close(req->request); 305 | } 306 | } 307 | 308 | req->status = NULL; 309 | req->scode = 0; 310 | req->header = NULL; 311 | req->body = NULL; 312 | req->body_size = 0; 313 | req->toid = 0; 314 | req->request = NULL; 315 | } 316 | 317 | void 318 | steam_http_req_free(SteamHttpReq *req) 319 | { 320 | if (G_UNLIKELY(req == NULL)) { 321 | return; 322 | } 323 | 324 | steam_http_req_close(req, TRUE); 325 | 326 | if (req->err != NULL) { 327 | g_error_free(req->err); 328 | } 329 | 330 | g_hash_table_destroy(req->headers); 331 | g_hash_table_destroy(req->params); 332 | 333 | g_free(req->path); 334 | g_free(req->host); 335 | g_free(req); 336 | } 337 | 338 | static void 339 | steam_http_req_debug(SteamHttpReq *req, gboolean response, 340 | const gchar *header, const gchar *body) 341 | { 342 | const gchar *act; 343 | const gchar *prot; 344 | const gchar *type; 345 | gchar **ls; 346 | gchar *str; 347 | guint i; 348 | 349 | if (req->err != NULL) { 350 | str = g_strdup_printf(" (%s)", req->err->message); 351 | } else if (req->status != NULL) { 352 | str = g_strdup_printf(" (%s)", req->status); 353 | } else { 354 | str = g_strdup(""); 355 | } 356 | 357 | act = response ? "Response" : "Request"; 358 | type = (req->flags & STEAM_HTTP_REQ_FLAG_POST) ? "POST" : "GET"; 359 | prot = (req->flags & STEAM_HTTP_REQ_FLAG_SSL) ? "https" : "http"; 360 | 361 | steam_util_debug_info("%s %s (%p): %s://%s:%d%s%s", type, act, req, 362 | prot, req->host, req->port, req->path, str); 363 | g_free(str); 364 | 365 | if (req->rsc > 0) { 366 | steam_util_debug_info("Reattempt: #%u", req->rsc); 367 | } 368 | 369 | if ((header != NULL) && (strlen(header) > 0)) { 370 | ls = g_strsplit(header, "\n", 0); 371 | 372 | for (i = 0; ls[i] != NULL; i++) { 373 | steam_util_debug_info(" %s", ls[i]); 374 | } 375 | 376 | g_strfreev(ls); 377 | } else { 378 | steam_util_debug_info(" ** No header data **"); 379 | steam_util_debug_info("%s", ""); 380 | } 381 | 382 | if ((body != NULL) && (strlen(body) > 0)) { 383 | ls = g_strsplit(body, "\n", 0); 384 | 385 | for (i = 0; ls[i] != NULL; i++) { 386 | steam_util_debug_info(" %s", ls[i]); 387 | } 388 | 389 | g_strfreev(ls); 390 | } else { 391 | steam_util_debug_info(" ** No body data **"); 392 | } 393 | } 394 | 395 | void 396 | steam_http_req_headers_set(SteamHttpReq *req, const SteamHttpPair *pair, ...) 397 | { 398 | va_list ap; 399 | 400 | g_return_if_fail(req != NULL); 401 | 402 | va_start(ap, pair); 403 | steam_http_tree_ins(req->headers, pair, ap); 404 | va_end(ap); 405 | } 406 | 407 | void 408 | steam_http_req_params_set(SteamHttpReq *req, const SteamHttpPair *pair, ...) 409 | { 410 | va_list ap; 411 | 412 | g_return_if_fail(req != NULL); 413 | 414 | va_start(ap, pair); 415 | steam_http_tree_ins(req->params, pair, ap); 416 | va_end(ap); 417 | } 418 | 419 | static gboolean 420 | steam_http_req_done_error(gpointer data, gint fd, b_input_condition cond) 421 | { 422 | SteamHttpReq *req = data; 423 | steam_http_req_send(req); 424 | return FALSE; 425 | } 426 | 427 | static void 428 | steam_http_req_done(SteamHttpReq *req) 429 | { 430 | steam_http_req_debug(req, TRUE, req->header, req->body); 431 | 432 | if (req->err != NULL) { 433 | if (req->rsc < STEAM_HTTP_RESEND_MAX) { 434 | steam_http_req_close(req, FALSE); 435 | g_error_free(req->err); 436 | req->err = NULL; 437 | 438 | req->toid = b_timeout_add(STEAM_HTTP_RESEND_TIMEOUT, 439 | steam_http_req_done_error, req); 440 | req->rsc++; 441 | return; 442 | } 443 | 444 | g_prefix_error(&req->err, "HTTP: "); 445 | } 446 | 447 | g_hash_table_remove(req->http->reqs, req); 448 | steam_http_req_free(req); 449 | } 450 | 451 | static void 452 | steam_http_req_cb(struct http_request *request) 453 | { 454 | SteamHttpReq *req = request->data; 455 | 456 | /* Shortcut request elements */ 457 | req->status = request->status_string; 458 | req->scode = request->status_code; 459 | req->header = request->reply_headers; 460 | req->body = request->reply_body; 461 | req->body_size = request->body_size; 462 | 463 | switch (req->scode) { 464 | case 200: 465 | case 301: 466 | case 302: 467 | case 303: 468 | case 307: 469 | break; 470 | 471 | default: 472 | g_set_error(&req->err, STEAM_HTTP_ERROR, req->scode, "%s", req->status); 473 | } 474 | 475 | req->request->flags |= STEAM_HTTP_CLIENT_FREED; 476 | steam_http_req_done(req); 477 | } 478 | 479 | static gboolean 480 | steam_http_req_send_timeout(gpointer data, gint fd, 481 | b_input_condition cond) 482 | { 483 | SteamHttpReq *req = data; 484 | 485 | g_set_error(&req->err, STEAM_HTTP_ERROR, STEAM_HTTP_ERROR_TIMEOUT, 486 | "Request timed out"); 487 | 488 | req->toid = 0; 489 | steam_http_req_done(req); 490 | return FALSE; 491 | } 492 | 493 | static void 494 | steam_http_req_asm(SteamHttpReq *req, gchar **hs, gchar **ps, gchar **fs) 495 | { 496 | gchar *key; 497 | gchar *str; 498 | gchar *val; 499 | GHashTableIter iter; 500 | GString *hgs; 501 | GString *pgs; 502 | 503 | g_hash_table_iter_init(&iter, req->params); 504 | pgs = g_string_new(NULL); 505 | 506 | while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val)) { 507 | if (val == NULL) { 508 | val = ""; 509 | } 510 | 511 | key = steam_http_uri_escape(key); 512 | val = steam_http_uri_escape(val); 513 | 514 | str = (pgs->len > 0) ? "&" : ""; 515 | g_string_append_printf(pgs, "%s%s=%s", str, key, val); 516 | 517 | g_free(key); 518 | g_free(val); 519 | } 520 | 521 | if (g_hash_table_size(req->http->cookies) > 0) { 522 | str = steam_http_cookies_str(req->http); 523 | steam_http_req_headers_set(req, STEAM_HTTP_PAIR("Cookie", str), NULL); 524 | g_free(str); 525 | } 526 | 527 | if (req->flags & STEAM_HTTP_REQ_FLAG_POST) { 528 | str = g_strdup_printf("%" G_GSIZE_FORMAT, pgs->len); 529 | 530 | steam_http_req_headers_set(req, 531 | STEAM_HTTP_PAIR("Content-Type", "application/" 532 | "x-www-form-urlencoded"), 533 | STEAM_HTTP_PAIR("Content-Length", str), 534 | NULL 535 | ); 536 | 537 | g_free(str); 538 | } 539 | 540 | g_hash_table_iter_init(&iter, req->headers); 541 | hgs = g_string_new(NULL); 542 | 543 | while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val)) { 544 | if (val == NULL) { 545 | val = ""; 546 | } 547 | 548 | g_string_append_printf(hgs, "%s: %s\r\n", key, val); 549 | } 550 | 551 | if (req->flags & STEAM_HTTP_REQ_FLAG_POST) { 552 | *fs = g_strdup_printf("POST %s HTTP/1.1\r\n%s\r\n%s", 553 | req->path, hgs->str, pgs->str); 554 | } else { 555 | *fs = g_strdup_printf("GET %s?%s HTTP/1.1\r\n%s\r\n", 556 | req->path, pgs->str, hgs->str); 557 | } 558 | 559 | *hs = g_string_free(hgs, FALSE); 560 | *ps = g_string_free(pgs, FALSE); 561 | } 562 | 563 | void 564 | steam_http_req_send(SteamHttpReq *req) 565 | { 566 | gchar *hs; 567 | gchar *ps; 568 | gchar *str; 569 | 570 | g_return_if_fail(req != NULL); 571 | steam_http_req_asm(req, &hs, &ps, &str); 572 | steam_http_req_debug(req, FALSE, hs, ps); 573 | 574 | req->request = http_dorequest(req->host, req->port, 575 | (req->flags & STEAM_HTTP_REQ_FLAG_SSL), 576 | str, steam_http_req_cb, req); 577 | g_hash_table_replace(req->http->reqs, req, req); 578 | 579 | g_free(hs); 580 | g_free(ps); 581 | g_free(str); 582 | 583 | if (G_UNLIKELY(req->request == NULL)) { 584 | g_set_error(&req->err, STEAM_HTTP_ERROR, STEAM_HTTP_ERROR_INIT, 585 | "Failed to init request"); 586 | steam_http_req_done(req); 587 | return; 588 | } 589 | 590 | /* Prevent automatic redirection */ 591 | req->request->redir_ttl = 0; 592 | 593 | if (req->timeout > 0) { 594 | req->toid = b_timeout_add(req->timeout, steam_http_req_send_timeout, 595 | req); 596 | } 597 | } 598 | 599 | gchar * 600 | steam_http_uri_escape(const gchar *unescaped) 601 | { 602 | gchar *ret; 603 | gchar *str; 604 | 605 | g_return_val_if_fail(unescaped != NULL, NULL); 606 | str = g_strndup(unescaped, (strlen(unescaped) * 3) + 1); 607 | http_encode(str); 608 | 609 | ret = g_strdup(str); 610 | g_free(str); 611 | return ret; 612 | } 613 | 614 | gchar * 615 | steam_http_uri_unescape(const gchar *escaped) 616 | { 617 | gchar *ret; 618 | gchar *str; 619 | 620 | g_return_val_if_fail(escaped != NULL, NULL); 621 | str = g_strdup(escaped); 622 | http_decode(str); 623 | 624 | ret = g_strdup(str); 625 | g_free(str); 626 | return ret; 627 | } 628 | 629 | gchar * 630 | steam_http_uri_join(const gchar *first, ...) 631 | { 632 | const gchar *s; 633 | GString *ret; 634 | va_list ap; 635 | 636 | g_return_val_if_fail(first != NULL, NULL); 637 | 638 | ret = g_string_new(first); 639 | va_start(ap, first); 640 | s = va_arg(ap, const gchar *); 641 | 642 | while (s != NULL) { 643 | if ((ret->len > 0) && (ret->str[ret->len - 1] != '/')) { 644 | g_string_append_c(ret, '/'); 645 | } 646 | 647 | g_string_append(ret, s); 648 | s = va_arg(ap, const gchar *); 649 | } 650 | 651 | va_end(ap); 652 | return g_string_free(ret, FALSE); 653 | } 654 | -------------------------------------------------------------------------------- /steam/steam-http.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2016 James Geboski 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef _STEAM_HTTP_H_ 19 | #define _STEAM_HTTP_H_ 20 | 21 | /** 22 | * SECTION:http 23 | * @section_id: steam-http 24 | * @short_description: steam-http.h 25 | * @title: HTTP Client 26 | * 27 | * The HTTP client. 28 | */ 29 | 30 | #include 31 | 32 | #include "steam-glib.h" 33 | 34 | /** 35 | * STEAM_HTTP_CLIENT_FREED: 36 | * 37 | * Flag denoting the HTTP request as being freed elsewhere. 38 | */ 39 | #define STEAM_HTTP_CLIENT_FREED (1 << 31) 40 | 41 | /** 42 | * STEAM_HTTP_RESEND_MAX: 43 | * 44 | * The maximum amount of times to resend a failed request. 45 | */ 46 | #define STEAM_HTTP_RESEND_MAX 3 47 | 48 | /** 49 | * STEAM_HTTP_RESEND_TIMEOUT: 50 | * 51 | * The timeout (in milliseconds) between resend attempts. 52 | */ 53 | #define STEAM_HTTP_RESEND_TIMEOUT 2000 54 | 55 | /** 56 | * STEAM_HTTP_ERROR: 57 | * 58 | * The #GQuark of the domain of HTTP errors. 59 | */ 60 | #define STEAM_HTTP_ERROR steam_http_error_quark() 61 | 62 | /** 63 | * STEAM_HTTP_PAIR: 64 | * @k: The key. 65 | * @v: The value. 66 | * 67 | * Creates a #SteamHttpPair in-line. 68 | * 69 | * Returns: The resulting SteamHttpPair. 70 | */ 71 | #define STEAM_HTTP_PAIR(k, v) ((SteamHttpPair *) &((SteamHttpPair) {k, v})) 72 | 73 | typedef struct _SteamHttp SteamHttp; 74 | typedef struct _SteamHttpPair SteamHttpPair; 75 | typedef struct _SteamHttpReq SteamHttpReq; 76 | 77 | /** 78 | * SteamHttpFunc: 79 | * @req: The #SteamHttpReq. 80 | * @data: The user defined data or #NULL. 81 | * 82 | * The type of callback for #SteamHttpReq operations. 83 | */ 84 | typedef void (*SteamHttpFunc) (SteamHttpReq *req, gpointer data); 85 | 86 | /** 87 | * SteamHttpError: 88 | * @STEAM_HTTP_ERROR_CLOSED: Request closed. 89 | * @STEAM_HTTP_ERROR_INIT: Initialization failed. 90 | * @STEAM_HTTP_ERROR_TIMEOUT: Request timed out. 91 | * 92 | * The error codes for the #STEAM_HTTP_ERROR domain. 93 | */ 94 | typedef enum 95 | { 96 | STEAM_HTTP_ERROR_CLOSED = 1, 97 | STEAM_HTTP_ERROR_INIT, 98 | STEAM_HTTP_ERROR_TIMEOUT 99 | } SteamHttpError; 100 | 101 | /** 102 | * SteamHttpReqFlags: 103 | * @STEAM_HTTP_REQ_FLAG_GET: Use the GET method. 104 | * @STEAM_HTTP_REQ_FLAG_POST: Use the POST method. 105 | * @STEAM_HTTP_REQ_FLAG_SSL: Use encryption via HTTPS. 106 | * 107 | * The #SteamHttpReq flags. 108 | */ 109 | typedef enum 110 | { 111 | STEAM_HTTP_REQ_FLAG_GET = 1 << 0, 112 | STEAM_HTTP_REQ_FLAG_POST = 1 << 1, 113 | STEAM_HTTP_REQ_FLAG_SSL = 1 << 2 114 | } SteamHttpReqFlags; 115 | 116 | /** 117 | * SteamHttp: 118 | * @agent: The user-agent string. 119 | * @cookies: The table of cookies. 120 | * @reqs: The table of #SteamHttpReq. 121 | * 122 | * Represents an HTTP client (a set of #SteamHttpReq). 123 | */ 124 | struct _SteamHttp 125 | { 126 | gchar *agent; 127 | GHashTable *cookies; 128 | GHashTable *reqs; 129 | }; 130 | 131 | /** 132 | * SteamHttpPair: 133 | * @key: The key. 134 | * @val: The value. 135 | * 136 | * Represents a key/value pair of strings. 137 | */ 138 | struct _SteamHttpPair 139 | { 140 | const gchar *key; 141 | const gchar *val; 142 | }; 143 | 144 | /** 145 | * SteamHttpReq: 146 | * @http: The #SteamHttp. 147 | * @flags: The #SteamHttpReqFlags. 148 | * @host: The hostname. 149 | * @port: The port number. 150 | * @path: The pathname. 151 | * @timeout: The timeout. 152 | * @headers: The table of headers. 153 | * @params: The table of parameters. 154 | * @func: The #SteamHttpFunc or #NULL. 155 | * @request: The underlying #http_request. 156 | * @err: The #GError or #NULL. 157 | * @status: Shortcut to `request->status_string`. 158 | * @scode: Shortcut to `request->status_code`. 159 | * @header: Shortcut to `request->reply_headers`. 160 | * @body: Shortcut to `request->reply_body`. 161 | * @body_size: Shortcut to `request->body_size`. 162 | * @toid: The event identifier for the timeout. 163 | * @rsc: The resend count. 164 | * 165 | * Represents a #SteamHttp request. 166 | */ 167 | struct _SteamHttpReq 168 | { 169 | SteamHttp *http; 170 | SteamHttpReqFlags flags; 171 | 172 | gchar *host; 173 | gint port; 174 | gchar *path; 175 | gint timeout; 176 | 177 | GHashTable *headers; 178 | GHashTable *params; 179 | 180 | SteamHttpFunc func; 181 | gpointer data; 182 | struct http_request *request; 183 | 184 | GError *err; 185 | gchar *status; 186 | gint scode; 187 | gchar *header; 188 | gchar *body; 189 | gint body_size; 190 | 191 | gint toid; 192 | guint8 rsc; 193 | }; 194 | 195 | /** 196 | * steam_http_error_quark: 197 | * 198 | * Gets the #GQuark of the domain of HTTP errors. 199 | * 200 | * Returns: The #GQuark of the domain. 201 | */ 202 | GQuark 203 | steam_http_error_quark(void); 204 | 205 | /** 206 | * steam_http_new: 207 | * @agent: The HTTP agent. 208 | * 209 | * Creates a new #SteamHttp. The returned #SteamHttp should be freed 210 | * with #steam_http_free() when no longer needed. 211 | * 212 | * Returns: The #SteamHttp. 213 | */ 214 | SteamHttp * 215 | steam_http_new(const gchar *agent); 216 | 217 | /** 218 | * steam_http_free_reqs: 219 | * @http: The #SteamHttp. 220 | * 221 | * Frees all #SteamHttpReq inside the #SteamHttp. 222 | */ 223 | void 224 | steam_http_free_reqs(SteamHttp *http); 225 | 226 | /** 227 | * steam_http_free: 228 | * @http: The #SteamHttp. 229 | * 230 | * Frees all memory used by the #SteamHttp. 231 | */ 232 | void 233 | steam_http_free(SteamHttp *http); 234 | 235 | /** 236 | * steam_http_cookies_get: 237 | * @http: The #SteamHttp. 238 | * @name: The cookie name. 239 | * 240 | * Gets the value of a cookie from the #SteamHttp. 241 | * 242 | * Returns: The value of the cookie, or #NULL for a nonexistent cookie. 243 | */ 244 | const gchar * 245 | steam_http_cookies_get(SteamHttp *http, const gchar *name); 246 | 247 | /** 248 | * steam_http_cookies_set: 249 | * @http: The #SteamHttp. 250 | * @pair: The first #SteamHttpPair. 251 | * @...: The additional #SteamHttpPair. 252 | * 253 | * Sets cookies from the #SteamHttpPair. If a cookie already exists, it 254 | * is overwritten with the new value. 255 | */ 256 | void 257 | steam_http_cookies_set(SteamHttp *http, const SteamHttpPair *pair, ...) 258 | G_GNUC_NULL_TERMINATED; 259 | 260 | /** 261 | * steam_http_cookies_parse_req: 262 | * @http: The #SteamHttp. 263 | * @req: The #SteamHttpReq. 264 | * 265 | * Parses cookies from the #SteamHttpReq. If a cookie already exists, 266 | * it is overwritten with the new value. 267 | */ 268 | void 269 | steam_http_cookies_parse_req(SteamHttp *http, const SteamHttpReq *req); 270 | 271 | /** 272 | * steam_http_cookies_parse_str: 273 | * @http: The #SteamHttp. 274 | * @data: The string. 275 | * 276 | * Parses cookies from the string. If a cookie already exists, it is 277 | * overwritten with the new value. 278 | */ 279 | void 280 | steam_http_cookies_parse_str(SteamHttp *http, const gchar *data); 281 | 282 | /** 283 | * steam_http_cookies_str: 284 | * @http: The #SteamHttp. 285 | * 286 | * Gets a string representation of the cookies of the #SteamHttp. The 287 | * returned string should be freed with #g_free() when no longer 288 | * needed. 289 | * 290 | * Returns: The string representation. 291 | */ 292 | gchar * 293 | steam_http_cookies_str(SteamHttp *http); 294 | 295 | /** 296 | * steam_http_req_new: 297 | * @http: The #SteamHttp. 298 | * @host: The hostname. 299 | * @port: The port number. 300 | * @path: The pathname. 301 | * @func: The user callback function or #NULL. 302 | * @data: The user define data or #NULL. 303 | * 304 | * Creates a new #SteamHttpReq. The returned #SteamHttpReq should be 305 | * freed with #steam_http_req_free() when no longer needed. 306 | * 307 | * Returns: The #SteamHttpReq. 308 | */ 309 | SteamHttpReq * 310 | steam_http_req_new(SteamHttp *http, const gchar *host, gint port, 311 | const gchar *path, SteamHttpFunc func, gpointer data); 312 | 313 | /** 314 | * steam_http_req_free: 315 | * @req: The #SteamHttpReq. 316 | * 317 | * Frees all memory used by the #SteamHttpReq. 318 | */ 319 | void 320 | steam_http_req_free(SteamHttpReq *req); 321 | 322 | /** 323 | * steam_http_req_headers_set: 324 | * @req: The #SteamHttpReq. 325 | * @pair: The first #SteamHttpPair. 326 | * @...: The additional #SteamHttpPair. 327 | * 328 | * Sets headers from the #SteamHttpPair. If a header already exists, it 329 | * is overwritten with the new value. 330 | */ 331 | void 332 | steam_http_req_headers_set(SteamHttpReq *req, const SteamHttpPair *pair, ...) 333 | G_GNUC_NULL_TERMINATED; 334 | 335 | /** 336 | * steam_http_req_params_set: 337 | * @req: The #SteamHttpReq. 338 | * @pair: The first #SteamHttpPair. 339 | * @...: The additional #SteamHttpPair. 340 | * 341 | * Sets parameters from the #SteamHttpPair. If a parameter already 342 | * exists, it is overwritten with the new value. 343 | */ 344 | void 345 | steam_http_req_params_set(SteamHttpReq *req, const SteamHttpPair *pair, ...) 346 | G_GNUC_NULL_TERMINATED; 347 | 348 | /** 349 | * steam_http_req_send: 350 | * @req: The #SteamHttpReq. 351 | * 352 | * Sends a #SteamHttpReq. 353 | */ 354 | void 355 | steam_http_req_send(SteamHttpReq *req); 356 | 357 | /** 358 | * steam_http_uri_escape: 359 | * @unescaped: The string. 360 | * 361 | * Escapes the characters of the string to make it URL safe. The 362 | * returned string should be freed with #g_free() when no longer 363 | * needed. 364 | * 365 | * Returns: The escaped string or #NULL on error. 366 | */ 367 | gchar * 368 | steam_http_uri_escape(const gchar *unescaped); 369 | 370 | /** 371 | * steam_http_uri_unescape: 372 | * @escaped: The string. 373 | * 374 | * Unescapes the characters of the string to make it a normal string. 375 | * The returned string should be freed with #g_free() when no longer 376 | * needed. 377 | * 378 | * Returns: The unescaped string or #NULL on error. 379 | */ 380 | gchar * 381 | steam_http_uri_unescape(const gchar *escaped); 382 | 383 | /** 384 | * steam_http_uri_join: 385 | * @first: The first URI segment. 386 | * @...: The additional URI segments. 387 | * 388 | * Joins URI segments into a single URI. This simply ensures the URI 389 | * segments are joined by only one forward slash. The returned string 390 | * should be freed with #g_free() when no longer needed. 391 | * 392 | * Returns: The joined URI or #NULL on error. 393 | */ 394 | gchar * 395 | steam_http_uri_join(const gchar *first, ...) 396 | G_GNUC_NULL_TERMINATED; 397 | 398 | #endif /* _STEAM_HTTP_H_ */ 399 | -------------------------------------------------------------------------------- /steam/steam-id.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2016 James Geboski 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef _STEAM_ID_H_ 19 | #define _STEAM_ID_H_ 20 | 21 | /** 22 | * SECTION:id 23 | * @section_id: steam-id 24 | * @short_description: steam-id.h 25 | * @title: Steam Identifier 26 | * 27 | * The Steam identifier utilities. 28 | */ 29 | 30 | #include "steam-glib.h" 31 | 32 | /** 33 | * STEAM_ID_FORMAT: 34 | * 35 | * The format specifier for printing and scanning a #SteamId. 36 | */ 37 | #define STEAM_ID_FORMAT G_GINT64_FORMAT 38 | 39 | /** 40 | * STEAM_ID_MODIFIER: 41 | * 42 | * The length modifier for printing a #SteamId. 43 | */ 44 | #define STEAM_ID_MODIFIER G_GINT64_MODIFIER 45 | 46 | /** 47 | * STEAM_ID_STRMAX: 48 | * 49 | * The maximum length, including a null-terminating character, of the 50 | * string representation of a #SteamId. 51 | */ 52 | #define STEAM_ID_STRMAX 21 53 | 54 | /** 55 | * STEAM_TYPE_ID: 56 | * 57 | * The #GType of a #SteamId. 58 | */ 59 | #define STEAM_TYPE_ID G_TYPE_INT64 60 | 61 | /** 62 | * steam_id_equal: 63 | * 64 | * Compares the values of two #SteamId's for equality. See 65 | * #g_int64_equal. 66 | */ 67 | #define steam_id_equal g_int64_equal 68 | 69 | /** 70 | * steam_id_hash: 71 | * 72 | * Converts a pointer to a #SteamId hash value. See #g_int64_hash. 73 | */ 74 | #define steam_id_hash g_int64_hash 75 | 76 | /** 77 | * FB_ID_CONSTANT: 78 | * @v: The value. 79 | * 80 | * Inserts a literal #SteamId into source code. 81 | * 82 | * Return: The literal #SteamId value. 83 | */ 84 | #define STEAM_ID_CONSTANT(v) G_GINT64_CONSTANT(v) 85 | 86 | /** 87 | * STEAM_ID_NEW: 88 | * @u: The #SteamIdUniv. 89 | * @t: The #SteamIdType. 90 | * @i: The #SteamIdInst. 91 | * @n: The AccountID. 92 | * 93 | * Creates a new #SteamId. 94 | * 95 | * Returns: The resulting #SteamId. 96 | */ 97 | #define STEAM_ID_NEW(u, t, i, n) ((SteamId) ( \ 98 | ((gint32) n) | \ 99 | (((gint64) i) << 32) | \ 100 | (((gint64) t) << 52) | \ 101 | (((gint64) u) << 56) \ 102 | )) 103 | 104 | /** 105 | * STEAM_ID_NEW_STR: 106 | * @s: The string #SteamId. 107 | * 108 | * Creates a new #SteamId from a string. 109 | * 110 | * Returns: The resulting #SteamId. 111 | */ 112 | #define STEAM_ID_NEW_STR(s) \ 113 | g_ascii_strtoll(s, NULL, 10) 114 | 115 | /** 116 | * STEAM_ID_STR: 117 | * @id: The string #SteamId. 118 | * @s: The string buffer. 119 | * 120 | * Gets the string representation of the #SteamId. 121 | */ 122 | #define STEAM_ID_STR(id, s) \ 123 | g_sprintf(s, "%" STEAM_ID_FORMAT, (SteamId) id) 124 | 125 | /** 126 | * STEAM_ID_ACCID_STR: 127 | * @id: The string #SteamId. 128 | * @s: The string buffer. 129 | * 130 | * Gets the string representation of the #SteamId AccountID. 131 | */ 132 | #define STEAM_ID_ACCID_STR(id, s) \ 133 | g_sprintf(s, "%" G_GINT32_FORMAT, STEAM_ID_ACCID(id)) 134 | 135 | /** 136 | * STEAM_ID_ACCID: 137 | * @id: The #SteamId. 138 | * 139 | * Gets the 32-bit AccountID from the #SteamId. 140 | * 141 | * Returns: The resulting AccountID. 142 | */ 143 | #define STEAM_ID_ACCID(id) ((gint32) ( \ 144 | ((SteamId) id) & 0xFFFFFFFF \ 145 | )) 146 | 147 | /** 148 | * STEAM_ID_INST: 149 | * @id: The #SteamId. 150 | * 151 | * Gets the #SteamIdInst from the #SteamId. 152 | * 153 | * Returns: The resulting #SteamIdInst. 154 | */ 155 | #define STEAM_ID_INST(id) ((SteamIdInst) ( \ 156 | (((SteamId) id) >> 32) & 0x0FFFFF \ 157 | )) 158 | 159 | /** 160 | * STEAM_ID_TYPE: 161 | * @id: The #SteamId. 162 | * 163 | * Gets the #SteamIdType from the #SteamId. 164 | * 165 | * Returns: The resulting #SteamIdType. 166 | */ 167 | #define STEAM_ID_TYPE(id) ((SteamIdType) ( \ 168 | (((SteamId) id) >> 52) & 0x0F \ 169 | )) 170 | 171 | /** 172 | * STEAM_ID_UNIV: 173 | * @id: The #SteamId. 174 | * 175 | * Gets the #SteamIdUniv from the #SteamId. 176 | * 177 | * Returns: The resulting #SteamIdUniv. 178 | */ 179 | #define STEAM_ID_UNIV(id) ((SteamIdUniv) ( \ 180 | ((SteamId) id) >> 56 \ 181 | )) 182 | 183 | /** 184 | * SteamId: 185 | * 186 | * Represents a numeric Steam identifier. 187 | */ 188 | typedef gint64 SteamId; 189 | 190 | /** 191 | * SteamIdInst: 192 | * @STEAM_ID_INST_ALL: All. 193 | * @STEAM_ID_INST_DESKTOP: Desktop. 194 | * @STEAM_ID_INST_CONSOLE: Console. 195 | * @STEAM_ID_INST_WEB: Web. 196 | * 197 | * The #SteamId instances. 198 | */ 199 | typedef enum 200 | { 201 | STEAM_ID_INST_ALL = 0, 202 | STEAM_ID_INST_DESKTOP = 1, 203 | STEAM_ID_INST_CONSOLE = 2, 204 | STEAM_ID_INST_WEB = 4 205 | } SteamIdInst; 206 | 207 | /** 208 | * SteamIdType: 209 | * @STEAM_ID_TYPE_INVALID: Invalid/ 210 | * @STEAM_ID_TYPE_INDIVIDUAL: Individual (user). 211 | * @STEAM_ID_TYPE_MULTISEAT: Multiseat. 212 | * @STEAM_ID_TYPE_GAMESERVER: Game server. 213 | * @STEAM_ID_TYPE_ANONGAMESERVER: Anonymous game server. 214 | * @STEAM_ID_TYPE_PENDING: Pending. 215 | * @STEAM_ID_TYPE_CONTENTSERVER: Content server. 216 | * @STEAM_ID_TYPE_CLAN: Clan or group. 217 | * @STEAM_ID_TYPE_CHAT: Chat. 218 | * @STEAM_ID_TYPE_SUPERSEEDER: P2P super seeder. 219 | * @STEAM_ID_TYPE_ANONUSER: Anonymous user. 220 | * 221 | * The #SteamId types. 222 | */ 223 | typedef enum 224 | { 225 | STEAM_ID_TYPE_INVALID = 0, 226 | STEAM_ID_TYPE_INDIVIDUAL = 1, 227 | STEAM_ID_TYPE_MULTISEAT = 2, 228 | STEAM_ID_TYPE_GAMESERVER = 3, 229 | STEAM_ID_TYPE_ANONGAMESERVER = 4, 230 | STEAM_ID_TYPE_PENDING = 5, 231 | STEAM_ID_TYPE_CONTENTSERVER = 6, 232 | STEAM_ID_TYPE_CLAN = 7, 233 | STEAM_ID_TYPE_CHAT = 8, 234 | STEAM_ID_TYPE_SUPERSEEDER = 9, 235 | STEAM_ID_TYPE_ANONUSER = 10 236 | } SteamIdType; 237 | 238 | /** 239 | * SteamIdUniv: 240 | * @STEAM_ID_UNIV_UNKNOWN: Unknown. 241 | * @STEAM_ID_UNIV_PUBLIC: Public. 242 | * @STEAM_ID_UNIV_BETA: Beta. 243 | * @STEAM_ID_UNIV_INTERNAL: Internal. 244 | * @STEAM_ID_UNIV_DEV: Development. 245 | * @STEAM_ID_UNIV_RC: Release Candidate. 246 | * 247 | * The #SteamId universes. 248 | */ 249 | typedef enum 250 | { 251 | STEAM_ID_UNIV_UNKNOWN = 0, 252 | STEAM_ID_UNIV_PUBLIC = 1, 253 | STEAM_ID_UNIV_BETA = 2, 254 | STEAM_ID_UNIV_INTERNAL = 3, 255 | STEAM_ID_UNIV_DEV = 4, 256 | STEAM_ID_UNIV_RC = 5 257 | } SteamIdUniv; 258 | 259 | #endif /* _STEAM_ID_H_ */ 260 | -------------------------------------------------------------------------------- /steam/steam-json.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2016 James Geboski 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include 19 | #include 20 | 21 | #include "steam-json.h" 22 | 23 | GQuark 24 | steam_json_error_quark(void) 25 | { 26 | static GQuark q; 27 | 28 | if (G_UNLIKELY(q == 0)) { 29 | q = g_quark_from_static_string("steam-json-error-quark"); 30 | } 31 | 32 | return q; 33 | } 34 | 35 | json_value * 36 | steam_json_new(const gchar *data, gsize length, GError **err) 37 | { 38 | gchar *estr; 39 | json_settings js; 40 | json_value *json; 41 | 42 | memset(&js, 0, sizeof js); 43 | estr = g_new0(gchar, json_error_max); 44 | json = json_parse_ex(&js, data, length, estr); 45 | 46 | if ((json != NULL) && (strlen(estr) < 1)) { 47 | g_free(estr); 48 | return json; 49 | } 50 | 51 | g_set_error(err, STEAM_JSON_ERROR, STEAM_JSON_ERROR_PARSER, 52 | "Parser: %s", estr); 53 | 54 | g_free(estr); 55 | return NULL; 56 | } 57 | 58 | gchar * 59 | steam_json_valstr(const json_value *json) 60 | { 61 | g_return_val_if_fail(json != NULL, NULL); 62 | 63 | switch (json->type) { 64 | case json_integer: 65 | return g_strdup_printf("%" PRId64, json->u.integer); 66 | 67 | case json_double: 68 | return g_strdup_printf("%f", json->u.dbl); 69 | 70 | case json_string: 71 | return g_strdup(json->u.string.ptr); 72 | 73 | case json_boolean: 74 | return g_strdup(json->u.boolean ? "true" : "false"); 75 | 76 | case json_null: 77 | return g_strdup("null"); 78 | 79 | default: 80 | return NULL; 81 | } 82 | } 83 | 84 | json_value * 85 | steam_json_val(const json_value *json, const gchar *name, json_type type) 86 | { 87 | json_value *val; 88 | 89 | if (!steam_json_val_chk(json, name, type, &val)) { 90 | return NULL; 91 | } 92 | 93 | return val; 94 | } 95 | 96 | gboolean 97 | steam_json_val_chk(const json_value *json, const gchar *name, 98 | json_type type, json_value **val) 99 | { 100 | g_return_val_if_fail(json != NULL, FALSE); 101 | g_return_val_if_fail(name != NULL, FALSE); 102 | g_return_val_if_fail(val != NULL, FALSE); 103 | 104 | *val = json_o_get(json, name); 105 | 106 | if ((*val == NULL) || ((*val)->type != type)) { 107 | *val = NULL; 108 | return FALSE; 109 | } 110 | 111 | return TRUE; 112 | } 113 | 114 | json_value * 115 | steam_json_array(const json_value *json, const gchar *name) 116 | { 117 | json_value *val; 118 | 119 | if (!steam_json_array_chk(json, name, &val)) { 120 | return NULL; 121 | } 122 | 123 | return val; 124 | } 125 | 126 | gboolean 127 | steam_json_array_chk(const json_value *json, const gchar *name, 128 | json_value **val) 129 | { 130 | return steam_json_val_chk(json, name, json_array, val); 131 | } 132 | 133 | gboolean 134 | steam_json_bool(const json_value *json, const gchar *name) 135 | { 136 | gboolean val; 137 | 138 | if (!steam_json_bool_chk(json, name, &val)) { 139 | return FALSE; 140 | } 141 | 142 | return val; 143 | } 144 | 145 | gboolean 146 | steam_json_bool_chk(const json_value *json, const gchar *name, gboolean *val) 147 | { 148 | json_value *jv; 149 | 150 | g_return_val_if_fail(val != NULL, FALSE); 151 | 152 | if (!steam_json_val_chk(json, name, json_boolean, &jv)) { 153 | *val = FALSE; 154 | return FALSE; 155 | } 156 | 157 | *val = jv->u.boolean; 158 | return TRUE; 159 | } 160 | 161 | gint64 162 | steam_json_int(const json_value *json, const gchar *name) 163 | { 164 | gint64 val; 165 | 166 | if (!steam_json_int_chk(json, name, &val)) { 167 | return 0; 168 | } 169 | 170 | return val; 171 | } 172 | 173 | gboolean 174 | steam_json_int_chk(const json_value *json, const gchar *name, gint64 *val) 175 | { 176 | json_value *jv; 177 | 178 | g_return_val_if_fail(val != NULL, FALSE); 179 | 180 | if (!steam_json_val_chk(json, name, json_integer, &jv)) { 181 | *val = 0; 182 | return FALSE; 183 | } 184 | 185 | *val = jv->u.integer; 186 | return TRUE; 187 | } 188 | 189 | const gchar * 190 | steam_json_str(const json_value *json, const gchar *name) 191 | { 192 | const gchar *val; 193 | 194 | if (!steam_json_str_chk(json, name, &val)) { 195 | return NULL; 196 | } 197 | 198 | return val; 199 | } 200 | 201 | gboolean 202 | steam_json_str_chk(const json_value *json, const gchar *name, 203 | const gchar **val) 204 | { 205 | json_value *jv; 206 | 207 | g_return_val_if_fail(val != NULL, FALSE); 208 | 209 | if (!steam_json_val_chk(json, name, json_string, &jv)) { 210 | *val = NULL; 211 | return FALSE; 212 | } 213 | 214 | *val = jv->u.string.ptr; 215 | return TRUE; 216 | } 217 | -------------------------------------------------------------------------------- /steam/steam-json.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2016 James Geboski 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef _STEAM_JSON_H_ 19 | #define _STEAM_JSON_H_ 20 | 21 | /** 22 | * SECTION:json 23 | * @section_id: steam-json 24 | * @short_description: steam-json.h 25 | * @title: JSON Utilities 26 | * 27 | * The JSON utilities. 28 | */ 29 | 30 | #include 31 | 32 | #include "steam-glib.h" 33 | 34 | /** 35 | * STEAM_JSON_ERROR: 36 | * 37 | * The #GQuark of the domain of JSON errors. 38 | */ 39 | #define STEAM_JSON_ERROR steam_json_error_quark() 40 | 41 | /** 42 | * SteamJsonError: 43 | * @STEAM_JSON_ERROR_PARSER: Parser failed. 44 | * 45 | * The error codes for the #STEAM_JSON_ERROR domain. 46 | */ 47 | typedef enum 48 | { 49 | STEAM_JSON_ERROR_PARSER 50 | } SteamJsonError; 51 | 52 | /** 53 | * steam_json_error_quark: 54 | * 55 | * Gets the #GQuark of the domain of JSON errors. 56 | * 57 | * Returns: The #GQuark of the domain. 58 | */ 59 | GQuark 60 | steam_json_error_quark(void); 61 | 62 | /** 63 | * steam_json_new: 64 | * @data: The JSON data. 65 | * @length: The length of the JSON data. 66 | * @err: The return location for a GError or #NULL. 67 | * 68 | * Creates a new #json_value from JSON data. The returned #json_value 69 | * should be freed with #json_value_free() when no longer needed. 70 | * 71 | * Returns: The #json_value or #NULL on error. 72 | */ 73 | json_value * 74 | steam_json_new(const gchar *data, gsize length, GError **err); 75 | 76 | /** 77 | * steam_json_valstr: 78 | * @json: The #json_value. 79 | * 80 | * Gets the string representation of the #json_value. The returned 81 | * string should be freed with #g_free() when no longer needed. 82 | * 83 | * Returns: The resulting string or #NULL on error. 84 | */ 85 | gchar * 86 | steam_json_valstr(const json_value *json); 87 | 88 | /** 89 | * steam_json_val: 90 | * @json: The #json_value. 91 | * @name: The name. 92 | * @type: The #json_type. 93 | * 94 | * Gets a #json_value by name from the parent #json_value. 95 | * 96 | * Returns: The json_value if found, otherwise #NULL. 97 | */ 98 | json_value * 99 | steam_json_val(const json_value *json, const gchar *name, json_type type); 100 | 101 | /** 102 | * steam_json_val_chk: 103 | * @json: The #json_value. 104 | * @name: The name. 105 | * @type: The #json_type. 106 | * @val: The return location for the value. 107 | * 108 | * Gets a #json_value by name from the parent #json_value, and checks 109 | * for its existence and type. 110 | * 111 | * Returns: #TRUE if the value was found, otherwise #FALSE. 112 | */ 113 | gboolean 114 | steam_json_val_chk(const json_value *json, const gchar *name, 115 | json_type type, json_value **val); 116 | 117 | /** 118 | * steam_json_array: 119 | * @json: The #json_value. 120 | * @name: The name. 121 | * 122 | * Gets an array by name from the parent #json_value. 123 | * 124 | * Returns: The #json_value if found, otherwise #NULL. 125 | */ 126 | json_value * 127 | steam_json_array(const json_value *json, const gchar *name); 128 | 129 | /** 130 | * steam_json_array_chk: 131 | * @json: The #json_value. 132 | * @name: The name. 133 | * @type: The #json_type. 134 | * @val: The return location for the value. 135 | * 136 | * Gets an array by name from the parent #json_value, and checks for 137 | * its existence and type. 138 | * 139 | * Returns: #TRUE if the value was found, otherwise #FALSE. 140 | */ 141 | gboolean 142 | steam_json_array_chk(const json_value *json, const gchar *name, 143 | json_value **val); 144 | 145 | /** 146 | * steam_json_bool: 147 | * @json: The #json_value. 148 | * @name: The name. 149 | * 150 | * Gets a boolean value by name from the parent #json_value. 151 | * 152 | * Returns: The boolean value if found, otherwise #FALSE. 153 | */ 154 | gboolean 155 | steam_json_bool(const json_value *json, const gchar *name); 156 | 157 | /** 158 | * steam_json_bool_chk: 159 | * @json: The #json_value. 160 | * @name: The name. 161 | * @val: The return location for the value. 162 | * 163 | * Gets a boolean value by name from the parent #json_value, and checks 164 | * for its existence and type. 165 | * 166 | * Returns: The boolean value if found, otherwise #FALSE. 167 | */ 168 | gboolean 169 | steam_json_bool_chk(const json_value *json, const gchar *name, gboolean *val); 170 | 171 | /** 172 | * steam_json_int: 173 | * @json: The #json_value. 174 | * @name: The name. 175 | * 176 | * Gets an integer value by name from the parent #json_value. 177 | * 178 | * 179 | * Returns: The integer value if found, otherwise `0`. 180 | */ 181 | gint64 182 | steam_json_int(const json_value *json, const gchar *name); 183 | 184 | /** 185 | * steam_json_int_chk: 186 | * @json: The #json_value. 187 | * @name: The name. 188 | * @val: The return location for the value. 189 | * 190 | * Gets an integer value by name from the parent #json_value, and 191 | * checks for its existence and type. 192 | * 193 | * Returns: #TRUE if the value was found, otherwise #FALSE. 194 | */ 195 | gboolean 196 | steam_json_int_chk(const json_value *json, const gchar *name, gint64 *val); 197 | 198 | /** 199 | * steam_json_str: 200 | * @json: The #json_value. 201 | * @name: The name. 202 | * 203 | * Gets a string value by name from the parent #json_value. 204 | * 205 | * Returns: The string value if found, otherwise #NULL. 206 | */ 207 | const gchar * 208 | steam_json_str(const json_value *json, const gchar *name); 209 | 210 | /** 211 | * steam_json_str_chk: 212 | * @json: The #json_value. 213 | * @name: The name. 214 | * @val: The return location for the value. 215 | * 216 | * Gets a string value by name from the parent #json_value, and checks 217 | * for its existence and type. 218 | * 219 | * Returns: #TRUE if the value was found, otherwise #FALSE. 220 | */ 221 | gboolean 222 | steam_json_str_chk(const json_value *json, const gchar *name, 223 | const gchar **val); 224 | 225 | #endif /* _STEAM_JSON_H_ */ 226 | -------------------------------------------------------------------------------- /steam/steam-user.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2016 James Geboski 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include 19 | 20 | #include "steam-user.h" 21 | #include "steam-util.h" 22 | 23 | SteamUser * 24 | steam_user_new(bee_user_t *bu) 25 | { 26 | SteamUser *user; 27 | 28 | user = g_new0(SteamUser, 1); 29 | user->buser = bu; 30 | return user; 31 | } 32 | 33 | void 34 | steam_user_free(SteamUser *user) 35 | { 36 | if (G_UNLIKELY(user == NULL)) { 37 | return; 38 | } 39 | 40 | g_free(user->server); 41 | g_free(user->game); 42 | g_free(user); 43 | } 44 | 45 | void 46 | steam_user_chans_msg(SteamUser *user, const gchar *format, ...) 47 | { 48 | gchar *str; 49 | GSList *l; 50 | irc_channel_t *ic; 51 | irc_user_t *iu; 52 | va_list ap; 53 | 54 | g_return_if_fail(user != NULL); 55 | g_return_if_fail(format != NULL); 56 | iu = user->buser->ui_data; 57 | 58 | va_start(ap, format); 59 | str = g_strdup_vprintf(format, ap); 60 | va_end(ap); 61 | 62 | for (l = iu->irc->channels; l != NULL; l = l->next) { 63 | ic = l->data; 64 | 65 | if (irc_channel_has_user(ic, iu) != NULL) { 66 | irc_send_msg(iu, "PRIVMSG", ic->name, str, NULL); 67 | } 68 | } 69 | 70 | g_free(str); 71 | } 72 | 73 | gchar * 74 | steam_user_flags_str(SteamUserFlags flags) 75 | { 76 | gchar *str; 77 | gchar **strs; 78 | 79 | static const SteamUtilEnum enums[] = { 80 | {STEAM_USER_FLAG_WEB, "Web"}, 81 | {STEAM_USER_FLAG_MOBILE, "Mobile"}, 82 | {STEAM_USER_FLAG_BIGPIC, "Big Picture"}, 83 | STEAM_UTIL_ENUM_NULL 84 | }; 85 | 86 | strs = (gchar **) steam_util_enum_ptrs(enums, flags); 87 | 88 | if (strs[0] == NULL) { 89 | g_free(strs); 90 | return NULL; 91 | } 92 | 93 | str = g_strjoinv(", ", strs); 94 | 95 | g_free(strs); 96 | return str; 97 | } 98 | 99 | SteamUserInfo * 100 | steam_user_info_new(SteamId id) 101 | { 102 | SteamUserInfo *info; 103 | 104 | info = g_new0(SteamUserInfo, 1); 105 | info->id = id; 106 | info->act = STEAM_USER_ACT_NONE; 107 | 108 | return info; 109 | } 110 | 111 | void 112 | steam_user_info_free(SteamUserInfo *info) 113 | { 114 | if (G_UNLIKELY(info == NULL)) { 115 | return; 116 | } 117 | 118 | g_slist_free_full(info->nicks, g_free); 119 | 120 | g_free(info->profile); 121 | g_free(info->server); 122 | g_free(info->game); 123 | g_free(info->fullname); 124 | g_free(info->nick); 125 | g_free(info); 126 | } 127 | 128 | SteamUserMsg * 129 | steam_user_msg_new(SteamId id) 130 | { 131 | SteamUserMsg *msg; 132 | 133 | msg = g_new0(SteamUserMsg, 1); 134 | msg->info = steam_user_info_new(id); 135 | return msg; 136 | } 137 | 138 | void 139 | steam_user_msg_free(SteamUserMsg *msg) 140 | { 141 | if (G_UNLIKELY(msg == NULL)) { 142 | return; 143 | } 144 | 145 | steam_user_info_free(msg->info); 146 | g_free(msg->text); 147 | g_free(msg); 148 | } 149 | 150 | const gchar * 151 | steam_user_msg_type_str(SteamUserMsgType type) 152 | { 153 | static const SteamUtilEnum enums[] = { 154 | {STEAM_USER_MSG_TYPE_SAYTEXT, "saytext"}, 155 | {STEAM_USER_MSG_TYPE_EMOTE, "emote"}, 156 | {STEAM_USER_MSG_TYPE_LEFT_CONV, "leftconversation"}, 157 | {STEAM_USER_MSG_TYPE_RELATIONSHIP, "personarelationship"}, 158 | {STEAM_USER_MSG_TYPE_STATE, "personastate"}, 159 | {STEAM_USER_MSG_TYPE_TYPING, "typing"}, 160 | {STEAM_USER_MSG_TYPE_MY_SAYTEXT, "my_saytext"}, 161 | {STEAM_USER_MSG_TYPE_MY_EMOTE, "my_emote"}, 162 | STEAM_UTIL_ENUM_NULL 163 | }; 164 | 165 | return steam_util_enum_ptr(enums, NULL, type); 166 | } 167 | 168 | SteamUserMsgType 169 | steam_user_msg_type_from_str(const gchar *type) 170 | { 171 | static const SteamUtilEnum enums[] = { 172 | {STEAM_USER_MSG_TYPE_SAYTEXT, "saytext"}, 173 | {STEAM_USER_MSG_TYPE_EMOTE, "emote"}, 174 | {STEAM_USER_MSG_TYPE_LEFT_CONV, "leftconversation"}, 175 | {STEAM_USER_MSG_TYPE_RELATIONSHIP, "personarelationship"}, 176 | {STEAM_USER_MSG_TYPE_STATE, "personastate"}, 177 | {STEAM_USER_MSG_TYPE_TYPING, "typing"}, 178 | {STEAM_USER_MSG_TYPE_MY_SAYTEXT, "my_saytext"}, 179 | {STEAM_USER_MSG_TYPE_MY_EMOTE, "my_emote"}, 180 | STEAM_UTIL_ENUM_NULL 181 | }; 182 | 183 | return steam_util_enum_val(enums, STEAM_USER_MSG_TYPE_UNKNOWN, type, 184 | (GCompareFunc) g_ascii_strcasecmp); 185 | } 186 | 187 | const gchar * 188 | steam_user_state_str(SteamUserState state) 189 | { 190 | static const SteamUtilEnum enums[] = { 191 | {STEAM_USER_STATE_OFFLINE, "Offline"}, 192 | {STEAM_USER_STATE_ONLINE, "Online"}, 193 | {STEAM_USER_STATE_BUSY, "Busy"}, 194 | {STEAM_USER_STATE_AWAY, "Away"}, 195 | {STEAM_USER_STATE_SNOOZE, "Snooze"}, 196 | {STEAM_USER_STATE_TRADE, "Looking to Trade"}, 197 | {STEAM_USER_STATE_PLAY, "Looking to Play"}, 198 | STEAM_UTIL_ENUM_NULL 199 | }; 200 | 201 | return steam_util_enum_ptr(enums, NULL, state); 202 | } 203 | -------------------------------------------------------------------------------- /steam/steam-user.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2016 James Geboski 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef _STEAM_USER_H_ 19 | #define _STEAM_USER_H_ 20 | 21 | /** 22 | * SECTION:user 23 | * @section_id: steam-user 24 | * @short_description: steam-user.h 25 | * @title: JSON Utilities 26 | * 27 | * The JSON utilities. 28 | */ 29 | 30 | #include 31 | 32 | #include "steam-glib.h" 33 | #include "steam-id.h" 34 | 35 | typedef struct _SteamUser SteamUser; 36 | typedef struct _SteamUserInfo SteamUserInfo; 37 | typedef struct _SteamUserMsg SteamUserMsg; 38 | 39 | /** 40 | * SteamUserAct: 41 | * @STEAM_USER_ACT_REMOVE: Removed. 42 | * @STEAM_USER_ACT_IGNORE: Ignored. 43 | * @STEAM_USER_ACT_REQUEST: Friendship request. 44 | * @STEAM_USER_ACT_ADD: Added. 45 | * @STEAM_USER_ACT_REQUESTED: Requested friendship. 46 | * @STEAM_USER_ACT_NONE: None. 47 | * 48 | * The #SteamUserInfo actions. 49 | */ 50 | typedef enum 51 | { 52 | STEAM_USER_ACT_REMOVE = 0, 53 | STEAM_USER_ACT_IGNORE = 1, 54 | STEAM_USER_ACT_REQUEST = 2, 55 | STEAM_USER_ACT_ADD = 3, 56 | STEAM_USER_ACT_REQUESTED = 4, 57 | 58 | STEAM_USER_ACT_NONE 59 | } SteamUserAct; 60 | 61 | /** 62 | * SteamUserMsgType: 63 | * @STEAM_USER_MSG_TYPE_SAYTEXT: Say text. 64 | * @STEAM_USER_MSG_TYPE_EMOTE: Emote. 65 | * @STEAM_USER_MSG_TYPE_LEFT_CONV: Left conversation. 66 | * @STEAM_USER_MSG_TYPE_RELATIONSHIP: Relationship. 67 | * @STEAM_USER_MSG_TYPE_STATE: State. 68 | * @STEAM_USER_MSG_TYPE_TYPING: Typing. 69 | * @STEAM_USER_MSG_TYPE_MY_SAYTEXT: My say text. 70 | * @STEAM_USER_MSG_TYPE_MY_EMOTE: My emote. 71 | * @STEAM_USER_MSG_TYPE_UNKNOWN: Unknown. 72 | * 73 | * The #SteamUserMsg types. 74 | */ 75 | typedef enum 76 | { 77 | STEAM_USER_MSG_TYPE_SAYTEXT = 0, 78 | STEAM_USER_MSG_TYPE_EMOTE, 79 | STEAM_USER_MSG_TYPE_LEFT_CONV, 80 | STEAM_USER_MSG_TYPE_RELATIONSHIP, 81 | STEAM_USER_MSG_TYPE_STATE, 82 | STEAM_USER_MSG_TYPE_TYPING, 83 | STEAM_USER_MSG_TYPE_MY_SAYTEXT, 84 | STEAM_USER_MSG_TYPE_MY_EMOTE, 85 | 86 | STEAM_USER_MSG_TYPE_UNKNOWN 87 | } SteamUserMsgType; 88 | 89 | /** 90 | * SteamUserRel: 91 | * @STEAM_USER_REL_FRIEND: Friend. 92 | * @STEAM_USER_REL_IGNORE: Ignored. 93 | * 94 | * The #SteamUserInfo relationships. 95 | */ 96 | typedef enum 97 | { 98 | STEAM_USER_REL_FRIEND = 0, 99 | STEAM_USER_REL_IGNORE 100 | } SteamUserRel; 101 | 102 | /** 103 | * SteamUserState: 104 | * @STEAM_USER_STATE_OFFLINE: Offline. 105 | * @STEAM_USER_STATE_ONLINE: Online. 106 | * @STEAM_USER_STATE_BUSY: Busy. 107 | * @STEAM_USER_STATE_AWAY: Away. 108 | * @STEAM_USER_STATE_SNOOZE: Snooze. 109 | * @STEAM_USER_STATE_TRADE: Trade. 110 | * @STEAM_USER_STATE_PLAY: Play. 111 | * 112 | * The #SteamUserInfo states. 113 | */ 114 | typedef enum 115 | { 116 | STEAM_USER_STATE_OFFLINE = 0, 117 | STEAM_USER_STATE_ONLINE = 1, 118 | STEAM_USER_STATE_BUSY = 2, 119 | STEAM_USER_STATE_AWAY = 3, 120 | STEAM_USER_STATE_SNOOZE = 4, 121 | STEAM_USER_STATE_TRADE = 5, 122 | STEAM_USER_STATE_PLAY = 6 123 | } SteamUserState; 124 | 125 | /** 126 | * SteamUserFlags: 127 | * @STEAM_USER_FLAG_WEB: Using a web client. 128 | * @STEAM_USER_FLAG_MOBILE: Using a mobile client. 129 | * @STEAM_USER_FLAG_BIGPIC: Using Big Picture mode. 130 | * 131 | * The #SteamUserInfo flags. 132 | */ 133 | typedef enum 134 | { 135 | STEAM_USER_FLAG_WEB = 1 << 8, 136 | STEAM_USER_FLAG_MOBILE = 1 << 9, 137 | STEAM_USER_FLAG_BIGPIC = 1 << 10 138 | } SteamUserFlags; 139 | 140 | /** 141 | * SteamUser: 142 | * @buser: The #bee_user. 143 | * @game: The game name or #NULL. 144 | * @server: The game server or #NULL. 145 | * @vtime: The last view timestamp (UTC). 146 | * 147 | * Represents a Steam user. 148 | */ 149 | struct _SteamUser 150 | { 151 | bee_user_t *buser; 152 | gchar *game; 153 | gchar *server; 154 | gint64 vtime; 155 | }; 156 | 157 | /** 158 | * SteamUserInfo: 159 | * @id: The #SteamId. 160 | * @nicks: The #GSList of prior nicknames. 161 | * @state: The #SteamUserState. 162 | * @flags: The #SteamUserFlags. 163 | * @rel: The #SteamUserRel. 164 | * @act: The #SteamUserAct. 165 | * @nick: The nickname. 166 | * @fullname: The full name. 167 | * @game: The game name or #NULL. 168 | * @server: The game server or #NULL. 169 | * @profile: The profile URL or #NULL. 170 | * @ltime: The last logoff timestamp (UTC). 171 | * @vtime: The last view timestamp (UTC). 172 | * @unread: The unread message count. 173 | * 174 | * Represents Steam user information. 175 | */ 176 | struct _SteamUserInfo 177 | { 178 | SteamId id; 179 | GSList *nicks; 180 | 181 | SteamUserState state; 182 | SteamUserFlags flags; 183 | SteamUserRel rel; 184 | SteamUserAct act; 185 | 186 | gchar *nick; 187 | gchar *fullname; 188 | gchar *game; 189 | gchar *server; 190 | gchar *profile; 191 | 192 | gint64 ltime; 193 | gint64 vtime; 194 | guint unread; 195 | }; 196 | 197 | /** 198 | * SteamUserMsg: 199 | * @type: The #SteamUserMsgType. 200 | * @info: The #SteamUserInfo. 201 | * @text: The message text or #NULL. 202 | * @time: The message timestamp (UTC). 203 | * 204 | * Represents a steam user messages. 205 | */ 206 | struct _SteamUserMsg 207 | { 208 | SteamUserMsgType type; 209 | SteamUserInfo *info; 210 | 211 | gchar *text; 212 | gint64 time; 213 | }; 214 | 215 | /** 216 | * steam_user_new: 217 | * @bu: The #bee_user. 218 | * 219 | * Creates a new #SteamUser. The returned #SteamUser should be freed 220 | * with #steam_user_free() when no longer needed. 221 | * 222 | * Returns: The #SteamUser. 223 | */ 224 | SteamUser * 225 | steam_user_new(bee_user_t *bu); 226 | 227 | /** 228 | * steam_user_free: 229 | * @user: The #SteamUser. 230 | * 231 | * Frees all memory used by the #SteamUser. 232 | */ 233 | void 234 | steam_user_free(SteamUser *user); 235 | 236 | /** 237 | * steam_user_chans_msg: 238 | * @user: The #SteamUser. 239 | * @format: The format string. 240 | * @...: The arguments for the format string. 241 | * 242 | * Sends a message to all channels which the #SteamUser is occupying 243 | * with the sender being the #SteamUser. 244 | */ 245 | void 246 | steam_user_chans_msg(SteamUser *user, const gchar *fmt, ...) 247 | G_GNUC_PRINTF(2, 3); 248 | 249 | /** 250 | * steam_user_flags_str: 251 | * @flags: The #SteamUserFlags. 252 | * 253 | * Gets the string representation of the #SteamUserFlags. The returned 254 | * string should be freed with #g_free() when no longer needed. 255 | * 256 | * Returns: The string representation or #NULL on error. 257 | */ 258 | gchar * 259 | steam_user_flags_str(SteamUserFlags flags); 260 | 261 | /** 262 | * steam_user_info_new: 263 | * @id: The #SteamId. 264 | * 265 | * Creates a new #SteamUserInfo. The returned #SteamUserInfo should be 266 | * freed with #steam_user_info_free() when no longer needed. 267 | * 268 | * Returns: The #SteamUserInfo or #NULL on error. 269 | */ 270 | SteamUserInfo * 271 | steam_user_info_new(SteamId id); 272 | 273 | /** 274 | * steam_user_info_free: 275 | * @info: The #SteamUserInfo. 276 | * 277 | * Frees all memory used by the #SteamUserInfo. 278 | */ 279 | void 280 | steam_user_info_free(SteamUserInfo *info); 281 | 282 | 283 | /** 284 | * steam_user_msg_new: 285 | * @id: The #SteamId. 286 | * 287 | * Creates a new #SteamUserMsg. The returned #SteamUserMsg should be 288 | * freed with #steam_user_msg_free() when no longer needed. 289 | * 290 | * Returns: The #SteamUserMsg. 291 | */ 292 | SteamUserMsg * 293 | steam_user_msg_new(SteamId id); 294 | 295 | /** 296 | * steam_user_msg_free: 297 | * @msg: The #SteamUserMsg. 298 | * 299 | * Frees all memory used by the #SteamUserMsg. 300 | */ 301 | void 302 | steam_user_msg_free(SteamUserMsg *msg); 303 | 304 | /** 305 | * steam_user_msg_type_str: 306 | * @type: The #SteamUserMsgType. 307 | * 308 | * Gets the string representation of the #SteamUserMsgType. 309 | * 310 | * Returns: The string representation or #NULL on error. 311 | */ 312 | const gchar * 313 | steam_user_msg_type_str(SteamUserMsgType type); 314 | 315 | /** 316 | * steam_user_msg_type_from_str: 317 | * @type: The string. 318 | * 319 | * Gets the #SteamUserMsgType value of the string. 320 | * 321 | * Returns: The #SteamUserMsgType value. 322 | */ 323 | SteamUserMsgType 324 | steam_user_msg_type_from_str(const gchar *type); 325 | 326 | /** 327 | * steam_user_state_str: 328 | * @state: The #SteamUserState. 329 | * 330 | * Gets the string representation of the #SteamUserState. 331 | * 332 | * Returns: The string representation or #NULL on error. 333 | */ 334 | const gchar * 335 | steam_user_state_str(SteamUserState state); 336 | 337 | #endif /* _STEAM_USER_H_ */ 338 | -------------------------------------------------------------------------------- /steam/steam-util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2016 James Geboski 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include 19 | #include 20 | 21 | #include "steam-util.h" 22 | 23 | void 24 | steam_util_debug(SteamDebugLevel level, const gchar *format, ...) 25 | { 26 | va_list ap; 27 | 28 | va_start(ap, format); 29 | steam_util_vdebug(level, format, ap); 30 | va_end(ap); 31 | } 32 | 33 | void 34 | steam_util_vdebug(SteamDebugLevel level, const gchar *format, va_list ap) 35 | { 36 | const gchar *lstr; 37 | gchar *str; 38 | 39 | static gboolean debug = FALSE; 40 | static gboolean setup = FALSE; 41 | 42 | g_return_if_fail(format != NULL); 43 | 44 | if (G_UNLIKELY(!setup)) { 45 | debug = (g_getenv("BITLBEE_DEBUG") != NULL) || 46 | (g_getenv("BITLBEE_DEBUG_STEAM") != NULL); 47 | setup = TRUE; 48 | } 49 | 50 | if (!debug) { 51 | return; 52 | } 53 | 54 | switch (level) { 55 | case STEAM_UTIL_DEBUG_LEVEL_MISC: 56 | lstr = "MISC"; 57 | break; 58 | case STEAM_UTIL_DEBUG_LEVEL_INFO: 59 | lstr = "INFO"; 60 | break; 61 | case STEAM_UTIL_DEBUG_LEVEL_WARN: 62 | lstr = "WARN"; 63 | break; 64 | case STEAM_UTIL_DEBUG_LEVEL_ERROR: 65 | lstr = "ERROR"; 66 | break; 67 | case STEAM_UTIL_DEBUG_LEVEL_FATAL: 68 | lstr = "FATAL"; 69 | break; 70 | 71 | default: 72 | g_return_if_reached(); 73 | return; 74 | } 75 | 76 | str = g_strdup_vprintf(format, ap); 77 | g_print("[%s] %s: %s\n", lstr, "steam", str); 78 | g_free(str); 79 | } 80 | 81 | void 82 | steam_util_debug_misc(const gchar *format, ...) 83 | { 84 | va_list ap; 85 | 86 | va_start(ap, format); 87 | steam_util_vdebug(STEAM_UTIL_DEBUG_LEVEL_MISC, format, ap); 88 | va_end(ap); 89 | } 90 | 91 | void 92 | steam_util_debug_info(const gchar *format, ...) 93 | { 94 | va_list ap; 95 | 96 | va_start(ap, format); 97 | steam_util_vdebug(STEAM_UTIL_DEBUG_LEVEL_INFO, format, ap); 98 | va_end(ap); 99 | } 100 | 101 | void 102 | steam_util_debug_warn(const gchar *format, ...) 103 | { 104 | va_list ap; 105 | 106 | va_start(ap, format); 107 | steam_util_vdebug(STEAM_UTIL_DEBUG_LEVEL_WARN, format, ap); 108 | va_end(ap); 109 | } 110 | 111 | void 112 | steam_util_debug_error(const gchar *format, ...) 113 | { 114 | va_list ap; 115 | 116 | va_start(ap, format); 117 | steam_util_vdebug(STEAM_UTIL_DEBUG_LEVEL_ERROR, format, ap); 118 | va_end(ap); 119 | } 120 | 121 | void 122 | steam_util_debug_fatal(const gchar *format, ...) 123 | { 124 | va_list ap; 125 | 126 | va_start(ap, format); 127 | steam_util_vdebug(STEAM_UTIL_DEBUG_LEVEL_FATAL, format, ap); 128 | va_end(ap); 129 | } 130 | 131 | gpointer 132 | steam_util_enum_ptr(const SteamUtilEnum *enums, gpointer def, guint val) 133 | { 134 | guint i; 135 | 136 | g_return_val_if_fail(enums != NULL, NULL); 137 | 138 | for (i = 0; enums[i].ptr != NULL; i++) { 139 | if (enums[i].val == val) { 140 | return enums[i].ptr; 141 | } 142 | } 143 | 144 | return def; 145 | } 146 | 147 | gpointer * 148 | steam_util_enum_ptrs(const SteamUtilEnum *enums, guint vals) 149 | { 150 | gpointer *ptrs; 151 | gsize size = 0; 152 | guint i; 153 | guint j; 154 | 155 | g_return_val_if_fail(enums != NULL, g_new0(gpointer, 0)); 156 | 157 | for (i = 0; enums[i].ptr != NULL; i++) { 158 | if (vals & enums[i].val) { 159 | size++; 160 | } 161 | } 162 | 163 | ptrs = g_new0(gpointer, ++size); 164 | 165 | for (i = 0, j = 0; enums[i].ptr != NULL; i++) { 166 | if (vals & enums[i].val) { 167 | ptrs[j++] = enums[i].ptr; 168 | } 169 | } 170 | 171 | return ptrs; 172 | } 173 | 174 | guint 175 | steam_util_enum_val(const SteamUtilEnum *enums, guint def, 176 | gconstpointer ptr, GCompareFunc cmpfunc) 177 | { 178 | guint i; 179 | 180 | g_return_val_if_fail(enums != NULL, 0); 181 | g_return_val_if_fail(ptr != NULL, 0); 182 | g_return_val_if_fail(cmpfunc != NULL, 0); 183 | 184 | for (i = 0; enums[i].ptr != NULL; i++) { 185 | if (cmpfunc(ptr, enums[i].ptr) == 0) { 186 | return enums[i].val; 187 | } 188 | } 189 | 190 | return def; 191 | } 192 | 193 | GByteArray * 194 | steam_util_str_hex2bytes(const gchar *str) 195 | { 196 | gboolean hax; 197 | GByteArray *ret; 198 | gchar val; 199 | gsize size; 200 | guint d; 201 | guint i; 202 | 203 | g_return_val_if_fail(str != NULL, NULL); 204 | 205 | size = strlen(str); 206 | hax = (size % 2) != 0; 207 | 208 | ret = g_byte_array_new(); 209 | g_byte_array_set_size(ret, (size + 1) / 2); 210 | memset(ret->data, 0, ret->len); 211 | 212 | for (d = i = 0; i < size; i++, hax = !hax) { 213 | val = g_ascii_xdigit_value(str[i]); 214 | 215 | if (val < 0) { 216 | g_byte_array_free(ret, TRUE); 217 | return NULL; 218 | } 219 | 220 | if (hax) { 221 | ret->data[d++] |= val & 0x0F; 222 | } else { 223 | ret->data[d] |= (val << 4) & 0xF0; 224 | } 225 | } 226 | 227 | return ret; 228 | } 229 | 230 | gboolean 231 | steam_util_str_iequal(const gchar *s1, const gchar *s2) 232 | { 233 | return g_ascii_strcasecmp(s1, s2) == 0; 234 | } 235 | 236 | gchar * 237 | steam_util_time_span_str(GTimeSpan span) 238 | { 239 | gchar *str; 240 | guint i; 241 | 242 | static const SteamUtilTimeSpan spans[] = { 243 | {"second", 1}, 244 | {"minute", 60}, 245 | {"hour", 60 * 60}, 246 | {"day", 60 * 60 * 24}, 247 | {"week", 60 * 60 * 24 * 7}, 248 | {"month", 60 * 60 * 24 * 30}, 249 | {"year", 60 * 60 * 24 * 365}, 250 | {NULL, 0} 251 | }; 252 | 253 | span /= G_TIME_SPAN_SECOND; 254 | 255 | for (i = 1; spans[i].name != NULL; i++) { 256 | if (span < spans[i].span) { 257 | span /= spans[--i].span; 258 | break; 259 | } 260 | 261 | if (G_UNLIKELY(spans[i + 1].name == NULL)) { 262 | span /= spans[i].span; 263 | break; 264 | } 265 | } 266 | 267 | str = g_strdup_printf("%" G_GINT64_FORMAT " %s%s", span, spans[i].name, 268 | ((span > 1) ? "s" : "")); 269 | 270 | return str; 271 | } 272 | 273 | gchar * 274 | steam_util_time_since_utc(gint64 timestamp) 275 | { 276 | GDateTime *beg; 277 | GDateTime *end; 278 | GTimeSpan spn; 279 | 280 | beg = g_date_time_new_from_unix_utc(timestamp); 281 | end = g_date_time_new_now_utc(); 282 | spn = g_date_time_difference(end, beg); 283 | 284 | g_date_time_unref(beg); 285 | g_date_time_unref(end); 286 | 287 | if (G_UNLIKELY(spn < 0)) { 288 | spn = -spn; 289 | } 290 | 291 | return steam_util_time_span_str(spn); 292 | } 293 | 294 | gchar * 295 | steam_util_ustrchr(const gchar *str, gchar chr) 296 | { 297 | gchar qc; 298 | gsize cs; 299 | gsize i; 300 | gsize ssz; 301 | gssize j; 302 | 303 | if (G_UNLIKELY(str == NULL)) { 304 | return NULL; 305 | } 306 | 307 | ssz = strlen(str); 308 | 309 | for (qc = i = 0; i < ssz; i++) { 310 | if ((qc == 0) && (str[i] == chr)) { 311 | return (gchar *) str + i; 312 | } 313 | 314 | if ((str[i] != '"') && (str[i] != '\'')) { 315 | continue; 316 | } 317 | 318 | if ((qc != 0) && (str[i] != qc)) { 319 | continue; 320 | } 321 | 322 | for (cs = 0, j = i - 1; (j >= 0) && (str[j] == '\\'); j--, cs++); 323 | 324 | if ((cs % 2) == 0) { 325 | qc = (qc == 0) ? str[i] : 0; 326 | } 327 | } 328 | 329 | return NULL; 330 | } 331 | -------------------------------------------------------------------------------- /steam/steam-util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2016 James Geboski 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef _STEAM_UTIL_H_ 19 | #define _STEAM_UTIL_H_ 20 | 21 | /** 22 | * SECTION:util 23 | * @section_id: steam-util 24 | * @short_description: steam-util.h 25 | * @title: General Utilities 26 | * 27 | * The general utilities. 28 | */ 29 | 30 | #include "steam-glib.h" 31 | 32 | /** 33 | * STEAM_UTIL_ENUM_NULL: 34 | * 35 | * The #NULL terminating #SteamUtilEnum. 36 | */ 37 | #define STEAM_UTIL_ENUM_NULL {0, NULL} 38 | 39 | typedef struct _SteamUtilEnum SteamUtilEnum; 40 | typedef struct _SteamUtilTimeSpan SteamUtilTimeSpan; 41 | 42 | /** 43 | * SteamDebugLevel: 44 | * STEAM_UTIL_DEBUG_LEVEL_MISC: Miscellaneous message. 45 | * STEAM_UTIL_DEBUG_LEVEL_INFO: Information message. 46 | * STEAM_UTIL_DEBUG_LEVEL_WARN: Warning message. 47 | * STEAM_UTIL_DEBUG_LEVEL_ERROR: Error message. 48 | * STEAM_UTIL_DEBUG_LEVEL_FATAL: Fatal message. 49 | * 50 | * The log message types. 51 | */ 52 | typedef enum 53 | { 54 | STEAM_UTIL_DEBUG_LEVEL_MISC, 55 | STEAM_UTIL_DEBUG_LEVEL_INFO, 56 | STEAM_UTIL_DEBUG_LEVEL_WARN, 57 | STEAM_UTIL_DEBUG_LEVEL_ERROR, 58 | STEAM_UTIL_DEBUG_LEVEL_FATAL 59 | } SteamDebugLevel; 60 | 61 | /** 62 | * SteamUtilEnum: 63 | * @val: The value. 64 | * @ptr: The pointer. 65 | * 66 | * Represents a value/pointer pair for an enumerator. 67 | */ 68 | struct _SteamUtilEnum 69 | { 70 | guint val; 71 | gpointer ptr; 72 | }; 73 | 74 | /** 75 | * SteamUtilTimeSpan: 76 | * @name: The name. 77 | * @span: The span. 78 | * 79 | * Represents a name/span pair for a time span. 80 | */ 81 | struct _SteamUtilTimeSpan 82 | { 83 | gchar *name; 84 | gint64 span; 85 | }; 86 | 87 | /** 88 | * steam_util_debug: 89 | * @level: The #SteamDebugLevel. 90 | * @format: The format string literal. 91 | * @...: The arguments for @format. 92 | * 93 | * Logs a debugging message. 94 | */ 95 | void 96 | steam_util_debug(SteamDebugLevel level, const gchar *format, ...) 97 | G_GNUC_PRINTF(2, 3); 98 | 99 | /** 100 | * steam_util_vdebug: 101 | * @level: The #SteamDebugLevel. 102 | * @format: The format string literal. 103 | * @ap: The #va_list. 104 | * 105 | * Logs a debugging message. 106 | */ 107 | void 108 | steam_util_vdebug(SteamDebugLevel level, const gchar *format, va_list ap); 109 | 110 | /** 111 | * steam_util_debug_misc: 112 | * @format: The format string literal. 113 | * @...: The arguments for @format. 114 | * 115 | * Logs a debugging message with the level of 116 | * #STEAM_UTIL_DEBUG_LEVEL_MISC. 117 | * 118 | */ 119 | void 120 | steam_util_debug_misc(const gchar *format, ...) 121 | G_GNUC_PRINTF(1, 2); 122 | 123 | /** 124 | * steam_util_debug_info: 125 | * @format: The format string literal. 126 | * @...: The arguments for @format. 127 | * 128 | * Logs a debugging message with the level of 129 | * #STEAM_UTIL_DEBUG_LEVEL_INFO. 130 | * 131 | */ 132 | void 133 | steam_util_debug_info(const gchar *format, ...) 134 | G_GNUC_PRINTF(1, 2); 135 | 136 | /** 137 | * steam_util_debug_warn: 138 | * @format: The format string literal. 139 | * @...: The arguments for @format. 140 | * 141 | * Logs a debugging message with the level of 142 | * #STEAM_UTIL_DEBUG_LEVEL_WARN. 143 | * 144 | */ 145 | void 146 | steam_util_debug_warn(const gchar *format, ...) 147 | G_GNUC_PRINTF(1, 2); 148 | 149 | /** 150 | * steam_util_debug_error: 151 | * @format: The format string literal. 152 | * @...: The arguments for @format. 153 | * 154 | * Logs a debugging message with the level of 155 | * #STEAM_UTIL_DEBUG_LEVEL_ERROR. 156 | * 157 | */ 158 | void 159 | steam_util_debug_error(const gchar *format, ...) 160 | G_GNUC_PRINTF(1, 2); 161 | 162 | /** 163 | * steam_util_debug_fatal: 164 | * @format: The format string literal. 165 | * @...: The arguments for @format. 166 | * 167 | * Logs a debugging message with the level of 168 | * #STEAM_UTIL_DEBUG_LEVEL_FATAL. 169 | * 170 | */ 171 | void 172 | steam_util_debug_fatal(const gchar *format, ...) 173 | G_GNUC_PRINTF(1, 2); 174 | 175 | /** 176 | * steam_util_enum_ptr: 177 | * @enums: The array of #SteamUtilEnum. 178 | * @def: The default return value. 179 | * @val: The enumerator value. 180 | * 181 | * Gets the enumerator pointer from its value. 182 | * 183 | * Returns: The enumerator pointer or #NULL on error. 184 | */ 185 | gpointer 186 | steam_util_enum_ptr(const SteamUtilEnum *enums, gpointer def, guint val); 187 | 188 | /** 189 | * steam_util_enum_ptrs: 190 | * @enums: The array of #SteamUtilEnum. 191 | * @vals: The enumerator values. 192 | * 193 | * Gets the enumerator pointers from its value. The returned array 194 | * should be freed when no longer needed. 195 | * 196 | * Returns: The enumerator pointer array. 197 | */ 198 | gpointer * 199 | steam_util_enum_ptrs(const SteamUtilEnum *enums, guint vals); 200 | 201 | /** 202 | * steam_util_enum_val: 203 | * @enums: The array of #SteamUtilEnum. 204 | * @ptr: The enumerator pointer. 205 | * @def: The default return value. 206 | * @cmpfunc: The #GCompareFunc. 207 | * 208 | * Gets the enumerator value from its pointer. 209 | * 210 | * Returns: The enumerator value or `0` on error. 211 | */ 212 | guint 213 | steam_util_enum_val(const SteamUtilEnum *enums, guint def, 214 | gconstpointer ptr, GCompareFunc cmpfunc); 215 | 216 | /** 217 | * steam_util_str_hex2bytes: 218 | * @str: The hexadecimal string. 219 | * 220 | * Converts the hexadecimal string to a #GByteArray. The returned 221 | * #GByteArray should be freed with #g_byte_array_free() when no 222 | * longer needed. 223 | * 224 | * Returns: The #GByteArray or #NULL on error. 225 | */ 226 | GByteArray * 227 | steam_util_str_hex2bytes(const gchar *str); 228 | 229 | /** 230 | * steam_util_str_iequal: 231 | * @s1: The first string. 232 | * @s2: The second string. 233 | * 234 | * Compare two strings case insensitively. This is useful for where 235 | * the return value must be a boolean, such as with a #GEqualFunc. 236 | * 237 | * Returns: #TRUE if the strings are equal, otherwise #FALSE. 238 | */ 239 | gboolean 240 | steam_util_str_iequal(const gchar *s1, const gchar *s2); 241 | 242 | /** 243 | * steam_util_time_span_str: 244 | * @span: The #GTimeSpan. 245 | * 246 | * Gets the string representation of the timespan. The returned string 247 | * should be freed with #g_free() when no longer needed. 248 | * 249 | * Returns: The string representation. 250 | */ 251 | gchar * 252 | steam_util_time_span_str(GTimeSpan span); 253 | 254 | /** 255 | * steam_util_time_since_utc: 256 | * @span: The timestamp (UTC). 257 | * 258 | * Gets the string representation of the timespan since the given 259 | * timestamp. The returned string should be freed with #g_free() when 260 | * no longer needed. 261 | * 262 | * Returns: The string representation. 263 | */ 264 | gchar * 265 | steam_util_time_since_utc(gint64 timestamp); 266 | 267 | /** 268 | * steam_util_ustrchr: 269 | * @str: The string. 270 | * @chr: The character. 271 | * 272 | * Find the first occurrence of the character in a string not contained 273 | * inside quotes (single or double). 274 | * 275 | * Returns: A pointer to the character or #NULL on error. 276 | */ 277 | gchar * 278 | steam_util_ustrchr(const gchar *str, gchar chr); 279 | 280 | #endif /* _STEAM_UTIL_H_ */ 281 | -------------------------------------------------------------------------------- /steam/steam.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2016 James Geboski 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include "steam.h" 23 | #include "steam-util.h" 24 | 25 | #ifndef OPT_SELFMESSAGE 26 | #define OPT_SELFMESSAGE 0 27 | #endif 28 | 29 | static void 30 | steam_cb_relogon(SteamApiReq *req, gpointer data); 31 | 32 | static void 33 | steam_cb_msgs(SteamApiReq *req, gpointer data); 34 | 35 | static void 36 | steam_cb_poll(SteamApiReq *req, gpointer data); 37 | 38 | static void 39 | steam_cb_user_info_nicks(SteamApiReq *req, gpointer data); 40 | 41 | SteamData * 42 | steam_data_new(account_t *acc) 43 | { 44 | SteamData *sata; 45 | 46 | g_return_val_if_fail(acc != NULL, NULL); 47 | 48 | sata = g_new0(SteamData, 1); 49 | sata->api = steam_api_new(); 50 | sata->ic = imcb_new(acc); 51 | sata->ic->proto_data = sata; 52 | 53 | sata->api->umqid = g_strdup(set_getstr(&acc->set, "umqid")); 54 | sata->api->token = g_strdup(set_getstr(&acc->set, "token")); 55 | sata->api->sessid = g_strdup(set_getstr(&acc->set, "sessid")); 56 | sata->game_status = set_getbool(&acc->set, "game_status"); 57 | 58 | steam_api_rehash(sata->api); 59 | return sata; 60 | } 61 | 62 | void 63 | steam_data_free(SteamData *sata) 64 | { 65 | if (G_UNLIKELY(sata == NULL)) { 66 | return; 67 | } 68 | 69 | steam_api_free(sata->api); 70 | g_free(sata); 71 | } 72 | 73 | static gboolean 74 | steam_req_error(SteamData *sata, SteamApiReq *req, gboolean logout) 75 | { 76 | if (req->err == NULL) { 77 | return FALSE; 78 | } 79 | 80 | if (g_error_matches(req->err, STEAM_API_ERROR, STEAM_API_ERROR_EXPRIED)) { 81 | steam_util_debug_info("Relogging on due to expired session"); 82 | steam_http_free_reqs(req->api->http); 83 | req = steam_api_req_new(req->api, steam_cb_relogon, sata); 84 | steam_api_req_logon(req); 85 | return TRUE; 86 | } 87 | 88 | if (g_error_matches(req->err, STEAM_HTTP_ERROR, STEAM_HTTP_ERROR_CLOSED)) { 89 | steam_util_debug_warn("Request (%p) forcefully closed", req->req); 90 | /* Ignore closed HTTP connections */ 91 | return TRUE; 92 | } 93 | 94 | steam_util_debug_error("Error: %s", req->err->message); 95 | imcb_error(sata->ic, "%s", req->err->message); 96 | 97 | if (logout) { 98 | steam_util_debug_info("Reconnecting due to error"); 99 | imc_logout(sata->ic, logout); 100 | } 101 | 102 | return TRUE; 103 | } 104 | 105 | static void 106 | steam_user_status(SteamData *sata, const SteamUserInfo *info, bee_user_t *bu) 107 | { 108 | const gchar *m; 109 | gboolean cgm; 110 | gboolean csv; 111 | gchar *game; 112 | gchar sid[STEAM_ID_STRMAX]; 113 | gint f; 114 | SteamUser *user; 115 | 116 | STEAM_ID_STR(info->id, sid); 117 | 118 | if (bu == NULL) { 119 | bu = imcb_buddy_by_handle(sata->ic, sid); 120 | 121 | if (G_UNLIKELY(bu == NULL)) { 122 | return; 123 | } 124 | } 125 | 126 | if (info->state == STEAM_USER_STATE_OFFLINE) { 127 | imcb_buddy_status(sata->ic, sid, 0, NULL, NULL); 128 | return; 129 | } 130 | 131 | f = BEE_USER_ONLINE; 132 | m = steam_user_state_str(info->state); 133 | 134 | if (info->game != NULL) { 135 | f |= BEE_USER_SPECIAL; 136 | } else if (info->state != STEAM_USER_STATE_ONLINE) { 137 | f |= BEE_USER_AWAY; 138 | } 139 | 140 | user = bu->data; 141 | cgm = g_strcmp0(info->game, user->game) != 0; 142 | csv = g_strcmp0(info->server, user->server) != 0; 143 | 144 | if (!cgm && !csv) { 145 | imcb_buddy_status(sata->ic, sid, f, m, bu->status_msg); 146 | return; 147 | } 148 | 149 | if (info->server != NULL) { 150 | game = g_strdup_printf("%s (%s)", info->game, info->server); 151 | } else { 152 | game = g_strdup(info->game); 153 | } 154 | 155 | if (cgm) { 156 | g_free(user->game); 157 | user->game = g_strdup(info->game); 158 | } 159 | 160 | if (csv) { 161 | g_free(user->server); 162 | user->server = g_strdup(info->server); 163 | } 164 | 165 | if (sata->game_status && (game != NULL)) { 166 | steam_user_chans_msg(user, "/me is now playing: %s", game); 167 | } 168 | 169 | imcb_buddy_status(sata->ic, sid, f, m, game); 170 | g_free(game); 171 | } 172 | 173 | static void 174 | steam_user_msg(SteamData *sata, SteamUserMsg *msg, gint64 time) 175 | { 176 | bee_user_t *bu; 177 | gchar sid[STEAM_ID_STRMAX]; 178 | gchar *str; 179 | guint32 f; 180 | SteamUserInfo *info = msg->info; 181 | 182 | STEAM_ID_STR(info->id, sid); 183 | steam_util_debug_info("Incoming message from %s (Type: %u, Act: %u)", 184 | sid, msg->type, info->act); 185 | 186 | switch (msg->type) { 187 | case STEAM_USER_MSG_TYPE_MY_EMOTE: 188 | case STEAM_USER_MSG_TYPE_MY_SAYTEXT: 189 | if (set_find(&sata->ic->bee->set, "self_messages") == NULL) { 190 | return; 191 | } 192 | 193 | if (msg->type == STEAM_USER_MSG_TYPE_MY_EMOTE) { 194 | str = g_strconcat("/me ", msg->text, NULL); 195 | } else { 196 | str = g_strdup(msg->text); 197 | } 198 | 199 | imcb_buddy_msg(sata->ic, sid, str, OPT_SELFMESSAGE, time); 200 | g_free(str); 201 | return; 202 | 203 | case STEAM_USER_MSG_TYPE_EMOTE: 204 | case STEAM_USER_MSG_TYPE_SAYTEXT: 205 | bu = imcb_buddy_by_handle(sata->ic, sid); 206 | 207 | if ((bu != NULL) && (bu->flags & OPT_TYPING)) { 208 | imcb_buddy_typing(sata->ic, sid, 0); 209 | } 210 | 211 | if (msg->type == STEAM_USER_MSG_TYPE_EMOTE) { 212 | str = g_strconcat("/me ", msg->text, NULL); 213 | } else { 214 | str = g_strdup(msg->text); 215 | } 216 | 217 | imcb_buddy_msg(sata->ic, sid, str, 0, time); 218 | g_free(str); 219 | return; 220 | 221 | case STEAM_USER_MSG_TYPE_LEFT_CONV: 222 | imcb_buddy_typing(sata->ic, sid, 0); 223 | return; 224 | 225 | case STEAM_USER_MSG_TYPE_RELATIONSHIP: 226 | goto relationship; 227 | 228 | case STEAM_USER_MSG_TYPE_TYPING: 229 | bu = imcb_buddy_by_handle(sata->ic, sid); 230 | 231 | if (G_UNLIKELY(bu == NULL)) { 232 | return; 233 | } 234 | 235 | f = (bu->flags & OPT_TYPING) ? 0 : OPT_TYPING; 236 | imcb_buddy_typing(sata->ic, sid, f); 237 | return; 238 | 239 | default: 240 | steam_user_status(sata, info, NULL); 241 | return; 242 | } 243 | 244 | relationship: 245 | switch (info->act) { 246 | case STEAM_USER_ACT_REMOVE: 247 | case STEAM_USER_ACT_IGNORE: 248 | imcb_remove_buddy(sata->ic, sid, NULL); 249 | return; 250 | 251 | case STEAM_USER_ACT_REQUEST: 252 | imcb_ask_auth(sata->ic, sid, info->nick); 253 | return; 254 | 255 | case STEAM_USER_ACT_ADD: 256 | imcb_add_buddy(sata->ic, sid, NULL); 257 | imcb_buddy_nick_hint(sata->ic, sid, info->nick); 258 | imcb_rename_buddy(sata->ic, sid, info->fullname); 259 | steam_user_status(sata, info, NULL); 260 | return; 261 | 262 | default: 263 | return; 264 | } 265 | } 266 | 267 | static void 268 | steam_cb_auth(SteamApiReq *req, gpointer data) 269 | { 270 | gchar *str; 271 | SteamData *sata = data; 272 | account_t *acc = sata->ic->acc; 273 | 274 | set_setint(&acc->set, "autht", req->api->autht); 275 | set_setstr(&acc->set, "cgid", req->api->cgid); 276 | set_setstr(&acc->set, "esid", req->api->esid); 277 | set_setstr(&acc->set, "sessid", req->api->sessid); 278 | set_setstr(&acc->set, "token", req->api->token); 279 | 280 | if (steam_req_error(sata, req, FALSE)) { 281 | if (req->err->domain != STEAM_API_ERROR) { 282 | imc_logout(sata->ic, FALSE); 283 | return; 284 | } 285 | 286 | switch (req->err->code) { 287 | case STEAM_API_ERROR_CAPTCHA: 288 | str = steam_api_captcha_url(req->api->cgid); 289 | imcb_log(sata->ic, "View: %s", str); 290 | imcb_log(sata->ic, "Run: account %s set captcha ", acc->tag); 291 | g_free(str); 292 | break; 293 | 294 | case STEAM_API_ERROR_STEAMGUARD: 295 | imcb_log(sata->ic, "Run: account %s set authcode ", acc->tag); 296 | break; 297 | } 298 | 299 | imc_logout(sata->ic, FALSE); 300 | return; 301 | } 302 | 303 | imcb_log(sata->ic, "Authentication finished"); 304 | steam_api_free_auth(req->api); 305 | account_off(acc->bee, acc); 306 | account_on(acc->bee, acc); 307 | } 308 | 309 | static void 310 | steam_cb_friends(SteamApiReq *req, gpointer data) 311 | { 312 | bee_user_t *bu; 313 | gchar sid[STEAM_ID_STRMAX]; 314 | GList *l; 315 | SteamData *sata = data; 316 | SteamUser *user; 317 | SteamUserInfo *info; 318 | struct im_connection *ic = sata->ic; 319 | 320 | if (steam_req_error(sata, req, TRUE)) { 321 | return; 322 | } 323 | 324 | if (!(ic->flags & BEE_USER_ONLINE)) { 325 | imcb_connected(ic); 326 | } 327 | 328 | for (l = req->infs->head; l != NULL; l = l->next) { 329 | info = l->data; 330 | STEAM_ID_STR(info->id, sid); 331 | 332 | /* Attempt to grab the buddy before adding */ 333 | bu = bee_user_by_handle(sata->ic->bee, sata->ic, sid); 334 | 335 | if (bu == NULL) { 336 | imcb_add_buddy(sata->ic, sid, NULL); 337 | imcb_buddy_nick_hint(sata->ic, sid, info->nick); 338 | imcb_rename_buddy(sata->ic, sid, info->fullname); 339 | } 340 | 341 | bu = bee_user_by_handle(sata->ic->bee, sata->ic, sid); 342 | 343 | if (G_UNLIKELY(bu == NULL)) { 344 | continue; 345 | } 346 | 347 | user = bu->data; 348 | user->vtime = info->vtime; 349 | 350 | switch (info->rel) { 351 | case STEAM_USER_REL_FRIEND: 352 | steam_user_status(sata, info, bu); 353 | break; 354 | 355 | case STEAM_USER_REL_IGNORE: 356 | ic->deny = g_slist_prepend(ic->deny, g_strdup(bu->handle)); 357 | break; 358 | } 359 | 360 | if (info->unread > 0) { 361 | req = steam_api_req_new(req->api, steam_cb_msgs, sata); 362 | steam_api_req_msgs(req, info->id, info->vtime); 363 | } 364 | } 365 | 366 | req = steam_api_req_new(req->api, steam_cb_poll, sata); 367 | steam_api_req_poll(req); 368 | } 369 | 370 | static void 371 | steam_cb_key(SteamApiReq *req, gpointer data) 372 | { 373 | account_t *acc; 374 | gchar *ac; 375 | gchar *cc; 376 | SteamData *sata = data; 377 | 378 | if (steam_req_error(sata, req, TRUE)) { 379 | return; 380 | } 381 | 382 | acc = sata->ic->acc; 383 | ac = set_getstr(&acc->set, "authcode"); 384 | cc = set_getstr(&acc->set, "captcha"); 385 | 386 | imcb_log(sata->ic, "Requesting authentication token"); 387 | req = steam_api_req_new(req->api, steam_cb_auth, sata); 388 | steam_api_req_auth(req, acc->user, acc->pass, ac, cc); 389 | } 390 | 391 | static void 392 | steam_cb_logoff(SteamApiReq *req, gpointer data) 393 | { 394 | SteamData *sata = data; 395 | steam_data_free(sata); 396 | } 397 | 398 | static void 399 | steam_cb_logon(SteamApiReq *req, gpointer data) 400 | { 401 | SteamData *sata = data; 402 | 403 | if (steam_req_error(sata, req, TRUE)) { 404 | return; 405 | } 406 | 407 | set_setstr(&sata->ic->acc->set, "umqid", req->api->umqid); 408 | imcb_log(sata->ic, "Requesting friends list"); 409 | 410 | req = steam_api_req_new(req->api, steam_cb_friends, sata); 411 | steam_api_req_friends(req); 412 | } 413 | 414 | static void 415 | steam_cb_relogon(SteamApiReq *req, gpointer data) 416 | { 417 | SteamData *sata = data; 418 | 419 | if (steam_req_error(sata, req, TRUE)) { 420 | return; 421 | } 422 | 423 | steam_util_debug_info("Relogon completed"); 424 | req = steam_api_req_new(req->api, steam_cb_friends, sata); 425 | steam_api_req_friends(req); 426 | } 427 | 428 | static void 429 | steam_cb_msg(SteamApiReq *req, gpointer data) 430 | { 431 | SteamData *sata = data; 432 | steam_req_error(sata, req, TRUE); 433 | } 434 | 435 | static void 436 | steam_cb_msgs(SteamApiReq *req, gpointer data) 437 | { 438 | bee_user_t *bu; 439 | gchar sid[STEAM_ID_STRMAX]; 440 | GList *l; 441 | SteamData *sata = data; 442 | SteamUser *user; 443 | SteamUserInfo *info; 444 | SteamUserMsg *msg; 445 | 446 | if (steam_req_error(sata, req, TRUE)) { 447 | return; 448 | } 449 | 450 | for (bu = NULL, l = req->msgs->head; l != NULL; l = l->next) { 451 | msg = l->data; 452 | info = msg->info; 453 | STEAM_ID_STR(info->id, sid); 454 | 455 | if ((bu == NULL) || (g_strcmp0(sid, bu->handle) != 0)) { 456 | bu = bee_user_by_handle(sata->ic->bee, sata->ic, sid); 457 | 458 | if (G_UNLIKELY(bu == NULL)) { 459 | continue; 460 | } 461 | 462 | user = bu->data; 463 | } 464 | 465 | if (msg->time > user->vtime) { 466 | steam_user_msg(sata, msg, msg->time); 467 | } 468 | } 469 | } 470 | 471 | static void 472 | steam_cb_poll(SteamApiReq *req, gpointer data) 473 | { 474 | GList *l; 475 | SteamData *sata = data; 476 | 477 | if (steam_req_error(sata, req, TRUE)) { 478 | return; 479 | } 480 | 481 | for (l = req->msgs->head; l != NULL; l = l->next) { 482 | steam_user_msg(sata, l->data, 0); 483 | } 484 | 485 | req = steam_api_req_new(req->api, steam_cb_poll, sata); 486 | steam_api_req_poll(req); 487 | } 488 | 489 | static void 490 | steam_cb_user_action(SteamApiReq *req, gpointer data) 491 | { 492 | SteamData *sata = data; 493 | SteamUserInfo *info = req->infs->head->data; 494 | 495 | if (steam_req_error(sata, req, TRUE)) { 496 | return; 497 | } 498 | 499 | steam_user_status(sata, info, NULL); 500 | } 501 | 502 | static void 503 | steam_cb_user_info(SteamApiReq *req, gpointer data) 504 | { 505 | req = steam_api_req_fwd(req); 506 | req->func = steam_cb_user_info_nicks; 507 | steam_api_req_user_info_nicks(req); 508 | } 509 | 510 | static void 511 | steam_cb_user_info_nicks(SteamApiReq *req, gpointer data) 512 | { 513 | const gchar *ctr; 514 | gchar *str; 515 | GSList *l; 516 | guint i; 517 | SteamData *sata = data; 518 | SteamUserInfo *info = req->infs->head->data; 519 | 520 | if (steam_req_error(sata, req, TRUE)) { 521 | return; 522 | } 523 | 524 | if (info->fullname != NULL) { 525 | imcb_log(sata->ic, "Name: %s (%s)", info->nick, info->fullname); 526 | } else { 527 | imcb_log(sata->ic, "Name: %s", info->nick); 528 | } 529 | 530 | if (info->game != NULL) { 531 | if (info->server != NULL) { 532 | imcb_log(sata->ic, "Playing: %s - steam://connect/%s", 533 | info->game, info->server); 534 | } else { 535 | imcb_log(sata->ic, "Playing: %s", info->game); 536 | } 537 | } 538 | 539 | ctr = steam_user_state_str(info->state); 540 | 541 | if (info->state == STEAM_USER_STATE_OFFLINE) { 542 | str = steam_util_time_since_utc(info->ltime); 543 | } else { 544 | str = steam_user_flags_str(info->flags); 545 | } 546 | 547 | if (str != NULL) { 548 | imcb_log(sata->ic, "Status: %s (%s)", ctr, str); 549 | g_free(str); 550 | } else { 551 | imcb_log(sata->ic, "Status: %s", ctr); 552 | } 553 | 554 | imcb_log(sata->ic, "Steam ID: %" STEAM_ID_FORMAT " (%" G_GINT32_FORMAT ")", 555 | info->id, STEAM_ID_ACCID(info->id)); 556 | 557 | if (info->profile != NULL) { 558 | imcb_log(sata->ic, "Profile: %s", info->profile); 559 | } 560 | 561 | if (info->nicks != NULL) { 562 | imcb_log(sata->ic, "Nicknames:"); 563 | 564 | for (l = info->nicks, i = 1; l != NULL; l = l->next, i++) { 565 | imcb_log(sata->ic, "%u. `%s'", i, (gchar *) l->data); 566 | } 567 | } 568 | 569 | steam_user_status(sata, info, NULL); 570 | } 571 | 572 | static void 573 | steam_cb_user_search(SteamApiReq *req, gpointer data) 574 | { 575 | const gchar *tag; 576 | gchar sid[STEAM_ID_STRMAX]; 577 | GList *l; 578 | guint i; 579 | SteamData *sata = data; 580 | SteamUserInfo *info; 581 | 582 | if (steam_req_error(sata, req, TRUE)) { 583 | return; 584 | } 585 | 586 | for (l = req->infs->head, i = 0; (l != NULL) && (i < 2); l = l->next, i++); 587 | 588 | switch (i) { 589 | case 0: 590 | imcb_error(sata->ic, "Failed to find any friend(s)"); 591 | return; 592 | 593 | case 1: 594 | info = req->infs->head->data; 595 | req = steam_api_req_new(req->api, steam_cb_user_action, sata); 596 | steam_api_req_user_add(req, info->id); 597 | return; 598 | } 599 | 600 | imcb_log(sata->ic, "Select from one of the following Steam Friends:"); 601 | tag = sata->ic->acc->tag; 602 | 603 | for (l = req->infs->head, i = 1; l != NULL; l = l->next, i++) { 604 | info = l->data; 605 | STEAM_ID_STR(info->id, sid); 606 | 607 | imcb_log(sata->ic, "%u. `%s' %s", i, info->nick, info->profile); 608 | imcb_log(sata->ic, "-- add %s steamid:%s", tag, sid); 609 | } 610 | } 611 | 612 | static char * 613 | steam_eval_accounton(set_t *set, char *value) 614 | { 615 | account_t *acc = set->data; 616 | 617 | if ((acc->ic != NULL) && (acc->ic->flags & BEE_USER_ONLINE)) { 618 | return value; 619 | } 620 | 621 | /* Some hackery to auto connect upon authcode entry */ 622 | 623 | g_free(set->value); 624 | set->value = g_strdup(value); 625 | account_on(acc->bee, acc); 626 | 627 | g_free(set->value); 628 | set->value = NULL; 629 | return value; 630 | } 631 | 632 | static char * 633 | steam_eval_game_status(set_t *set, char *value) 634 | { 635 | account_t *acc = set->data; 636 | SteamData *sata; 637 | 638 | if (!is_bool(value)) { 639 | return SET_INVALID; 640 | } 641 | 642 | if (acc->ic == NULL) { 643 | return value; 644 | } 645 | 646 | sata = acc->ic->proto_data; 647 | sata->game_status = bool2int(value); 648 | return value; 649 | } 650 | 651 | static char * 652 | steam_eval_password(set_t *set, char *value) 653 | { 654 | account_t *acc = set->data; 655 | 656 | value = set_eval_account(set, value); 657 | set_reset(&acc->set, "token"); 658 | 659 | if (acc->ic != NULL) { 660 | account_off(acc->bee, acc); 661 | account_on(acc->bee, acc); 662 | } else if (acc->reconnect != 0) { 663 | account_on(acc->bee, acc); 664 | } 665 | 666 | return value; 667 | } 668 | 669 | static void 670 | steam_init(account_t *acc) 671 | { 672 | set_t *s; 673 | 674 | s = set_add(&acc->set, "authcode", NULL, steam_eval_accounton, acc); 675 | s->flags = SET_NULL_OK | SET_HIDDEN | SET_NOSAVE; 676 | 677 | s = set_add(&acc->set, "captcha", NULL, steam_eval_accounton, acc); 678 | s->flags = SET_NULL_OK | SET_HIDDEN | SET_NOSAVE; 679 | 680 | s = set_add(&acc->set, "autht", NULL, NULL, acc); 681 | s->flags = SET_NULL_OK | SET_HIDDEN | SET_NOSAVE; 682 | 683 | s = set_add(&acc->set, "esid", NULL, NULL, acc); 684 | s->flags = SET_NULL_OK | SET_HIDDEN | SET_NOSAVE; 685 | 686 | s = set_add(&acc->set, "cgid", NULL, NULL, acc); 687 | s->flags = SET_NULL_OK | SET_HIDDEN | SET_NOSAVE; 688 | 689 | s = set_add(&acc->set, "umqid", NULL, NULL, acc); 690 | s->flags = SET_NULL_OK | SET_HIDDEN; 691 | 692 | s = set_add(&acc->set, "token", NULL, NULL, acc); 693 | s->flags = SET_NULL_OK | SET_HIDDEN | SET_PASSWORD; 694 | 695 | s = set_add(&acc->set, "sessid", NULL, NULL, acc); 696 | s->flags = SET_NULL_OK | SET_HIDDEN | SET_PASSWORD; 697 | 698 | set_add(&acc->set, "game_status", "false", steam_eval_game_status, acc); 699 | set_add(&acc->set, "password", NULL, steam_eval_password, acc); 700 | } 701 | 702 | static void 703 | steam_login(account_t *acc) 704 | { 705 | gchar *str; 706 | SteamApiReq *req; 707 | SteamData *sata; 708 | 709 | sata = steam_data_new(acc); 710 | imcb_log(sata->ic, "Connecting"); 711 | 712 | if ((sata->api->token == NULL) || (sata->api->sessid == NULL)) { 713 | str = set_getstr(&acc->set, "cgid"); 714 | g_free(sata->api->cgid); 715 | sata->api->cgid = g_strdup(str); 716 | 717 | str = set_getstr(&acc->set, "esid"); 718 | g_free(sata->api->esid); 719 | sata->api->esid = g_strdup(str); 720 | 721 | sata->api->autht = set_getint(&acc->set, "autht"); 722 | imcb_log(sata->ic, "Requesting authentication key"); 723 | req = steam_api_req_new(sata->api, steam_cb_key, sata); 724 | steam_api_req_key(req, acc->user); 725 | return; 726 | } 727 | 728 | imcb_log(sata->ic, "Sending logon request"); 729 | req = steam_api_req_new(sata->api, steam_cb_logon, sata); 730 | steam_api_req_logon(req); 731 | } 732 | 733 | static void 734 | steam_logout(struct im_connection *ic) 735 | { 736 | SteamApiReq *req; 737 | SteamData *sata = ic->proto_data; 738 | 739 | steam_http_free_reqs(sata->api->http); 740 | 741 | if (ic->flags & BEE_USER_ONLINE) { 742 | req = steam_api_req_new(sata->api, steam_cb_logoff, sata); 743 | steam_api_req_logoff(req); 744 | } else { 745 | steam_data_free(sata); 746 | } 747 | } 748 | 749 | static int 750 | steam_buddy_msg(struct im_connection *ic, char *to, char *message, int flags) 751 | { 752 | SteamApiReq *req; 753 | SteamData *sata = ic->proto_data; 754 | SteamUserMsg *msg; 755 | 756 | msg = steam_user_msg_new(STEAM_ID_NEW_STR(to)); 757 | msg->type = STEAM_USER_MSG_TYPE_SAYTEXT; 758 | msg->text = g_strdup(message); 759 | 760 | /* As of January 23, 2013, Valve has disabled support for /me. It 761 | * was disabled as it "allowed some users to modify the color of 762 | * their chat text." 763 | * 764 | * See the ChangeLog for more information: http://goo.gl/TETV5 765 | */ 766 | 767 | /* 768 | if (g_str_has_prefix(message, "/me")) { 769 | if (strlen(message) < 5) 770 | return 0; 771 | 772 | msg->type = STEAM_USER_MSG_TYPE_EMOTE; 773 | msg->text = g_strdup(message + 4); 774 | } else { 775 | msg->type = STEAM_USER_MSG_TYPE_SAYTEXT; 776 | msg->text = g_strdup(message); 777 | } 778 | */ 779 | 780 | req = steam_api_req_new(sata->api, steam_cb_msg, sata); 781 | steam_api_req_msg(req, msg); 782 | steam_user_msg_free(msg); 783 | return 0; 784 | } 785 | 786 | static void 787 | steam_set_away(struct im_connection *ic, char *state, char *message) 788 | { 789 | SteamData *sata = ic->proto_data; 790 | 791 | if (g_strcmp0(state, "Away") == 0) { 792 | sata->api->info->state = STEAM_USER_STATE_AWAY; 793 | } else if (g_strcmp0(state, "Snooze") == 0) { 794 | sata->api->info->state = STEAM_USER_STATE_SNOOZE; 795 | } else { 796 | sata->api->info->state = STEAM_USER_STATE_ONLINE; 797 | } 798 | } 799 | 800 | static int 801 | steam_send_typing(struct im_connection *ic, char *who, int flags) 802 | { 803 | SteamApiReq *req; 804 | SteamData *sata = ic->proto_data; 805 | SteamUserMsg *msg; 806 | 807 | msg = steam_user_msg_new(STEAM_ID_NEW_STR(who)); 808 | msg->type = STEAM_USER_MSG_TYPE_TYPING; 809 | 810 | req = steam_api_req_new(sata->api, steam_cb_msg, sata); 811 | steam_api_req_msg(req, msg); 812 | steam_user_msg_free(msg); 813 | return 0; 814 | } 815 | 816 | static void 817 | steam_add_buddy(struct im_connection *ic, char *name, char *group) 818 | { 819 | gchar *str; 820 | SteamApiReq *req; 821 | SteamData *sata = ic->proto_data; 822 | 823 | if (g_ascii_strncasecmp(name, "steamid:", 8) != 0) { 824 | req = steam_api_req_new(sata->api, steam_cb_user_search, sata); 825 | steam_api_req_user_search(req, name, 5); 826 | return; 827 | } 828 | 829 | str = strchr(name, ':'); 830 | 831 | if ((str != NULL) && ((++str)[0] != 0)) { 832 | req = steam_api_req_new(sata->api, steam_cb_user_action, sata); 833 | steam_api_req_user_add(req, STEAM_ID_NEW_STR(str)); 834 | } else { 835 | imcb_error(sata->ic, "No Steam ID specified"); 836 | } 837 | } 838 | 839 | static void 840 | steam_remove_buddy(struct im_connection *ic, char *name, char *group) 841 | { 842 | SteamApiReq *req; 843 | SteamData *sata = ic->proto_data; 844 | 845 | req = steam_api_req_new(sata->api, steam_cb_user_action, sata); 846 | steam_api_req_user_remove(req, STEAM_ID_NEW_STR(name)); 847 | } 848 | 849 | static void 850 | steam_add_permit(struct im_connection *ic, char *who) 851 | { 852 | 853 | } 854 | 855 | static void 856 | steam_add_deny(struct im_connection *ic, char *who) 857 | { 858 | SteamApiReq *req; 859 | SteamData *sata = ic->proto_data; 860 | 861 | imcb_buddy_status(ic, who, 0, NULL, NULL); 862 | req = steam_api_req_new(sata->api, steam_cb_user_action, sata); 863 | steam_api_req_user_ignore(req, STEAM_ID_NEW_STR(who), TRUE); 864 | } 865 | 866 | static void 867 | steam_rem_permit(struct im_connection *ic, char *who) 868 | { 869 | 870 | } 871 | 872 | static void 873 | steam_rem_deny(struct im_connection *ic, char *who) 874 | { 875 | SteamApiReq *req; 876 | SteamData *sata = ic->proto_data; 877 | 878 | req = steam_api_req_new(sata->api, steam_cb_user_action, sata); 879 | steam_api_req_user_ignore(req, STEAM_ID_NEW_STR(who), FALSE); 880 | } 881 | 882 | static void 883 | steam_get_info(struct im_connection *ic, char *who) 884 | { 885 | SteamApiReq *req; 886 | SteamData *sata = ic->proto_data; 887 | SteamUserInfo *info; 888 | 889 | info = steam_user_info_new(STEAM_ID_NEW_STR(who)); 890 | req = steam_api_req_new(sata->api, steam_cb_user_info, sata); 891 | 892 | g_queue_push_head(req->infs, info); 893 | steam_api_req_user_info(req); 894 | } 895 | 896 | static GList * 897 | steam_away_states(struct im_connection *ic) 898 | { 899 | static GList *states = NULL; 900 | 901 | if (G_UNLIKELY(states == NULL)) { 902 | /* Steam only support setting "Away" and "Snooze" */ 903 | states = g_list_prepend(states, "Snooze"); 904 | states = g_list_prepend(states, "Away"); 905 | } 906 | 907 | return states; 908 | } 909 | 910 | static void 911 | steam_auth_allow(struct im_connection *ic, const char *who) 912 | { 913 | SteamApiReq *req; 914 | SteamData *sata = ic->proto_data; 915 | 916 | req = steam_api_req_new(sata->api, steam_cb_user_action, sata); 917 | steam_api_req_user_accept(req, STEAM_ID_NEW_STR(who), 918 | STEAM_API_ACCEPT_TYPE_DEFAULT); 919 | } 920 | 921 | static void 922 | steam_auth_deny(struct im_connection *ic, const char *who) 923 | { 924 | SteamApiReq *req; 925 | SteamData *sata = ic->proto_data; 926 | 927 | req = steam_api_req_new(sata->api, steam_cb_user_action, sata); 928 | steam_api_req_user_accept(req, STEAM_ID_NEW_STR(who), 929 | STEAM_API_ACCEPT_TYPE_IGNORE); 930 | } 931 | 932 | static void 933 | steam_buddy_data_add(struct bee_user *bu) 934 | { 935 | bu->data = steam_user_new(bu); 936 | } 937 | 938 | static void 939 | steam_buddy_data_free(struct bee_user *bu) 940 | { 941 | steam_user_free(bu->data); 942 | } 943 | 944 | G_MODULE_EXPORT void 945 | init_plugin(void); 946 | 947 | G_MODULE_EXPORT void 948 | init_plugin(void) 949 | { 950 | struct prpl *dpp; 951 | 952 | static const struct prpl pp = { 953 | .name = "steam", 954 | .init = steam_init, 955 | .login = steam_login, 956 | .logout = steam_logout, 957 | .buddy_msg = steam_buddy_msg, 958 | .set_away = steam_set_away, 959 | .send_typing = steam_send_typing, 960 | .add_buddy = steam_add_buddy, 961 | .remove_buddy = steam_remove_buddy, 962 | .add_permit = steam_add_permit, 963 | .add_deny = steam_add_deny, 964 | .rem_permit = steam_rem_permit, 965 | .rem_deny = steam_rem_deny, 966 | .get_info = steam_get_info, 967 | .away_states = steam_away_states, 968 | .handle_cmp = g_ascii_strcasecmp, 969 | .auth_allow = steam_auth_allow, 970 | .auth_deny = steam_auth_deny, 971 | .buddy_data_add = steam_buddy_data_add, 972 | .buddy_data_free = steam_buddy_data_free 973 | }; 974 | 975 | if (gcry_check_version(GCRYPT_VERSION) == NULL) { 976 | steam_util_debug_fatal("Failed to initialize libgcrypt"); 977 | return; 978 | } 979 | 980 | dpp = g_memdup(&pp, sizeof pp); 981 | register_protocol(dpp); 982 | } 983 | 984 | #ifdef BITLBEE_ABI_VERSION_CODE 985 | G_MODULE_EXPORT struct plugin_info * 986 | init_plugin_info(void); 987 | 988 | G_MODULE_EXPORT struct plugin_info * 989 | init_plugin_info(void) 990 | { 991 | static struct plugin_info info = { 992 | BITLBEE_ABI_VERSION_CODE, 993 | "steam", 994 | PACKAGE_VERSION, 995 | "Steam protocol plugin", 996 | "James Geboski ", 997 | PACKAGE_URL 998 | }; 999 | 1000 | return &info; 1001 | } 1002 | #endif /* BITLBEE_ABI_VERSION_CODE */ 1003 | -------------------------------------------------------------------------------- /steam/steam.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2016 James Geboski 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef _STEAM_H_ 19 | #define _STEAM_H_ 20 | 21 | /** 22 | * SECTION:steam 23 | * @section_id: steam-plugin 24 | * @short_description: steam.h 25 | * @title: Steam Plugin 26 | * 27 | * The Steam BitlBee #prpl. 28 | */ 29 | 30 | #include 31 | 32 | #include "steam-api.h" 33 | #include "steam-glib.h" 34 | 35 | typedef struct _SteamData SteamData; 36 | 37 | /** 38 | * SteamData: 39 | * @api: The #SteamApi. 40 | * @ic: The #im_connection. 41 | * @game_status: #TRUE to print game statues, otherwise #FALSE. 42 | * 43 | * The main data structure for the plugin. 44 | */ 45 | struct _SteamData 46 | { 47 | SteamApi *api; 48 | struct im_connection *ic; 49 | gboolean game_status; 50 | }; 51 | 52 | /** 53 | * steam_data_new: 54 | * @acc: The #account. 55 | * 56 | * Creates a new #SteamData. The returned #SteamData should be freed 57 | * with #steam_data_free() when no longer needed. 58 | * 59 | * Returns: The #SteamData. 60 | */ 61 | SteamData * 62 | steam_data_new(account_t *acc); 63 | 64 | /** 65 | * steam_data_free: 66 | * @sata: The #SteamData. 67 | * 68 | * Frees all memory used by the #SteamData. 69 | */ 70 | void 71 | steam_data_free(SteamData *sata); 72 | 73 | #endif /* _STEAM_H_ */ 74 | --------------------------------------------------------------------------------