├── 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 |
--------------------------------------------------------------------------------