├── LICENSE ├── Makefile ├── README.md ├── aes.c ├── aes.h ├── gc-pbkdf2-sha1.c ├── glibcompat.h ├── libjson-glib-1.0.dll ├── libpulsesms.c ├── pulsesms16.png ├── pulsesms22.png ├── pulsesms24.png ├── pulsesms48.png ├── purple2compat ├── circularbuffer.h ├── glibcompat.h ├── http.c ├── http.h ├── image-store.h ├── image.h ├── internal.h ├── plugins.h ├── purple-socket.c └── purple-socket.h └── purplecompat.h /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | PIDGIN_TREE_TOP ?= ../pidgin-2.10.11 3 | PIDGIN3_TREE_TOP ?= ../pidgin-main 4 | LIBPURPLE_DIR ?= $(PIDGIN_TREE_TOP)/libpurple 5 | WIN32_DEV_TOP ?= $(PIDGIN_TREE_TOP)/../win32-dev 6 | 7 | WIN32_CC ?= $(WIN32_DEV_TOP)/mingw-4.7.2/bin/gcc 8 | MAKENSIS ?= makensis 9 | 10 | PKG_CONFIG ?= pkg-config 11 | 12 | CFLAGS ?= -O2 -g -pipe 13 | LDFLAGS ?= 14 | 15 | # Do some nasty OS and purple version detection 16 | ifeq ($(OS),Windows_NT) 17 | #only defined on 64-bit windows 18 | PROGFILES32 = ${ProgramFiles(x86)} 19 | ifndef PROGFILES32 20 | PROGFILES32 = $(PROGRAMFILES) 21 | endif 22 | PULSESMS_TARGET = libpulsesms.dll 23 | PULSESMS_DEST = "$(PROGFILES32)/Pidgin/plugins" 24 | PULSESMS_ICONS_DEST = "$(PROGFILES32)/Pidgin/pixmaps/pidgin/protocols" 25 | MAKENSIS = "$(PROGFILES32)/NSIS/makensis.exe" 26 | else 27 | 28 | UNAME_S := $(shell uname -s) 29 | 30 | #.. There are special flags we need for OSX 31 | ifeq ($(UNAME_S), Darwin) 32 | # 33 | #.. /opt/local/include and subdirs are included here to ensure this compiles 34 | # for folks using Macports. I believe Homebrew uses /usr/local/include 35 | # so things should "just work". You *must* make sure your packages are 36 | # all up to date or you will most likely get compilation errors. 37 | # 38 | INCLUDES = -I/opt/local/include -lz $(OS) 39 | 40 | CC = gcc 41 | else 42 | INCLUDES = 43 | CC ?= gcc 44 | endif 45 | 46 | ifeq ($(shell $(PKG_CONFIG) --exists purple-3 2>/dev/null && echo "true"),) 47 | ifeq ($(shell $(PKG_CONFIG) --exists purple 2>/dev/null && echo "true"),) 48 | PULSESMS_TARGET = FAILNOPURPLE 49 | PULSESMS_DEST = 50 | PULSESMS_ICONS_DEST = 51 | else 52 | PULSESMS_TARGET = libpulsesms.so 53 | PULSESMS_DEST = $(DESTDIR)`$(PKG_CONFIG) --variable=plugindir purple` 54 | PULSESMS_ICONS_DEST = $(DESTDIR)`$(PKG_CONFIG) --variable=datadir purple`/pixmaps/pidgin/protocols 55 | endif 56 | else 57 | PULSESMS_TARGET = libpulsesms3.so 58 | PULSESMS_DEST = $(DESTDIR)`$(PKG_CONFIG) --variable=plugindir purple-3` 59 | PULSESMS_ICONS_DEST = $(DESTDIR)`$(PKG_CONFIG) --variable=datadir purple-3`/pixmaps/pidgin/protocols 60 | endif 61 | endif 62 | 63 | WIN32_CFLAGS = -I$(WIN32_DEV_TOP)/glib-2.28.8/include -I$(WIN32_DEV_TOP)/glib-2.28.8/include/glib-2.0 -I$(WIN32_DEV_TOP)/glib-2.28.8/lib/glib-2.0/include -I$(WIN32_DEV_TOP)/json-glib-0.14/include/json-glib-1.0 -DENABLE_NLS -DPACKAGE_VERSION='"$(PLUGIN_VERSION)"' -Wall -Wextra -Werror -Wno-deprecated-declarations -Wno-unused-parameter -fno-strict-aliasing -Wformat 64 | WIN32_LDFLAGS = -L$(WIN32_DEV_TOP)/glib-2.28.8/lib -L$(WIN32_DEV_TOP)/json-glib-0.14/lib -lpurple -lintl -lglib-2.0 -lgobject-2.0 -ljson-glib-1.0 -g -ggdb -static-libgcc -lz 65 | WIN32_PIDGIN2_CFLAGS = -I$(PIDGIN_TREE_TOP)/libpurple -I$(PIDGIN_TREE_TOP) $(WIN32_CFLAGS) 66 | WIN32_PIDGIN3_CFLAGS = -I$(PIDGIN3_TREE_TOP)/libpurple -I$(PIDGIN3_TREE_TOP) -I$(WIN32_DEV_TOP)/gplugin-dev/gplugin $(WIN32_CFLAGS) 67 | WIN32_PIDGIN2_LDFLAGS = -L$(PIDGIN_TREE_TOP)/libpurple $(WIN32_LDFLAGS) 68 | WIN32_PIDGIN3_LDFLAGS = -L$(PIDGIN3_TREE_TOP)/libpurple -L$(WIN32_DEV_TOP)/gplugin-dev/gplugin $(WIN32_LDFLAGS) -lgplugin 69 | 70 | C_FILES := aes.c 71 | PURPLE_COMPAT_FILES := purple2compat/http.c purple2compat/purple-socket.c 72 | PURPLE_C_FILES := libpulsesms.c $(C_FILES) 73 | 74 | AES_FLAGS += -DAES256=1 -DECB=0 -DCTR=0 -DCBC=1 -DMULTIPLY_AS_A_FUNCTION=1 75 | 76 | .PHONY: all install FAILNOPURPLE clean 77 | 78 | all: $(PULSESMS_TARGET) 79 | 80 | libpulsesms.so: $(PURPLE_C_FILES) $(PURPLE_COMPAT_FILES) 81 | $(CC) -fPIC $(AES_FLAGS) $(CFLAGS) -shared -o $@ $^ $(LDFLAGS) `$(PKG_CONFIG) purple glib-2.0 json-glib-1.0 --libs --cflags` $(INCLUDES) -Ipurple2compat -g -ggdb 82 | 83 | libpulsesms3.so: $(PURPLE_C_FILES) 84 | $(CC) -fPIC $(AES_FLAGS) $(CFLAGS) -shared -o $@ $^ $(LDFLAGS) `$(PKG_CONFIG) purple-3 glib-2.0 json-glib-1.0 --libs --cflags` $(INCLUDES) -g -ggdb 85 | 86 | libpulsesms.dll: $(PURPLE_C_FILES) $(PURPLE_COMPAT_FILES) 87 | $(WIN32_CC) $(AES_FLAGS) -shared -o $@ $^ $(WIN32_PIDGIN2_CFLAGS) $(WIN32_PIDGIN2_LDFLAGS) -Ipurple2compat 88 | 89 | libpulsesms3.dll: $(PURPLE_C_FILES) 90 | $(WIN32_CC) $(AES_FLAGS) -shared -o $@ $^ $(WIN32_PIDGIN3_CFLAGS) $(WIN32_PIDGIN3_LDFLAGS) 91 | 92 | install: $(PULSESMS_TARGET) install-icons 93 | mkdir -p $(PULSESMS_DEST) 94 | install -p $(PULSESMS_TARGET) $(PULSESMS_DEST) 95 | 96 | install-icons: pulsesms16.png pulsesms22.png pulsesms48.png 97 | mkdir -p $(PULSESMS_ICONS_DEST)/16 98 | mkdir -p $(PULSESMS_ICONS_DEST)/22 99 | mkdir -p $(PULSESMS_ICONS_DEST)/48 100 | install pulsesms16.png $(PULSESMS_ICONS_DEST)/16/pulsesms.png 101 | install pulsesms22.png $(PULSESMS_ICONS_DEST)/22/pulsesms.png 102 | install pulsesms48.png $(PULSESMS_ICONS_DEST)/48/pulsesms.png 103 | 104 | FAILNOPURPLE: 105 | echo "You need libpurple development headers installed to be able to compile this plugin" 106 | 107 | clean: 108 | rm -f $(PULSESMS_TARGET) 109 | 110 | 111 | installer: purple-pulsesms.nsi libpulsesms.dll 112 | $(MAKENSIS) purple-pulsesms.nsi 113 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PulseSMS Plugin for Pidgin 2 | 3 | This plugin adds support for an additional protocol to Pidgin and libpurple-based instant messenger clients (eg bitlbee, spectrum2, Finch) to connect to the PulseSMS SMS relay app that runs on your Android phone. 4 | 5 | # Requirements # 6 | * An account at https://messenger.klinkerapps.com/overview/index.html 7 | * The [PulseSMS Android app](https://play.google.com/store/apps/details?id=xyz.klinker.messenger&hl=en) 8 | 9 | # Supported Features # 10 | * one-to-one SMS 11 | * image/MMS receiving 12 | 13 | # Currently known issues # 14 | * Messages can take up to 60s to appear in Pidgin due to message polling 15 | * Group SMS/MMS don't display correctly 16 | 17 | # Installation # 18 | ## Linux install ## 19 | Requires devel headers/libs for libpurple and libjson-glib [libglib2.0-dev, libjson-glib-dev and libpurple-dev] 20 | ```bash 21 | git clone git://github.com/EionRobb/purple-pulsesms.git 22 | cd purple-pulsesms 23 | make 24 | sudo make install 25 | ``` 26 | 27 | ## Windows install ## 28 | Download nightly builds of [libpulsesms.dll](https://eion.robbmob.com/libpulsesms.dll) and copy into your C:\Program Files (x86)\Pidgin\plugins\ folder 29 | 30 | -------------------------------------------------------------------------------- /aes.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This is an implementation of the AES algorithm, specifically ECB, CTR and CBC mode. 4 | Block size can be chosen in aes.h - available choices are AES128, AES192, AES256. 5 | 6 | The implementation is verified against the test vectors in: 7 | National Institute of Standards and Technology Special Publication 800-38A 2001 ED 8 | 9 | ECB-AES128 10 | ---------- 11 | 12 | plain-text: 13 | 6bc1bee22e409f96e93d7e117393172a 14 | ae2d8a571e03ac9c9eb76fac45af8e51 15 | 30c81c46a35ce411e5fbc1191a0a52ef 16 | f69f2445df4f9b17ad2b417be66c3710 17 | 18 | key: 19 | 2b7e151628aed2a6abf7158809cf4f3c 20 | 21 | resulting cipher 22 | 3ad77bb40d7a3660a89ecaf32466ef97 23 | f5d3d58503b9699de785895a96fdbaaf 24 | 43b1cd7f598ece23881b00e3ed030688 25 | 7b0c785e27e8ad3f8223207104725dd4 26 | 27 | 28 | NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0) 29 | You should pad the end of the string with zeros if this is not the case. 30 | For AES192/256 the key size is proportionally larger. 31 | 32 | */ 33 | 34 | 35 | /*****************************************************************************/ 36 | /* Includes: */ 37 | /*****************************************************************************/ 38 | #include 39 | #include // CBC mode, for memset 40 | #include "aes.h" 41 | 42 | /*****************************************************************************/ 43 | /* Defines: */ 44 | /*****************************************************************************/ 45 | // The number of columns comprising a state in AES. This is a constant in AES. Value=4 46 | #define Nb 4 47 | 48 | #if defined(AES256) && (AES256 == 1) 49 | #define Nk 8 50 | #define Nr 14 51 | #elif defined(AES192) && (AES192 == 1) 52 | #define Nk 6 53 | #define Nr 12 54 | #else 55 | #define Nk 4 // The number of 32 bit words in a key. 56 | #define Nr 10 // The number of rounds in AES Cipher. 57 | #endif 58 | 59 | // jcallan@github points out that declaring Multiply as a function 60 | // reduces code size considerably with the Keil ARM compiler. 61 | // See this link for more information: https://github.com/kokke/tiny-AES-C/pull/3 62 | #ifndef MULTIPLY_AS_A_FUNCTION 63 | #define MULTIPLY_AS_A_FUNCTION 0 64 | #endif 65 | 66 | 67 | 68 | 69 | /*****************************************************************************/ 70 | /* Private variables: */ 71 | /*****************************************************************************/ 72 | // state - array holding the intermediate results during decryption. 73 | typedef uint8_t state_t[4][4]; 74 | 75 | 76 | 77 | // The lookup-tables are marked const so they can be placed in read-only storage instead of RAM 78 | // The numbers below can be computed dynamically trading ROM for RAM - 79 | // This can be useful in (embedded) bootloader applications, where ROM is often limited. 80 | static const uint8_t sbox[256] = { 81 | //0 1 2 3 4 5 6 7 8 9 A B C D E F 82 | 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 83 | 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 84 | 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 85 | 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 86 | 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 87 | 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 88 | 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 89 | 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 90 | 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 91 | 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 92 | 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 93 | 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 94 | 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 95 | 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 96 | 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 97 | 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; 98 | 99 | static const uint8_t rsbox[256] = { 100 | 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 101 | 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 102 | 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 103 | 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 104 | 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 105 | 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 106 | 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 107 | 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 108 | 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 109 | 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 110 | 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 111 | 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 112 | 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 113 | 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 114 | 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 115 | 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d }; 116 | 117 | // The round constant word array, Rcon[i], contains the values given by 118 | // x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8) 119 | static const uint8_t Rcon[11] = { 120 | 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 }; 121 | 122 | /* 123 | * Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12), 124 | * that you can remove most of the elements in the Rcon array, because they are unused. 125 | * 126 | * From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon 127 | * 128 | * "Only the first some of these constants are actually used – up to rcon[10] for AES-128 (as 11 round keys are needed), 129 | * up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm." 130 | */ 131 | 132 | 133 | /*****************************************************************************/ 134 | /* Private functions: */ 135 | /*****************************************************************************/ 136 | /* 137 | static uint8_t getSBoxValue(uint8_t num) 138 | { 139 | return sbox[num]; 140 | } 141 | */ 142 | #define getSBoxValue(num) (sbox[(num)]) 143 | /* 144 | static uint8_t getSBoxInvert(uint8_t num) 145 | { 146 | return rsbox[num]; 147 | } 148 | */ 149 | #define getSBoxInvert(num) (rsbox[(num)]) 150 | 151 | // This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states. 152 | static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key) 153 | { 154 | unsigned i, j, k; 155 | uint8_t tempa[4]; // Used for the column/row operations 156 | 157 | // The first round key is the key itself. 158 | for (i = 0; i < Nk; ++i) 159 | { 160 | RoundKey[(i * 4) + 0] = Key[(i * 4) + 0]; 161 | RoundKey[(i * 4) + 1] = Key[(i * 4) + 1]; 162 | RoundKey[(i * 4) + 2] = Key[(i * 4) + 2]; 163 | RoundKey[(i * 4) + 3] = Key[(i * 4) + 3]; 164 | } 165 | 166 | // All other round keys are found from the previous round keys. 167 | for (i = Nk; i < Nb * (Nr + 1); ++i) 168 | { 169 | { 170 | k = (i - 1) * 4; 171 | tempa[0]=RoundKey[k + 0]; 172 | tempa[1]=RoundKey[k + 1]; 173 | tempa[2]=RoundKey[k + 2]; 174 | tempa[3]=RoundKey[k + 3]; 175 | 176 | } 177 | 178 | if (i % Nk == 0) 179 | { 180 | // This function shifts the 4 bytes in a word to the left once. 181 | // [a0,a1,a2,a3] becomes [a1,a2,a3,a0] 182 | 183 | // Function RotWord() 184 | { 185 | k = tempa[0]; 186 | tempa[0] = tempa[1]; 187 | tempa[1] = tempa[2]; 188 | tempa[2] = tempa[3]; 189 | tempa[3] = k; 190 | } 191 | 192 | // SubWord() is a function that takes a four-byte input word and 193 | // applies the S-box to each of the four bytes to produce an output word. 194 | 195 | // Function Subword() 196 | { 197 | tempa[0] = getSBoxValue(tempa[0]); 198 | tempa[1] = getSBoxValue(tempa[1]); 199 | tempa[2] = getSBoxValue(tempa[2]); 200 | tempa[3] = getSBoxValue(tempa[3]); 201 | } 202 | 203 | tempa[0] = tempa[0] ^ Rcon[i/Nk]; 204 | } 205 | #if defined(AES256) && (AES256 == 1) 206 | if (i % Nk == 4) 207 | { 208 | // Function Subword() 209 | { 210 | tempa[0] = getSBoxValue(tempa[0]); 211 | tempa[1] = getSBoxValue(tempa[1]); 212 | tempa[2] = getSBoxValue(tempa[2]); 213 | tempa[3] = getSBoxValue(tempa[3]); 214 | } 215 | } 216 | #endif 217 | j = i * 4; k=(i - Nk) * 4; 218 | RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0]; 219 | RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1]; 220 | RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2]; 221 | RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3]; 222 | } 223 | } 224 | 225 | void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key) 226 | { 227 | KeyExpansion(ctx->RoundKey, key); 228 | } 229 | #if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) 230 | void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv) 231 | { 232 | KeyExpansion(ctx->RoundKey, key); 233 | memcpy (ctx->Iv, iv, AES_BLOCKLEN); 234 | } 235 | void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv) 236 | { 237 | memcpy (ctx->Iv, iv, AES_BLOCKLEN); 238 | } 239 | #endif 240 | 241 | // This function adds the round key to state. 242 | // The round key is added to the state by an XOR function. 243 | static void AddRoundKey(uint8_t round,state_t* state,uint8_t* RoundKey) 244 | { 245 | uint8_t i,j; 246 | for (i = 0; i < 4; ++i) 247 | { 248 | for (j = 0; j < 4; ++j) 249 | { 250 | (*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j]; 251 | } 252 | } 253 | } 254 | 255 | // The SubBytes Function Substitutes the values in the 256 | // state matrix with values in an S-box. 257 | static void SubBytes(state_t* state) 258 | { 259 | uint8_t i, j; 260 | for (i = 0; i < 4; ++i) 261 | { 262 | for (j = 0; j < 4; ++j) 263 | { 264 | (*state)[j][i] = getSBoxValue((*state)[j][i]); 265 | } 266 | } 267 | } 268 | 269 | // The ShiftRows() function shifts the rows in the state to the left. 270 | // Each row is shifted with different offset. 271 | // Offset = Row number. So the first row is not shifted. 272 | static void ShiftRows(state_t* state) 273 | { 274 | uint8_t temp; 275 | 276 | // Rotate first row 1 columns to left 277 | temp = (*state)[0][1]; 278 | (*state)[0][1] = (*state)[1][1]; 279 | (*state)[1][1] = (*state)[2][1]; 280 | (*state)[2][1] = (*state)[3][1]; 281 | (*state)[3][1] = temp; 282 | 283 | // Rotate second row 2 columns to left 284 | temp = (*state)[0][2]; 285 | (*state)[0][2] = (*state)[2][2]; 286 | (*state)[2][2] = temp; 287 | 288 | temp = (*state)[1][2]; 289 | (*state)[1][2] = (*state)[3][2]; 290 | (*state)[3][2] = temp; 291 | 292 | // Rotate third row 3 columns to left 293 | temp = (*state)[0][3]; 294 | (*state)[0][3] = (*state)[3][3]; 295 | (*state)[3][3] = (*state)[2][3]; 296 | (*state)[2][3] = (*state)[1][3]; 297 | (*state)[1][3] = temp; 298 | } 299 | 300 | static uint8_t xtime(uint8_t x) 301 | { 302 | return ((x<<1) ^ (((x>>7) & 1) * 0x1b)); 303 | } 304 | 305 | // MixColumns function mixes the columns of the state matrix 306 | static void MixColumns(state_t* state) 307 | { 308 | uint8_t i; 309 | uint8_t Tmp, Tm, t; 310 | for (i = 0; i < 4; ++i) 311 | { 312 | t = (*state)[i][0]; 313 | Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ; 314 | Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ; 315 | Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ; 316 | Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ; 317 | Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ; 318 | } 319 | } 320 | 321 | // Multiply is used to multiply numbers in the field GF(2^8) 322 | #if MULTIPLY_AS_A_FUNCTION 323 | static uint8_t Multiply(uint8_t x, uint8_t y) 324 | { 325 | return (((y & 1) * x) ^ 326 | ((y>>1 & 1) * xtime(x)) ^ 327 | ((y>>2 & 1) * xtime(xtime(x))) ^ 328 | ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ 329 | ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))); 330 | } 331 | #else 332 | #define Multiply(x, y) \ 333 | ( ((y & 1) * x) ^ \ 334 | ((y>>1 & 1) * xtime(x)) ^ \ 335 | ((y>>2 & 1) * xtime(xtime(x))) ^ \ 336 | ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \ 337 | ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \ 338 | 339 | #endif 340 | 341 | // MixColumns function mixes the columns of the state matrix. 342 | // The method used to multiply may be difficult to understand for the inexperienced. 343 | // Please use the references to gain more information. 344 | static void InvMixColumns(state_t* state) 345 | { 346 | int i; 347 | uint8_t a, b, c, d; 348 | for (i = 0; i < 4; ++i) 349 | { 350 | a = (*state)[i][0]; 351 | b = (*state)[i][1]; 352 | c = (*state)[i][2]; 353 | d = (*state)[i][3]; 354 | 355 | (*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09); 356 | (*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d); 357 | (*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b); 358 | (*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e); 359 | } 360 | } 361 | 362 | 363 | // The SubBytes Function Substitutes the values in the 364 | // state matrix with values in an S-box. 365 | static void InvSubBytes(state_t* state) 366 | { 367 | uint8_t i, j; 368 | for (i = 0; i < 4; ++i) 369 | { 370 | for (j = 0; j < 4; ++j) 371 | { 372 | (*state)[j][i] = getSBoxInvert((*state)[j][i]); 373 | } 374 | } 375 | } 376 | 377 | static void InvShiftRows(state_t* state) 378 | { 379 | uint8_t temp; 380 | 381 | // Rotate first row 1 columns to right 382 | temp = (*state)[3][1]; 383 | (*state)[3][1] = (*state)[2][1]; 384 | (*state)[2][1] = (*state)[1][1]; 385 | (*state)[1][1] = (*state)[0][1]; 386 | (*state)[0][1] = temp; 387 | 388 | // Rotate second row 2 columns to right 389 | temp = (*state)[0][2]; 390 | (*state)[0][2] = (*state)[2][2]; 391 | (*state)[2][2] = temp; 392 | 393 | temp = (*state)[1][2]; 394 | (*state)[1][2] = (*state)[3][2]; 395 | (*state)[3][2] = temp; 396 | 397 | // Rotate third row 3 columns to right 398 | temp = (*state)[0][3]; 399 | (*state)[0][3] = (*state)[1][3]; 400 | (*state)[1][3] = (*state)[2][3]; 401 | (*state)[2][3] = (*state)[3][3]; 402 | (*state)[3][3] = temp; 403 | } 404 | 405 | 406 | // Cipher is the main function that encrypts the PlainText. 407 | static void Cipher(state_t* state, uint8_t* RoundKey) 408 | { 409 | uint8_t round = 0; 410 | 411 | // Add the First round key to the state before starting the rounds. 412 | AddRoundKey(0, state, RoundKey); 413 | 414 | // There will be Nr rounds. 415 | // The first Nr-1 rounds are identical. 416 | // These Nr-1 rounds are executed in the loop below. 417 | for (round = 1; round < Nr; ++round) 418 | { 419 | SubBytes(state); 420 | ShiftRows(state); 421 | MixColumns(state); 422 | AddRoundKey(round, state, RoundKey); 423 | } 424 | 425 | // The last round is given below. 426 | // The MixColumns function is not here in the last round. 427 | SubBytes(state); 428 | ShiftRows(state); 429 | AddRoundKey(Nr, state, RoundKey); 430 | } 431 | 432 | static void InvCipher(state_t* state,uint8_t* RoundKey) 433 | { 434 | uint8_t round = 0; 435 | 436 | // Add the First round key to the state before starting the rounds. 437 | AddRoundKey(Nr, state, RoundKey); 438 | 439 | // There will be Nr rounds. 440 | // The first Nr-1 rounds are identical. 441 | // These Nr-1 rounds are executed in the loop below. 442 | for (round = (Nr - 1); round > 0; --round) 443 | { 444 | InvShiftRows(state); 445 | InvSubBytes(state); 446 | AddRoundKey(round, state, RoundKey); 447 | InvMixColumns(state); 448 | } 449 | 450 | // The last round is given below. 451 | // The MixColumns function is not here in the last round. 452 | InvShiftRows(state); 453 | InvSubBytes(state); 454 | AddRoundKey(0, state, RoundKey); 455 | } 456 | 457 | 458 | /*****************************************************************************/ 459 | /* Public functions: */ 460 | /*****************************************************************************/ 461 | #if defined(ECB) && (ECB == 1) 462 | 463 | 464 | void AES_ECB_encrypt(struct AES_ctx *ctx,const uint8_t* buf) 465 | { 466 | // The next function call encrypts the PlainText with the Key using AES algorithm. 467 | Cipher((state_t*)buf, ctx->RoundKey); 468 | } 469 | 470 | void AES_ECB_decrypt(struct AES_ctx* ctx,const uint8_t* buf) 471 | { 472 | // The next function call decrypts the PlainText with the Key using AES algorithm. 473 | InvCipher((state_t*)buf, ctx->RoundKey); 474 | } 475 | 476 | 477 | #endif // #if defined(ECB) && (ECB == 1) 478 | 479 | 480 | 481 | 482 | 483 | #if defined(CBC) && (CBC == 1) 484 | 485 | 486 | static void XorWithIv(uint8_t* buf, uint8_t* Iv) 487 | { 488 | uint8_t i; 489 | for (i = 0; i < AES_BLOCKLEN; ++i) // The block in AES is always 128bit no matter the key size 490 | { 491 | buf[i] ^= Iv[i]; 492 | } 493 | } 494 | 495 | void AES_CBC_encrypt_buffer(struct AES_ctx *ctx,uint8_t* buf, uint32_t length) 496 | { 497 | uintptr_t i; 498 | uint8_t *Iv = ctx->Iv; 499 | for (i = 0; i < length; i += AES_BLOCKLEN) 500 | { 501 | XorWithIv(buf, Iv); 502 | Cipher((state_t*)buf, ctx->RoundKey); 503 | Iv = buf; 504 | buf += AES_BLOCKLEN; 505 | //printf("Step %d - %d", i/16, i); 506 | } 507 | /* store Iv in ctx for next call */ 508 | memcpy(ctx->Iv, Iv, AES_BLOCKLEN); 509 | } 510 | 511 | void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length) 512 | { 513 | uintptr_t i; 514 | uint8_t storeNextIv[AES_BLOCKLEN]; 515 | for (i = 0; i < length; i += AES_BLOCKLEN) 516 | { 517 | memcpy(storeNextIv, buf, AES_BLOCKLEN); 518 | InvCipher((state_t*)buf, ctx->RoundKey); 519 | XorWithIv(buf, ctx->Iv); 520 | memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN); 521 | buf += AES_BLOCKLEN; 522 | } 523 | 524 | } 525 | 526 | #endif // #if defined(CBC) && (CBC == 1) 527 | 528 | 529 | 530 | #if defined(CTR) && (CTR == 1) 531 | 532 | /* Symmetrical operation: same function for encrypting as for decrypting. Note any IV/nonce should never be reused with the same key */ 533 | void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length) 534 | { 535 | uint8_t buffer[AES_BLOCKLEN]; 536 | 537 | unsigned i; 538 | int bi; 539 | for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi) 540 | { 541 | if (bi == AES_BLOCKLEN) /* we need to regen xor compliment in buffer */ 542 | { 543 | 544 | memcpy(buffer, ctx->Iv, AES_BLOCKLEN); 545 | Cipher((state_t*)buffer,ctx->RoundKey); 546 | 547 | /* Increment Iv and handle overflow */ 548 | for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi) 549 | { 550 | /* inc will owerflow */ 551 | if (ctx->Iv[bi] == 255) 552 | { 553 | ctx->Iv[bi] = 0; 554 | continue; 555 | } 556 | ctx->Iv[bi] += 1; 557 | break; 558 | } 559 | bi = 0; 560 | } 561 | 562 | buf[i] = (buf[i] ^ buffer[bi]); 563 | } 564 | } 565 | 566 | #endif // #if defined(CTR) && (CTR == 1) 567 | 568 | -------------------------------------------------------------------------------- /aes.h: -------------------------------------------------------------------------------- 1 | #ifndef _AES_H_ 2 | #define _AES_H_ 3 | 4 | #include 5 | 6 | // #define the macros below to 1/0 to enable/disable the mode of operation. 7 | // 8 | // CBC enables AES encryption in CBC-mode of operation. 9 | // CTR enables encryption in counter-mode. 10 | // ECB enables the basic ECB 16-byte block algorithm. All can be enabled simultaneously. 11 | 12 | // The #ifndef-guard allows it to be configured before #include'ing or at compile time. 13 | #ifndef CBC 14 | #define CBC 1 15 | #endif 16 | 17 | #ifndef ECB 18 | #define ECB 1 19 | #endif 20 | 21 | #ifndef CTR 22 | #define CTR 1 23 | #endif 24 | 25 | 26 | #define AES128 1 27 | //#define AES192 1 28 | //#define AES256 1 29 | 30 | #define AES_BLOCKLEN 16 //Block length in bytes AES is 128b block only 31 | 32 | #if defined(AES256) && (AES256 == 1) 33 | #define AES_KEYLEN 32 34 | #define AES_keyExpSize 240 35 | #elif defined(AES192) && (AES192 == 1) 36 | #define AES_KEYLEN 24 37 | #define AES_keyExpSize 208 38 | #else 39 | #define AES_KEYLEN 16 // Key length in bytes 40 | #define AES_keyExpSize 176 41 | #endif 42 | 43 | struct AES_ctx 44 | { 45 | uint8_t RoundKey[AES_keyExpSize]; 46 | #if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) 47 | uint8_t Iv[AES_BLOCKLEN]; 48 | #endif 49 | }; 50 | 51 | void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key); 52 | #if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) 53 | void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv); 54 | void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv); 55 | #endif 56 | 57 | #if defined(ECB) && (ECB == 1) 58 | // buffer size is exactly AES_BLOCKLEN bytes; 59 | // you need only AES_init_ctx as IV is not used in ECB 60 | // NB: ECB is considered insecure for most uses 61 | void AES_ECB_encrypt(struct AES_ctx* ctx, const uint8_t* buf); 62 | void AES_ECB_decrypt(struct AES_ctx* ctx, const uint8_t* buf); 63 | 64 | #endif // #if defined(ECB) && (ECB == !) 65 | 66 | 67 | #if defined(CBC) && (CBC == 1) 68 | // buffer size MUST be mutile of AES_BLOCKLEN; 69 | // Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme 70 | // NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv() 71 | // no IV should ever be reused with the same key 72 | void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length); 73 | void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length); 74 | 75 | #endif // #if defined(CBC) && (CBC == 1) 76 | 77 | 78 | #if defined(CTR) && (CTR == 1) 79 | 80 | // Same function for encrypting as for decrypting. 81 | // IV is incremented for every block, and used after encryption as XOR-compliment for output 82 | // Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme 83 | // NOTES: you need to set IV in ctx with AES_init_ctx_iv() or AES_ctx_set_iv() 84 | // no IV should ever be reused with the same key 85 | void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length); 86 | 87 | #endif // #if defined(CTR) && (CTR == 1) 88 | 89 | 90 | #endif //_AES_H_ 91 | -------------------------------------------------------------------------------- /gc-pbkdf2-sha1.c: -------------------------------------------------------------------------------- 1 | /* gc-pbkdf2-sha1.c --- Password-Based Key Derivation Function a'la PKCS#5 2 | Copyright (C) 2002-2006, 2009-2016 Free Software Foundation, Inc. 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, or (at your option) 7 | 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 | /* Written by Simon Josefsson. */ 18 | 19 | 20 | #include 21 | #include 22 | 23 | /* Implement PKCS#5 PBKDF2 as per RFC 2898. The PRF to use is hard 24 | coded to be HMAC-SHA1. Inputs are the password P of length PLEN, 25 | the salt S of length SLEN, the iteration counter C (> 0), and the 26 | desired derived output length DKLEN. Output buffer is DK which 27 | must have room for at least DKLEN octets. The output buffer will 28 | be filled with the derived data. */ 29 | int 30 | gc_pbkdf2_sha1 (const char *P, size_t Plen, 31 | const char *S, size_t Slen, 32 | unsigned int c, 33 | char *DK, size_t dkLen) 34 | { 35 | unsigned int hLen = 20; 36 | char U[20]; 37 | char T[20]; 38 | unsigned int u; 39 | unsigned int l; 40 | unsigned int r; 41 | unsigned int i; 42 | unsigned int k; 43 | int rc; 44 | char *tmp; 45 | size_t tmplen = Slen + 4; 46 | 47 | if (c == 0) 48 | return -1; 49 | 50 | if (dkLen == 0) 51 | return -2; 52 | 53 | if (dkLen > 4294967295U) 54 | return -3; 55 | 56 | l = ((dkLen - 1) / hLen) + 1; 57 | r = dkLen - (l - 1) * hLen; 58 | 59 | tmp = malloc (tmplen); 60 | if (tmp == NULL) 61 | return -4; 62 | 63 | memcpy (tmp, S, Slen); 64 | 65 | for (i = 1; i <= l; i++) 66 | { 67 | memset (T, 0, hLen); 68 | 69 | for (u = 1; u <= c; u++) 70 | { 71 | if (u == 1) 72 | { 73 | tmp[Slen + 0] = (i & 0xff000000) >> 24; 74 | tmp[Slen + 1] = (i & 0x00ff0000) >> 16; 75 | tmp[Slen + 2] = (i & 0x0000ff00) >> 8; 76 | tmp[Slen + 3] = (i & 0x000000ff) >> 0; 77 | 78 | rc = gc_hmac_sha1 (P, Plen, tmp, tmplen, U); 79 | } 80 | else 81 | rc = gc_hmac_sha1 (P, Plen, U, hLen, U); 82 | 83 | if (rc != 1) 84 | { 85 | free (tmp); 86 | return rc; 87 | } 88 | 89 | for (k = 0; k < hLen; k++) 90 | T[k] ^= U[k]; 91 | } 92 | 93 | memcpy (DK + (i - 1) * hLen, T, i == l ? r : hLen); 94 | } 95 | 96 | free (tmp); 97 | 98 | return 1; 99 | } 100 | -------------------------------------------------------------------------------- /glibcompat.h: -------------------------------------------------------------------------------- 1 | #ifndef _GLIBCOMPAT_H_ 2 | #define _GLIBCOMPAT_H_ 3 | 4 | #if !GLIB_CHECK_VERSION(2, 32, 0) 5 | #define g_hash_table_contains(hash_table, key) g_hash_table_lookup_extended(hash_table, key, NULL, NULL) 6 | #endif /* 2.32.0 */ 7 | 8 | 9 | #if !GLIB_CHECK_VERSION(2, 28, 0) 10 | gint64 11 | g_get_real_time() 12 | { 13 | GTimeVal val; 14 | 15 | g_get_current_time (&val); 16 | 17 | return (((gint64) val.tv_sec) * 1000000) + val.tv_usec; 18 | } 19 | #endif /* 2.28.0 */ 20 | 21 | #endif /*_GLIBCOMPAT_H_*/ -------------------------------------------------------------------------------- /libjson-glib-1.0.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EionRobb/purple-pulsesms/b7991fa92bf67bc08eb01e124bc90431541ab688/libjson-glib-1.0.dll -------------------------------------------------------------------------------- /libpulsesms.c: -------------------------------------------------------------------------------- 1 | /* 2 | * PulseSMS Plugin for libpurple/Pidgin 3 | * Copyright (c) 2015-2016 Eion Robb, Mike Ruprecht 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | 20 | #define PULSESMS_PLUGIN_ID "prpl-eionrobb-pulsesms" 21 | #define PULSESMS_PLUGIN_VERSION "0.1" 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #if !GLIB_CHECK_VERSION(2, 32, 0) 30 | #define g_hash_table_contains(hash_table, key) g_hash_table_lookup_extended(hash_table, key, NULL, NULL) 31 | #endif /* 2.32.0 */ 32 | 33 | #include 34 | 35 | #include "purplecompat.h" 36 | 37 | #include 38 | 39 | // AES library from https://github.com/kokke/tiny-AES-c 40 | #include "aes.h" 41 | 42 | // Use purple's hmac-sha1 impl 43 | int gc_hmac_sha1(const void *key, size_t keylen, const void *in, size_t inlen, void *resbuf); 44 | 45 | // From https://github.com/gagern/gnulib/blob/master/lib/gc-pbkdf2-sha1.c 46 | #include "gc-pbkdf2-sha1.c" 47 | 48 | typedef struct { 49 | PurpleAccount *account; 50 | PurpleConnection *pc; 51 | 52 | PurpleHttpKeepalivePool *keepalive_pool; 53 | GHashTable *sent_message_ids;// A store of message id's that we generated from this instance 54 | 55 | struct AES_ctx* ctx; 56 | guint conv_fetch_timeout; 57 | gint64 last_conv_timestamp; 58 | 59 | GHashTable *im_conversations; // conv_id -> phone number 60 | GHashTable *im_conversations_rev; // phone# -> conv_id 61 | 62 | GHashTable *normalised_phone_lookup; // phone# -> id 63 | GHashTable *normalised_id_lookup; // id -> phone# 64 | } PulseSMSAccount; 65 | 66 | 67 | #ifdef ENABLE_NLS 68 | # define GETTEXT_PACKAGE "purple-pulsesms" 69 | # include 70 | # ifdef _WIN32 71 | # ifdef LOCALEDIR 72 | # unset LOCALEDIR 73 | # endif 74 | # define LOCALEDIR wpurple_locale_dir() 75 | # endif 76 | #else 77 | # define _(a) (a) 78 | # define N_(a) (a) 79 | #endif 80 | 81 | #define PULSESMS_API_HOST "https://api.messenger.klinkerapps.com" 82 | 83 | 84 | static void pulsesms_create_ctx(PulseSMSAccount *psa); 85 | 86 | /*****************************************************************************/ 87 | 88 | static gchar * 89 | pulsesms_decrypt_len(PulseSMSAccount *psa, const gchar *data, gsize *len) 90 | { 91 | if (data == NULL) { 92 | return NULL; 93 | } 94 | 95 | gchar **parts = g_strsplit(data, "-:-", 2); 96 | purple_str_strip_char(parts[0], '\n'); 97 | purple_str_strip_char(parts[1], '\n'); 98 | 99 | gsize text_len, iv_len; 100 | guchar *ciphertext = g_base64_decode(parts[1], &text_len); 101 | guchar *IV = g_base64_decode(parts[0], &iv_len); 102 | 103 | if (!ciphertext || !IV) { 104 | purple_debug_error("pulsesms", "Error, unable to base64 decode %s\n", data); 105 | g_free(ciphertext); 106 | g_free(IV); 107 | return NULL; 108 | } 109 | 110 | gsize buf_len = text_len + AES_BLOCKLEN - (text_len % AES_BLOCKLEN); 111 | 112 | guchar *buf = g_new0(guchar, buf_len); 113 | 114 | memcpy(buf, ciphertext, text_len); 115 | //XXX: does this need to be PKCS#7 padded? 116 | 117 | AES_ctx_set_iv(psa->ctx, IV); 118 | AES_CBC_decrypt_buffer(psa->ctx, buf, text_len); 119 | 120 | g_free(ciphertext); 121 | g_free(IV); 122 | g_strfreev(parts); 123 | 124 | //strip PKCS#5 padding 125 | buf[text_len - buf[text_len - 1]] = '\0'; 126 | 127 | if (len != NULL) { 128 | *len = text_len; 129 | } 130 | 131 | return (gchar *) buf; 132 | } 133 | 134 | 135 | static gchar * 136 | pulsesms_decrypt(PulseSMSAccount *psa, const gchar *data) 137 | { 138 | return pulsesms_decrypt_len(psa, data, NULL); 139 | } 140 | 141 | // A reimplementation of gnulib's function, but using purple/glib functions 142 | int 143 | gc_hmac_sha1(const void *key, size_t keylen, const void *in, size_t inlen, void *resbuf) 144 | { 145 | #if PURPLE_VERSION_CHECK(3, 0, 0) 146 | GHmac *hmac; 147 | 148 | hmac = g_hmac_new(G_CHECKSUM_SHA1, key, keylen); 149 | g_hmac_update(hmac, in, inlen); 150 | g_hmac_get_digest(hmac, resbuf, 20); 151 | g_hmac_unref(hmac); 152 | 153 | #else 154 | PurpleCipherContext *hmac; 155 | 156 | hmac = purple_cipher_context_new_by_name("hmac", NULL); 157 | 158 | purple_cipher_context_set_option(hmac, "hash", "sha1"); 159 | purple_cipher_context_set_key_with_len(hmac, (guchar *)key, keylen); 160 | purple_cipher_context_append(hmac, (guchar *)in, inlen); 161 | purple_cipher_context_digest(hmac, 20, resbuf, NULL); 162 | purple_cipher_context_destroy(hmac); 163 | 164 | #endif 165 | 166 | return 1; 167 | } 168 | 169 | JsonNode * 170 | json_decode(const gchar *data, gssize len) 171 | { 172 | JsonParser *parser = json_parser_new(); 173 | JsonNode *root = NULL; 174 | 175 | if (!data || !json_parser_load_from_data(parser, data, len, NULL)) 176 | { 177 | purple_debug_error("pulsesms", "Error parsing JSON: %s\n", data); 178 | } else { 179 | root = json_parser_get_root(parser); 180 | if (root != NULL) { 181 | root = json_node_copy(root); 182 | } 183 | } 184 | g_object_unref(parser); 185 | 186 | return root; 187 | } 188 | 189 | JsonArray * 190 | json_decode_array(const gchar *data, gssize len) 191 | { 192 | JsonNode *root = json_decode(data, len); 193 | JsonArray *ret; 194 | 195 | g_return_val_if_fail(root, NULL); 196 | 197 | if (!JSON_NODE_HOLDS_ARRAY(root)) { 198 | // That ain't my belly button! 199 | json_node_free(root); 200 | return NULL; 201 | } 202 | 203 | ret = json_node_dup_array(root); 204 | 205 | json_node_free(root); 206 | 207 | return ret; 208 | } 209 | 210 | JsonObject * 211 | json_decode_object(const gchar *data, gssize len) 212 | { 213 | JsonNode *root = json_decode(data, len); 214 | JsonObject *ret; 215 | 216 | g_return_val_if_fail(root, NULL); 217 | 218 | if (!JSON_NODE_HOLDS_OBJECT(root)) { 219 | // That ain't my thumb, neither! 220 | json_node_free(root); 221 | return NULL; 222 | } 223 | 224 | ret = json_node_dup_object(root); 225 | 226 | json_node_free(root); 227 | 228 | return ret; 229 | } 230 | 231 | 232 | /*****************************************************************************/ 233 | 234 | static const char * 235 | pulsesms_normalize(const PurpleAccount *account, const char *who) 236 | { 237 | PurpleConnection *pc = purple_account_get_connection(account); 238 | PulseSMSAccount *psa = pc ? purple_connection_get_protocol_data(pc) : NULL; 239 | 240 | // if (who[0] == '+') { 241 | // return who; 242 | // } 243 | 244 | if (!pc || !psa) { 245 | return who; 246 | } 247 | 248 | const gchar *normalised_id = g_hash_table_lookup(psa->normalised_phone_lookup, who); 249 | if (normalised_id) { 250 | const gchar *normalised_phone = g_hash_table_lookup(psa->normalised_id_lookup, normalised_id); 251 | if (normalised_phone) { 252 | return normalised_phone; 253 | } 254 | } 255 | 256 | return who; 257 | } 258 | 259 | static int 260 | pulsesms_send_im(PurpleConnection *pc, 261 | #if PURPLE_VERSION_CHECK(3, 0, 0) 262 | PurpleMessage *msg) 263 | { 264 | const gchar *who = purple_message_get_recipient(msg); 265 | const gchar *message = purple_message_get_contents(msg); 266 | #else 267 | const gchar *who, const gchar *message, PurpleMessageFlags flags) 268 | { 269 | #endif 270 | GString *postbody; 271 | PulseSMSAccount *psa = purple_connection_get_protocol_data(pc); 272 | gchar *stripped_message; 273 | 274 | PurpleHttpRequest *request = purple_http_request_new(PULSESMS_API_HOST "/api/v1/messages/forward_to_phone"); 275 | purple_http_request_set_keepalive_pool(request, psa->keepalive_pool); 276 | 277 | purple_http_request_set_method(request, "POST"); 278 | purple_http_request_header_set(request, "Content-type", "application/x-www-form-urlencoded; charset=UTF-8"); 279 | 280 | stripped_message = g_strstrip(purple_markup_strip_html(message)); 281 | 282 | postbody = g_string_new(NULL); 283 | g_string_append_printf(postbody, "account_id=%s&", purple_url_encode(purple_account_get_string(psa->account, "account_id", ""))); 284 | g_string_append_printf(postbody, "to=%s&", purple_url_encode(who)); 285 | g_string_append_printf(postbody, "message=%s&", purple_url_encode(stripped_message)); 286 | g_string_append_printf(postbody, "sent_device=3&"); //Native client 287 | purple_http_request_set_contents(request, postbody->str, postbody->len); 288 | g_string_free(postbody, TRUE); 289 | 290 | purple_http_request(psa->pc, request, NULL, NULL); 291 | purple_http_request_unref(request); 292 | 293 | g_free(stripped_message); 294 | return 1; 295 | } 296 | 297 | static void 298 | pulsesms_got_contacts(PurpleHttpConnection *http_conn, PurpleHttpResponse *response, gpointer user_data) 299 | { 300 | PulseSMSAccount *psa = user_data; 301 | gsize len; 302 | const gchar *data = purple_http_response_get_data(response, &len); 303 | JsonArray *contacts = json_decode_array(data, len); 304 | int i; 305 | 306 | PurpleGroup *group = purple_blist_find_group("PulseSMS"); 307 | 308 | if (!group) { 309 | group = purple_group_new("PulseSMS"); 310 | purple_blist_add_group(group, NULL); 311 | } 312 | 313 | for (i = json_array_get_length(contacts) - 1; i >= 0; i--) { 314 | JsonObject *contact = json_array_get_object_element(contacts, i); 315 | 316 | gchar *phone_number = pulsesms_decrypt(psa, json_object_get_string_member(contact, "phone_number")); 317 | gchar *name = pulsesms_decrypt(psa, json_object_get_string_member(contact, "name")); 318 | gchar *id_matcher = pulsesms_decrypt(psa, json_object_get_string_member(contact, "id_matcher")); 319 | 320 | if (!phone_number) { 321 | g_free(name); 322 | g_free(id_matcher); 323 | continue; 324 | } 325 | 326 | purple_debug_info("pulsesms", "phone_number: %s, name: %s, id_matcher: %s\n", phone_number, name, id_matcher); 327 | 328 | //TODO use this to join contacts together with their international number equivalents 329 | g_hash_table_insert(psa->normalised_phone_lookup, g_strdup(phone_number), g_strdup(id_matcher)); 330 | if (phone_number[0] == '+' || purple_strequal(id_matcher, phone_number)) { 331 | g_hash_table_insert(psa->normalised_id_lookup, g_strdup(id_matcher), g_strdup(phone_number)); 332 | } 333 | 334 | PurpleBuddy *buddy = purple_blist_find_buddy(psa->account, phone_number); 335 | 336 | if (buddy == NULL) { 337 | buddy = purple_buddy_new(psa->account, phone_number, name); 338 | purple_blist_add_buddy(buddy, NULL, group, NULL); 339 | } 340 | 341 | purple_protocol_got_user_status(psa->account, phone_number, "mobile", NULL); 342 | 343 | g_free(phone_number); 344 | g_free(name); 345 | g_free(id_matcher); 346 | } 347 | 348 | json_array_unref(contacts); 349 | } 350 | 351 | static void 352 | pulsesms_fetch_contacts(PulseSMSAccount *psa) 353 | { 354 | const gchar *account_id = purple_account_get_string(psa->account, "account_id", NULL); 355 | 356 | PurpleHttpRequest *request = purple_http_request_new(NULL); 357 | purple_http_request_set_keepalive_pool(request, psa->keepalive_pool); 358 | 359 | purple_http_request_set_url_printf(request, PULSESMS_API_HOST "/api/v1/contacts/simple?account_id=%s", purple_url_encode(account_id)); 360 | 361 | purple_http_request(psa->pc, request, pulsesms_got_contacts, psa); 362 | purple_http_request_unref(request); 363 | } 364 | 365 | static void 366 | pulsesms_got_http_image_for_conv(PurpleHttpConnection *http_conn, PurpleHttpResponse *response, gpointer user_data) 367 | { 368 | PulseSMSAccount *psa = user_data; 369 | PurpleHttpRequest *request = purple_http_conn_get_request(http_conn); 370 | const gchar *phone_number = g_dataset_get_data(request, "phone_number"); 371 | gint message_type = GPOINTER_TO_INT(g_dataset_get_data(request, "message_type")); 372 | gint timestamp = GPOINTER_TO_INT(g_dataset_get_data(request, "timestamp")); 373 | gsize len; 374 | const gchar *data = purple_http_response_get_data(response, &len); 375 | PurpleImage *image; 376 | guint image_id; 377 | gchar *image_message; 378 | 379 | if (purple_http_response_get_error(response) != NULL) { 380 | g_dataset_destroy(request); 381 | return; 382 | } 383 | 384 | gsize image_len; 385 | gchar *image_data = pulsesms_decrypt_len(psa, data, &image_len); 386 | 387 | image = purple_image_new_from_data(image_data, image_len); 388 | image_id = purple_image_store_add(image); 389 | image_message = g_strdup_printf("", image_id); 390 | 391 | if (message_type == 0) { 392 | purple_serv_got_im(psa->pc, phone_number, image_message, PURPLE_MESSAGE_RECV | PURPLE_MESSAGE_IMAGES, timestamp); 393 | 394 | } else if (message_type == 1 || message_type == 2) { 395 | PurpleConversation *conv; 396 | PurpleIMConversation *imconv; 397 | PurpleMessage *msg; 398 | 399 | imconv = purple_conversations_find_im_with_account(phone_number, psa->account); 400 | 401 | if (imconv == NULL) { 402 | imconv = purple_im_conversation_new(psa->account, phone_number); 403 | } 404 | 405 | conv = PURPLE_CONVERSATION(imconv); 406 | 407 | msg = purple_message_new_outgoing(phone_number, image_message, PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_REMOTE_SEND | PURPLE_MESSAGE_DELAYED | PURPLE_MESSAGE_IMAGES); 408 | purple_message_set_time(msg, timestamp); 409 | purple_conversation_write_message(conv, msg); 410 | purple_message_destroy(msg); 411 | } 412 | 413 | g_free(image_message); 414 | g_dataset_destroy(request); 415 | } 416 | 417 | static void 418 | pulsesms_got_conversation_history(PurpleHttpConnection *http_conn, PurpleHttpResponse *response, gpointer user_data) 419 | { 420 | PulseSMSAccount *psa = user_data; 421 | PurpleHttpRequest *request = purple_http_conn_get_request(http_conn); 422 | gchar *conv_id_str = g_dataset_get_data(request, "conv_id"); 423 | gchar *since_str = g_dataset_get_data(request, "since"); 424 | gsize len; 425 | gint i; 426 | const gchar *data = purple_http_response_get_data(response, &len); 427 | JsonArray *messages = json_decode_array(data, len); 428 | gint64 conv_id = g_ascii_strtoll(conv_id_str, NULL, 10); 429 | gint64 since = g_ascii_strtoll(since_str, NULL, 10); 430 | const gchar *phone_number = g_hash_table_lookup(psa->im_conversations, &conv_id); 431 | 432 | if (phone_number == NULL) { 433 | purple_debug_error("pulsesms", "Error, unknown conversation id %s\n", conv_id_str); 434 | 435 | // use /api/v1/conversations/" conv_id "?account_id=... to lookup unknown conv ids 436 | 437 | g_dataset_destroy(request); 438 | g_free(conv_id_str); 439 | g_free(since_str); 440 | json_array_unref(messages); 441 | return; 442 | } 443 | 444 | for (i = json_array_get_length(messages) - 1; i >= 0; i--) { 445 | JsonObject *message = json_array_get_object_element(messages, i); 446 | gint64 message_type = json_object_get_int_member(message, "message_type"); 447 | gchar *data = pulsesms_decrypt(psa, json_object_get_string_member(message, "data")); 448 | gchar *escaped_data = purple_markup_escape_text(data, -1); 449 | gint64 timestamp = json_object_get_int_member(message, "timestamp"); 450 | gchar *mime_type = pulsesms_decrypt(psa, json_object_get_string_member(message, "mime_type")); 451 | 452 | //if (message.message_type == 0) { // received or media 453 | //} else if (message.message_type == 6) { //media preview 454 | //} else if (message.message_type == 3) { //error 455 | //} else if (message.message_type == 5) { //info 456 | //} else { //sent message (2 sending, 1 sent, 4 delivered) 457 | if (timestamp > since) { 458 | if (mime_type && strncmp(mime_type, "image/", 6) == 0) { 459 | gint64 message_id = json_object_get_int_member(message, "device_id"); 460 | PurpleHttpRequest *request = purple_http_request_new(NULL); 461 | const gchar *account_id = purple_account_get_string(psa->account, "account_id", NULL); 462 | 463 | purple_http_request_set_url_printf(request, PULSESMS_API_HOST "/api/v1/media/%" G_GINT64_FORMAT "?account_id=%s", message_id, purple_url_encode(account_id)); 464 | 465 | g_dataset_set_data(request, "message_type", GINT_TO_POINTER((int) message_type)); 466 | g_dataset_set_data(request, "timestamp", GINT_TO_POINTER((int) (timestamp / 1000))); 467 | g_dataset_set_data_full(request, "phone_number", g_strdup(phone_number), g_free); 468 | purple_http_request_set_max_len(request, -1); 469 | purple_http_request_header_set(request, "Accept-Encoding", " "); // Disable compression to disable crashing 470 | purple_http_request(psa->pc, request, pulsesms_got_http_image_for_conv, psa); 471 | purple_http_request_unref(request); 472 | 473 | } else { 474 | if (message_type == 0) { 475 | purple_serv_got_im(psa->pc, phone_number, escaped_data, PURPLE_MESSAGE_RECV, timestamp / 1000); 476 | 477 | } else if (message_type == 1 || message_type == 2) { 478 | PurpleConversation *conv; 479 | PurpleIMConversation *imconv; 480 | PurpleMessage *msg; 481 | 482 | imconv = purple_conversations_find_im_with_account(phone_number, psa->account); 483 | 484 | if (imconv == NULL) { 485 | imconv = purple_im_conversation_new(psa->account, phone_number); 486 | } 487 | 488 | conv = PURPLE_CONVERSATION(imconv); 489 | 490 | if (escaped_data && *escaped_data) { 491 | msg = purple_message_new_outgoing(phone_number, escaped_data, PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_REMOTE_SEND | PURPLE_MESSAGE_DELAYED); 492 | purple_message_set_time(msg, timestamp / 1000); 493 | purple_conversation_write_message(conv, msg); 494 | purple_message_destroy(msg); 495 | } 496 | } 497 | 498 | } 499 | } 500 | 501 | g_free(data); 502 | g_free(mime_type); 503 | g_free(escaped_data); 504 | 505 | } 506 | 507 | g_dataset_destroy(request); 508 | g_free(conv_id_str); 509 | g_free(since_str); 510 | json_array_unref(messages); 511 | } 512 | 513 | static void 514 | pulsesms_fetch_conversation_history(PulseSMSAccount *psa, gint64 conv_id, gint64 since) 515 | { 516 | const gchar *account_id = purple_account_get_string(psa->account, "account_id", NULL); 517 | 518 | PurpleHttpRequest *request = purple_http_request_new(NULL); 519 | purple_http_request_set_keepalive_pool(request, psa->keepalive_pool); 520 | 521 | purple_http_request_set_url_printf(request, PULSESMS_API_HOST "/api/v1/messages?account_id=%s&conversation_id=%" G_GINT64_FORMAT "&limit=20", purple_url_encode(account_id), conv_id); 522 | 523 | g_dataset_set_data(request, "conv_id", g_strdup_printf("%" G_GINT64_FORMAT, conv_id)); 524 | g_dataset_set_data(request, "since", g_strdup_printf("%" G_GINT64_FORMAT, since)); 525 | purple_http_request(psa->pc, request, pulsesms_got_conversation_history, psa); 526 | purple_http_request_unref(request); 527 | } 528 | 529 | static void 530 | pulsesms_got_conversations(PurpleHttpConnection *http_conn, PurpleHttpResponse *response, gpointer user_data) 531 | { 532 | PulseSMSAccount *psa = user_data; 533 | gsize len; 534 | const gchar *data = purple_http_response_get_data(response, &len); 535 | JsonArray *conversations = json_decode_array(data, len); 536 | int i; 537 | gint64 max_timestamp = 0; 538 | 539 | for (i = json_array_get_length(conversations) - 1; i >= 0; i--) { 540 | JsonObject *conversation = json_array_get_object_element(conversations, i); 541 | 542 | gint64 conv_id = json_object_get_int_member(conversation, "device_id"); 543 | gchar *phone_number = pulsesms_decrypt(psa, json_object_get_string_member(conversation, "phone_numbers")); 544 | gint64 timestamp = json_object_get_int_member(conversation, "timestamp"); 545 | 546 | //purple_debug_misc("pulsesms", "Phone number %s conv_id %" G_GINT64_FORMAT " timestamp %" G_GINT64_FORMAT "\n", phone_number, conv_id, timestamp); 547 | 548 | if (timestamp > max_timestamp) { 549 | max_timestamp = timestamp; 550 | } 551 | if (psa->last_conv_timestamp && timestamp > psa->last_conv_timestamp) { 552 | if (!g_hash_table_contains(psa->im_conversations, &conv_id)) { 553 | g_hash_table_insert(psa->im_conversations, g_memdup(&conv_id, sizeof(gint64)), g_strdup(phone_number)); 554 | g_hash_table_insert(psa->im_conversations_rev, g_strdup(phone_number), GUINT_TO_POINTER((guint) conv_id)); 555 | } 556 | 557 | pulsesms_fetch_conversation_history(psa, conv_id, psa->last_conv_timestamp); 558 | } 559 | 560 | g_free(phone_number); 561 | } 562 | 563 | if (max_timestamp != 0) { 564 | psa->last_conv_timestamp = max_timestamp; 565 | 566 | purple_account_set_int(psa->account, "last_conv_timestamp_high", max_timestamp >> 32); 567 | purple_account_set_int(psa->account, "last_conv_timestamp_low", max_timestamp & 0xFFFFFFFF); 568 | } 569 | 570 | json_array_unref(conversations); 571 | } 572 | 573 | static gboolean 574 | pulsesms_fetch_conversations(gpointer data) 575 | { 576 | PulseSMSAccount *psa = data; 577 | const gchar *account_id = purple_account_get_string(psa->account, "account_id", NULL); 578 | 579 | PurpleHttpRequest *request = purple_http_request_new(NULL); 580 | purple_http_request_set_keepalive_pool(request, psa->keepalive_pool); 581 | 582 | purple_http_request_set_url_printf(request, PULSESMS_API_HOST "/api/v1/conversations/index_public_unarchived?account_id=%s", purple_url_encode(account_id)); 583 | 584 | purple_http_request(psa->pc, request, pulsesms_got_conversations, psa); 585 | purple_http_request_unref(request); 586 | 587 | return TRUE; 588 | } 589 | 590 | static void 591 | pulsesms_start_stuff(PulseSMSAccount *psa) 592 | { 593 | //TODO 594 | //wss://api.messenger.klinkerapps.com/api/v1/stream?account_id= 595 | 596 | 597 | pulsesms_create_ctx(psa); 598 | pulsesms_fetch_contacts(psa); 599 | 600 | pulsesms_fetch_conversations(psa); 601 | psa->conv_fetch_timeout = g_timeout_add_seconds(60, pulsesms_fetch_conversations, psa); 602 | 603 | purple_connection_set_state(psa->pc, PURPLE_CONNECTION_CONNECTED); 604 | } 605 | 606 | static void 607 | pulsesms_got_login(PurpleHttpConnection *http_conn, PurpleHttpResponse *response, gpointer user_data) 608 | { 609 | PulseSMSAccount *psa = user_data; 610 | gsize len; 611 | const gchar *data = purple_http_response_get_data(response, &len); 612 | JsonObject *info = json_decode_object(data, len); 613 | 614 | if (!info) { 615 | purple_connection_error(psa->pc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, "Invalid username/password"); 616 | return; 617 | } 618 | 619 | purple_account_set_string(psa->account, "account_id", json_object_get_string_member(info, "account_id")); 620 | purple_account_set_string(psa->account, "salt", json_object_get_string_member(info, "salt1")); 621 | 622 | const gchar *password = purple_connection_get_password(psa->pc); 623 | const gchar *salt2 = json_object_get_string_member(info, "salt2"); 624 | unsigned dklen = 32; 625 | unsigned rounds = 10000; 626 | uint8_t DK[ dklen ]; 627 | 628 | gc_pbkdf2_sha1(password, strlen(password), salt2, strlen(salt2), rounds, (char*) DK, dklen); 629 | 630 | gchar *hash = g_base64_encode(DK, dklen); 631 | 632 | purple_account_set_string(psa->account, "hash", hash); 633 | 634 | g_free(hash); 635 | 636 | pulsesms_start_stuff(psa); 637 | 638 | json_object_unref(info); 639 | } 640 | 641 | static void 642 | pulsesms_send_login(PulseSMSAccount *psa) 643 | { 644 | GString *postbody; 645 | 646 | PurpleHttpRequest *request = purple_http_request_new(PULSESMS_API_HOST "/api/v1/accounts/login"); 647 | purple_http_request_set_keepalive_pool(request, psa->keepalive_pool); 648 | 649 | purple_http_request_set_method(request, "POST"); 650 | purple_http_request_header_set(request, "Content-type", "application/x-www-form-urlencoded; charset=UTF-8"); 651 | 652 | postbody = g_string_new(NULL); 653 | g_string_append_printf(postbody, "username=%s&", purple_url_encode(purple_account_get_username(psa->account))); 654 | g_string_append_printf(postbody, "password=%s&", purple_url_encode(purple_connection_get_password(psa->pc))); 655 | purple_http_request_set_contents(request, postbody->str, postbody->len); 656 | g_string_free(postbody, TRUE); 657 | 658 | purple_http_request(psa->pc, request, pulsesms_got_login, psa); 659 | purple_http_request_unref(request); 660 | } 661 | 662 | /*****************************************************************************/ 663 | 664 | 665 | static GList * 666 | pulsesms_add_account_options(GList *account_options) 667 | { 668 | // PurpleAccountOption *option; 669 | 670 | // option = purple_account_option_bool_new(N_("Show call links in chat"), "show-call-links", !purple_media_manager_get()); 671 | // account_options = g_list_append(account_options, option); 672 | 673 | // option = purple_account_option_bool_new(N_("Un-Googlify URLs"), "unravel_google_url", FALSE); 674 | // account_options = g_list_append(account_options, option); 675 | 676 | // option = purple_account_option_bool_new(N_("Treat invisible users as offline"), "treat_invisible_as_offline", FALSE); 677 | // account_options = g_list_append(account_options, option); 678 | 679 | return account_options; 680 | } 681 | 682 | static GList * 683 | pulsesms_actions( 684 | #if !PURPLE_VERSION_CHECK(3, 0, 0) 685 | PurplePlugin *plugin, gpointer context 686 | #else 687 | PurpleConnection *pc 688 | #endif 689 | ) 690 | { 691 | GList *m = NULL; 692 | // PurpleProtocolAction *act; 693 | 694 | // act = purple_protocol_action_new(_("Search for friends..."), pulsesms_search_users); 695 | // m = g_list_append(m, act); 696 | 697 | // act = purple_protocol_action_new(_("Join a group chat by URL..."), pulsesms_join_chat_by_url_action); 698 | // m = g_list_append(m, act); 699 | 700 | return m; 701 | } 702 | 703 | static void 704 | pulsesms_create_ctx(PulseSMSAccount *psa) 705 | { 706 | const gchar *account_id = purple_account_get_string(psa->account, "account_id", NULL); 707 | const gchar *hash = purple_account_get_string(psa->account, "hash", NULL); 708 | const gchar *salt = purple_account_get_string(psa->account, "salt", NULL); 709 | 710 | gchar *combined_key = g_strdup_printf("%s:%s\n", account_id, hash); 711 | 712 | unsigned dklen = 32; 713 | unsigned rounds = 10000; 714 | uint8_t DK[ dklen ]; 715 | 716 | gc_pbkdf2_sha1(combined_key, strlen(combined_key), salt, strlen(salt), rounds, (char*) DK, dklen); 717 | 718 | AES_init_ctx(psa->ctx, DK); 719 | 720 | g_free(combined_key); 721 | } 722 | 723 | static void 724 | pulsesms_login(PurpleAccount *account) 725 | { 726 | PurpleConnection *pc; 727 | PulseSMSAccount *psa; 728 | const gchar *password; 729 | // PurpleConnectionFlags pc_flags; 730 | 731 | pc = purple_account_get_connection(account); 732 | password = purple_connection_get_password(pc); 733 | 734 | // pc_flags = purple_connection_get_flags(pc); 735 | // pc_flags |= PURPLE_CONNECTION_FLAG_HTML; 736 | // pc_flags |= PURPLE_CONNECTION_FLAG_NO_FONTSIZE; 737 | // pc_flags |= PURPLE_CONNECTION_FLAG_NO_BGCOLOR; 738 | // pc_flags &= ~PURPLE_CONNECTION_FLAG_NO_IMAGES; 739 | // purple_connection_set_flags(pc, pc_flags); 740 | 741 | psa = g_new0(PulseSMSAccount, 1); 742 | psa->account = account; 743 | psa->pc = pc; 744 | psa->keepalive_pool = purple_http_keepalive_pool_new(); 745 | psa->sent_message_ids = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); 746 | psa->im_conversations = g_hash_table_new_full(g_int64_hash, g_int64_equal, g_free, g_free); 747 | psa->im_conversations_rev = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); 748 | psa->normalised_phone_lookup = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); 749 | psa->normalised_id_lookup = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); 750 | psa->ctx = g_new0(struct AES_ctx, 1); 751 | 752 | psa->last_conv_timestamp = purple_account_get_int(account, "last_conv_timestamp_high", 0); 753 | if (psa->last_conv_timestamp != 0) { 754 | psa->last_conv_timestamp = (psa->last_conv_timestamp << 32) | ((gint64) purple_account_get_int(account, "last_conv_timestamp_low", 0) & 0xFFFFFFFF); 755 | } 756 | 757 | purple_connection_set_protocol_data(pc, psa); 758 | 759 | if (purple_account_get_string(account, "account_id", NULL) && 760 | purple_account_get_string(account, "hash", NULL) && 761 | purple_account_get_string(account, "salt", NULL)) { 762 | 763 | pulsesms_start_stuff(psa); 764 | } else if (password && *password) { 765 | purple_connection_update_progress(pc, _("Authenticating"), 1, 3); 766 | pulsesms_send_login(psa); 767 | } 768 | } 769 | 770 | static void 771 | pulsesms_close(PurpleConnection *pc) 772 | { 773 | PulseSMSAccount *psa; 774 | 775 | psa = purple_connection_get_protocol_data(pc); 776 | purple_signals_disconnect_by_handle(psa->account); 777 | 778 | if (psa->conv_fetch_timeout) { 779 | g_source_remove(psa->conv_fetch_timeout); 780 | } 781 | 782 | purple_http_conn_cancel_all(pc); 783 | 784 | purple_http_keepalive_pool_unref(psa->keepalive_pool); 785 | 786 | g_hash_table_remove_all(psa->sent_message_ids); 787 | g_hash_table_unref(psa->sent_message_ids); 788 | 789 | g_hash_table_remove_all(psa->im_conversations); 790 | g_hash_table_unref(psa->im_conversations); 791 | g_hash_table_remove_all(psa->im_conversations_rev); 792 | g_hash_table_unref(psa->im_conversations_rev); 793 | 794 | g_hash_table_remove_all(psa->normalised_phone_lookup); 795 | g_hash_table_unref(psa->normalised_phone_lookup); 796 | psa->normalised_phone_lookup = NULL; 797 | g_hash_table_remove_all(psa->normalised_id_lookup); 798 | g_hash_table_unref(psa->normalised_id_lookup); 799 | psa->normalised_id_lookup = NULL; 800 | 801 | g_free(psa->ctx); 802 | 803 | g_free(psa); 804 | } 805 | 806 | 807 | static const char * 808 | pulsesms_list_icon(PurpleAccount *account, PurpleBuddy *buddy) 809 | { 810 | return "pulsesms"; 811 | } 812 | 813 | GList * 814 | pulsesms_status_types(PurpleAccount *account) 815 | { 816 | GList *types = NULL; 817 | PurpleStatusType *status; 818 | 819 | status = purple_status_type_new_full(PURPLE_STATUS_MOBILE, "mobile", _("Phone"), FALSE, FALSE, FALSE); 820 | types = g_list_append(types, status); 821 | 822 | status = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, NULL, NULL, TRUE, TRUE, FALSE); 823 | types = g_list_append(types, status); 824 | 825 | status = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, NULL, NULL, TRUE, TRUE, FALSE); 826 | types = g_list_append(types, status); 827 | 828 | return types; 829 | } 830 | 831 | static gboolean 832 | pulsesms_offline_message(const PurpleBuddy *buddy) 833 | { 834 | return TRUE; 835 | } 836 | 837 | 838 | /*****************************************************************************/ 839 | 840 | static gboolean 841 | plugin_load(PurplePlugin *plugin, GError **error) 842 | { 843 | return TRUE; 844 | } 845 | 846 | static gboolean 847 | plugin_unload(PurplePlugin *plugin, GError **error) 848 | { 849 | purple_signals_disconnect_by_handle(plugin); 850 | 851 | return TRUE; 852 | } 853 | 854 | #if PURPLE_VERSION_CHECK(3, 0, 0) 855 | 856 | G_MODULE_EXPORT GType pulsesms_protocol_get_type(void); 857 | #define PULSESMS_TYPE_PROTOCOL (pulsesms_protocol_get_type()) 858 | #define PULSESMS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PULSESMS_TYPE_PROTOCOL, PulseSMSProtocol)) 859 | #define PULSESMS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PULSESMS_TYPE_PROTOCOL, PulseSMSProtocolClass)) 860 | #define PULSESMS_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PULSESMS_TYPE_PROTOCOL)) 861 | #define PULSESMS_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PULSESMS_TYPE_PROTOCOL)) 862 | #define PULSESMS_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PULSESMS_TYPE_PROTOCOL, PulseSMSProtocolClass)) 863 | 864 | typedef struct _PulseSMSProtocol 865 | { 866 | PurpleProtocol parent; 867 | } PulseSMSProtocol; 868 | 869 | typedef struct _PulseSMSProtocolClass 870 | { 871 | PurpleProtocolClass parent_class; 872 | } PulseSMSProtocolClass; 873 | 874 | static void 875 | pulsesms_protocol_init(PurpleProtocol *prpl_info) 876 | { 877 | PurpleProtocol *plugin = prpl_info, *info = prpl_info; 878 | 879 | info->id = PULSESMS_PLUGIN_ID; 880 | info->name = "PulseSMS"; 881 | 882 | prpl_info->account_options = pulsesms_add_account_options(prpl_info->account_options); 883 | } 884 | 885 | static void 886 | pulsesms_protocol_class_init(PurpleProtocolClass *prpl_info) 887 | { 888 | prpl_info->login = pulsesms_login; 889 | prpl_info->close = pulsesms_close; 890 | prpl_info->status_types = pulsesms_status_types; 891 | prpl_info->list_icon = pulsesms_list_icon; 892 | } 893 | 894 | static void 895 | pulsesms_protocol_client_iface_init(PurpleProtocolClientIface *prpl_info) 896 | { 897 | prpl_info->offline_message = pulsesms_offline_message; 898 | } 899 | 900 | static void 901 | pulsesms_protocol_im_iface_init(PurpleProtocolIMIface *prpl_info) 902 | { 903 | prpl_info->send = pulsesms_send_im; 904 | } 905 | 906 | static PurpleProtocol *pulsesms_protocol; 907 | 908 | PURPLE_DEFINE_TYPE_EXTENDED( 909 | PulseSMSProtocol, pulsesms_protocol, PURPLE_TYPE_PROTOCOL, 0, 910 | 911 | PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE, 912 | pulsesms_protocol_im_iface_init) 913 | 914 | PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE, 915 | pulsesms_protocol_client_iface_init) 916 | ); 917 | 918 | static gboolean 919 | libpurple3_plugin_load(PurplePlugin *plugin, GError **error) 920 | { 921 | pulsesms_protocol_register_type(plugin); 922 | pulsesms_protocol = purple_protocols_add(PULSESMS_TYPE_PROTOCOL, error); 923 | if (!pulsesms_protocol) 924 | return FALSE; 925 | 926 | return plugin_load(plugin, error); 927 | } 928 | 929 | static gboolean 930 | libpurple3_plugin_unload(PurplePlugin *plugin, GError **error) 931 | { 932 | if (!plugin_unload(plugin, error)) 933 | return FALSE; 934 | 935 | if (!purple_protocols_remove(pulsesms_protocol, error)) 936 | return FALSE; 937 | 938 | return TRUE; 939 | } 940 | 941 | static PurplePluginInfo * 942 | plugin_query(GError **error) 943 | { 944 | return purple_plugin_info_new( 945 | "id", PULSESMS_PLUGIN_ID, 946 | "name", "PulseSMS", 947 | "version", PULSESMS_PLUGIN_VERSION, 948 | "category", N_("Protocol"), 949 | "summary", N_("PulseSMS Protocol Plugins."), 950 | "description", N_("Adds SMS support (via Pulse SMS) to libpurple."), 951 | "website", "https://bitbucket.org/EionRobb/purple-pulsesms/", 952 | "abi-version", PURPLE_ABI_VERSION, 953 | "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL | 954 | PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD, 955 | NULL 956 | ); 957 | } 958 | 959 | PURPLE_PLUGIN_INIT(pulsesms, plugin_query, 960 | libpurple3_plugin_load, libpurple3_plugin_unload); 961 | 962 | #else 963 | 964 | // Normally set in core.c in purple3 965 | void _purple_socket_init(void); 966 | void _purple_socket_uninit(void); 967 | 968 | 969 | static gboolean 970 | libpurple2_plugin_load(PurplePlugin *plugin) 971 | { 972 | _purple_socket_init(); 973 | purple_http_init(); 974 | 975 | return plugin_load(plugin, NULL); 976 | } 977 | 978 | static gboolean 979 | libpurple2_plugin_unload(PurplePlugin *plugin) 980 | { 981 | _purple_socket_uninit(); 982 | purple_http_uninit(); 983 | 984 | return plugin_unload(plugin, NULL); 985 | } 986 | 987 | static PurplePluginInfo info = 988 | { 989 | PURPLE_PLUGIN_MAGIC, 990 | PURPLE_MAJOR_VERSION, 991 | PURPLE_MINOR_VERSION, 992 | PURPLE_PLUGIN_PROTOCOL, /**< type */ 993 | NULL, /**< ui_requirement */ 994 | 0, /**< flags */ 995 | NULL, /**< dependencies */ 996 | PURPLE_PRIORITY_DEFAULT, /**< priority */ 997 | 998 | PULSESMS_PLUGIN_ID, /**< id */ 999 | N_("PulseSMS"), /**< name */ 1000 | PULSESMS_PLUGIN_VERSION, /**< version */ 1001 | 1002 | N_("PulseSMS Protocol Plugins."), /**< summary */ 1003 | 1004 | N_("Adds SMS support (via Pulse SMS) to libpurple."), /**< description */ 1005 | "Eion Robb ", /**< author */ 1006 | "https://bitbucket.org/EionRobb/purple-pulsesms/", /**< homepage */ 1007 | 1008 | libpurple2_plugin_load, /**< load */ 1009 | libpurple2_plugin_unload, /**< unload */ 1010 | NULL, /**< destroy */ 1011 | 1012 | NULL, /**< ui_info */ 1013 | NULL, /**< extra_info */ 1014 | NULL, /**< prefs_info */ 1015 | NULL, /**< actions */ 1016 | 1017 | /* padding */ 1018 | NULL, 1019 | NULL, 1020 | NULL, 1021 | NULL 1022 | }; 1023 | 1024 | static void 1025 | init_plugin(PurplePlugin *plugin) 1026 | { 1027 | PurplePluginInfo *info; 1028 | PurplePluginProtocolInfo *prpl_info = g_new0(PurplePluginProtocolInfo, 1); 1029 | 1030 | info = plugin->info; 1031 | if (info == NULL) { 1032 | plugin->info = info = g_new0(PurplePluginInfo, 1); 1033 | } 1034 | 1035 | prpl_info->protocol_options = pulsesms_add_account_options(prpl_info->protocol_options); 1036 | 1037 | prpl_info->login = pulsesms_login; 1038 | prpl_info->close = pulsesms_close; 1039 | prpl_info->status_types = pulsesms_status_types; 1040 | prpl_info->list_icon = pulsesms_list_icon; 1041 | prpl_info->offline_message = pulsesms_offline_message; 1042 | prpl_info->normalize = pulsesms_normalize; 1043 | 1044 | prpl_info->send_im = pulsesms_send_im; 1045 | 1046 | info->extra_info = prpl_info; 1047 | #if PURPLE_MINOR_VERSION >= 5 1048 | prpl_info->struct_size = sizeof(PurplePluginProtocolInfo); 1049 | #endif 1050 | 1051 | info->actions = pulsesms_actions; 1052 | } 1053 | 1054 | PURPLE_INIT_PLUGIN(pulsesms, init_plugin, info); 1055 | 1056 | #endif -------------------------------------------------------------------------------- /pulsesms16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EionRobb/purple-pulsesms/b7991fa92bf67bc08eb01e124bc90431541ab688/pulsesms16.png -------------------------------------------------------------------------------- /pulsesms22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EionRobb/purple-pulsesms/b7991fa92bf67bc08eb01e124bc90431541ab688/pulsesms22.png -------------------------------------------------------------------------------- /pulsesms24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EionRobb/purple-pulsesms/b7991fa92bf67bc08eb01e124bc90431541ab688/pulsesms24.png -------------------------------------------------------------------------------- /pulsesms48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EionRobb/purple-pulsesms/b7991fa92bf67bc08eb01e124bc90431541ab688/pulsesms48.png -------------------------------------------------------------------------------- /purple2compat/circularbuffer.h: -------------------------------------------------------------------------------- 1 | #ifndef _CIRCULARBUFFER_H_ 2 | #define _CIRCULARBUFFER_H_ 3 | 4 | #include "circbuffer.h" 5 | 6 | #define PurpleCircularBuffer PurpleCircBuffer 7 | #define purple_circular_buffer_new purple_circ_buffer_new 8 | #define purple_circular_buffer_destroy purple_circ_buffer_destroy 9 | #define purple_circular_buffer_append purple_circ_buffer_append 10 | #define purple_circular_buffer_get_max_read purple_circ_buffer_get_max_read 11 | #define purple_circular_buffer_mark_read purple_circ_buffer_mark_read 12 | #define purple_circular_buffer_get_output(buf) ((const gchar *) (buf)->outptr) 13 | #define purple_circular_buffer_get_used(buf) ((buf)->bufused) 14 | 15 | #endif /*_CIRCULARBUFFER_H_*/ 16 | -------------------------------------------------------------------------------- /purple2compat/glibcompat.h: -------------------------------------------------------------------------------- 1 | #include "../glibcompat.h" 2 | -------------------------------------------------------------------------------- /purple2compat/http.h: -------------------------------------------------------------------------------- 1 | /* purple 2 | * 3 | * Purple is the legal property of its developers, whose names are too numerous 4 | * to list here. Please refer to the COPYRIGHT file distributed with this 5 | * source distribution. 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 20 | */ 21 | 22 | #ifndef _PURPLE_HTTP_H_ 23 | #define _PURPLE_HTTP_H_ 24 | /** 25 | * SECTION:http 26 | * @section_id: libpurple-http 27 | * @short_description: http.h 28 | * @title: HTTP API 29 | */ 30 | 31 | #include 32 | 33 | #include "connection.h" 34 | 35 | /** 36 | * PurpleHttpRequest: 37 | * 38 | * A structure containing all data required to generate a single HTTP request. 39 | */ 40 | typedef struct _PurpleHttpRequest PurpleHttpRequest; 41 | 42 | /** 43 | * PurpleHttpConnection: 44 | * 45 | * A representation of actually running HTTP request. Can be used to cancel the 46 | * request. 47 | */ 48 | typedef struct _PurpleHttpConnection PurpleHttpConnection; 49 | 50 | /** 51 | * PurpleHttpResponse: 52 | * 53 | * All information got with response for HTTP request. 54 | */ 55 | typedef struct _PurpleHttpResponse PurpleHttpResponse; 56 | 57 | /** 58 | * PurpleHttpURL: 59 | * 60 | * Parsed representation for the URL. 61 | */ 62 | typedef struct _PurpleHttpURL PurpleHttpURL; 63 | 64 | /** 65 | * PurpleHttpCookieJar: 66 | * 67 | * An collection of cookies, got from HTTP response or provided for HTTP 68 | * request. 69 | */ 70 | typedef struct _PurpleHttpCookieJar PurpleHttpCookieJar; 71 | 72 | /** 73 | * PurpleHttpKeepalivePool: 74 | * 75 | * A pool of TCP connections for HTTP Keep-Alive session. 76 | */ 77 | typedef struct _PurpleHttpKeepalivePool PurpleHttpKeepalivePool; 78 | 79 | /** 80 | * PurpleHttpConnectionSet: 81 | * 82 | * A set of running HTTP requests. Can be used to cancel all of them at once. 83 | */ 84 | typedef struct _PurpleHttpConnectionSet PurpleHttpConnectionSet; 85 | 86 | /** 87 | * PurpleHttpCallback: 88 | * 89 | * An callback called after performing (successfully or not) HTTP request. 90 | */ 91 | typedef void (*PurpleHttpCallback)(PurpleHttpConnection *http_conn, 92 | PurpleHttpResponse *response, gpointer user_data); 93 | 94 | /** 95 | * PurpleHttpContentReaderCb: 96 | * 97 | * An callback called after storing data requested by PurpleHttpContentReader. 98 | */ 99 | typedef void (*PurpleHttpContentReaderCb)(PurpleHttpConnection *http_conn, 100 | gboolean success, gboolean eof, size_t stored); 101 | 102 | /** 103 | * PurpleHttpContentReader: 104 | * @http_conn: Connection, which requests data. 105 | * @buffer: Buffer to store data to (with offset ignored). 106 | * @offset: Position, from where to read data. 107 | * @length: Length of data to read. 108 | * @user_data: The user data passed with callback function. 109 | * @cb: The function to call after storing data to buffer. 110 | * 111 | * An callback for getting large request contents (ie. from file stored on 112 | * disk). 113 | */ 114 | typedef void (*PurpleHttpContentReader)(PurpleHttpConnection *http_conn, 115 | gchar *buffer, size_t offset, size_t length, gpointer user_data, 116 | PurpleHttpContentReaderCb cb); 117 | 118 | /** 119 | * PurpleHttpContentWriter: 120 | * @http_conn: Connection, which requests data. 121 | * @response: Response at point got so far (may change later). 122 | * @buffer: Buffer to read data from (with offset ignored). 123 | * @offset: Position of data got (its value is offset + length of 124 | * previous call), can be safely ignored. 125 | * @length: Length of data read. 126 | * @user_data: The user data passed with callback function. 127 | * 128 | * An callback for writting large response contents. 129 | * 130 | * Returns: TRUE, if succeeded, FALSE otherwise. 131 | */ 132 | typedef gboolean (*PurpleHttpContentWriter)(PurpleHttpConnection *http_conn, 133 | PurpleHttpResponse *response, const gchar *buffer, size_t offset, 134 | size_t length, gpointer user_data); 135 | 136 | /** 137 | * PurpleHttpProgressWatcher: 138 | * @http_conn: The HTTP Connection. 139 | * @reading_state: FALSE, is we are sending the request, TRUE, when reading 140 | * the response. 141 | * @processed: The amount of data already processed. 142 | * @total: Total amount of data (in current state). 143 | * @user_data: The user data passed with callback function. 144 | * 145 | * An callback for watching HTTP connection progress. 146 | */ 147 | typedef void (*PurpleHttpProgressWatcher)(PurpleHttpConnection *http_conn, 148 | gboolean reading_state, int processed, int total, gpointer user_data); 149 | 150 | G_BEGIN_DECLS 151 | 152 | /**************************************************************************/ 153 | /* Performing HTTP requests */ 154 | /**************************************************************************/ 155 | 156 | /** 157 | * purple_http_get: 158 | * @gc: The connection for which the request is needed, or NULL. 159 | * @callback: (scope call): The callback function. 160 | * @user_data: The user data to pass to the callback function. 161 | * @url: The URL. 162 | * 163 | * Fetches the data from a URL with GET request, and passes it to a callback 164 | * function. 165 | * 166 | * Returns: The HTTP connection struct. 167 | */ 168 | PurpleHttpConnection * purple_http_get(PurpleConnection *gc, 169 | PurpleHttpCallback callback, gpointer user_data, const gchar *url); 170 | 171 | /** 172 | * purple_http_get_printf: 173 | * @gc: The connection for which the request is needed, or NULL. 174 | * @callback: (scope call): The callback function. 175 | * @user_data: The user data to pass to the callback function. 176 | * @format: The format string. 177 | * 178 | * Constructs an URL and fetches the data from it with GET request, then passes 179 | * it to a callback function. 180 | * 181 | * Returns: The HTTP connection struct. 182 | */ 183 | PurpleHttpConnection * purple_http_get_printf(PurpleConnection *gc, 184 | PurpleHttpCallback callback, gpointer user_data, 185 | const gchar *format, ...) G_GNUC_PRINTF(4, 5); 186 | 187 | /** 188 | * purple_http_request: 189 | * @gc: The connection for which the request is needed, or NULL. 190 | * @request: The request. 191 | * @callback: (scope call): The callback function. 192 | * @user_data: The user data to pass to the callback function. 193 | * 194 | * Fetches a HTTP request and passes the response to a callback function. 195 | * Provided request struct can be shared by multiple http requests but can not 196 | * be modified when any of these is running. 197 | * 198 | * Returns: The HTTP connection struct. 199 | */ 200 | PurpleHttpConnection * purple_http_request(PurpleConnection *gc, 201 | PurpleHttpRequest *request, PurpleHttpCallback callback, 202 | gpointer user_data); 203 | 204 | /**************************************************************************/ 205 | /* HTTP connection API */ 206 | /**************************************************************************/ 207 | 208 | /** 209 | * purple_http_conn_cancel: 210 | * @http_conn: The data returned when you initiated the HTTP request. 211 | * 212 | * Cancel a pending HTTP request. 213 | */ 214 | void purple_http_conn_cancel(PurpleHttpConnection *http_conn); 215 | 216 | /** 217 | * purple_http_conn_cancel_all: 218 | * @gc: The handle. 219 | * 220 | * Cancels all HTTP connections associated with the specified handle. 221 | */ 222 | void purple_http_conn_cancel_all(PurpleConnection *gc); 223 | 224 | /** 225 | * purple_http_conn_is_running: 226 | * @http_conn: The HTTP connection (may be invalid pointer). 227 | * 228 | * Checks, if provided HTTP request is running. 229 | * 230 | * Returns: TRUE, if provided connection is currently running. 231 | */ 232 | gboolean purple_http_conn_is_running(PurpleHttpConnection *http_conn); 233 | 234 | /** 235 | * purple_http_conn_get_request: 236 | * @http_conn: The HTTP connection. 237 | * 238 | * Gets PurpleHttpRequest used for specified HTTP connection. 239 | * 240 | * Returns: The PurpleHttpRequest object. 241 | */ 242 | PurpleHttpRequest * purple_http_conn_get_request( 243 | PurpleHttpConnection *http_conn); 244 | 245 | /** 246 | * purple_http_conn_get_cookie_jar: 247 | * @http_conn: The HTTP connection. 248 | * 249 | * Gets cookie jar used within connection. 250 | * 251 | * Returns: The cookie jar. 252 | */ 253 | PurpleHttpCookieJar * purple_http_conn_get_cookie_jar( 254 | PurpleHttpConnection *http_conn); 255 | 256 | /** 257 | * purple_http_conn_get_purple_connection: 258 | * @http_conn: The HTTP connection. 259 | * 260 | * Gets PurpleConnection tied with specified HTTP connection. 261 | * 262 | * Returns: The PurpleConnection object. 263 | */ 264 | PurpleConnection * purple_http_conn_get_purple_connection( 265 | PurpleHttpConnection *http_conn); 266 | 267 | /** 268 | * purple_http_conn_set_progress_watcher: 269 | * @http_conn: The HTTP connection. 270 | * @watcher: (scope call): The watcher. 271 | * @user_data: The user data to pass to the callback function. 272 | * @interval_threshold: Minimum interval (in microseconds) of calls to 273 | * watcher, or -1 for default. 274 | * 275 | * Sets the watcher, called after writing or reading data to/from HTTP stream. 276 | * May be used for updating transfer progress gauge. 277 | */ 278 | void purple_http_conn_set_progress_watcher(PurpleHttpConnection *http_conn, 279 | PurpleHttpProgressWatcher watcher, gpointer user_data, 280 | gint interval_threshold); 281 | 282 | 283 | /**************************************************************************/ 284 | /* URL processing API */ 285 | /**************************************************************************/ 286 | 287 | /** 288 | * purple_http_url_parse: 289 | * @url: The URL to parse. 290 | * 291 | * Parses a URL. 292 | * 293 | * The returned data must be freed with purple_http_url_free. 294 | * 295 | * Returns: The parsed url or NULL, if the URL is invalid. 296 | */ 297 | PurpleHttpURL * 298 | purple_http_url_parse(const char *url); 299 | 300 | /** 301 | * purple_http_url_free: 302 | * @parsed_url: The parsed URL struct, or NULL. 303 | * 304 | * Frees the parsed URL struct. 305 | */ 306 | void 307 | purple_http_url_free(PurpleHttpURL *parsed_url); 308 | 309 | /** 310 | * purple_http_url_relative: 311 | * @base_url: The base URL. The result is stored here. 312 | * @relative_url: The relative URL. 313 | * 314 | * Converts the base URL to the absolute form of the provided relative URL. 315 | * 316 | * Example: "https://example.com/path/to/file.html" + "subdir/other-file.html" = 317 | * "https://example.com/path/to/subdir/another-file.html" 318 | */ 319 | void 320 | purple_http_url_relative(PurpleHttpURL *base_url, PurpleHttpURL *relative_url); 321 | 322 | /** 323 | * purple_http_url_print: 324 | * @parsed_url: The URL struct. 325 | * 326 | * Converts the URL struct to the printable form. The result may not be a valid 327 | * URL (in cases, when the struct doesn't have all fields filled properly). 328 | * 329 | * The result must be g_free'd. 330 | * 331 | * Returns: The printable form of the URL. 332 | */ 333 | gchar * 334 | purple_http_url_print(PurpleHttpURL *parsed_url); 335 | 336 | /** 337 | * purple_http_url_get_protocol: 338 | * @parsed_url: The URL struct. 339 | * 340 | * Gets the protocol part of URL. 341 | * 342 | * Returns: The protocol. 343 | */ 344 | const gchar * 345 | purple_http_url_get_protocol(const PurpleHttpURL *parsed_url); 346 | 347 | /** 348 | * purple_http_url_get_username: 349 | * @parsed_url: The URL struct. 350 | * 351 | * Gets the username part of URL. 352 | * 353 | * Returns: The username. 354 | */ 355 | const gchar * 356 | purple_http_url_get_username(const PurpleHttpURL *parsed_url); 357 | 358 | /** 359 | * purple_http_url_get_password: 360 | * @parsed_url: The URL struct. 361 | * 362 | * Gets the password part of URL. 363 | * 364 | * Returns: The password. 365 | */ 366 | const gchar * 367 | purple_http_url_get_password(const PurpleHttpURL *parsed_url); 368 | 369 | /** 370 | * purple_http_url_get_host: 371 | * @parsed_url: The URL struct. 372 | * 373 | * Gets the hostname part of URL. 374 | * 375 | * Returns: The hostname. 376 | */ 377 | const gchar * 378 | purple_http_url_get_host(const PurpleHttpURL *parsed_url); 379 | 380 | /** 381 | * purple_http_url_get_port: 382 | * @parsed_url: The URL struct. 383 | * 384 | * Gets the port part of URL. 385 | * 386 | * Returns: The port number. 387 | */ 388 | int 389 | purple_http_url_get_port(const PurpleHttpURL *parsed_url); 390 | 391 | /** 392 | * purple_http_url_get_path: 393 | * @parsed_url: The URL struct. 394 | * 395 | * Gets the path part of URL. 396 | * 397 | * Returns: The path. 398 | */ 399 | const gchar * 400 | purple_http_url_get_path(const PurpleHttpURL *parsed_url); 401 | 402 | /** 403 | * purple_http_url_get_fragment: 404 | * @parsed_url: The URL struct. 405 | * 406 | * Gets the fragment part of URL. 407 | * 408 | * Returns: The fragment. 409 | */ 410 | const gchar * 411 | purple_http_url_get_fragment(const PurpleHttpURL *parsed_url); 412 | 413 | 414 | /**************************************************************************/ 415 | /* Cookie jar API */ 416 | /**************************************************************************/ 417 | 418 | /** 419 | * purple_http_cookie_jar_new: 420 | * 421 | * Creates new cookie jar, 422 | * 423 | * Returns: empty cookie jar. 424 | */ 425 | PurpleHttpCookieJar * purple_http_cookie_jar_new(void); 426 | 427 | /** 428 | * purple_http_cookie_jar_ref: 429 | * @cookie_jar: The cookie jar. 430 | * 431 | * Increment the reference count. 432 | */ 433 | void purple_http_cookie_jar_ref(PurpleHttpCookieJar *cookie_jar); 434 | 435 | /** 436 | * purple_http_cookie_jar_unref: 437 | * @cookie_jar: The cookie jar. 438 | * 439 | * Decrement the reference count. 440 | * 441 | * If the reference count reaches zero, the cookie jar will be freed. 442 | * 443 | * Returns: @cookie_jar or %NULL if the reference count reached zero. 444 | */ 445 | PurpleHttpCookieJar * purple_http_cookie_jar_unref( 446 | PurpleHttpCookieJar *cookie_jar); 447 | 448 | /** 449 | * purple_http_cookie_jar_set: 450 | * @cookie_jar: The cookie jar. 451 | * @name: Cookie name. 452 | * @value: Cookie contents. 453 | * 454 | * Sets the cookie. 455 | */ 456 | void purple_http_cookie_jar_set(PurpleHttpCookieJar *cookie_jar, 457 | const gchar *name, const gchar *value); 458 | 459 | /** 460 | * purple_http_cookie_jar_get: 461 | * @cookie_jar: The cookie jar. 462 | * @name: Cookie name. 463 | * 464 | * Gets the cookie. 465 | * 466 | * The result must be g_free'd. 467 | * 468 | * Returns: Cookie contents, or NULL, if cookie doesn't exists. 469 | */ 470 | gchar * purple_http_cookie_jar_get(PurpleHttpCookieJar *cookie_jar, 471 | const gchar *name); 472 | 473 | /** 474 | * purple_http_cookie_jar_is_empty: 475 | * @cookie_jar: The cookie jar. 476 | * 477 | * Checks, if the cookie jar contains any cookies. 478 | * 479 | * Returns: TRUE, if cookie jar contains any cookie, FALSE otherwise. 480 | */ 481 | gboolean purple_http_cookie_jar_is_empty(PurpleHttpCookieJar *cookie_jar); 482 | 483 | 484 | /**************************************************************************/ 485 | /* HTTP Request API */ 486 | /**************************************************************************/ 487 | 488 | /** 489 | * purple_http_request_new: 490 | * @url: The URL to request for, or NULL to leave empty (to be set with 491 | * purple_http_request_set_url). 492 | * 493 | * Creates the new instance of HTTP request configuration. 494 | * 495 | * Returns: The new instance of HTTP request struct. 496 | */ 497 | PurpleHttpRequest * purple_http_request_new(const gchar *url); 498 | 499 | /** 500 | * purple_http_request_ref: 501 | * @request: The request. 502 | * 503 | * Increment the reference count. 504 | */ 505 | void purple_http_request_ref(PurpleHttpRequest *request); 506 | 507 | /** 508 | * purple_http_request_unref: 509 | * @request: The request. 510 | * 511 | * Decrement the reference count. 512 | * 513 | * If the reference count reaches zero, the http request struct will be freed. 514 | * 515 | * Returns: @request or %NULL if the reference count reached zero. 516 | */ 517 | PurpleHttpRequest * purple_http_request_unref(PurpleHttpRequest *request); 518 | 519 | /** 520 | * purple_http_request_set_url: 521 | * @request: The request. 522 | * @url: The url. 523 | * 524 | * Sets URL for HTTP request. 525 | */ 526 | void purple_http_request_set_url(PurpleHttpRequest *request, const gchar *url); 527 | 528 | /** 529 | * purple_http_request_set_url_printf: 530 | * @request: The request. 531 | * @format: The format string. 532 | * 533 | * Constructs and sets an URL for HTTP request. 534 | */ 535 | void purple_http_request_set_url_printf(PurpleHttpRequest *request, 536 | const gchar *format, ...) G_GNUC_PRINTF(2, 3); 537 | 538 | /** 539 | * purple_http_request_get_url: 540 | * @request: The request. 541 | * 542 | * Gets URL set for the HTTP request. 543 | * 544 | * Returns: URL set for this request. 545 | */ 546 | const gchar * purple_http_request_get_url(PurpleHttpRequest *request); 547 | 548 | /** 549 | * purple_http_request_set_method: 550 | * @request: The request. 551 | * @method: The method, or NULL for default. 552 | * 553 | * Sets custom HTTP method used for the request. 554 | */ 555 | void purple_http_request_set_method(PurpleHttpRequest *request, 556 | const gchar *method); 557 | 558 | /** 559 | * purple_http_request_get_method: 560 | * @request: The request. 561 | * 562 | * Gets HTTP method set for the request. 563 | * 564 | * Returns: The method. 565 | */ 566 | const gchar * purple_http_request_get_method(PurpleHttpRequest *request); 567 | 568 | /** 569 | * purple_http_request_set_keepalive_pool: 570 | * @request: The request. 571 | * @pool: The new KeepAlive pool, or NULL to reset. 572 | * 573 | * Sets HTTP KeepAlive connections pool for the request. 574 | * 575 | * It increases pool's reference count. 576 | */ 577 | void 578 | purple_http_request_set_keepalive_pool(PurpleHttpRequest *request, 579 | PurpleHttpKeepalivePool *pool); 580 | 581 | /** 582 | * purple_http_request_get_keepalive_pool: 583 | * @request: The request. 584 | * 585 | * Gets HTTP KeepAlive connections pool associated with the request. 586 | * 587 | * It doesn't affect pool's reference count. 588 | * 589 | * Returns: The KeepAlive pool, used for the request. 590 | */ 591 | PurpleHttpKeepalivePool * 592 | purple_http_request_get_keepalive_pool(PurpleHttpRequest *request); 593 | 594 | /** 595 | * purple_http_request_set_contents: 596 | * @request: The request. 597 | * @contents: The contents. 598 | * @length: The length of contents (-1 if it's a NULL-terminated string) 599 | * 600 | * Sets contents of HTTP request (for example, POST data). 601 | */ 602 | void purple_http_request_set_contents(PurpleHttpRequest *request, 603 | const gchar *contents, int length); 604 | 605 | /** 606 | * purple_http_request_set_contents_reader: 607 | * @request: The request. 608 | * @reader: (scope call): The reader callback. 609 | * @contents_length: The size of all contents. 610 | * @user_data: The user data to pass to the callback function. 611 | * 612 | * Sets contents reader for HTTP request, used mainly for possible large 613 | * uploads. 614 | */ 615 | void purple_http_request_set_contents_reader(PurpleHttpRequest *request, 616 | PurpleHttpContentReader reader, int contents_length, gpointer user_data); 617 | 618 | /** 619 | * purple_http_request_set_response_writer: 620 | * @request: The request. 621 | * @writer: (scope call): The writer callback, or %NULL to remove existing. 622 | * @user_data: The user data to pass to the callback function. 623 | * 624 | * Set contents writer for HTTP response. 625 | */ 626 | void purple_http_request_set_response_writer(PurpleHttpRequest *request, 627 | PurpleHttpContentWriter writer, gpointer user_data); 628 | 629 | /** 630 | * purple_http_request_set_timeout: 631 | * @request: The request. 632 | * @timeout: Time (in seconds) after that timeout will be cancelled, 633 | * -1 for infinite time. 634 | * 635 | * Set maximum amount of time, that request is allowed to run. 636 | */ 637 | void purple_http_request_set_timeout(PurpleHttpRequest *request, int timeout); 638 | 639 | /** 640 | * purple_http_request_get_timeout: 641 | * @request: The request. 642 | * 643 | * Get maximum amount of time, that request is allowed to run. 644 | * 645 | * Returns: Timeout currently set (-1 for infinite). 646 | */ 647 | int purple_http_request_get_timeout(PurpleHttpRequest *request); 648 | 649 | /** 650 | * purple_http_request_set_max_redirects: 651 | * @request: The request. 652 | * @max_redirects: Maximum amount of redirects, or -1 for unlimited. 653 | * 654 | * Sets maximum amount of redirects. 655 | */ 656 | void purple_http_request_set_max_redirects(PurpleHttpRequest *request, 657 | int max_redirects); 658 | 659 | /** 660 | * purple_http_request_get_max_redirects: 661 | * @request: The request. 662 | * 663 | * Gets maximum amount of redirects. 664 | * 665 | * Returns: Current maximum amount of redirects (-1 for unlimited). 666 | */ 667 | int purple_http_request_get_max_redirects(PurpleHttpRequest *request); 668 | 669 | /** 670 | * purple_http_request_set_cookie_jar: 671 | * @request: The request. 672 | * @cookie_jar: The cookie jar. 673 | * 674 | * Sets cookie jar used for the request. 675 | */ 676 | void purple_http_request_set_cookie_jar(PurpleHttpRequest *request, 677 | PurpleHttpCookieJar *cookie_jar); 678 | 679 | /** 680 | * purple_http_request_get_cookie_jar: 681 | * @request: The request. 682 | * 683 | * Gets cookie jar used for the request. 684 | * 685 | * Returns: The cookie jar. 686 | */ 687 | PurpleHttpCookieJar * purple_http_request_get_cookie_jar( 688 | PurpleHttpRequest *request); 689 | 690 | /** 691 | * purple_http_request_set_http11: 692 | * @request: The request. 693 | * @http11: TRUE for HTTP/1.1, FALSE for HTTP/1.0. 694 | * 695 | * Sets HTTP version to use. 696 | */ 697 | void purple_http_request_set_http11(PurpleHttpRequest *request, 698 | gboolean http11); 699 | 700 | /** 701 | * purple_http_request_is_http11: 702 | * @request: The request. 703 | * 704 | * Gets used HTTP version. 705 | * 706 | * Returns: TRUE, if we use HTTP/1.1, FALSE for HTTP/1.0. 707 | */ 708 | gboolean purple_http_request_is_http11(PurpleHttpRequest *request); 709 | 710 | /** 711 | * purple_http_request_set_max_len: 712 | * @request: The request. 713 | * @max_len: Maximum length of response to read (-1 for the maximum 714 | * supported amount). 715 | * 716 | * Sets maximum length of response content to read. 717 | * 718 | * Headers length doesn't count here. 719 | * 720 | */ 721 | void purple_http_request_set_max_len(PurpleHttpRequest *request, int max_len); 722 | 723 | /** 724 | * purple_http_request_get_max_len: 725 | * @request: The request. 726 | * 727 | * Gets maximum length of response content to read. 728 | * 729 | * Returns: Maximum length of response to read, or -1 if unlimited. 730 | */ 731 | int purple_http_request_get_max_len(PurpleHttpRequest *request); 732 | 733 | /** 734 | * purple_http_request_header_set: 735 | * @request: The request. 736 | * @key: A header to be set. 737 | * @value: A value to set, or NULL to remove specified header. 738 | * 739 | * Sets (replaces, if exists) specified HTTP request header with provided value. 740 | * 741 | * See purple_http_request_header_add(). 742 | */ 743 | void purple_http_request_header_set(PurpleHttpRequest *request, 744 | const gchar *key, const gchar *value); 745 | 746 | /** 747 | * purple_http_request_header_set_printf: 748 | * @request: The request. 749 | * @key: A header to be set. 750 | * @format: The format string. 751 | * 752 | * Constructs and sets (replaces, if exists) specified HTTP request header. 753 | */ 754 | void purple_http_request_header_set_printf(PurpleHttpRequest *request, 755 | const gchar *key, const gchar *format, ...) G_GNUC_PRINTF(3, 4); 756 | 757 | /** 758 | * purple_http_request_header_add: 759 | * @key: A header to be set. 760 | * @value: A value to set. 761 | * 762 | * Adds (without replacing, if exists) an HTTP request header. 763 | * 764 | * See purple_http_request_header_set(). 765 | */ 766 | void purple_http_request_header_add(PurpleHttpRequest *request, 767 | const gchar *key, const gchar *value); 768 | 769 | 770 | /**************************************************************************/ 771 | /* HTTP Keep-Alive pool API */ 772 | /**************************************************************************/ 773 | 774 | /** 775 | * purple_http_keepalive_pool_new: 776 | * 777 | * Creates a new HTTP Keep-Alive pool. 778 | */ 779 | PurpleHttpKeepalivePool * 780 | purple_http_keepalive_pool_new(void); 781 | 782 | /** 783 | * purple_http_keepalive_pool_ref: 784 | * @pool: The HTTP Keep-Alive pool. 785 | * 786 | * Increment the reference count. 787 | */ 788 | void 789 | purple_http_keepalive_pool_ref(PurpleHttpKeepalivePool *pool); 790 | 791 | /** 792 | * purple_http_keepalive_pool_unref: 793 | * @pool: The HTTP Keep-Alive pool. 794 | * 795 | * Decrement the reference count. 796 | * 797 | * If the reference count reaches zero, the pool will be freed and all 798 | * connections will be closed. 799 | * 800 | * Returns: @pool or %NULL if the reference count reached zero. 801 | */ 802 | PurpleHttpKeepalivePool * 803 | purple_http_keepalive_pool_unref(PurpleHttpKeepalivePool *pool); 804 | 805 | /** 806 | * purple_http_keepalive_pool_set_limit_per_host: 807 | * @pool: The HTTP Keep-Alive pool. 808 | * @limit: The new limit, 0 for unlimited. 809 | * 810 | * Sets maximum allowed number of connections to specific host-triple (is_ssl + 811 | * hostname + port). 812 | */ 813 | void 814 | purple_http_keepalive_pool_set_limit_per_host(PurpleHttpKeepalivePool *pool, 815 | guint limit); 816 | 817 | /** 818 | * purple_http_keepalive_pool_get_limit_per_host: 819 | * @pool: The HTTP Keep-Alive pool. 820 | * 821 | * Gets maximum allowed number of connections to specific host-triple (is_ssl + 822 | * hostname + port). 823 | * 824 | * Returns: The limit. 825 | */ 826 | guint 827 | purple_http_keepalive_pool_get_limit_per_host(PurpleHttpKeepalivePool *pool); 828 | 829 | 830 | /**************************************************************************/ 831 | /* HTTP connection set API */ 832 | /**************************************************************************/ 833 | 834 | PurpleHttpConnectionSet * 835 | purple_http_connection_set_new(void); 836 | 837 | void 838 | purple_http_connection_set_destroy(PurpleHttpConnectionSet *set); 839 | 840 | void 841 | purple_http_connection_set_add(PurpleHttpConnectionSet *set, 842 | PurpleHttpConnection *http_conn); 843 | 844 | 845 | /**************************************************************************/ 846 | /* HTTP response API */ 847 | /**************************************************************************/ 848 | 849 | /** 850 | * purple_http_response_is_successful: 851 | * @response: The response. 852 | * 853 | * Checks, if HTTP request was performed successfully. 854 | * 855 | * Returns: TRUE, if request was performed successfully. 856 | */ 857 | gboolean purple_http_response_is_successful(PurpleHttpResponse *response); 858 | 859 | /** 860 | * purple_http_response_get_code: 861 | * @response: The response. 862 | * 863 | * Gets HTTP response code. 864 | * 865 | * Returns: HTTP response code. 866 | */ 867 | int purple_http_response_get_code(PurpleHttpResponse *response); 868 | 869 | /** 870 | * purple_http_response_get_error: 871 | * @response: The response. 872 | * 873 | * Gets error description. 874 | * 875 | * Returns: Localized error description or NULL, if there was no error. 876 | */ 877 | const gchar * purple_http_response_get_error(PurpleHttpResponse *response); 878 | 879 | /** 880 | * purple_http_response_get_data_len: 881 | * @response: The response. 882 | * 883 | * Gets HTTP response data length. 884 | * 885 | * Returns: Data length; 886 | */ 887 | gsize purple_http_response_get_data_len(PurpleHttpResponse *response); 888 | 889 | /** 890 | * purple_http_response_get_data: 891 | * @response: The response. 892 | * @len: Return address for the size of the data. Can be NULL. 893 | * 894 | * Gets HTTP response data. 895 | * 896 | * Response data is not written, if writer callback was set for request. 897 | * 898 | * Returns: The data. 899 | */ 900 | const gchar * purple_http_response_get_data(PurpleHttpResponse *response, size_t *len); 901 | 902 | /** 903 | * purple_http_response_get_all_headers: 904 | * @response: The response. 905 | * 906 | * Gets all headers got with response. 907 | * 908 | * Returns: GList of PurpleKeyValuePair, which keys are header field 909 | * names (gchar*) and values are its contents (gchar*). 910 | */ 911 | const GList * purple_http_response_get_all_headers(PurpleHttpResponse *response); 912 | 913 | /** 914 | * purple_http_response_get_headers_by_name: 915 | * @response: The response. 916 | * @name: The name of header field. 917 | * 918 | * Gets all headers with specified name got with response. 919 | * 920 | * Returns: GList of header field records contents (gchar*). 921 | */ 922 | const GList * purple_http_response_get_headers_by_name( 923 | PurpleHttpResponse *response, const gchar *name); 924 | 925 | /** 926 | * purple_http_response_get_header: 927 | * @response: The response. 928 | * @name: The name of header field. 929 | * 930 | * Gets one header contents with specified name got with response. 931 | * 932 | * To get all headers with the same name, use 933 | * purple_http_response_get_headers_by_name instead. 934 | * 935 | * Returns: Header field contents or NULL, if there is no such one. 936 | */ 937 | const gchar * purple_http_response_get_header(PurpleHttpResponse *response, 938 | const gchar *name); 939 | 940 | 941 | /**************************************************************************/ 942 | /* HTTP Subsystem */ 943 | /**************************************************************************/ 944 | 945 | /** 946 | * purple_http_init: 947 | * 948 | * Initializes the http subsystem. 949 | */ 950 | void purple_http_init(void); 951 | 952 | /** 953 | * purple_http_uninit: 954 | * 955 | * Uninitializes the http subsystem. 956 | */ 957 | void purple_http_uninit(void); 958 | 959 | G_END_DECLS 960 | 961 | #endif /* _PURPLE_HTTP_H_ */ 962 | -------------------------------------------------------------------------------- /purple2compat/image-store.h: -------------------------------------------------------------------------------- 1 | #ifndef _IMAGE_STORE_H_ 2 | #define _IMAGE_STORE_H_ 3 | 4 | #include "imgstore.h" 5 | #include "image.h" 6 | 7 | 8 | #define purple_image_store_add(img) purple_imgstore_add_with_id( \ 9 | g_memdup(purple_imgstore_get_data(img), purple_imgstore_get_size(img)), \ 10 | purple_imgstore_get_size(img), purple_imgstore_get_filename(img)) 11 | #define purple_image_store_get purple_imgstore_find_by_id 12 | 13 | 14 | #endif /*_IMAGE_STORE_H_*/ 15 | -------------------------------------------------------------------------------- /purple2compat/image.h: -------------------------------------------------------------------------------- 1 | #ifndef _IMAGE_H_ 2 | #define _IMAGE_H_ 3 | 4 | #include "imgstore.h" 5 | 6 | #define PurpleImage PurpleStoredImage 7 | #define purple_image_new_from_file(p, e) purple_imgstore_new_from_file(p) 8 | #define purple_image_new_from_data(d, l) purple_imgstore_add(d, l, NULL) 9 | #define purple_image_get_path purple_imgstore_get_filename 10 | #define purple_image_get_data_size purple_imgstore_get_size 11 | #define purple_image_get_data purple_imgstore_get_data 12 | #define purple_image_get_extension purple_imgstore_get_extension 13 | 14 | #endif /* _IMAGE_H_ */ 15 | -------------------------------------------------------------------------------- /purple2compat/internal.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #ifdef _WIN32 6 | #include "win32/win32dep.h" 7 | #endif 8 | 9 | #include "../purplecompat.h" 10 | 11 | #ifndef N_ 12 | # define N_(a) (a) 13 | #endif 14 | 15 | #ifndef _ 16 | # define _(a) (a) 17 | #endif 18 | -------------------------------------------------------------------------------- /purple2compat/plugins.h: -------------------------------------------------------------------------------- 1 | #include "plugin.h" 2 | -------------------------------------------------------------------------------- /purple2compat/purple-socket.c: -------------------------------------------------------------------------------- 1 | /* purple 2 | * 3 | * Purple is the legal property of its developers, whose names are too numerous 4 | * to list here. Please refer to the COPYRIGHT file distributed with this 5 | * source distribution. 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 20 | */ 21 | 22 | #include "purple-socket.h" 23 | 24 | #ifndef _WIN32 25 | #include 26 | #include 27 | #endif 28 | 29 | #include "internal.h" 30 | 31 | #include "debug.h" 32 | #include "proxy.h" 33 | #include "sslconn.h" 34 | 35 | typedef enum { 36 | PURPLE_SOCKET_STATE_DISCONNECTED = 0, 37 | PURPLE_SOCKET_STATE_CONNECTING, 38 | PURPLE_SOCKET_STATE_CONNECTED, 39 | PURPLE_SOCKET_STATE_ERROR 40 | } PurpleSocketState; 41 | 42 | struct _PurpleSocket 43 | { 44 | PurpleConnection *gc; 45 | gchar *host; 46 | int port; 47 | gboolean is_tls; 48 | GHashTable *data; 49 | 50 | PurpleSocketState state; 51 | 52 | PurpleSslConnection *tls_connection; 53 | PurpleProxyConnectData *raw_connection; 54 | int fd; 55 | guint inpa; 56 | 57 | PurpleSocketConnectCb cb; 58 | gpointer cb_data; 59 | }; 60 | 61 | static GHashTable *handles = NULL; 62 | 63 | static void 64 | handle_add(PurpleSocket *ps) 65 | { 66 | PurpleConnection *gc = ps->gc; 67 | GSList *l; 68 | 69 | l = g_hash_table_lookup(handles, gc); 70 | l = g_slist_prepend(l, ps); 71 | g_hash_table_insert(handles, gc, l); 72 | } 73 | 74 | static void 75 | handle_remove(PurpleSocket *ps) 76 | { 77 | PurpleConnection *gc = ps->gc; 78 | GSList *l; 79 | 80 | l = g_hash_table_lookup(handles, gc); 81 | if (l != NULL) { 82 | l = g_slist_remove(l, ps); 83 | g_hash_table_insert(handles, gc, l); 84 | } 85 | } 86 | 87 | void 88 | _purple_socket_init(void) 89 | { 90 | handles = g_hash_table_new(g_direct_hash, g_direct_equal); 91 | } 92 | 93 | void 94 | _purple_socket_uninit(void) 95 | { 96 | g_hash_table_destroy(handles); 97 | handles = NULL; 98 | } 99 | 100 | PurpleSocket * 101 | purple_socket_new(PurpleConnection *gc) 102 | { 103 | PurpleSocket *ps = g_new0(PurpleSocket, 1); 104 | 105 | ps->gc = gc; 106 | ps->fd = -1; 107 | ps->port = -1; 108 | ps->data = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); 109 | 110 | handle_add(ps); 111 | 112 | return ps; 113 | } 114 | 115 | PurpleConnection * 116 | purple_socket_get_connection(PurpleSocket *ps) 117 | { 118 | g_return_val_if_fail(ps != NULL, NULL); 119 | 120 | return ps->gc; 121 | } 122 | 123 | static gboolean 124 | purple_socket_check_state(PurpleSocket *ps, PurpleSocketState wanted_state) 125 | { 126 | g_return_val_if_fail(ps != NULL, FALSE); 127 | 128 | if (ps->state == wanted_state) 129 | return TRUE; 130 | 131 | purple_debug_error("socket", "invalid state: %d (should be: %d)", 132 | ps->state, wanted_state); 133 | ps->state = PURPLE_SOCKET_STATE_ERROR; 134 | return FALSE; 135 | } 136 | 137 | void 138 | purple_socket_set_tls(PurpleSocket *ps, gboolean is_tls) 139 | { 140 | g_return_if_fail(ps != NULL); 141 | 142 | if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED)) 143 | return; 144 | 145 | ps->is_tls = is_tls; 146 | } 147 | 148 | void 149 | purple_socket_set_host(PurpleSocket *ps, const gchar *host) 150 | { 151 | g_return_if_fail(ps != NULL); 152 | 153 | if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED)) 154 | return; 155 | 156 | g_free(ps->host); 157 | ps->host = g_strdup(host); 158 | } 159 | 160 | void 161 | purple_socket_set_port(PurpleSocket *ps, int port) 162 | { 163 | g_return_if_fail(ps != NULL); 164 | g_return_if_fail(port >= 0); 165 | g_return_if_fail(port <= 65535); 166 | 167 | if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED)) 168 | return; 169 | 170 | ps->port = port; 171 | } 172 | 173 | static void 174 | _purple_socket_connected_raw(gpointer _ps, gint fd, const gchar *error_message) 175 | { 176 | PurpleSocket *ps = _ps; 177 | 178 | ps->raw_connection = NULL; 179 | 180 | if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTING)) { 181 | if (fd > 0) 182 | close(fd); 183 | ps->cb(ps, _("Invalid socket state"), ps->cb_data); 184 | return; 185 | } 186 | 187 | if (fd <= 0 || error_message != NULL) { 188 | if (error_message == NULL) 189 | error_message = _("Unknown error"); 190 | ps->fd = -1; 191 | ps->state = PURPLE_SOCKET_STATE_ERROR; 192 | ps->cb(ps, error_message, ps->cb_data); 193 | return; 194 | } 195 | 196 | ps->state = PURPLE_SOCKET_STATE_CONNECTED; 197 | ps->fd = fd; 198 | ps->cb(ps, NULL, ps->cb_data); 199 | } 200 | 201 | static void 202 | _purple_socket_connected_tls(gpointer _ps, PurpleSslConnection *tls_connection, 203 | PurpleInputCondition cond) 204 | { 205 | PurpleSocket *ps = _ps; 206 | 207 | if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTING)) { 208 | purple_ssl_close(tls_connection); 209 | ps->tls_connection = NULL; 210 | ps->cb(ps, _("Invalid socket state"), ps->cb_data); 211 | return; 212 | } 213 | 214 | if (ps->tls_connection->fd <= 0) { 215 | ps->state = PURPLE_SOCKET_STATE_ERROR; 216 | purple_ssl_close(tls_connection); 217 | ps->tls_connection = NULL; 218 | ps->cb(ps, _("Invalid file descriptor"), ps->cb_data); 219 | return; 220 | } 221 | 222 | ps->state = PURPLE_SOCKET_STATE_CONNECTED; 223 | ps->fd = ps->tls_connection->fd; 224 | ps->cb(ps, NULL, ps->cb_data); 225 | } 226 | 227 | static void 228 | _purple_socket_connected_tls_error(PurpleSslConnection *ssl_connection, 229 | PurpleSslErrorType error, gpointer _ps) 230 | { 231 | PurpleSocket *ps = _ps; 232 | 233 | ps->state = PURPLE_SOCKET_STATE_ERROR; 234 | ps->tls_connection = NULL; 235 | ps->cb(ps, purple_ssl_strerror(error), ps->cb_data); 236 | } 237 | 238 | gboolean 239 | purple_socket_connect(PurpleSocket *ps, PurpleSocketConnectCb cb, 240 | gpointer user_data) 241 | { 242 | PurpleAccount *account = NULL; 243 | 244 | g_return_val_if_fail(ps != NULL, FALSE); 245 | 246 | if (ps->gc && purple_connection_is_disconnecting(ps->gc)) { 247 | purple_debug_error("socket", "connection is being destroyed"); 248 | ps->state = PURPLE_SOCKET_STATE_ERROR; 249 | return FALSE; 250 | } 251 | 252 | if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED)) 253 | return FALSE; 254 | ps->state = PURPLE_SOCKET_STATE_CONNECTING; 255 | 256 | if (ps->host == NULL || ps->port < 0) { 257 | purple_debug_error("socket", "Host or port is not specified"); 258 | ps->state = PURPLE_SOCKET_STATE_ERROR; 259 | return FALSE; 260 | } 261 | 262 | if (ps->gc != NULL) 263 | account = purple_connection_get_account(ps->gc); 264 | 265 | ps->cb = cb; 266 | ps->cb_data = user_data; 267 | 268 | if (ps->is_tls) { 269 | ps->tls_connection = purple_ssl_connect(account, ps->host, 270 | ps->port, _purple_socket_connected_tls, 271 | _purple_socket_connected_tls_error, ps); 272 | } else { 273 | ps->raw_connection = purple_proxy_connect(ps->gc, account, 274 | ps->host, ps->port, _purple_socket_connected_raw, ps); 275 | } 276 | 277 | if (ps->tls_connection == NULL && 278 | ps->raw_connection == NULL) 279 | { 280 | ps->state = PURPLE_SOCKET_STATE_ERROR; 281 | return FALSE; 282 | } 283 | 284 | return TRUE; 285 | } 286 | 287 | gssize 288 | purple_socket_read(PurpleSocket *ps, guchar *buf, size_t len) 289 | { 290 | g_return_val_if_fail(ps != NULL, -1); 291 | g_return_val_if_fail(buf != NULL, -1); 292 | 293 | if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTED)) 294 | return -1; 295 | 296 | if (ps->is_tls) 297 | return purple_ssl_read(ps->tls_connection, buf, len); 298 | else 299 | return read(ps->fd, buf, len); 300 | } 301 | 302 | gssize 303 | purple_socket_write(PurpleSocket *ps, const guchar *buf, size_t len) 304 | { 305 | g_return_val_if_fail(ps != NULL, -1); 306 | g_return_val_if_fail(buf != NULL, -1); 307 | 308 | if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTED)) 309 | return -1; 310 | 311 | if (ps->is_tls) 312 | return purple_ssl_write(ps->tls_connection, buf, len); 313 | else 314 | return write(ps->fd, buf, len); 315 | } 316 | 317 | void 318 | purple_socket_watch(PurpleSocket *ps, PurpleInputCondition cond, 319 | PurpleInputFunction func, gpointer user_data) 320 | { 321 | g_return_if_fail(ps != NULL); 322 | 323 | if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTED)) 324 | return; 325 | 326 | if (ps->inpa > 0) 327 | purple_input_remove(ps->inpa); 328 | ps->inpa = 0; 329 | 330 | g_return_if_fail(ps->fd > 0); 331 | 332 | if (func != NULL) 333 | ps->inpa = purple_input_add(ps->fd, cond, func, user_data); 334 | } 335 | 336 | int 337 | purple_socket_get_fd(PurpleSocket *ps) 338 | { 339 | g_return_val_if_fail(ps != NULL, -1); 340 | 341 | if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTED)) 342 | return -1; 343 | 344 | g_return_val_if_fail(ps->fd > 0, -1); 345 | 346 | return ps->fd; 347 | } 348 | 349 | void 350 | purple_socket_set_data(PurpleSocket *ps, const gchar *key, gpointer data) 351 | { 352 | g_return_if_fail(ps != NULL); 353 | g_return_if_fail(key != NULL); 354 | 355 | if (data == NULL) 356 | g_hash_table_remove(ps->data, key); 357 | else 358 | g_hash_table_insert(ps->data, g_strdup(key), data); 359 | } 360 | 361 | gpointer 362 | purple_socket_get_data(PurpleSocket *ps, const gchar *key) 363 | { 364 | g_return_val_if_fail(ps != NULL, NULL); 365 | g_return_val_if_fail(key != NULL, NULL); 366 | 367 | return g_hash_table_lookup(ps->data, key); 368 | } 369 | 370 | static void 371 | purple_socket_cancel(PurpleSocket *ps) 372 | { 373 | if (ps->inpa > 0) 374 | purple_input_remove(ps->inpa); 375 | ps->inpa = 0; 376 | 377 | if (ps->tls_connection != NULL) { 378 | purple_ssl_close(ps->tls_connection); 379 | ps->fd = -1; 380 | } 381 | ps->tls_connection = NULL; 382 | 383 | if (ps->raw_connection != NULL) 384 | purple_proxy_connect_cancel(ps->raw_connection); 385 | ps->raw_connection = NULL; 386 | 387 | if (ps->fd > 0) 388 | close(ps->fd); 389 | ps->fd = 0; 390 | } 391 | 392 | void 393 | purple_socket_destroy(PurpleSocket *ps) 394 | { 395 | if (ps == NULL) 396 | return; 397 | 398 | handle_remove(ps); 399 | 400 | purple_socket_cancel(ps); 401 | 402 | g_free(ps->host); 403 | g_hash_table_destroy(ps->data); 404 | g_free(ps); 405 | } 406 | 407 | void 408 | _purple_socket_cancel_with_connection(PurpleConnection *gc) 409 | { 410 | GSList *it; 411 | 412 | it = g_hash_table_lookup(handles, gc); 413 | for (; it; it = g_slist_next(it)) { 414 | PurpleSocket *ps = it->data; 415 | purple_socket_cancel(ps); 416 | } 417 | } 418 | -------------------------------------------------------------------------------- /purple2compat/purple-socket.h: -------------------------------------------------------------------------------- 1 | /* purple 2 | * 3 | * Purple is the legal property of its developers, whose names are too numerous 4 | * to list here. Please refer to the COPYRIGHT file distributed with this 5 | * source distribution. 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 20 | */ 21 | 22 | #ifndef _PURPLE_SOCKET_H_ 23 | #define _PURPLE_SOCKET_H_ 24 | /** 25 | * SECTION:purple-socket 26 | * @section_id: libpurple-purple-socket 27 | * @short_description: purple-socket.h 28 | * @title: Generic Sockets 29 | */ 30 | 31 | #include "connection.h" 32 | 33 | /** 34 | * PurpleSocket: 35 | * 36 | * A structure holding all resources needed for the TCP connection. 37 | */ 38 | typedef struct _PurpleSocket PurpleSocket; 39 | 40 | /** 41 | * PurpleSocketConnectCb: 42 | * @ps: The socket. 43 | * @error: Error message, or NULL if connection was successful. 44 | * @user_data: The user data passed with callback function. 45 | * 46 | * A callback fired after (successfully or not) establishing a connection. 47 | */ 48 | typedef void (*PurpleSocketConnectCb)(PurpleSocket *ps, const gchar *error, 49 | gpointer user_data); 50 | 51 | /** 52 | * purple_socket_new: 53 | * @gc: The connection for which the socket is needed, or NULL. 54 | * 55 | * Creates new, disconnected socket. 56 | * 57 | * Passing a PurpleConnection allows for proper proxy handling. 58 | * 59 | * Returns: The new socket struct. 60 | */ 61 | PurpleSocket * 62 | purple_socket_new(PurpleConnection *gc); 63 | 64 | /** 65 | * purple_socket_get_connection: 66 | * @ps: The socket. 67 | * 68 | * Gets PurpleConnection tied with specified socket. 69 | * 70 | * Returns: The PurpleConnection object. 71 | */ 72 | PurpleConnection * 73 | purple_socket_get_connection(PurpleSocket *ps); 74 | 75 | /** 76 | * purple_socket_set_tls: 77 | * @ps: The socket. 78 | * @is_tls: TRUE, if TLS should be handled transparently, FALSE otherwise. 79 | * 80 | * Determines, if socket should handle TLS. 81 | */ 82 | void 83 | purple_socket_set_tls(PurpleSocket *ps, gboolean is_tls); 84 | 85 | /** 86 | * purple_socket_set_host: 87 | * @ps: The socket. 88 | * @host: The connection host. 89 | * 90 | * Sets connection host. 91 | */ 92 | void 93 | purple_socket_set_host(PurpleSocket *ps, const gchar *host); 94 | 95 | /** 96 | * purple_socket_set_port: 97 | * @ps: The socket. 98 | * @port: The connection port. 99 | * 100 | * Sets connection port. 101 | */ 102 | void 103 | purple_socket_set_port(PurpleSocket *ps, int port); 104 | 105 | /** 106 | * purple_socket_connect: 107 | * @ps: The socket. 108 | * @cb: The function to call after establishing a connection, or on 109 | * error. 110 | * @user_data: The user data to be passed to callback function. 111 | * 112 | * Establishes a connection. 113 | * 114 | * Returns: TRUE on success (this doesn't mean it's connected yet), FALSE 115 | * otherwise. 116 | */ 117 | gboolean 118 | purple_socket_connect(PurpleSocket *ps, PurpleSocketConnectCb cb, 119 | gpointer user_data); 120 | 121 | /** 122 | * purple_socket_read: 123 | * @ps: The socket. 124 | * @buf: The buffer to write data to. 125 | * @len: The buffer size. 126 | * 127 | * Reads incoming data from socket. 128 | * 129 | * This function deals with TLS, if the socket is configured to do it. 130 | * 131 | * Returns: Amount of data written, or -1 on error (errno will be also be set). 132 | */ 133 | gssize 134 | purple_socket_read(PurpleSocket *ps, guchar *buf, size_t len); 135 | 136 | /** 137 | * purple_socket_write: 138 | * @ps: The socket. 139 | * @buf: The buffer to read data from. 140 | * @len: The amount of data to read and send. 141 | * 142 | * Sends data through socket. 143 | * 144 | * This function deals with TLS, if the socket is configured to do it. 145 | * 146 | * Returns: Amount of data sent, or -1 on error (errno will albo be set). 147 | */ 148 | gssize 149 | purple_socket_write(PurpleSocket *ps, const guchar *buf, size_t len); 150 | 151 | /** 152 | * purple_socket_watch: 153 | * @ps: The socket. 154 | * @cond: The condition type. 155 | * @func: The callback function for data, or NULL to remove any 156 | * existing callbacks. 157 | * @user_data: The user data to be passed to callback function. 158 | * 159 | * Adds an input handler for the socket. 160 | * 161 | * If the specified socket had input handler already registered, it will be 162 | * removed. To remove any input handlers, pass an NULL handler function. 163 | */ 164 | void 165 | purple_socket_watch(PurpleSocket *ps, PurpleInputCondition cond, 166 | PurpleInputFunction func, gpointer user_data); 167 | 168 | /** 169 | * purple_socket_get_fd: 170 | * @ps: The socket 171 | * 172 | * Gets underlying file descriptor for socket. 173 | * 174 | * It's not meant to read/write data (use purple_socket_read/ 175 | * purple_socket_write), rather for watching for changes with select(). 176 | * 177 | * Returns: The file descriptor, or -1 on error. 178 | */ 179 | int 180 | purple_socket_get_fd(PurpleSocket *ps); 181 | 182 | /** 183 | * purple_socket_set_data: 184 | * @ps: The socket. 185 | * @key: The unique key. 186 | * @data: The data to assign, or NULL to remove. 187 | * 188 | * Sets extra data for a socket. 189 | */ 190 | void 191 | purple_socket_set_data(PurpleSocket *ps, const gchar *key, gpointer data); 192 | 193 | /** 194 | * purple_socket_get_data: 195 | * @ps: The socket. 196 | * @key: The unqiue key. 197 | * 198 | * Returns extra data in a socket. 199 | * 200 | * Returns: The data associated with the key. 201 | */ 202 | gpointer 203 | purple_socket_get_data(PurpleSocket *ps, const gchar *key); 204 | 205 | /** 206 | * purple_socket_destroy: 207 | * @ps: The socket. 208 | * 209 | * Destroys the socket, closes connection and frees all resources. 210 | * 211 | * If file descriptor for the socket was extracted with purple_socket_get_fd and 212 | * added to event loop, it have to be removed prior this. 213 | */ 214 | void 215 | purple_socket_destroy(PurpleSocket *ps); 216 | 217 | #endif /* _PURPLE_SOCKET_H_ */ 218 | -------------------------------------------------------------------------------- /purplecompat.h: -------------------------------------------------------------------------------- 1 | #ifndef _PURPLECOMPAT_H_ 2 | #define _PURPLECOMPAT_H_ 3 | 4 | #include 5 | #include "version.h" 6 | 7 | #if PURPLE_VERSION_CHECK(3, 0, 0) 8 | #include 9 | 10 | #define purple_conversation_set_data(conv, key, value) g_object_set_data(G_OBJECT(conv), key, value) 11 | #define purple_conversation_get_data(conv, key) g_object_get_data(G_OBJECT(conv), key) 12 | 13 | #define purple_circular_buffer_destroy g_object_unref 14 | #define purple_hash_destroy g_object_unref 15 | #define purple_message_destroy g_object_unref 16 | #define purple_buddy_destroy g_object_unref 17 | 18 | #define PURPLE_TYPE_STRING G_TYPE_STRING 19 | 20 | #define purple_protocol_action_get_connection(action) ((action)->connection) 21 | 22 | #define purple_chat_user_set_alias(cb, alias) g_object_set((cb), "alias", (alias), NULL) 23 | #define purple_chat_get_alias(chat) g_object_get_data(G_OBJECT(chat), "alias") 24 | 25 | //TODO remove this when dx adds this to the PurpleMessageFlags enum 26 | #define PURPLE_MESSAGE_REMOTE_SEND 0x10000 27 | 28 | #else /*!PURPLE_VERSION_CHECK(3, 0, 0)*/ 29 | 30 | #include "connection.h" 31 | 32 | #define purple_blist_find_buddy purple_find_buddy 33 | #define purple_blist_find_buddies purple_find_buddies 34 | #define purple_blist_find_group purple_find_group 35 | #define PURPLE_IS_BUDDY PURPLE_BLIST_NODE_IS_BUDDY 36 | #define PURPLE_IS_CHAT PURPLE_BLIST_NODE_IS_CHAT 37 | #define purple_chat_get_name_only purple_chat_get_name 38 | #define purple_chat_set_alias purple_blist_alias_chat 39 | #define purple_chat_get_alias(chat) ((chat)->alias) 40 | #define purple_buddy_set_server_alias purple_blist_server_alias_buddy 41 | static inline void 42 | purple_blist_node_set_transient(PurpleBlistNode *node, gboolean transient) 43 | { 44 | PurpleBlistNodeFlags old_flags = purple_blist_node_get_flags(node); 45 | PurpleBlistNodeFlags new_flags; 46 | 47 | if (transient) 48 | new_flags = old_flags | PURPLE_BLIST_NODE_FLAG_NO_SAVE; 49 | else 50 | new_flags = old_flags & ~PURPLE_BLIST_NODE_FLAG_NO_SAVE; 51 | 52 | purple_blist_node_set_flags(node, new_flags); 53 | } 54 | 55 | #define PURPLE_CMD_FLAG_PROTOCOL_ONLY PURPLE_CMD_FLAG_PRPL_ONLY 56 | 57 | #define PURPLE_TYPE_CONNECTION purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION) 58 | #define PURPLE_IS_CONNECTION PURPLE_CONNECTION_IS_VALID 59 | 60 | #define PURPLE_CONNECTION_DISCONNECTED PURPLE_DISCONNECTED 61 | #define PURPLE_CONNECTION_CONNECTING PURPLE_CONNECTING 62 | #define PURPLE_CONNECTION_CONNECTED PURPLE_CONNECTED 63 | #define PURPLE_CONNECTION_FLAG_HTML PURPLE_CONNECTION_HTML 64 | #define PURPLE_CONNECTION_FLAG_NO_BGCOLOR PURPLE_CONNECTION_NO_BGCOLOR 65 | #define PURPLE_CONNECTION_FLAG_NO_FONTSIZE PURPLE_CONNECTION_NO_FONTSIZE 66 | #define PURPLE_CONNECTION_FLAG_NO_IMAGES PURPLE_CONNECTION_NO_IMAGES 67 | 68 | #define purple_request_cpar_from_connection(a) purple_connection_get_account(a), NULL, NULL 69 | #define purple_connection_get_protocol purple_connection_get_prpl 70 | #define purple_connection_error purple_connection_error_reason 71 | #define purple_connection_is_disconnecting(c) FALSE 72 | #define purple_connection_set_flags(pc, f) ((pc)->flags = (f)) 73 | #define purple_connection_get_flags(pc) ((pc)->flags) 74 | 75 | #define PurpleConversationUpdateType PurpleConvUpdateType 76 | #define PURPLE_CONVERSATION_UPDATE_TOPIC PURPLE_CONV_UPDATE_TOPIC 77 | #define PURPLE_CONVERSATION_UPDATE_UNSEEN PURPLE_CONV_UPDATE_UNSEEN 78 | #define PurpleChatConversation PurpleConvChat 79 | #define PurpleIMConversation PurpleConvIm 80 | #define purple_conversations_find_chat_with_account(id, account) \ 81 | PURPLE_CONV_CHAT(purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, id, account)) 82 | #define purple_conversations_find_chat(pc, id) PURPLE_CONV_CHAT(purple_find_chat(pc, id)) 83 | #define purple_conversations_get_all purple_get_conversations 84 | #define purple_conversation_get_connection purple_conversation_get_gc 85 | #define purple_chat_conversation_get_id purple_conv_chat_get_id 86 | #define purple_conversations_find_im_with_account(name, account) \ 87 | PURPLE_CONV_IM(purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name, account)) 88 | #define purple_im_conversation_new(account, from) PURPLE_CONV_IM(purple_conversation_new(PURPLE_CONV_TYPE_IM, account, from)) 89 | #define PURPLE_CONVERSATION(chatorim) ((chatorim) == NULL ? NULL : (chatorim)->conv) 90 | #define PURPLE_IM_CONVERSATION(conv) PURPLE_CONV_IM(conv) 91 | #define PURPLE_CHAT_CONVERSATION(conv) PURPLE_CONV_CHAT(conv) 92 | #define PURPLE_IS_IM_CONVERSATION(conv) (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) 93 | #define PURPLE_IS_CHAT_CONVERSATION(conv) (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) 94 | #define purple_chat_conversation_add_user purple_conv_chat_add_user 95 | #define purple_chat_conversation_has_left purple_conv_chat_has_left 96 | #define purple_chat_conversation_remove_user purple_conv_chat_remove_user 97 | 98 | #define PurpleMessage PurpleConvMessage 99 | #define purple_message_set_time(msg, time) ((msg)->when = (time)) 100 | #define purple_conversation_write_message(conv, msg) purple_conversation_write(conv, msg->who, msg->what, msg->flags, msg->when) 101 | static inline PurpleMessage * 102 | purple_message_new_outgoing(const gchar *who, const gchar *contents, PurpleMessageFlags flags) 103 | { 104 | PurpleMessage *message = g_new0(PurpleMessage, 1); 105 | 106 | message->who = g_strdup(who); 107 | message->what = g_strdup(contents); 108 | message->flags = flags; 109 | message->when = time(NULL); 110 | 111 | return message; 112 | } 113 | static inline void 114 | purple_message_destroy(PurpleMessage *message) 115 | { 116 | g_free(message->who); 117 | g_free(message->what); 118 | g_free(message); 119 | } 120 | #if !PURPLE_VERSION_CHECK(2, 12, 0) 121 | # define PURPLE_MESSAGE_REMOTE_SEND 0x10000 122 | #endif 123 | 124 | #define PurpleProtocolChatEntry struct proto_chat_entry 125 | #define PurpleChatUserFlags PurpleConvChatBuddyFlags 126 | #define PURPLE_CHAT_USER_NONE PURPLE_CBFLAGS_NONE 127 | #define PURPLE_CHAT_USER_OP PURPLE_CBFLAGS_OP 128 | #define PURPLE_CHAT_USER_FOUNDER PURPLE_CBFLAGS_FOUNDER 129 | #define PURPLE_CHAT_USER_TYPING PURPLE_CBFLAGS_TYPING 130 | #define PURPLE_CHAT_USER_AWAY PURPLE_CBFLAGS_AWAY 131 | #define PURPLE_CHAT_USER_HALFOP PURPLE_CBFLAGS_HALFOP 132 | #define PURPLE_CHAT_USER_VOICE PURPLE_CBFLAGS_VOICE 133 | #define PURPLE_CHAT_USER_TYPING PURPLE_CBFLAGS_TYPING 134 | #define PurpleChatUser PurpleConvChatBuddy 135 | 136 | static inline PurpleChatUser * 137 | purple_chat_conversation_find_user(PurpleChatConversation *chat, const char *name) 138 | { 139 | PurpleChatUser *cb = purple_conv_chat_cb_find(chat, name); 140 | 141 | if (cb != NULL) { 142 | g_dataset_set_data(cb, "chat", chat); 143 | } 144 | 145 | return cb; 146 | } 147 | #define purple_chat_user_get_flags(cb) purple_conv_chat_user_get_flags(g_dataset_get_data((cb), "chat"), (cb)->name) 148 | #define purple_chat_user_set_flags(cb, f) purple_conv_chat_user_set_flags(g_dataset_get_data((cb), "chat"), (cb)->name, (f)) 149 | #define purple_chat_user_set_alias(cb, a) ((cb)->alias = (a)) 150 | 151 | #define PurpleIMTypingState PurpleTypingState 152 | #define PURPLE_IM_NOT_TYPING PURPLE_NOT_TYPING 153 | #define PURPLE_IM_TYPING PURPLE_TYPING 154 | #define PURPLE_IM_TYPED PURPLE_TYPED 155 | 156 | #define purple_media_set_protocol_data purple_media_set_prpl_data 157 | #if PURPLE_VERSION_CHECK(2, 10, 12) 158 | // Handle ABI breakage 159 | # define PURPLE_MEDIA_NETWORK_PROTOCOL_TCP PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE 160 | #endif 161 | 162 | #undef purple_notify_error 163 | #define purple_notify_error(handle, title, primary, secondary, cpar) \ 164 | purple_notify_message((handle), PURPLE_NOTIFY_MSG_ERROR, (title), \ 165 | (primary), (secondary), NULL, NULL) 166 | #undef purple_notify_warning 167 | #define purple_notify_warning(handle, title, primary, secondary, cpar) \ 168 | purple_notify_message((handle), PURPLE_NOTIFY_MSG_WARNING, (title), \ 169 | (primary), (secondary), NULL, NULL) 170 | #define purple_notify_user_info_add_pair_html purple_notify_user_info_add_pair 171 | 172 | #define PurpleProtocolAction PurplePluginAction 173 | #define purple_protocol_action_get_connection(action) ((PurpleConnection *) (action)->context) 174 | #define purple_protocol_action_new purple_plugin_action_new 175 | #define purple_protocol_get_id purple_plugin_get_id 176 | 177 | #define purple_protocol_got_user_status purple_prpl_got_user_status 178 | 179 | #define purple_account_privacy_deny_add purple_privacy_deny_add 180 | #define purple_account_privacy_deny_remove purple_privacy_deny_remove 181 | #define purple_account_set_password(account, password, dummy1, dummy2) \ 182 | purple_account_set_password(account, password); 183 | #define purple_account_set_private_alias purple_account_set_alias 184 | #define purple_account_get_private_alias purple_account_get_alias 185 | 186 | #define purple_proxy_info_get_proxy_type purple_proxy_info_get_type 187 | 188 | #define purple_serv_got_im serv_got_im 189 | #define purple_serv_got_typing serv_got_typing 190 | #define purple_serv_got_alias serv_got_alias 191 | #define purple_serv_got_chat_in serv_got_chat_in 192 | #define purple_serv_got_chat_left serv_got_chat_left 193 | #define purple_serv_got_joined_chat(pc, id, name) PURPLE_CONV_CHAT(serv_got_joined_chat(pc, id, name)) 194 | 195 | #define purple_status_get_status_type purple_status_get_type 196 | 197 | #define g_timeout_add_seconds purple_timeout_add_seconds 198 | #define g_timeout_add purple_timeout_add 199 | #define g_source_remove purple_timeout_remove 200 | 201 | #define PurpleXmlNode xmlnode 202 | #define purple_xmlnode_new xmlnode_new 203 | #define purple_xmlnode_new_child xmlnode_new_child 204 | #define purple_xmlnode_from_str xmlnode_from_str 205 | #define purple_xmlnode_to_str xmlnode_to_str 206 | #define purple_xmlnode_get_child xmlnode_get_child 207 | #define purple_xmlnode_get_next_twin xmlnode_get_next_twin 208 | #define purple_xmlnode_get_data xmlnode_get_data 209 | #define purple_xmlnode_get_attrib xmlnode_get_attrib 210 | #define purple_xmlnode_set_attrib xmlnode_set_attrib 211 | #define purple_xmlnode_insert_data xmlnode_insert_data 212 | #define purple_xmlnode_free xmlnode_free 213 | 214 | #ifndef _IMAGE_STORE_H_ 215 | #define _IMAGE_STORE_H_ 216 | 217 | #include "imgstore.h" 218 | 219 | #define purple_image_store_add(img) purple_imgstore_add_with_id( \ 220 | g_memdup(purple_imgstore_get_data(img), purple_imgstore_get_size(img)), \ 221 | purple_imgstore_get_size(img), purple_imgstore_get_filename(img)) 222 | #define purple_image_store_get purple_imgstore_find_by_id 223 | 224 | 225 | #endif /*_IMAGE_STORE_H_*/ 226 | 227 | #ifndef _IMAGE_H_ 228 | #define _IMAGE_H_ 229 | 230 | #define PurpleImage PurpleStoredImage 231 | #define purple_image_new_from_file(p, e) purple_imgstore_new_from_file(p) 232 | #define purple_image_new_from_data(d, l) purple_imgstore_add(d, l, NULL) 233 | #define purple_image_get_path purple_imgstore_get_filename 234 | #define purple_image_get_data_size purple_imgstore_get_size 235 | #define purple_image_get_data purple_imgstore_get_data 236 | #define purple_image_get_extension purple_imgstore_get_extension 237 | 238 | #endif /* _IMAGE_H_ */ 239 | 240 | 241 | 242 | 243 | #endif 244 | 245 | #endif /*_PURPLECOMPAT_H_*/ 246 | --------------------------------------------------------------------------------