Creative Mail – Easier WordPress & WooCommerce Email Marketing - Version 1.0.0

Version Description

Download this release

Release Info

Developer constantcontact
Plugin Icon 128x128 Creative Mail – Easier WordPress & WooCommerce Email Marketing
Version 1.0.0
Comparing to
See all releases

Version 1.0.0

Files changed (108) hide show
  1. CHANGELOG.md +6 -0
  2. LICENSE +339 -0
  3. README.md +37 -0
  4. assets/css/admin.css +627 -0
  5. assets/images/icon.svg +3 -0
  6. assets/images/logo.svg +18 -0
  7. assets/images/wp-plugin-marketing.png +0 -0
  8. composer.json +21 -0
  9. composer.lock +180 -0
  10. creative-mail-plugin.php +50 -0
  11. readme.txt +86 -0
  12. src/constants/environment-names.php +9 -0
  13. src/creativemail.php +71 -0
  14. src/helpers/encryption-helper.php +83 -0
  15. src/helpers/environment-helper.php +73 -0
  16. src/helpers/guid-helper.php +18 -0
  17. src/helpers/options-helper.php +174 -0
  18. src/helpers/sso-helper.php +61 -0
  19. src/integrations/integration.php +66 -0
  20. src/managers/admin-manager.php +143 -0
  21. src/managers/api-manager.php +214 -0
  22. src/managers/instance-manager.php +54 -0
  23. src/managers/integration-manager.php +150 -0
  24. src/modules/blog/models/BlogAttachment.php +21 -0
  25. src/modules/blog/models/BlogInformation.php +37 -0
  26. src/modules/blog/models/BlogPost.php +41 -0
  27. src/modules/contacts/Handlers/BaseContactFormPluginHandler.php +56 -0
  28. src/modules/contacts/Handlers/ContactFormSevenPluginHandler.php +156 -0
  29. src/modules/contacts/Handlers/NewsLetterContactFormPluginHandler.php +124 -0
  30. src/modules/contacts/Handlers/WooCommercePluginHandler.php +121 -0
  31. src/modules/contacts/Handlers/WpFormsLitePluginHandler.php +82 -0
  32. src/modules/contacts/Services/ContactsSyncService.php +115 -0
  33. src/modules/contacts/models/ContactAddressModel.php +81 -0
  34. src/modules/contacts/models/ContactFormSevenSubmission.php +11 -0
  35. src/modules/contacts/models/ContactModel.php +130 -0
  36. src/modules/contacts/models/OptActionBy.php +11 -0
  37. src/modules/woocommerce/models/WCProductModel.php +42 -0
  38. src/modules/woocommerce/models/WCStoreInformation.php +34 -0
  39. src/views/activated-integrations.php +32 -0
  40. src/views/available-integrations.php +19 -0
  41. src/views/consent.php +127 -0
  42. src/views/dashboard.php +45 -0
  43. src/views/onboarding.php +132 -0
  44. src/views/pending-setup.php +1 -0
  45. src/views/settings-internal.php +30 -0
  46. src/views/settings.php +71 -0
  47. src/views/unlink.php +10 -0
  48. vendor/autoload.php +7 -0
  49. vendor/bin/generate-defuse-key +14 -0
  50. vendor/composer/ClassLoader.php +445 -0
  51. vendor/composer/LICENSE +21 -0
  52. vendor/composer/autoload_classmap.php +55 -0
  53. vendor/composer/autoload_namespaces.php +9 -0
  54. vendor/composer/autoload_psr4.php +17 -0
  55. vendor/composer/autoload_real.php +55 -0
  56. vendor/composer/autoload_static.php +122 -0
  57. vendor/composer/installed.json +166 -0
  58. vendor/defuse/php-encryption/.gitignore +11 -0
  59. vendor/defuse/php-encryption/.php_cs +60 -0
  60. vendor/defuse/php-encryption/LICENSE +21 -0
  61. vendor/defuse/php-encryption/README.md +102 -0
  62. vendor/defuse/php-encryption/bin/generate-defuse-key +14 -0
  63. vendor/defuse/php-encryption/composer.json +35 -0
  64. vendor/defuse/php-encryption/dist/Makefile +37 -0
  65. vendor/defuse/php-encryption/dist/box.json +25 -0
  66. vendor/defuse/php-encryption/dist/signingkey.asc +52 -0
  67. vendor/defuse/php-encryption/docs/CryptoDetails.md +64 -0
  68. vendor/defuse/php-encryption/docs/FAQ.md +51 -0
  69. vendor/defuse/php-encryption/docs/InstallingAndVerifying.md +53 -0
  70. vendor/defuse/php-encryption/docs/InternalDeveloperDocs.md +166 -0
  71. vendor/defuse/php-encryption/docs/Tutorial.md +314 -0
  72. vendor/defuse/php-encryption/docs/UpgradingFromV1.2.md +51 -0
  73. vendor/defuse/php-encryption/docs/classes/Crypto.md +280 -0
  74. vendor/defuse/php-encryption/docs/classes/File.md +486 -0
  75. vendor/defuse/php-encryption/docs/classes/Key.md +117 -0
  76. vendor/defuse/php-encryption/docs/classes/KeyProtectedByPassword.md +259 -0
  77. vendor/defuse/php-encryption/psalm.xml +13 -0
  78. vendor/defuse/php-encryption/src/Core.php +448 -0
  79. vendor/defuse/php-encryption/src/Crypto.php +445 -0
  80. vendor/defuse/php-encryption/src/DerivedKeys.php +50 -0
  81. vendor/defuse/php-encryption/src/Encoding.php +269 -0
  82. vendor/defuse/php-encryption/src/Exception/BadFormatException.php +7 -0
  83. vendor/defuse/php-encryption/src/Exception/CryptoException.php +7 -0
  84. vendor/defuse/php-encryption/src/Exception/EnvironmentIsBrokenException.php +7 -0
  85. vendor/defuse/php-encryption/src/Exception/IOException.php +7 -0
  86. vendor/defuse/php-encryption/src/Exception/WrongKeyOrModifiedCiphertextException.php +7 -0
  87. vendor/defuse/php-encryption/src/File.php +762 -0
  88. vendor/defuse/php-encryption/src/Key.php +94 -0
  89. vendor/defuse/php-encryption/src/KeyOrPassword.php +149 -0
  90. vendor/defuse/php-encryption/src/KeyProtectedByPassword.php +145 -0
  91. vendor/defuse/php-encryption/src/RuntimeTests.php +228 -0
  92. vendor/firebase/php-jwt/LICENSE +30 -0
  93. vendor/firebase/php-jwt/README.md +200 -0
  94. vendor/firebase/php-jwt/composer.json +33 -0
  95. vendor/firebase/php-jwt/src/BeforeValidException.php +6 -0
  96. vendor/firebase/php-jwt/src/ExpiredException.php +6 -0
  97. vendor/firebase/php-jwt/src/JWK.php +171 -0
  98. vendor/firebase/php-jwt/src/JWT.php +512 -0
  99. vendor/firebase/php-jwt/src/SignatureInvalidException.php +6 -0
  100. vendor/paragonie/random_compat/LICENSE +22 -0
  101. vendor/paragonie/random_compat/build-phar.sh +5 -0
  102. vendor/paragonie/random_compat/composer.json +34 -0
  103. vendor/paragonie/random_compat/dist/random_compat.phar.pubkey +5 -0
  104. vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc +11 -0
  105. vendor/paragonie/random_compat/lib/random.php +32 -0
  106. vendor/paragonie/random_compat/other/build_phar.php +57 -0
  107. vendor/paragonie/random_compat/psalm-autoload.php +9 -0
  108. vendor/paragonie/random_compat/psalm.xml +19 -0
CHANGELOG.md ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ Changelog
2
+ =========
3
+
4
+ #### 1.0.0 - May 29 2020
5
+
6
+ - Initial version of the plugin
LICENSE ADDED
@@ -0,0 +1,339 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 2, June 1991
3
+
4
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
5
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6
+ Everyone is permitted to copy and distribute verbatim copies
7
+ of this license document, but changing it is not allowed.
8
+
9
+ Preamble
10
+
11
+ The licenses for most software are designed to take away your
12
+ freedom to share and change it. By contrast, the GNU General Public
13
+ License is intended to guarantee your freedom to share and change free
14
+ software--to make sure the software is free for all its users. This
15
+ General Public License applies to most of the Free Software
16
+ Foundation's software and to any other program whose authors commit to
17
+ using it. (Some other Free Software Foundation software is covered by
18
+ the GNU Lesser General Public License instead.) You can apply it to
19
+ your programs, too.
20
+
21
+ When we speak of free software, we are referring to freedom, not
22
+ price. Our General Public Licenses are designed to make sure that you
23
+ have the freedom to distribute copies of free software (and charge for
24
+ this service if you wish), that you receive source code or can get it
25
+ if you want it, that you can change the software or use pieces of it
26
+ in new free programs; and that you know you can do these things.
27
+
28
+ To protect your rights, we need to make restrictions that forbid
29
+ anyone to deny you these rights or to ask you to surrender the rights.
30
+ These restrictions translate to certain responsibilities for you if you
31
+ distribute copies of the software, or if you modify it.
32
+
33
+ For example, if you distribute copies of such a program, whether
34
+ gratis or for a fee, you must give the recipients all the rights that
35
+ you have. You must make sure that they, too, receive or can get the
36
+ source code. And you must show them these terms so they know their
37
+ rights.
38
+
39
+ We protect your rights with two steps: (1) copyright the software, and
40
+ (2) offer you this license which gives you legal permission to copy,
41
+ distribute and/or modify the software.
42
+
43
+ Also, for each author's protection and ours, we want to make certain
44
+ that everyone understands that there is no warranty for this free
45
+ software. If the software is modified by someone else and passed on, we
46
+ want its recipients to know that what they have is not the original, so
47
+ that any problems introduced by others will not reflect on the original
48
+ authors' reputations.
49
+
50
+ Finally, any free program is threatened constantly by software
51
+ patents. We wish to avoid the danger that redistributors of a free
52
+ program will individually obtain patent licenses, in effect making the
53
+ program proprietary. To prevent this, we have made it clear that any
54
+ patent must be licensed for everyone's free use or not licensed at all.
55
+
56
+ The precise terms and conditions for copying, distribution and
57
+ modification follow.
58
+
59
+ GNU GENERAL PUBLIC LICENSE
60
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
+
62
+ 0. This License applies to any program or other work which contains
63
+ a notice placed by the copyright holder saying it may be distributed
64
+ under the terms of this General Public License. The "Program", below,
65
+ refers to any such program or work, and a "work based on the Program"
66
+ means either the Program or any derivative work under copyright law:
67
+ that is to say, a work containing the Program or a portion of it,
68
+ either verbatim or with modifications and/or translated into another
69
+ language. (Hereinafter, translation is included without limitation in
70
+ the term "modification".) Each licensee is addressed as "you".
71
+
72
+ Activities other than copying, distribution and modification are not
73
+ covered by this License; they are outside its scope. The act of
74
+ running the Program is not restricted, and the output from the Program
75
+ is covered only if its contents constitute a work based on the
76
+ Program (independent of having been made by running the Program).
77
+ Whether that is true depends on what the Program does.
78
+
79
+ 1. You may copy and distribute verbatim copies of the Program's
80
+ source code as you receive it, in any medium, provided that you
81
+ conspicuously and appropriately publish on each copy an appropriate
82
+ copyright notice and disclaimer of warranty; keep intact all the
83
+ notices that refer to this License and to the absence of any warranty;
84
+ and give any other recipients of the Program a copy of this License
85
+ along with the Program.
86
+
87
+ You may charge a fee for the physical act of transferring a copy, and
88
+ you may at your option offer warranty protection in exchange for a fee.
89
+
90
+ 2. You may modify your copy or copies of the Program or any portion
91
+ of it, thus forming a work based on the Program, and copy and
92
+ distribute such modifications or work under the terms of Section 1
93
+ above, provided that you also meet all of these conditions:
94
+
95
+ a) You must cause the modified files to carry prominent notices
96
+ stating that you changed the files and the date of any change.
97
+
98
+ b) You must cause any work that you distribute or publish, that in
99
+ whole or in part contains or is derived from the Program or any
100
+ part thereof, to be licensed as a whole at no charge to all third
101
+ parties under the terms of this License.
102
+
103
+ c) If the modified program normally reads commands interactively
104
+ when run, you must cause it, when started running for such
105
+ interactive use in the most ordinary way, to print or display an
106
+ announcement including an appropriate copyright notice and a
107
+ notice that there is no warranty (or else, saying that you provide
108
+ a warranty) and that users may redistribute the program under
109
+ these conditions, and telling the user how to view a copy of this
110
+ License. (Exception: if the Program itself is interactive but
111
+ does not normally print such an announcement, your work based on
112
+ the Program is not required to print an announcement.)
113
+
114
+ These requirements apply to the modified work as a whole. If
115
+ identifiable sections of that work are not derived from the Program,
116
+ and can be reasonably considered independent and separate works in
117
+ themselves, then this License, and its terms, do not apply to those
118
+ sections when you distribute them as separate works. But when you
119
+ distribute the same sections as part of a whole which is a work based
120
+ on the Program, the distribution of the whole must be on the terms of
121
+ this License, whose permissions for other licensees extend to the
122
+ entire whole, and thus to each and every part regardless of who wrote it.
123
+
124
+ Thus, it is not the intent of this section to claim rights or contest
125
+ your rights to work written entirely by you; rather, the intent is to
126
+ exercise the right to control the distribution of derivative or
127
+ collective works based on the Program.
128
+
129
+ In addition, mere aggregation of another work not based on the Program
130
+ with the Program (or with a work based on the Program) on a volume of
131
+ a storage or distribution medium does not bring the other work under
132
+ the scope of this License.
133
+
134
+ 3. You may copy and distribute the Program (or a work based on it,
135
+ under Section 2) in object code or executable form under the terms of
136
+ Sections 1 and 2 above provided that you also do one of the following:
137
+
138
+ a) Accompany it with the complete corresponding machine-readable
139
+ source code, which must be distributed under the terms of Sections
140
+ 1 and 2 above on a medium customarily used for software interchange; or,
141
+
142
+ b) Accompany it with a written offer, valid for at least three
143
+ years, to give any third party, for a charge no more than your
144
+ cost of physically performing source distribution, a complete
145
+ machine-readable copy of the corresponding source code, to be
146
+ distributed under the terms of Sections 1 and 2 above on a medium
147
+ customarily used for software interchange; or,
148
+
149
+ c) Accompany it with the information you received as to the offer
150
+ to distribute corresponding source code. (This alternative is
151
+ allowed only for noncommercial distribution and only if you
152
+ received the program in object code or executable form with such
153
+ an offer, in accord with Subsection b above.)
154
+
155
+ The source code for a work means the preferred form of the work for
156
+ making modifications to it. For an executable work, complete source
157
+ code means all the source code for all modules it contains, plus any
158
+ associated interface definition files, plus the scripts used to
159
+ control compilation and installation of the executable. However, as a
160
+ special exception, the source code distributed need not include
161
+ anything that is normally distributed (in either source or binary
162
+ form) with the major components (compiler, kernel, and so on) of the
163
+ operating system on which the executable runs, unless that component
164
+ itself accompanies the executable.
165
+
166
+ If distribution of executable or object code is made by offering
167
+ access to copy from a designated place, then offering equivalent
168
+ access to copy the source code from the same place counts as
169
+ distribution of the source code, even though third parties are not
170
+ compelled to copy the source along with the object code.
171
+
172
+ 4. You may not copy, modify, sublicense, or distribute the Program
173
+ except as expressly provided under this License. Any attempt
174
+ otherwise to copy, modify, sublicense or distribute the Program is
175
+ void, and will automatically terminate your rights under this License.
176
+ However, parties who have received copies, or rights, from you under
177
+ this License will not have their licenses terminated so long as such
178
+ parties remain in full compliance.
179
+
180
+ 5. You are not required to accept this License, since you have not
181
+ signed it. However, nothing else grants you permission to modify or
182
+ distribute the Program or its derivative works. These actions are
183
+ prohibited by law if you do not accept this License. Therefore, by
184
+ modifying or distributing the Program (or any work based on the
185
+ Program), you indicate your acceptance of this License to do so, and
186
+ all its terms and conditions for copying, distributing or modifying
187
+ the Program or works based on it.
188
+
189
+ 6. Each time you redistribute the Program (or any work based on the
190
+ Program), the recipient automatically receives a license from the
191
+ original licensor to copy, distribute or modify the Program subject to
192
+ these terms and conditions. You may not impose any further
193
+ restrictions on the recipients' exercise of the rights granted herein.
194
+ You are not responsible for enforcing compliance by third parties to
195
+ this License.
196
+
197
+ 7. If, as a consequence of a court judgment or allegation of patent
198
+ infringement or for any other reason (not limited to patent issues),
199
+ conditions are imposed on you (whether by court order, agreement or
200
+ otherwise) that contradict the conditions of this License, they do not
201
+ excuse you from the conditions of this License. If you cannot
202
+ distribute so as to satisfy simultaneously your obligations under this
203
+ License and any other pertinent obligations, then as a consequence you
204
+ may not distribute the Program at all. For example, if a patent
205
+ license would not permit royalty-free redistribution of the Program by
206
+ all those who receive copies directly or indirectly through you, then
207
+ the only way you could satisfy both it and this License would be to
208
+ refrain entirely from distribution of the Program.
209
+
210
+ If any portion of this section is held invalid or unenforceable under
211
+ any particular circumstance, the balance of the section is intended to
212
+ apply and the section as a whole is intended to apply in other
213
+ circumstances.
214
+
215
+ It is not the purpose of this section to induce you to infringe any
216
+ patents or other property right claims or to contest validity of any
217
+ such claims; this section has the sole purpose of protecting the
218
+ integrity of the free software distribution system, which is
219
+ implemented by public license practices. Many people have made
220
+ generous contributions to the wide range of software distributed
221
+ through that system in reliance on consistent application of that
222
+ system; it is up to the author/donor to decide if he or she is willing
223
+ to distribute software through any other system and a licensee cannot
224
+ impose that choice.
225
+
226
+ This section is intended to make thoroughly clear what is believed to
227
+ be a consequence of the rest of this License.
228
+
229
+ 8. If the distribution and/or use of the Program is restricted in
230
+ certain countries either by patents or by copyrighted interfaces, the
231
+ original copyright holder who places the Program under this License
232
+ may add an explicit geographical distribution limitation excluding
233
+ those countries, so that distribution is permitted only in or among
234
+ countries not thus excluded. In such case, this License incorporates
235
+ the limitation as if written in the body of this License.
236
+
237
+ 9. The Free Software Foundation may publish revised and/or new versions
238
+ of the General Public License from time to time. Such new versions will
239
+ be similar in spirit to the present version, but may differ in detail to
240
+ address new problems or concerns.
241
+
242
+ Each version is given a distinguishing version number. If the Program
243
+ specifies a version number of this License which applies to it and "any
244
+ later version", you have the option of following the terms and conditions
245
+ either of that version or of any later version published by the Free
246
+ Software Foundation. If the Program does not specify a version number of
247
+ this License, you may choose any version ever published by the Free Software
248
+ Foundation.
249
+
250
+ 10. If you wish to incorporate parts of the Program into other free
251
+ programs whose distribution conditions are different, write to the author
252
+ to ask for permission. For software which is copyrighted by the Free
253
+ Software Foundation, write to the Free Software Foundation; we sometimes
254
+ make exceptions for this. Our decision will be guided by the two goals
255
+ of preserving the free status of all derivatives of our free software and
256
+ of promoting the sharing and reuse of software generally.
257
+
258
+ NO WARRANTY
259
+
260
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261
+ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266
+ TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267
+ PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268
+ REPAIR OR CORRECTION.
269
+
270
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274
+ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275
+ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276
+ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277
+ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278
+ POSSIBILITY OF SUCH DAMAGES.
279
+
280
+ END OF TERMS AND CONDITIONS
281
+
282
+ How to Apply These Terms to Your New Programs
283
+
284
+ If you develop a new program, and you want it to be of the greatest
285
+ possible use to the public, the best way to achieve this is to make it
286
+ free software which everyone can redistribute and change under these terms.
287
+
288
+ To do so, attach the following notices to the program. It is safest
289
+ to attach them to the start of each source file to most effectively
290
+ convey the exclusion of warranty; and each file should have at least
291
+ the "copyright" line and a pointer to where the full notice is found.
292
+
293
+ {description}
294
+ Copyright (C) {year} {fullname}
295
+
296
+ This program is free software; you can redistribute it and/or modify
297
+ it under the terms of the GNU General Public License as published by
298
+ the Free Software Foundation; either version 2 of the License, or
299
+ (at your option) any later version.
300
+
301
+ This program is distributed in the hope that it will be useful,
302
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
303
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304
+ GNU General Public License for more details.
305
+
306
+ You should have received a copy of the GNU General Public License along
307
+ with this program; if not, write to the Free Software Foundation, Inc.,
308
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309
+
310
+ Also add information on how to contact you by electronic and paper mail.
311
+
312
+ If the program is interactive, make it output a short notice like this
313
+ when it starts in an interactive mode:
314
+
315
+ Gnomovision version 69, Copyright (C) year name of author
316
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317
+ This is free software, and you are welcome to redistribute it
318
+ under certain conditions; type `show c' for details.
319
+
320
+ The hypothetical commands `show w' and `show c' should show the appropriate
321
+ parts of the General Public License. Of course, the commands you use may
322
+ be called something other than `show w' and `show c'; they could even be
323
+ mouse-clicks or menu items--whatever suits your program.
324
+
325
+ You should also get your employer (if you work as a programmer) or your
326
+ school, if any, to sign a "copyright disclaimer" for the program, if
327
+ necessary. Here is a sample; alter the names:
328
+
329
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
331
+
332
+ {signature of Ty Coon}, 1 April 1989
333
+ Ty Coon, President of Vice
334
+
335
+ This General Public License does not permit incorporating your program into
336
+ proprietary programs. If your program is a subroutine library, you may
337
+ consider it more useful to permit linking proprietary applications with the
338
+ library. If this is what you want to do, use the GNU Lesser General
339
+ Public License instead of this License.
README.md ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ### Creative Mail by Constant Contact
2
+ - Tags: email, marketing, newsletter, subscribe, contact form 7, woocommerce, constant contact
3
+ - Requires at least: 4.6
4
+ - Tested up to: 5.4.1
5
+ - Stable tag: 1.0.0
6
+ - License: GPLv2 or later
7
+ - License URI: http://www.gnu.org/licenses/gpl-2.0.html
8
+ - Requires PHP: 7.1
9
+
10
+ #### Description
11
+ Power your WooCommerce Store or WordPress Blog with simple & free email marketing from Constant Contact.
12
+ With the official Creative Mail for WooCommerce plugin, your products, blog posts, images and store links are automatically included as rich shoppable email marketing content for your customers. Our included CRM also intelligently pulls in and identifies your WordPress site contacts and WooCommerce store customers. That makes it easy to build audiences and send targeted customer campaigns. Get free email marketing, 98% deliverability, and Constant Contact rock solid reliability all without ever needing to leave your WP Admin.
13
+
14
+ #### Features
15
+ A Constant Contact WordPress plugin that gathers contacts submitted via the WordPress site forms and enables users to send email campaigns in a simple and intuitive way.
16
+
17
+ The plugin offers the following functionality:
18
+
19
+ - Automated email creation
20
+ - Section based, offering preset vs unlimited design options
21
+ - Seamless integration with the following Contact opt-ins without need for adding JMML:
22
+ - Contact Form 7
23
+ - WooCommerce
24
+ - WPForms Lite
25
+ - Newsletter
26
+ - Combined Contacts CRM (fed from WordPress Site forms & WooCommerce store)
27
+ - Stock images (Unsplash integration, for free)
28
+ - WordPress Blog Posts & WooCommerce Products integration to allow easy sharing via email campaigns
29
+
30
+
31
+ #### Installing the plugin
32
+ 1. In your WordPress admin panel, go to *Plugins > New Plugin*, search for `Creative Mail by Constant Contact` and click "*Install now*"
33
+ 1. Alternatively, download the plugin and upload the contents of `*.zip` to your plugins directory, which usually is `/wp-content/plugins/`.
34
+ 1. Activate the plugin
35
+ 1. Log Into your account or sign up.
36
+
37
+
assets/css/admin.css ADDED
@@ -0,0 +1,627 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .ce4wp-admin-wrapper {
2
+ margin-right: 20px;
3
+ margin-top: 20px;
4
+ font-size: 14px;
5
+ }
6
+
7
+ .ce4wp-iframe {
8
+ margin-left: -20px;
9
+ position: absolute;
10
+ width: calc(100% + 20px);
11
+ z-index: 10;
12
+ left: 0;
13
+ right: 0;
14
+ top: 0;
15
+ bottom: 0;
16
+ }
17
+
18
+ @media (max-width: 600px) {
19
+ .ce4wp-iframe {
20
+ top: 46px;
21
+ }
22
+ }
23
+
24
+ .ce4wp-welcome-image {
25
+ display: block;
26
+ }
27
+
28
+ @media (max-width: 600px) {
29
+ .ce4wp-welcome-image {
30
+ display: none;
31
+ }
32
+ }
33
+
34
+
35
+ .ce4wp-header {
36
+ height: 64px;
37
+ background: #ffffff;
38
+ -webkit-box-align: center;
39
+ align-items: center;
40
+ display: flex;
41
+ position: relative;
42
+ }
43
+
44
+ .ce4wp-header::after {
45
+ height: 284px;
46
+ width: 100%;
47
+ content: "";
48
+ position: absolute;
49
+ top: 64px;
50
+ background-color: rgb(122,76,168);
51
+ }
52
+
53
+ .ce4wp-header-blue::before {
54
+ top: 20px;
55
+ left: 0px;
56
+ right: 0px;
57
+ bottom: initial;
58
+ height: 284px;
59
+ content: "";
60
+ position: absolute;
61
+ background-color: rgb(122,76,168);
62
+
63
+ }
64
+
65
+ .ce4wp-container {
66
+ margin-left: 30px;
67
+ margin-right: 30px;
68
+ margin-top: 30px;
69
+ }
70
+
71
+ .ce4wp-container h2 {
72
+ font-size: 24px;
73
+ }
74
+
75
+ .ce4wp-card {
76
+ background-attachment:scroll;
77
+ background-clip:border-box;
78
+ background-color:rgb(255, 255, 255);
79
+ background-image:none;
80
+ background-origin:padding-box;
81
+ background-position-x:0%;
82
+ background-position-y:0%;
83
+ background-size:auto;
84
+ border-bottom-left-radius:4px;
85
+ border-bottom-right-radius:4px;
86
+ border-top-left-radius:4px;
87
+ border-top-right-radius:4px;
88
+ box-shadow:rgba(0, 0, 0, 0.06) 0px 0px 0px 1px, rgba(0, 0, 0, 0.05) 0px 2px 4px 0px, rgba(0, 0, 0, 0.08) 0px 2px 1px 0px;
89
+ box-sizing:border-box;
90
+ color:rgba(0, 0, 0, 0.9);
91
+ display:block;
92
+ font-size:15px;
93
+ letter-spacing:normal;
94
+ margin-bottom:24px;
95
+ min-height:150px;
96
+ min-width:200px;
97
+ overflow-x:hidden;
98
+ overflow-y:hidden;
99
+ position:relative;
100
+ transition-delay:0s;
101
+ transition-duration:0.3s;
102
+ transition-property:box-shadow;
103
+ transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);
104
+ -webkit-font-smoothing:antialiased;
105
+ padding: 15px;
106
+ }
107
+
108
+ .ce4wp-card h2 {
109
+ font-size: 28px;
110
+ font-weight: 700;
111
+ line-height: 32px;
112
+ }
113
+
114
+ .ce4wp-card h4 {
115
+ font-size: 16px;
116
+ font-weight: 700;
117
+ line-height: 20px;
118
+ margin-top: 5px;
119
+ }
120
+
121
+ .ce4wp-card h5 {
122
+ font-size: 14px;
123
+ font-weight: 700;
124
+ line-height: 20px;
125
+ margin: 0;
126
+ }
127
+
128
+ .ce4wp-card h6 {
129
+ font-size: 14px;
130
+ font-weight: 400;
131
+ line-height: 20px;
132
+ color: rgba(0, 0, 0, 0.6);
133
+ }
134
+
135
+ .ce4wp-card p {
136
+ font-size: 14px;
137
+ }
138
+
139
+ .ce4wp-card ul {
140
+ padding-left: 10px;
141
+ }
142
+ .ce4wp-card ul li {
143
+ font-size: 14px;
144
+ font-weight: 400;
145
+ }
146
+
147
+ .ce4wp-card .ce4wp-kvp {
148
+ margin-top: 10px;
149
+ }
150
+
151
+ .ce4wp-card .ce4wp-kvp h6 {
152
+ margin: 0;
153
+ }
154
+
155
+ .ce4wp-logo {
156
+ background-image: url("../images/logo.svg");
157
+ background-size: contain;
158
+ background-position-y: 50%;
159
+ width: 142px;
160
+ height: 41px;
161
+ margin-left: 24px;
162
+ background-repeat: no-repeat;
163
+ }
164
+
165
+ .ce4wp-button-base-root {
166
+ color: inherit;
167
+ border: 0;
168
+ cursor: pointer;
169
+ margin: 0;
170
+ display: inline-flex;
171
+ outline: 0;
172
+ padding: 0;
173
+ position: relative;
174
+ align-items: center;
175
+ user-select: none;
176
+ border-radius: 0;
177
+ vertical-align: middle;
178
+ -moz-appearance: none;
179
+ justify-content: center;
180
+ text-decoration: none;
181
+ background-color: transparent;
182
+ -webkit-appearance: none;
183
+ -webkit-tap-highlight-color: transparent;
184
+ }
185
+ .ce4wp-button-root {
186
+ color: rgba(0, 0, 0, 0.9);
187
+ height: 40px;
188
+ padding: 6px 16px;
189
+ shadows: none;
190
+ font-size: 14px;
191
+ min-width: auto;
192
+ box-shadow: none;
193
+ box-sizing: border-box;
194
+ transition: background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,
195
+ box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,
196
+ border 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
197
+ font-weight: 700;
198
+ line-height: inherit;
199
+ border-radius: 4px;
200
+ text-transform: none;
201
+ }
202
+
203
+ .ce4wp-button-root:hover{
204
+ color: rgba(255, 255, 255, 0.7);
205
+ }
206
+
207
+ .ce4wp-button-contained {
208
+ color: rgba(0, 0, 0, 0.87);
209
+ box-shadow: none;
210
+ text-transform: none;
211
+ background-color: #e0e0e0;
212
+ }
213
+ .ce4wp-button-contained-primary {
214
+ color: #ffffff;
215
+ border: 1px solid #7A4CA8;
216
+ font-size: 14px;
217
+ font-weight: 700;
218
+ background-color: #7A4CA8;
219
+ }
220
+
221
+ .ce4wp-button-text-primary {
222
+ align-items:center;
223
+ background-color:rgba(0, 0, 0, 0);
224
+ border-bottom-color:rgb(122,76,168);
225
+ border-bottom-left-radius:4px;
226
+ border-bottom-right-radius:4px;
227
+ border-bottom-style:none;
228
+ border-bottom-width:0px;
229
+ border-image-outset:0px;
230
+ border-image-repeat:stretch;
231
+ border-image-slice:100%;
232
+ border-image-source:none;
233
+ border-image-width:1;
234
+ border-left-color:rgb(122,76,168);
235
+ border-left-style:none;
236
+ border-left-width:0px;
237
+ border-right-color:rgb(122,76,168);
238
+ border-right-style:none;
239
+ border-right-width:0px;
240
+ border-top-color:rgb(122,76,168);
241
+ border-top-left-radius:4px;
242
+ border-top-right-radius:4px;
243
+ border-top-style:none;
244
+ border-top-width:0px;
245
+ box-shadow:none;
246
+ box-sizing:border-box;
247
+ color:rgb(122,76,168);
248
+ cursor:pointer;
249
+ direction:ltr;
250
+ display:inline-flex;
251
+ font-size:14px;
252
+ font-stretch:100%;
253
+ font-style:normal;
254
+ font-variant-caps:normal;
255
+ font-variant-east-asian:normal;
256
+ font-variant-ligatures:normal;
257
+ font-variant-numeric:normal;
258
+ font-weight:700;
259
+ height:40px;
260
+ justify-content:center;
261
+ letter-spacing:normal;
262
+ line-height:20px;
263
+ margin-bottom:0px;
264
+ margin-left:0px;
265
+ margin-right:0px;
266
+ margin-top:0px;
267
+ min-width:0px;
268
+ outline-color:rgb(122,76,168);
269
+ outline-style:none;
270
+ outline-width:0px;
271
+ padding-bottom:6px;
272
+ padding-left:8px;
273
+ padding-right:8px;
274
+ padding-top:6px;
275
+ position:relative;
276
+ text-align:center;
277
+ text-decoration-color:rgb(122,76,168);
278
+ text-decoration-line:none;
279
+ text-decoration-style:solid;
280
+ text-indent:0px;
281
+ text-rendering:auto;
282
+ text-shadow:none;
283
+ text-transform:none;
284
+ transition-delay:0s, 0s, 0s;
285
+ transition-duration:0.25s, 0.25s, 0.25s;
286
+ transition-property:background-color, box-shadow, border;
287
+ transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1), cubic-bezier(0.4, 0, 0.2, 1), cubic-bezier(0.4, 0, 0.2, 1);
288
+ user-select:none;
289
+ vertical-align:middle;
290
+ white-space:nowrap;
291
+ word-spacing:0px;
292
+ writing-mode:horizontal-tb;
293
+ -webkit-appearance:none;
294
+ -webkit-font-smoothing:antialiased;
295
+ -webkit-tap-highlight-color:rgba(0, 0, 0, 0);
296
+ -webkit-border-image:none;
297
+ }
298
+
299
+ .ce4wp-button-text-primary:hover {
300
+ background-color: rgba(122,76,168, 0.04);
301
+ }
302
+
303
+ .ce4wp-button-text-primary.destructive {
304
+ color: #D42424;
305
+ }
306
+
307
+ .ce4wp-button-text-primary.destructive:hover {
308
+ background-color: rgba(212, 36, 36, 0.1);
309
+ }
310
+
311
+ .ce4wp-right {
312
+ float: right;
313
+ }
314
+
315
+ .ce4wp-left {
316
+ float: left;
317
+ }
318
+
319
+ .ce4wp-checkbox {
320
+ z-index: 0;
321
+ position: relative;
322
+ display: inline-block;
323
+ color: rgba(var(--pure-material-onsurface-rgb, 0, 0, 0), 0.87);
324
+ font-family: var(--pure-material-font, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);
325
+ font-size: 14px;
326
+ line-height: 1.5;
327
+ }
328
+
329
+ /* Input */
330
+ .ce4wp-checkbox > input {
331
+ appearance: none;
332
+ -moz-appearance: none;
333
+ -webkit-appearance: none;
334
+ z-index: -1;
335
+ position: absolute;
336
+ left: -10px;
337
+ top: -8px;
338
+ display: block;
339
+ margin: 0;
340
+ border-radius: 50%;
341
+ width: 40px;
342
+ height: 40px;
343
+ background-color: rgba(var(--pure-material-onsurface-rgb, 0, 0, 0), 0.6);
344
+ box-shadow: none;
345
+ outline: none;
346
+ opacity: 0;
347
+ transform: scale(1);
348
+ pointer-events: none;
349
+ transition: opacity 0.3s, transform 0.2s;
350
+ }
351
+
352
+ /* Span */
353
+ .ce4wp-checkbox > span {
354
+ display: inline-block;
355
+ width: 100%;
356
+ cursor: pointer;
357
+ }
358
+
359
+ /* Box */
360
+ .ce4wp-checkbox > span::before {
361
+ content: "";
362
+ display: inline-block;
363
+ box-sizing: border-box;
364
+ margin: 3px 11px 3px 1px;
365
+ border: solid 2px; /* Safari */
366
+ border-color: rgba(var(--pure-material-onsurface-rgb, 0, 0, 0), 0.6);
367
+ border-radius: 2px;
368
+ width: 18px;
369
+ height: 18px;
370
+ vertical-align: top;
371
+ transition: border-color 0.2s, background-color 0.2s;
372
+ }
373
+
374
+ /* Checkmark */
375
+ .ce4wp-checkbox > span::after {
376
+ content: "";
377
+ display: block;
378
+ position: absolute;
379
+ top: 3px;
380
+ left: 1px;
381
+ width: 10px;
382
+ height: 5px;
383
+ border: solid 2px transparent;
384
+ border-right: none;
385
+ border-top: none;
386
+ transform: translate(3px, 4px) rotate(-45deg);
387
+ }
388
+
389
+ /* Checked, Indeterminate */
390
+ .ce4wp-checkbox > input:checked,
391
+ .ce4wp-checkbox > input:indeterminate {
392
+ background-color: rgb(var(--pure-material-primary-rgb, 122, 76, 168));
393
+ }
394
+
395
+ .ce4wp-checkbox > input:checked + span::before,
396
+ .ce4wp-checkbox > input:indeterminate + span::before {
397
+ border-color: rgb(var(--pure-material-primary-rgb, 122, 76, 168));
398
+ background-color: rgb(var(--pure-material-primary-rgb, 122, 76, 168));
399
+ }
400
+
401
+ .ce4wp-checkbox > input:checked + span::after,
402
+ .ce4wp-checkbox > input:indeterminate + span::after {
403
+ border-color: rgb(var(--pure-material-onprimary-rgb, 255, 255, 255));
404
+ }
405
+
406
+ .ce4wp-checkbox > input:indeterminate + span::after {
407
+ border-left: none;
408
+ transform: translate(4px, 3px);
409
+ }
410
+
411
+ /* Hover, Focus */
412
+ .ce4wp-checkbox:hover > input {
413
+ opacity: 0.04;
414
+ }
415
+
416
+ .ce4wp-checkbox > input:focus {
417
+ opacity: 0.12;
418
+ }
419
+
420
+ .ce4wp-checkbox:hover > input:focus {
421
+ opacity: 0.16;
422
+ }
423
+
424
+ /* Active */
425
+ .ce4wp-checkbox > input:active {
426
+ opacity: 1;
427
+ transform: scale(0);
428
+ transition: transform 0s, opacity 0s;
429
+ }
430
+
431
+ .ce4wp-checkbox > input:active + span::before {
432
+ border-color: rgb(var(--pure-material-primary-rgb, 122, 76, 168));
433
+ }
434
+
435
+ .ce4wp-checkbox > input:checked:active + span::before {
436
+ border-color: transparent;
437
+ background-color: rgba(var(--pure-material-onsurface-rgb, 0, 0, 0), 0.6);
438
+ }
439
+
440
+ /* Disabled */
441
+ .ce4wp-checkbox > input:disabled {
442
+ opacity: 0;
443
+ }
444
+
445
+ .ce4wp-checkbox > input:disabled + span {
446
+ color: rgba(var(--pure-material-onsurface-rgb, 0, 0, 0), 0.38);
447
+ cursor: initial;
448
+ }
449
+
450
+ .ce4wp-checkbox > input:disabled + span::before {
451
+ border-color: currentColor;
452
+ }
453
+
454
+ .ce4wp-checkbox > input:checked:disabled + span::before,
455
+ .ce4wp-checkbox > input:indeterminate:disabled + span::before {
456
+ border-color: transparent;
457
+ background-color: currentColor;
458
+ }
459
+
460
+ .ce4wp-redirector {
461
+ margin: 0 auto;
462
+ position: relative;
463
+ padding-top: 40px;
464
+ }
465
+ @media (min-width: 600px) {
466
+ .ce4wp-redirector {
467
+ max-width: 890px;
468
+ padding-top: 72px;
469
+ }
470
+ }
471
+
472
+ .ce4wp-mt-4,
473
+ .ce4wp-my-4 {
474
+ margin-top: 1.5rem !important;
475
+ }
476
+
477
+ .ce4wp-pb-4,
478
+ .ce4wp-py-4 {
479
+ padding-bottom: 1.5rem !important;
480
+ }
481
+
482
+ .ce4wp-pr-3,
483
+ .ce4wp-px-3 {
484
+ padding-right: 1rem !important;
485
+ }
486
+
487
+ .ce4wp-pb-1,
488
+ .ce4wp-py-1 {
489
+ padding-bottom: 0.25rem !important;
490
+ }
491
+ .ce4wp-pt-1,
492
+ .ce4wp-py-1 {
493
+ padding-top: 0.25rem !important;
494
+ }
495
+ .ce4wp-m-0 {
496
+ margin: 0 !important;
497
+ }
498
+
499
+ .ce4wp-p-3 {
500
+ padding: 1rem !important;
501
+ }
502
+ .ce4wp-row {
503
+ display: flex;
504
+ flex-wrap: wrap;
505
+ margin-right: -15px;
506
+ margin-left: -15px;
507
+ }
508
+
509
+ .ce4wp-col,
510
+ .ce4wp-col-auto {
511
+ position: relative;
512
+ width: 100%;
513
+ min-height: 1px;
514
+ padding-right: 15px;
515
+ padding-left: 15px;
516
+ }
517
+
518
+ .ce4wp-col {
519
+ flex-basis: 0;
520
+ flex-grow: 1;
521
+ max-width: 100%;
522
+ }
523
+
524
+ .ce4wp-col-auto {
525
+ flex: 0 0 auto;
526
+ width: auto;
527
+ max-width: none;
528
+ }
529
+
530
+ .ce4wp-center {
531
+ display: flex;
532
+ -webkit-box-align: center;
533
+ align-items: center;
534
+ -webkit-box-pack: center;
535
+ justify-content: center;
536
+ }
537
+
538
+ .ce4wp-typography-root {
539
+ margin: 0;
540
+ }
541
+
542
+ .ce4wp-typography-gutter-bottom {
543
+ margin-bottom: 12px;
544
+ }
545
+
546
+ .ce4wp-typography-subtitle1 {
547
+ color: rgba(0, 0, 0, 0.6);
548
+ font-size: 16px;
549
+ font-weight: 400;
550
+ line-height: 24px;
551
+ }
552
+
553
+ .ce4wp-typography-h2 {
554
+ font-size: 28px;
555
+ font-weight: 700;
556
+ line-height: 32px;
557
+ }
558
+
559
+ .ce4wp-typography-h4 {
560
+ font-size: 16px;
561
+ font-weight: 700;
562
+ line-height: 20px;
563
+ }
564
+
565
+ .ce4wp-typography-color-text-secondary {
566
+ color: rgba(0, 0, 0, 0.6);
567
+ }
568
+
569
+ .ce4wp-list-root {
570
+ margin: 0;
571
+ padding-left: 0 !important;
572
+ padding-right: 0 !important;
573
+ position: relative;
574
+ list-style: none;
575
+ }
576
+
577
+ .ce4wp-list-padding {
578
+ padding-top: 8px;
579
+ padding-bottom: 8px;
580
+ }
581
+
582
+ .ce4wp-list-item-gutters {
583
+ padding-left: 8px;
584
+ padding-right: 8px;
585
+ }
586
+ .ce4wp-list-item-root {
587
+ width: 100%;
588
+ display: flex;
589
+ position: relative;
590
+ box-sizing: border-box;
591
+ min-height: 40px;
592
+ text-align: left;
593
+ align-items: center;
594
+ padding-top: 8px;
595
+ padding-bottom: 8px;
596
+ justify-content: flex-start;
597
+ text-decoration: none;
598
+ }
599
+ .ce4wp-list-item-text-root {
600
+ flex: 1 1 auto;
601
+ padding: 0;
602
+ min-width: 0;
603
+ margin-top: 4px;
604
+ text-align: left;
605
+ margin-bottom: 4px;
606
+ }
607
+
608
+ .ce4wp-svg-icon-root {
609
+ fill: currentColor;
610
+ width: 1em;
611
+ height: 1em;
612
+ display: inline-block;
613
+ font-size: 1.5rem;
614
+ transition: fill 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
615
+ flex-shrink: 0;
616
+ user-select: none;
617
+ }
618
+ .ce4wp-svg-icon-color {
619
+ color: rgb(122,76,168);
620
+ }
621
+
622
+ .ce4wp-flex-column {
623
+ flex-direction: column !important;
624
+ }
625
+ .ce4wp-d-flex {
626
+ display: flex !important;
627
+ }
assets/images/icon.svg ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M24 0C10.7452 0 0 10.7452 0 24V232C0 245.255 10.7452 256 24 256H232C245.255 256 256 245.255 256 232V24C256 10.7452 245.255 0 232 0H24ZM102.4 87.52C94.28 81.1733 84.06 78 71.74 78C62.22 78 53.8667 80.1467 46.68 84.44C39.4933 88.64 33.8933 94.5667 29.88 102.22C25.96 109.873 24 118.6 24 128.4C24 138.107 25.96 146.787 29.88 154.44C33.8933 162.093 39.4933 168.067 46.68 172.36C53.96 176.56 62.3133 178.66 71.74 178.66C84.06 178.66 94.28 175.487 102.4 169.14C110.52 162.793 115.887 154.16 118.5 143.24H97.78C95.6333 148.56 92.2733 152.76 87.7 155.84C83.22 158.92 77.8067 160.46 71.46 160.46C66.2333 160.46 61.52 159.153 57.32 156.54C53.12 153.927 49.8533 150.193 47.52 145.34C45.1867 140.487 44.02 134.84 44.02 128.4C44.02 121.867 45.1867 116.173 47.52 111.32C49.8533 106.467 53.12 102.733 57.32 100.12C61.52 97.5067 66.2333 96.2 71.46 96.2C77.8067 96.2 83.22 97.74 87.7 100.82C92.2733 103.9 95.6333 108.1 97.78 113.42H118.5C115.887 102.5 110.52 93.8667 102.4 87.52ZM231.45 177.82V80.1H207.65L178.53 153.6L148.85 80.1H124.91V177.82H144.65V109.92L169.71 177.82H186.79L211.85 109.64V177.82H231.45Z" fill="#9A9A9A"/>
3
+ </svg>
assets/images/logo.svg ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <svg width="385" height="90" viewBox="0 0 385 90" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M383.864 62.6442C383.864 63.7914 382.926 64.9385 381.57 64.9385C380.263 64.9385 379.275 63.8412 379.275 62.6442C379.275 60.9733 380.632 60.2949 381.57 60.2949C382.198 60.2999 383.864 60.7139 383.864 62.6442ZM383.555 62.6442C383.555 61.497 382.722 60.659 381.625 60.659C380.582 60.659 379.694 61.492 379.694 62.5893C379.694 63.8961 380.842 64.5196 381.575 64.5196C382.667 64.5794 383.555 63.6916 383.555 62.6442ZM382.717 63.8462H382.039L381.57 62.8038H381.256V63.8462H380.682V61.2875H381.675C382.143 61.2875 382.772 61.3922 382.772 62.0157C382.772 62.3798 382.562 62.5893 382.198 62.694L382.717 63.8462ZM382.093 62.0706C382.093 61.7563 381.834 61.7065 381.465 61.7065H381.206V62.4895H381.465C381.829 62.4397 382.093 62.3848 382.093 62.0706Z" fill="#2E323B"/>
3
+ <path d="M130.239 66.648C130.671 66.008 131.263 65.488 132.015 65.088C132.783 64.688 133.655 64.488 134.631 64.488C135.767 64.488 136.791 64.768 137.703 65.328C138.631 65.888 139.359 66.688 139.887 67.728C140.431 68.752 140.703 69.944 140.703 71.304C140.703 72.664 140.431 73.872 139.887 74.928C139.359 75.968 138.631 76.776 137.703 77.352C136.791 77.928 135.767 78.216 134.631 78.216C133.639 78.216 132.767 78.024 132.015 77.64C131.279 77.24 130.687 76.728 130.239 76.104V78H126.879V60.24H130.239V66.648ZM137.271 71.304C137.271 70.504 137.103 69.816 136.767 69.24C136.447 68.648 136.015 68.2 135.471 67.896C134.943 67.592 134.367 67.44 133.743 67.44C133.135 67.44 132.559 67.6 132.015 67.92C131.487 68.224 131.055 68.672 130.719 69.264C130.399 69.856 130.239 70.552 130.239 71.352C130.239 72.152 130.399 72.848 130.719 73.44C131.055 74.032 131.487 74.488 132.015 74.808C132.559 75.112 133.135 75.264 133.743 75.264C134.367 75.264 134.943 75.104 135.471 74.784C136.015 74.464 136.447 74.008 136.767 73.416C137.103 72.824 137.271 72.12 137.271 71.304ZM155.864 64.704L147.632 84.288H144.056L146.936 77.664L141.608 64.704H145.376L148.808 73.992L152.288 64.704H155.864ZM162.578 69.6C162.578 67.952 162.946 66.48 163.682 65.184C164.434 63.872 165.45 62.856 166.73 62.136C168.026 61.4 169.474 61.032 171.074 61.032C172.946 61.032 174.586 61.512 175.994 62.472C177.402 63.432 178.386 64.76 178.946 66.456H175.082C174.698 65.656 174.154 65.056 173.45 64.656C172.762 64.256 171.962 64.056 171.05 64.056C170.074 64.056 169.202 64.288 168.434 64.752C167.682 65.2 167.09 65.84 166.658 66.672C166.242 67.504 166.034 68.48 166.034 69.6C166.034 70.704 166.242 71.68 166.658 72.528C167.09 73.36 167.682 74.008 168.434 74.472C169.202 74.92 170.074 75.144 171.05 75.144C171.962 75.144 172.762 74.944 173.45 74.544C174.154 74.128 174.698 73.52 175.082 72.72H178.946C178.386 74.432 177.402 75.768 175.994 76.728C174.602 77.672 172.962 78.144 171.074 78.144C169.474 78.144 168.026 77.784 166.73 77.064C165.45 76.328 164.434 75.312 163.682 74.016C162.946 72.72 162.578 71.248 162.578 69.6ZM187.72 78.216C186.44 78.216 185.288 77.936 184.264 77.376C183.24 76.8 182.432 75.992 181.84 74.952C181.264 73.912 180.976 72.712 180.976 71.352C180.976 69.992 181.272 68.792 181.864 67.752C182.472 66.712 183.296 65.912 184.336 65.352C185.376 64.776 186.536 64.488 187.816 64.488C189.096 64.488 190.256 64.776 191.296 65.352C192.336 65.912 193.152 66.712 193.744 67.752C194.352 68.792 194.656 69.992 194.656 71.352C194.656 72.712 194.344 73.912 193.72 74.952C193.112 75.992 192.28 76.8 191.224 77.376C190.184 77.936 189.016 78.216 187.72 78.216ZM187.72 75.288C188.328 75.288 188.896 75.144 189.424 74.856C189.968 74.552 190.4 74.104 190.72 73.512C191.04 72.92 191.2 72.2 191.2 71.352C191.2 70.088 190.864 69.12 190.192 68.448C189.536 67.76 188.728 67.416 187.768 67.416C186.808 67.416 186 67.76 185.344 68.448C184.704 69.12 184.384 70.088 184.384 71.352C184.384 72.616 184.696 73.592 185.32 74.28C185.96 74.952 186.76 75.288 187.72 75.288ZM204.489 64.512C206.073 64.512 207.353 65.016 208.329 66.024C209.305 67.016 209.793 68.408 209.793 70.2V78H206.433V70.656C206.433 69.6 206.169 68.792 205.641 68.232C205.113 67.656 204.393 67.368 203.481 67.368C202.553 67.368 201.817 67.656 201.273 68.232C200.745 68.792 200.481 69.6 200.481 70.656V78H197.121V64.704H200.481V66.36C200.929 65.784 201.497 65.336 202.185 65.016C202.889 64.68 203.657 64.512 204.489 64.512ZM218.076 78.216C216.988 78.216 216.012 78.024 215.148 77.64C214.284 77.24 213.596 76.704 213.084 76.032C212.588 75.36 212.316 74.616 212.268 73.8H215.652C215.716 74.312 215.964 74.736 216.396 75.072C216.844 75.408 217.396 75.576 218.052 75.576C218.692 75.576 219.188 75.448 219.54 75.192C219.908 74.936 220.092 74.608 220.092 74.208C220.092 73.776 219.868 73.456 219.42 73.248C218.988 73.024 218.292 72.784 217.332 72.528C216.34 72.288 215.524 72.04 214.884 71.784C214.26 71.528 213.716 71.136 213.252 70.608C212.804 70.08 212.58 69.368 212.58 68.472C212.58 67.736 212.788 67.064 213.204 66.456C213.636 65.848 214.244 65.368 215.028 65.016C215.828 64.664 216.764 64.488 217.836 64.488C219.42 64.488 220.684 64.888 221.628 65.688C222.572 66.472 223.092 67.536 223.188 68.88H219.972C219.924 68.352 219.7 67.936 219.3 67.632C218.916 67.312 218.396 67.152 217.74 67.152C217.132 67.152 216.66 67.264 216.324 67.488C216.004 67.712 215.844 68.024 215.844 68.424C215.844 68.872 216.068 69.216 216.516 69.456C216.964 69.68 217.66 69.912 218.604 70.152C219.564 70.392 220.356 70.64 220.98 70.896C221.604 71.152 222.14 71.552 222.588 72.096C223.052 72.624 223.292 73.328 223.308 74.208C223.308 74.976 223.092 75.664 222.66 76.272C222.244 76.88 221.636 77.36 220.836 77.712C220.052 78.048 219.132 78.216 218.076 78.216ZM229.978 67.464V73.896C229.978 74.344 230.082 74.672 230.29 74.88C230.514 75.072 230.882 75.168 231.394 75.168H232.954V78H230.842C228.01 78 226.594 76.624 226.594 73.872V67.464H225.01V64.704H226.594V61.416H229.978V64.704H232.954V67.464H229.978ZM234.507 71.304C234.507 69.96 234.771 68.768 235.299 67.728C235.843 66.688 236.571 65.888 237.483 65.328C238.411 64.768 239.443 64.488 240.579 64.488C241.571 64.488 242.435 64.688 243.171 65.088C243.923 65.488 244.523 65.992 244.971 66.6V64.704H248.355V78H244.971V76.056C244.539 76.68 243.939 77.2 243.171 77.616C242.419 78.016 241.547 78.216 240.555 78.216C239.435 78.216 238.411 77.928 237.483 77.352C236.571 76.776 235.843 75.968 235.299 74.928C234.771 73.872 234.507 72.664 234.507 71.304ZM244.971 71.352C244.971 70.536 244.811 69.84 244.491 69.264C244.171 68.672 243.739 68.224 243.195 67.92C242.651 67.6 242.067 67.44 241.443 67.44C240.819 67.44 240.243 67.592 239.715 67.896C239.187 68.2 238.755 68.648 238.419 69.24C238.099 69.816 237.939 70.504 237.939 71.304C237.939 72.104 238.099 72.808 238.419 73.416C238.755 74.008 239.187 74.464 239.715 74.784C240.259 75.104 240.835 75.264 241.443 75.264C242.067 75.264 242.651 75.112 243.195 74.808C243.739 74.488 244.171 74.04 244.491 73.464C244.811 72.872 244.971 72.168 244.971 71.352ZM259.004 64.512C260.588 64.512 261.868 65.016 262.844 66.024C263.82 67.016 264.308 68.408 264.308 70.2V78H260.948V70.656C260.948 69.6 260.684 68.792 260.156 68.232C259.628 67.656 258.908 67.368 257.996 67.368C257.068 67.368 256.332 67.656 255.788 68.232C255.26 68.792 254.996 69.6 254.996 70.656V78H251.636V64.704H254.996V66.36C255.444 65.784 256.012 65.336 256.7 65.016C257.404 64.68 258.172 64.512 259.004 64.512ZM271.416 67.464V73.896C271.416 74.344 271.52 74.672 271.728 74.88C271.952 75.072 272.32 75.168 272.832 75.168H274.392V78H272.28C269.448 78 268.032 76.624 268.032 73.872V67.464H266.448V64.704H268.032V61.416H271.416V64.704H274.392V67.464H271.416ZM281.711 69.6C281.711 67.952 282.079 66.48 282.815 65.184C283.567 63.872 284.583 62.856 285.863 62.136C287.159 61.4 288.607 61.032 290.207 61.032C292.079 61.032 293.719 61.512 295.127 62.472C296.535 63.432 297.519 64.76 298.079 66.456H294.215C293.831 65.656 293.287 65.056 292.583 64.656C291.895 64.256 291.095 64.056 290.183 64.056C289.207 64.056 288.335 64.288 287.567 64.752C286.815 65.2 286.223 65.84 285.791 66.672C285.375 67.504 285.167 68.48 285.167 69.6C285.167 70.704 285.375 71.68 285.791 72.528C286.223 73.36 286.815 74.008 287.567 74.472C288.335 74.92 289.207 75.144 290.183 75.144C291.095 75.144 291.895 74.944 292.583 74.544C293.287 74.128 293.831 73.52 294.215 72.72H298.079C297.519 74.432 296.535 75.768 295.127 76.728C293.735 77.672 292.095 78.144 290.207 78.144C288.607 78.144 287.159 77.784 285.863 77.064C284.583 76.328 283.567 75.312 282.815 74.016C282.079 72.72 281.711 71.248 281.711 69.6ZM306.853 78.216C305.573 78.216 304.421 77.936 303.397 77.376C302.373 76.8 301.565 75.992 300.973 74.952C300.397 73.912 300.109 72.712 300.109 71.352C300.109 69.992 300.405 68.792 300.997 67.752C301.605 66.712 302.429 65.912 303.469 65.352C304.509 64.776 305.669 64.488 306.949 64.488C308.229 64.488 309.389 64.776 310.429 65.352C311.469 65.912 312.285 66.712 312.877 67.752C313.485 68.792 313.789 69.992 313.789 71.352C313.789 72.712 313.477 73.912 312.853 74.952C312.245 75.992 311.413 76.8 310.357 77.376C309.317 77.936 308.149 78.216 306.853 78.216ZM306.853 75.288C307.461 75.288 308.029 75.144 308.557 74.856C309.101 74.552 309.533 74.104 309.853 73.512C310.173 72.92 310.333 72.2 310.333 71.352C310.333 70.088 309.997 69.12 309.325 68.448C308.669 67.76 307.861 67.416 306.901 67.416C305.941 67.416 305.133 67.76 304.477 68.448C303.837 69.12 303.517 70.088 303.517 71.352C303.517 72.616 303.829 73.592 304.453 74.28C305.093 74.952 305.893 75.288 306.853 75.288ZM323.622 64.512C325.206 64.512 326.486 65.016 327.462 66.024C328.438 67.016 328.926 68.408 328.926 70.2V78H325.566V70.656C325.566 69.6 325.302 68.792 324.774 68.232C324.246 67.656 323.526 67.368 322.614 67.368C321.686 67.368 320.95 67.656 320.406 68.232C319.878 68.792 319.614 69.6 319.614 70.656V78H316.254V64.704H319.614V66.36C320.062 65.784 320.63 65.336 321.318 65.016C322.022 64.68 322.79 64.512 323.622 64.512ZM336.033 67.464V73.896C336.033 74.344 336.137 74.672 336.345 74.88C336.569 75.072 336.937 75.168 337.449 75.168H339.009V78H336.897C334.065 78 332.649 76.624 332.649 73.872V67.464H331.065V64.704H332.649V61.416H336.033V64.704H339.009V67.464H336.033ZM340.562 71.304C340.562 69.96 340.826 68.768 341.354 67.728C341.898 66.688 342.626 65.888 343.538 65.328C344.466 64.768 345.498 64.488 346.634 64.488C347.626 64.488 348.49 64.688 349.226 65.088C349.978 65.488 350.578 65.992 351.026 66.6V64.704H354.41V78H351.026V76.056C350.594 76.68 349.994 77.2 349.226 77.616C348.474 78.016 347.602 78.216 346.61 78.216C345.49 78.216 344.466 77.928 343.538 77.352C342.626 76.776 341.898 75.968 341.354 74.928C340.826 73.872 340.562 72.664 340.562 71.304ZM351.026 71.352C351.026 70.536 350.866 69.84 350.546 69.264C350.226 68.672 349.794 68.224 349.25 67.92C348.706 67.6 348.122 67.44 347.498 67.44C346.874 67.44 346.298 67.592 345.77 67.896C345.242 68.2 344.81 68.648 344.474 69.24C344.154 69.816 343.994 70.504 343.994 71.304C343.994 72.104 344.154 72.808 344.474 73.416C344.81 74.008 345.242 74.464 345.77 74.784C346.314 75.104 346.89 75.264 347.498 75.264C348.122 75.264 348.706 75.112 349.25 74.808C349.794 74.488 350.226 74.04 350.546 73.464C350.866 72.872 351.026 72.168 351.026 71.352ZM356.827 71.352C356.827 69.976 357.107 68.776 357.667 67.752C358.227 66.712 359.003 65.912 359.995 65.352C360.987 64.776 362.123 64.488 363.403 64.488C365.051 64.488 366.411 64.904 367.483 65.736C368.571 66.552 369.299 67.704 369.667 69.192H366.043C365.851 68.616 365.523 68.168 365.059 67.848C364.611 67.512 364.051 67.344 363.379 67.344C362.419 67.344 361.659 67.696 361.099 68.4C360.539 69.088 360.259 70.072 360.259 71.352C360.259 72.616 360.539 73.6 361.099 74.304C361.659 74.992 362.419 75.336 363.379 75.336C364.739 75.336 365.627 74.728 366.043 73.512H369.667C369.299 74.952 368.571 76.096 367.483 76.944C366.395 77.792 365.035 78.216 363.403 78.216C362.123 78.216 360.987 77.936 359.995 77.376C359.003 76.8 358.227 76 357.667 74.976C357.107 73.936 356.827 72.728 356.827 71.352ZM376.041 67.464V73.896C376.041 74.344 376.145 74.672 376.353 74.88C376.577 75.072 376.945 75.168 377.457 75.168H379.017V78H376.905C374.073 78 372.657 76.624 372.657 73.872V67.464H371.073V64.704H372.657V61.416H376.041V64.704H379.017V67.464H376.041Z" fill="#313944"/>
4
+ <path d="M0 25.7607C0 21.6982 0.907156 18.0696 2.72147 14.8748C4.57522 11.6406 7.07976 9.13607 10.2351 7.3612C13.4299 5.54689 16.9993 4.63973 20.9435 4.63973C25.5581 4.63973 29.6009 5.82298 33.0718 8.18947C36.5426 10.556 38.9683 13.8296 40.3487 18.0104H30.8236C29.877 16.0383 28.536 14.5593 26.8005 13.5732C25.1046 12.5872 23.1325 12.0942 20.8843 12.0942C18.4784 12.0942 16.3288 12.6661 14.4356 13.8099C12.5819 14.9143 11.1225 16.4919 10.0576 18.5429C9.03212 20.5938 8.51938 22.9998 8.51938 25.7607C8.51938 28.4822 9.03212 30.8881 10.0576 32.9785C11.1225 35.0295 12.5819 36.6268 14.4356 37.7706C16.3288 38.875 18.4784 39.4272 20.8843 39.4272C23.1325 39.4272 25.1046 38.9342 26.8005 37.9481C28.536 36.9227 29.877 35.4239 30.8236 33.4518H40.3487C38.9683 37.672 36.5426 40.9654 33.0718 43.3319C29.6403 45.659 25.5976 46.8225 20.9435 46.8225C16.9993 46.8225 13.4299 45.9351 10.2351 44.1602C7.07976 42.3459 4.57522 39.8413 2.72147 36.6466C0.907156 33.4518 0 29.8232 0 25.7607Z" fill="#313944"/>
5
+ <path d="M55.706 18.7795C56.7709 17.0441 58.1514 15.6834 59.8473 14.6973C61.5828 13.7113 63.5548 13.2183 65.7636 13.2183V21.9151H63.5746C60.9714 21.9151 58.9993 22.5265 57.6583 23.7492C56.3568 24.9719 55.706 27.1017 55.706 30.1387V46.4675H47.4232V13.6916H55.706V18.7795Z" fill="#313944"/>
6
+ <path d="M101.811 29.3696C101.811 30.5528 101.732 31.6178 101.574 32.5644H77.6136C77.8108 34.9309 78.6391 36.7846 80.0984 38.1256C81.5578 39.4666 83.3524 40.1371 85.4822 40.1371C88.5586 40.1371 90.7476 38.8158 92.0492 36.1733H100.983C100.036 39.3286 98.2218 41.9317 95.5398 43.9827C92.8578 45.9942 89.5644 47 85.6597 47C82.5044 47 79.6646 46.3097 77.1403 44.9293C74.6555 43.5094 72.7031 41.5176 71.2832 38.9539C69.9028 36.3902 69.2126 33.4321 69.2126 30.0795C69.2126 26.6876 69.9028 23.7097 71.2832 21.146C72.6637 18.5823 74.5963 16.6102 77.0811 15.2298C79.566 13.8493 82.4255 13.1591 85.6597 13.1591C88.7756 13.1591 91.5562 13.8296 94.0016 15.1706C96.4864 16.5116 98.3993 18.4246 99.7403 20.9094C101.121 23.3548 101.811 26.1748 101.811 29.3696ZM93.2325 27.0031C93.193 24.8733 92.4239 23.1773 90.9251 21.9151C89.4264 20.6136 87.5923 19.9628 85.423 19.9628C83.3721 19.9628 81.6366 20.5938 80.2168 21.856C78.8363 23.0787 77.9883 24.7944 77.6728 27.0031H93.2325Z" fill="#313944"/>
7
+ <path d="M105.727 29.9612C105.727 26.6481 106.378 23.7097 107.679 21.146C109.02 18.5823 110.815 16.6102 113.063 15.2298C115.351 13.8493 117.895 13.1591 120.695 13.1591C123.14 13.1591 125.27 13.6521 127.084 14.6382C128.938 15.6242 130.417 16.8666 131.522 18.3654V13.6916H139.863V46.4675H131.522V41.6754C130.457 43.2136 128.978 44.4954 127.084 45.5209C125.231 46.507 123.081 47 120.636 47C117.875 47 115.351 46.29 113.063 44.8701C110.815 43.4502 109.02 41.4584 107.679 38.8947C106.378 36.2916 105.727 33.3138 105.727 29.9612ZM131.522 30.0795C131.522 28.068 131.127 26.3523 130.338 24.9324C129.55 23.4731 128.485 22.3687 127.144 21.6193C125.803 20.8305 124.363 20.4361 122.825 20.4361C121.287 20.4361 119.867 20.8108 118.565 21.5602C117.263 22.3096 116.199 23.4139 115.37 24.8733C114.581 26.2932 114.187 27.9891 114.187 29.9612C114.187 31.9333 114.581 33.6687 115.37 35.1675C116.199 36.6268 117.263 37.7509 118.565 38.5398C119.906 39.3286 121.326 39.723 122.825 39.723C124.363 39.723 125.803 39.3483 127.144 38.5989C128.485 37.8101 129.55 36.7057 130.338 35.2858C131.127 33.8265 131.522 32.0911 131.522 30.0795Z" fill="#313944"/>
8
+ <path d="M157.596 20.4952V36.3507C157.596 37.4551 157.853 38.2637 158.366 38.7764C158.918 39.2497 159.825 39.4864 161.087 39.4864H164.933V46.4675H159.726C152.745 46.4675 149.255 43.0755 149.255 36.2916V20.4952H145.35V13.6916H149.255V5.58633H157.596V13.6916H164.933V20.4952H157.596Z" fill="#313944"/>
9
+ <path d="M175.09 9.78686C173.631 9.78686 172.408 9.33328 171.422 8.42612C170.476 7.47952 170.003 6.316 170.003 4.93554C170.003 3.55509 170.476 2.41128 171.422 1.50412C172.408 0.557527 173.631 0.0842285 175.09 0.0842285C176.55 0.0842285 177.753 0.557527 178.699 1.50412C179.685 2.41128 180.178 3.55509 180.178 4.93554C180.178 6.316 179.685 7.47952 178.699 8.42612C177.753 9.33328 176.55 9.78686 175.09 9.78686ZM179.173 13.6916V46.4675H170.89V13.6916H179.173Z" fill="#313944"/>
10
+ <path d="M201.023 38.8356L209.305 13.6916H218.12L205.992 46.4675H195.935L183.865 13.6916H192.74L201.023 38.8356Z" fill="#313944"/>
11
+ <path d="M260.637 29.3696C260.637 30.5528 260.558 31.6178 260.4 32.5644H236.439C236.636 34.9309 237.465 36.7846 238.924 38.1256C240.383 39.4666 242.178 40.1371 244.308 40.1371C247.384 40.1371 249.573 38.8158 250.875 36.1733H259.808C258.862 39.3286 257.047 41.9317 254.365 43.9827C251.683 45.9942 248.39 47 244.485 47C241.33 47 238.49 46.3097 235.966 44.9293C233.481 43.5094 231.529 41.5176 230.109 38.9539C228.728 36.3902 228.038 33.4321 228.038 30.0795C228.038 26.6876 228.728 23.7097 230.109 21.146C231.489 18.5823 233.422 16.6102 235.907 15.2298C238.391 13.8493 241.251 13.1591 244.485 13.1591C247.601 13.1591 250.382 13.8296 252.827 15.1706C255.312 16.5116 257.225 18.4246 258.566 20.9094C259.946 23.3548 260.637 26.1748 260.637 29.3696ZM252.058 27.0031C252.019 24.8733 251.249 23.1773 249.751 21.9151C248.252 20.6136 246.418 19.9628 244.249 19.9628C242.198 19.9628 240.462 20.5938 239.042 21.856C237.662 23.0787 236.814 24.7944 236.498 27.0031H252.058Z" fill="#313944"/>
12
+ <path d="M217.42 45.3419C218.406 46.249 219.629 46.7026 221.088 46.7026C222.547 46.7026 223.75 46.249 224.697 45.3419C225.683 44.3953 226.176 43.2318 226.176 41.8513C226.176 40.4709 225.683 39.3271 224.697 38.4199C223.75 37.4733 222.547 37 221.088 37C219.629 37 218.406 37.4733 217.42 38.4199C216.473 39.3271 216 40.4709 216 41.8513C216 43.2318 216.473 44.3953 217.42 45.3419Z" fill="#663399"/>
13
+ <path d="M311.705 5.17207V46.4674H303.422V19.6077L292.359 46.4674H286.088L274.965 19.6077V46.4674H266.683V5.17207H276.089L289.223 35.8773L302.357 5.17207H311.705Z" fill="#663399"/>
14
+ <path d="M317.764 29.9611C317.764 26.648 318.415 23.7096 319.717 21.1459C321.058 18.5822 322.852 16.6101 325.1 15.2297C327.388 13.8492 329.932 13.159 332.732 13.159C335.178 13.159 337.307 13.652 339.122 14.638C340.976 15.6241 342.455 16.8665 343.559 18.3653V13.6915H351.901V46.4674H343.559V41.6752C342.494 43.2135 341.015 44.4953 339.122 45.5208C337.268 46.5068 335.119 46.9999 332.673 46.9999C329.912 46.9999 327.388 46.2899 325.1 44.87C322.852 43.4501 321.058 41.4583 319.717 38.8946C318.415 36.2915 317.764 33.3136 317.764 29.9611ZM343.559 30.0794C343.559 28.0679 343.165 26.3522 342.376 24.9323C341.587 23.473 340.522 22.3686 339.181 21.6192C337.84 20.8304 336.4 20.436 334.862 20.436C333.324 20.436 331.904 20.8107 330.602 21.56C329.301 22.3094 328.236 23.4138 327.408 24.8731C326.619 26.293 326.224 27.989 326.224 29.9611C326.224 31.9332 326.619 33.6686 327.408 35.1674C328.236 36.6267 329.301 37.7508 330.602 38.5396C331.943 39.3285 333.363 39.7229 334.862 39.7229C336.4 39.7229 337.84 39.3482 339.181 38.5988C340.522 37.81 341.587 36.7056 342.376 35.2857C343.165 33.8264 343.559 32.0909 343.559 30.0794Z" fill="#663399"/>
15
+ <path d="M368.273 13.6915V46.4674H359.99V13.6915H368.273Z" fill="#663399"/>
16
+ <path d="M384.739 2.68726V46.4674H376.456V2.68726H384.739Z" fill="#663399"/>
17
+ <path d="M360.42 8.34189C361.406 9.24905 362.629 9.70263 364.088 9.70263C365.547 9.70263 366.75 9.24905 367.697 8.34189C368.683 7.39529 369.176 6.23177 369.176 4.85131C369.176 3.47086 368.683 2.32705 367.697 1.4199C366.75 0.473298 365.547 0 364.088 0C362.629 0 361.406 0.473298 360.42 1.4199C359.473 2.32705 359 3.47086 359 4.85131C359 6.23177 359.473 7.39529 360.42 8.34189Z" fill="#313944"/>
18
+ </svg>
assets/images/wp-plugin-marketing.png ADDED
Binary file
composer.json ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "config": {
3
+ "optimize-autoloader": true
4
+ },
5
+ "require": {
6
+ "defuse/php-encryption": "^2.2",
7
+ "firebase/php-jwt": "^5.0",
8
+ "ext-curl": "*",
9
+ "ext-json": "*"
10
+ },
11
+ "autoload": {
12
+ "psr-4": {
13
+ "CreativeMail\\Managers\\": "src/managers/",
14
+ "CreativeMail\\Helpers\\": "src/helpers/",
15
+ "CreativeMail\\Modules\\": "src/modules/",
16
+ "CreativeMail\\Constants\\": "src/constants/",
17
+ "CreativeMail\\Integrations\\": "src/integrations/",
18
+ "CreativeMail\\": "src/"
19
+ }
20
+ }
21
+ }
composer.lock ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_readme": [
3
+ "This file locks the dependencies of your project to a known state",
4
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5
+ "This file is @generated automatically"
6
+ ],
7
+ "content-hash": "847414a798ac582fc4a5208f894349ce",
8
+ "packages": [
9
+ {
10
+ "name": "defuse/php-encryption",
11
+ "version": "v2.2.1",
12
+ "source": {
13
+ "type": "git",
14
+ "url": "https://github.com/defuse/php-encryption.git",
15
+ "reference": "0f407c43b953d571421e0020ba92082ed5fb7620"
16
+ },
17
+ "dist": {
18
+ "type": "zip",
19
+ "url": "https://api.github.com/repos/defuse/php-encryption/zipball/0f407c43b953d571421e0020ba92082ed5fb7620",
20
+ "reference": "0f407c43b953d571421e0020ba92082ed5fb7620",
21
+ "shasum": ""
22
+ },
23
+ "require": {
24
+ "ext-openssl": "*",
25
+ "paragonie/random_compat": ">= 2",
26
+ "php": ">=5.4.0"
27
+ },
28
+ "require-dev": {
29
+ "nikic/php-parser": "^2.0|^3.0|^4.0",
30
+ "phpunit/phpunit": "^4|^5"
31
+ },
32
+ "bin": [
33
+ "bin/generate-defuse-key"
34
+ ],
35
+ "type": "library",
36
+ "autoload": {
37
+ "psr-4": {
38
+ "Defuse\\Crypto\\": "src"
39
+ }
40
+ },
41
+ "notification-url": "https://packagist.org/downloads/",
42
+ "license": [
43
+ "MIT"
44
+ ],
45
+ "authors": [
46
+ {
47
+ "name": "Taylor Hornby",
48
+ "email": "taylor@defuse.ca",
49
+ "homepage": "https://defuse.ca/"
50
+ },
51
+ {
52
+ "name": "Scott Arciszewski",
53
+ "email": "info@paragonie.com",
54
+ "homepage": "https://paragonie.com"
55
+ }
56
+ ],
57
+ "description": "Secure PHP Encryption Library",
58
+ "keywords": [
59
+ "aes",
60
+ "authenticated encryption",
61
+ "cipher",
62
+ "crypto",
63
+ "cryptography",
64
+ "encrypt",
65
+ "encryption",
66
+ "openssl",
67
+ "security",
68
+ "symmetric key cryptography"
69
+ ],
70
+ "time": "2018-07-24T23:27:56+00:00"
71
+ },
72
+ {
73
+ "name": "firebase/php-jwt",
74
+ "version": "v5.2.0",
75
+ "source": {
76
+ "type": "git",
77
+ "url": "https://github.com/firebase/php-jwt.git",
78
+ "reference": "feb0e820b8436873675fd3aca04f3728eb2185cb"
79
+ },
80
+ "dist": {
81
+ "type": "zip",
82
+ "url": "https://api.github.com/repos/firebase/php-jwt/zipball/feb0e820b8436873675fd3aca04f3728eb2185cb",
83
+ "reference": "feb0e820b8436873675fd3aca04f3728eb2185cb",
84
+ "shasum": ""
85
+ },
86
+ "require": {
87
+ "php": ">=5.3.0"
88
+ },
89
+ "require-dev": {
90
+ "phpunit/phpunit": ">=4.8 <=9"
91
+ },
92
+ "type": "library",
93
+ "autoload": {
94
+ "psr-4": {
95
+ "Firebase\\JWT\\": "src"
96
+ }
97
+ },
98
+ "notification-url": "https://packagist.org/downloads/",
99
+ "license": [
100
+ "BSD-3-Clause"
101
+ ],
102
+ "authors": [
103
+ {
104
+ "name": "Neuman Vong",
105
+ "email": "neuman+pear@twilio.com",
106
+ "role": "Developer"
107
+ },
108
+ {
109
+ "name": "Anant Narayanan",
110
+ "email": "anant@php.net",
111
+ "role": "Developer"
112
+ }
113
+ ],
114
+ "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
115
+ "homepage": "https://github.com/firebase/php-jwt",
116
+ "keywords": [
117
+ "jwt",
118
+ "php"
119
+ ],
120
+ "time": "2020-03-25T18:49:23+00:00"
121
+ },
122
+ {
123
+ "name": "paragonie/random_compat",
124
+ "version": "v9.99.99",
125
+ "source": {
126
+ "type": "git",
127
+ "url": "https://github.com/paragonie/random_compat.git",
128
+ "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95"
129
+ },
130
+ "dist": {
131
+ "type": "zip",
132
+ "url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95",
133
+ "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95",
134
+ "shasum": ""
135
+ },
136
+ "require": {
137
+ "php": "^7"
138
+ },
139
+ "require-dev": {
140
+ "phpunit/phpunit": "4.*|5.*",
141
+ "vimeo/psalm": "^1"
142
+ },
143
+ "suggest": {
144
+ "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
145
+ },
146
+ "type": "library",
147
+ "notification-url": "https://packagist.org/downloads/",
148
+ "license": [
149
+ "MIT"
150
+ ],
151
+ "authors": [
152
+ {
153
+ "name": "Paragon Initiative Enterprises",
154
+ "email": "security@paragonie.com",
155
+ "homepage": "https://paragonie.com"
156
+ }
157
+ ],
158
+ "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
159
+ "keywords": [
160
+ "csprng",
161
+ "polyfill",
162
+ "pseudorandom",
163
+ "random"
164
+ ],
165
+ "time": "2018-07-02T15:55:56+00:00"
166
+ }
167
+ ],
168
+ "packages-dev": [],
169
+ "aliases": [],
170
+ "minimum-stability": "stable",
171
+ "stability-flags": [],
172
+ "prefer-stable": false,
173
+ "prefer-lowest": false,
174
+ "platform": {
175
+ "ext-curl": "*",
176
+ "ext-json": "*"
177
+ },
178
+ "platform-dev": [],
179
+ "plugin-api-version": "1.1.0"
180
+ }
creative-mail-plugin.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ use CreativeMail\CreativeMail;
3
+
4
+ /**
5
+ * Plugin Name: Creative Mail by Constant Contact
6
+ * Plugin URI: https://bitbucket.org/creativemail/creativemail-for-wordpress/src
7
+ * Description: Power your WooCommerce Store or WordPress Blog with simple & free email marketing from Constant Contact. With the official Creative Mail for WooCommerce plugin, your products, blog posts, images and store links are automatically included as rich shoppable email marketing content for your customers. Our included CRM also intelligently pulls in and identifies your WordPress site contacts and WooCommerce store customers. That makes it easy to build audiences and send targeted customer campaigns. Get free email marketing, 98% deliverability, and Constant Contact rock solid reliability all without ever needing to leave your WP Admin.
8
+ * Author: Constant Contact
9
+ * Version: 1.0.0
10
+ * Author URI: https://www.constantcontact.com
11
+ */
12
+
13
+ function _load_ce4wp_plugin() {
14
+
15
+ global $creativemail;
16
+
17
+ if($creativemail != null){
18
+ return true;
19
+ }
20
+
21
+ define('CE4WP_PLUGIN_DIR', __DIR__ . '/');
22
+ define('CE4WP_PLUGIN_URL', plugin_dir_url(__FILE__) . '/');
23
+ define('CE4WP_PLUGIN_VERSION', '1.0.0');
24
+ define('CE4WP_INSTANCE_UUID_KEY', 'ce4wp_instance_uuid');
25
+ define('CE4WP_INSTANCE_HANDSHAKE_TOKEN', 'ce4wp_handshake_token');
26
+ define('CE4WP_INSTANCE_HANDSHAKE_EXPIRATION', 'ce4wp_handshake_expiration');
27
+ define('CE4WP_INSTANCE_ID_KEY', 'ce4wp_instance_id');
28
+ define('CE4WP_INSTANCE_API_KEY_KEY', 'ce4wp_instance_api_key');
29
+ define('CE4WP_ENCRYPTION_KEY_KEY', 'ce4wp_encryption_key');
30
+ define('CE4WP_CONNECTED_ACCOUNT_ID', 'ce4wp_connected_account_id');
31
+ define('CE4WP_ACTIVATED_PLUGINS', 'ce4wp_activated_plugins');
32
+ define('CE4WP_ACCEPTED_CONSENT', 'ce4wp_accepted_consent');
33
+ define('CE4WP_SYNCHRONIZE_ACTION', 'ce4wp_synchronize_contacts');
34
+ define('CE4WP_APP_GATEWAY_URL', 'https://app-gateway.creativemail.com/');
35
+ define('CE4WP_APP_URL', 'https://app.creativemail.com/');
36
+ define('CE4WP_ENVIRONMENT', 'PRODUCTION');
37
+ define('CE4WP_BATCH_SIZE', 500);
38
+
39
+ // Load all the required files
40
+ if (file_exists(__DIR__ . '/vendor/autoload.php')) {
41
+ require_once __DIR__ . '/vendor/autoload.php';
42
+ }
43
+
44
+ $creativemail = CreativeMail::get_instance();
45
+ $creativemail->add_hooks();
46
+
47
+ return true;
48
+ }
49
+
50
+ add_action('plugins_loaded', '_load_ce4wp_plugin', 10);
readme.txt ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === Creative Mail – Easier WordPress & WooCommerce Email Marketing ===
2
+ Tags: email, marketing, newsletter, subscribe, contact form 7, woocommerce, constant contact
3
+ Requires at least: 4.6
4
+ Tested up to: 5.4.1
5
+ Stable tag: 1.0.0
6
+ License: GPLv2 or later
7
+ License URI: http://www.gnu.org/licenses/gpl-2.0.html
8
+ Requires PHP: 7.1
9
+ Website: https://www.creativemail.com
10
+
11
+ == Description ==
12
+ Creative Mail was designed specifically for WordPress and WooCommerce.
13
+
14
+ Our intelligent (and super fun) email editor simplifies email creation and pulls your WordPress blog posts, website images and WooCommerce products right into your email content. Leads from your WordPress website, ecommerce store and contact forms are automatically captured and routed into our included Contacts CRM and synced with your email marketing lists.
15
+ It’s perfect for newsletters and announcements, to promote events, share product specials, retarget ecommerce shoppers, send postcards, provide updates and more.
16
+
17
+ Create awesome emails right from your WordPress Admin Dashboard that are all powered by the award-winning & rock-solid reliability of Constant Contact.
18
+
19
+ ## Features:
20
+ ### WOOCOMMERCE & WORDPRESS INTEGRATION:
21
+ Turn your WooCommerce store and your WordPress site into efficient marketing engines. All e-commerce purchases and form entries are all captured in our included CRM and synced automatically with Creative Mail.
22
+
23
+ - **Enhanced Ecommerce:** WooCommerce store purchase emails and ecommerce inquiries are all captured automatically within your email marketing list. Retarget and re-engage your customers. Sell more stuff.
24
+ - **Better Transactional Emails:** Standard WooCommerce triggered emails can be replaced to match your branding and style. Hey, style matters. (Coming Soon)
25
+ - **WordPress Forms Integration:** Collect and sync subscribers and site visitors directory into the Creative Mail Contacts CRM.
26
+ - **Build Better Branding:** Creative includes our free LogoMaker and image editing suite to enhance your brand.
27
+ - **Amazing Unsplash Images:** You get free access to the completely integrated Unsplash photo library (in addition to your own WordPress media library) to make amazing campaigns with award winning images. Unsplash is simply awesome!
28
+ - **Get Better Deliverability:** Other email solutions require complex SMTP solutions, external gateways or have you sending from their less than stellar IPs. As a result, your emails can get bounced or never delivered. Creative Mail is an all in one solution that uses Constant Contact’s rock solid infrastructure, for superior deliverability. Boom! ‘nuff said.
29
+ - **Live Support:** With our paid plans you get access to phone and chat support to help you get answers from real live, nice humans. Imagine that!
30
+
31
+ ### OPT-IN EMAIL FORMS:
32
+ - **WordPress Website Forms:** Creative Mail detects the current website forms used on your site, and automatically adds contacts to your email lists. Automagically awesome!
33
+ - **JMML Form Options:** Create Mail includes a JMML (join my mailing list) form. When activated, contacts who sign up for your mailing list through one of our JMML forms are sent an automatic “Confirm Opt-in” message to the email address they provided, asking them to confirm their subscription. (Coming Soon)
34
+
35
+ ### EMAIL AUTOMATIONS:
36
+ - **Scheduled Sends:** Schedule the time and date of outgoing emails based on your business or organizations needs.
37
+ - **Single-Step Automations:** Replace your non-branded triggered emails with on-brand Creative Mail emails for deeper customer engagement.
38
+ - **Multi-Step Automations:** Develop sophisticated CLM (that’s marketing speak for - customer lifecycle marketing) campaigns by leveraging our “if this, then that” campaign automation engine that responds to a customers actions or purchases. (Coming Soon)
39
+
40
+ ### ANALYTICS & INSIGHTS:
41
+ - **Realtime Email Statistics:** Bounces, opens, clicks, forwards, complaints, unsubscribes and more are easily tracked and managed. Be a control freak, it’s OK.
42
+ - **Campaign Mapview:** With our mapview you can see who's opening your emails on what devices on an interactive visual map.
43
+
44
+ ### CREATIVE CONTACTS CRM:
45
+ - **Contact Lists:** Within the Creative Mail Contacts CRM you can quickly and easily manage all your Contacts, Subscribers and Unsubscribes.
46
+ - **Contact Activity:** Drill into the purchases and behaviors of your contacts.
47
+ - **List Sources:** You’ll know where your contacts come from whether it’s manual entry, your WordPress website form, WooCommerce Store, or another defined source.
48
+ - **Custom Labels:** Further refine your marketing by adding custom labels to subscribers or customers (ex. Truck Buyers, Concert Attendee, Dog Owners, etc.).
49
+
50
+ ### IMPORT & EXPORT:
51
+ - **Contacts Sync & Import:** We’ll do the heavy lifting to sync and import your WordPress or WooCommerce contacts automatically.
52
+ - **Import & Export Via CSV:** From our Creative Mail Contacts CRM you can import bulk lists (limits may apply), add subscribers one by one, or export your contacts into a CSV file.
53
+
54
+ ### CAMPAIGNS:
55
+ - **AI Emails:** Forget templates, let our A.I. build emails for you. Pull in WordPress posts or products for sale and you’re good to go. Let our robot help!
56
+ - **Email Campaign:** Build your campaigns in seconds from Your WordPress Admin Dashboard.
57
+ - **Awesome Deliverability:** Your campaigns are sent and delivered by the award-winning power of Constant Contact technology. We got you.
58
+ - **Email Automations:** Send multi-step emails automatically based on triggers you define, whether that's based on time or behavioral actions. (Coming Soon)
59
+
60
+ ### EMAIL LIST MANAGEMENT:
61
+ - **Join My Mailing List:** Creative Mail collects leads from our opt-in form (Join My Mailing List) or the top WordPress lead capture forms and adds them directly to your email lists. (Coming Soon)
62
+ - **Automate email:** With our “Welcome” email trigger you can send a Creative Mail welcome message to new subscribers.
63
+ - **Auto List Updater:** Creative Mail automatically updates your contact lists for email bounces or unsubscribes.
64
+
65
+ ## CREATIVE MAIL IS:
66
+ 1. Incredibly easy WordPress email marketing
67
+ 1. Deeply connected to your website & WooCommerce store
68
+ 1. Accessed from within your WP Admin Dashboard
69
+ 1. Automatically syncing your contacts and building your marketing lists
70
+ 1. Powered by the reliability superior deliverability of Constant Contact
71
+ 1. Fun, which makes life way better :)
72
+
73
+ ## TERMS OF SERVICE & PRIVACY POLICY
74
+ On behalf of our lawyers (seriously, they’re nice people), please feel free to review our:
75
+ Creative Mail by Constant Contact [Terms of Service](https://www.constantcontact.com/website/terms)
76
+ Creative Mail by Constant Contact [Privacy Policy](https://www.endurance.com/privacy/privacy)
77
+
78
+ == Screenshots ==
79
+ 1. Our Awesome Email Editor
80
+ 2. Style your campaign
81
+ 3. All of your Contacts in one place
82
+ 4. Your Awesome Campaigns
83
+ 5. Open your email marketing from your WP-admin dashboard
84
+
85
+ == Changelog ==
86
+ * 1.0.0 Initial Commit.
src/constants/environment-names.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace CreativeMail\Constants;
4
+
5
+ class EnvironmentNames
6
+ {
7
+ const DEVELOPMENT = 'DEVELOP';
8
+ const PRODUCTION = 'PRODUCTION';
9
+ }
src/creativemail.php ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ namespace CreativeMail;
5
+
6
+ use CreativeMail\Managers\AdminManager;
7
+ use CreativeMail\Managers\ApiManager;
8
+ use CreativeMail\Managers\InstanceManager;
9
+ use CreativeMail\Managers\IntegrationManager;
10
+
11
+ class CreativeMail
12
+ {
13
+ private static $instance;
14
+
15
+ private $admin_manager;
16
+ private $api_manager;
17
+ private $instance_manager;
18
+ private $integration_manager;
19
+
20
+
21
+ public function __construct() {
22
+
23
+ if (current_user_can('administrator')) {
24
+
25
+ $this->admin_manager = new AdminManager();
26
+ $this->admin_manager->add_hooks();
27
+
28
+ }
29
+
30
+ $this->instance_manager = new InstanceManager();
31
+ $this->api_manager = new ApiManager();
32
+ $this->integration_manager = new IntegrationManager();
33
+
34
+ }
35
+
36
+ public function add_hooks() {
37
+
38
+ if ($this->admin_manager !== null) {
39
+ $this->admin_manager->add_hooks();
40
+ }
41
+
42
+ $this->api_manager->add_hooks();
43
+ $this->integration_manager->add_hooks();
44
+ $this->instance_manager->add_hooks();
45
+ }
46
+
47
+ public function get_integration_manager() {
48
+ return $this->integration_manager;
49
+ }
50
+
51
+ public function get_instance_manager() {
52
+ return $this->instance_manager;
53
+ }
54
+
55
+ public function get_api_manager() {
56
+ return $this->api_manager;
57
+ }
58
+
59
+ public function get_admin_manager() {
60
+ return $this->admin_manager;
61
+ }
62
+
63
+ public static function get_instance() {
64
+
65
+ if (self::$instance === null){
66
+ self::$instance = new CreativeMail();
67
+ }
68
+
69
+ return self::$instance;
70
+ }
71
+ }
src/helpers/encryption-helper.php ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ namespace CreativeMail\Helpers;
5
+
6
+ use Defuse\Crypto\Crypto;
7
+ use Defuse\Crypto\Key;
8
+
9
+ class EncryptionHelper {
10
+
11
+ /**
12
+ * Will get the previously used encryption key, or will generate a new key of no key is present.
13
+ * @return Key
14
+ * @throws \Defuse\Crypto\Exception\BadFormatException
15
+ * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException
16
+ */
17
+ private static function get_encryption_key()
18
+ {
19
+ $key = get_option(CE4WP_ENCRYPTION_KEY_KEY, null);
20
+ if ($key === null) {
21
+ $key = Key::createNewRandomKey();
22
+ update_option(CE4WP_ENCRYPTION_KEY_KEY, $key->saveToAsciiSafeString());
23
+ }
24
+ else {
25
+ $key = Key::loadFromAsciiSafeString($key);
26
+ }
27
+
28
+ return $key;
29
+ }
30
+
31
+ /**
32
+ * Will update an existing option or create the option if it is not available.
33
+ *
34
+ * @param $option string The name of the option.
35
+ * @param $value mixed The value that should be stored encrypted
36
+ * @param $autoload bool Should this option be auto loaded.
37
+ *
38
+ * @return bool
39
+ * @throws \Defuse\Crypto\Exception\BadFormatException
40
+ * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException
41
+ */
42
+ public static function update_option($option, $value, $autoload = null)
43
+ {
44
+ return update_option($option, Crypto::encrypt( $value, self::get_encryption_key()), $autoload );
45
+ }
46
+
47
+ /**
48
+ * Will store and encrypt the option.
49
+ *
50
+ * @param $option string The name of the option.
51
+ * @param $value mixed The value that should be stored encrypted
52
+ * @param $autoload bool Should this option be auto loaded.
53
+ *
54
+ * @throws \Defuse\Crypto\Exception\BadFormatException
55
+ * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException
56
+ */
57
+ public static function add_option($option, $value, $autoload = true)
58
+ {
59
+ add_option( $option, Crypto::encrypt($value, self::get_encryption_key()), '', $autoload );
60
+ }
61
+
62
+ /**
63
+ * Will load and decrypt the option.
64
+ *
65
+ * @param $option string The name of the option you want to load.
66
+ * @param bool $default The fallback value that should be used when the option is not available.
67
+ *
68
+ * @return mixed
69
+ */
70
+ public static function get_option($option, $default = false)
71
+ {
72
+ $encrypted = get_option( $option, $default );
73
+ if ( $encrypted === $default ) {
74
+ return $default;
75
+ } else {
76
+ try {
77
+ return Crypto::decrypt( $encrypted, self::get_encryption_key() );
78
+ } catch ( \Exception $e ) {
79
+ return $encrypted;
80
+ }
81
+ }
82
+ }
83
+ }
src/helpers/environment-helper.php ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ namespace CreativeMail\Helpers;
5
+
6
+ use CreativeMail\Constants\EnvironmentNames;
7
+ use http\Env;
8
+
9
+ /**
10
+ * Class EnvironmentHelper
11
+ * @package CreativeMail\Helpers
12
+ */
13
+ class EnvironmentHelper
14
+ {
15
+ /**
16
+ * Determines if the plugin is currently pointing towards a test environment.
17
+ *
18
+ * @returns bool
19
+ */
20
+ public static function is_test_environment() {
21
+ return self::get_environment() !== EnvironmentNames::PRODUCTION;
22
+ }
23
+
24
+ /**
25
+ * Gets the name of the environment this version of the plugin is build for.
26
+ * @return string
27
+ */
28
+ public static function get_environment() {
29
+
30
+ $environment = CE4WP_ENVIRONMENT;
31
+ if ($environment === "{ENV}") {
32
+ return EnvironmentNames::PRODUCTION;
33
+ }
34
+
35
+ return $environment;
36
+ }
37
+
38
+ /**
39
+ * Gets the url of the app-gateway.
40
+ * @param null $path
41
+ * @return string
42
+ */
43
+ public static function get_app_gateway_url($path = null) {
44
+ $url = CE4WP_APP_GATEWAY_URL;
45
+ if ($url === '{GATEWAY_URL}') {
46
+ $url = 'https://app-gateway.creativemail.com/';
47
+ }
48
+
49
+ if (is_null($path)) {
50
+ return $url;
51
+ }
52
+
53
+ if (isset($path) && !empty($path))
54
+ {
55
+ return $url.$path;
56
+ }
57
+
58
+ return $url;
59
+ }
60
+
61
+ /**
62
+ * Gets the url of the app.
63
+ * @return string
64
+ */
65
+ public static function get_app_url() {
66
+ $url = CE4WP_APP_URL;
67
+ if ($url === '{APP_URL}') {
68
+ return 'https://app.creativemail.com/';
69
+ }
70
+
71
+ return $url;
72
+ }
73
+ }
src/helpers/guid-helper.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace CreativeMail\Helpers;
4
+
5
+ /**
6
+ * Class GuidHelper
7
+ * @package CreativeMail\Helpers
8
+ */
9
+ class GuidHelper
10
+ {
11
+
12
+ public static function generate_guid()
13
+ {
14
+
15
+ return sprintf('%04X%04X-%04X-%04X-%04X-%04X%04X%04X', mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(16384, 20479), mt_rand(32768, 49151), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535));
16
+ }
17
+
18
+ }
src/helpers/options-helper.php ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace CreativeMail\Helpers;
4
+
5
+ /**
6
+ * Class CE4WP_OptionsHelper
7
+ * Exposes a wrapper around all the options that we register within the plugin.
8
+ * @package CreativeMail\Helpers
9
+ * @access private
10
+ */
11
+ class OptionsHelper
12
+ {
13
+ /**
14
+ * Gets the generated unique id for this WP instance, or will generate a new unique id if none is present.
15
+ * @return string
16
+ */
17
+ public static function get_instance_uuid()
18
+ {
19
+
20
+ // Do we already have a UUID?
21
+ $instanceUuid = get_option(CE4WP_INSTANCE_UUID_KEY, null);
22
+ if ($instanceUuid === null) {
23
+
24
+ // Just generate one and store it
25
+ $instanceUuid = uniqid();
26
+ add_option(CE4WP_INSTANCE_UUID_KEY, $instanceUuid);
27
+ }
28
+
29
+ return $instanceUuid;
30
+ }
31
+
32
+ /**
33
+ * Gets the generated handshake token that should be used during setup.
34
+ * @return string
35
+ */
36
+ public static function get_handshake_token()
37
+ {
38
+
39
+ // Do we already have a UUID?
40
+ $token = get_option(CE4WP_INSTANCE_HANDSHAKE_TOKEN, null);
41
+ $expiration = self::get_handshake_expiration();
42
+ if ($token === null || $expiration === null || $expiration < time()) {
43
+
44
+ // No token is known or it expired, generate a new one
45
+ $token = GuidHelper::generate_guid();
46
+ update_option(CE4WP_INSTANCE_HANDSHAKE_TOKEN, $token);
47
+ update_option(CE4WP_INSTANCE_HANDSHAKE_EXPIRATION, time() + 3600);
48
+ }
49
+
50
+ return $token;
51
+ }
52
+
53
+ /**
54
+ * Gets the expiration time associated with the generated handshake token.
55
+ * @return int|null
56
+ */
57
+ public static function get_handshake_expiration()
58
+ {
59
+ return get_option(CE4WP_INSTANCE_HANDSHAKE_EXPIRATION, null);
60
+ }
61
+
62
+ /**
63
+ * Gets the assigned instance id.
64
+ * @return int|null
65
+ */
66
+ public static function get_instance_id()
67
+ {
68
+ return get_option(CE4WP_INSTANCE_ID_KEY, null);
69
+ }
70
+
71
+ /**
72
+ * Sets the assigned instance id that is generated when connecting this WP instance to the Creative Mail account.
73
+ * @param $value int
74
+ */
75
+ public static function set_instance_id($value)
76
+ {
77
+ add_option(CE4WP_INSTANCE_ID_KEY, $value);
78
+ }
79
+
80
+ /**
81
+ * Gets the id of the account that is connected to the combination of this WP unique id and Creative Mail account id.
82
+ * @return int|null
83
+ */
84
+ public static function get_connected_account_id()
85
+ {
86
+ return get_option(CE4WP_CONNECTED_ACCOUNT_ID, null);
87
+ }
88
+
89
+ /**
90
+ * Sets the id of the account that is connected to the combination of this WP unique id and Creative Mail account id.
91
+ * @param $value int
92
+ */
93
+ public static function set_connected_account_id($value)
94
+ {
95
+ add_option(CE4WP_CONNECTED_ACCOUNT_ID, $value);
96
+ }
97
+
98
+ /**
99
+ * Gets the API key that can be used to interact with the Creative Mail platform.
100
+ * @return string|null
101
+ */
102
+ public static function get_instance_api_key()
103
+ {
104
+ return EncryptionHelper::get_option(CE4WP_INSTANCE_API_KEY_KEY, null);
105
+ }
106
+
107
+ /**
108
+ * Sets the API key that can be used to interact with the Creative Mail platform.
109
+ *
110
+ * @param $value string
111
+ *
112
+ * @throws \Defuse\Crypto\Exception\BadFormatException
113
+ * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException
114
+ */
115
+ public static function set_instance_api_key($value)
116
+ {
117
+ EncryptionHelper::add_option(CE4WP_INSTANCE_API_KEY_KEY, $value);
118
+ }
119
+
120
+ /**
121
+ * Gets a string representing all the plugins that were activated for synchronization during the setup process.
122
+ * @return string|array
123
+ */
124
+ public static function get_activated_plugins()
125
+ {
126
+ return get_option(CE4WP_ACTIVATED_PLUGINS, array());
127
+ }
128
+
129
+ /**
130
+ * Sets a string representing all the plugins that were activated for synchronization during the setup process.
131
+ * @param $plugins
132
+ */
133
+ public static function set_activated_plugins($plugins)
134
+ {
135
+ update_option(CE4WP_ACTIVATED_PLUGINS, $plugins);
136
+ }
137
+
138
+ /**
139
+ * Gets an int value representing when the user did accept the terms on our consent screen.
140
+ * @return int|null
141
+ */
142
+ public static function get_consent_accept_date()
143
+ {
144
+ return get_option(CE4WP_ACCEPTED_CONSENT, null);
145
+ }
146
+
147
+ /**
148
+ * Sets the current time value indicated the user accepted the terms on the consent screen.
149
+ */
150
+ public static function set_did_accept_consent()
151
+ {
152
+ update_option(CE4WP_ACCEPTED_CONSENT, time());
153
+ }
154
+
155
+ /**
156
+ * Will clear all the registered options for this plugin.
157
+ * Only the Unique Id won't be cleared so that we can restore the link when the plugin is reactivated.
158
+ *
159
+ * @param $clear_all bool When set to 'true' the instance UUID will be re-generated, this will cause the link between the plugin and the user account to break.
160
+ */
161
+ public static function clear_options($clear_all)
162
+ {
163
+ delete_option(CE4WP_INSTANCE_ID_KEY);
164
+ delete_option(CE4WP_INSTANCE_API_KEY_KEY);
165
+ delete_option(CE4WP_CONNECTED_ACCOUNT_ID);
166
+ delete_option(CE4WP_ACTIVATED_PLUGINS);
167
+ delete_option(CE4WP_ACCEPTED_CONSENT);
168
+
169
+ if($clear_all === true) {
170
+ delete_option(CE4WP_INSTANCE_UUID_KEY);
171
+ delete_option(CE4WP_ENCRYPTION_KEY_KEY);
172
+ }
173
+ }
174
+ }
src/helpers/sso-helper.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ namespace CreativeMail\Helpers;
5
+
6
+ use Exception;
7
+
8
+ /**
9
+ * Class SsoHelper
10
+ * @package CreativeMail\Helpers
11
+ */
12
+ class SsoHelper
13
+ {
14
+
15
+ /**
16
+ * Will request a one-time use link that can be used to initiate a single sign on into the Creative Mail product.
17
+ * @param $instanceId int
18
+ * @param $apiKey string
19
+ * @param $connectedAccountId int
20
+ * @return string|null Returns the sso link or null if the link could not be generated.
21
+ * @throws Exception When one of the required arguments is not present.
22
+ */
23
+ public static function generate_sso_link($instanceId, $apiKey, $connectedAccountId)
24
+ {
25
+
26
+ if(!isset($instanceId)) throw new Exception("Please provide a valid siteId");
27
+ if(!isset($apiKey)) throw new Exception("Please provide a valid apiKey");
28
+ if(!isset($connectedAccountId)) throw new Exception("Please provide a valid connectedAccountId");
29
+
30
+ // Build the request
31
+ $arguments = array(
32
+ 'method' => 'POST',
33
+ 'headers' => array(
34
+ 'x-api-key' => $apiKey,
35
+ 'x-account-id' => $connectedAccountId,
36
+ 'content-type' => 'application/json'
37
+ ),
38
+ 'body' => wp_json_encode(array(
39
+ 'instance_url' => get_bloginfo('wpurl'),
40
+ 'plugin_version' => CE4WP_PLUGIN_VERSION,
41
+ 'word_press_version' => get_bloginfo('version')
42
+ ))
43
+ );
44
+
45
+ $response = wp_remote_post(EnvironmentHelper::get_app_gateway_url() . 'wordpress/v1.0/account/sso', $arguments);
46
+ if (is_wp_error($response)) {
47
+ return null;
48
+ }
49
+
50
+ $properties = json_decode($response["body"], true);
51
+
52
+ if ($properties === null) {
53
+ return null;
54
+ }
55
+ if(array_key_exists('login_url', $properties)) {
56
+ return $properties['login_url'];
57
+ }
58
+
59
+ return null;
60
+ }
61
+ }
src/integrations/integration.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace CreativeMail\Integrations;
4
+
5
+ /**
6
+ * Class Integration
7
+ *
8
+ * Describes an integration between Creative Mail and WordPress.
9
+ *
10
+ * @package CreativeMail\Integrations
11
+ */
12
+ class Integration
13
+ {
14
+ private $name;
15
+ private $class;
16
+ private $integrationHandler;
17
+ private $slug;
18
+
19
+ /**
20
+ * Integration constructor.
21
+ *
22
+ * @param $slug string The slug that you want to use for this integration.
23
+ * @param $name string The display name of the plugin
24
+ * @param $class string The path the the plugin class that should be used to check if the plugin required for this integration is installed.
25
+ * @param $integration_handler string The name of the class that should be instantiated when this integration gets activated.
26
+ */
27
+ public function __construct($slug, $name, $class, $integration_handler)
28
+ {
29
+ $this->slug = $slug;
30
+ $this->name = $name;
31
+ $this->class = $class;
32
+ $this->integrationHandler = $integration_handler;
33
+ }
34
+
35
+ /**
36
+ * Gets the slug assigned to this integration.
37
+ * @return string
38
+ */
39
+ public function get_slug() {
40
+ return $this->slug;
41
+ }
42
+
43
+ /**
44
+ * Gets the display name assigned to this integration.
45
+ * @return string
46
+ */
47
+ public function get_name() {
48
+ return $this->name;
49
+ }
50
+
51
+ /**
52
+ * Gets the path to the main class of the plugin that is required for this integration.
53
+ * @return string
54
+ */
55
+ public function get_class() {
56
+ return $this->class;
57
+ }
58
+
59
+ /**
60
+ * Gets the name of the class that should be instantiated when activating this integration.
61
+ * @return string
62
+ */
63
+ public function get_integration_handler() {
64
+ return $this->integrationHandler;
65
+ }
66
+ }
src/managers/admin-manager.php ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace CreativeMail\Managers;
4
+
5
+ use CreativeMail\Helpers\EnvironmentHelper;
6
+ use CreativeMail\Helpers\OptionsHelper;
7
+ use CreativeMail\Helpers\SsoHelper;
8
+ use Exception;
9
+
10
+ /**
11
+ * The AdminManager will manage the admin section of the plugin.
12
+ *
13
+ * @ignore
14
+ */
15
+ class AdminManager
16
+ {
17
+
18
+ protected $instance_name;
19
+ protected $instance_uuid;
20
+ protected $instance_handshake_token;
21
+ protected $instance_id;
22
+ protected $instance_url;
23
+ protected $instance_callback_url;
24
+ protected $dashboard_url;
25
+
26
+ /**
27
+ * AdminManager constructor.
28
+ */
29
+ public function __construct()
30
+ {
31
+ $this->instance_name = rawurlencode(get_bloginfo('name'));
32
+ $this->instance_handshake_token = OptionsHelper::get_handshake_token();
33
+ $this->instance_uuid = OptionsHelper::get_instance_uuid();
34
+ $this->instance_id = OptionsHelper::get_instance_id();
35
+ $this->instance_url = rawurlencode(get_bloginfo('wpurl'));
36
+ $this->instance_callback_url = rawurlencode(get_bloginfo('wpurl') . '?rest_route=/creativemail/v1/callback');
37
+ $this->dashboard_url = EnvironmentHelper::get_app_url() . 'marketing/dashboard?wp_site_name=' . $this->instance_name
38
+ . '&wp_site_uuid=' . $this->instance_uuid
39
+ . '&wp_callback_url=' . $this->instance_callback_url
40
+ . '&wp_instance_url=' . $this->instance_url
41
+ . '&wp_version=' . get_bloginfo('version')
42
+ . '&plugin_version=' . CE4WP_PLUGIN_VERSION;
43
+ }
44
+
45
+ /**
46
+ * Will register all the hooks for the admin portion of the plugin.
47
+ */
48
+ public function add_hooks()
49
+ {
50
+ add_action('admin_menu', array( $this, 'build_menu' ));
51
+ add_action('admin_enqueue_scripts', array( $this, 'add_assets' ));
52
+ }
53
+
54
+ /**
55
+ * Will add all the required assets for the admin portion of the plugin.
56
+ */
57
+ public function add_assets()
58
+ {
59
+ wp_register_style('ce4wp_admin', CE4WP_PLUGIN_URL . 'assets/css/admin.css', null, CE4WP_PLUGIN_VERSION);
60
+ wp_enqueue_style( 'ce4wp_admin');
61
+ }
62
+
63
+ /**
64
+ * Will build the menu for WP-Admin
65
+ */
66
+ public function build_menu()
67
+ {
68
+ // Did the user complete the entire setup?
69
+ $main_action = OptionsHelper::get_instance_id() !== null
70
+ ? array( $this, 'show_dashboard' )
71
+ : array( $this, 'show_setup' );
72
+
73
+ // Create the root menu item
74
+ $icon = file_get_contents( CE4WP_PLUGIN_DIR . 'assets/images/icon.svg');
75
+ add_menu_page('Creative Mail', esc_html__('Creative Mail'), 'manage_options', 'creativemail', $main_action, 'data:image/svg+xml;base64,' . base64_encode( $icon ), '99.68491');
76
+
77
+ $sub_actions = array(
78
+ array(
79
+ 'title' => esc_html__( 'Settings', 'ce4wp' ),
80
+ 'text' => 'Settings',
81
+ 'slug' => 'creativemail_settings',
82
+ 'callback' => array( $this, 'show_settings_page' )
83
+ )
84
+ );
85
+
86
+ foreach ($sub_actions as $sub_action) {
87
+ add_submenu_page( 'creativemail', 'Creative Mail - ' . $sub_action['title'], $sub_action['text'], 'manage_options', $sub_action['slug'], $sub_action['callback']);
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Renders the onboarding flow.
93
+ */
94
+ public function show_setup()
95
+ {
96
+ $accepted_consent = OptionsHelper::get_consent_accept_date();
97
+ if($accepted_consent === null) {
98
+ $this->show_consent();
99
+ return;
100
+ }
101
+
102
+ require CE4WP_PLUGIN_DIR . 'src/views/onboarding.php';
103
+ }
104
+
105
+ /**
106
+ * Renders the consent screen.
107
+ */
108
+ public function show_consent()
109
+ {
110
+ require CE4WP_PLUGIN_DIR . 'src/views/consent.php';
111
+ }
112
+
113
+ /**
114
+ * Renders the Creative Mail dashboard when the site is connected to an account.
115
+ */
116
+ public function show_dashboard()
117
+ {
118
+ // If all the three values are available, we can use the SSO flow
119
+ $instance_id = OptionsHelper::get_instance_id();
120
+ $instance_api_key = OptionsHelper::get_instance_api_key();
121
+ $connected_account_id = OptionsHelper::get_connected_account_id();
122
+
123
+ if (isset($instance_id) && isset($instance_api_key) && isset($connected_account_id)) {
124
+ try {
125
+ $sso_link = SsoHelper::generate_sso_link($instance_id, $instance_api_key, $connected_account_id);
126
+ if(isset($sso_link)){
127
+ $this->dashboard_url = $sso_link;
128
+ }
129
+ }
130
+ catch(Exception $ex) { }
131
+ }
132
+
133
+ require CE4WP_PLUGIN_DIR . 'src/views/dashboard.php';
134
+ }
135
+
136
+ /**
137
+ * Renders the settings page for this plugin.
138
+ */
139
+ public function show_settings_page()
140
+ {
141
+ require CE4WP_PLUGIN_DIR . 'src/views/settings.php';
142
+ }
143
+ }
src/managers/api-manager.php ADDED
@@ -0,0 +1,214 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ namespace CreativeMail\Managers;
5
+
6
+ use CreativeMail\CreativeMail;
7
+ use CreativeMail\Helpers\OptionsHelper;
8
+ use CreativeMail\Modules\WooCommerce\Models\WCProductModel;
9
+ use CreativeMail\Modules\Blog\Models\BlogInformation;
10
+ use CreativeMail\Modules\Blog\Models\BlogPost;
11
+ use CreativeMail\Modules\Blog\Models\BlogAttachment;
12
+ use WP_Error;
13
+ use WP_REST_Response;
14
+
15
+ /**
16
+ * Class ApiManager
17
+ * @package CreativeMail\Managers
18
+ */
19
+ class ApiManager
20
+ {
21
+ const api_namespace = "creativemail/v1";
22
+
23
+ function __construct()
24
+ {
25
+ }
26
+
27
+ /**
28
+ * Will add all the hooks that are required to setup our plugin API.
29
+ */
30
+ public function add_hooks() {
31
+
32
+ add_action( 'rest_api_init', array($this, 'add_rest_endpoints'));
33
+
34
+ }
35
+
36
+ public function add_rest_endpoints() {
37
+ // Add the endpoint to handle the callback
38
+ $routes = array (
39
+ array (
40
+ 'path' => '/callback',
41
+ 'methods' => 'POST',
42
+ 'callback' => array(CreativeMail::get_instance()->get_instance_manager(), 'handle_callback'),
43
+ 'require_wp_admin' => false,
44
+ 'require_api_key' => false
45
+ ),
46
+ array (
47
+ 'path' => '/available_plugins',
48
+ 'methods' => 'GET',
49
+ 'callback' => function() {
50
+ $result = array();
51
+ $plugins = CreativeMail::get_instance()->get_integration_manager()->get_active_plugins();
52
+ foreach ($plugins as $activePlugin) {
53
+ array_push($result, array(
54
+ 'name' => $activePlugin->get_name(),
55
+ 'slug' => $activePlugin->get_slug()
56
+ ));
57
+ }
58
+
59
+ return new WP_REST_Response($result, 200);
60
+ }
61
+ ),
62
+ array (
63
+ 'path' => '/available_plugins',
64
+ 'methods' => 'POST',
65
+ 'callback' => function($request) {
66
+ CreativeMail::get_instance()->get_integration_manager()->set_activated_plugins(json_decode($request->get_body()));
67
+ }
68
+ ),
69
+ array (
70
+ 'path' => '/synchronize',
71
+ 'methods' => 'POST',
72
+ 'require_wp_admin' => false,
73
+ 'require_api_key' => true,
74
+ 'callback' => function() {
75
+ do_action(CE4WP_SYNCHRONIZE_ACTION, 250);
76
+ return new WP_REST_Response(null, 200);
77
+ }
78
+ ),
79
+ array (
80
+ 'path' => '/wc_products',
81
+ 'methods' => 'GET',
82
+ 'require_wp_admin' => false,
83
+ 'require_api_key' => true,
84
+ 'callback' => function() {
85
+ $productData = array();
86
+ if (in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins'))))
87
+ {
88
+ // Get 25 most recent products
89
+ $products = wc_get_products(array(
90
+ 'limit' => 25,
91
+ ));
92
+ foreach ($products as $product) {
93
+ array_push($productData, new WCProductModel($product->get_data()));
94
+ }
95
+ }
96
+ return new WP_REST_Response($productData, 200);
97
+ }
98
+ ),
99
+ array (
100
+ 'path' => '/blog_information',
101
+ 'methods' => 'GET',
102
+ 'require_wp_admin' => false,
103
+ 'require_api_key' => true,
104
+ 'callback' => function() {
105
+ return new WP_REST_Response(new BlogInformation(), 200);
106
+ }
107
+ ),
108
+ array (
109
+ 'path' => '/wp_posts',
110
+ 'methods' => 'GET',
111
+ 'require_wp_admin' => false,
112
+ 'require_api_key' => true,
113
+ 'callback' => function() {
114
+ $postData = array();
115
+
116
+ $posts = get_posts(array(
117
+ 'posts_per_page' => -1
118
+ ));
119
+
120
+ foreach ($posts as $post)
121
+ {
122
+ array_push($postData, new BlogPost($post));
123
+ }
124
+
125
+ return new WP_REST_Response($postData, 200);
126
+ }
127
+ ),
128
+ array (
129
+ 'path' => '/images',
130
+ 'methods' => 'GET',
131
+ 'require_wp_admin' => false,
132
+ 'require_api_key' => true,
133
+ 'callback' => function() {
134
+ $attachmentData = array();
135
+ $attachments = get_posts(array(
136
+ 'post_type' => 'attachment',
137
+ 'post_mime_type' => 'image',
138
+ 'post_status' => 'inherit',
139
+ 'posts_per_page' => -1
140
+ ));
141
+
142
+ foreach ($attachments as $attachment)
143
+ {
144
+ array_push($attachmentData, new BlogAttachment($attachment));
145
+ }
146
+
147
+ return new WP_REST_Response($attachmentData, 200);
148
+ }
149
+ )
150
+ );
151
+
152
+ foreach ($routes as $route) {
153
+ $this->register_route($route);
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Registers a route to the WP Rest endpoints for this plugin.
159
+ * @param array $route
160
+ */
161
+ private function register_route(array $route) {
162
+
163
+ // Make sure the route is valid
164
+ $path = $route['path'];
165
+ $methods = $route['methods'];
166
+ $callback = $route['callback'];
167
+ $require_wp_admin = $route['require_wp_admin'] ?? false;
168
+ $require_api_key = $route['require_api_key'] ?? true;
169
+
170
+ // Make sure we at least have a path
171
+ if (empty($path)) return;
172
+
173
+ // Skip the route if we are not in admin mode and the route should only be available in admin mode.
174
+ $is_admin = current_user_can('administrator');
175
+ if($require_wp_admin === true && !$is_admin) {
176
+ return;
177
+ }
178
+
179
+ // If we don't have a method, assume it is GET
180
+ if(empty($methods)) {
181
+ $methods = 'GET';
182
+ }
183
+
184
+ $arguments = array(
185
+ 'methods' => $methods,
186
+ 'callback' => $callback
187
+ );
188
+
189
+ $permission_callback = null;
190
+ if ($require_api_key === true) {
191
+ $arguments['permission_callback'] = array($this, 'validate_api_key');
192
+ }
193
+
194
+ register_rest_route(self::api_namespace, $path, $arguments);
195
+
196
+ }
197
+
198
+ /**
199
+ * Will validate if the request is presenting a valid API key.
200
+ * @param $request
201
+ * @return bool|WP_Error
202
+ */
203
+ public function validate_api_key($request) {
204
+
205
+ $key = OptionsHelper::get_instance_api_key();
206
+ $apiKey = $request->get_header("x-api-key");
207
+ if ($apiKey === $key) {
208
+ return true;
209
+ }
210
+
211
+ return new WP_Error( 'rest_forbidden', __( 'Sorry, you are not allowed to do that.' ), array( 'status' => 401 ) );
212
+ }
213
+
214
+ }
src/managers/instance-manager.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ namespace CreativeMail\Managers;
5
+
6
+ use CreativeMail\Helpers\OptionsHelper;
7
+ use WP_Error;
8
+
9
+ /**
10
+ * Class InstanceManager
11
+ * @package CreativeMail\Managers
12
+ */
13
+ class InstanceManager
14
+ {
15
+ public function __construct() {
16
+
17
+ }
18
+
19
+ public function add_hooks() {
20
+
21
+ }
22
+
23
+ /**
24
+ * Handles the callback from the WordPress API and will store all the instance details.
25
+ * @param $request
26
+ * @return bool|WP_Error
27
+ */
28
+ public function handle_callback($request) {
29
+
30
+ $account_information = json_decode($request->get_body());
31
+ if ($account_information === null) {
32
+ return new WP_Error( 'rest_bad_request', 'Invalid account details', array('status' => 400));
33
+ }
34
+
35
+ // Verify handshake expiration
36
+ $expiration = OptionsHelper::get_handshake_expiration();
37
+ if ($expiration === null || $expiration < time()) {
38
+ return new WP_Error( 'rest_unauthorized', 'Unauthorized', array('status' => 401));
39
+ }
40
+
41
+ // Verify handshake
42
+ $apiKey = $request->get_header('x-api-key');
43
+ if($apiKey !== OptionsHelper::get_handshake_token()){
44
+ return new WP_Error( 'rest_unauthorized', 'Unauthorized', array('status' => 401));
45
+ }
46
+
47
+ // Store the account information in the settings
48
+ OptionsHelper::set_instance_id($account_information->site_id);
49
+ OptionsHelper::set_instance_api_key($account_information->api_key);
50
+ OptionsHelper::set_connected_account_id($account_information->account_id);
51
+
52
+ return true;
53
+ }
54
+ }
src/managers/integration-manager.php ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ namespace CreativeMail\Managers;
5
+
6
+ use CreativeMail\Helpers\OptionsHelper;
7
+ use CreativeMail\Integrations\Integration;
8
+ use CreativeMail\Modules\Contacts\Handlers\ContactFormSevenPluginHandler;
9
+ use CreativeMail\Modules\Contacts\Handlers\NewsLetterContactFormPluginHandler;
10
+ use CreativeMail\Modules\Contacts\Handlers\WooCommercePluginHandler;
11
+ use CreativeMail\Modules\Contacts\Handlers\WpFormsLitePluginHandler;
12
+ use ReflectionClass;
13
+
14
+ /**
15
+ * Class IntegrationManager
16
+ *
17
+ * The IntegrationManager will manage all the supported integrations with third party plugins.
18
+ *
19
+ * @package CreativeMail\Managers
20
+ */
21
+ class IntegrationManager
22
+ {
23
+ private $supported_integrations;
24
+ private $active_integrations;
25
+
26
+ public function __construct()
27
+ {
28
+
29
+ $this->active_integrations = array();
30
+
31
+ // Setup the default integrations
32
+ $this->supported_integrations = array(
33
+ new Integration('contact-form-7', 'Contact Form 7', 'contact-form-7/wp-contact-form-7.php', ContactFormSevenPluginHandler::class),
34
+ new Integration('newsletter','Newsletter', 'newsletter/plugin.php', NewsLetterContactFormPluginHandler::class),
35
+ new Integration('woocommerce','WooCommerce', 'woocommerce/woocommerce.php', WooCommercePluginHandler::class),
36
+ new Integration('wpformslite', 'WPForms Lite', 'wpforms-lite/wpforms.php', WpFormsLitePluginHandler::class)
37
+ );
38
+ }
39
+
40
+ /**
41
+ * Will register all the required hooks for this manager.
42
+ */
43
+ public function add_hooks()
44
+ {
45
+ $active_plugins = array_filter($this->get_active_plugins(), function($item) {
46
+ return array_search($item->get_slug(), $this->get_activated_plugins(), true) !== false;
47
+ });
48
+
49
+ foreach ($active_plugins as $active_plugin)
50
+ {
51
+ try
52
+ {
53
+ if (array_key_exists($active_plugin->get_slug(), $this->active_integrations) === false)
54
+ {
55
+ // use reflection to create instance of class
56
+ $class = new ReflectionClass($active_plugin->get_integration_handler());
57
+ $this->active_integrations[$active_plugin->get_slug()] = $class->newInstance();
58
+ }
59
+ // register hooks for integration class
60
+ $this->active_integrations[$active_plugin->get_slug()]->registerHooks();
61
+ } catch (\Exception $e) {
62
+ // silent
63
+ }
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Will remove all the registered hooks.
69
+ */
70
+ public function remove_hooks()
71
+ {
72
+ foreach($this->active_integrations as $active_integration){
73
+ $active_integration->unregisterHooks();
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Will get all the supported plugins that are installed and active on this WP instance.
79
+ * @return array
80
+ */
81
+ public function get_active_plugins()
82
+ {
83
+
84
+ $activated_plugins = array();
85
+
86
+ foreach ($this->supported_integrations as $integration) {
87
+
88
+ // Check if the plugin is activated
89
+ if (in_array($integration->get_class(), apply_filters('active_plugins', get_option('active_plugins'))))
90
+ {
91
+ array_push($activated_plugins, $integration);
92
+ }
93
+ }
94
+
95
+ return $activated_plugins;
96
+ }
97
+
98
+ /**
99
+ * Stores the plugins that were activated by the user.
100
+ * @param $plugins
101
+ */
102
+ public function set_activated_plugins($plugins)
103
+ {
104
+
105
+ // Store the activated plugins
106
+ OptionsHelper::set_activated_plugins(implode(';', $plugins));
107
+
108
+ // Remove the hooks and add them again
109
+ $this->remove_hooks();
110
+ $this->add_hooks();
111
+
112
+ do_action(CE4WP_SYNCHRONIZE_ACTION, 250);
113
+ }
114
+
115
+ /**
116
+ * Gets a list of slugs representing the plugins that were activated by the user.
117
+ * @return array
118
+ */
119
+ public function get_activated_plugins()
120
+ {
121
+ $activated_plugins = OptionsHelper::get_activated_plugins();
122
+ if (is_null($activated_plugins)) {
123
+ $activated_plugins = '';
124
+ }
125
+ if (is_array($activated_plugins)) {
126
+ $activated_plugins = implode(';', $activated_plugins);
127
+ }
128
+ return explode(';', $activated_plugins);
129
+ }
130
+
131
+ /**
132
+ * Will return a list of the activated integrations.
133
+ * @return array
134
+ */
135
+ public function get_activated_integrations()
136
+ {
137
+ return array_filter($this->get_active_plugins(), function($item) {
138
+ return array_search($item->get_slug(), $this->get_activated_plugins(), true) !== false;
139
+ });
140
+ }
141
+
142
+ /**
143
+ * Will return a list of all the integrations that we support.
144
+ * @return array A list of all the supported integrations.
145
+ */
146
+ public function get_supported_integrations()
147
+ {
148
+ return $this->supported_integrations;
149
+ }
150
+ }
src/modules/blog/models/BlogAttachment.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace CreativeMail\Modules\Blog\Models;
4
+
5
+ class BlogAttachment {
6
+ public $id;
7
+ public $date;
8
+ public $name;
9
+ public $modified;
10
+ public $url;
11
+ public $thumbnail;
12
+
13
+ function __construct($wp_attachment) {
14
+ $this->id = $wp_attachment->ID;
15
+ $this->name = $wp_attachment->post_title;
16
+ $this->date = $wp_attachment->post_date;
17
+ $this->modified = $wp_attachment->post_modified;
18
+ $this->url = wp_get_attachment_url($wp_attachment->ID);
19
+ $this->thumbnail = wp_get_attachment_image_url($wp_attachment->ID, 'medium_large');
20
+ }
21
+ }
src/modules/blog/models/BlogInformation.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace CreativeMail\Modules\Blog\Models;
4
+
5
+ use CreativeMail\Modules\WooCommerce\Models\WCStoreInformation;
6
+
7
+ class BlogInformation {
8
+ public $title;
9
+ public $description;
10
+ public $url;
11
+ public $admin_email;
12
+ public $language;
13
+ public $rss2_url;
14
+ public $logo;
15
+ public $template;
16
+
17
+ public $first_name;
18
+ public $last_name;
19
+ public $company;
20
+ public $email;
21
+
22
+ public $woocommerce;
23
+
24
+ function __construct() {
25
+ $this->title = get_bloginfo('name');
26
+ $this->description = get_bloginfo('description');
27
+ $this->url = get_bloginfo('url');
28
+ $this->admin_email = get_bloginfo('admin_email');
29
+ $this->language = get_bloginfo('language');
30
+ $this->rss2_url = get_bloginfo('rss2_url');
31
+ if(has_custom_logo()) {
32
+ $this->logo = get_custom_logo();
33
+ }
34
+ $this->template = get_template();
35
+ $this->woocommerce = new WCStoreInformation();
36
+ }
37
+ }
src/modules/blog/models/BlogPost.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace CreativeMail\Modules\Blog\Models;
4
+
5
+ class BlogPost {
6
+ public $id;
7
+ public $author;
8
+ public $date;
9
+ public $content;
10
+
11
+ public $title;
12
+ public $excerpt;
13
+ public $status;
14
+ public $name;
15
+
16
+ public $modified;
17
+ public $parent_id;
18
+ public $menu_order;
19
+ public $comment_count;
20
+
21
+ public $url;
22
+ public $thumbnail;
23
+
24
+ function __construct($wp_post) {
25
+ $this->id = $wp_post->ID;
26
+ $this->author = $wp_post->post_author;
27
+ $this->date = $wp_post->post_date;
28
+ $this->modified = $wp_post->post_modified;
29
+ $this->content = apply_filters("the_content", $wp_post->post_content);
30
+ $this->title = apply_filters("the_title", $wp_post->post_title);
31
+ $this->excerpt = $wp_post->post_excerpt;
32
+ $this->status = $wp_post->post_status;
33
+ $this->parent_id = $wp_post->post_parent;
34
+ $this->menu_order = $wp_post->menu_order;
35
+ $this->comment_count = $wp_post->comment_count;
36
+ $this->url = get_permalink($wp_post->ID);
37
+ if (has_post_thumbnail($wp_post->ID)) {
38
+ $this->thumbnail = get_the_post_thumbnail_url($wp_post->ID);
39
+ }
40
+ }
41
+ }
src/modules/contacts/Handlers/BaseContactFormPluginHandler.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace CreativeMail\Modules\Contacts\Handlers;
4
+
5
+ use CreativeMail\Modules\Contacts\Services\ContactsSyncService;
6
+ use Exception;
7
+
8
+ abstract class BaseContactFormPluginHandler
9
+ {
10
+ private $contactSyncService;
11
+
12
+ public abstract function convertToContactModel($contactForm);
13
+ public abstract function registerHooks();
14
+ public abstract function unregisterHooks();
15
+ public abstract function syncAction($limit = null);
16
+
17
+ public function upsertContact($model) {
18
+
19
+ if (!isset($model)) {
20
+ throw new Exception('No model provided');
21
+ }
22
+
23
+ $contactModel = null;
24
+ if (!is_a($model, 'CreativeMail\Modules\Contacts\Models\ContactModel')) {
25
+ $contactModel = $this->convertToContactModel($model);
26
+ }
27
+ else {
28
+ $contactModel = $model;
29
+ }
30
+
31
+ $this->contactSyncService->upsertContact($contactModel);
32
+ }
33
+
34
+ public function batchUpsertContacts($models) {
35
+ if (!isset($models)) {
36
+ throw new Exception('No models provided');
37
+ }
38
+
39
+ $this->contactSyncService->upsertContacts($models);
40
+ }
41
+
42
+ protected function isNotNullOrEmpty($value) {
43
+ return isset($value) && !empty($value);
44
+ }
45
+
46
+ function __construct()
47
+ {
48
+ $this->contactSyncService = new ContactsSyncService();
49
+ $this->registerHooks();
50
+ }
51
+
52
+ function __destruct()
53
+ {
54
+ $this->unregisterHooks();
55
+ }
56
+ }
src/modules/contacts/Handlers/ContactFormSevenPluginHandler.php ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace CreativeMail\Modules\Contacts\Handlers;
4
+
5
+ define('CE4WP_CF7_EventType', 'WordPress - Contact Form 7');
6
+
7
+ use CreativeMail\Modules\Contacts\Models\ContactFormSevenSubmission;
8
+ use CreativeMail\Modules\Contacts\Models\ContactModel;
9
+ use CreativeMail\Modules\Contacts\Models\OptActionBy;
10
+
11
+ class ContactFormSevenPluginHandler extends BaseContactFormPluginHandler {
12
+
13
+ private $emailFields = array('your-email','email', 'emailaddress', 'email_address');
14
+ private $firstnameFields = array('firstname', 'first_name','name','your-name');
15
+ private $lastnameFields = array('lastname', 'last_name');
16
+
17
+ private function findValue($data, $fieldOptions) {
18
+ foreach ($fieldOptions as $fieldOption) {
19
+ $value = $data->get_posted_data($fieldOption);
20
+ if (isset($value) && !empty($value)) {
21
+ return $value;
22
+ }
23
+ }
24
+
25
+ return null;
26
+ }
27
+
28
+ private function findValueFromDb($formData, $fieldOptions) {
29
+ foreach ($fieldOptions as $fieldOption) {
30
+ if (array_key_exists($fieldOption, $formData)) {
31
+ $value = $formData[$fieldOption];
32
+ if ($this->isNotNullOrEmpty($value)) {
33
+ return $value;
34
+ }
35
+ }
36
+ }
37
+ return null;
38
+ }
39
+
40
+ public function convertToContactModel($contactForm)
41
+ {
42
+ $contactForm = ContactFormSevenSubmission::get_instance( null, array('skip_mail' => true));
43
+
44
+ // convert
45
+ $contactModel = new ContactModel();
46
+
47
+ $email = $this->findValue($contactForm, $this->emailFields);
48
+ if ($this->isNotNullOrEmpty($email)) {
49
+ $contactModel->setEmail($email);
50
+ }
51
+
52
+ $firstName = $this->findValue($contactForm, $this->firstnameFields);
53
+ if ($this->isNotNullOrEmpty($firstName)) {
54
+ $contactModel->setFirstName($firstName);
55
+ }
56
+
57
+ $lastName = $this->findValue($contactForm, $this->lastnameFields);
58
+ if ($this->isNotNullOrEmpty($lastName)) {
59
+ $contactModel->setLastName($lastName);
60
+ }
61
+
62
+ $contactModel->setEventType(CE4WP_CF7_EventType);
63
+
64
+ return $contactModel;
65
+ }
66
+
67
+ public function registerHooks()
68
+ {
69
+ add_action('wpcf7_mail_sent', array($this, 'ceHandleContactFormSevenSubmit'));
70
+ // add hook function to synchronize
71
+ add_action(CE4WP_SYNCHRONIZE_ACTION, array($this, 'syncAction'));
72
+ }
73
+
74
+ public function unregisterHooks()
75
+ {
76
+ remove_action('wpcf7_mail_sent', array($this, 'ceHandleContactFormSevenSubmit'));
77
+ // add hook function to synchronize
78
+ remove_action(CE4WP_SYNCHRONIZE_ACTION, array($this, 'syncAction'));
79
+ }
80
+
81
+ public function syncAction($limit = null)
82
+ {
83
+ if (!is_int($limit) || $limit <= 0) {
84
+ $limit = null;
85
+ }
86
+
87
+ // Relies on plugin => Contact Form CFDB7
88
+ if (in_array('contact-form-cfdb7/contact-form-cfdb-7.php', apply_filters('active_plugins', get_option('active_plugins')))) {
89
+ global $wpdb;
90
+
91
+ $cfdb = apply_filters( 'cfdb7_database', $wpdb );
92
+ $cfdbtable = $cfdb->prefix.'db7_forms';
93
+ $cfdbQuery = "SELECT form_id, form_post_id, form_value FROM $cfdbtable";
94
+
95
+ // Do we need to limit the number of results
96
+ if ($limit != null) {
97
+ $cfdbQuery .= " LIMIT %d";
98
+ $cfdbQuery = $cfdb->prepare($cfdbQuery, $limit);
99
+ }
100
+ else {
101
+ $cfdbQuery = $cfdb->prepare($cfdbQuery);
102
+ }
103
+
104
+ $results = $cfdb->get_results($cfdbQuery, OBJECT);
105
+
106
+ $contactsArray = array();
107
+
108
+ foreach ($results as $formSubmission) {
109
+ $form_data = unserialize($formSubmission->form_value);
110
+ $contactModel = new ContactModel();
111
+ $contactModel->setOptIn(true);
112
+ $contactModel->setOptActionBy(OptActionBy::Visitor);
113
+ $email = $this->findValueFromDb($form_data, $this->emailFields);
114
+ if ($this->isNotNullOrEmpty($email)) {
115
+ $contactModel->setEmail($email);
116
+ }
117
+ $firstname = $this->findValueFromDb($form_data, $this->firstnameFields);
118
+ if ($this->isNotNullOrEmpty($firstname)) {
119
+ $contactModel->setFirstName($firstname);
120
+ }
121
+ $lastname = $this->findValueFromDb($form_data, $this->lastnameFields);
122
+ if ($this->isNotNullOrEmpty($lastname)) {
123
+ $contactModel->setLastName($lastname);
124
+ }
125
+
126
+ if ($this->isNotNullOrEmpty($contactModel->getEmail())){
127
+ $contactModel->setEventType(CE4WP_CF7_EventType);
128
+ array_push($contactsArray, $contactModel);
129
+ }
130
+ }
131
+
132
+ if (!empty($contactsArray)) {
133
+
134
+ $batches = array_chunk($contactsArray, CE4WP_BATCH_SIZE);
135
+ foreach($batches as $batch){
136
+ $this->batchUpsertContacts($batch);
137
+ }
138
+ }
139
+ }
140
+ }
141
+
142
+ function ceHandleContactFormSevenSubmit($contact_form)
143
+ {
144
+ try {
145
+ $this->upsertContact($this->convertToContactModel($contact_form));
146
+ }
147
+ catch (\Exception $exception) {
148
+ // silent exception
149
+ }
150
+ }
151
+
152
+ function __construct()
153
+ {
154
+ parent::__construct();
155
+ }
156
+ }
src/modules/contacts/Handlers/NewsLetterContactFormPluginHandler.php ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace CreativeMail\Modules\Contacts\Handlers;
4
+
5
+ define('CE4WP_NL_EventType', 'WordPress - NewsLetter');
6
+
7
+ use CreativeMail\Modules\Contacts\Models\ContactModel;
8
+ use CreativeMail\Modules\Contacts\Models\OptActionBy;
9
+
10
+ class NewsLetterContactFormPluginHandler extends BaseContactFormPluginHandler
11
+ {
12
+ public function convertToContactModel($user)
13
+ {
14
+ $contactModel = new ContactModel();
15
+
16
+ $contactModel->setEventType(CE4WP_NL_EventType);
17
+ $contactModel->setOptIn(true);
18
+ $contactModel->setOptActionBy(OptActionBy::Visitor);
19
+
20
+ $email = $user->email;
21
+ if ($this->isNotNullOrEmpty($email)) {
22
+ $contactModel->setEmail($email);
23
+ }
24
+
25
+ $name = $user->name;
26
+ if ($this->isNotNullOrEmpty($name)) {
27
+ $contactModel->setFirstName($name);
28
+ }
29
+
30
+ $surname = $user->surname;
31
+ if ($this->isNotNullOrEmpty($surname)) {
32
+ $contactModel->setLastName($surname);
33
+ }
34
+
35
+ return $contactModel;
36
+ }
37
+
38
+ public function ceHandleContactNewsletterSubmit($user) {
39
+ try {
40
+ $this->upsertContact($this->convertToContactModel($user));
41
+ }
42
+ catch (\Exception $exception) {
43
+ // silent exception
44
+ }
45
+ }
46
+
47
+ public function registerHooks()
48
+ {
49
+ add_action( 'newsletter_user_confirmed', array($this, 'ceHandleContactNewsletterSubmit'));
50
+ // add hook function to synchronize
51
+ add_action(CE4WP_SYNCHRONIZE_ACTION, array($this, 'syncAction'));
52
+ }
53
+
54
+ public function unregisterHooks()
55
+ {
56
+ remove_action( 'newsletter_user_confirmed', array($this, 'ceHandleContactNewsletterSubmit'));
57
+ // remove hook function to synchronize
58
+ remove_action(CE4WP_SYNCHRONIZE_ACTION, array($this, 'syncAction'));
59
+ }
60
+
61
+ public function syncAction($limit = null)
62
+ {
63
+ if (!is_int($limit) || $limit <= 0) {
64
+ $limit = null;
65
+ }
66
+
67
+ global $wpdb;
68
+
69
+ $query = 'select * from wp_newsletter order by id desc';
70
+
71
+ if ($limit != null) {
72
+ $query .= " LIMIT %d";
73
+ $query = $wpdb->prepare($query, $limit);
74
+ }
75
+ else {
76
+ $query = $wpdb->prepare($query);
77
+ }
78
+
79
+ $result = $wpdb->get_results($query);
80
+
81
+ $backfillArray = array();
82
+
83
+ if (isset($result) && !empty($result)) {
84
+ foreach ($result as $contact) {
85
+ $contactModel = new ContactModel();
86
+ $contactModel->setEventType(CE4WP_NL_EventType);
87
+ $contactModel->setOptIn(true);
88
+ $contactModel->setOptActionBy(OptActionBy::Visitor);
89
+
90
+ $email = $contact->email;
91
+ if ($this->isNotNullOrEmpty($email)) {
92
+ $contactModel->setEmail($email);
93
+ }
94
+
95
+ $name = $contact->name;
96
+ if ($this->isNotNullOrEmpty($name)) {
97
+ $contactModel->setFirstName($name);
98
+ }
99
+
100
+ $surname = $contact->surname;
101
+ if ($this->isNotNullOrEmpty($surname)) {
102
+ $contactModel->setLastName($surname);
103
+ }
104
+
105
+ if ($this->isNotNullOrEmpty($contactModel->getEmail())) {
106
+ array_push($backfillArray, $contactModel);
107
+ }
108
+ }
109
+ }
110
+
111
+ if (!empty($backfillArray)) {
112
+
113
+ $batches = array_chunk($backfillArray, CE4WP_BATCH_SIZE);
114
+ foreach($batches as $batch){
115
+ $this->batchUpsertContacts($batch);
116
+ }
117
+ }
118
+ }
119
+
120
+ function __construct()
121
+ {
122
+ parent::__construct();
123
+ }
124
+ }
src/modules/contacts/Handlers/WooCommercePluginHandler.php ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Created by PhpStorm.
4
+ * User: Martijn
5
+ * Date: 2020-02-10
6
+ * Time: 13:42
7
+ */
8
+
9
+ namespace CreativeMail\Modules\Contacts\Handlers;
10
+
11
+ define('CE4WP_WC_EventType', 'WordPress - WooCommerce');
12
+
13
+ use CreativeMail\Modules\Contacts\Models\ContactModel;
14
+
15
+ class WooCommercePluginHandler extends BaseContactFormPluginHandler
16
+ {
17
+ public function convertToContactModel($orderId)
18
+ {
19
+ $contactModel = new ContactModel();
20
+ $products_detail = get_post_meta($orderId);
21
+
22
+ if (isset($products_detail)) {
23
+ if ($this->isNotNullOrEmpty($products_detail["_billing_first_name"])) {
24
+ $contactModel->setFirstName($products_detail["_billing_first_name"][0]);
25
+ }
26
+ if ($this->isNotNullOrEmpty($products_detail["_billing_last_name"])) {
27
+ $contactModel->setLastName($products_detail["_billing_last_name"][0]);
28
+ }
29
+
30
+ if ($this->isNotNullOrEmpty($products_detail["_billing_email"])) {
31
+ $contactModel->setEmail($products_detail["_billing_email"][0]);
32
+ }
33
+
34
+ if ($this->isNotNullOrEmpty($contactModel->getEmail())) {
35
+ $contactModel->setEventType(CE4WP_WC_EventType);
36
+ $contactModel->setOptActionBy(1);
37
+ $contactModel->setOptIn(true);
38
+ }
39
+ }
40
+ return $contactModel;
41
+ }
42
+
43
+ // public function ceHandlerWooCommerceNewCustomer($customer_id, $new_customer_data) {
44
+ // try {
45
+ // $this->upsertContact($this->convertToContactModel($new_customer_data));
46
+ // }
47
+ // catch (\Exception $exception) {
48
+ // // silent exception
49
+ // }
50
+ // }
51
+
52
+ function ceHandlerWooCommerceNewOrder($order_id) {
53
+ try {
54
+ $order = wc_get_order( $order_id );
55
+ $this->upsertContact($this->convertToContactModel($order->ID));
56
+ }
57
+ catch (\Exception $exception) {
58
+ // silent exception
59
+ }
60
+ }
61
+
62
+ public function registerHooks()
63
+ {
64
+ add_action('woocommerce_new_order', array($this, 'ceHandlerWooCommerceNewOrder'));
65
+ // hook function to synchronize
66
+ add_action(CE4WP_SYNCHRONIZE_ACTION, array($this, 'syncAction'));
67
+ //add_action('woocommerce_created_customer', array($this,'ceHandlerWooCommerceNewCustomer'));
68
+ }
69
+
70
+ public function unregisterHooks()
71
+ {
72
+ remove_action('woocommerce_new_order', array($this, 'ceHandlerWooCommerceOrder'));
73
+ // remove hook function to synchronize
74
+ remove_action(CE4WP_SYNCHRONIZE_ACTION, array($this, 'syncAction'));
75
+ //remove_action('woocommerce_created_customer', array($this,'ceHandlerWooCommerceNewCustomer'));
76
+ }
77
+
78
+ public function syncAction($limit = null)
79
+ {
80
+ if(!is_int($limit) || $limit <= 0){
81
+ $limit = null;
82
+ }
83
+
84
+ $backfillArray = array();
85
+
86
+ $args = array(
87
+ 'posts_per_page' => -1,
88
+ 'post_type' => 'shop_order',
89
+ 'post_status'=> array_keys(wc_get_order_statuses())
90
+ );
91
+
92
+ if ($limit != null) {
93
+ $args['numberposts'] = $limit;
94
+ }
95
+
96
+ $products_orders = get_posts($args);
97
+
98
+ foreach ( $products_orders as $products_order ) {
99
+
100
+ $contactModel = $this->convertToContactModel($products_order->ID);
101
+
102
+ if($this->isNotNullOrEmpty($contactModel->getEmail())) {
103
+ array_push($backfillArray, $contactModel);
104
+ }
105
+
106
+ }
107
+
108
+ if (!empty($backfillArray)) {
109
+
110
+ $batches = array_chunk($backfillArray, CE4WP_BATCH_SIZE);
111
+ foreach($batches as $batch){
112
+ $this->batchUpsertContacts($batch);
113
+ }
114
+ }
115
+ }
116
+
117
+ function __construct()
118
+ {
119
+ parent::__construct();
120
+ }
121
+ }
src/modules/contacts/Handlers/WpFormsLitePluginHandler.php ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ namespace CreativeMail\Modules\Contacts\Handlers;
5
+
6
+ define('CE4WP_WPF_EventType', 'WordPress - WPFormsLite');
7
+
8
+ use CreativeMail\Modules\Contacts\Models\ContactModel;
9
+ use CreativeMail\Modules\Contacts\Models\OptActionBy;
10
+
11
+ class WpFormsLitePluginHandler extends BaseContactFormPluginHandler
12
+ {
13
+ private function get_form_type_field($formData, $type)
14
+ {
15
+ foreach ($formData as $field) {
16
+ if(array_key_exists('type', $field) && $field['type'] === $type) {
17
+ return $field;
18
+ }
19
+ }
20
+ return null;
21
+ }
22
+
23
+ public function convertToContactModel($formData)
24
+ {
25
+ $contactModel = new ContactModel();
26
+
27
+ $contactModel->setEventType(CE4WP_WPF_EventType);
28
+ $contactModel->setOptIn(true);
29
+ $contactModel->setOptActionBy(OptActionBy::Visitor);
30
+
31
+ $emailField = $this->get_form_type_field($formData, 'email');
32
+ if (array_key_exists('value', $emailField)) {
33
+ if ($this->isNotNullOrEmpty($emailField['value'])) {
34
+ $contactModel->setEmail($emailField['value']);
35
+ }
36
+ }
37
+
38
+ $nameField = $this->get_form_type_field($formData, 'name');
39
+ if (array_key_exists('first', $nameField)) {
40
+ if ($this->isNotNullOrEmpty($nameField['first'])) {
41
+ $contactModel->setFirstName($nameField['first']);
42
+ }
43
+ }
44
+ if (array_key_exists('last', $nameField)) {
45
+ if ($this->isNotNullOrEmpty($nameField['last'])) {
46
+ $contactModel->setLastName($nameField['last']);
47
+ }
48
+ }
49
+
50
+ return $contactModel;
51
+ }
52
+
53
+ public function ceHandleWpFormsProcessComplete($fields, $entry, $form_data, $entry_id) {
54
+ try {
55
+ $this->upsertContact($this->convertToContactModel($fields));
56
+ }
57
+ catch (\Exception $exception) {
58
+ // silent exception
59
+ }
60
+ }
61
+
62
+ public function registerHooks()
63
+ {
64
+ // https://wpforms.com/developers/wpforms_process_complete/
65
+ add_action( 'wpforms_process_complete', array($this, 'ceHandleWpFormsProcessComplete'), 10, 4);
66
+ }
67
+
68
+ public function unregisterHooks()
69
+ {
70
+ remove_action( 'wpforms_process_complete', array($this, 'ceHandleWpFormsProcessComplete'));
71
+ }
72
+
73
+ public function syncAction($limit = null)
74
+ {
75
+
76
+ }
77
+
78
+ function __construct()
79
+ {
80
+ parent::__construct();
81
+ }
82
+ }
src/modules/contacts/Services/ContactsSyncService.php ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace CreativeMail\Modules\Contacts\Services;
4
+
5
+ use CreativeMail\Helpers\EnvironmentHelper;
6
+ use CreativeMail\Helpers\OptionsHelper;
7
+ use CreativeMail\Modules\Contacts\Models\ContactModel;
8
+ use Exception;
9
+
10
+ class ContactsSyncService
11
+ {
12
+ private $apiKey;
13
+ private $baseUrl;
14
+ private $accountId;
15
+
16
+ private function validate_email_address($emailAddress) {
17
+ if (!isset($emailAddress) && empty($emailAddress)) {
18
+ throw new Exception('No valid email address provided');
19
+ }
20
+ }
21
+
22
+ private function ensure_event_type($eventType) {
23
+ // DEV: For now, we only support WordPress.
24
+ if (isset($eventType) && !empty($eventType)) {
25
+ return $eventType;
26
+ }
27
+
28
+ return 'WordPress';
29
+ }
30
+
31
+ // private function buildInstanceJwt() {
32
+ // $payload = array(
33
+ // 'instanceId' => $this->instanceId,
34
+ // 'exp' => time() + 3600
35
+ // );
36
+ //
37
+ // return JWT::encode($payload, $this->apiSecret, 'HS256');
38
+ // }
39
+
40
+ private function build_payload($contactModels) {
41
+ $contacts = array();
42
+ foreach ($contactModels as $model) {
43
+ array_push($contacts, $model->toArray());
44
+ }
45
+
46
+ $data = array(
47
+ "contacts" => $contacts
48
+ );
49
+
50
+ return wp_json_encode($data);
51
+ }
52
+
53
+ private function send($httpMethod, $endpoint, $payload) {
54
+
55
+ if (!isset($httpMethod) || empty($httpMethod)) {
56
+ // throw exception
57
+ }
58
+
59
+ if (!isset($endpoint) || empty($endpoint)) {
60
+ throw new Exception('No endpoint provided');
61
+ }
62
+
63
+ $httpMethod = strtoupper($httpMethod);
64
+
65
+ if ($httpMethod === 'POST') {
66
+ return wp_remote_post($endpoint, array(
67
+ 'method' => 'POST',
68
+ 'headers' => array(
69
+ 'x-account-id' => $this->accountId,
70
+ 'x-api-key' => $this->apiKey,
71
+ 'content-type' => 'application/json'
72
+ ),
73
+ 'body' => $payload
74
+ ));
75
+ }
76
+
77
+ return wp_remote_get($endpoint, array(
78
+ 'method' => 'GET',
79
+ 'headers' => array(
80
+ 'x-account-id' => $this->accountId,
81
+ 'x-api-key' => $this->apiKey,
82
+ 'content-type' => 'application/json'
83
+ )
84
+ ));
85
+ }
86
+
87
+ public function upsertContact(ContactModel $contactModel) {
88
+ if(!isset($contactModel)) {
89
+ return false;
90
+ }
91
+
92
+ $this->validate_email_address($contactModel->getEmail());
93
+ $contactModel->setEventType($this->ensure_event_type($contactModel->getEventType()));
94
+
95
+ $url = $this->baseUrl . '/v1.0/contacts';
96
+ $result = $this->send('POST', $url, $this->build_payload(array($contactModel)));
97
+
98
+ return (isset($result) && !empty($result));
99
+ }
100
+
101
+ public function upsertContacts($contactModels) {
102
+
103
+ $url = $this->baseUrl . '/v1.0/contacts';
104
+ $jsonData = $this->build_payload($contactModels);
105
+ $result = $this->send('POST', $url, $jsonData);
106
+
107
+ return (isset($result));
108
+ }
109
+
110
+ function __construct() {
111
+ $this->apiKey = OptionsHelper::get_instance_api_key();
112
+ $this->accountId = OptionsHelper::get_connected_account_id();
113
+ $this->baseUrl = EnvironmentHelper::get_app_gateway_url('wordpress');
114
+ }
115
+ }
src/modules/contacts/models/ContactAddressModel.php ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace CreativeMail\Modules\Contacts\Models;
4
+
5
+ class ContactAddressModel {
6
+ public $countryCode;
7
+ public $postalCode;
8
+ public $state;
9
+ public $stateCode;
10
+ public $address;
11
+ public $address2;
12
+ public $city;
13
+
14
+ public function setCountryCode($countryCode) {
15
+ $this->countryCode = $countryCode;
16
+ }
17
+
18
+ public function getCountryCode() {
19
+ return $this->countryCode;
20
+ }
21
+
22
+ public function setPostalCode($postalCode) {
23
+ $this->postalCode = $postalCode;
24
+ }
25
+
26
+ public function getPostalCode() {
27
+ return $this->postalCode;
28
+ }
29
+
30
+ public function setState($state) {
31
+ $this->state = $state;
32
+ }
33
+
34
+ public function getState() {
35
+ return $this->state;
36
+ }
37
+
38
+ public function setStateCode($stateCode) {
39
+ $this->stateCode = $stateCode;
40
+ }
41
+
42
+ public function getStateCode() {
43
+ return $this->stateCode;
44
+ }
45
+
46
+ public function setAddress($address) {
47
+ $this->address = $address;
48
+ }
49
+
50
+ public function getAddress() {
51
+ return $this->address;
52
+ }
53
+
54
+ public function setAddress2($address2) {
55
+ $this->address2 = $address2;
56
+ }
57
+
58
+ public function getAddress2() {
59
+ return $this->address2;
60
+ }
61
+
62
+ public function setCity($city) {
63
+ $this->city = $city;
64
+ }
65
+
66
+ public function getCity() {
67
+ return $this->city;
68
+ }
69
+
70
+ public function toArray() {
71
+ return array(
72
+ "country_code" => $this->getCountryCode(),
73
+ "state_code" => $this->getStateCode(),
74
+ "state" => $this->getState(),
75
+ "postal_code" => $this->getPostalCode(),
76
+ "address" => $this->getAddress(),
77
+ "address2" => $this->getAddress2(),
78
+ "city" => $this->getCity()
79
+ );
80
+ }
81
+ }
src/modules/contacts/models/ContactFormSevenSubmission.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ namespace CreativeMail\Modules\Contacts\Models;
5
+
6
+ use WPCF7_Submission;
7
+
8
+ class ContactFormSevenSubmission extends WPCF7_Submission
9
+ {
10
+
11
+ }
src/modules/contacts/models/ContactModel.php ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace CreativeMail\Modules\Contacts\Models;
4
+
5
+ class ContactModel
6
+ {
7
+ public $email;
8
+ public $phone;
9
+ public $companyName;
10
+ public $name;
11
+ public $firstName;
12
+ public $lastName;
13
+ public $optIn;
14
+ public $optActionBy;
15
+ public $contactAddresses;
16
+ public $eventType;
17
+
18
+ function __construct() {
19
+ }
20
+
21
+ public function setEmail($email) {
22
+ if (isset($email) && !empty($email)) {
23
+ $this->email = $email;
24
+ }
25
+ else {
26
+ throw new Exception('invalid value for email');
27
+ }
28
+ }
29
+
30
+ public function getEmail() {
31
+ return $this->email;
32
+ }
33
+
34
+ public function setPhone($phone) {
35
+ $this->phone = $phone;
36
+ }
37
+
38
+ public function getPhone() {
39
+ return $this->phone;
40
+ }
41
+
42
+ public function setCompanyName($companyName) {
43
+ $this->companyName = $companyName;
44
+ }
45
+
46
+ public function getCompanyName() {
47
+ return $this->companyName;
48
+ }
49
+
50
+ public function setName($name) {
51
+ $this->name = $name;
52
+ }
53
+
54
+ public function getName() {
55
+ return $this->name;
56
+ }
57
+
58
+ public function setFirstName($firstName) {
59
+ $this->firstName = $firstName;
60
+ }
61
+
62
+ public function getFirstName() {
63
+ return $this->firstName;
64
+ }
65
+
66
+ public function setLastName($lastName) {
67
+ $this->lastName = $lastName;
68
+ }
69
+
70
+ public function getLastName() {
71
+ return $this->lastName;
72
+ }
73
+
74
+ public function setOptIn($optIn) {
75
+ $this->optIn = $optIn;
76
+ }
77
+
78
+ public function getOptIn() {
79
+ return $this->optIn;
80
+ }
81
+
82
+ public function setOptActionBy($optActionBy) {
83
+ $this->optActionBy = $optActionBy;
84
+ }
85
+
86
+ public function getOptActionBy() {
87
+ return $this->optActionBy;
88
+ }
89
+
90
+ public function setContactAddress(ContactAddressModel $contactAddresses) {
91
+ $this->contactAddresses = $contactAddresses;
92
+ }
93
+
94
+ public function getContactAddress() {
95
+ return $this->contactAddresses;
96
+ }
97
+
98
+ public function setEventType($eventType) {
99
+ $this->eventType = $eventType;
100
+ }
101
+
102
+ public function getEventType() {
103
+ return $this->eventType;
104
+ }
105
+
106
+ function toArray() {
107
+ $result = array(
108
+ "email" => $this->getEmail(),
109
+ "phone" => $this->getPhone(),
110
+ "company_name" => $this->getCompanyName(),
111
+ "name" => $this->getName(),
112
+ "first_name" => $this->getFirstName(),
113
+ "last_name" => $this->getLastName(),
114
+ "opt_in" => $this->getOptIn(),
115
+ "opt_action_by" => $this->getOptActionBy(),
116
+ "event_type" => $this->getEventType()
117
+ );
118
+
119
+ $address = $this->getContactAddress();
120
+ if(isset($address)){
121
+ $result["addresses"] = array($address->toArray());
122
+ }
123
+
124
+ return $result;
125
+ }
126
+
127
+ function toJson() {
128
+ return wp_json_encode($this->toArray());
129
+ }
130
+ }
src/modules/contacts/models/OptActionBy.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ namespace CreativeMail\Modules\Contacts\Models;
5
+
6
+
7
+ class OptActionBy
8
+ {
9
+ const Visitor = 1;
10
+ const Owner = 2;
11
+ }
src/modules/woocommerce/models/WCProductModel.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace CreativeMail\Modules\WooCommerce\Models;
4
+
5
+ class WCProductModel {
6
+ public $id;
7
+ public $name;
8
+ public $sku;
9
+ public $slug;
10
+ public $description;
11
+ public $short_description;
12
+ public $price;
13
+ public $regular_price;
14
+ public $sale_price;
15
+ public $date_created;
16
+ public $date_modified;
17
+ public $status;
18
+ public $stock_status;
19
+ public $url;
20
+ public $image_url;
21
+
22
+ function __construct($data) {
23
+ $this->id = $data['id'];
24
+ $this->name = $data['name'];
25
+ $this->sku = $data['sku'];
26
+ $this->slug = $data['slug'];
27
+ $this->description = $data['description'];
28
+ $this->short_description = $data['short_description'];
29
+ $this->price = $data['price'];
30
+ $this->regular_price = $data['regular_price'];
31
+ $this->sale_price = $data['sale_price'];
32
+ $this->date_created = $data['date_created'];
33
+ $this->date_modified = $data['date_modified'];
34
+ $this->status = $data['status'];
35
+ $this->stock_status = $data['stock_status'];
36
+ $this->url = get_permalink($data['id']);
37
+ $this->image_url = null;
38
+ if ($data['image_id'] !== "") {
39
+ $this->image_url = wp_get_attachment_url($data['image_id']);
40
+ }
41
+ }
42
+ }
src/modules/woocommerce/models/WCStoreInformation.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ namespace CreativeMail\Modules\WooCommerce\Models;
5
+
6
+ use WC_Countries;
7
+
8
+ class WCStoreInformation
9
+ {
10
+ public $address1;
11
+ public $address2;
12
+ public $city;
13
+ public $postcode;
14
+ public $state;
15
+ public $country;
16
+ public $currency;
17
+ public $currency_symbol;
18
+
19
+ function __construct()
20
+ {
21
+ if (in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins'))))
22
+ {
23
+ $location = wc_get_base_location();
24
+ $this->address1 = apply_filters( 'woocommerce_countries_base_address', get_option( 'woocommerce_store_address', '' ));
25
+ $this->address2 = apply_filters( 'woocommerce_countries_base_address_2', get_option( 'woocommerce_store_address_2', '' ));
26
+ $this->city = apply_filters( 'woocommerce_countries_base_city', $location);
27
+ $this->postcode = apply_filters( 'woocommerce_countries_base_postcode', $location );
28
+ $this->state = apply_filters( 'woocommerce_countries_base_country', $location );
29
+ $this->country = apply_filters( 'woocommerce_countries_base_country', $location );
30
+ $this->currency_symbol = get_woocommerce_currency_symbol();
31
+ $this->currency = get_woocommerce_currency();
32
+ }
33
+ }
34
+ }
src/views/activated-integrations.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ use CreativeMail\CreativeMail;
4
+
5
+ $available_integrations = CreativeMail::get_instance()->get_integration_manager()->get_active_plugins();
6
+ $activated_integrations = CreativeMail::get_instance()->get_integration_manager()->get_activated_integrations();
7
+
8
+ ?>
9
+
10
+
11
+ <p>We will synchronise your contacts from the following plugins:</p>
12
+
13
+
14
+ <form name="plugins" action="" method="post">
15
+ <input type="hidden" name="action" value="change_activated_plugins" />
16
+ <ul>
17
+ <?php
18
+
19
+ foreach ($available_integrations as $available_integration){
20
+
21
+ $active = in_array($available_integration, $activated_integrations);
22
+ $checked = $active === true ? 'checked' : '';
23
+
24
+ echo '<li><label class="ce4wp-checkbox"><input type="checkbox" name="activated_plugins[]" value="' . esc_attr($available_integration->get_slug()) . '" '.esc_attr($checked).' /><span>' . esc_html($available_integration->get_name()) . '</span></label></li>';
25
+ }
26
+
27
+ ?>
28
+ </ul>
29
+ <div class="ce-kvp">
30
+ <input name="save_button" type="submit" class="ce4wp-button-text-primary ce4wp-right" id="save-activated-plugins" value="Save" />
31
+ </div>
32
+ </form>
src/views/available-integrations.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ use CreativeMail\CreativeMail;
4
+
5
+ $supported_integrations = CreativeMail::get_instance()->get_integration_manager()->get_supported_integrations();
6
+
7
+ ?>
8
+
9
+ <p>
10
+ We couldn't find any plugins that we support. <br/>
11
+ In order to help you sync your contacts to Creative Mail we have build integrations with the following plugins:
12
+ </p>
13
+ <ul>
14
+ <?php
15
+ foreach ($supported_integrations as $supported_integration) {
16
+ echo '<li>- ' . esc_html($supported_integration->get_name()) . '</li>';
17
+ }
18
+ ?>
19
+ </ul>
src/views/consent.php ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ use CreativeMail\Helpers\EnvironmentHelper;
4
+ use CreativeMail\Helpers\OptionsHelper;
5
+
6
+ if ( $_SERVER['REQUEST_METHOD'] === 'POST') {
7
+
8
+ if($_POST['action'] === 'consent') {
9
+ OptionsHelper::set_did_accept_consent();
10
+ require 'onboarding.php';
11
+ exit;
12
+ }
13
+ }
14
+
15
+ ?>
16
+
17
+ <div class="ce4wp-admin-wrapper">
18
+
19
+ <header class="ce4wp-header">
20
+ <div class="ce4wp-logo"></div>
21
+ </header>
22
+
23
+ <div class="ce4wp-redirector">
24
+ <div class="ce4wp-card">
25
+ <div class="ce4wp-p-3 ce4wp-row">
26
+ <div class="ce4wp-center ce4wp-col">
27
+ <div>
28
+ <h2 class="ce4wp-typography-root ce4wp-typography-h2 ce4wp-typography-gutter-bottom">
29
+ WordPress + Email Marketing, A Beautiful Combination!
30
+ </h2>
31
+ <h6 class="ce4wp-typography-root ce4wp-subtitle1 ce4wp-typography-gutter-bottom">
32
+ We've created an email marketing solution that works seamlessly with your website, domain and WooCommerce store. Send great looking emails to your customers and prospects through our simple all-in-one solution. The goal... to make you a great marketer in no time.
33
+ </h6>
34
+
35
+ <h4 class="ce4wp-typography-root ce4wp-typography-h4 ce4wp-typography-gutter-bottom">
36
+ Send:
37
+ </h4>
38
+
39
+ <ul class="ce4wp-list-root pb-4 ce4wp-list-padding">
40
+ <li class="ce4wp-list-item-root ce4wp-list-item-gutters">
41
+ <div class="ce4wp-list-item-icon-root ce4wp-pr-3">
42
+ <svg class="ce4wp-svg-icon-root ce4wp-svg-icon-color" focusable="false" viewBox="0 0 24 24" aria-hidden="true">
43
+ <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"></path>
44
+ </svg>
45
+ </div>
46
+ <div class="ce4wp-list-item-test-root ce4wp-d-flex ce4wp-flex-column ce4wp-m-0 ">
47
+ <span class="ce4wp-typography-root ce4wp-list-item-text-primary ce4wp-body1-54 ce4wp-display-block">
48
+ <p class="ce4wp-typography-root ce4wp-body1-54 ce4wp-colorTextSecondary-78">
49
+ <span>Blog Updates</span>
50
+ </p>
51
+ </span>
52
+ </div>
53
+ </li>
54
+ <li class="ce4wp-list-item-root ce4wp-list-item-gutters">
55
+ <div class="ce4wp-list-item-icon-root ce4wp-pr-3">
56
+ <svg class="ce4wp-svg-icon-root ce4wp-svg-icon-color" focusable="false" viewBox="0 0 24 24" aria-hidden="true">
57
+ <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z">
58
+ </path>
59
+ </svg>
60
+ </div>
61
+ <div class="ce4wp-list-item-test-root ce4wp-d-flex ce4wp-flex-column ce4wp-m-0 ">
62
+ <span class="ce4wp-typography-root ce4wp-list-item-text-primary ce4wp-body1-54 ce4wp-display-block">
63
+ <p class="ce4wp-typography-root ce4wp-body1-54 ce4wp-colorTextSecondary-78">
64
+ <span>Amazing Announcements</span>
65
+ </p>
66
+ </span>
67
+ </div>
68
+ </li>
69
+ <li class="ce4wp-list-item-root ce4wp-list-item-gutters">
70
+ <div class="ce4wp-list-item-icon-root ce4wp-pr-3">
71
+ <svg class="ce4wp-svg-icon-root ce4wp-svg-icon-color" focusable="false" viewBox="0 0 24 24" aria-hidden="true">
72
+ <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z">
73
+ </path>
74
+ </svg>
75
+ </div>
76
+ <div class="ce4wp-list-item-test-root ce4wp-d-flex ce4wp-flex-column ce4wp-m-0 ">
77
+ <span class="ce4wp-typography-root ce4wp-list-item-text-primary ce4wp-body1-54 ce4wp-display-block">
78
+ <p class="ce4wp-typography-root ce4wp-body1-54 ce4wp-colorTextSecondary-78">
79
+ <span>Sweet Store Promotions</span>
80
+ </p>
81
+ </span>
82
+ </div>
83
+ </li>
84
+ <li class="ce4wp-list-item-root ce4wp-list-item-gutters">
85
+ <div class="ce4wp-list-item-icon-root ce4wp-pr-3">
86
+ <svg class="ce4wp-svg-icon-root ce4wp-svg-icon-color" focusable="false" viewBox="0 0 24 24" aria-hidden="true">
87
+ <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z">
88
+ </path>
89
+ </svg>
90
+ </div>
91
+ <div class="ce4wp-list-item-test-root ce4wp-d-flex ce4wp-flex-column ce4wp-m-0 ">
92
+ <span class="ce4wp-typography-root ce4wp-list-item-text-primary ce4wp-body1-54 ce4wp-display-block">
93
+ <p class="ce4wp-typography-root ce4wp-body1-54 ce4wp-colorTextSecondary-78">
94
+ <span>Event Updates</span>
95
+ </p>
96
+ </span>
97
+ </div>
98
+ </li>
99
+ </ul>
100
+
101
+ <!-- Consent text -->
102
+
103
+ <h4 class="ce4wp-typography-root ce4wp-typography-h4 ce4wp-typography-gutter-bottom">Before you continue:</h4>
104
+ <h6 class="ce4wp-typography-root ce4wp-subtitle1 ce4wp-typography-gutter-bottom">
105
+ <ul>
106
+ <li>- Creative Mail is a stand alone product that we load within WordPress for an awesome experience, but is not in any way related to or managed by WordPress;</li>
107
+ <li>- By using Creative Mail you'll share basic information about your site (like the site name and associated URL) with Constant Contact so that we can retrieve your blog posts, media and WooCommerce products for use in your emails;</li>
108
+ <li>- Creative Mail also uses different tools and cookies to improve the performance and experience of the product, for more information you can read our <a href="https://www.endurance.com/privacy/privacy" target="_blank">privacy notice</a>.</li>
109
+ </ul>
110
+ </h6>
111
+
112
+ <div class="ce4wp-pt-1 ce4wp-pb-1">
113
+ <form name="disconnect" action="" method="post">
114
+ <input type="hidden" name="action" value="consent" />
115
+ <input name="disconnect_button" type="submit" class="ce4wp-button-base-root ce4wp-button-root ce4wp-button-contained ce4wp-button-contained-primary" id="disconnect-instance" value="I Agree and let's get started!" />
116
+ </form>
117
+ </div>
118
+
119
+ </div>
120
+ </div>
121
+ <div class="col-auto ce4wp-welcome-image">
122
+ <img src="<?php echo CE4WP_PLUGIN_URL . 'assets/images/wp-plugin-marketing.png'; ?>" />
123
+ </div>
124
+ </div>
125
+ </div>
126
+ </div>
127
+ </div>
src/views/dashboard.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ use CreativeMail\Helpers\EnvironmentHelper;
3
+ ?>
4
+
5
+ <div class="ce4wp-admin-wrapper">
6
+
7
+ <header class="ce4wp-header">
8
+ <div class="ce4wp-logo"></div>
9
+ </header>
10
+
11
+ <div class="ce4wp-redirector">
12
+ <div class="ce4wp-card">
13
+ <div class="ce4wp-p-3 ce4wp-row">
14
+ <div class="ce4wp-center ce4wp-col">
15
+ <div>
16
+ <h2 class="ce4wp-typography-root ce4wp-typography-h2 ce4wp-typography-gutter-bottom">
17
+ WordPress + Email Marketing, A Beautiful Combination!
18
+ </h2>
19
+ <h6 class="ce4wp-typography-root ce4wp-subtitle1 ce4wp-typography-gutter-bottom">
20
+ You are all set, we have linked your WordPress site to your Creative Mail account, click the button below to go to your Creative Mail dashboard.
21
+ </h6>
22
+
23
+ <div class="ce4wp-pt-1 ce4wp-pb-1">
24
+ <a id="ce4wp-go-button" href="<?php echo esc_url($this->dashboard_url) ?>" class="ce4wp-button-base-root ce4wp-button-root ce4wp-button-contained ce4wp-button-contained-primary" target="_blank">
25
+ Go to Creative Mail dashboard
26
+ </a>
27
+ </div>
28
+ </div>
29
+ </div>
30
+ <div class="col-auto">
31
+ <img src="<?php echo esc_url( CE4WP_PLUGIN_URL. '/assets/images/wp-plugin-marketing.png'); ?>" alt="" />
32
+ </div>
33
+ </div>
34
+ </div>
35
+ </div>
36
+ </div>
37
+
38
+ <script type="application/javascript">
39
+ let blurred = false;
40
+ window.onblur = function() {
41
+ blurred = true;
42
+ document.getElementById('ce4wp-go-button').style.display = "none";
43
+ };
44
+ window.onfocus = function() { blurred && (location.reload()); };
45
+ </script>
src/views/onboarding.php ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ use CreativeMail\Helpers\EnvironmentHelper;
3
+
4
+ $redirectUrl = EnvironmentHelper::get_app_gateway_url('wordpress/v1.0/instances/open?clearSession=true&redirectUrl=');
5
+ $onboardingUrl = EnvironmentHelper::get_app_url() . 'marketing/onboarding/signup?wp_site_name=' . $this->instance_name
6
+ . '&wp_site_uuid=' . $this->instance_uuid
7
+ . '&wp_handshake=' . $this->instance_handshake_token
8
+ . '&wp_callback_url=' . $this->instance_callback_url
9
+ . '&wp_instance_url=' . $this->instance_url
10
+ . '&wp_version=' . get_bloginfo('version')
11
+ . '&plugin_version=' . CE4WP_PLUGIN_VERSION;
12
+ ?>
13
+
14
+ <div class="ce4wp-admin-wrapper">
15
+
16
+ <header class="ce4wp-header">
17
+ <div class="ce4wp-logo"></div>
18
+ </header>
19
+
20
+ <div class="ce4wp-redirector">
21
+ <div class="ce4wp-card">
22
+ <div class="ce4wp-p-3 ce4wp-row">
23
+ <div class="ce4wp-center ce4wp-col">
24
+ <div>
25
+ <h2 class="ce4wp-typography-root ce4wp-typography-h2 ce4wp-typography-gutter-bottom">
26
+ WordPress + Email Marketing, A Beautiful Combination!
27
+ </h2>
28
+ <h6 class="ce4wp-typography-root ce4wp-subtitle1 ce4wp-typography-gutter-bottom">
29
+ We've created an email marketing solution that works seamlessly with your website, domain and WooCommerce store. Send great looking emails to your customers and prospects through our simple all-in-one solution. The goal... to make you a great marketer in no time.
30
+ </h6>
31
+
32
+ <h4 class="ce4wp-typography-root ce4wp-typography-h4 ce4wp-typography-gutter-bottom">
33
+ Send:
34
+ </h4>
35
+
36
+ <ul class="ce4wp-list-root pb-4 ce4wp-list-padding">
37
+ <li class="ce4wp-list-item-root ce4wp-list-item-gutters">
38
+ <div class="ce4wp-list-item-icon-root ce4wp-pr-3">
39
+ <svg class="ce4wp-svg-icon-root ce4wp-svg-icon-color" focusable="false" viewBox="0 0 24 24" aria-hidden="true">
40
+ <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"></path>
41
+ </svg>
42
+ </div>
43
+ <div class="ce4wp-list-item-test-root ce4wp-d-flex ce4wp-flex-column ce4wp-m-0 ">
44
+ <span class="ce4wp-typography-root ce4wp-list-item-text-primary ce4wp-body1-54 ce4wp-display-block">
45
+ <p class="ce4wp-typography-root ce4wp-body1-54 ce4wp-colorTextSecondary-78">
46
+ <span>Blog Updates</span>
47
+ </p>
48
+ </span>
49
+ </div>
50
+ </li>
51
+ <li class="ce4wp-list-item-root ce4wp-list-item-gutters">
52
+ <div class="ce4wp-list-item-icon-root ce4wp-pr-3">
53
+ <svg class="ce4wp-svg-icon-root ce4wp-svg-icon-color" focusable="false" viewBox="0 0 24 24" aria-hidden="true">
54
+ <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z">
55
+ </path>
56
+ </svg>
57
+ </div>
58
+ <div class="ce4wp-list-item-test-root ce4wp-d-flex ce4wp-flex-column ce4wp-m-0 ">
59
+ <span class="ce4wp-typography-root ce4wp-list-item-text-primary ce4wp-body1-54 ce4wp-display-block">
60
+ <p class="ce4wp-typography-root ce4wp-body1-54 ce4wp-colorTextSecondary-78">
61
+ <span>Amazing Announcements</span>
62
+ </p>
63
+ </span>
64
+ </div>
65
+ </li>
66
+ <li class="ce4wp-list-item-root ce4wp-list-item-gutters">
67
+ <div class="ce4wp-list-item-icon-root ce4wp-pr-3">
68
+ <svg class="ce4wp-svg-icon-root ce4wp-svg-icon-color" focusable="false" viewBox="0 0 24 24" aria-hidden="true">
69
+ <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z">
70
+ </path>
71
+ </svg>
72
+ </div>
73
+ <div class="ce4wp-list-item-test-root ce4wp-d-flex ce4wp-flex-column ce4wp-m-0 ">
74
+ <span class="ce4wp-typography-root ce4wp-list-item-text-primary ce4wp-body1-54 ce4wp-display-block">
75
+ <p class="ce4wp-typography-root ce4wp-body1-54 ce4wp-colorTextSecondary-78">
76
+ <span>Sweet Store Promotions</span>
77
+ </p>
78
+ </span>
79
+ </div>
80
+ </li>
81
+ <li class="ce4wp-list-item-root ce4wp-list-item-gutters">
82
+ <div class="ce4wp-list-item-icon-root ce4wp-pr-3">
83
+ <svg class="ce4wp-svg-icon-root ce4wp-svg-icon-color" focusable="false" viewBox="0 0 24 24" aria-hidden="true">
84
+ <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z">
85
+ </path>
86
+ </svg>
87
+ </div>
88
+ <div class="ce4wp-list-item-test-root ce4wp-d-flex ce4wp-flex-column ce4wp-m-0 ">
89
+ <span class="ce4wp-typography-root ce4wp-list-item-text-primary ce4wp-body1-54 ce4wp-display-block">
90
+ <p class="ce4wp-typography-root ce4wp-body1-54 ce4wp-colorTextSecondary-78">
91
+ <span>Event Updates</span>
92
+ </p>
93
+ </span>
94
+ </div>
95
+ </li>
96
+ </ul>
97
+
98
+ <!-- Consent text -->
99
+
100
+ <h4 class="ce4wp-typography-root ce4wp-typography-h4 ce4wp-typography-gutter-bottom">Before you continue:</h4>
101
+ <h6 class="ce4wp-typography-root ce4wp-subtitle1 ce4wp-typography-gutter-bottom">
102
+ <ul>
103
+ <li>- Creative Mail is a stand alone product that we load within WordPress for an awesome experience, but is not in any way related to or managed by WordPress;</li>
104
+ <li>- By using Creative Mail you'll share basic information about your site (like the site name and associated URL) with Constant Contact so that we can retrieve your blog posts, media and WooCommerce products for use in your emails;</li>
105
+ <li>- Creative Mail also uses different tools and cookies to improve the performance and experience of the product, for more information you can read our <a href="https://www.endurance.com/privacy/privacy" target="_blank">privacy notice</a>.</li>
106
+ </ul>
107
+ </h6>
108
+
109
+ <div class="ce4wp-pt-1 ce4wp-pb-1">
110
+ <a id="ce4wp-go-button" href="<?php echo esc_url($redirectUrl . rawurlencode($onboardingUrl)) ?>" class="ce4wp-button-base-root ce4wp-button-root ce4wp-button-contained ce4wp-button-contained-primary" target="_blank">
111
+ I Agree and let's get started!
112
+ </a>
113
+ </div>
114
+
115
+ </div>
116
+ </div>
117
+ <div class="col-auto">
118
+ <img src="<?php echo esc_url( CE4WP_PLUGIN_URL. '/assets/images/wp-plugin-marketing.png'); ?>" alt="" />
119
+ </div>
120
+ </div>
121
+ </div>
122
+ </div>
123
+ </div>
124
+
125
+ <script type="application/javascript">
126
+ let blurred = false;
127
+ window.onblur = function() {
128
+ blurred = true;
129
+ document.getElementById('ce4wp-go-button').style.display = "none";
130
+ };
131
+ window.onfocus = function() { blurred && (location.reload()); };
132
+ </script>
src/views/pending-setup.php ADDED
@@ -0,0 +1 @@
 
1
+ <p>Power your WooCommerce Store or WordPress Blog with simple & free email marketing from Constant Contact. With the official Creative Mail for WooCommerce plugin, your products, blog posts, images and store links are automatically included as rich shoppable email marketing content for your customers. Our included CRM also intelligently pulls in and identifies your WordPress site contacts and WooCommerce store customers. That makes it easy to build audiences and send targeted customer campaigns. Get free email marketing, 98% deliverability, and Constant Contact rock solid reliability all without ever needing to leave your WP Admin.</p>
src/views/settings-internal.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ use CreativeMail\Helpers\EnvironmentHelper;
3
+ ?>
4
+
5
+ <h5>Technical details</h5>
6
+
7
+ <div class="ce4wp-kvp">
8
+ <h6>Instance UUID</h6>
9
+ <h5><?php echo esc_html($this->instance_uuid) ?></h5>
10
+ </div>
11
+
12
+ <div class="ce4wp-kvp">
13
+ <h6>Instance Id</h6>
14
+ <h5><?php echo esc_html($this->instance_id) ?></h5>
15
+ </div>
16
+
17
+ <div class="ce4wp-kvp">
18
+ <h6>Environment</h6>
19
+ <h5><?php echo esc_html(EnvironmentHelper::get_environment()) ?></h5>
20
+ </div>
21
+
22
+ <div class="ce4wp-kvp">
23
+ <h6>App</h6>
24
+ <h5><?php echo esc_js(EnvironmentHelper::get_app_url()) ?></h5>
25
+ </div>
26
+
27
+ <div class="ce4wp-kvp">
28
+ <h6>App Gateway</h6>
29
+ <h5><?php echo esc_js(EnvironmentHelper::get_app_gateway_url()) ?></h5>
30
+ </div>
src/views/settings.php ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ use CreativeMail\CreativeMail;
4
+ use CreativeMail\Helpers\EnvironmentHelper;
5
+ use CreativeMail\Helpers\OptionsHelper;
6
+
7
+ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
8
+
9
+ if($_POST['action'] === 'disconnect') {
10
+ OptionsHelper::clear_options(true);
11
+ $this->instance_id = null;
12
+ }
13
+
14
+ if($_POST['action'] === 'change_activated_plugins') {
15
+ $activated_plugins = array();
16
+ $keys = $_POST["activated_plugins"];
17
+ foreach ($keys as $key) {
18
+ array_push($activated_plugins, sanitize_key($key));
19
+ }
20
+
21
+ CreativeMail::get_instance()->get_integration_manager()->set_activated_plugins($activated_plugins);
22
+ }
23
+ }
24
+
25
+ $contact_sync_available = !empty(CreativeMail::get_instance()->get_integration_manager()->get_active_plugins());
26
+
27
+ ?>
28
+
29
+ <div class="ce4wp-admin-wrapper">
30
+ <header class="ce4wp-header">
31
+ <div class="ce4wp-logo"></div>
32
+ </header>
33
+ <div class="ce4wp-container">
34
+
35
+ <h2>Settings</h2>
36
+ <div class="ce4wp-card">
37
+ <h4>Creative Mail by Constant Contact</h4>
38
+
39
+ <?php
40
+ if(EnvironmentHelper::is_test_environment()) {
41
+ include 'settings-internal.php';
42
+ }
43
+ ?>
44
+
45
+ <?php
46
+ if(OptionsHelper::get_instance_id()) {
47
+ include 'unlink.php';
48
+ }
49
+ else {
50
+ include 'pending-setup.php';
51
+ }
52
+ ?>
53
+
54
+ </div>
55
+
56
+ <div class="ce4wp-card" style="display: <?php echo !empty($this->instance_id) ? 'block' : 'none' ?>">
57
+
58
+ <h4>Contact Synchronization</h4>
59
+
60
+ <?php
61
+ if($contact_sync_available){
62
+ include 'activated-integrations.php';
63
+ }
64
+ else {
65
+ include 'available-integrations.php';
66
+ }
67
+ ?>
68
+
69
+ </div>
70
+ </div>
71
+ </div>
src/views/unlink.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <div class="ce4wp-kvp">
2
+ <p>Your WordPress instance is connected to your Creative Mail account. If you would like to unlink your WordPress instance from your account, please click the 'Unlink' button below. <b>Unlinking your account is permanent and cannot be undone.</b></p>
3
+ </div>
4
+
5
+ <div class="ce4wp-kvp">
6
+ <form name="disconnect" action="" method="post">
7
+ <input type="hidden" name="action" value="disconnect" />
8
+ <input name="disconnect_button" type="submit" class="ce4wp-button-text-primary destructive ce4wp-right" id="disconnect-instance" value="Unlink" onclick="return confirm('Are you sure you want to unlink your CreativeMail account from your WordPress site? This action is permanent and cannot be undone.')" />
9
+ </form>
10
+ </div>
vendor/autoload.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload.php @generated by Composer
4
+
5
+ require_once __DIR__ . '/composer/autoload_real.php';
6
+
7
+ return ComposerAutoloaderInitb56847a9532368130d0132145aed966e::getLoader();
vendor/bin/generate-defuse-key ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env php
2
+ <?php
3
+
4
+ use Defuse\Crypto\Key;
5
+
6
+ foreach ([__DIR__ . '/../../../autoload.php', __DIR__ . '/../vendor/autoload.php'] as $file) {
7
+ if (file_exists($file)) {
8
+ require $file;
9
+ break;
10
+ }
11
+ }
12
+
13
+ $key = Key::createNewRandomKey();
14
+ echo $key->saveToAsciiSafeString(), "\n";
vendor/composer/ClassLoader.php ADDED
@@ -0,0 +1,445 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Composer.
5
+ *
6
+ * (c) Nils Adermann <naderman@naderman.de>
7
+ * Jordi Boggiano <j.boggiano@seld.be>
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ namespace Composer\Autoload;
14
+
15
+ /**
16
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
17
+ *
18
+ * $loader = new \Composer\Autoload\ClassLoader();
19
+ *
20
+ * // register classes with namespaces
21
+ * $loader->add('Symfony\Component', __DIR__.'/component');
22
+ * $loader->add('Symfony', __DIR__.'/framework');
23
+ *
24
+ * // activate the autoloader
25
+ * $loader->register();
26
+ *
27
+ * // to enable searching the include path (eg. for PEAR packages)
28
+ * $loader->setUseIncludePath(true);
29
+ *
30
+ * In this example, if you try to use a class in the Symfony\Component
31
+ * namespace or one of its children (Symfony\Component\Console for instance),
32
+ * the autoloader will first look for the class under the component/
33
+ * directory, and it will then fallback to the framework/ directory if not
34
+ * found before giving up.
35
+ *
36
+ * This class is loosely based on the Symfony UniversalClassLoader.
37
+ *
38
+ * @author Fabien Potencier <fabien@symfony.com>
39
+ * @author Jordi Boggiano <j.boggiano@seld.be>
40
+ * @see http://www.php-fig.org/psr/psr-0/
41
+ * @see http://www.php-fig.org/psr/psr-4/
42
+ */
43
+ class ClassLoader
44
+ {
45
+ // PSR-4
46
+ private $prefixLengthsPsr4 = array();
47
+ private $prefixDirsPsr4 = array();
48
+ private $fallbackDirsPsr4 = array();
49
+
50
+ // PSR-0
51
+ private $prefixesPsr0 = array();
52
+ private $fallbackDirsPsr0 = array();
53
+
54
+ private $useIncludePath = false;
55
+ private $classMap = array();
56
+ private $classMapAuthoritative = false;
57
+ private $missingClasses = array();
58
+ private $apcuPrefix;
59
+
60
+ public function getPrefixes()
61
+ {
62
+ if (!empty($this->prefixesPsr0)) {
63
+ return call_user_func_array('array_merge', $this->prefixesPsr0);
64
+ }
65
+
66
+ return array();
67
+ }
68
+
69
+ public function getPrefixesPsr4()
70
+ {
71
+ return $this->prefixDirsPsr4;
72
+ }
73
+
74
+ public function getFallbackDirs()
75
+ {
76
+ return $this->fallbackDirsPsr0;
77
+ }
78
+
79
+ public function getFallbackDirsPsr4()
80
+ {
81
+ return $this->fallbackDirsPsr4;
82
+ }
83
+
84
+ public function getClassMap()
85
+ {
86
+ return $this->classMap;
87
+ }
88
+
89
+ /**
90
+ * @param array $classMap Class to filename map
91
+ */
92
+ public function addClassMap(array $classMap)
93
+ {
94
+ if ($this->classMap) {
95
+ $this->classMap = array_merge($this->classMap, $classMap);
96
+ } else {
97
+ $this->classMap = $classMap;
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Registers a set of PSR-0 directories for a given prefix, either
103
+ * appending or prepending to the ones previously set for this prefix.
104
+ *
105
+ * @param string $prefix The prefix
106
+ * @param array|string $paths The PSR-0 root directories
107
+ * @param bool $prepend Whether to prepend the directories
108
+ */
109
+ public function add($prefix, $paths, $prepend = false)
110
+ {
111
+ if (!$prefix) {
112
+ if ($prepend) {
113
+ $this->fallbackDirsPsr0 = array_merge(
114
+ (array) $paths,
115
+ $this->fallbackDirsPsr0
116
+ );
117
+ } else {
118
+ $this->fallbackDirsPsr0 = array_merge(
119
+ $this->fallbackDirsPsr0,
120
+ (array) $paths
121
+ );
122
+ }
123
+
124
+ return;
125
+ }
126
+
127
+ $first = $prefix[0];
128
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
129
+ $this->prefixesPsr0[$first][$prefix] = (array) $paths;
130
+
131
+ return;
132
+ }
133
+ if ($prepend) {
134
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
135
+ (array) $paths,
136
+ $this->prefixesPsr0[$first][$prefix]
137
+ );
138
+ } else {
139
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
140
+ $this->prefixesPsr0[$first][$prefix],
141
+ (array) $paths
142
+ );
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Registers a set of PSR-4 directories for a given namespace, either
148
+ * appending or prepending to the ones previously set for this namespace.
149
+ *
150
+ * @param string $prefix The prefix/namespace, with trailing '\\'
151
+ * @param array|string $paths The PSR-4 base directories
152
+ * @param bool $prepend Whether to prepend the directories
153
+ *
154
+ * @throws \InvalidArgumentException
155
+ */
156
+ public function addPsr4($prefix, $paths, $prepend = false)
157
+ {
158
+ if (!$prefix) {
159
+ // Register directories for the root namespace.
160
+ if ($prepend) {
161
+ $this->fallbackDirsPsr4 = array_merge(
162
+ (array) $paths,
163
+ $this->fallbackDirsPsr4
164
+ );
165
+ } else {
166
+ $this->fallbackDirsPsr4 = array_merge(
167
+ $this->fallbackDirsPsr4,
168
+ (array) $paths
169
+ );
170
+ }
171
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
172
+ // Register directories for a new namespace.
173
+ $length = strlen($prefix);
174
+ if ('\\' !== $prefix[$length - 1]) {
175
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
176
+ }
177
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
178
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
179
+ } elseif ($prepend) {
180
+ // Prepend directories for an already registered namespace.
181
+ $this->prefixDirsPsr4[$prefix] = array_merge(
182
+ (array) $paths,
183
+ $this->prefixDirsPsr4[$prefix]
184
+ );
185
+ } else {
186
+ // Append directories for an already registered namespace.
187
+ $this->prefixDirsPsr4[$prefix] = array_merge(
188
+ $this->prefixDirsPsr4[$prefix],
189
+ (array) $paths
190
+ );
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Registers a set of PSR-0 directories for a given prefix,
196
+ * replacing any others previously set for this prefix.
197
+ *
198
+ * @param string $prefix The prefix
199
+ * @param array|string $paths The PSR-0 base directories
200
+ */
201
+ public function set($prefix, $paths)
202
+ {
203
+ if (!$prefix) {
204
+ $this->fallbackDirsPsr0 = (array) $paths;
205
+ } else {
206
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Registers a set of PSR-4 directories for a given namespace,
212
+ * replacing any others previously set for this namespace.
213
+ *
214
+ * @param string $prefix The prefix/namespace, with trailing '\\'
215
+ * @param array|string $paths The PSR-4 base directories
216
+ *
217
+ * @throws \InvalidArgumentException
218
+ */
219
+ public function setPsr4($prefix, $paths)
220
+ {
221
+ if (!$prefix) {
222
+ $this->fallbackDirsPsr4 = (array) $paths;
223
+ } else {
224
+ $length = strlen($prefix);
225
+ if ('\\' !== $prefix[$length - 1]) {
226
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
227
+ }
228
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
229
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Turns on searching the include path for class files.
235
+ *
236
+ * @param bool $useIncludePath
237
+ */
238
+ public function setUseIncludePath($useIncludePath)
239
+ {
240
+ $this->useIncludePath = $useIncludePath;
241
+ }
242
+
243
+ /**
244
+ * Can be used to check if the autoloader uses the include path to check
245
+ * for classes.
246
+ *
247
+ * @return bool
248
+ */
249
+ public function getUseIncludePath()
250
+ {
251
+ return $this->useIncludePath;
252
+ }
253
+
254
+ /**
255
+ * Turns off searching the prefix and fallback directories for classes
256
+ * that have not been registered with the class map.
257
+ *
258
+ * @param bool $classMapAuthoritative
259
+ */
260
+ public function setClassMapAuthoritative($classMapAuthoritative)
261
+ {
262
+ $this->classMapAuthoritative = $classMapAuthoritative;
263
+ }
264
+
265
+ /**
266
+ * Should class lookup fail if not found in the current class map?
267
+ *
268
+ * @return bool
269
+ */
270
+ public function isClassMapAuthoritative()
271
+ {
272
+ return $this->classMapAuthoritative;
273
+ }
274
+
275
+ /**
276
+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
277
+ *
278
+ * @param string|null $apcuPrefix
279
+ */
280
+ public function setApcuPrefix($apcuPrefix)
281
+ {
282
+ $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
283
+ }
284
+
285
+ /**
286
+ * The APCu prefix in use, or null if APCu caching is not enabled.
287
+ *
288
+ * @return string|null
289
+ */
290
+ public function getApcuPrefix()
291
+ {
292
+ return $this->apcuPrefix;
293
+ }
294
+
295
+ /**
296
+ * Registers this instance as an autoloader.
297
+ *
298
+ * @param bool $prepend Whether to prepend the autoloader or not
299
+ */
300
+ public function register($prepend = false)
301
+ {
302
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
303
+ }
304
+
305
+ /**
306
+ * Unregisters this instance as an autoloader.
307
+ */
308
+ public function unregister()
309
+ {
310
+ spl_autoload_unregister(array($this, 'loadClass'));
311
+ }
312
+
313
+ /**
314
+ * Loads the given class or interface.
315
+ *
316
+ * @param string $class The name of the class
317
+ * @return bool|null True if loaded, null otherwise
318
+ */
319
+ public function loadClass($class)
320
+ {
321
+ if ($file = $this->findFile($class)) {
322
+ includeFile($file);
323
+
324
+ return true;
325
+ }
326
+ }
327
+
328
+ /**
329
+ * Finds the path to the file where the class is defined.
330
+ *
331
+ * @param string $class The name of the class
332
+ *
333
+ * @return string|false The path if found, false otherwise
334
+ */
335
+ public function findFile($class)
336
+ {
337
+ // class map lookup
338
+ if (isset($this->classMap[$class])) {
339
+ return $this->classMap[$class];
340
+ }
341
+ if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
342
+ return false;
343
+ }
344
+ if (null !== $this->apcuPrefix) {
345
+ $file = apcu_fetch($this->apcuPrefix.$class, $hit);
346
+ if ($hit) {
347
+ return $file;
348
+ }
349
+ }
350
+
351
+ $file = $this->findFileWithExtension($class, '.php');
352
+
353
+ // Search for Hack files if we are running on HHVM
354
+ if (false === $file && defined('HHVM_VERSION')) {
355
+ $file = $this->findFileWithExtension($class, '.hh');
356
+ }
357
+
358
+ if (null !== $this->apcuPrefix) {
359
+ apcu_add($this->apcuPrefix.$class, $file);
360
+ }
361
+
362
+ if (false === $file) {
363
+ // Remember that this class does not exist.
364
+ $this->missingClasses[$class] = true;
365
+ }
366
+
367
+ return $file;
368
+ }
369
+
370
+ private function findFileWithExtension($class, $ext)
371
+ {
372
+ // PSR-4 lookup
373
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
374
+
375
+ $first = $class[0];
376
+ if (isset($this->prefixLengthsPsr4[$first])) {
377
+ $subPath = $class;
378
+ while (false !== $lastPos = strrpos($subPath, '\\')) {
379
+ $subPath = substr($subPath, 0, $lastPos);
380
+ $search = $subPath . '\\';
381
+ if (isset($this->prefixDirsPsr4[$search])) {
382
+ $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
383
+ foreach ($this->prefixDirsPsr4[$search] as $dir) {
384
+ if (file_exists($file = $dir . $pathEnd)) {
385
+ return $file;
386
+ }
387
+ }
388
+ }
389
+ }
390
+ }
391
+
392
+ // PSR-4 fallback dirs
393
+ foreach ($this->fallbackDirsPsr4 as $dir) {
394
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
395
+ return $file;
396
+ }
397
+ }
398
+
399
+ // PSR-0 lookup
400
+ if (false !== $pos = strrpos($class, '\\')) {
401
+ // namespaced class name
402
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
403
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
404
+ } else {
405
+ // PEAR-like class name
406
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
407
+ }
408
+
409
+ if (isset($this->prefixesPsr0[$first])) {
410
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
411
+ if (0 === strpos($class, $prefix)) {
412
+ foreach ($dirs as $dir) {
413
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
414
+ return $file;
415
+ }
416
+ }
417
+ }
418
+ }
419
+ }
420
+
421
+ // PSR-0 fallback dirs
422
+ foreach ($this->fallbackDirsPsr0 as $dir) {
423
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
424
+ return $file;
425
+ }
426
+ }
427
+
428
+ // PSR-0 include paths.
429
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
430
+ return $file;
431
+ }
432
+
433
+ return false;
434
+ }
435
+ }
436
+
437
+ /**
438
+ * Scope isolated include.
439
+ *
440
+ * Prevents access to $this/self from included files.
441
+ */
442
+ function includeFile($file)
443
+ {
444
+ include $file;
445
+ }
vendor/composer/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ Copyright (c) Nils Adermann, Jordi Boggiano
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is furnished
9
+ to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
21
+
vendor/composer/autoload_classmap.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_classmap.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ 'CreativeMail\\Constants\\EnvironmentNames' => $baseDir . '/src/constants/environment-names.php',
10
+ 'CreativeMail\\CreativeMail' => $baseDir . '/src/creativemail.php',
11
+ 'CreativeMail\\Helpers\\EncryptionHelper' => $baseDir . '/src/helpers/encryption-helper.php',
12
+ 'CreativeMail\\Helpers\\EnvironmentHelper' => $baseDir . '/src/helpers/environment-helper.php',
13
+ 'CreativeMail\\Helpers\\GuidHelper' => $baseDir . '/src/helpers/guid-helper.php',
14
+ 'CreativeMail\\Helpers\\OptionsHelper' => $baseDir . '/src/helpers/options-helper.php',
15
+ 'CreativeMail\\Helpers\\SsoHelper' => $baseDir . '/src/helpers/sso-helper.php',
16
+ 'CreativeMail\\Integrations\\Integration' => $baseDir . '/src/integrations/integration.php',
17
+ 'CreativeMail\\Managers\\AdminManager' => $baseDir . '/src/managers/admin-manager.php',
18
+ 'CreativeMail\\Managers\\ApiManager' => $baseDir . '/src/managers/api-manager.php',
19
+ 'CreativeMail\\Managers\\InstanceManager' => $baseDir . '/src/managers/instance-manager.php',
20
+ 'CreativeMail\\Managers\\IntegrationManager' => $baseDir . '/src/managers/integration-manager.php',
21
+ 'CreativeMail\\Modules\\Blog\\Models\\BlogAttachment' => $baseDir . '/src/modules/blog/models/BlogAttachment.php',
22
+ 'CreativeMail\\Modules\\Blog\\Models\\BlogInformation' => $baseDir . '/src/modules/blog/models/BlogInformation.php',
23
+ 'CreativeMail\\Modules\\Blog\\Models\\BlogPost' => $baseDir . '/src/modules/blog/models/BlogPost.php',
24
+ 'CreativeMail\\Modules\\Contacts\\Handlers\\BaseContactFormPluginHandler' => $baseDir . '/src/modules/contacts/Handlers/BaseContactFormPluginHandler.php',
25
+ 'CreativeMail\\Modules\\Contacts\\Handlers\\ContactFormSevenPluginHandler' => $baseDir . '/src/modules/contacts/Handlers/ContactFormSevenPluginHandler.php',
26
+ 'CreativeMail\\Modules\\Contacts\\Handlers\\NewsLetterContactFormPluginHandler' => $baseDir . '/src/modules/contacts/Handlers/NewsLetterContactFormPluginHandler.php',
27
+ 'CreativeMail\\Modules\\Contacts\\Handlers\\WooCommercePluginHandler' => $baseDir . '/src/modules/contacts/Handlers/WooCommercePluginHandler.php',
28
+ 'CreativeMail\\Modules\\Contacts\\Handlers\\WpFormsLitePluginHandler' => $baseDir . '/src/modules/contacts/Handlers/WpFormsLitePluginHandler.php',
29
+ 'CreativeMail\\Modules\\Contacts\\Models\\ContactAddressModel' => $baseDir . '/src/modules/contacts/models/ContactAddressModel.php',
30
+ 'CreativeMail\\Modules\\Contacts\\Models\\ContactFormSevenSubmission' => $baseDir . '/src/modules/contacts/models/ContactFormSevenSubmission.php',
31
+ 'CreativeMail\\Modules\\Contacts\\Models\\ContactModel' => $baseDir . '/src/modules/contacts/models/ContactModel.php',
32
+ 'CreativeMail\\Modules\\Contacts\\Models\\OptActionBy' => $baseDir . '/src/modules/contacts/models/OptActionBy.php',
33
+ 'CreativeMail\\Modules\\Contacts\\Services\\ContactsSyncService' => $baseDir . '/src/modules/contacts/Services/ContactsSyncService.php',
34
+ 'CreativeMail\\Modules\\WooCommerce\\Models\\WCProductModel' => $baseDir . '/src/modules/woocommerce/models/WCProductModel.php',
35
+ 'CreativeMail\\Modules\\WooCommerce\\Models\\WCStoreInformation' => $baseDir . '/src/modules/woocommerce/models/WCStoreInformation.php',
36
+ 'Defuse\\Crypto\\Core' => $vendorDir . '/defuse/php-encryption/src/Core.php',
37
+ 'Defuse\\Crypto\\Crypto' => $vendorDir . '/defuse/php-encryption/src/Crypto.php',
38
+ 'Defuse\\Crypto\\DerivedKeys' => $vendorDir . '/defuse/php-encryption/src/DerivedKeys.php',
39
+ 'Defuse\\Crypto\\Encoding' => $vendorDir . '/defuse/php-encryption/src/Encoding.php',
40
+ 'Defuse\\Crypto\\Exception\\BadFormatException' => $vendorDir . '/defuse/php-encryption/src/Exception/BadFormatException.php',
41
+ 'Defuse\\Crypto\\Exception\\CryptoException' => $vendorDir . '/defuse/php-encryption/src/Exception/CryptoException.php',
42
+ 'Defuse\\Crypto\\Exception\\EnvironmentIsBrokenException' => $vendorDir . '/defuse/php-encryption/src/Exception/EnvironmentIsBrokenException.php',
43
+ 'Defuse\\Crypto\\Exception\\IOException' => $vendorDir . '/defuse/php-encryption/src/Exception/IOException.php',
44
+ 'Defuse\\Crypto\\Exception\\WrongKeyOrModifiedCiphertextException' => $vendorDir . '/defuse/php-encryption/src/Exception/WrongKeyOrModifiedCiphertextException.php',
45
+ 'Defuse\\Crypto\\File' => $vendorDir . '/defuse/php-encryption/src/File.php',
46
+ 'Defuse\\Crypto\\Key' => $vendorDir . '/defuse/php-encryption/src/Key.php',
47
+ 'Defuse\\Crypto\\KeyOrPassword' => $vendorDir . '/defuse/php-encryption/src/KeyOrPassword.php',
48
+ 'Defuse\\Crypto\\KeyProtectedByPassword' => $vendorDir . '/defuse/php-encryption/src/KeyProtectedByPassword.php',
49
+ 'Defuse\\Crypto\\RuntimeTests' => $vendorDir . '/defuse/php-encryption/src/RuntimeTests.php',
50
+ 'Firebase\\JWT\\BeforeValidException' => $vendorDir . '/firebase/php-jwt/src/BeforeValidException.php',
51
+ 'Firebase\\JWT\\ExpiredException' => $vendorDir . '/firebase/php-jwt/src/ExpiredException.php',
52
+ 'Firebase\\JWT\\JWK' => $vendorDir . '/firebase/php-jwt/src/JWK.php',
53
+ 'Firebase\\JWT\\JWT' => $vendorDir . '/firebase/php-jwt/src/JWT.php',
54
+ 'Firebase\\JWT\\SignatureInvalidException' => $vendorDir . '/firebase/php-jwt/src/SignatureInvalidException.php',
55
+ );
vendor/composer/autoload_namespaces.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_namespaces.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ );
vendor/composer/autoload_psr4.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_psr4.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ 'Firebase\\JWT\\' => array($vendorDir . '/firebase/php-jwt/src'),
10
+ 'Defuse\\Crypto\\' => array($vendorDir . '/defuse/php-encryption/src'),
11
+ 'CreativeMail\\Modules\\' => array($baseDir . '/src/modules'),
12
+ 'CreativeMail\\Managers\\' => array($baseDir . '/src/managers'),
13
+ 'CreativeMail\\Integrations\\' => array($baseDir . '/src/integrations'),
14
+ 'CreativeMail\\Helpers\\' => array($baseDir . '/src/helpers'),
15
+ 'CreativeMail\\Constants\\' => array($baseDir . '/src/constants'),
16
+ 'CreativeMail\\' => array($baseDir . '/src'),
17
+ );
vendor/composer/autoload_real.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_real.php @generated by Composer
4
+
5
+ class ComposerAutoloaderInitb56847a9532368130d0132145aed966e
6
+ {
7
+ private static $loader;
8
+
9
+ public static function loadClassLoader($class)
10
+ {
11
+ if ('Composer\Autoload\ClassLoader' === $class) {
12
+ require __DIR__ . '/ClassLoader.php';
13
+ }
14
+ }
15
+
16
+ /**
17
+ * @return \Composer\Autoload\ClassLoader
18
+ */
19
+ public static function getLoader()
20
+ {
21
+ if (null !== self::$loader) {
22
+ return self::$loader;
23
+ }
24
+
25
+ spl_autoload_register(array('ComposerAutoloaderInitb56847a9532368130d0132145aed966e', 'loadClassLoader'), true, true);
26
+ self::$loader = $loader = new \Composer\Autoload\ClassLoader();
27
+ spl_autoload_unregister(array('ComposerAutoloaderInitb56847a9532368130d0132145aed966e', 'loadClassLoader'));
28
+
29
+ $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
30
+ if ($useStaticLoader) {
31
+ require_once __DIR__ . '/autoload_static.php';
32
+
33
+ call_user_func(\Composer\Autoload\ComposerStaticInitb56847a9532368130d0132145aed966e::getInitializer($loader));
34
+ } else {
35
+ $map = require __DIR__ . '/autoload_namespaces.php';
36
+ foreach ($map as $namespace => $path) {
37
+ $loader->set($namespace, $path);
38
+ }
39
+
40
+ $map = require __DIR__ . '/autoload_psr4.php';
41
+ foreach ($map as $namespace => $path) {
42
+ $loader->setPsr4($namespace, $path);
43
+ }
44
+
45
+ $classMap = require __DIR__ . '/autoload_classmap.php';
46
+ if ($classMap) {
47
+ $loader->addClassMap($classMap);
48
+ }
49
+ }
50
+
51
+ $loader->register(true);
52
+
53
+ return $loader;
54
+ }
55
+ }
vendor/composer/autoload_static.php ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_static.php @generated by Composer
4
+
5
+ namespace Composer\Autoload;
6
+
7
+ class ComposerStaticInitb56847a9532368130d0132145aed966e
8
+ {
9
+ public static $prefixLengthsPsr4 = array (
10
+ 'F' =>
11
+ array (
12
+ 'Firebase\\JWT\\' => 13,
13
+ ),
14
+ 'D' =>
15
+ array (
16
+ 'Defuse\\Crypto\\' => 14,
17
+ ),
18
+ 'C' =>
19
+ array (
20
+ 'CreativeMail\\Modules\\' => 21,
21
+ 'CreativeMail\\Managers\\' => 22,
22
+ 'CreativeMail\\Integrations\\' => 26,
23
+ 'CreativeMail\\Helpers\\' => 21,
24
+ 'CreativeMail\\Constants\\' => 23,
25
+ 'CreativeMail\\' => 13,
26
+ ),
27
+ );
28
+
29
+ public static $prefixDirsPsr4 = array (
30
+ 'Firebase\\JWT\\' =>
31
+ array (
32
+ 0 => __DIR__ . '/..' . '/firebase/php-jwt/src',
33
+ ),
34
+ 'Defuse\\Crypto\\' =>
35
+ array (
36
+ 0 => __DIR__ . '/..' . '/defuse/php-encryption/src',
37
+ ),
38
+ 'CreativeMail\\Modules\\' =>
39
+ array (
40
+ 0 => __DIR__ . '/../..' . '/src/modules',
41
+ ),
42
+ 'CreativeMail\\Managers\\' =>
43
+ array (
44
+ 0 => __DIR__ . '/../..' . '/src/managers',
45
+ ),
46
+ 'CreativeMail\\Integrations\\' =>
47
+ array (
48
+ 0 => __DIR__ . '/../..' . '/src/integrations',
49
+ ),
50
+ 'CreativeMail\\Helpers\\' =>
51
+ array (
52
+ 0 => __DIR__ . '/../..' . '/src/helpers',
53
+ ),
54
+ 'CreativeMail\\Constants\\' =>
55
+ array (
56
+ 0 => __DIR__ . '/../..' . '/src/constants',
57
+ ),
58
+ 'CreativeMail\\' =>
59
+ array (
60
+ 0 => __DIR__ . '/../..' . '/src',
61
+ ),
62
+ );
63
+
64
+ public static $classMap = array (
65
+ 'CreativeMail\\Constants\\EnvironmentNames' => __DIR__ . '/../..' . '/src/constants/environment-names.php',
66
+ 'CreativeMail\\CreativeMail' => __DIR__ . '/../..' . '/src/creativemail.php',
67
+ 'CreativeMail\\Helpers\\EncryptionHelper' => __DIR__ . '/../..' . '/src/helpers/encryption-helper.php',
68
+ 'CreativeMail\\Helpers\\EnvironmentHelper' => __DIR__ . '/../..' . '/src/helpers/environment-helper.php',
69
+ 'CreativeMail\\Helpers\\GuidHelper' => __DIR__ . '/../..' . '/src/helpers/guid-helper.php',
70
+ 'CreativeMail\\Helpers\\OptionsHelper' => __DIR__ . '/../..' . '/src/helpers/options-helper.php',
71
+ 'CreativeMail\\Helpers\\SsoHelper' => __DIR__ . '/../..' . '/src/helpers/sso-helper.php',
72
+ 'CreativeMail\\Integrations\\Integration' => __DIR__ . '/../..' . '/src/integrations/integration.php',
73
+ 'CreativeMail\\Managers\\AdminManager' => __DIR__ . '/../..' . '/src/managers/admin-manager.php',
74
+ 'CreativeMail\\Managers\\ApiManager' => __DIR__ . '/../..' . '/src/managers/api-manager.php',
75
+ 'CreativeMail\\Managers\\InstanceManager' => __DIR__ . '/../..' . '/src/managers/instance-manager.php',
76
+ 'CreativeMail\\Managers\\IntegrationManager' => __DIR__ . '/../..' . '/src/managers/integration-manager.php',
77
+ 'CreativeMail\\Modules\\Blog\\Models\\BlogAttachment' => __DIR__ . '/../..' . '/src/modules/blog/models/BlogAttachment.php',
78
+ 'CreativeMail\\Modules\\Blog\\Models\\BlogInformation' => __DIR__ . '/../..' . '/src/modules/blog/models/BlogInformation.php',
79
+ 'CreativeMail\\Modules\\Blog\\Models\\BlogPost' => __DIR__ . '/../..' . '/src/modules/blog/models/BlogPost.php',
80
+ 'CreativeMail\\Modules\\Contacts\\Handlers\\BaseContactFormPluginHandler' => __DIR__ . '/../..' . '/src/modules/contacts/Handlers/BaseContactFormPluginHandler.php',
81
+ 'CreativeMail\\Modules\\Contacts\\Handlers\\ContactFormSevenPluginHandler' => __DIR__ . '/../..' . '/src/modules/contacts/Handlers/ContactFormSevenPluginHandler.php',
82
+ 'CreativeMail\\Modules\\Contacts\\Handlers\\NewsLetterContactFormPluginHandler' => __DIR__ . '/../..' . '/src/modules/contacts/Handlers/NewsLetterContactFormPluginHandler.php',
83
+ 'CreativeMail\\Modules\\Contacts\\Handlers\\WooCommercePluginHandler' => __DIR__ . '/../..' . '/src/modules/contacts/Handlers/WooCommercePluginHandler.php',
84
+ 'CreativeMail\\Modules\\Contacts\\Handlers\\WpFormsLitePluginHandler' => __DIR__ . '/../..' . '/src/modules/contacts/Handlers/WpFormsLitePluginHandler.php',
85
+ 'CreativeMail\\Modules\\Contacts\\Models\\ContactAddressModel' => __DIR__ . '/../..' . '/src/modules/contacts/models/ContactAddressModel.php',
86
+ 'CreativeMail\\Modules\\Contacts\\Models\\ContactFormSevenSubmission' => __DIR__ . '/../..' . '/src/modules/contacts/models/ContactFormSevenSubmission.php',
87
+ 'CreativeMail\\Modules\\Contacts\\Models\\ContactModel' => __DIR__ . '/../..' . '/src/modules/contacts/models/ContactModel.php',
88
+ 'CreativeMail\\Modules\\Contacts\\Models\\OptActionBy' => __DIR__ . '/../..' . '/src/modules/contacts/models/OptActionBy.php',
89
+ 'CreativeMail\\Modules\\Contacts\\Services\\ContactsSyncService' => __DIR__ . '/../..' . '/src/modules/contacts/Services/ContactsSyncService.php',
90
+ 'CreativeMail\\Modules\\WooCommerce\\Models\\WCProductModel' => __DIR__ . '/../..' . '/src/modules/woocommerce/models/WCProductModel.php',
91
+ 'CreativeMail\\Modules\\WooCommerce\\Models\\WCStoreInformation' => __DIR__ . '/../..' . '/src/modules/woocommerce/models/WCStoreInformation.php',
92
+ 'Defuse\\Crypto\\Core' => __DIR__ . '/..' . '/defuse/php-encryption/src/Core.php',
93
+ 'Defuse\\Crypto\\Crypto' => __DIR__ . '/..' . '/defuse/php-encryption/src/Crypto.php',
94
+ 'Defuse\\Crypto\\DerivedKeys' => __DIR__ . '/..' . '/defuse/php-encryption/src/DerivedKeys.php',
95
+ 'Defuse\\Crypto\\Encoding' => __DIR__ . '/..' . '/defuse/php-encryption/src/Encoding.php',
96
+ 'Defuse\\Crypto\\Exception\\BadFormatException' => __DIR__ . '/..' . '/defuse/php-encryption/src/Exception/BadFormatException.php',
97
+ 'Defuse\\Crypto\\Exception\\CryptoException' => __DIR__ . '/..' . '/defuse/php-encryption/src/Exception/CryptoException.php',
98
+ 'Defuse\\Crypto\\Exception\\EnvironmentIsBrokenException' => __DIR__ . '/..' . '/defuse/php-encryption/src/Exception/EnvironmentIsBrokenException.php',
99
+ 'Defuse\\Crypto\\Exception\\IOException' => __DIR__ . '/..' . '/defuse/php-encryption/src/Exception/IOException.php',
100
+ 'Defuse\\Crypto\\Exception\\WrongKeyOrModifiedCiphertextException' => __DIR__ . '/..' . '/defuse/php-encryption/src/Exception/WrongKeyOrModifiedCiphertextException.php',
101
+ 'Defuse\\Crypto\\File' => __DIR__ . '/..' . '/defuse/php-encryption/src/File.php',
102
+ 'Defuse\\Crypto\\Key' => __DIR__ . '/..' . '/defuse/php-encryption/src/Key.php',
103
+ 'Defuse\\Crypto\\KeyOrPassword' => __DIR__ . '/..' . '/defuse/php-encryption/src/KeyOrPassword.php',
104
+ 'Defuse\\Crypto\\KeyProtectedByPassword' => __DIR__ . '/..' . '/defuse/php-encryption/src/KeyProtectedByPassword.php',
105
+ 'Defuse\\Crypto\\RuntimeTests' => __DIR__ . '/..' . '/defuse/php-encryption/src/RuntimeTests.php',
106
+ 'Firebase\\JWT\\BeforeValidException' => __DIR__ . '/..' . '/firebase/php-jwt/src/BeforeValidException.php',
107
+ 'Firebase\\JWT\\ExpiredException' => __DIR__ . '/..' . '/firebase/php-jwt/src/ExpiredException.php',
108
+ 'Firebase\\JWT\\JWK' => __DIR__ . '/..' . '/firebase/php-jwt/src/JWK.php',
109
+ 'Firebase\\JWT\\JWT' => __DIR__ . '/..' . '/firebase/php-jwt/src/JWT.php',
110
+ 'Firebase\\JWT\\SignatureInvalidException' => __DIR__ . '/..' . '/firebase/php-jwt/src/SignatureInvalidException.php',
111
+ );
112
+
113
+ public static function getInitializer(ClassLoader $loader)
114
+ {
115
+ return \Closure::bind(function () use ($loader) {
116
+ $loader->prefixLengthsPsr4 = ComposerStaticInitb56847a9532368130d0132145aed966e::$prefixLengthsPsr4;
117
+ $loader->prefixDirsPsr4 = ComposerStaticInitb56847a9532368130d0132145aed966e::$prefixDirsPsr4;
118
+ $loader->classMap = ComposerStaticInitb56847a9532368130d0132145aed966e::$classMap;
119
+
120
+ }, null, ClassLoader::class);
121
+ }
122
+ }
vendor/composer/installed.json ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "name": "defuse/php-encryption",
4
+ "version": "v2.2.1",
5
+ "version_normalized": "2.2.1.0",
6
+ "source": {
7
+ "type": "git",
8
+ "url": "https://github.com/defuse/php-encryption.git",
9
+ "reference": "0f407c43b953d571421e0020ba92082ed5fb7620"
10
+ },
11
+ "dist": {
12
+ "type": "zip",
13
+ "url": "https://api.github.com/repos/defuse/php-encryption/zipball/0f407c43b953d571421e0020ba92082ed5fb7620",
14
+ "reference": "0f407c43b953d571421e0020ba92082ed5fb7620",
15
+ "shasum": ""
16
+ },
17
+ "require": {
18
+ "ext-openssl": "*",
19
+ "paragonie/random_compat": ">= 2",
20
+ "php": ">=5.4.0"
21
+ },
22
+ "require-dev": {
23
+ "nikic/php-parser": "^2.0|^3.0|^4.0",
24
+ "phpunit/phpunit": "^4|^5"
25
+ },
26
+ "time": "2018-07-24T23:27:56+00:00",
27
+ "bin": [
28
+ "bin/generate-defuse-key"
29
+ ],
30
+ "type": "library",
31
+ "installation-source": "dist",
32
+ "autoload": {
33
+ "psr-4": {
34
+ "Defuse\\Crypto\\": "src"
35
+ }
36
+ },
37
+ "notification-url": "https://packagist.org/downloads/",
38
+ "license": [
39
+ "MIT"
40
+ ],
41
+ "authors": [
42
+ {
43
+ "name": "Taylor Hornby",
44
+ "email": "taylor@defuse.ca",
45
+ "homepage": "https://defuse.ca/"
46
+ },
47
+ {
48
+ "name": "Scott Arciszewski",
49
+ "email": "info@paragonie.com",
50
+ "homepage": "https://paragonie.com"
51
+ }
52
+ ],
53
+ "description": "Secure PHP Encryption Library",
54
+ "keywords": [
55
+ "aes",
56
+ "authenticated encryption",
57
+ "cipher",
58
+ "crypto",
59
+ "cryptography",
60
+ "encrypt",
61
+ "encryption",
62
+ "openssl",
63
+ "security",
64
+ "symmetric key cryptography"
65
+ ]
66
+ },
67
+ {
68
+ "name": "firebase/php-jwt",
69
+ "version": "v5.2.0",
70
+ "version_normalized": "5.2.0.0",
71
+ "source": {
72
+ "type": "git",
73
+ "url": "https://github.com/firebase/php-jwt.git",
74
+ "reference": "feb0e820b8436873675fd3aca04f3728eb2185cb"
75
+ },
76
+ "dist": {
77
+ "type": "zip",
78
+ "url": "https://api.github.com/repos/firebase/php-jwt/zipball/feb0e820b8436873675fd3aca04f3728eb2185cb",
79
+ "reference": "feb0e820b8436873675fd3aca04f3728eb2185cb",
80
+ "shasum": ""
81
+ },
82
+ "require": {
83
+ "php": ">=5.3.0"
84
+ },
85
+ "require-dev": {
86
+ "phpunit/phpunit": ">=4.8 <=9"
87
+ },
88
+ "time": "2020-03-25T18:49:23+00:00",
89
+ "type": "library",
90
+ "installation-source": "dist",
91
+ "autoload": {
92
+ "psr-4": {
93
+ "Firebase\\JWT\\": "src"
94
+ }
95
+ },
96
+ "notification-url": "https://packagist.org/downloads/",
97
+ "license": [
98
+ "BSD-3-Clause"
99
+ ],
100
+ "authors": [
101
+ {
102
+ "name": "Neuman Vong",
103
+ "email": "neuman+pear@twilio.com",
104
+ "role": "Developer"
105
+ },
106
+ {
107
+ "name": "Anant Narayanan",
108
+ "email": "anant@php.net",
109
+ "role": "Developer"
110
+ }
111
+ ],
112
+ "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
113
+ "homepage": "https://github.com/firebase/php-jwt",
114
+ "keywords": [
115
+ "jwt",
116
+ "php"
117
+ ]
118
+ },
119
+ {
120
+ "name": "paragonie/random_compat",
121
+ "version": "v9.99.99",
122
+ "version_normalized": "9.99.99.0",
123
+ "source": {
124
+ "type": "git",
125
+ "url": "https://github.com/paragonie/random_compat.git",
126
+ "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95"
127
+ },
128
+ "dist": {
129
+ "type": "zip",
130
+ "url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95",
131
+ "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95",
132
+ "shasum": ""
133
+ },
134
+ "require": {
135
+ "php": "^7"
136
+ },
137
+ "require-dev": {
138
+ "phpunit/phpunit": "4.*|5.*",
139
+ "vimeo/psalm": "^1"
140
+ },
141
+ "suggest": {
142
+ "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
143
+ },
144
+ "time": "2018-07-02T15:55:56+00:00",
145
+ "type": "library",
146
+ "installation-source": "dist",
147
+ "notification-url": "https://packagist.org/downloads/",
148
+ "license": [
149
+ "MIT"
150
+ ],
151
+ "authors": [
152
+ {
153
+ "name": "Paragon Initiative Enterprises",
154
+ "email": "security@paragonie.com",
155
+ "homepage": "https://paragonie.com"
156
+ }
157
+ ],
158
+ "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
159
+ "keywords": [
160
+ "csprng",
161
+ "polyfill",
162
+ "pseudorandom",
163
+ "random"
164
+ ]
165
+ }
166
+ ]
vendor/defuse/php-encryption/.gitignore ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ *~
2
+ /test/unit/File/big-generated-file
3
+ /composer.lock
4
+ /vendor
5
+ defuse-crypto.phar
6
+ defuse-crypto.phar.sig
7
+ composer.phar
8
+ box.phar
9
+ phpunit.phar
10
+ phpunit.phar.asc
11
+ test/unit/File/tmp
vendor/defuse/php-encryption/.php_cs ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $config = Symfony\CS\Config\Config::create()
4
+ ->level(Symfony\CS\FixerInterface::PSR2_LEVEL)
5
+ ->fixers([
6
+ 'blankline_after_open_tag',
7
+ 'empty_return',
8
+ 'extra_empty_lines',
9
+ 'function_typehint_space',
10
+ 'join_function',
11
+ 'method_argument_default_value',
12
+ 'multiline_array_trailing_comma',
13
+ 'no_blank_lines_after_class_opening',
14
+ 'no_empty_lines_after_phpdocs',
15
+ 'phpdoc_indent',
16
+ 'phpdoc_no_access',
17
+ 'phpdoc_no_empty_return',
18
+ 'phpdoc_no_package',
19
+ 'phpdoc_params',
20
+ 'phpdoc_scalar',
21
+ 'phpdoc_separation',
22
+ 'phpdoc_trim',
23
+ 'phpdoc_type_to_var',
24
+ 'phpdoc_types',
25
+ 'phpdoc_var_without_name',
26
+ 'remove_leading_slash_use',
27
+ 'remove_lines_between_uses',
28
+ 'short_bool_cast',
29
+ 'single_quote',
30
+ 'spaces_after_semicolon',
31
+ 'spaces_before_semicolon',
32
+ 'spaces_cast',
33
+ 'standardize_not_equal',
34
+ 'ternary_spaces',
35
+ 'trim_array_spaces',
36
+ 'unneeded_control_parentheses',
37
+ 'unused_use',
38
+ 'whitespacy_lines',
39
+ 'align_double_arrow',
40
+ 'concat_with_spaces',
41
+ 'logical_not_operators_with_successor_space',
42
+ 'multiline_spaces_before_semicolon',
43
+ 'newline_after_open_tag',
44
+ 'ordered_use',
45
+ 'php_unit_construct',
46
+ 'phpdoc_order',
47
+ 'short_array_syntax',
48
+ ]);
49
+
50
+ if (null === $input->getArgument('path')) {
51
+ $config
52
+ ->finder(
53
+ Symfony\CS\Finder\DefaultFinder::create()
54
+ ->in('src')
55
+ ->in('test')
56
+ ->exclude('vendor')
57
+ );
58
+ }
59
+
60
+ return $config;
vendor/defuse/php-encryption/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Taylor Hornby <https://defuse.ca> and Paragon Initiative
4
+ Enterprises <https://paragonie.com>.
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
7
+ this software and associated documentation files (the "Software"), to deal in
8
+ the Software without restriction, including without limitation the rights to
9
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
10
+ the Software, and to permit persons to whom the Software is furnished to do so,
11
+ subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
vendor/defuse/php-encryption/README.md ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ php-encryption
2
+ ===============
3
+
4
+ [![Build Status](https://travis-ci.org/defuse/php-encryption.svg?branch=master)](https://travis-ci.org/defuse/php-encryption)
5
+ [![codecov](https://codecov.io/gh/defuse/php-encryption/branch/master/graph/badge.svg)](https://codecov.io/gh/defuse/php-encryption)
6
+ [![Latest Stable Version](https://poser.pugx.org/defuse/php-encryption/v/stable)](https://packagist.org/packages/defuse/php-encryption)
7
+ [![Latest Unstable Version](https://poser.pugx.org/defuse/php-encryption/v/unstable)](https://packagist.org/packages/defuse/php-encryption)
8
+ [![License](https://poser.pugx.org/defuse/php-encryption/license)](https://packagist.org/packages/defuse/php-encryption)
9
+ [![Downloads](https://img.shields.io/packagist/dt/defuse/php-encryption.svg)](https://packagist.org/packages/defuse/php-encryption)
10
+
11
+ This is a library for encrypting data with a key or password in PHP. **It
12
+ requires PHP 5.6 or newer and OpenSSL 1.0.1 or newer.** The current version is
13
+ v2.2.1, which is expected to remain stable and supported by its authors with
14
+ security and bugfixes until at least January 1st, 2020.
15
+
16
+ The library is a joint effort between [Taylor Hornby](https://defuse.ca/) and
17
+ [Scott Arciszewski](https://paragonie.com/blog/author/scott-arcizewski) as well
18
+ as numerous open-source contributors.
19
+
20
+ What separates this library from other PHP encryption libraries is, firstly,
21
+ that it is secure. The authors used to encounter insecure PHP encryption code on
22
+ a daily basis, so they created this library to bring more security to the
23
+ ecosystem. Secondly, this library is "difficult to misuse." Like
24
+ [libsodium](https://github.com/jedisct1/libsodium), its API is designed to be
25
+ easy to use in a secure way and hard to use in an insecure way.
26
+
27
+
28
+ Dependencies
29
+ ------------
30
+
31
+ This library requires no special dependencies except for PHP 5.6 or newer with
32
+ the OpenSSL extensions (version 1.0.1 or later) enabled (this is the default).
33
+ It uses [random\_compat](https://github.com/paragonie/random_compat), which is
34
+ bundled in with this library so that your users will not need to follow any
35
+ special installation steps.
36
+
37
+ Getting Started
38
+ ----------------
39
+
40
+ Start with the [**Tutorial**](docs/Tutorial.md). You can find instructions for
41
+ obtaining this library's code securely in the [Installing and
42
+ Verifying](docs/InstallingAndVerifying.md) documentation.
43
+
44
+ After you've read the tutorial and got the code, refer to the formal
45
+ documentation for each of the classes this library provides:
46
+
47
+ - [Crypto](docs/classes/Crypto.md)
48
+ - [File](docs/classes/File.md)
49
+ - [Key](docs/classes/Key.md)
50
+ - [KeyProtectedByPassword](docs/classes/KeyProtectedByPassword.md)
51
+
52
+ If you encounter difficulties, see the [FAQ](docs/FAQ.md) answers. The fixes to
53
+ the most commonly-reported problems are explained there.
54
+
55
+ If you're a cryptographer and want to understand the nitty-gritty details of how
56
+ this library works, look at the [Cryptography Details](docs/CryptoDetails.md)
57
+ documentation.
58
+
59
+ If you're interested in contributing to this library, see the [Internal
60
+ Developer Documentation](docs/InternalDeveloperDocs.md).
61
+
62
+ Other Language Support
63
+ ----------------------
64
+
65
+ This library is intended for server-side PHP software that needs to encrypt data at rest.
66
+ If you are building software that needs to encrypt client-side, or building a system that
67
+ requires cross-platform encryption/decryption support, we strongly recommend using
68
+ [libsodium](https://download.libsodium.org/doc/bindings_for_other_languages) instead.
69
+
70
+ Examples
71
+ ---------
72
+
73
+ If the documentation is not enough for you to understand how to use this
74
+ library, then you can look at an example project that uses this library:
75
+
76
+ - [encutil](https://github.com/defuse/encutil)
77
+ - [fileencrypt](https://github.com/tsusanka/fileencrypt)
78
+
79
+ Security Audit Status
80
+ ---------------------
81
+
82
+ This code has not been subjected to a formal, paid, security audit. However, it
83
+ has received lots of review from members of the PHP security community, and the
84
+ authors are experienced with cryptography. In all likelihood, you are safer
85
+ using this library than almost any other encryption library for PHP.
86
+
87
+ If you use this library as a part of your business and would like to help fund
88
+ a formal audit, please [contact Taylor Hornby](https://defuse.ca/contact.htm).
89
+
90
+ Public Keys
91
+ ------------
92
+
93
+ The GnuPG public key used to sign releases is available in
94
+ [dist/signingkey.asc](https://github.com/defuse/php-encryption/raw/master/dist/signingkey.asc). Its fingerprint is:
95
+
96
+ ```
97
+ 2FA6 1D8D 99B9 2658 6BAC 3D53 385E E055 A129 1538
98
+ ```
99
+
100
+ You can verify it against Taylor Hornby's [contact
101
+ page](https://defuse.ca/contact.htm) and
102
+ [twitter](https://twitter.com/DefuseSec/status/723741424253059074).
vendor/defuse/php-encryption/bin/generate-defuse-key ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env php
2
+ <?php
3
+
4
+ use Defuse\Crypto\Key;
5
+
6
+ foreach ([__DIR__ . '/../../../autoload.php', __DIR__ . '/../vendor/autoload.php'] as $file) {
7
+ if (file_exists($file)) {
8
+ require $file;
9
+ break;
10
+ }
11
+ }
12
+
13
+ $key = Key::createNewRandomKey();
14
+ echo $key->saveToAsciiSafeString(), "\n";
vendor/defuse/php-encryption/composer.json ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "defuse/php-encryption",
3
+ "description": "Secure PHP Encryption Library",
4
+ "license": "MIT",
5
+ "keywords": ["security", "encryption", "AES", "openssl", "cipher", "cryptography", "symmetric key cryptography", "crypto", "encrypt", "authenticated encryption"],
6
+ "authors": [
7
+ {
8
+ "name": "Taylor Hornby",
9
+ "email": "taylor@defuse.ca",
10
+ "homepage": "https://defuse.ca/"
11
+ },
12
+ {
13
+ "name": "Scott Arciszewski",
14
+ "email": "info@paragonie.com",
15
+ "homepage": "https://paragonie.com"
16
+ }
17
+ ],
18
+ "autoload": {
19
+ "psr-4": {
20
+ "Defuse\\Crypto\\": "src"
21
+ }
22
+ },
23
+ "require": {
24
+ "paragonie/random_compat": ">= 2",
25
+ "ext-openssl": "*",
26
+ "php": ">=5.4.0"
27
+ },
28
+ "require-dev": {
29
+ "phpunit/phpunit": "^4|^5",
30
+ "nikic/php-parser": "^2.0|^3.0|^4.0"
31
+ },
32
+ "bin": [
33
+ "bin/generate-defuse-key"
34
+ ]
35
+ }
vendor/defuse/php-encryption/dist/Makefile ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This builds defuse-crypto.phar. To run this Makefile, `box` and `composer`
2
+ # must be installed and in your $PATH. Run it from inside the dist/ directory.
3
+
4
+ box := $(shell which box)
5
+ composer := "composer"
6
+
7
+ .PHONY: all
8
+ all: build-phar
9
+
10
+ .PHONY: sign-phar
11
+ sign-phar:
12
+ gpg -u 7B4B2D98 --armor --output defuse-crypto.phar.sig --detach-sig defuse-crypto.phar
13
+
14
+ # ensure we run in clean tree. export git tree and run there.
15
+ .PHONY: build-phar
16
+ build-phar:
17
+ @echo "Creating .phar from revision $(shell git rev-parse HEAD)."
18
+ rm -rf worktree
19
+ install -d worktree
20
+ (cd $(CURDIR)/..; git archive HEAD) | tar -x -C worktree
21
+ $(MAKE) -f $(CURDIR)/Makefile -C worktree defuse-crypto.phar
22
+ mv worktree/*.phar .
23
+ rm -rf worktree
24
+
25
+ .PHONY: clean
26
+ clean:
27
+ rm -vf defuse-crypto.phar defuse-crypto.phar.sig
28
+
29
+ # Inside workdir/:
30
+
31
+ defuse-crypto.phar: dist/box.json composer.lock
32
+ cp dist/box.json .
33
+ php -d phar.readonly=0 $(box) build -c box.json -v
34
+
35
+ composer.lock:
36
+ $(composer) install --no-dev
37
+
vendor/defuse/php-encryption/dist/box.json ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "chmod": "0755",
3
+ "finder": [
4
+ {
5
+ "in": "src",
6
+ "name": "*.php"
7
+ },
8
+ {
9
+ "in": "vendor/composer",
10
+ "name": "*.php"
11
+ },
12
+ {
13
+ "in": "vendor/paragonie",
14
+ "name": "*.php",
15
+ "exclude": "other"
16
+ }
17
+ ],
18
+ "compactors": [
19
+ "Herrera\\Box\\Compactor\\Php"
20
+ ],
21
+ "main": "vendor/autoload.php",
22
+ "output": "defuse-crypto.phar",
23
+ "shebang": false,
24
+ "stub": true
25
+ }
vendor/defuse/php-encryption/dist/signingkey.asc ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -----BEGIN PGP PUBLIC KEY BLOCK-----
2
+ Version: GnuPG v2
3
+
4
+ mQINBFarvO4BEACdQBaLt6SUBx1cB5liUu1qo+YwVLh9bxTregQtmEREMdTVqXYt
5
+ e5b79uL4pQp2GlKHcEyRURS+6rIIruM0oh9ZYGTJYPAkCDzJxaU2awZeFbfBvpCm
6
+ iF66/O4ZJI4mlT8dFKmxBJxDhfeOR2UmmhDiEsJK9FxBKUzvo/dWrX2pBzf8Y122
7
+ iIaVraSo+tymaf7vriaIf/NnSKhDw8dtQYGM4NMrxxsPTfbCF8XiboDgTkoD2A+6
8
+ NpOJYxA4Veedsf2TP9YLhljH4m5yYlfjjqBzbBCPWuE6Hhy5Xze9mncgDr7LKenm
9
+ Ctf2NxW6y4O3RCI+9eLlBfFWB+KuGV87/b5daetX7NNLbjID8z2rqEa+d6wu5xA5
10
+ Ta2uiVkAOEovr3XnkayZ9zth+Za7w7Ai0ln0N/LVMkM+Gu4z/pJv6HjmTGDM2wJb
11
+ fs+UOM0TFdg+N81Do67XT2M4o0MeHyUqsIiWpYa2Qf1PNmqTQNJnRk8uZZ9I96Nh
12
+ eCgNuCbhsQiYBMicox+xmuWAlGAfA06y0kCtmqGhiBGArdJlWvUqPqGiZ4Hln9z0
13
+ FJmXDOh0Q/FIPxcDg8mKRRbx+lOP389PLsPpj4b2B/4PEgfpCCOwuKpLotATZxC1
14
+ 9JwFk0Y/cvUUkq4a+nAJBNtBbtRJkEesuuUnRq6XexmnE3uUucDcV0XCSwARAQAB
15
+ tCBUYXlsb3IgSG9ybmJ5IDx0YXlsb3JAZGVmdXNlLmNhPokCPQQTAQgAJwUCVqu8
16
+ 7gIbAwUJB4TOAAULCQgHAgYVCAkKCwIEFgIDAQIeAQIXgAAKCRA4XuBVoSkVOJbx
17
+ EACG0F9blPMAsK05EWyNnnS4mw25zPfbaqqEvYbquAeM0nBpRDm7sRn2MNR0AL4g
18
+ 7XrtxE/4qYkdEl6f2wFCQeRhZgxE3w22llredzLme11Hic8hn4i7ysdIw0r9dMMR
19
+ kjgR5UcWpv8iU847czyK09PkKW2EaLRbX2qbA7rNU5qCFKeD4Sy4bBTteISeVsHo
20
+ Vr9o1/bRrMhgZ++ts8hYf0LmujIf5cxp+qcdKwCXSnS/gmmXaKRMCPv/Wdlq9bt6
21
+ LX9jZB9lXBdGxcBJeFOsTG+QRDiVjg3d6i3o3TAKV87ALBI4v2ADEYtN8lviHo3/
22
+ SovVKv6zrUsZHxhoGiLTiksNrYsKMmiMxdJCoOazmtUPnZ4UOtT8NdqMPoKvdoRz
23
+ f4rhZ+f5jSVD9OuX2PDmfyq21Rdiym7Vcgr+uTIFJ3ShRHjWb/ytCwoB2FeGY6+G
24
+ AKY58bTQvUIqEJvSov/+TAqZ4BfOuSdTLcHglV1OdUu2SFZvU2gmyVp0l5elGv5t
25
+ FyUlBJUkQT9MtvsdLOR7vQi8QapV+9LWpqwvaj9hyEJz848DQ2sdYTphUytFHv7H
26
+ k58DAtVhTrVjHyeefjiYtMl6vSAgTjy5LWAUpo5TfhdGrAi0Tdd/GD7amHoWoDy8
27
+ EKXKq2xPLo3JOdkWYQUi5NErzEskfsSzpCOgyDJmGetWK7kCDQRWq7zuARAAu7/i
28
+ cm8cjgLhHEX/bgfwOT2hLOLSjjve0O8YFSuJO9XqIHXqmfVOrqWtfG0Mh4bwlfqc
29
+ MAvBfF5NSSPfAE4ftBAQ1e5jEv8hJeqICpq3IHTFX4etBs49NfNkyveQl/amVTu1
30
+ +/O5J4CuIcsEf3y0Xuu38n7EB3SfMQCWLcOR1NyZoX3bI+CGRpOVVoFse3ljSWL4
31
+ LhLVl0WiEMXULsussEoN+c6x9KCyAi/jFOrxrTrFC//sZesKj6KucoqKGfwMWrrv
32
+ IeRT9Ga8Wn5MJnQu0aWg+zVVYqTedXZLNLODgQIInFnXO0seBXy15yDok1y5bkx2
33
+ sinKg4+mueYaGUpoUti0hM3J3yaC34i6Cwa8MQoLNw1JIS/oNtKjpMxyV10w8aoc
34
+ PHRK3n7UYp10mJHx7aM+lldSKvVS1NTQmI4vloNLwMp324H5ANDFAlRUz7mysVnu
35
+ DEEvigPSPxs5ZYENu/i7pCQC5qHfhrlBrQwTjhegr0pQPcumy2fO5SGC9l/5B7ev
36
+ bqQSZmDeWWoTvh2w2wl5/RWAsgZKx6rDtkCqYx7sSBY17uorrxP24LP4zhq7NxRV
37
+ nfdsLogbCFNVQ66u7qvq5zFccdFtg9h1HQWdS7wbnKSBGZoo5gl6js7GGtxfGbb0
38
+ oQ9kp6eciF4U92r6POhVgbRe4CfPo50nqgZBddkAEQEAAYkCJQQYAQgADwUCVqu8
39
+ 7gIbDAUJB4TOAAAKCRA4XuBVoSkVOFJ8D/9J8IJ4XWUU3FYIaHJ3XeSoxDmTi7d5
40
+ WmNdf1lmwz82MQjG4uw17oCbvQzmj4/a/CM1Ly4v0WwBhUf9aiNErD0ByHASFnuc
41
+ tlQBLVJdk0vRyD0fZakGg64qCA76hiySjMhlGHkQFyP2mDORc2GNu/OqFGm79pXT
42
+ ZUplXxd431E603/agM5xJrweutMMpP1nBFTSEMJvbMNzDVN8I1A1CH4zVmAVxOUk
43
+ sQ5L5rXW+KeXWyiMF24+l2CMnkQ2CxfHpkcpfPJs1Cbt+TIBSSofIqK8QJXrb/2f
44
+ Zpl/ftqW7Xe86rJFrB/Y/77LDWx10rqWEvfCqrBxrMj7ONAQfbKQF/IjAwDN17Wf
45
+ 1K74rqKnRu+KHCyNM89s1iDbQC9kzZfzYt4AEOvAH/ZQDMZffzPSbnfkBerExFpa
46
+ 93XMuiR66jiBsf9IXIQeydpJD4Ogl2sSUSxFEJxJ/bBSxPxC5w7/BVMA7Am1y8Zo
47
+ 3hrpqnX2PBzxG7L0FZ6fYkfR3p8JS7vI6nByBf2IDv8W32wn43olPf+u6uobHLvt
48
+ ttapOjwPAhPDalRuxs9U6WSg06QJkT/0F8TFUPWpsFmKTl+G4Ty7PHWsjeeNHJCL
49
+ 7/5RQboFY3k8Jy3/sIofABO6Un9LJivDuu9PxqA0IgvaS6Mja8JdCCk9Nyk4vHB7
50
+ IEgAL/CYqrk38w==
51
+ =lmD7
52
+ -----END PGP PUBLIC KEY BLOCK-----
vendor/defuse/php-encryption/docs/CryptoDetails.md ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Cryptography Details
2
+ =====================
3
+
4
+ Here is a high-level description of how this library works. Any discrepancy
5
+ between this documentation and the actual implementation will be considered
6
+ a security bug.
7
+
8
+ Let's start with the following definitions:
9
+
10
+ - HKDF-SHA256(*k*, *n*, *info*, *s*) is the key derivation function specified in
11
+ RFC 5869 (using the SHA256 hash function). The parameters are:
12
+ - *k*: The initial keying material.
13
+ - *n*: The number of output bytes.
14
+ - *info*: The info string.
15
+ - *s*: The salt.
16
+ - AES-256-CTR(*m*, *k*, *iv*) is AES-256 encryption in CTR mode. The parameters
17
+ are:
18
+ - *m*: An arbitrary-length (possibly zero-length) message.
19
+ - *k*: A 32-byte key.
20
+ - *iv*: A 16-byte initialization vector (nonce).
21
+ - PBKDF2-SHA256(*p*, *s*, *i*, *n*) is the password-based key derivation
22
+ function defined in RFC 2898 (using the SHA256 hash function). The parameters
23
+ are:
24
+ - *p*: The password string.
25
+ - *s*: The salt string.
26
+ - *i*: The iteration count.
27
+ - *n*: The output length in bytes.
28
+ - VERSION is the string `"\xDE\xF5\x02\x00"`.
29
+ - AUTHINFO is the string `"DefusePHP|V2|KeyForAuthentication"`.
30
+ - ENCRINFO is the string `"DefusePHP|V2|KeyForEncryption"`.
31
+
32
+ To encrypt a message *m* using a 32-byte key *k*, the following steps are taken:
33
+
34
+ 1. Generate a random 32-byte string *salt*.
35
+ 2. Derive the 32-byte authentication key *akey* = HKDF-SHA256(*k*, 32, AUTHINFO, *salt*).
36
+ 3. Derive the 32-byte encryption key *ekey* = HKDF-SHA256(*k*, 32, ENCRINFO, *salt*).
37
+ 4. Generate a random 16-byte initialization vector *iv*.
38
+ 5. Compute *c* = AES-256-CTR(*m*, *ekey*, *iv*).
39
+ 6. Combine *ctxt* = VERSION || *salt* || *iv* || *c*.
40
+ 7. Compute *h* = HMAC-SHA256(*ctxt*, *akey*).
41
+ 8. Output *ctxt* || *h*.
42
+
43
+ Decryption is roughly the reverse process (see the code for details, since the
44
+ security of the decryption routine is highly implementation-dependent).
45
+
46
+ For encryption using a password *p*, steps 1-3 above are replaced by:
47
+
48
+ 1. Generate a random 32-byte string *salt*.
49
+ 2. Compute *k* = PBKDF2-SHA256(SHA256(*p*), *salt*, 100000, 32).
50
+ 3. Derive the 32-byte authentication key *akey* = HKDF-SHA256(*k*, 32, AUTHINFO, *salt*)
51
+ 4. Derive the 32-byte encryption key *ekey* = HKDF-SHA256(*k*, 32, ENCRINFO, *salt*)
52
+
53
+ The remainder of the process is the same. Notice the reuse of the same *salt*
54
+ for PBKDF2-SHA256 and HKDF-SHA256. The prehashing of the password in step 2 is
55
+ done to prevent a [DoS attack using long
56
+ passwords](https://github.com/defuse/php-encryption/issues/230).
57
+
58
+ For `KeyProtectedByPassword`, the serialized key is encrypted according to the
59
+ password encryption defined above. However, the actual password used for
60
+ encryption is the SHA256 hash of the password the user provided. This is done in
61
+ order to provide domain separation between the message encryption in the user's
62
+ application and the internal key encryption done by this library. It fixes
63
+ a [key replacement chosen-protocol
64
+ attack](https://github.com/defuse/php-encryption/issues/240).
vendor/defuse/php-encryption/docs/FAQ.md ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Frequently Asked Questions
2
+ ===========================
3
+
4
+ How do I use this library to encrypt passwords?
5
+ ------------------------------------------------
6
+
7
+ Passwords should not be encrypted, they should be hashed with a *slow* password
8
+ hashing function that's designed to slow down password guessing attacks. See
9
+ [How to Safely Store Your Users' Passwords in
10
+ 2016](https://paragonie.com/blog/2016/02/how-safely-store-password-in-2016).
11
+
12
+ How do I give it the same key every time instead of a new random key?
13
+ ----------------------------------------------------------------------
14
+
15
+ A `Key` object can be saved to a string by calling its `saveToAsciiSafeString()`
16
+ method. You will have to save that string somewhere safe, and then load it back
17
+ into a `Key` object using `Key`'s `loadFromAsciiSafeString` static method.
18
+
19
+ Where you store the string depends on your application. For example if you are
20
+ using `KeyProtectedByPassword` to encrypt files with a user's login password,
21
+ then you should not store the `Key` at all. If you are protecting sensitive data
22
+ on a server that may be compromised, then you should store it in a hardware
23
+ security module. When in doubt, consult a security expert.
24
+
25
+ Why is an EnvironmentIsBrokenException getting thrown?
26
+ -------------------------------------------------------
27
+
28
+ Either you've encountered a bug in this library, or your system doesn't support
29
+ the use of this library. For example, if your system does not have a secure
30
+ random number generator, this library will refuse to run, by throwing that
31
+ exception, instead of falling back to an insecure random number generator.
32
+
33
+ Why am I getting a BadFormatException when loading a Key from a string?
34
+ ------------------------------------------------------------------------
35
+
36
+ If you're getting this exception, then the string you're giving to
37
+ `loadFromAsciiSafeString()` is *not* the same as the string you got from
38
+ `saveToAsciiSafeString()`. Perhaps your database column isn't wide enough and
39
+ it's truncating the string as you insert it?
40
+
41
+ Does encrypting hide the length of the plaintext?
42
+ --------------------------------------------------
43
+
44
+ Encryption does not, and is not intended to, hide the length of the data being
45
+ encrypted. For example, it is not safe to encrypt a field in which only a small
46
+ number of different-length values are possible (e.g. "male" or "female") since
47
+ it would be possible to tell what the plaintext is by looking at the length of
48
+ the ciphertext. In order to do this safely, it is your responsibility to, before
49
+ encrypting, pad the data out to the length of the longest string that will ever
50
+ be encrypted. This way, all plaintexts are the same length, and no information
51
+ about the plaintext can be gleaned from the length of the ciphertext.
vendor/defuse/php-encryption/docs/InstallingAndVerifying.md ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Getting The Code
2
+ =================
3
+
4
+ There are two ways to use this library in your applications. You can either:
5
+
6
+ 1. Use [Composer](https://getcomposer.org/), or
7
+ 2. `require_once` a single `.phar` file in your application.
8
+
9
+ If you are not using either option (for example, because you're using Git submodules), you may need to write your own autoloader ([example](https://gist.github.com/paragonie-scott/949daee819bb7f19c50e5e103170b400)).
10
+
11
+ Option 1: Using Composer
12
+ -------------------------
13
+
14
+ Run this inside the directory of your composer-enabled project:
15
+
16
+ ```sh
17
+ composer require defuse/php-encryption
18
+ ```
19
+
20
+ Unfortunately, composer doesn't provide a way for you to verify that the code
21
+ you're getting was signed by this library's authors. If you want a more secure
22
+ option, use the `.phar` method described below.
23
+
24
+ Option 2: Including a PHAR
25
+ ----------------------------
26
+
27
+ The `.phar` option lets you include this library into your project simply by
28
+ calling `require_once` on a single file. Download `defuse-crypto.phar` and
29
+ `defuse-crypto.phar.sig` from this project's
30
+ [releases](https://github.com/defuse/php-encryption/releases) page.
31
+
32
+ You should verify the integrity of the `.phar`. The `defuse-crypto.phar.sig`
33
+ contains the signature of `defuse-crypto.phar`. It is signed with Taylor
34
+ Hornby's PGP key. You can find Taylor's public key in `dist/signingkey.asc`. You
35
+ can verify the public key's fingerprint against the Taylor Hornby's [contact
36
+ page](https://defuse.ca/contact.htm) and
37
+ [twitter](https://twitter.com/DefuseSec/status/723741424253059074).
38
+
39
+ Once you have verified the signature, it is safe to use the `.phar`. Place it
40
+ somewhere in your file system, e.g. `/var/www/lib/defuse-crypto.phar`, and then
41
+ pass that path to `require_once`.
42
+
43
+ ```php
44
+ <?php
45
+
46
+ require_once('/var/www/lib/defuse-crypto.phar');
47
+
48
+ // ... the Crypto, File, Key, and KeyProtectedByPassword classes are now
49
+ // available ...
50
+
51
+ // ...
52
+ ```
53
+
vendor/defuse/php-encryption/docs/InternalDeveloperDocs.md ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Information for the Developers of php-encryption
2
+ =================================================
3
+
4
+ Status
5
+ -------
6
+
7
+ This library is currently frozen under a long-term support release. We do not
8
+ plan to add any new features. We will maintain the library by fixing any bugs
9
+ that are reported, or security vulnerabilities that are found.
10
+
11
+ Development Environment
12
+ ------------------------
13
+
14
+ Development is done on Linux. To run the tests, you will need to have the
15
+ following tools installed:
16
+
17
+ - `php` (with OpenSSL enabled, if you're compiling from source).
18
+ - `gpg`
19
+ - `composer`
20
+
21
+ Running the Tests
22
+ ------------------
23
+
24
+ First do `composer install` and then you can run the tests by running
25
+ `./test.sh`. This will download a PHPUnit PHAR, verify its cryptographic
26
+ signatures, and then use it to run the tests in `test/unit`.
27
+
28
+ Getting and Using Psalm
29
+ -----------------------
30
+
31
+ [Psalm](https://github.com/vimeo/psalm) is a static analysis suite for PHP projects.
32
+ We use Psalm to ensure type safety throughout our library.
33
+
34
+ To install Psalm, you just need to run one command:
35
+
36
+ composer require --dev "vimeo/psalm:dev-master"
37
+
38
+ To verify that your code changes are still strictly type-safe, run the following
39
+ command:
40
+
41
+ vendor/bin/psalm
42
+
43
+ Reporting Bugs
44
+ ---------------
45
+
46
+ Please report bugs, even critical security vulnerabilities, by opening an issue
47
+ on GitHub. We recommend disclosing security vulnerabilities found in this
48
+ library *publicly* as soon as possible.
49
+
50
+ Philosophy
51
+ -----------
52
+
53
+ This library is developed around several core values:
54
+
55
+ - Rule #1: Security is prioritized over everything else.
56
+
57
+ > Whenever there is a conflict between security and some other property,
58
+ > security will be favored. For example, the library has runtime tests,
59
+ > which make it slower, but will hopefully stop it from encrypting stuff
60
+ > if the platform it's running on is broken.
61
+
62
+ - Rule #2: It should be difficult to misuse the library.
63
+
64
+ > We assume the developers using this library have no experience with
65
+ > cryptography. We only assume that they know that the "key" is something
66
+ > you need to encrypt and decrypt the messages, and that it must be kept
67
+ > secret. Whenever possible, the library should refuse to encrypt or decrypt
68
+ > messages when it is not being used correctly.
69
+
70
+ - Rule #3: The library aims only to be compatible with itself.
71
+
72
+ > Other PHP encryption libraries try to support every possible type of
73
+ > encryption, even the insecure ones (e.g. ECB mode). Because there are so
74
+ > many options, inexperienced developers must decide whether to use "CBC
75
+ > mode" or "ECB mode" when both are meaningless terms to them. This
76
+ > inevitably leads to vulnerabilities.
77
+
78
+ > This library will only support one secure mode. A developer using this
79
+ > library will call "encrypt" and "decrypt" methods without worrying about
80
+ > how they are implemented.
81
+
82
+ - Rule #4: The library should require no special installation.
83
+
84
+ > Some PHP encryption libraries, like libsodium-php, are not straightforward
85
+ > to install and cannot packaged with "just download and extract"
86
+ > applications. This library will always be just a handful of PHP files that
87
+ > you can copy to your source tree and require().
88
+
89
+ Publishing Releases
90
+ --------------------
91
+
92
+ To make a release, you will need to install [composer](https://getcomposer.org/)
93
+ and [box](https://github.com/box-project/box2) on your system. They will need to
94
+ be available in your `$PATH` so that running the commands `composer` and `box`
95
+ in your terminal run them, respectively. You will also need the private key for
96
+ signing (ID: 7B4B2D98) available.
97
+
98
+ Once you have those tools installed and the key available follow these steps:
99
+
100
+ **Remember to set the version number in `composer.json`!**
101
+
102
+ Make a fresh clone of the repository:
103
+
104
+ ```
105
+ git clone <url>
106
+ ```
107
+
108
+ Check out the branch you want to release:
109
+
110
+ ```
111
+ git checkout <branchname>
112
+ ```
113
+
114
+ Check that the version number in composer.json is correct:
115
+
116
+ ```
117
+ cat composer.json
118
+ ```
119
+
120
+ Check that the version number and support lifetime in README.md are correct:
121
+
122
+ ```
123
+ cat README.md
124
+ ```
125
+
126
+ Run the tests:
127
+
128
+ ```
129
+ composer install
130
+ ./test.sh
131
+ ```
132
+
133
+ Generate the `.phar`:
134
+
135
+ ```
136
+ cd dist
137
+ make build-phar
138
+ ```
139
+
140
+ Test the `.phar`:
141
+
142
+ ```
143
+ cd ../
144
+ ./test.sh dist/defuse-crypto.phar
145
+ ```
146
+
147
+ Sign the `.phar`:
148
+
149
+ ```
150
+ cd dist
151
+ make sign-phar
152
+ ```
153
+
154
+ Tag the release:
155
+
156
+ ```
157
+ git -c user.signingkey=7B4B2D98 tag -s "<TAG NAME>" -m "<TAG MESSAGE>"
158
+ ```
159
+
160
+ `<TAG NAME>` should be in the format `v2.0.0` and `<TAG MESSAGE>` should look
161
+ like "Release of v2.0.0."
162
+
163
+ Push the tag to github, then use the
164
+ [releases](https://github.com/defuse/php-encryption/releases) page to draft
165
+ a new release for that tag. Upload the `.phar` and the `.phar.sig` file to be
166
+ included as part of that release.
vendor/defuse/php-encryption/docs/Tutorial.md ADDED
@@ -0,0 +1,314 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Tutorial
2
+ =========
3
+
4
+ Hello! If you're reading this file, it's because you want to add encryption to
5
+ one of your PHP projects. My job, as the person writing this documentation, is
6
+ to help you make sure you're doing the right thing and then show you how to use
7
+ this library to do it. To help me help you, please read the documentation
8
+ *carefully* and *deliberately*.
9
+
10
+ A Word of Caution
11
+ ------------------
12
+
13
+ Encryption is not magic dust you can sprinkle on a system to make it more
14
+ secure. The way encryption is integrated into a system's design needs to be
15
+ carefully thought out. Sometimes, encryption is the wrong thing to use. Other
16
+ times, encryption needs to be used in a very specific way in order for it to
17
+ work as intended. Even if you are sure of what you are doing, we strongly
18
+ recommend seeking advice from an expert.
19
+
20
+ The first step is to think about your application's threat model. Ask yourself
21
+ the following questions. Who will want to attack my application, and what will
22
+ they get out of it? Are they trying to steal some information? Trying to alter
23
+ or destroy some information? Or just trying to make the system go down so people
24
+ can't access it? Then ask yourself how encryption can help combat those threats.
25
+ If you're going to add encryption to your application, you should have a very
26
+ clear idea of exactly which kinds of attacks it's helping to secure your
27
+ application against. Once you have your threat model, think about what kinds of
28
+ attacks it *does not* cover, and whether or not you should improve your threat
29
+ model to include those attacks.
30
+
31
+ **This isn't for storing user login passwords:** The most common use of
32
+ cryptography in web applications is to protect the users' login passwords. If
33
+ you're trying to use this library to "encrypt" your users' passwords, you're in
34
+ the wrong place. Passwords shouldn't be *encrypted*, they should be *hashed*
35
+ with a slow computation-heavy function that makes password guessing attacks more
36
+ expensive. See [How to Safely Store Your Users' Passwords in
37
+ 2016](https://paragonie.com/blog/2016/02/how-safely-store-password-in-2016).
38
+
39
+ **This isn't for encrypting network communication:** Likewise, if you're trying
40
+ to encrypt messages sent between two parties over the Internet, you don't want
41
+ to be using this library. For that, set up a TLS connection between the two
42
+ points, or, if it's a chat app, use the [Signal
43
+ Protocol](https://whispersystems.org/blog/advanced-ratcheting/).
44
+
45
+ What this library provides is symmetric encryption for "data at rest." This
46
+ means it is not suitable for use in building protocols where "data is in motion"
47
+ (i.e. moving over a network) except in limited set of cases.
48
+
49
+ Please note that **encryption does not, and is not intended to, hide the
50
+ *length* of the data being encrypted.** For example, it is not safe to encrypt
51
+ a field in which only a small number of different-length values are possible
52
+ (e.g. "male" or "female") since it would be possible to tell what the plaintext
53
+ is by looking at the length of the ciphertext. In order to do this safely, it is
54
+ your responsibility to, before encrypting, pad the data out to the length of the
55
+ longest string that will ever be encrypted. This way, all plaintexts are the
56
+ same length, and no information about the plaintext can be gleaned from the
57
+ length of the ciphertext.
58
+
59
+ Getting the Code
60
+ -----------------
61
+
62
+ There are several different ways to obtain this library's code and to add it to
63
+ your project. Even if you've already cloned the code from GitHub, you should
64
+ take steps to verify the cryptographic signatures to make sure the code you got
65
+ was not intercepted and modified by an attacker.
66
+
67
+ Please head over to the [**Installing and
68
+ Verifying**](InstallingAndVerifying.md) documentation to get the code, and then
69
+ come back here to continue the tutorial.
70
+
71
+ Using the Library
72
+ ------------------
73
+
74
+ I'm going to assume you know what symmetric encryption is, and the difference
75
+ between symmetric and asymmetric encryption. If you don't, I recommend taking
76
+ [Dan Boneh's Cryptography I course](https://www.coursera.org/learn/crypto/) on
77
+ Coursera.
78
+
79
+ To give you a quick introduction to the library, I'm going to explain how it
80
+ would be used in two sterotypical scenarios. Hopefully, one of these sterotypes
81
+ is close enough to what you want to do that you'll be able to figure out what
82
+ needs to be different on your own.
83
+
84
+ ### Formal Documentation
85
+
86
+ While this tutorial should get you up and running fast, it's important to
87
+ understand how this library behaves. Please make sure to read the formal
88
+ documentation of all of the functions you're using, since there are some
89
+ important security warnings there.
90
+
91
+ The following classes are available for you to use:
92
+
93
+ - [Crypto](classes/Crypto.md): Encrypting and decrypting strings.
94
+ - [File](classes/File.md): Encrypting and decrypting files.
95
+ - [Key](classes/Key.md): Represents a secret encryption key.
96
+ - [KeyProtectedByPassword](classes/KeyProtectedByPassword.md): Represents
97
+ a secret encryption key that needs to be "unlocked" by a password before it
98
+ can be used.
99
+
100
+ ### Scenario #1: Keep data secret from the database administrator
101
+
102
+ In this scenario, our threat model is as follows. Alice is a server
103
+ administrator responsible for managing a trusted web server. Eve is a database
104
+ administrator responsible for managing a database server. Dave is a web
105
+ developer working on code that will eventually run on the trusted web server.
106
+
107
+ Let's say Alice and Dave trust each other, and Alice is going to host Dave's
108
+ application on her server. But both Alice and Dave don't trust Eve. They know
109
+ Eve is a good database administrator, but she might have incentive to steal the
110
+ data from the database. They want to keep some of the web application's data
111
+ secret from Eve.
112
+
113
+ In order to do that, Alice will use the included `generate-defuse-key` script
114
+ which generates a random encryption key and prints it to standard output:
115
+
116
+ ```sh
117
+ $ composer require defuse/php-encryption
118
+ $ vendor/bin/generate-defuse-key
119
+ ```
120
+
121
+ Alice will run this script once and save the output to a configuration file, say
122
+ in `/etc/daveapp-secret-key.txt` and set the file permissions so that only the
123
+ user that the website PHP scripts run as can access it.
124
+
125
+ Dave will write his code to load the key from the configuration file:
126
+
127
+ ```php
128
+ <?php
129
+ use Defuse\Crypto\Key;
130
+
131
+ function loadEncryptionKeyFromConfig()
132
+ {
133
+ $keyAscii = // ... load the contents of /etc/daveapp-secret-key.txt
134
+ return Key::loadFromAsciiSafeString($keyAscii);
135
+ }
136
+ ```
137
+
138
+ Then, whenever Dave wants to save a secret value to the database, he will first
139
+ encrypt it:
140
+
141
+ ```php
142
+ <?php
143
+ use Defuse\Crypto\Crypto;
144
+
145
+ // ...
146
+ $key = loadEncryptionKeyFromConfig();
147
+ // ...
148
+ $ciphertext = Crypto::encrypt($secret_data, $key);
149
+ // ... save $ciphertext into the database ...
150
+ ```
151
+
152
+ Whenever Dave wants to get the value back from the database, he must decrypt it
153
+ using the same key:
154
+
155
+ ```php
156
+ <?php
157
+ use Defuse\Crypto\Crypto;
158
+
159
+ // ...
160
+ $key = loadEncryptionKeyFromConfig();
161
+ // ...
162
+ $ciphertext = // ... load $ciphertext from the database
163
+ try {
164
+ $secret_data = Crypto::decrypt($ciphertext, $key);
165
+ } catch (\Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
166
+ // An attack! Either the wrong key was loaded, or the ciphertext has
167
+ // changed since it was created -- either corrupted in the database or
168
+ // intentionally modified by Eve trying to carry out an attack.
169
+
170
+ // ... handle this case in a way that's suitable to your application ...
171
+ }
172
+ ```
173
+
174
+ Note that if anyone ever steals the key from Alice's server, they can decrypt
175
+ all of the ciphertexts that are stored in the database. As part of our threat
176
+ model, we are assuming Alice's server administration skills and Dave's secure
177
+ coding skills are good enough to stop Eve from being able to steal the key.
178
+ Under those assumptions, this solution will prevent Eve from seeing data that's
179
+ stored in the database.
180
+
181
+ However, notice that our threat model says nothing about what could happen if
182
+ Eve wants to *modify* the data. With this solution, Eve will not be able to
183
+ alter any individual ciphertext (because each ciphertext has its own
184
+ cryptographic integrity check), but Eve *will* be able to swap ciphertexts for
185
+ one another, and revert ciphertexts to what they used to be at previous times.
186
+ If we needed to defend against such attacks, we would have to re-design our
187
+ threat model and come up with a different solution.
188
+
189
+ ### Scenario #2: Encrypting account data with the user's login password
190
+
191
+ This scenario is like Scenario 1, but subtly different. The threat model is as
192
+ follows. We have Alice, a server administrator, and Dave, the developer. Alice
193
+ and Dave trust each other, and Alice wants to host Dave's web application,
194
+ including its database, on her server. Alice is worried about her server getting
195
+ hacked. The application will store the users' credit card numbers, and Alice
196
+ wants to protect them in case the server gets hacked.
197
+
198
+ We can model the situation like this: after the server gets hacked, the attacker
199
+ will have read and write access to all data on it until the attack is detected
200
+ and Alice rebuilds the server. We'll call the time the attacker has access to
201
+ the server the *exposure window.* One idea to minimize loss is to encrypt the
202
+ users' credit card numbers using a key made from their login password. Then, as
203
+ long as the users all have strong passwords, and they are never logged in during
204
+ the exposure window, their credit cards will be protected from the attacker.
205
+
206
+ To implement this, Dave will use the `KeyProtectedByPassword` class. When a new
207
+ user account is created, Dave will save a new key to their account, one that's
208
+ protected by their login password:
209
+
210
+ ```php
211
+ <?php
212
+ use Defuse\Crypto\KeyProtectedByPassword;
213
+
214
+ function CreateUserAccount($username, $password)
215
+ {
216
+ // ... other user account creation stuff, including password hashing
217
+
218
+ $protected_key = KeyProtectedByPassword::createRandomPasswordProtectedKey($password);
219
+ $protected_key_encoded = $protected_key->saveToAsciiSafeString();
220
+ // ... save $protected_key_encoded into the user's account record
221
+ }
222
+ ```
223
+
224
+ **WARNING:** Because of the way `KeyProtectedByPassword` is implemented, knowing
225
+ `SHA256($password)` is enough to decrypt a `KeyProtectedByPassword`. To be
226
+ secure, your application MUST NOT EVER compute `SHA256($password)` and use or
227
+ store it for any reason. You must also make sure that other libraries your
228
+ application is using don't compute it either.
229
+
230
+ Then, when the user logs in, Dave's code will load the protected key from the
231
+ user's account record, unlock it to get a `Key` object, and save the `Key`
232
+ object somewhere safe (like temporary memory-backed session storage). Note that
233
+ wherever Dave's code saves the key, it must be destroyed once the user logs out,
234
+ or else the attacker might be able to find users' keys even if they were never
235
+ logged in during the attack.
236
+
237
+ ```php
238
+ <?php
239
+ use Defuse\Crypto\KeyProtectedByPassword;
240
+
241
+ // ... authenticate the user using a good password hashing scheme
242
+ // keep the user's password in $password
243
+
244
+ $protected_key_encoded = // ... load it from the user's account record
245
+ $protected_key = KeyProtectedByPassword::loadFromAsciiSafeString($protected_key_encoded);
246
+ $user_key = $protected_key->unlockKey($password);
247
+ $user_key_encoded = $user_key->saveToAsciiSafeString();
248
+ // ... save $user_key_encoded in the session
249
+ ```
250
+
251
+ ```php
252
+ <?php
253
+ // ... when the user is logging out ...
254
+ // ... securely wipe the saved $user_key_encoded from the system ...
255
+ ```
256
+
257
+ When a user adds their credit card number, Dave's code will get the key from the
258
+ session and use it to encrypt the credit card number:
259
+
260
+ ```php
261
+ <?php
262
+ use Defuse\Crypto\Crypto;
263
+ use Defuse\Crypto\Key;
264
+
265
+ // ...
266
+
267
+ $user_key_encoded = // ... get it out of the session ...
268
+ $user_key = Key::loadFromAsciiSafeString($user_key_encoded);
269
+
270
+ // ...
271
+
272
+ $credit_card_number = // ... get credit card number from the user
273
+ $encrypted_card_number = Crypto::encrypt($credit_card_number, $user_key);
274
+ // ... save $encrypted_card_number in the database
275
+ ```
276
+
277
+ When the application needs to use the credit card number, it will decrypt it:
278
+
279
+ ```php
280
+ <?php
281
+ use Defuse\Crypto\Crypto;
282
+ use Defuse\Crypto\Key;
283
+
284
+ // ...
285
+
286
+ $user_key_encoded = // ... get it out of the session
287
+ $user_key = Key::loadFromAsciiSafeString($user_key_encoded);
288
+
289
+ // ...
290
+
291
+ $encrypted_card_number = // ... load it from the database ...
292
+ try {
293
+ $credit_card_number = Crypto::decrypt($encrypted_card_number, $user_key);
294
+ } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
295
+ // Either there's a bug in our code, we're trying to decrypt with the
296
+ // wrong key, or the encrypted credit card number was corrupted in the
297
+ // database.
298
+
299
+ // ... handle this case ...
300
+ }
301
+ ```
302
+
303
+ With all caveats carefully heeded, this solution limits credit card number
304
+ exposure in the case where Alice's server gets hacked for a short amount of
305
+ time. Remember to think about the attacks that *aren't* included in our threat
306
+ model. The attacker is still free to do all sorts of harmful things like
307
+ modifying the server's data which may go undetected if Alice doesn't have secure
308
+ backups to compare against.
309
+
310
+ Getting Help
311
+ -------------
312
+
313
+ If you're having difficulty using the library, see if your problem is already
314
+ solved by an answer in the [FAQ](FAQ.md).
vendor/defuse/php-encryption/docs/UpgradingFromV1.2.md ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Upgrading From Version 1.2
2
+ ===========================
3
+
4
+ With version 2.0.0 of this library came major changes to the ciphertext format,
5
+ algorithms used for encryption, and API.
6
+
7
+ In version 1.2, keys were represented by 16-byte string variables. In version
8
+ 2.0.0, keys are represented by objects, instances of the `Key` class. This
9
+ change was made in order to make it harder to misuse the API. For example, in
10
+ version 1.2, you could pass in *any* 16-byte string, but in version 2.0.0 you
11
+ need a `Key` object, which you can only get if you're "doing the right thing."
12
+
13
+ This means that for all of your old version 1.2 keys, you'll have to:
14
+
15
+ 1. Generate a new version 2.0.0 key.
16
+ 2. For all of the ciphertexts encrypted under the old key:
17
+ 1. Decrypt the ciphertext using the old version 1.2 key.
18
+ 2. Re-encrypt it using the new version 2.0.0 key.
19
+
20
+ Use the special `Crypto::legacyDecrypt()` method to decrypt the old ciphertexts
21
+ using the old key and then re-encrypt them using `Crypto::encrypt()` with the
22
+ new key. Your code will look something like the following. To avoid data loss,
23
+ securely back up your keys and data before running your upgrade code.
24
+
25
+ ```php
26
+ <?php
27
+
28
+ // ...
29
+
30
+ $legacy_ciphertext = // ... get the ciphertext you want to upgrade ...
31
+ $legacy_key = // ... get the key to decrypt this ciphertext ...
32
+
33
+ // Generate the new key that we'll re-encrypt the ciphertext with.
34
+ $new_key = Key::createNewRandomKey();
35
+ // ... save it somewhere ...
36
+
37
+ // Decrypt it.
38
+ try {
39
+ $plaintext = Crypto::legacyDecrypt($legacy_ciphertext, $legacy_key);
40
+ } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex)
41
+ {
42
+ // ... TODO: handle this case appropriately ...
43
+ }
44
+
45
+ // Re-encrypt it.
46
+ $new_ciphertext = Crypto::encrypt($plaintext, $new_key);
47
+
48
+ // ... replace the old $legacy_ciphertext with the new $new_ciphertext
49
+
50
+ // ...
51
+ ```
vendor/defuse/php-encryption/docs/classes/Crypto.md ADDED
@@ -0,0 +1,280 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Class: Defuse\Crypto\Crypto
2
+ ============================
3
+
4
+ The `Crypto` class provides encryption and decryption of strings either using
5
+ a secret key or secret password. For encryption and decryption of large files,
6
+ see the `File` class.
7
+
8
+ This code for this class is in `src/Crypto.php`.
9
+
10
+ Instance Methods
11
+ -----------------
12
+
13
+ This class has no instance methods.
14
+
15
+ Static Methods
16
+ ---------------
17
+
18
+ ### Crypto::encrypt($plaintext, Key $key, $raw\_binary = false)
19
+
20
+ **Description:**
21
+
22
+ Encrypts a plaintext string using a secret key.
23
+
24
+ **Parameters:**
25
+
26
+ 1. `$plaintext` is the string to encrypt.
27
+ 2. `$key` is an instance of `Key` containing the secret key for encryption.
28
+ 3. `$raw_binary` determines whether the output will be a byte string (true) or
29
+ hex encoded (false, the default).
30
+
31
+ **Return value:**
32
+
33
+ Returns a ciphertext string representing `$plaintext` encrypted with the key
34
+ `$key`. Knowledge of `$key` is required in order to decrypt the ciphertext and
35
+ recover the plaintext.
36
+
37
+ **Exceptions:**
38
+
39
+ - `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
40
+ the platform the code is running on cannot safely perform encryption for some
41
+ reason (e.g. it lacks a secure random number generator), or the runtime tests
42
+ detected a bug in this library.
43
+
44
+ - `\TypeError` is thrown if the parameters are not of the expected types.
45
+
46
+ **Side-effects and performance:**
47
+
48
+ This method runs a small and very fast set of self-tests if it is the very first
49
+ time one of the `Crypto` methods has been called. The performance overhead is
50
+ negligible and can be safely ignored in all applications.
51
+
52
+ **Cautions:**
53
+
54
+ The ciphertext returned by this method is decryptable by anyone with knowledge
55
+ of the key `$key`. It is the caller's responsibility to keep `$key` secret.
56
+ Where `$key` should be stored is up to the caller and depends on the threat
57
+ model the caller is designing their application under. If you are unsure where
58
+ to store `$key`, consult with a professional cryptographer to get help designing
59
+ your application.
60
+
61
+ Please note that **encryption does not, and is not intended to, hide the
62
+ *length* of the data being encrypted.** For example, it is not safe to encrypt
63
+ a field in which only a small number of different-length values are possible
64
+ (e.g. "male" or "female") since it would be possible to tell what the plaintext
65
+ is by looking at the length of the ciphertext. In order to do this safely, it is
66
+ your responsibility to, before encrypting, pad the data out to the length of the
67
+ longest string that will ever be encrypted. This way, all plaintexts are the
68
+ same length, and no information about the plaintext can be gleaned from the
69
+ length of the ciphertext.
70
+
71
+ ### Crypto::decrypt($ciphertext, Key $key, $raw\_binary = false)
72
+
73
+ **Description:**
74
+
75
+ Decrypts a ciphertext string using a secret key.
76
+
77
+ **Parameters:**
78
+
79
+ 1. `$ciphertext` is the ciphertext to be decrypted.
80
+ 2. `$key` is an instance of `Key` containing the secret key for decryption.
81
+ 3. `$raw_binary` must have the same value as the `$raw_binary` given to the
82
+ call to `encrypt()` that generated `$ciphertext`.
83
+
84
+ **Return value:**
85
+
86
+ If the decryption succeeds, returns a string containing the same value as the
87
+ string that was passed to `encrypt()` when `$ciphertext` was produced. Upon
88
+ a successful return, the caller can be assured that `$ciphertext` could not have
89
+ been produced except by someone with knowledge of `$key`.
90
+
91
+ **Exceptions:**
92
+
93
+ - `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
94
+ the platform the code is running on cannot safely perform encryption for some
95
+ reason (e.g. it lacks a secure random number generator), or the runtime tests
96
+ detected a bug in this library.
97
+
98
+ - `Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException` is thrown if
99
+ the `$key` is not the correct key for the given ciphertext, or if the
100
+ ciphertext has been modified (possibly maliciously). There is no way to
101
+ distinguish between these two cases.
102
+
103
+ - `\TypeError` is thrown if the parameters are not of the expected types.
104
+
105
+ **Side-effects and performance:**
106
+
107
+ This method runs a small and very fast set of self-tests if it is the very first
108
+ time one of the `Crypto` methods has been called. The performance overhead is
109
+ negligible and can be safely ignored in all applications.
110
+
111
+ **Cautions:**
112
+
113
+ It is impossible in principle to distinguish between the case where you attempt
114
+ to decrypt with the wrong key and the case where you attempt to decrypt
115
+ a modified (corrupted) ciphertext. It is up to the caller how to best deal with
116
+ this ambiguity, as it depends on the application this library is being used in.
117
+ If in doubt, consult with a professional cryptographer.
118
+
119
+ ### Crypto::encryptWithPassword($plaintext, $password, $raw\_binary = false)
120
+
121
+ **Description:**
122
+
123
+ Encrypts a plaintext string using a secret password.
124
+
125
+ **Parameters:**
126
+
127
+ 1. `$plaintext` is the string to encrypt.
128
+ 2. `$password` is a string containing the secret password used for encryption.
129
+ 3. `$raw_binary` determines whether the output will be a byte string (true) or
130
+ hex encoded (false, the default).
131
+
132
+ **Return value:**
133
+
134
+ Returns a ciphertext string representing `$plaintext` encrypted with a key
135
+ derived from `$password`. Knowledge of `$password` is required in order to
136
+ decrypt the ciphertext and recover the plaintext.
137
+
138
+ **Exceptions:**
139
+
140
+ - `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
141
+ the platform the code is running on cannot safely perform encryption for some
142
+ reason (e.g. it lacks a secure random number generator), or the runtime tests
143
+ detected a bug in this library.
144
+
145
+ - `\TypeError` is thrown if the parameters are not of the expected types.
146
+
147
+ **Side-effects and performance:**
148
+
149
+ This method is intentionally slow, using a lot of CPU resources for a fraction
150
+ of a second. It applies key stretching to the password in order to make password
151
+ guessing attacks more computationally expensive. If you need a faster way to
152
+ encrypt multiple ciphertexts under the same password, see the
153
+ `KeyProtectedByPassword` class.
154
+
155
+ This method runs a small and very fast set of self-tests if it is the very first
156
+ time one of the `Crypto` methods has been called. The performance overhead is
157
+ negligible and can be safely ignored in all applications.
158
+
159
+ **Cautions:**
160
+
161
+ PHP stack traces display (portions of) the arguments passed to methods on the
162
+ call stack. If an exception is thrown inside this call, and it is uncaught, the
163
+ value of `$password` may be leaked out to an attacker through the stack trace.
164
+ We recommend configuring PHP to never output stack traces (either displaying
165
+ them to the user or saving them to log files).
166
+
167
+ ### Crypto::decryptWithPassword($ciphertext, $password, $raw\_binary = false)
168
+
169
+ **Description:**
170
+
171
+ Decrypts a ciphertext string using a secret password.
172
+
173
+ **Parameters:**
174
+
175
+ 1. `$ciphertext` is the ciphertext to be decrypted.
176
+ 2. `$password` is a string containing the secret password used for decryption.
177
+ 3. `$raw_binary` must have the same value as the `$raw_binary` given to the
178
+ call to `encryptWithPassword()` that generated `$ciphertext`.
179
+
180
+ **Return value:**
181
+
182
+ If the decryption succeeds, returns a string containing the same value as the
183
+ string that was passed to `encryptWithPassword()` when `$ciphertext` was
184
+ produced. Upon a successful return, the caller can be assured that `$ciphertext`
185
+ could not have been produced except by someone with knowledge of `$password`.
186
+
187
+ **Exceptions:**
188
+
189
+ - `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
190
+ the platform the code is running on cannot safely perform encryption for some
191
+ reason (e.g. it lacks a secure random number generator), or the runtime tests
192
+ detected a bug in this library.
193
+
194
+ - `Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException` is thrown if
195
+ the `$password` is not the correct password for the given ciphertext, or if
196
+ the ciphertext has been modified (possibly maliciously). There is no way to
197
+ distinguish between these two cases.
198
+
199
+ - `\TypeError` is thrown if the parameters are not of the expected types.
200
+
201
+ **Side-effects:**
202
+
203
+ This method is intentionally slow. It applies key stretching to the password in
204
+ order to make password guessing attacks more computationally expensive. If you
205
+ need a faster way to encrypt multiple ciphertexts under the same password, see
206
+ the `KeyProtectedByPassword` class.
207
+
208
+ This method runs a small and very fast set of self-tests if it is the very first
209
+ time one of the `Crypto` methods has been called. The performance overhead is
210
+ negligible and can be safely ignored in all applications.
211
+
212
+ **Cautions:**
213
+
214
+ PHP stack traces display (portions of) the arguments passed to methods on the
215
+ call stack. If an exception is thrown inside this call, and it is uncaught, the
216
+ value of `$password` may be leaked out to an attacker through the stack trace.
217
+ We recommend configuring PHP to never output stack traces (either displaying
218
+ them to the user or saving them to log files).
219
+
220
+ It is impossible in principle to distinguish between the case where you attempt
221
+ to decrypt with the wrong password and the case where you attempt to decrypt
222
+ a modified (corrupted) ciphertext. It is up to the caller how to best deal with
223
+ this ambiguity, as it depends on the application this library is being used in.
224
+ If in doubt, consult with a professional cryptographer.
225
+
226
+ ### Crypto::legacyDecrypt($ciphertext, $key)
227
+
228
+ **Description:**
229
+
230
+ Decrypts a ciphertext produced by version 1 of this library so that the
231
+ plaintext can be re-encrypted into a version 2 ciphertext. See [Upgrading from
232
+ v1.2](../UpgradingFromV1.2.md).
233
+
234
+ **Parameters:**
235
+
236
+ 1. `$ciphertext` is a ciphertext produced by version 1.x of this library.
237
+ 2. `$key` is a 16-byte string (*not* a Key object) containing the key that was
238
+ used with version 1.x of this library to produce `$ciphertext`.
239
+
240
+ **Return value:**
241
+
242
+ If the decryption succeeds, returns the string that was encrypted to make
243
+ `$ciphertext` by version 1.x of this library. Upon a successful return, the
244
+ caller can be assured that `$ciphertext` could not have been produced except by
245
+ someone with knowledge of `$key`.
246
+
247
+ **Exceptions:**
248
+
249
+ - `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
250
+ the platform the code is running on cannot safely perform encryption for some
251
+ reason (e.g. it lacks a secure random number generator), or the runtime tests
252
+ detected a bug in this library.
253
+
254
+ - `Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException` is thrown if
255
+ the `$key` is not the correct key for the given ciphertext, or if the
256
+ ciphertext has been modified (possibly maliciously). There is no way to
257
+ distinguish between these two cases.
258
+
259
+ - `\TypeError` is thrown if the parameters are not of the expected types.
260
+
261
+ **Side-effects:**
262
+
263
+ This method runs a small and very fast set of self-tests if it is the very first
264
+ time one of the `Crypto` methods has been called. The performance overhead is
265
+ negligible and can be safely ignored in all applications.
266
+
267
+ **Cautions:**
268
+
269
+ PHP stack traces display (portions of) the arguments passed to methods on the
270
+ call stack. If an exception is thrown inside this call, and it is uncaught, the
271
+ value of `$key` may be leaked out to an attacker through the stack trace. We
272
+ recommend configuring PHP to never output stack traces (either displaying them
273
+ to the user or saving them to log files).
274
+
275
+ It is impossible in principle to distinguish between the case where you attempt
276
+ to decrypt with the wrong key and the case where you attempt to decrypt
277
+ a modified (corrupted) ciphertext. It is up to the caller how to best deal with
278
+ this ambiguity, as it depends on the application this library is being used in.
279
+ If in doubt, consult with a professional cryptographer.
280
+
vendor/defuse/php-encryption/docs/classes/File.md ADDED
@@ -0,0 +1,486 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Class: Defuse\Crypto\File
2
+ ==========================
3
+
4
+ Instance Methods
5
+ -----------------
6
+
7
+ This class has no instance methods.
8
+
9
+ Static Methods
10
+ ---------------
11
+
12
+ ### File::encryptFile($inputFilename, $outputFilename, Key $key)
13
+
14
+ **Description:**
15
+
16
+ Encrypts a file using a secret key.
17
+
18
+ **Parameters:**
19
+
20
+ 1. `$inputFilename` is the path to a file containing the plaintext to encrypt.
21
+ 2. `$outputFilename` is the path to save the ciphertext file.
22
+ 3. `$key` is an instance of `Key` containing the secret key for encryption.
23
+
24
+ **Behavior:**
25
+
26
+ Encrypts the contents of the input file, writing the result to the output file.
27
+ If the output file already exists, it is overwritten.
28
+
29
+ **Return value:**
30
+
31
+ Does not return a value.
32
+
33
+ **Exceptions:**
34
+
35
+ - `Defuse\Crypto\Exception\IOException` is thrown if there is an I/O error.
36
+
37
+ - `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
38
+ the platform the code is running on cannot safely perform encryption for some
39
+ reason (e.g. it lacks a secure random number generator), or the runtime tests
40
+ detected a bug in this library.
41
+
42
+ **Side-effects and performance:**
43
+
44
+ None.
45
+
46
+ **Cautions:**
47
+
48
+ The ciphertext output by this method is decryptable by anyone with knowledge of
49
+ the key `$key`. It is the caller's responsibility to keep `$key` secret. Where
50
+ `$key` should be stored is up to the caller and depends on the threat model the
51
+ caller is designing their application under. If you are unsure where to store
52
+ `$key`, consult with a professional cryptographer to get help designing your
53
+ application.
54
+
55
+ Please note that **encryption does not, and is not intended to, hide the
56
+ *length* of the data being encrypted.** For example, it is not safe to encrypt
57
+ a field in which only a small number of different-length values are possible
58
+ (e.g. "male" or "female") since it would be possible to tell what the plaintext
59
+ is by looking at the length of the ciphertext. In order to do this safely, it is
60
+ your responsibility to, before encrypting, pad the data out to the length of the
61
+ longest string that will ever be encrypted. This way, all plaintexts are the
62
+ same length, and no information about the plaintext can be gleaned from the
63
+ length of the ciphertext.
64
+
65
+ ### File::decryptFile($inputFilename, $outputFilename, Key $key)
66
+
67
+ **Description:**
68
+
69
+ Decrypts a file using a secret key.
70
+
71
+ **Parameters:**
72
+
73
+ 1. `$inputFilename` is the path to a file containing the ciphertext to decrypt.
74
+ 2. `$outputFilename` is the path to save the decrypted plaintext file.
75
+ 3. `$key` is an instance of `Key` containing the secret key for decryption.
76
+
77
+ **Behavior:**
78
+
79
+ Decrypts the contents of the input file, writing the result to the output file.
80
+ If the output file already exists, it is overwritten.
81
+
82
+ **Return value:**
83
+
84
+ Does not return a value.
85
+
86
+ **Exceptions:**
87
+
88
+ - `Defuse\Crypto\Exception\IOException` is thrown if there is an I/O error.
89
+
90
+ - `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
91
+ the platform the code is running on cannot safely perform encryption for some
92
+ reason (e.g. it lacks a secure random number generator), or the runtime tests
93
+ detected a bug in this library.
94
+
95
+ - `Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException` is thrown if
96
+ the `$key` is not the correct key for the given ciphertext, or if the
97
+ ciphertext has been modified (possibly maliciously). There is no way to
98
+ distinguish between these two cases.
99
+
100
+ **Side-effects and performance:**
101
+
102
+ The input ciphertext is processed in two passes. The first pass verifies the
103
+ integrity and the second pass performs the actual decryption of the file and
104
+ writing to the output file. This is done in a streaming manner so that only
105
+ a small part of the file is ever loaded into memory at a time.
106
+
107
+ **Cautions:**
108
+
109
+ Be aware that when `Defuse\Crypto\WrongKeyOrModifiedCiphertextException` is
110
+ thrown, some partial plaintext data may have been written to the output. Any
111
+ plaintext data that is output is guaranteed to be a prefix of the original
112
+ plaintext (i.e. at worst it was truncated). This can only happen if an attacker
113
+ modifies the input between the first pass (integrity check) and the second pass
114
+ (decryption) over the file.
115
+
116
+ It is impossible in principle to distinguish between the case where you attempt
117
+ to decrypt with the wrong key and the case where you attempt to decrypt
118
+ a modified (corrupted) ciphertext. It is up to the caller how to best deal with
119
+ this ambiguity, as it depends on the application this library is being used in.
120
+ If in doubt, consult with a professional cryptographer.
121
+
122
+ ### File::encryptFileWithPassword($inputFilename, $outputFilename, $password)
123
+
124
+ **Description:**
125
+
126
+ Encrypts a file with a password.
127
+
128
+ **Parameters:**
129
+
130
+ 1. `$inputFilename` is the path to a file containing the plaintext to encrypt.
131
+ 2. `$outputFilename` is the path to save the ciphertext file.
132
+ 3. `$password` is the password used for decryption.
133
+
134
+ **Behavior:**
135
+
136
+ Encrypts the contents of the input file, writing the result to the output file.
137
+ If the output file already exists, it is overwritten.
138
+
139
+ **Return value:**
140
+
141
+ Does not return a value.
142
+
143
+ **Exceptions:**
144
+
145
+ - `Defuse\Crypto\Exception\IOException` is thrown if there is an I/O error.
146
+
147
+ - `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
148
+ the platform the code is running on cannot safely perform encryption for some
149
+ reason (e.g. it lacks a secure random number generator), or the runtime tests
150
+ detected a bug in this library.
151
+
152
+ **Side-effects and performance:**
153
+
154
+ This method is intentionally slow, using a lot of CPU resources for a fraction
155
+ of a second. It applies key stretching to the password in order to make password
156
+ guessing attacks more computationally expensive. If you need a faster way to
157
+ encrypt multiple ciphertexts under the same password, see the
158
+ `KeyProtectedByPassword` class.
159
+
160
+ **Cautions:**
161
+
162
+ PHP stack traces display (portions of) the arguments passed to methods on the
163
+ call stack. If an exception is thrown inside this call, and it is uncaught, the
164
+ value of `$password` may be leaked out to an attacker through the stack trace.
165
+ We recommend configuring PHP to never output stack traces (either displaying
166
+ them to the user or saving them to log files).
167
+
168
+ Please note that **encryption does not, and is not intended to, hide the
169
+ *length* of the data being encrypted.** For example, it is not safe to encrypt
170
+ a field in which only a small number of different-length values are possible
171
+ (e.g. "male" or "female") since it would be possible to tell what the plaintext
172
+ is by looking at the length of the ciphertext. In order to do this safely, it is
173
+ your responsibility to, before encrypting, pad the data out to the length of the
174
+ longest string that will ever be encrypted. This way, all plaintexts are the
175
+ same length, and no information about the plaintext can be gleaned from the
176
+ length of the ciphertext.
177
+
178
+ ### File::decryptFileWithPassword($inputFilename, $outputFilename, $password)
179
+
180
+ **Description:**
181
+
182
+ Decrypts a file with a password.
183
+
184
+ **Parameters:**
185
+
186
+ 1. `$inputFilename` is the path to a file containing the ciphertext to decrypt.
187
+ 2. `$outputFilename` is the path to save the decrypted plaintext file.
188
+ 3. `$password` is the password used for decryption.
189
+
190
+ **Behavior:**
191
+
192
+ Decrypts the contents of the input file, writing the result to the output file.
193
+ If the output file already exists, it is overwritten.
194
+
195
+ **Return value:**
196
+
197
+ Does not return a value.
198
+
199
+ **Exceptions:**
200
+
201
+ - `Defuse\Crypto\Exception\IOException` is thrown if there is an I/O error.
202
+
203
+ - `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
204
+ the platform the code is running on cannot safely perform encryption for some
205
+ reason (e.g. it lacks a secure random number generator), or the runtime tests
206
+ detected a bug in this library.
207
+
208
+ - `Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException` is thrown if
209
+ the `$password` is not the correct key for the given ciphertext, or if the
210
+ ciphertext has been modified (possibly maliciously). There is no way to
211
+ distinguish between these two cases.
212
+
213
+ **Side-effects and performance:**
214
+
215
+ This method is intentionally slow, using a lot of CPU resources for a fraction
216
+ of a second. It applies key stretching to the password in order to make password
217
+ guessing attacks more computationally expensive. If you need a faster way to
218
+ encrypt multiple ciphertexts under the same password, see the
219
+ `KeyProtectedByPassword` class.
220
+
221
+ The input ciphertext is processed in two passes. The first pass verifies the
222
+ integrity and the second pass performs the actual decryption of the file and
223
+ writing to the output file. This is done in a streaming manner so that only
224
+ a small part of the file is ever loaded into memory at a time.
225
+
226
+ **Cautions:**
227
+
228
+ PHP stack traces display (portions of) the arguments passed to methods on the
229
+ call stack. If an exception is thrown inside this call, and it is uncaught, the
230
+ value of `$password` may be leaked out to an attacker through the stack trace.
231
+ We recommend configuring PHP to never output stack traces (either displaying
232
+ them to the user or saving them to log files).
233
+
234
+ Be aware that when `Defuse\Crypto\WrongKeyOrModifiedCiphertextException` is
235
+ thrown, some partial plaintext data may have been written to the output. Any
236
+ plaintext data that is output is guaranteed to be a prefix of the original
237
+ plaintext (i.e. at worst it was truncated). This can only happen if an attacker
238
+ modifies the input between the first pass (integrity check) and the second pass
239
+ (decryption) over the file.
240
+
241
+ It is impossible in principle to distinguish between the case where you attempt
242
+ to decrypt with the wrong password and the case where you attempt to decrypt
243
+ a modified (corrupted) ciphertext. It is up to the caller how to best deal with
244
+ this ambiguity, as it depends on the application this library is being used in.
245
+ If in doubt, consult with a professional cryptographer.
246
+
247
+ ### File::encryptResource($inputHandle, $outputHandle, Key $key)
248
+
249
+ **Description:**
250
+
251
+ Encrypts a resource (stream) with a secret key.
252
+
253
+ **Parameters:**
254
+
255
+ 1. `$inputHandle` is a handle to a resource (like a file pointer) containing the
256
+ plaintext to encrypt.
257
+ 2. `$outputHandle` is a handle to a resource (like a file pointer) that the
258
+ ciphertext will be written to.
259
+ 3. `$key` is an instance of `Key` containing the secret key for encryption.
260
+
261
+ **Behavior:**
262
+
263
+ Encrypts the data read from the input stream and writes it to the output stream.
264
+
265
+ **Return value:**
266
+
267
+ Does not return a value.
268
+
269
+ **Exceptions:**
270
+
271
+ - `Defuse\Crypto\Exception\IOException` is thrown if there is an I/O error.
272
+
273
+ - `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
274
+ the platform the code is running on cannot safely perform encryption for some
275
+ reason (e.g. it lacks a secure random number generator), or the runtime tests
276
+ detected a bug in this library.
277
+
278
+ **Side-effects and performance:**
279
+
280
+ None.
281
+
282
+ **Cautions:**
283
+
284
+ The ciphertext output by this method is decryptable by anyone with knowledge of
285
+ the key `$key`. It is the caller's responsibility to keep `$key` secret. Where
286
+ `$key` should be stored is up to the caller and depends on the threat model the
287
+ caller is designing their application under. If you are unsure where to store
288
+ `$key`, consult with a professional cryptographer to get help designing your
289
+ application.
290
+
291
+ Please note that **encryption does not, and is not intended to, hide the
292
+ *length* of the data being encrypted.** For example, it is not safe to encrypt
293
+ a field in which only a small number of different-length values are possible
294
+ (e.g. "male" or "female") since it would be possible to tell what the plaintext
295
+ is by looking at the length of the ciphertext. In order to do this safely, it is
296
+ your responsibility to, before encrypting, pad the data out to the length of the
297
+ longest string that will ever be encrypted. This way, all plaintexts are the
298
+ same length, and no information about the plaintext can be gleaned from the
299
+ length of the ciphertext.
300
+
301
+ ### File::decryptResource($inputHandle, $outputHandle, Key $key)
302
+
303
+ **Description:**
304
+
305
+ Decrypts a resource (stream) with a secret key.
306
+
307
+ **Parameters:**
308
+
309
+ 1. `$inputHandle` is a handle to a file-backed resource containing the
310
+ ciphertext to decrypt. It must be a file not a network stream or standard
311
+ input.
312
+ 2. `$outputHandle` is a handle to a resource (like a file pointer) that the
313
+ plaintext will be written to.
314
+ 3. `$key` is an instance of `Key` containing the secret key for decryption.
315
+
316
+ **Behavior:**
317
+
318
+ Decrypts the data read from the input stream and writes it to the output stream.
319
+
320
+ **Return value:**
321
+
322
+ Does not return a value.
323
+
324
+ **Exceptions:**
325
+
326
+ - `Defuse\Crypto\Exception\IOException` is thrown if there is an I/O error.
327
+
328
+ - `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
329
+ the platform the code is running on cannot safely perform encryption for some
330
+ reason (e.g. it lacks a secure random number generator), or the runtime tests
331
+ detected a bug in this library.
332
+
333
+ - `Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException` is thrown if
334
+ the `$key` is not the correct key for the given ciphertext, or if the
335
+ ciphertext has been modified (possibly maliciously). There is no way to
336
+ distinguish between these two cases.
337
+
338
+ **Side-effects and performance:**
339
+
340
+ The input ciphertext is processed in two passes. The first pass verifies the
341
+ integrity and the second pass performs the actual decryption of the file and
342
+ writing to the output file. This is done in a streaming manner so that only
343
+ a small part of the file is ever loaded into memory at a time.
344
+
345
+ **Cautions:**
346
+
347
+ Be aware that when `Defuse\Crypto\WrongKeyOrModifiedCiphertextException` is
348
+ thrown, some partial plaintext data may have been written to the output. Any
349
+ plaintext data that is output is guaranteed to be a prefix of the original
350
+ plaintext (i.e. at worst it was truncated). This can only happen if an attacker
351
+ modifies the input between the first pass (integrity check) and the second pass
352
+ (decryption) over the file.
353
+
354
+ It is impossible in principle to distinguish between the case where you attempt
355
+ to decrypt with the wrong key and the case where you attempt to decrypt
356
+ a modified (corrupted) ciphertext. It is up to the caller how to best deal with
357
+ this ambiguity, as it depends on the application this library is being used in.
358
+ If in doubt, consult with a professional cryptographer.
359
+
360
+ ### File::encryptResourceWithPassword($inputHandle, $outputHandle, $password)
361
+
362
+ **Description:**
363
+
364
+ Encrypts a resource (stream) with a password.
365
+
366
+ **Parameters:**
367
+
368
+ 1. `$inputHandle` is a handle to a resource (like a file pointer) containing the
369
+ plaintext to encrypt.
370
+ 2. `$outputHandle` is a handle to a resource (like a file pointer) that the
371
+ ciphertext will be written to.
372
+ 3. `$password` is the password used for encryption.
373
+
374
+ **Behavior:**
375
+
376
+ Encrypts the data read from the input stream and writes it to the output stream.
377
+
378
+ **Return value:**
379
+
380
+ Does not return a value.
381
+
382
+ **Exceptions:**
383
+
384
+ - `Defuse\Crypto\Exception\IOException` is thrown if there is an I/O error.
385
+
386
+ - `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
387
+ the platform the code is running on cannot safely perform encryption for some
388
+ reason (e.g. it lacks a secure random number generator), or the runtime tests
389
+ detected a bug in this library.
390
+
391
+ **Side-effects and performance:**
392
+
393
+ This method is intentionally slow, using a lot of CPU resources for a fraction
394
+ of a second. It applies key stretching to the password in order to make password
395
+ guessing attacks more computationally expensive. If you need a faster way to
396
+ encrypt multiple ciphertexts under the same password, see the
397
+ `KeyProtectedByPassword` class.
398
+
399
+ **Cautions:**
400
+
401
+ PHP stack traces display (portions of) the arguments passed to methods on the
402
+ call stack. If an exception is thrown inside this call, and it is uncaught, the
403
+ value of `$password` may be leaked out to an attacker through the stack trace.
404
+ We recommend configuring PHP to never output stack traces (either displaying
405
+ them to the user or saving them to log files).
406
+
407
+ Please note that **encryption does not, and is not intended to, hide the
408
+ *length* of the data being encrypted.** For example, it is not safe to encrypt
409
+ a field in which only a small number of different-length values are possible
410
+ (e.g. "male" or "female") since it would be possible to tell what the plaintext
411
+ is by looking at the length of the ciphertext. In order to do this safely, it is
412
+ your responsibility to, before encrypting, pad the data out to the length of the
413
+ longest string that will ever be encrypted. This way, all plaintexts are the
414
+ same length, and no information about the plaintext can be gleaned from the
415
+ length of the ciphertext.
416
+
417
+ ### File::decryptResourceWithPassword($inputHandle, $outputHandle, $password)
418
+
419
+ **Description:**
420
+
421
+ Decrypts a resource (stream) with a password.
422
+
423
+ **Parameters:**
424
+
425
+ 1. `$inputHandle` is a handle to a file-backed resource containing the
426
+ ciphertext to decrypt. It must be a file not a network stream or standard
427
+ input.
428
+ 2. `$outputHandle` is a handle to a resource (like a file pointer) that the
429
+ plaintext will be written to.
430
+ 3. `$password` is the password used for decryption.
431
+
432
+ **Behavior:**
433
+
434
+ Decrypts the data read from the input stream and writes it to the output stream.
435
+
436
+ **Return value:**
437
+
438
+ Does not return a value.
439
+
440
+ **Exceptions:**
441
+
442
+ - `Defuse\Crypto\Exception\IOException` is thrown if there is an I/O error.
443
+
444
+ - `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
445
+ the platform the code is running on cannot safely perform encryption for some
446
+ reason (e.g. it lacks a secure random number generator), or the runtime tests
447
+ detected a bug in this library.
448
+
449
+ - `Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException` is thrown if
450
+ the `$password` is not the correct key for the given ciphertext, or if the
451
+ ciphertext has been modified (possibly maliciously). There is no way to
452
+ distinguish between these two cases.
453
+
454
+ **Side-effects and performance:**
455
+
456
+ This method is intentionally slow, using a lot of CPU resources for a fraction
457
+ of a second. It applies key stretching to the password in order to make password
458
+ guessing attacks more computationally expensive. If you need a faster way to
459
+ encrypt multiple ciphertexts under the same password, see the
460
+ `KeyProtectedByPassword` class.
461
+
462
+ The input ciphertext is processed in two passes. The first pass verifies the
463
+ integrity and the second pass performs the actual decryption of the file and
464
+ writing to the output file. This is done in a streaming manner so that only
465
+ a small part of the file is ever loaded into memory at a time.
466
+
467
+ **Cautions:**
468
+
469
+ PHP stack traces display (portions of) the arguments passed to methods on the
470
+ call stack. If an exception is thrown inside this call, and it is uncaught, the
471
+ value of `$password` may be leaked out to an attacker through the stack trace.
472
+ We recommend configuring PHP to never output stack traces (either displaying
473
+ them to the user or saving them to log files).
474
+
475
+ Be aware that when `Defuse\Crypto\WrongKeyOrModifiedCiphertextException` is
476
+ thrown, some partial plaintext data may have been written to the output. Any
477
+ plaintext data that is output is guaranteed to be a prefix of the original
478
+ plaintext (i.e. at worst it was truncated). This can only happen if an attacker
479
+ modifies the input between the first pass (integrity check) and the second pass
480
+ (decryption) over the file.
481
+
482
+ It is impossible in principle to distinguish between the case where you attempt
483
+ to decrypt with the wrong password and the case where you attempt to decrypt
484
+ a modified (corrupted) ciphertext. It is up to the caller how to best deal with
485
+ this ambiguity, as it depends on the application this library is being used in.
486
+ If in doubt, consult with a professional cryptographer.
vendor/defuse/php-encryption/docs/classes/Key.md ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Class: Defuse\Crypto\Key
2
+ =========================
3
+
4
+ The `Key` class represents a secret key used for encrypting and decrypting. Once
5
+ you have a `Key` instance, you can use it with the `Crypto` class to encrypt and
6
+ decrypt strings and with the `File` class to encrypt and decrypt files.
7
+
8
+ Instance Methods
9
+ -----------------
10
+
11
+ ### saveToAsciiSafeString()
12
+
13
+ **Description:**
14
+
15
+ Saves the encryption key to a string of printable ASCII characters, which can be
16
+ loaded again into a `Key` instance using `Key::loadFromAsciiSafeString()`.
17
+
18
+ **Parameters:**
19
+
20
+ This method does not take any parameters.
21
+
22
+ **Return value:**
23
+
24
+ Returns a string of printable ASCII characters representing this `Key` instance,
25
+ which can be loaded back into an instance of `Key` using
26
+ `Key::loadFromAsciiSafeString()`.
27
+
28
+ **Exceptions:**
29
+
30
+ - `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
31
+ the platform the code is running on cannot safely perform encryption for some
32
+ reason (e.g. it lacks a secure random number generator), or the runtime tests
33
+ detected a bug in this library.
34
+
35
+ **Side-effects and performance:**
36
+
37
+ None.
38
+
39
+ **Cautions:**
40
+
41
+ This method currently returns a hexadecimal string. You should not rely on this
42
+ behavior. For example, it may be improved in the future to return a base64
43
+ string.
44
+
45
+ Static Methods
46
+ ---------------
47
+
48
+ ### Key::createNewRandomKey()
49
+
50
+ **Description:**
51
+
52
+ Generates a new random key and returns an instance of `Key`.
53
+
54
+ **Parameters:**
55
+
56
+ This method does not take any parameters.
57
+
58
+ **Return value:**
59
+
60
+ Returns an instance of `Key` containing a randomly-generated encryption key.
61
+
62
+ **Exceptions:**
63
+
64
+ - `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
65
+ the platform the code is running on cannot safely perform encryption for some
66
+ reason (e.g. it lacks a secure random number generator), or the runtime tests
67
+ detected a bug in this library.
68
+
69
+ **Side-effects and performance:**
70
+
71
+ None.
72
+
73
+ **Cautions:**
74
+
75
+ None.
76
+
77
+ ### Key::loadFromAsciiSafeString($saved\_key\_string, $do\_not\_trim = false)
78
+
79
+ **Description:**
80
+
81
+ Loads an instance of `Key` that was saved to a string by
82
+ `saveToAsciiSafeString()`.
83
+
84
+ By default, this function will call `Encoding::trimTrailingWhitespace()`
85
+ to remove trailing CR, LF, NUL, TAB, and SPACE characters, which are commonly
86
+ appended to files when working with text editors.
87
+
88
+ **Parameters:**
89
+
90
+ 1. `$saved_key_string` is the string returned from `saveToAsciiSafeString()`
91
+ when the original `Key` instance was saved.
92
+ 2. `$do_not_trim` should be set to `TRUE` if you do not wish for the library
93
+ to automatically strip trailing whitespace from the string.
94
+
95
+ **Return value:**
96
+
97
+ Returns an instance of `Key` representing the same encryption key as the one
98
+ that was represented by the `Key` instance that got saved into
99
+ `$saved_key_string` by a call to `saveToAsciiSafeString()`.
100
+
101
+ **Exceptions:**
102
+
103
+ - `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
104
+ the platform the code is running on cannot safely perform encryption for some
105
+ reason (e.g. it lacks a secure random number generator), or the runtime tests
106
+ detected a bug in this library.
107
+
108
+ - `Defuse\Crypto\Exception\BadFormatException` is thrown whenever
109
+ `$saved_key_string` does not represent a valid `Key` instance.
110
+
111
+ **Side-effects and performance:**
112
+
113
+ None.
114
+
115
+ **Cautions:**
116
+
117
+ None.
vendor/defuse/php-encryption/docs/classes/KeyProtectedByPassword.md ADDED
@@ -0,0 +1,259 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Class: Defuse\Crypto\KeyProtectedByPassword
2
+ ============================================
3
+
4
+ The `KeyProtectedByPassword` class represents a key that is "locked" with
5
+ a password. In order to obtain an instance of `Key` that you can use for
6
+ encrypting and decrypting, a `KeyProtectedByPassword` must first be "unlocked"
7
+ by providing the correct password.
8
+
9
+ `KeyProtectedByPassword` provides an alternative to using the
10
+ `encryptWithPassword()`, `decryptWithPassword()`, `encryptFileWithPassword()`,
11
+ and `decryptFileWithPassword()` methods with several advantages:
12
+
13
+ - The slow and computationally-expensive key stretching is run only once when
14
+ you unlock a `KeyProtectedByPassword` to obtain the `Key`.
15
+ - You do not have to keep the original password in memory to encrypt and decrypt
16
+ things. After you've obtained the `Key` from a `KeyProtectedByPassword`, the
17
+ password is no longer necessary.
18
+
19
+ Instance Methods
20
+ -----------------
21
+
22
+ ### saveToAsciiSafeString()
23
+
24
+ **Description:**
25
+
26
+ Saves the protected key to a string of printable ASCII characters, which can be
27
+ loaded again into a `KeyProtectedByPassword` instance using
28
+ `KeyProtectedByPassword::loadFromAsciiSafeString()`.
29
+
30
+ **Parameters:**
31
+
32
+ This method does not take any parameters.
33
+
34
+ **Return value:**
35
+
36
+ Returns a string of printable ASCII characters representing this
37
+ `KeyProtectedByPassword` instance, which can be loaded back into an instance of
38
+ `KeyProtectedByPassword` using
39
+ `KeyProtectedByPassword::loadFromAsciiSafeString()`.
40
+
41
+ **Exceptions:**
42
+
43
+ - `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
44
+ the platform the code is running on cannot safely perform encryption for some
45
+ reason (e.g. it lacks a secure random number generator), or the runtime tests
46
+ detected a bug in this library.
47
+
48
+ **Side-effects and performance:**
49
+
50
+ None.
51
+
52
+ **Cautions:**
53
+
54
+ This method currently returns a hexadecimal string. You should not rely on this
55
+ behavior. For example, it may be improved in the future to return a base64
56
+ string.
57
+
58
+ ### unlockKey($password)
59
+
60
+ **Description:**
61
+
62
+ Unlocks the password-protected key, obtaining a `Key` which can be used for
63
+ encryption and decryption.
64
+
65
+ **Parameters:**
66
+
67
+ 1. `$password` is the password required to unlock this `KeyProtectedByPassword`
68
+ to obtain the `Key`.
69
+
70
+ **Return value:**
71
+
72
+ If `$password` is the correct password, then this method returns an instance of
73
+ the `Key` class.
74
+
75
+ **Exceptions:**
76
+
77
+ - `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
78
+ the platform the code is running on cannot safely perform encryption for some
79
+ reason (e.g. it lacks a secure random number generator), or the runtime tests
80
+ detected a bug in this library.
81
+
82
+ - `Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException` is thrown if
83
+ either the given `$password` is not the correct password for this
84
+ `KeyProtectedByPassword` or the ciphertext stored internally by this object
85
+ has been modified, i.e. it was accidentally corrupted or intentionally
86
+ corrupted by an attacker. There is no way for the caller to distinguish
87
+ between these two cases.
88
+
89
+ **Side-effects and performance:**
90
+
91
+ This method runs a small and very fast set of self-tests if it is the very first
92
+ time this method or one of the `Crypto` methods has been called. The performance
93
+ overhead is negligible and can be safely ignored in all applications.
94
+
95
+ **Cautions:**
96
+
97
+ PHP stack traces display (portions of) the arguments passed to methods on the
98
+ call stack. If an exception is thrown inside this call, and it is uncaught, the
99
+ value of `$password` may be leaked out to an attacker through the stack trace.
100
+ We recommend configuring PHP to never output stack traces (either displaying
101
+ them to the user or saving them to log files).
102
+
103
+ It is impossible in principle to distinguish between the case where you attempt
104
+ to unlock with the wrong password and the case where you attempt to unlock
105
+ a modified (corrupted) `KeyProtectedByPassword`. It is up to the caller how to
106
+ best deal with this ambiguity, as it depends on the application this library is
107
+ being used in. If in doubt, consult with a professional cryptographer.
108
+
109
+ ### changePassword($current\_password, $new\_password)
110
+
111
+ **Description:**
112
+
113
+ Changes the password, so that calling `unlockKey` on this object in the future
114
+ will require you to pass `$new\_password` instead of the old password. It is
115
+ your responsibility to overwrite all stored copies of this
116
+ `KeyProtectedByPassword`. Any copies you leave lying around can still be
117
+ decrypted with the old password.
118
+
119
+ **Parameters:**
120
+
121
+ 1. `$current\_password` is the password that this `KeyProtectedByPassword` is
122
+ currently protected with.
123
+ 2. `$new\_password` is the new password, which the `KeyProtectedByPassword` will
124
+ be protected with once this operation completes.
125
+
126
+ **Return value:**
127
+
128
+ If `$current\_password` is the correct password, then this method updates itself
129
+ to be protected with the new password, and also returns itself.
130
+
131
+ **Exceptions:**
132
+
133
+ - `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
134
+ the platform the code is running on cannot safely perform encryption for some
135
+ reason (e.g. it lacks a secure random number generator), or the runtime tests
136
+ detected a bug in this library.
137
+
138
+ - `Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException` is thrown if
139
+ either the given `$current\_password` is not the correct password for this
140
+ `KeyProtectedByPassword` or the ciphertext stored internally by this object
141
+ has been modified, i.e. it was accidentally corrupted or intentionally
142
+ corrupted by an attacker. There is no way for the caller to distinguish
143
+ between these two cases.
144
+
145
+ **Side-effects and performance:**
146
+
147
+ This method runs a small and very fast set of self-tests if it is the very first
148
+ time this method or one of the `Crypto` methods has been called. The performance
149
+ overhead is negligible and can be safely ignored in all applications.
150
+
151
+ **Cautions:**
152
+
153
+ PHP stack traces display (portions of) the arguments passed to methods on the
154
+ call stack. If an exception is thrown inside this call, and it is uncaught, the
155
+ value of `$password` may be leaked out to an attacker through the stack trace.
156
+ We recommend configuring PHP to never output stack traces (either displaying
157
+ them to the user or saving them to log files).
158
+
159
+ It is impossible in principle to distinguish between the case where you attempt
160
+ to unlock with the wrong password and the case where you attempt to unlock
161
+ a modified (corrupted) `KeyProtectedByPassword`. It is up to the caller how to
162
+ best deal with this ambiguity, as it depends on the application this library is
163
+ being used in. If in doubt, consult with a professional cryptographer.
164
+
165
+ **WARNING:** Because of the way `KeyProtectedByPassword` is implemented, knowing
166
+ `SHA256($password)` is enough to decrypt a `KeyProtectedByPassword`. To be
167
+ secure, your application MUST NOT EVER compute `SHA256($password)` and use or
168
+ store it for any reason. You must also make sure that other libraries your
169
+ application is using don't compute it either.
170
+
171
+ Static Methods
172
+ ---------------
173
+
174
+ ### KeyProtectedByPassword::createRandomPasswordProtectedKey($password)
175
+
176
+ **Description:**
177
+
178
+ Generates a new random key that's protected by the string `$password` and
179
+ returns an instance of `KeyProtectedByPassword`.
180
+
181
+ **Parameters:**
182
+
183
+ 1. `$password` is the password used to protect the random key.
184
+
185
+ **Return value:**
186
+
187
+ Returns an instance of `KeyProtectedByPassword` containing a randomly-generated
188
+ encryption key that's protected by the password `$password`.
189
+
190
+ **Exceptions:**
191
+
192
+ - `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
193
+ the platform the code is running on cannot safely perform encryption for some
194
+ reason (e.g. it lacks a secure random number generator), or the runtime tests
195
+ detected a bug in this library.
196
+
197
+ **Side-effects and performance:**
198
+
199
+ This method runs a small and very fast set of self-tests if it is the very first
200
+ time this method or one of the `Crypto` methods has been called. The performance
201
+ overhead is negligible and can be safely ignored in all applications.
202
+
203
+ **Cautions:**
204
+
205
+ PHP stack traces display (portions of) the arguments passed to methods on the
206
+ call stack. If an exception is thrown inside this call, and it is uncaught, the
207
+ value of `$password` may be leaked out to an attacker through the stack trace.
208
+ We recommend configuring PHP to never output stack traces (either displaying
209
+ them to the user or saving them to log files).
210
+
211
+ Be aware that if you protecting multiple keys with the same password, an
212
+ attacker with write access to your system will be able to swap the protected
213
+ keys around so that the wrong key gets used next time it is unlocked. This could
214
+ lead to data being encrypted with the wrong key, perhaps one that the attacker
215
+ knows.
216
+
217
+ **WARNING:** Because of the way `KeyProtectedByPassword` is implemented, knowing
218
+ `SHA256($password)` is enough to decrypt a `KeyProtectedByPassword`. To be
219
+ secure, your application MUST NOT EVER compute `SHA256($password)` and use or
220
+ store it for any reason. You must also make sure that other libraries your
221
+ application is using don't compute it either.
222
+
223
+ ### KeyProtectedByPassword::loadFromAsciiSafeString($saved\_key\_string)
224
+
225
+ **Description:**
226
+
227
+ Loads an instance of `KeyProtectedByPassword` that was saved to a string by
228
+ `saveToAsciiSafeString()`.
229
+
230
+ **Parameters:**
231
+
232
+ 1. `$saved_key_string` is the string returned from `saveToAsciiSafeString()`
233
+ when the original `KeyProtectedByPassword` instance was saved.
234
+
235
+ **Return value:**
236
+
237
+ Returns an instance of `KeyProtectedByPassword` representing the same
238
+ password-protected key as the one that was represented by the
239
+ `KeyProtectedByPassword` instance that got saved into `$saved_key_string` by
240
+ a call to `saveToAsciiSafeString()`.
241
+
242
+ **Exceptions:**
243
+
244
+ - `Defuse\Crypto\Exception\EnvironmentIsBrokenException` is thrown either when
245
+ the platform the code is running on cannot safely perform encryption for some
246
+ reason (e.g. it lacks a secure random number generator), or the runtime tests
247
+ detected a bug in this library.
248
+
249
+ - `Defuse\Crypto\Exception\BadFormatException` is thrown whenever
250
+ `$saved_key_string` does not represent a valid `KeyProtectedByPassword`
251
+ instance.
252
+
253
+ **Side-effects and performance:**
254
+
255
+ None.
256
+
257
+ **Cautions:**
258
+
259
+ None.
vendor/defuse/php-encryption/psalm.xml ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <psalm
3
+ useDocblockTypes="true"
4
+ >
5
+ <projectFiles>
6
+ <directory name="src" />
7
+ </projectFiles>
8
+ <issueHandlers>
9
+ <DocblockTypeContradiction errorLevel="info" />
10
+ <RedundantCondition errorLevel="info" />
11
+ <RedundantConditionGivenDocblockType errorLevel="info" />
12
+ </issueHandlers>
13
+ </psalm>
vendor/defuse/php-encryption/src/Core.php ADDED
@@ -0,0 +1,448 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Defuse\Crypto;
4
+
5
+ use Defuse\Crypto\Exception as Ex;
6
+
7
+ final class Core
8
+ {
9
+ const HEADER_VERSION_SIZE = 4;
10
+ const MINIMUM_CIPHERTEXT_SIZE = 84;
11
+
12
+ const CURRENT_VERSION = "\xDE\xF5\x02\x00";
13
+
14
+ const CIPHER_METHOD = 'aes-256-ctr';
15
+ const BLOCK_BYTE_SIZE = 16;
16
+ const KEY_BYTE_SIZE = 32;
17
+ const SALT_BYTE_SIZE = 32;
18
+ const MAC_BYTE_SIZE = 32;
19
+ const HASH_FUNCTION_NAME = 'sha256';
20
+ const ENCRYPTION_INFO_STRING = 'DefusePHP|V2|KeyForEncryption';
21
+ const AUTHENTICATION_INFO_STRING = 'DefusePHP|V2|KeyForAuthentication';
22
+ const BUFFER_BYTE_SIZE = 1048576;
23
+
24
+ const LEGACY_CIPHER_METHOD = 'aes-128-cbc';
25
+ const LEGACY_BLOCK_BYTE_SIZE = 16;
26
+ const LEGACY_KEY_BYTE_SIZE = 16;
27
+ const LEGACY_HASH_FUNCTION_NAME = 'sha256';
28
+ const LEGACY_MAC_BYTE_SIZE = 32;
29
+ const LEGACY_ENCRYPTION_INFO_STRING = 'DefusePHP|KeyForEncryption';
30
+ const LEGACY_AUTHENTICATION_INFO_STRING = 'DefusePHP|KeyForAuthentication';
31
+
32
+ /*
33
+ * V2.0 Format: VERSION (4 bytes) || SALT (32 bytes) || IV (16 bytes) ||
34
+ * CIPHERTEXT (varies) || HMAC (32 bytes)
35
+ *
36
+ * V1.0 Format: HMAC (32 bytes) || IV (16 bytes) || CIPHERTEXT (varies).
37
+ */
38
+
39
+ /**
40
+ * Adds an integer to a block-sized counter.
41
+ *
42
+ * @param string $ctr
43
+ * @param int $inc
44
+ *
45
+ * @throws Ex\EnvironmentIsBrokenException
46
+ *
47
+ * @return string
48
+ *
49
+ * @psalm-suppress RedundantCondition - It's valid to use is_int to check for overflow.
50
+ */
51
+ public static function incrementCounter($ctr, $inc)
52
+ {
53
+ Core::ensureTrue(
54
+ Core::ourStrlen($ctr) === Core::BLOCK_BYTE_SIZE,
55
+ 'Trying to increment a nonce of the wrong size.'
56
+ );
57
+
58
+ Core::ensureTrue(
59
+ \is_int($inc),
60
+ 'Trying to increment nonce by a non-integer.'
61
+ );
62
+
63
+ // The caller is probably re-using CTR-mode keystream if they increment by 0.
64
+ Core::ensureTrue(
65
+ $inc > 0,
66
+ 'Trying to increment a nonce by a nonpositive amount'
67
+ );
68
+
69
+ Core::ensureTrue(
70
+ $inc <= PHP_INT_MAX - 255,
71
+ 'Integer overflow may occur'
72
+ );
73
+
74
+ /*
75
+ * We start at the rightmost byte (big-endian)
76
+ * So, too, does OpenSSL: http://stackoverflow.com/a/3146214/2224584
77
+ */
78
+ for ($i = Core::BLOCK_BYTE_SIZE - 1; $i >= 0; --$i) {
79
+ $sum = \ord($ctr[$i]) + $inc;
80
+
81
+ /* Detect integer overflow and fail. */
82
+ Core::ensureTrue(\is_int($sum), 'Integer overflow in CTR mode nonce increment');
83
+
84
+ $ctr[$i] = \pack('C', $sum & 0xFF);
85
+ $inc = $sum >> 8;
86
+ }
87
+ return $ctr;
88
+ }
89
+
90
+ /**
91
+ * Returns a random byte string of the specified length.
92
+ *
93
+ * @param int $octets
94
+ *
95
+ * @throws Ex\EnvironmentIsBrokenException
96
+ *
97
+ * @return string
98
+ */
99
+ public static function secureRandom($octets)
100
+ {
101
+ self::ensureFunctionExists('random_bytes');
102
+ try {
103
+ return \random_bytes($octets);
104
+ } catch (\Exception $ex) {
105
+ throw new Ex\EnvironmentIsBrokenException(
106
+ 'Your system does not have a secure random number generator.'
107
+ );
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Computes the HKDF key derivation function specified in
113
+ * http://tools.ietf.org/html/rfc5869.
114
+ *
115
+ * @param string $hash Hash Function
116
+ * @param string $ikm Initial Keying Material
117
+ * @param int $length How many bytes?
118
+ * @param string $info What sort of key are we deriving?
119
+ * @param string $salt
120
+ *
121
+ * @throws Ex\EnvironmentIsBrokenException
122
+ * @psalm-suppress UndefinedFunction - We're checking if the function exists first.
123
+ *
124
+ * @return string
125
+ */
126
+ public static function HKDF($hash, $ikm, $length, $info = '', $salt = null)
127
+ {
128
+ static $nativeHKDF = null;
129
+ if ($nativeHKDF === null) {
130
+ $nativeHKDF = \is_callable('\\hash_hkdf');
131
+ }
132
+ if ($nativeHKDF) {
133
+ if (\is_null($salt)) {
134
+ $salt = '';
135
+ }
136
+ return \hash_hkdf($hash, $ikm, $length, $info, $salt);
137
+ }
138
+
139
+ $digest_length = Core::ourStrlen(\hash_hmac($hash, '', '', true));
140
+
141
+ // Sanity-check the desired output length.
142
+ Core::ensureTrue(
143
+ !empty($length) && \is_int($length) && $length >= 0 && $length <= 255 * $digest_length,
144
+ 'Bad output length requested of HDKF.'
145
+ );
146
+
147
+ // "if [salt] not provided, is set to a string of HashLen zeroes."
148
+ if (\is_null($salt)) {
149
+ $salt = \str_repeat("\x00", $digest_length);
150
+ }
151
+
152
+ // HKDF-Extract:
153
+ // PRK = HMAC-Hash(salt, IKM)
154
+ // The salt is the HMAC key.
155
+ $prk = \hash_hmac($hash, $ikm, $salt, true);
156
+
157
+ // HKDF-Expand:
158
+
159
+ // This check is useless, but it serves as a reminder to the spec.
160
+ Core::ensureTrue(Core::ourStrlen($prk) >= $digest_length);
161
+
162
+ // T(0) = ''
163
+ $t = '';
164
+ $last_block = '';
165
+ for ($block_index = 1; Core::ourStrlen($t) < $length; ++$block_index) {
166
+ // T(i) = HMAC-Hash(PRK, T(i-1) | info | 0x??)
167
+ $last_block = \hash_hmac(
168
+ $hash,
169
+ $last_block . $info . \chr($block_index),
170
+ $prk,
171
+ true
172
+ );
173
+ // T = T(1) | T(2) | T(3) | ... | T(N)
174
+ $t .= $last_block;
175
+ }
176
+
177
+ // ORM = first L octets of T
178
+ /** @var string $orm */
179
+ $orm = Core::ourSubstr($t, 0, $length);
180
+ Core::ensureTrue(\is_string($orm));
181
+ return $orm;
182
+ }
183
+
184
+ /**
185
+ * Checks if two equal-length strings are the same without leaking
186
+ * information through side channels.
187
+ *
188
+ * @param string $expected
189
+ * @param string $given
190
+ *
191
+ * @throws Ex\EnvironmentIsBrokenException
192
+ *
193
+ * @return bool
194
+ */
195
+ public static function hashEquals($expected, $given)
196
+ {
197
+ static $native = null;
198
+ if ($native === null) {
199
+ $native = \function_exists('hash_equals');
200
+ }
201
+ if ($native) {
202
+ return \hash_equals($expected, $given);
203
+ }
204
+
205
+ // We can't just compare the strings with '==', since it would make
206
+ // timing attacks possible. We could use the XOR-OR constant-time
207
+ // comparison algorithm, but that may not be a reliable defense in an
208
+ // interpreted language. So we use the approach of HMACing both strings
209
+ // with a random key and comparing the HMACs.
210
+
211
+ // We're not attempting to make variable-length string comparison
212
+ // secure, as that's very difficult. Make sure the strings are the same
213
+ // length.
214
+ Core::ensureTrue(Core::ourStrlen($expected) === Core::ourStrlen($given));
215
+
216
+ $blind = Core::secureRandom(32);
217
+ $message_compare = \hash_hmac(Core::HASH_FUNCTION_NAME, $given, $blind);
218
+ $correct_compare = \hash_hmac(Core::HASH_FUNCTION_NAME, $expected, $blind);
219
+ return $correct_compare === $message_compare;
220
+ }
221
+ /**
222
+ * Throws an exception if the constant doesn't exist.
223
+ *
224
+ * @param string $name
225
+ * @return void
226
+ *
227
+ * @throws Ex\EnvironmentIsBrokenException
228
+ */
229
+ public static function ensureConstantExists($name)
230
+ {
231
+ Core::ensureTrue(\defined($name));
232
+ }
233
+
234
+ /**
235
+ * Throws an exception if the function doesn't exist.
236
+ *
237
+ * @param string $name
238
+ * @return void
239
+ *
240
+ * @throws Ex\EnvironmentIsBrokenException
241
+ */
242
+ public static function ensureFunctionExists($name)
243
+ {
244
+ Core::ensureTrue(\function_exists($name));
245
+ }
246
+
247
+ /**
248
+ * Throws an exception if the condition is false.
249
+ *
250
+ * @param bool $condition
251
+ * @param string $message
252
+ * @return void
253
+ *
254
+ * @throws Ex\EnvironmentIsBrokenException
255
+ */
256
+ public static function ensureTrue($condition, $message = '')
257
+ {
258
+ if (!$condition) {
259
+ throw new Ex\EnvironmentIsBrokenException($message);
260
+ }
261
+ }
262
+
263
+ /*
264
+ * We need these strlen() and substr() functions because when
265
+ * 'mbstring.func_overload' is set in php.ini, the standard strlen() and
266
+ * substr() are replaced by mb_strlen() and mb_substr().
267
+ */
268
+
269
+ /**
270
+ * Computes the length of a string in bytes.
271
+ *
272
+ * @param string $str
273
+ *
274
+ * @throws Ex\EnvironmentIsBrokenException
275
+ *
276
+ * @return int
277
+ */
278
+ public static function ourStrlen($str)
279
+ {
280
+ static $exists = null;
281
+ if ($exists === null) {
282
+ $exists = \extension_loaded('mbstring') && \ini_get('mbstring.func_overload') !== false && (int)\ini_get('mbstring.func_overload') & MB_OVERLOAD_STRING;
283
+ }
284
+ if ($exists) {
285
+ $length = \mb_strlen($str, '8bit');
286
+ Core::ensureTrue($length !== false);
287
+ return $length;
288
+ } else {
289
+ return \strlen($str);
290
+ }
291
+ }
292
+
293
+ /**
294
+ * Behaves roughly like the function substr() in PHP 7 does.
295
+ *
296
+ * @param string $str
297
+ * @param int $start
298
+ * @param int $length
299
+ *
300
+ * @throws Ex\EnvironmentIsBrokenException
301
+ *
302
+ * @return string|bool
303
+ */
304
+ public static function ourSubstr($str, $start, $length = null)
305
+ {
306
+ static $exists = null;
307
+ if ($exists === null) {
308
+ $exists = \extension_loaded('mbstring') && \ini_get('mbstring.func_overload') !== false && (int)\ini_get('mbstring.func_overload') & MB_OVERLOAD_STRING;
309
+ }
310
+
311
+ // This is required to make mb_substr behavior identical to substr.
312
+ // Without this, mb_substr() would return false, contra to what the
313
+ // PHP documentation says (it doesn't say it can return false.)
314
+ $input_len = Core::ourStrlen($str);
315
+ if ($start === $input_len && !$length) {
316
+ return '';
317
+ }
318
+
319
+ if ($start > $input_len) {
320
+ return false;
321
+ }
322
+
323
+ // mb_substr($str, 0, NULL, '8bit') returns an empty string on PHP 5.3,
324
+ // so we have to find the length ourselves. Also, substr() doesn't
325
+ // accept null for the length.
326
+ if (! isset($length)) {
327
+ if ($start >= 0) {
328
+ $length = $input_len - $start;
329
+ } else {
330
+ $length = -$start;
331
+ }
332
+ }
333
+
334
+ if ($length < 0) {
335
+ throw new \InvalidArgumentException(
336
+ "Negative lengths are not supported with ourSubstr."
337
+ );
338
+ }
339
+
340
+ if ($exists) {
341
+ $substr = \mb_substr($str, $start, $length, '8bit');
342
+ // At this point there are two cases where mb_substr can
343
+ // legitimately return an empty string. Either $length is 0, or
344
+ // $start is equal to the length of the string (both mb_substr and
345
+ // substr return an empty string when this happens). It should never
346
+ // ever return a string that's longer than $length.
347
+ if (Core::ourStrlen($substr) > $length || (Core::ourStrlen($substr) === 0 && $length !== 0 && $start !== $input_len)) {
348
+ throw new Ex\EnvironmentIsBrokenException(
349
+ 'Your version of PHP has bug #66797. Its implementation of
350
+ mb_substr() is incorrect. See the details here:
351
+ https://bugs.php.net/bug.php?id=66797'
352
+ );
353
+ }
354
+ return $substr;
355
+ }
356
+
357
+ return \substr($str, $start, $length);
358
+ }
359
+
360
+ /**
361
+ * Computes the PBKDF2 password-based key derivation function.
362
+ *
363
+ * The PBKDF2 function is defined in RFC 2898. Test vectors can be found in
364
+ * RFC 6070. This implementation of PBKDF2 was originally created by Taylor
365
+ * Hornby, with improvements from http://www.variations-of-shadow.com/.
366
+ *
367
+ * @param string $algorithm The hash algorithm to use. Recommended: SHA256
368
+ * @param string $password The password.
369
+ * @param string $salt A salt that is unique to the password.
370
+ * @param int $count Iteration count. Higher is better, but slower. Recommended: At least 1000.
371
+ * @param int $key_length The length of the derived key in bytes.
372
+ * @param bool $raw_output If true, the key is returned in raw binary format. Hex encoded otherwise.
373
+ *
374
+ * @throws Ex\EnvironmentIsBrokenException
375
+ *
376
+ * @return string A $key_length-byte key derived from the password and salt.
377
+ */
378
+ public static function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
379
+ {
380
+ // Type checks:
381
+ if (! \is_string($algorithm)) {
382
+ throw new \InvalidArgumentException(
383
+ 'pbkdf2(): algorithm must be a string'
384
+ );
385
+ }
386
+ if (! \is_string($password)) {
387
+ throw new \InvalidArgumentException(
388
+ 'pbkdf2(): password must be a string'
389
+ );
390
+ }
391
+ if (! \is_string($salt)) {
392
+ throw new \InvalidArgumentException(
393
+ 'pbkdf2(): salt must be a string'
394
+ );
395
+ }
396
+ // Coerce strings to integers with no information loss or overflow
397
+ $count += 0;
398
+ $key_length += 0;
399
+
400
+ $algorithm = \strtolower($algorithm);
401
+ Core::ensureTrue(
402
+ \in_array($algorithm, \hash_algos(), true),
403
+ 'Invalid or unsupported hash algorithm.'
404
+ );
405
+
406
+ // Whitelist, or we could end up with people using CRC32.
407
+ $ok_algorithms = [
408
+ 'sha1', 'sha224', 'sha256', 'sha384', 'sha512',
409
+ 'ripemd160', 'ripemd256', 'ripemd320', 'whirlpool',
410
+ ];
411
+ Core::ensureTrue(
412
+ \in_array($algorithm, $ok_algorithms, true),
413
+ 'Algorithm is not a secure cryptographic hash function.'
414
+ );
415
+
416
+ Core::ensureTrue($count > 0 && $key_length > 0, 'Invalid PBKDF2 parameters.');
417
+
418
+ if (\function_exists('hash_pbkdf2')) {
419
+ // The output length is in NIBBLES (4-bits) if $raw_output is false!
420
+ if (! $raw_output) {
421
+ $key_length = $key_length * 2;
422
+ }
423
+ return \hash_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output);
424
+ }
425
+
426
+ $hash_length = Core::ourStrlen(\hash($algorithm, '', true));
427
+ $block_count = \ceil($key_length / $hash_length);
428
+
429
+ $output = '';
430
+ for ($i = 1; $i <= $block_count; $i++) {
431
+ // $i encoded as 4 bytes, big endian.
432
+ $last = $salt . \pack('N', $i);
433
+ // first iteration
434
+ $last = $xorsum = \hash_hmac($algorithm, $last, $password, true);
435
+ // perform the other $count - 1 iterations
436
+ for ($j = 1; $j < $count; $j++) {
437
+ $xorsum ^= ($last = \hash_hmac($algorithm, $last, $password, true));
438
+ }
439
+ $output .= $xorsum;
440
+ }
441
+
442
+ if ($raw_output) {
443
+ return (string) Core::ourSubstr($output, 0, $key_length);
444
+ } else {
445
+ return Encoding::binToHex((string) Core::ourSubstr($output, 0, $key_length));
446
+ }
447
+ }
448
+ }
vendor/defuse/php-encryption/src/Crypto.php ADDED
@@ -0,0 +1,445 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Defuse\Crypto;
4
+
5
+ use Defuse\Crypto\Exception as Ex;
6
+
7
+ class Crypto
8
+ {
9
+ /**
10
+ * Encrypts a string with a Key.
11
+ *
12
+ * @param string $plaintext
13
+ * @param Key $key
14
+ * @param bool $raw_binary
15
+ *
16
+ * @throws Ex\EnvironmentIsBrokenException
17
+ * @throws \TypeError
18
+ *
19
+ * @return string
20
+ */
21
+ public static function encrypt($plaintext, $key, $raw_binary = false)
22
+ {
23
+ if (!\is_string($plaintext)) {
24
+ throw new \TypeError(
25
+ 'String expected for argument 1. ' . \ucfirst(\gettype($plaintext)) . ' given instead.'
26
+ );
27
+ }
28
+ if (!($key instanceof Key)) {
29
+ throw new \TypeError(
30
+ 'Key expected for argument 2. ' . \ucfirst(\gettype($key)) . ' given instead.'
31
+ );
32
+ }
33
+ if (!\is_bool($raw_binary)) {
34
+ throw new \TypeError(
35
+ 'Boolean expected for argument 3. ' . \ucfirst(\gettype($raw_binary)) . ' given instead.'
36
+ );
37
+ }
38
+ return self::encryptInternal(
39
+ $plaintext,
40
+ KeyOrPassword::createFromKey($key),
41
+ $raw_binary
42
+ );
43
+ }
44
+
45
+ /**
46
+ * Encrypts a string with a password, using a slow key derivation function
47
+ * to make password cracking more expensive.
48
+ *
49
+ * @param string $plaintext
50
+ * @param string $password
51
+ * @param bool $raw_binary
52
+ *
53
+ * @throws Ex\EnvironmentIsBrokenException
54
+ * @throws \TypeError
55
+ *
56
+ * @return string
57
+ */
58
+ public static function encryptWithPassword($plaintext, $password, $raw_binary = false)
59
+ {
60
+ if (!\is_string($plaintext)) {
61
+ throw new \TypeError(
62
+ 'String expected for argument 1. ' . \ucfirst(\gettype($plaintext)) . ' given instead.'
63
+ );
64
+ }
65
+ if (!\is_string($password)) {
66
+ throw new \TypeError(
67
+ 'String expected for argument 2. ' . \ucfirst(\gettype($password)) . ' given instead.'
68
+ );
69
+ }
70
+ if (!\is_bool($raw_binary)) {
71
+ throw new \TypeError(
72
+ 'Boolean expected for argument 3. ' . \ucfirst(\gettype($raw_binary)) . ' given instead.'
73
+ );
74
+ }
75
+ return self::encryptInternal(
76
+ $plaintext,
77
+ KeyOrPassword::createFromPassword($password),
78
+ $raw_binary
79
+ );
80
+ }
81
+
82
+ /**
83
+ * Decrypts a ciphertext to a string with a Key.
84
+ *
85
+ * @param string $ciphertext
86
+ * @param Key $key
87
+ * @param bool $raw_binary
88
+ *
89
+ * @throws \TypeError
90
+ * @throws Ex\EnvironmentIsBrokenException
91
+ * @throws Ex\WrongKeyOrModifiedCiphertextException
92
+ *
93
+ * @return string
94
+ */
95
+ public static function decrypt($ciphertext, $key, $raw_binary = false)
96
+ {
97
+ if (!\is_string($ciphertext)) {
98
+ throw new \TypeError(
99
+ 'String expected for argument 1. ' . \ucfirst(\gettype($ciphertext)) . ' given instead.'
100
+ );
101
+ }
102
+ if (!($key instanceof Key)) {
103
+ throw new \TypeError(
104
+ 'Key expected for argument 2. ' . \ucfirst(\gettype($key)) . ' given instead.'
105
+ );
106
+ }
107
+ if (!\is_bool($raw_binary)) {
108
+ throw new \TypeError(
109
+ 'Boolean expected for argument 3. ' . \ucfirst(\gettype($raw_binary)) . ' given instead.'
110
+ );
111
+ }
112
+ return self::decryptInternal(
113
+ $ciphertext,
114
+ KeyOrPassword::createFromKey($key),
115
+ $raw_binary
116
+ );
117
+ }
118
+
119
+ /**
120
+ * Decrypts a ciphertext to a string with a password, using a slow key
121
+ * derivation function to make password cracking more expensive.
122
+ *
123
+ * @param string $ciphertext
124
+ * @param string $password
125
+ * @param bool $raw_binary
126
+ *
127
+ * @throws Ex\EnvironmentIsBrokenException
128
+ * @throws Ex\WrongKeyOrModifiedCiphertextException
129
+ * @throws \TypeError
130
+ *
131
+ * @return string
132
+ */
133
+ public static function decryptWithPassword($ciphertext, $password, $raw_binary = false)
134
+ {
135
+ if (!\is_string($ciphertext)) {
136
+ throw new \TypeError(
137
+ 'String expected for argument 1. ' . \ucfirst(\gettype($ciphertext)) . ' given instead.'
138
+ );
139
+ }
140
+ if (!\is_string($password)) {
141
+ throw new \TypeError(
142
+ 'String expected for argument 2. ' . \ucfirst(\gettype($password)) . ' given instead.'
143
+ );
144
+ }
145
+ if (!\is_bool($raw_binary)) {
146
+ throw new \TypeError(
147
+ 'Boolean expected for argument 3. ' . \ucfirst(\gettype($raw_binary)) . ' given instead.'
148
+ );
149
+ }
150
+ return self::decryptInternal(
151
+ $ciphertext,
152
+ KeyOrPassword::createFromPassword($password),
153
+ $raw_binary
154
+ );
155
+ }
156
+
157
+ /**
158
+ * Decrypts a legacy ciphertext produced by version 1 of this library.
159
+ *
160
+ * @param string $ciphertext
161
+ * @param string $key
162
+ *
163
+ * @throws Ex\EnvironmentIsBrokenException
164
+ * @throws Ex\WrongKeyOrModifiedCiphertextException
165
+ * @throws \TypeError
166
+ *
167
+ * @return string
168
+ */
169
+ public static function legacyDecrypt($ciphertext, $key)
170
+ {
171
+ if (!\is_string($ciphertext)) {
172
+ throw new \TypeError(
173
+ 'String expected for argument 1. ' . \ucfirst(\gettype($ciphertext)) . ' given instead.'
174
+ );
175
+ }
176
+ if (!\is_string($key)) {
177
+ throw new \TypeError(
178
+ 'String expected for argument 2. ' . \ucfirst(\gettype($key)) . ' given instead.'
179
+ );
180
+ }
181
+
182
+ RuntimeTests::runtimeTest();
183
+
184
+ // Extract the HMAC from the front of the ciphertext.
185
+ if (Core::ourStrlen($ciphertext) <= Core::LEGACY_MAC_BYTE_SIZE) {
186
+ throw new Ex\WrongKeyOrModifiedCiphertextException(
187
+ 'Ciphertext is too short.'
188
+ );
189
+ }
190
+ /**
191
+ * @var string
192
+ */
193
+ $hmac = Core::ourSubstr($ciphertext, 0, Core::LEGACY_MAC_BYTE_SIZE);
194
+ Core::ensureTrue(\is_string($hmac));
195
+ /**
196
+ * @var string
197
+ */
198
+ $messageCiphertext = Core::ourSubstr($ciphertext, Core::LEGACY_MAC_BYTE_SIZE);
199
+ Core::ensureTrue(\is_string($messageCiphertext));
200
+
201
+ // Regenerate the same authentication sub-key.
202
+ $akey = Core::HKDF(
203
+ Core::LEGACY_HASH_FUNCTION_NAME,
204
+ $key,
205
+ Core::LEGACY_KEY_BYTE_SIZE,
206
+ Core::LEGACY_AUTHENTICATION_INFO_STRING,
207
+ null
208
+ );
209
+
210
+ if (self::verifyHMAC($hmac, $messageCiphertext, $akey)) {
211
+ // Regenerate the same encryption sub-key.
212
+ $ekey = Core::HKDF(
213
+ Core::LEGACY_HASH_FUNCTION_NAME,
214
+ $key,
215
+ Core::LEGACY_KEY_BYTE_SIZE,
216
+ Core::LEGACY_ENCRYPTION_INFO_STRING,
217
+ null
218
+ );
219
+
220
+ // Extract the IV from the ciphertext.
221
+ if (Core::ourStrlen($messageCiphertext) <= Core::LEGACY_BLOCK_BYTE_SIZE) {
222
+ throw new Ex\WrongKeyOrModifiedCiphertextException(
223
+ 'Ciphertext is too short.'
224
+ );
225
+ }
226
+ /**
227
+ * @var string
228
+ */
229
+ $iv = Core::ourSubstr($messageCiphertext, 0, Core::LEGACY_BLOCK_BYTE_SIZE);
230
+ Core::ensureTrue(\is_string($iv));
231
+
232
+ /**
233
+ * @var string
234
+ */
235
+ $actualCiphertext = Core::ourSubstr($messageCiphertext, Core::LEGACY_BLOCK_BYTE_SIZE);
236
+ Core::ensureTrue(\is_string($actualCiphertext));
237
+
238
+ // Do the decryption.
239
+ $plaintext = self::plainDecrypt($actualCiphertext, $ekey, $iv, Core::LEGACY_CIPHER_METHOD);
240
+ return $plaintext;
241
+ } else {
242
+ throw new Ex\WrongKeyOrModifiedCiphertextException(
243
+ 'Integrity check failed.'
244
+ );
245
+ }
246
+ }
247
+
248
+ /**
249
+ * Encrypts a string with either a key or a password.
250
+ *
251
+ * @param string $plaintext
252
+ * @param KeyOrPassword $secret
253
+ * @param bool $raw_binary
254
+ *
255
+ * @return string
256
+ */
257
+ private static function encryptInternal($plaintext, KeyOrPassword $secret, $raw_binary)
258
+ {
259
+ RuntimeTests::runtimeTest();
260
+
261
+ $salt = Core::secureRandom(Core::SALT_BYTE_SIZE);
262
+ $keys = $secret->deriveKeys($salt);
263
+ $ekey = $keys->getEncryptionKey();
264
+ $akey = $keys->getAuthenticationKey();
265
+ $iv = Core::secureRandom(Core::BLOCK_BYTE_SIZE);
266
+
267
+ $ciphertext = Core::CURRENT_VERSION . $salt . $iv . self::plainEncrypt($plaintext, $ekey, $iv);
268
+ $auth = \hash_hmac(Core::HASH_FUNCTION_NAME, $ciphertext, $akey, true);
269
+ $ciphertext = $ciphertext . $auth;
270
+
271
+ if ($raw_binary) {
272
+ return $ciphertext;
273
+ }
274
+ return Encoding::binToHex($ciphertext);
275
+ }
276
+
277
+ /**
278
+ * Decrypts a ciphertext to a string with either a key or a password.
279
+ *
280
+ * @param string $ciphertext
281
+ * @param KeyOrPassword $secret
282
+ * @param bool $raw_binary
283
+ *
284
+ * @throws Ex\EnvironmentIsBrokenException
285
+ * @throws Ex\WrongKeyOrModifiedCiphertextException
286
+ *
287
+ * @return string
288
+ */
289
+ private static function decryptInternal($ciphertext, KeyOrPassword $secret, $raw_binary)
290
+ {
291
+ RuntimeTests::runtimeTest();
292
+
293
+ if (! $raw_binary) {
294
+ try {
295
+ $ciphertext = Encoding::hexToBin($ciphertext);
296
+ } catch (Ex\BadFormatException $ex) {
297
+ throw new Ex\WrongKeyOrModifiedCiphertextException(
298
+ 'Ciphertext has invalid hex encoding.'
299
+ );
300
+ }
301
+ }
302
+
303
+ if (Core::ourStrlen($ciphertext) < Core::MINIMUM_CIPHERTEXT_SIZE) {
304
+ throw new Ex\WrongKeyOrModifiedCiphertextException(
305
+ 'Ciphertext is too short.'
306
+ );
307
+ }
308
+
309
+ // Get and check the version header.
310
+ /** @var string $header */
311
+ $header = Core::ourSubstr($ciphertext, 0, Core::HEADER_VERSION_SIZE);
312
+ if ($header !== Core::CURRENT_VERSION) {
313
+ throw new Ex\WrongKeyOrModifiedCiphertextException(
314
+ 'Bad version header.'
315
+ );
316
+ }
317
+
318
+ // Get the salt.
319
+ /** @var string $salt */
320
+ $salt = Core::ourSubstr(
321
+ $ciphertext,
322
+ Core::HEADER_VERSION_SIZE,
323
+ Core::SALT_BYTE_SIZE
324
+ );
325
+ Core::ensureTrue(\is_string($salt));
326
+
327
+ // Get the IV.
328
+ /** @var string $iv */
329
+ $iv = Core::ourSubstr(
330
+ $ciphertext,
331
+ Core::HEADER_VERSION_SIZE + Core::SALT_BYTE_SIZE,
332
+ Core::BLOCK_BYTE_SIZE
333
+ );
334
+ Core::ensureTrue(\is_string($iv));
335
+
336
+ // Get the HMAC.
337
+ /** @var string $hmac */
338
+ $hmac = Core::ourSubstr(
339
+ $ciphertext,
340
+ Core::ourStrlen($ciphertext) - Core::MAC_BYTE_SIZE,
341
+ Core::MAC_BYTE_SIZE
342
+ );
343
+ Core::ensureTrue(\is_string($hmac));
344
+
345
+ // Get the actual encrypted ciphertext.
346
+ /** @var string $encrypted */
347
+ $encrypted = Core::ourSubstr(
348
+ $ciphertext,
349
+ Core::HEADER_VERSION_SIZE + Core::SALT_BYTE_SIZE +
350
+ Core::BLOCK_BYTE_SIZE,
351
+ Core::ourStrlen($ciphertext) - Core::MAC_BYTE_SIZE - Core::SALT_BYTE_SIZE -
352
+ Core::BLOCK_BYTE_SIZE - Core::HEADER_VERSION_SIZE
353
+ );
354
+ Core::ensureTrue(\is_string($encrypted));
355
+
356
+ // Derive the separate encryption and authentication keys from the key
357
+ // or password, whichever it is.
358
+ $keys = $secret->deriveKeys($salt);
359
+
360
+ if (self::verifyHMAC($hmac, $header . $salt . $iv . $encrypted, $keys->getAuthenticationKey())) {
361
+ $plaintext = self::plainDecrypt($encrypted, $keys->getEncryptionKey(), $iv, Core::CIPHER_METHOD);
362
+ return $plaintext;
363
+ } else {
364
+ throw new Ex\WrongKeyOrModifiedCiphertextException(
365
+ 'Integrity check failed.'
366
+ );
367
+ }
368
+ }
369
+
370
+ /**
371
+ * Raw unauthenticated encryption (insecure on its own).
372
+ *
373
+ * @param string $plaintext
374
+ * @param string $key
375
+ * @param string $iv
376
+ *
377
+ * @throws Ex\EnvironmentIsBrokenException
378
+ *
379
+ * @return string
380
+ */
381
+ protected static function plainEncrypt($plaintext, $key, $iv)
382
+ {
383
+ Core::ensureConstantExists('OPENSSL_RAW_DATA');
384
+ Core::ensureFunctionExists('openssl_encrypt');
385
+ /** @var string $ciphertext */
386
+ $ciphertext = \openssl_encrypt(
387
+ $plaintext,
388
+ Core::CIPHER_METHOD,
389
+ $key,
390
+ OPENSSL_RAW_DATA,
391
+ $iv
392
+ );
393
+
394
+ Core::ensureTrue(\is_string($ciphertext), 'openssl_encrypt() failed');
395
+
396
+ return $ciphertext;
397
+ }
398
+
399
+ /**
400
+ * Raw unauthenticated decryption (insecure on its own).
401
+ *
402
+ * @param string $ciphertext
403
+ * @param string $key
404
+ * @param string $iv
405
+ * @param string $cipherMethod
406
+ *
407
+ * @throws Ex\EnvironmentIsBrokenException
408
+ *
409
+ * @return string
410
+ */
411
+ protected static function plainDecrypt($ciphertext, $key, $iv, $cipherMethod)
412
+ {
413
+ Core::ensureConstantExists('OPENSSL_RAW_DATA');
414
+ Core::ensureFunctionExists('openssl_decrypt');
415
+
416
+ /** @var string $plaintext */
417
+ $plaintext = \openssl_decrypt(
418
+ $ciphertext,
419
+ $cipherMethod,
420
+ $key,
421
+ OPENSSL_RAW_DATA,
422
+ $iv
423
+ );
424
+ Core::ensureTrue(\is_string($plaintext), 'openssl_decrypt() failed.');
425
+
426
+ return $plaintext;
427
+ }
428
+
429
+ /**
430
+ * Verifies an HMAC without leaking information through side-channels.
431
+ *
432
+ * @param string $expected_hmac
433
+ * @param string $message
434
+ * @param string $key
435
+ *
436
+ * @throws Ex\EnvironmentIsBrokenException
437
+ *
438
+ * @return bool
439
+ */
440
+ protected static function verifyHMAC($expected_hmac, $message, $key)
441
+ {
442
+ $message_hmac = \hash_hmac(Core::HASH_FUNCTION_NAME, $message, $key, true);
443
+ return Core::hashEquals($message_hmac, $expected_hmac);
444
+ }
445
+ }
vendor/defuse/php-encryption/src/DerivedKeys.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Defuse\Crypto;
4
+
5
+ /**
6
+ * Class DerivedKeys
7
+ * @package Defuse\Crypto
8
+ */
9
+ final class DerivedKeys
10
+ {
11
+ /**
12
+ * @var string
13
+ */
14
+ private $akey = '';
15
+
16
+ /**
17
+ * @var string
18
+ */
19
+ private $ekey = '';
20
+
21
+ /**
22
+ * Returns the authentication key.
23
+ * @return string
24
+ */
25
+ public function getAuthenticationKey()
26
+ {
27
+ return $this->akey;
28
+ }
29
+
30
+ /**
31
+ * Returns the encryption key.
32
+ * @return string
33
+ */
34
+ public function getEncryptionKey()
35
+ {
36
+ return $this->ekey;
37
+ }
38
+
39
+ /**
40
+ * Constructor for DerivedKeys.
41
+ *
42
+ * @param string $akey
43
+ * @param string $ekey
44
+ */
45
+ public function __construct($akey, $ekey)
46
+ {
47
+ $this->akey = $akey;
48
+ $this->ekey = $ekey;
49
+ }
50
+ }
vendor/defuse/php-encryption/src/Encoding.php ADDED
@@ -0,0 +1,269 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Defuse\Crypto;
4
+
5
+ use Defuse\Crypto\Exception as Ex;
6
+
7
+ final class Encoding
8
+ {
9
+ const CHECKSUM_BYTE_SIZE = 32;
10
+ const CHECKSUM_HASH_ALGO = 'sha256';
11
+ const SERIALIZE_HEADER_BYTES = 4;
12
+
13
+ /**
14
+ * Converts a byte string to a hexadecimal string without leaking
15
+ * information through side channels.
16
+ *
17
+ * @param string $byte_string
18
+ *
19
+ * @throws Ex\EnvironmentIsBrokenException
20
+ *
21
+ * @return string
22
+ */
23
+ public static function binToHex($byte_string)
24
+ {
25
+ $hex = '';
26
+ $len = Core::ourStrlen($byte_string);
27
+ for ($i = 0; $i < $len; ++$i) {
28
+ $c = \ord($byte_string[$i]) & 0xf;
29
+ $b = \ord($byte_string[$i]) >> 4;
30
+ $hex .= \pack(
31
+ 'CC',
32
+ 87 + $b + ((($b - 10) >> 8) & ~38),
33
+ 87 + $c + ((($c - 10) >> 8) & ~38)
34
+ );
35
+ }
36
+ return $hex;
37
+ }
38
+
39
+ /**
40
+ * Converts a hexadecimal string into a byte string without leaking
41
+ * information through side channels.
42
+ *
43
+ * @param string $hex_string
44
+ *
45
+ * @throws Ex\BadFormatException
46
+ * @throws Ex\EnvironmentIsBrokenException
47
+ *
48
+ * @return string
49
+ * @psalm-suppress TypeDoesNotContainType
50
+ */
51
+ public static function hexToBin($hex_string)
52
+ {
53
+ $hex_pos = 0;
54
+ $bin = '';
55
+ $hex_len = Core::ourStrlen($hex_string);
56
+ $state = 0;
57
+ $c_acc = 0;
58
+
59
+ while ($hex_pos < $hex_len) {
60
+ $c = \ord($hex_string[$hex_pos]);
61
+ $c_num = $c ^ 48;
62
+ $c_num0 = ($c_num - 10) >> 8;
63
+ $c_alpha = ($c & ~32) - 55;
64
+ $c_alpha0 = (($c_alpha - 10) ^ ($c_alpha - 16)) >> 8;
65
+ if (($c_num0 | $c_alpha0) === 0) {
66
+ throw new Ex\BadFormatException(
67
+ 'Encoding::hexToBin() input is not a hex string.'
68
+ );
69
+ }
70
+ $c_val = ($c_num0 & $c_num) | ($c_alpha & $c_alpha0);
71
+ if ($state === 0) {
72
+ $c_acc = $c_val * 16;
73
+ } else {
74
+ $bin .= \pack('C', $c_acc | $c_val);
75
+ }
76
+ $state ^= 1;
77
+ ++$hex_pos;
78
+ }
79
+ return $bin;
80
+ }
81
+
82
+ /**
83
+ * Remove trialing whitespace without table look-ups or branches.
84
+ *
85
+ * Calling this function may leak the length of the string as well as the
86
+ * number of trailing whitespace characters through side-channels.
87
+ *
88
+ * @param string $string
89
+ * @return string
90
+ */
91
+ public static function trimTrailingWhitespace($string = '')
92
+ {
93
+ $length = Core::ourStrlen($string);
94
+ if ($length < 1) {
95
+ return '';
96
+ }
97
+ do {
98
+ $prevLength = $length;
99
+ $last = $length - 1;
100
+ $chr = \ord($string[$last]);
101
+
102
+ /* Null Byte (0x00), a.k.a. \0 */
103
+ // if ($chr === 0x00) $length -= 1;
104
+ $sub = (($chr - 1) >> 8 ) & 1;
105
+ $length -= $sub;
106
+ $last -= $sub;
107
+
108
+ /* Horizontal Tab (0x09) a.k.a. \t */
109
+ $chr = \ord($string[$last]);
110
+ // if ($chr === 0x09) $length -= 1;
111
+ $sub = (((0x08 - $chr) & ($chr - 0x0a)) >> 8) & 1;
112
+ $length -= $sub;
113
+ $last -= $sub;
114
+
115
+ /* New Line (0x0a), a.k.a. \n */
116
+ $chr = \ord($string[$last]);
117
+ // if ($chr === 0x0a) $length -= 1;
118
+ $sub = (((0x09 - $chr) & ($chr - 0x0b)) >> 8) & 1;
119
+ $length -= $sub;
120
+ $last -= $sub;
121
+
122
+ /* Carriage Return (0x0D), a.k.a. \r */
123
+ $chr = \ord($string[$last]);
124
+ // if ($chr === 0x0d) $length -= 1;
125
+ $sub = (((0x0c - $chr) & ($chr - 0x0e)) >> 8) & 1;
126
+ $length -= $sub;
127
+ $last -= $sub;
128
+
129
+ /* Space */
130
+ $chr = \ord($string[$last]);
131
+ // if ($chr === 0x20) $length -= 1;
132
+ $sub = (((0x1f - $chr) & ($chr - 0x21)) >> 8) & 1;
133
+ $length -= $sub;
134
+ } while ($prevLength !== $length && $length > 0);
135
+ return (string) Core::ourSubstr($string, 0, $length);
136
+ }
137
+
138
+ /*
139
+ * SECURITY NOTE ON APPLYING CHECKSUMS TO SECRETS:
140
+ *
141
+ * The checksum introduces a potential security weakness. For example,
142
+ * suppose we apply a checksum to a key, and that an adversary has an
143
+ * exploit against the process containing the key, such that they can
144
+ * overwrite an arbitrary byte of memory and then cause the checksum to
145
+ * be verified and learn the result.
146
+ *
147
+ * In this scenario, the adversary can extract the key one byte at
148
+ * a time by overwriting it with their guess of its value and then
149
+ * asking if the checksum matches. If it does, their guess was right.
150
+ * This kind of attack may be more easy to implement and more reliable
151
+ * than a remote code execution attack.
152
+ *
153
+ * This attack also applies to authenticated encryption as a whole, in
154
+ * the situation where the adversary can overwrite a byte of the key
155
+ * and then cause a valid ciphertext to be decrypted, and then
156
+ * determine whether the MAC check passed or failed.
157
+ *
158
+ * By using the full SHA256 hash instead of truncating it, I'm ensuring
159
+ * that both ways of going about the attack are equivalently difficult.
160
+ * A shorter checksum of say 32 bits might be more useful to the
161
+ * adversary as an oracle in case their writes are coarser grained.
162
+ *
163
+ * Because the scenario assumes a serious vulnerability, we don't try
164
+ * to prevent attacks of this style.
165
+ */
166
+
167
+ /**
168
+ * INTERNAL USE ONLY: Applies a version header, applies a checksum, and
169
+ * then encodes a byte string into a range of printable ASCII characters.
170
+ *
171
+ * @param string $header
172
+ * @param string $bytes
173
+ *
174
+ * @throws Ex\EnvironmentIsBrokenException
175
+ *
176
+ * @return string
177
+ */
178
+ public static function saveBytesToChecksummedAsciiSafeString($header, $bytes)
179
+ {
180
+ // Headers must be a constant length to prevent one type's header from
181
+ // being a prefix of another type's header, leading to ambiguity.
182
+ Core::ensureTrue(
183
+ Core::ourStrlen($header) === self::SERIALIZE_HEADER_BYTES,
184
+ 'Header must be ' . self::SERIALIZE_HEADER_BYTES . ' bytes.'
185
+ );
186
+
187
+ return Encoding::binToHex(
188
+ $header .
189
+ $bytes .
190
+ \hash(
191
+ self::CHECKSUM_HASH_ALGO,
192
+ $header . $bytes,
193
+ true
194
+ )
195
+ );
196
+ }
197
+
198
+ /**
199
+ * INTERNAL USE ONLY: Decodes, verifies the header and checksum, and returns
200
+ * the encoded byte string.
201
+ *
202
+ * @param string $expected_header
203
+ * @param string $string
204
+ *
205
+ * @throws Ex\EnvironmentIsBrokenException
206
+ * @throws Ex\BadFormatException
207
+ *
208
+ * @return string
209
+ */
210
+ public static function loadBytesFromChecksummedAsciiSafeString($expected_header, $string)
211
+ {
212
+ // Headers must be a constant length to prevent one type's header from
213
+ // being a prefix of another type's header, leading to ambiguity.
214
+ Core::ensureTrue(
215
+ Core::ourStrlen($expected_header) === self::SERIALIZE_HEADER_BYTES,
216
+ 'Header must be 4 bytes.'
217
+ );
218
+
219
+ /* If you get an exception here when attempting to load from a file, first pass your
220
+ key to Encoding::trimTrailingWhitespace() to remove newline characters, etc. */
221
+ $bytes = Encoding::hexToBin($string);
222
+
223
+ /* Make sure we have enough bytes to get the version header and checksum. */
224
+ if (Core::ourStrlen($bytes) < self::SERIALIZE_HEADER_BYTES + self::CHECKSUM_BYTE_SIZE) {
225
+ throw new Ex\BadFormatException(
226
+ 'Encoded data is shorter than expected.'
227
+ );
228
+ }
229
+
230
+ /* Grab the version header. */
231
+ $actual_header = (string) Core::ourSubstr($bytes, 0, self::SERIALIZE_HEADER_BYTES);
232
+
233
+ if ($actual_header !== $expected_header) {
234
+ throw new Ex\BadFormatException(
235
+ 'Invalid header.'
236
+ );
237
+ }
238
+
239
+ /* Grab the bytes that are part of the checksum. */
240
+ $checked_bytes = (string) Core::ourSubstr(
241
+ $bytes,
242
+ 0,
243
+ Core::ourStrlen($bytes) - self::CHECKSUM_BYTE_SIZE
244
+ );
245
+
246
+ /* Grab the included checksum. */
247
+ $checksum_a = (string) Core::ourSubstr(
248
+ $bytes,
249
+ Core::ourStrlen($bytes) - self::CHECKSUM_BYTE_SIZE,
250
+ self::CHECKSUM_BYTE_SIZE
251
+ );
252
+
253
+ /* Re-compute the checksum. */
254
+ $checksum_b = \hash(self::CHECKSUM_HASH_ALGO, $checked_bytes, true);
255
+
256
+ /* Check if the checksum matches. */
257
+ if (! Core::hashEquals($checksum_a, $checksum_b)) {
258
+ throw new Ex\BadFormatException(
259
+ "Data is corrupted, the checksum doesn't match"
260
+ );
261
+ }
262
+
263
+ return (string) Core::ourSubstr(
264
+ $bytes,
265
+ self::SERIALIZE_HEADER_BYTES,
266
+ Core::ourStrlen($bytes) - self::SERIALIZE_HEADER_BYTES - self::CHECKSUM_BYTE_SIZE
267
+ );
268
+ }
269
+ }
vendor/defuse/php-encryption/src/Exception/BadFormatException.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Defuse\Crypto\Exception;
4
+
5
+ class BadFormatException extends \Defuse\Crypto\Exception\CryptoException
6
+ {
7
+ }
vendor/defuse/php-encryption/src/Exception/CryptoException.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Defuse\Crypto\Exception;
4
+
5
+ class CryptoException extends \Exception
6
+ {
7
+ }
vendor/defuse/php-encryption/src/Exception/EnvironmentIsBrokenException.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Defuse\Crypto\Exception;
4
+
5
+ class EnvironmentIsBrokenException extends \Defuse\Crypto\Exception\CryptoException
6
+ {
7
+ }
vendor/defuse/php-encryption/src/Exception/IOException.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Defuse\Crypto\Exception;
4
+
5
+ class IOException extends \Defuse\Crypto\Exception\CryptoException
6
+ {
7
+ }
vendor/defuse/php-encryption/src/Exception/WrongKeyOrModifiedCiphertextException.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Defuse\Crypto\Exception;
4
+
5
+ class WrongKeyOrModifiedCiphertextException extends \Defuse\Crypto\Exception\CryptoException
6
+ {
7
+ }
vendor/defuse/php-encryption/src/File.php ADDED
@@ -0,0 +1,762 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Defuse\Crypto;
4
+
5
+ use Defuse\Crypto\Exception as Ex;
6
+
7
+ final class File
8
+ {
9
+ /**
10
+ * Encrypts the input file, saving the ciphertext to the output file.
11
+ *
12
+ * @param string $inputFilename
13
+ * @param string $outputFilename
14
+ * @param Key $key
15
+ * @return void
16
+ *
17
+ * @throws Ex\EnvironmentIsBrokenException
18
+ * @throws Ex\IOException
19
+ */
20
+ public static function encryptFile($inputFilename, $outputFilename, Key $key)
21
+ {
22
+ self::encryptFileInternal(
23
+ $inputFilename,
24
+ $outputFilename,
25
+ KeyOrPassword::createFromKey($key)
26
+ );
27
+ }
28
+
29
+ /**
30
+ * Encrypts a file with a password, using a slow key derivation function to
31
+ * make password cracking more expensive.
32
+ *
33
+ * @param string $inputFilename
34
+ * @param string $outputFilename
35
+ * @param string $password
36
+ * @return void
37
+ *
38
+ * @throws Ex\EnvironmentIsBrokenException
39
+ * @throws Ex\IOException
40
+ */
41
+ public static function encryptFileWithPassword($inputFilename, $outputFilename, $password)
42
+ {
43
+ self::encryptFileInternal(
44
+ $inputFilename,
45
+ $outputFilename,
46
+ KeyOrPassword::createFromPassword($password)
47
+ );
48
+ }
49
+
50
+ /**
51
+ * Decrypts the input file, saving the plaintext to the output file.
52
+ *
53
+ * @param string $inputFilename
54
+ * @param string $outputFilename
55
+ * @param Key $key
56
+ * @return void
57
+ *
58
+ * @throws Ex\EnvironmentIsBrokenException
59
+ * @throws Ex\IOException
60
+ * @throws Ex\WrongKeyOrModifiedCiphertextException
61
+ */
62
+ public static function decryptFile($inputFilename, $outputFilename, Key $key)
63
+ {
64
+ self::decryptFileInternal(
65
+ $inputFilename,
66
+ $outputFilename,
67
+ KeyOrPassword::createFromKey($key)
68
+ );
69
+ }
70
+
71
+ /**
72
+ * Decrypts a file with a password, using a slow key derivation function to
73
+ * make password cracking more expensive.
74
+ *
75
+ * @param string $inputFilename
76
+ * @param string $outputFilename
77
+ * @param string $password
78
+ * @return void
79
+ *
80
+ * @throws Ex\EnvironmentIsBrokenException
81
+ * @throws Ex\IOException
82
+ * @throws Ex\WrongKeyOrModifiedCiphertextException
83
+ */
84
+ public static function decryptFileWithPassword($inputFilename, $outputFilename, $password)
85
+ {
86
+ self::decryptFileInternal(
87
+ $inputFilename,
88
+ $outputFilename,
89
+ KeyOrPassword::createFromPassword($password)
90
+ );
91
+ }
92
+
93
+ /**
94
+ * Takes two resource handles and encrypts the contents of the first,
95
+ * writing the ciphertext into the second.
96
+ *
97
+ * @param resource $inputHandle
98
+ * @param resource $outputHandle
99
+ * @param Key $key
100
+ * @return void
101
+ *
102
+ * @throws Ex\EnvironmentIsBrokenException
103
+ * @throws Ex\WrongKeyOrModifiedCiphertextException
104
+ */
105
+ public static function encryptResource($inputHandle, $outputHandle, Key $key)
106
+ {
107
+ self::encryptResourceInternal(
108
+ $inputHandle,
109
+ $outputHandle,
110
+ KeyOrPassword::createFromKey($key)
111
+ );
112
+ }
113
+
114
+ /**
115
+ * Encrypts the contents of one resource handle into another with a
116
+ * password, using a slow key derivation function to make password cracking
117
+ * more expensive.
118
+ *
119
+ * @param resource $inputHandle
120
+ * @param resource $outputHandle
121
+ * @param string $password
122
+ * @return void
123
+ *
124
+ * @throws Ex\EnvironmentIsBrokenException
125
+ * @throws Ex\IOException
126
+ * @throws Ex\WrongKeyOrModifiedCiphertextException
127
+ */
128
+ public static function encryptResourceWithPassword($inputHandle, $outputHandle, $password)
129
+ {
130
+ self::encryptResourceInternal(
131
+ $inputHandle,
132
+ $outputHandle,
133
+ KeyOrPassword::createFromPassword($password)
134
+ );
135
+ }
136
+
137
+ /**
138
+ * Takes two resource handles and decrypts the contents of the first,
139
+ * writing the plaintext into the second.
140
+ *
141
+ * @param resource $inputHandle
142
+ * @param resource $outputHandle
143
+ * @param Key $key
144
+ * @return void
145
+ *
146
+ * @throws Ex\EnvironmentIsBrokenException
147
+ * @throws Ex\IOException
148
+ * @throws Ex\WrongKeyOrModifiedCiphertextException
149
+ */
150
+ public static function decryptResource($inputHandle, $outputHandle, Key $key)
151
+ {
152
+ self::decryptResourceInternal(
153
+ $inputHandle,
154
+ $outputHandle,
155
+ KeyOrPassword::createFromKey($key)
156
+ );
157
+ }
158
+
159
+ /**
160
+ * Decrypts the contents of one resource into another with a password, using
161
+ * a slow key derivation function to make password cracking more expensive.
162
+ *
163
+ * @param resource $inputHandle
164
+ * @param resource $outputHandle
165
+ * @param string $password
166
+ * @return void
167
+ *
168
+ * @throws Ex\EnvironmentIsBrokenException
169
+ * @throws Ex\IOException
170
+ * @throws Ex\WrongKeyOrModifiedCiphertextException
171
+ */
172
+ public static function decryptResourceWithPassword($inputHandle, $outputHandle, $password)
173
+ {
174
+ self::decryptResourceInternal(
175
+ $inputHandle,
176
+ $outputHandle,
177
+ KeyOrPassword::createFromPassword($password)
178
+ );
179
+ }
180
+
181
+ /**
182
+ * Encrypts a file with either a key or a password.
183
+ *
184
+ * @param string $inputFilename
185
+ * @param string $outputFilename
186
+ * @param KeyOrPassword $secret
187
+ * @return void
188
+ *
189
+ * @throws Ex\CryptoException
190
+ * @throws Ex\IOException
191
+ */
192
+ private static function encryptFileInternal($inputFilename, $outputFilename, KeyOrPassword $secret)
193
+ {
194
+ /* Open the input file. */
195
+ $if = @\fopen($inputFilename, 'rb');
196
+ if ($if === false) {
197
+ throw new Ex\IOException(
198
+ 'Cannot open input file for encrypting: ' .
199
+ self::getLastErrorMessage()
200
+ );
201
+ }
202
+ if (\is_callable('\\stream_set_read_buffer')) {
203
+ /* This call can fail, but the only consequence is performance. */
204
+ \stream_set_read_buffer($if, 0);
205
+ }
206
+
207
+ /* Open the output file. */
208
+ $of = @\fopen($outputFilename, 'wb');
209
+ if ($of === false) {
210
+ \fclose($if);
211
+ throw new Ex\IOException(
212
+ 'Cannot open output file for encrypting: ' .
213
+ self::getLastErrorMessage()
214
+ );
215
+ }
216
+ if (\is_callable('\\stream_set_write_buffer')) {
217
+ /* This call can fail, but the only consequence is performance. */
218
+ \stream_set_write_buffer($of, 0);
219
+ }
220
+
221
+ /* Perform the encryption. */
222
+ try {
223
+ self::encryptResourceInternal($if, $of, $secret);
224
+ } catch (Ex\CryptoException $ex) {
225
+ \fclose($if);
226
+ \fclose($of);
227
+ throw $ex;
228
+ }
229
+
230
+ /* Close the input file. */
231
+ if (\fclose($if) === false) {
232
+ \fclose($of);
233
+ throw new Ex\IOException(
234
+ 'Cannot close input file after encrypting'
235
+ );
236
+ }
237
+
238
+ /* Close the output file. */
239
+ if (\fclose($of) === false) {
240
+ throw new Ex\IOException(
241
+ 'Cannot close output file after encrypting'
242
+ );
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Decrypts a file with either a key or a password.
248
+ *
249
+ * @param string $inputFilename
250
+ * @param string $outputFilename
251
+ * @param KeyOrPassword $secret
252
+ * @return void
253
+ *
254
+ * @throws Ex\CryptoException
255
+ * @throws Ex\IOException
256
+ */
257
+ private static function decryptFileInternal($inputFilename, $outputFilename, KeyOrPassword $secret)
258
+ {
259
+ /* Open the input file. */
260
+ $if = @\fopen($inputFilename, 'rb');
261
+ if ($if === false) {
262
+ throw new Ex\IOException(
263
+ 'Cannot open input file for decrypting: ' .
264
+ self::getLastErrorMessage()
265
+ );
266
+ }
267
+
268
+ if (\is_callable('\\stream_set_read_buffer')) {
269
+ /* This call can fail, but the only consequence is performance. */
270
+ \stream_set_read_buffer($if, 0);
271
+ }
272
+
273
+ /* Open the output file. */
274
+ $of = @\fopen($outputFilename, 'wb');
275
+ if ($of === false) {
276
+ \fclose($if);
277
+ throw new Ex\IOException(
278
+ 'Cannot open output file for decrypting: ' .
279
+ self::getLastErrorMessage()
280
+ );
281
+ }
282
+
283
+ if (\is_callable('\\stream_set_write_buffer')) {
284
+ /* This call can fail, but the only consequence is performance. */
285
+ \stream_set_write_buffer($of, 0);
286
+ }
287
+
288
+ /* Perform the decryption. */
289
+ try {
290
+ self::decryptResourceInternal($if, $of, $secret);
291
+ } catch (Ex\CryptoException $ex) {
292
+ \fclose($if);
293
+ \fclose($of);
294
+ throw $ex;
295
+ }
296
+
297
+ /* Close the input file. */
298
+ if (\fclose($if) === false) {
299
+ \fclose($of);
300
+ throw new Ex\IOException(
301
+ 'Cannot close input file after decrypting'
302
+ );
303
+ }
304
+
305
+ /* Close the output file. */
306
+ if (\fclose($of) === false) {
307
+ throw new Ex\IOException(
308
+ 'Cannot close output file after decrypting'
309
+ );
310
+ }
311
+ }
312
+
313
+ /**
314
+ * Encrypts a resource with either a key or a password.
315
+ *
316
+ * @param resource $inputHandle
317
+ * @param resource $outputHandle
318
+ * @param KeyOrPassword $secret
319
+ * @return void
320
+ *
321
+ * @throws Ex\EnvironmentIsBrokenException
322
+ * @throws Ex\IOException
323
+ */
324
+ private static function encryptResourceInternal($inputHandle, $outputHandle, KeyOrPassword $secret)
325
+ {
326
+ if (! \is_resource($inputHandle)) {
327
+ throw new Ex\IOException(
328
+ 'Input handle must be a resource!'
329
+ );
330
+ }
331
+ if (! \is_resource($outputHandle)) {
332
+ throw new Ex\IOException(
333
+ 'Output handle must be a resource!'
334
+ );
335
+ }
336
+
337
+ $inputStat = \fstat($inputHandle);
338
+ $inputSize = $inputStat['size'];
339
+
340
+ $file_salt = Core::secureRandom(Core::SALT_BYTE_SIZE);
341
+ $keys = $secret->deriveKeys($file_salt);
342
+ $ekey = $keys->getEncryptionKey();
343
+ $akey = $keys->getAuthenticationKey();
344
+
345
+ $ivsize = Core::BLOCK_BYTE_SIZE;
346
+ $iv = Core::secureRandom($ivsize);
347
+
348
+ /* Initialize a streaming HMAC state. */
349
+ /** @var resource $hmac */
350
+ $hmac = \hash_init(Core::HASH_FUNCTION_NAME, HASH_HMAC, $akey);
351
+ Core::ensureTrue(
352
+ \is_resource($hmac) || \is_object($hmac),
353
+ 'Cannot initialize a hash context'
354
+ );
355
+
356
+ /* Write the header, salt, and IV. */
357
+ self::writeBytes(
358
+ $outputHandle,
359
+ Core::CURRENT_VERSION . $file_salt . $iv,
360
+ Core::HEADER_VERSION_SIZE + Core::SALT_BYTE_SIZE + $ivsize
361
+ );
362
+
363
+ /* Add the header, salt, and IV to the HMAC. */
364
+ \hash_update($hmac, Core::CURRENT_VERSION);
365
+ \hash_update($hmac, $file_salt);
366
+ \hash_update($hmac, $iv);
367
+
368
+ /* $thisIv will be incremented after each call to the encryption. */
369
+ $thisIv = $iv;
370
+
371
+ /* How many blocks do we encrypt at a time? We increment by this value. */
372
+ $inc = (int) (Core::BUFFER_BYTE_SIZE / Core::BLOCK_BYTE_SIZE);
373
+
374
+ /* Loop until we reach the end of the input file. */
375
+ $at_file_end = false;
376
+ while (! (\feof($inputHandle) || $at_file_end)) {
377
+ /* Find out if we can read a full buffer, or only a partial one. */
378
+ /** @var int */
379
+ $pos = \ftell($inputHandle);
380
+ if (!\is_int($pos)) {
381
+ throw new Ex\IOException(
382
+ 'Could not get current position in input file during encryption'
383
+ );
384
+ }
385
+ if ($pos + Core::BUFFER_BYTE_SIZE >= $inputSize) {
386
+ /* We're at the end of the file, so we need to break out of the loop. */
387
+ $at_file_end = true;
388
+ $read = self::readBytes(
389
+ $inputHandle,
390
+ $inputSize - $pos
391
+ );
392
+ } else {
393
+ $read = self::readBytes(
394
+ $inputHandle,
395
+ Core::BUFFER_BYTE_SIZE
396
+ );
397
+ }
398
+
399
+ /* Encrypt this buffer. */
400
+ /** @var string */
401
+ $encrypted = \openssl_encrypt(
402
+ $read,
403
+ Core::CIPHER_METHOD,
404
+ $ekey,
405
+ OPENSSL_RAW_DATA,
406
+ $thisIv
407
+ );
408
+
409
+ Core::ensureTrue(\is_string($encrypted), 'OpenSSL encryption error');
410
+
411
+ /* Write this buffer's ciphertext. */
412
+ self::writeBytes($outputHandle, $encrypted, Core::ourStrlen($encrypted));
413
+ /* Add this buffer's ciphertext to the HMAC. */
414
+ \hash_update($hmac, $encrypted);
415
+
416
+ /* Increment the counter by the number of blocks in a buffer. */
417
+ $thisIv = Core::incrementCounter($thisIv, $inc);
418
+ /* WARNING: Usually, unless the file is a multiple of the buffer
419
+ * size, $thisIv will contain an incorrect value here on the last
420
+ * iteration of this loop. */
421
+ }
422
+
423
+ /* Get the HMAC and append it to the ciphertext. */
424
+ $final_mac = \hash_final($hmac, true);
425
+ self::writeBytes($outputHandle, $final_mac, Core::MAC_BYTE_SIZE);
426
+ }
427
+
428
+ /**
429
+ * Decrypts a file-backed resource with either a key or a password.
430
+ *
431
+ * @param resource $inputHandle
432
+ * @param resource $outputHandle
433
+ * @param KeyOrPassword $secret
434
+ * @return void
435
+ *
436
+ * @throws Ex\EnvironmentIsBrokenException
437
+ * @throws Ex\IOException
438
+ * @throws Ex\WrongKeyOrModifiedCiphertextException
439
+ */
440
+ public static function decryptResourceInternal($inputHandle, $outputHandle, KeyOrPassword $secret)
441
+ {
442
+ if (! \is_resource($inputHandle)) {
443
+ throw new Ex\IOException(
444
+ 'Input handle must be a resource!'
445
+ );
446
+ }
447
+ if (! \is_resource($outputHandle)) {
448
+ throw new Ex\IOException(
449
+ 'Output handle must be a resource!'
450
+ );
451
+ }
452
+
453
+ /* Make sure the file is big enough for all the reads we need to do. */
454
+ $stat = \fstat($inputHandle);
455
+ if ($stat['size'] < Core::MINIMUM_CIPHERTEXT_SIZE) {
456
+ throw new Ex\WrongKeyOrModifiedCiphertextException(
457
+ 'Input file is too small to have been created by this library.'
458
+ );
459
+ }
460
+
461
+ /* Check the version header. */
462
+ $header = self::readBytes($inputHandle, Core::HEADER_VERSION_SIZE);
463
+ if ($header !== Core::CURRENT_VERSION) {
464
+ throw new Ex\WrongKeyOrModifiedCiphertextException(
465
+ 'Bad version header.'
466
+ );
467
+ }
468
+
469
+ /* Get the salt. */
470
+ $file_salt = self::readBytes($inputHandle, Core::SALT_BYTE_SIZE);
471
+
472
+ /* Get the IV. */
473
+ $ivsize = Core::BLOCK_BYTE_SIZE;
474
+ $iv = self::readBytes($inputHandle, $ivsize);
475
+
476
+ /* Derive the authentication and encryption keys. */
477
+ $keys = $secret->deriveKeys($file_salt);
478
+ $ekey = $keys->getEncryptionKey();
479
+ $akey = $keys->getAuthenticationKey();
480
+
481
+ /* We'll store the MAC of each buffer-sized chunk as we verify the
482
+ * actual MAC, so that we can check them again when decrypting. */
483
+ $macs = [];
484
+
485
+ /* $thisIv will be incremented after each call to the decryption. */
486
+ $thisIv = $iv;
487
+
488
+ /* How many blocks do we encrypt at a time? We increment by this value. */
489
+ $inc = (int) (Core::BUFFER_BYTE_SIZE / Core::BLOCK_BYTE_SIZE);
490
+
491
+ /* Get the HMAC. */
492
+ if (\fseek($inputHandle, (-1 * Core::MAC_BYTE_SIZE), SEEK_END) === false) {
493
+ throw new Ex\IOException(
494
+ 'Cannot seek to beginning of MAC within input file'
495
+ );
496
+ }
497
+
498
+ /* Get the position of the last byte in the actual ciphertext. */
499
+ /** @var int $cipher_end */
500
+ $cipher_end = \ftell($inputHandle);
501
+ if (!\is_int($cipher_end)) {
502
+ throw new Ex\IOException(
503
+ 'Cannot read input file'
504
+ );
505
+ }
506
+ /* We have the position of the first byte of the HMAC. Go back by one. */
507
+ --$cipher_end;
508
+
509
+ /* Read the HMAC. */
510
+ /** @var string $stored_mac */
511
+ $stored_mac = self::readBytes($inputHandle, Core::MAC_BYTE_SIZE);
512
+
513
+ /* Initialize a streaming HMAC state. */
514
+ /** @var resource $hmac */
515
+ $hmac = \hash_init(Core::HASH_FUNCTION_NAME, HASH_HMAC, $akey);
516
+ Core::ensureTrue(\is_resource($hmac) || \is_object($hmac), 'Cannot initialize a hash context');
517
+
518
+ /* Reset file pointer to the beginning of the file after the header */
519
+ if (\fseek($inputHandle, Core::HEADER_VERSION_SIZE, SEEK_SET) === false) {
520
+ throw new Ex\IOException(
521
+ 'Cannot read seek within input file'
522
+ );
523
+ }
524
+
525
+ /* Seek to the start of the actual ciphertext. */
526
+ if (\fseek($inputHandle, Core::SALT_BYTE_SIZE + $ivsize, SEEK_CUR) === false) {
527
+ throw new Ex\IOException(
528
+ 'Cannot seek input file to beginning of ciphertext'
529
+ );
530
+ }
531
+
532
+ /* PASS #1: Calculating the HMAC. */
533
+
534
+ \hash_update($hmac, $header);
535
+ \hash_update($hmac, $file_salt);
536
+ \hash_update($hmac, $iv);
537
+ /** @var resource $hmac2 */
538
+ $hmac2 = \hash_copy($hmac);
539
+
540
+ $break = false;
541
+ while (! $break) {
542
+ /** @var int $pos */
543
+ $pos = \ftell($inputHandle);
544
+ if (!\is_int($pos)) {
545
+ throw new Ex\IOException(
546
+ 'Could not get current position in input file during decryption'
547
+ );
548
+ }
549
+
550
+ /* Read the next buffer-sized chunk (or less). */
551
+ if ($pos + Core::BUFFER_BYTE_SIZE >= $cipher_end) {
552
+ $break = true;
553
+ $read = self::readBytes(
554
+ $inputHandle,
555
+ $cipher_end - $pos + 1
556
+ );
557
+ } else {
558
+ $read = self::readBytes(
559
+ $inputHandle,
560
+ Core::BUFFER_BYTE_SIZE
561
+ );
562
+ }
563
+
564
+ /* Update the HMAC. */
565
+ \hash_update($hmac, $read);
566
+
567
+ /* Remember this buffer-sized chunk's HMAC. */
568
+ /** @var resource $chunk_mac */
569
+ $chunk_mac = \hash_copy($hmac);
570
+ Core::ensureTrue(\is_resource($chunk_mac) || \is_object($chunk_mac), 'Cannot duplicate a hash context');
571
+ $macs []= \hash_final($chunk_mac);
572
+ }
573
+
574
+ /* Get the final HMAC, which should match the stored one. */
575
+ /** @var string $final_mac */
576
+ $final_mac = \hash_final($hmac, true);
577
+
578
+ /* Verify the HMAC. */
579
+ if (! Core::hashEquals($final_mac, $stored_mac)) {
580
+ throw new Ex\WrongKeyOrModifiedCiphertextException(
581
+ 'Integrity check failed.'
582
+ );
583
+ }
584
+
585
+ /* PASS #2: Decrypt and write output. */
586
+
587
+ /* Rewind to the start of the actual ciphertext. */
588
+ if (\fseek($inputHandle, Core::SALT_BYTE_SIZE + $ivsize + Core::HEADER_VERSION_SIZE, SEEK_SET) === false) {
589
+ throw new Ex\IOException(
590
+ 'Could not move the input file pointer during decryption'
591
+ );
592
+ }
593
+
594
+ $at_file_end = false;
595
+ while (! $at_file_end) {
596
+ /** @var int $pos */
597
+ $pos = \ftell($inputHandle);
598
+ if (!\is_int($pos)) {
599
+ throw new Ex\IOException(
600
+ 'Could not get current position in input file during decryption'
601
+ );
602
+ }
603
+
604
+ /* Read the next buffer-sized chunk (or less). */
605
+ if ($pos + Core::BUFFER_BYTE_SIZE >= $cipher_end) {
606
+ $at_file_end = true;
607
+ $read = self::readBytes(
608
+ $inputHandle,
609
+ $cipher_end - $pos + 1
610
+ );
611
+ } else {
612
+ $read = self::readBytes(
613
+ $inputHandle,
614
+ Core::BUFFER_BYTE_SIZE
615
+ );
616
+ }
617
+
618
+ /* Recalculate the MAC (so far) and compare it with the one we
619
+ * remembered from pass #1 to ensure attackers didn't change the
620
+ * ciphertext after MAC verification. */
621
+ \hash_update($hmac2, $read);
622
+ /** @var resource $calc_mac */
623
+ $calc_mac = \hash_copy($hmac2);
624
+ Core::ensureTrue(\is_resource($calc_mac) || \is_object($calc_mac), 'Cannot duplicate a hash context');
625
+ $calc = \hash_final($calc_mac);
626
+
627
+ if (empty($macs)) {
628
+ throw new Ex\WrongKeyOrModifiedCiphertextException(
629
+ 'File was modified after MAC verification'
630
+ );
631
+ } elseif (! Core::hashEquals(\array_shift($macs), $calc)) {
632
+ throw new Ex\WrongKeyOrModifiedCiphertextException(
633
+ 'File was modified after MAC verification'
634
+ );
635
+ }
636
+
637
+ /* Decrypt this buffer-sized chunk. */
638
+ /** @var string $decrypted */
639
+ $decrypted = \openssl_decrypt(
640
+ $read,
641
+ Core::CIPHER_METHOD,
642
+ $ekey,
643
+ OPENSSL_RAW_DATA,
644
+ $thisIv
645
+ );
646
+ Core::ensureTrue(\is_string($decrypted), 'OpenSSL decryption error');
647
+
648
+ /* Write the plaintext to the output file. */
649
+ self::writeBytes(
650
+ $outputHandle,
651
+ $decrypted,
652
+ Core::ourStrlen($decrypted)
653
+ );
654
+
655
+ /* Increment the IV by the amount of blocks in a buffer. */
656
+ /** @var string $thisIv */
657
+ $thisIv = Core::incrementCounter($thisIv, $inc);
658
+ /* WARNING: Usually, unless the file is a multiple of the buffer
659
+ * size, $thisIv will contain an incorrect value here on the last
660
+ * iteration of this loop. */
661
+ }
662
+ }
663
+
664
+ /**
665
+ * Read from a stream; prevent partial reads.
666
+ *
667
+ * @param resource $stream
668
+ * @param int $num_bytes
669
+ * @return string
670
+ *
671
+ * @throws Ex\IOException
672
+ * @throws Ex\EnvironmentIsBrokenException
673
+ *
674
+ * @return string
675
+ */
676
+ public static function readBytes($stream, $num_bytes)
677
+ {
678
+ Core::ensureTrue($num_bytes >= 0, 'Tried to read less than 0 bytes');
679
+
680
+ if ($num_bytes === 0) {
681
+ return '';
682
+ }
683
+
684
+ $buf = '';
685
+ $remaining = $num_bytes;
686
+ while ($remaining > 0 && ! \feof($stream)) {
687
+ /** @var string $read */
688
+ $read = \fread($stream, $remaining);
689
+ if (!\is_string($read)) {
690
+ throw new Ex\IOException(
691
+ 'Could not read from the file'
692
+ );
693
+ }
694
+ $buf .= $read;
695
+ $remaining -= Core::ourStrlen($read);
696
+ }
697
+ if (Core::ourStrlen($buf) !== $num_bytes) {
698
+ throw new Ex\IOException(
699
+ 'Tried to read past the end of the file'
700
+ );
701
+ }
702
+ return $buf;
703
+ }
704
+
705
+ /**
706
+ * Write to a stream; prevents partial writes.
707
+ *
708
+ * @param resource $stream
709
+ * @param string $buf
710
+ * @param int $num_bytes
711
+ * @return int
712
+ *
713
+ * @throws Ex\IOException
714
+ *
715
+ * @return string
716
+ */
717
+ public static function writeBytes($stream, $buf, $num_bytes = null)
718
+ {
719
+ $bufSize = Core::ourStrlen($buf);
720
+ if ($num_bytes === null) {
721
+ $num_bytes = $bufSize;
722
+ }
723
+ if ($num_bytes > $bufSize) {
724
+ throw new Ex\IOException(
725
+ 'Trying to write more bytes than the buffer contains.'
726
+ );
727
+ }
728
+ if ($num_bytes < 0) {
729
+ throw new Ex\IOException(
730
+ 'Tried to write less than 0 bytes'
731
+ );
732
+ }
733
+ $remaining = $num_bytes;
734
+ while ($remaining > 0) {
735
+ /** @var int $written */
736
+ $written = \fwrite($stream, $buf, $remaining);
737
+ if (!\is_int($written)) {
738
+ throw new Ex\IOException(
739
+ 'Could not write to the file'
740
+ );
741
+ }
742
+ $buf = (string) Core::ourSubstr($buf, $written, null);
743
+ $remaining -= $written;
744
+ }
745
+ return $num_bytes;
746
+ }
747
+
748
+ /**
749
+ * Returns the last PHP error's or warning's message string.
750
+ *
751
+ * @return string
752
+ */
753
+ private static function getLastErrorMessage()
754
+ {
755
+ $error = error_get_last();
756
+ if ($error === null) {
757
+ return '[no PHP error]';
758
+ } else {
759
+ return $error['message'];
760
+ }
761
+ }
762
+ }
vendor/defuse/php-encryption/src/Key.php ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Defuse\Crypto;
4
+
5
+ use Defuse\Crypto\Exception as Ex;
6
+
7
+ final class Key
8
+ {
9
+ const KEY_CURRENT_VERSION = "\xDE\xF0\x00\x00";
10
+ const KEY_BYTE_SIZE = 32;
11
+
12
+ /**
13
+ * @var string
14
+ */
15
+ private $key_bytes;
16
+
17
+ /**
18
+ * Creates new random key.
19
+ *
20
+ * @throws Ex\EnvironmentIsBrokenException
21
+ *
22
+ * @return Key
23
+ */
24
+ public static function createNewRandomKey()
25
+ {
26
+ return new Key(Core::secureRandom(self::KEY_BYTE_SIZE));
27
+ }
28
+
29
+ /**
30
+ * Loads a Key from its encoded form.
31
+ *
32
+ * By default, this function will call Encoding::trimTrailingWhitespace()
33
+ * to remove trailing CR, LF, NUL, TAB, and SPACE characters, which are
34
+ * commonly appended to files when working with text editors.
35
+ *
36
+ * @param string $saved_key_string
37
+ * @param bool $do_not_trim (default: false)
38
+ *
39
+ * @throws Ex\BadFormatException
40
+ * @throws Ex\EnvironmentIsBrokenException
41
+ *
42
+ * @return Key
43
+ */
44
+ public static function loadFromAsciiSafeString($saved_key_string, $do_not_trim = false)
45
+ {
46
+ if (!$do_not_trim) {
47
+ $saved_key_string = Encoding::trimTrailingWhitespace($saved_key_string);
48
+ }
49
+ $key_bytes = Encoding::loadBytesFromChecksummedAsciiSafeString(self::KEY_CURRENT_VERSION, $saved_key_string);
50
+ return new Key($key_bytes);
51
+ }
52
+
53
+ /**
54
+ * Encodes the Key into a string of printable ASCII characters.
55
+ *
56
+ * @throws Ex\EnvironmentIsBrokenException
57
+ *
58
+ * @return string
59
+ */
60
+ public function saveToAsciiSafeString()
61
+ {
62
+ return Encoding::saveBytesToChecksummedAsciiSafeString(
63
+ self::KEY_CURRENT_VERSION,
64
+ $this->key_bytes
65
+ );
66
+ }
67
+
68
+ /**
69
+ * Gets the raw bytes of the key.
70
+ *
71
+ * @return string
72
+ */
73
+ public function getRawBytes()
74
+ {
75
+ return $this->key_bytes;
76
+ }
77
+
78
+ /**
79
+ * Constructs a new Key object from a string of raw bytes.
80
+ *
81
+ * @param string $bytes
82
+ *
83
+ * @throws Ex\EnvironmentIsBrokenException
84
+ */
85
+ private function __construct($bytes)
86
+ {
87
+ Core::ensureTrue(
88
+ Core::ourStrlen($bytes) === self::KEY_BYTE_SIZE,
89
+ 'Bad key length.'
90
+ );
91
+ $this->key_bytes = $bytes;
92
+ }
93
+
94
+ }
vendor/defuse/php-encryption/src/KeyOrPassword.php ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Defuse\Crypto;
4
+
5
+ use Defuse\Crypto\Exception as Ex;
6
+
7
+ final class KeyOrPassword
8
+ {
9
+ const PBKDF2_ITERATIONS = 100000;
10
+ const SECRET_TYPE_KEY = 1;
11
+ const SECRET_TYPE_PASSWORD = 2;
12
+
13
+ /**
14
+ * @var int
15
+ */
16
+ private $secret_type = 0;
17
+
18
+ /**
19
+ * @var Key|string
20
+ */
21
+ private $secret;
22
+
23
+ /**
24
+ * Initializes an instance of KeyOrPassword from a key.
25
+ *
26
+ * @param Key $key
27
+ *
28
+ * @return KeyOrPassword
29
+ */
30
+ public static function createFromKey(Key $key)
31
+ {
32
+ return new KeyOrPassword(self::SECRET_TYPE_KEY, $key);
33
+ }
34
+
35
+ /**
36
+ * Initializes an instance of KeyOrPassword from a password.
37
+ *
38
+ * @param string $password
39
+ *
40
+ * @return KeyOrPassword
41
+ */
42
+ public static function createFromPassword($password)
43
+ {
44
+ return new KeyOrPassword(self::SECRET_TYPE_PASSWORD, $password);
45
+ }
46
+
47
+ /**
48
+ * Derives authentication and encryption keys from the secret, using a slow
49
+ * key derivation function if the secret is a password.
50
+ *
51
+ * @param string $salt
52
+ *
53
+ * @throws Ex\CryptoException
54
+ * @throws Ex\EnvironmentIsBrokenException
55
+ *
56
+ * @return DerivedKeys
57
+ */
58
+ public function deriveKeys($salt)
59
+ {
60
+ Core::ensureTrue(
61
+ Core::ourStrlen($salt) === Core::SALT_BYTE_SIZE,
62
+ 'Bad salt.'
63
+ );
64
+
65
+ if ($this->secret_type === self::SECRET_TYPE_KEY) {
66
+ Core::ensureTrue($this->secret instanceof Key);
67
+ /**
68
+ * @psalm-suppress PossiblyInvalidMethodCall
69
+ */
70
+ $akey = Core::HKDF(
71
+ Core::HASH_FUNCTION_NAME,
72
+ $this->secret->getRawBytes(),
73
+ Core::KEY_BYTE_SIZE,
74
+ Core::AUTHENTICATION_INFO_STRING,
75
+ $salt
76
+ );
77
+ /**
78
+ * @psalm-suppress PossiblyInvalidMethodCall
79
+ */
80
+ $ekey = Core::HKDF(
81
+ Core::HASH_FUNCTION_NAME,
82
+ $this->secret->getRawBytes(),
83
+ Core::KEY_BYTE_SIZE,
84
+ Core::ENCRYPTION_INFO_STRING,
85
+ $salt
86
+ );
87
+ return new DerivedKeys($akey, $ekey);
88
+ } elseif ($this->secret_type === self::SECRET_TYPE_PASSWORD) {
89
+ Core::ensureTrue(\is_string($this->secret));
90
+ /* Our PBKDF2 polyfill is vulnerable to a DoS attack documented in
91
+ * GitHub issue #230. The fix is to pre-hash the password to ensure
92
+ * it is short. We do the prehashing here instead of in pbkdf2() so
93
+ * that pbkdf2() still computes the function as defined by the
94
+ * standard. */
95
+
96
+ /**
97
+ * @psalm-suppress PossiblyInvalidArgument
98
+ */
99
+ $prehash = \hash(Core::HASH_FUNCTION_NAME, $this->secret, true);
100
+
101
+ $prekey = Core::pbkdf2(
102
+ Core::HASH_FUNCTION_NAME,
103
+ $prehash,
104
+ $salt,
105
+ self::PBKDF2_ITERATIONS,
106
+ Core::KEY_BYTE_SIZE,
107
+ true
108
+ );
109
+ $akey = Core::HKDF(
110
+ Core::HASH_FUNCTION_NAME,
111
+ $prekey,
112
+ Core::KEY_BYTE_SIZE,
113
+ Core::AUTHENTICATION_INFO_STRING,
114
+ $salt
115
+ );
116
+ /* Note the cryptographic re-use of $salt here. */
117
+ $ekey = Core::HKDF(
118
+ Core::HASH_FUNCTION_NAME,
119
+ $prekey,
120
+ Core::KEY_BYTE_SIZE,
121
+ Core::ENCRYPTION_INFO_STRING,
122
+ $salt
123
+ );
124
+ return new DerivedKeys($akey, $ekey);
125
+ } else {
126
+ throw new Ex\EnvironmentIsBrokenException('Bad secret type.');
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Constructor for KeyOrPassword.
132
+ *
133
+ * @param int $secret_type
134
+ * @param mixed $secret (either a Key or a password string)
135
+ */
136
+ private function __construct($secret_type, $secret)
137
+ {
138
+ // The constructor is private, so these should never throw.
139
+ if ($secret_type === self::SECRET_TYPE_KEY) {
140
+ Core::ensureTrue($secret instanceof Key);
141
+ } elseif ($secret_type === self::SECRET_TYPE_PASSWORD) {
142
+ Core::ensureTrue(\is_string($secret));
143
+ } else {
144
+ throw new Ex\EnvironmentIsBrokenException('Bad secret type.');
145
+ }
146
+ $this->secret_type = $secret_type;
147
+ $this->secret = $secret;
148
+ }
149
+ }
vendor/defuse/php-encryption/src/KeyProtectedByPassword.php ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Defuse\Crypto;
4
+
5
+ use Defuse\Crypto\Exception as Ex;
6
+
7
+ final class KeyProtectedByPassword
8
+ {
9
+ const PASSWORD_KEY_CURRENT_VERSION = "\xDE\xF1\x00\x00";
10
+
11
+ /**
12
+ * @var string
13
+ */
14
+ private $encrypted_key = '';
15
+
16
+ /**
17
+ * Creates a random key protected by the provided password.
18
+ *
19
+ * @param string $password
20
+ *
21
+ * @throws Ex\EnvironmentIsBrokenException
22
+ *
23
+ * @return KeyProtectedByPassword
24
+ */
25
+ public static function createRandomPasswordProtectedKey($password)
26
+ {
27
+ $inner_key = Key::createNewRandomKey();
28
+ /* The password is hashed as a form of poor-man's domain separation
29
+ * between this use of encryptWithPassword() and other uses of
30
+ * encryptWithPassword() that the user may also be using as part of the
31
+ * same protocol. */
32
+ $encrypted_key = Crypto::encryptWithPassword(
33
+ $inner_key->saveToAsciiSafeString(),
34
+ \hash(Core::HASH_FUNCTION_NAME, $password, true),
35
+ true
36
+ );
37
+
38
+ return new KeyProtectedByPassword($encrypted_key);
39
+ }
40
+
41
+ /**
42
+ * Loads a KeyProtectedByPassword from its encoded form.
43
+ *
44
+ * @param string $saved_key_string
45
+ *
46
+ * @throws Ex\BadFormatException
47
+ *
48
+ * @return KeyProtectedByPassword
49
+ */
50
+ public static function loadFromAsciiSafeString($saved_key_string)
51
+ {
52
+ $encrypted_key = Encoding::loadBytesFromChecksummedAsciiSafeString(
53
+ self::PASSWORD_KEY_CURRENT_VERSION,
54
+ $saved_key_string
55
+ );
56
+ return new KeyProtectedByPassword($encrypted_key);
57
+ }
58
+
59
+ /**
60
+ * Encodes the KeyProtectedByPassword into a string of printable ASCII
61
+ * characters.
62
+ *
63
+ * @throws Ex\EnvironmentIsBrokenException
64
+ *
65
+ * @return string
66
+ */
67
+ public function saveToAsciiSafeString()
68
+ {
69
+ return Encoding::saveBytesToChecksummedAsciiSafeString(
70
+ self::PASSWORD_KEY_CURRENT_VERSION,
71
+ $this->encrypted_key
72
+ );
73
+ }
74
+
75
+ /**
76
+ * Decrypts the protected key, returning an unprotected Key object that can
77
+ * be used for encryption and decryption.
78
+ *
79
+ * @throws Ex\EnvironmentIsBrokenException
80
+ * @throws Ex\WrongKeyOrModifiedCiphertextException
81
+ *
82
+ * @param string $password
83
+ * @return Key
84
+ */
85
+ public function unlockKey($password)
86
+ {
87
+ try {
88
+ $inner_key_encoded = Crypto::decryptWithPassword(
89
+ $this->encrypted_key,
90
+ \hash(Core::HASH_FUNCTION_NAME, $password, true),
91
+ true
92
+ );
93
+ return Key::loadFromAsciiSafeString($inner_key_encoded);
94
+ } catch (Ex\BadFormatException $ex) {
95
+ /* This should never happen unless an attacker replaced the
96
+ * encrypted key ciphertext with some other ciphertext that was
97
+ * encrypted with the same password. We transform the exception type
98
+ * here in order to make the API simpler, avoiding the need to
99
+ * document that this method might throw an Ex\BadFormatException. */
100
+ throw new Ex\WrongKeyOrModifiedCiphertextException(
101
+ "The decrypted key was found to be in an invalid format. " .
102
+ "This very likely indicates it was modified by an attacker."
103
+ );
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Changes the password.
109
+ *
110
+ * @param string $current_password
111
+ * @param string $new_password
112
+ *
113
+ * @throws Ex\EnvironmentIsBrokenException
114
+ * @throws Ex\WrongKeyOrModifiedCiphertextException
115
+ *
116
+ * @return KeyProtectedByPassword
117
+ */
118
+ public function changePassword($current_password, $new_password)
119
+ {
120
+ $inner_key = $this->unlockKey($current_password);
121
+ /* The password is hashed as a form of poor-man's domain separation
122
+ * between this use of encryptWithPassword() and other uses of
123
+ * encryptWithPassword() that the user may also be using as part of the
124
+ * same protocol. */
125
+ $encrypted_key = Crypto::encryptWithPassword(
126
+ $inner_key->saveToAsciiSafeString(),
127
+ \hash(Core::HASH_FUNCTION_NAME, $new_password, true),
128
+ true
129
+ );
130
+
131
+ $this->encrypted_key = $encrypted_key;
132
+
133
+ return $this;
134
+ }
135
+
136
+ /**
137
+ * Constructor for KeyProtectedByPassword.
138
+ *
139
+ * @param string $encrypted_key
140
+ */
141
+ private function __construct($encrypted_key)
142
+ {
143
+ $this->encrypted_key = $encrypted_key;
144
+ }
145
+ }
vendor/defuse/php-encryption/src/RuntimeTests.php ADDED
@@ -0,0 +1,228 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Defuse\Crypto;
4
+
5
+ use Defuse\Crypto\Exception as Ex;
6
+
7
+ /*
8
+ * We're using static class inheritance to get access to protected methods
9
+ * inside Crypto. To make it easy to know where the method we're calling can be
10
+ * found, within this file, prefix calls with `Crypto::` or `RuntimeTests::`,
11
+ * and don't use `self::`.
12
+ */
13
+
14
+ class RuntimeTests extends Crypto
15
+ {
16
+ /**
17
+ * Runs the runtime tests.
18
+ *
19
+ * @throws Ex\EnvironmentIsBrokenException
20
+ * @return void
21
+ */
22
+ public static function runtimeTest()
23
+ {
24
+ // 0: Tests haven't been run yet.
25
+ // 1: Tests have passed.
26
+ // 2: Tests are running right now.
27
+ // 3: Tests have failed.
28
+ static $test_state = 0;
29
+
30
+ if ($test_state === 1 || $test_state === 2) {
31
+ return;
32
+ }
33
+
34
+ if ($test_state === 3) {
35
+ /* If an intermittent problem caused a test to fail previously, we
36
+ * want that to be indicated to the user with every call to this
37
+ * library. This way, if the user first does something they really
38
+ * don't care about, and just ignores all exceptions, they won't get
39
+ * screwed when they then start to use the library for something
40
+ * they do care about. */
41
+ throw new Ex\EnvironmentIsBrokenException('Tests failed previously.');
42
+ }
43
+
44
+ try {
45
+ $test_state = 2;
46
+
47
+ Core::ensureFunctionExists('openssl_get_cipher_methods');
48
+ if (\in_array(Core::CIPHER_METHOD, \openssl_get_cipher_methods()) === false) {
49
+ throw new Ex\EnvironmentIsBrokenException(
50
+ 'Cipher method not supported. This is normally caused by an outdated ' .
51
+ 'version of OpenSSL (and/or OpenSSL compiled for FIPS compliance). ' .
52
+ 'Please upgrade to a newer version of OpenSSL that supports ' .
53
+ Core::CIPHER_METHOD . ' to use this library.'
54
+ );
55
+ }
56
+
57
+ RuntimeTests::AESTestVector();
58
+ RuntimeTests::HMACTestVector();
59
+ RuntimeTests::HKDFTestVector();
60
+
61
+ RuntimeTests::testEncryptDecrypt();
62
+ Core::ensureTrue(Core::ourStrlen(Key::createNewRandomKey()->getRawBytes()) === Core::KEY_BYTE_SIZE);
63
+
64
+ Core::ensureTrue(Core::ENCRYPTION_INFO_STRING !== Core::AUTHENTICATION_INFO_STRING);
65
+ } catch (Ex\EnvironmentIsBrokenException $ex) {
66
+ // Do this, otherwise it will stay in the "tests are running" state.
67
+ $test_state = 3;
68
+ throw $ex;
69
+ }
70
+
71
+ // Change this to '0' make the tests always re-run (for benchmarking).
72
+ $test_state = 1;
73
+ }
74
+
75
+ /**
76
+ * High-level tests of Crypto operations.
77
+ *
78
+ * @throws Ex\EnvironmentIsBrokenException
79
+ * @return void
80
+ */
81
+ private static function testEncryptDecrypt()
82
+ {
83
+ $key = Key::createNewRandomKey();
84
+ $data = "EnCrYpT EvErYThInG\x00\x00";
85
+
86
+ // Make sure encrypting then decrypting doesn't change the message.
87
+ $ciphertext = Crypto::encrypt($data, $key, true);
88
+ try {
89
+ $decrypted = Crypto::decrypt($ciphertext, $key, true);
90
+ } catch (Ex\WrongKeyOrModifiedCiphertextException $ex) {
91
+ // It's important to catch this and change it into a
92
+ // Ex\EnvironmentIsBrokenException, otherwise a test failure could trick
93
+ // the user into thinking it's just an invalid ciphertext!
94
+ throw new Ex\EnvironmentIsBrokenException();
95
+ }
96
+ Core::ensureTrue($decrypted === $data);
97
+
98
+ // Modifying the ciphertext: Appending a string.
99
+ try {
100
+ Crypto::decrypt($ciphertext . 'a', $key, true);
101
+ throw new Ex\EnvironmentIsBrokenException();
102
+ } catch (Ex\WrongKeyOrModifiedCiphertextException $e) { /* expected */
103
+ }
104
+
105
+ // Modifying the ciphertext: Changing an HMAC byte.
106
+ $indices_to_change = [
107
+ 0, // The header.
108
+ Core::HEADER_VERSION_SIZE + 1, // the salt
109
+ Core::HEADER_VERSION_SIZE + Core::SALT_BYTE_SIZE + 1, // the IV
110
+ Core::HEADER_VERSION_SIZE + Core::SALT_BYTE_SIZE + Core::BLOCK_BYTE_SIZE + 1, // the ciphertext
111
+ ];
112
+
113
+ foreach ($indices_to_change as $index) {
114
+ try {
115
+ $ciphertext[$index] = \chr((\ord($ciphertext[$index]) + 1) % 256);
116
+ Crypto::decrypt($ciphertext, $key, true);
117
+ throw new Ex\EnvironmentIsBrokenException();
118
+ } catch (Ex\WrongKeyOrModifiedCiphertextException $e) { /* expected */
119
+ }
120
+ }
121
+
122
+ // Decrypting with the wrong key.
123
+ $key = Key::createNewRandomKey();
124
+ $data = 'abcdef';
125
+ $ciphertext = Crypto::encrypt($data, $key, true);
126
+ $wrong_key = Key::createNewRandomKey();
127
+ try {
128
+ Crypto::decrypt($ciphertext, $wrong_key, true);
129
+ throw new Ex\EnvironmentIsBrokenException();
130
+ } catch (Ex\WrongKeyOrModifiedCiphertextException $e) { /* expected */
131
+ }
132
+
133
+ // Ciphertext too small.
134
+ $key = Key::createNewRandomKey();
135
+ $ciphertext = \str_repeat('A', Core::MINIMUM_CIPHERTEXT_SIZE - 1);
136
+ try {
137
+ Crypto::decrypt($ciphertext, $key, true);
138
+ throw new Ex\EnvironmentIsBrokenException();
139
+ } catch (Ex\WrongKeyOrModifiedCiphertextException $e) { /* expected */
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Test HKDF against test vectors.
145
+ *
146
+ * @throws Ex\EnvironmentIsBrokenException
147
+ * @return void
148
+ */
149
+ private static function HKDFTestVector()
150
+ {
151
+ // HKDF test vectors from RFC 5869
152
+
153
+ // Test Case 1
154
+ $ikm = \str_repeat("\x0b", 22);
155
+ $salt = Encoding::hexToBin('000102030405060708090a0b0c');
156
+ $info = Encoding::hexToBin('f0f1f2f3f4f5f6f7f8f9');
157
+ $length = 42;
158
+ $okm = Encoding::hexToBin(
159
+ '3cb25f25faacd57a90434f64d0362f2a' .
160
+ '2d2d0a90cf1a5a4c5db02d56ecc4c5bf' .
161
+ '34007208d5b887185865'
162
+ );
163
+ $computed_okm = Core::HKDF('sha256', $ikm, $length, $info, $salt);
164
+ Core::ensureTrue($computed_okm === $okm);
165
+
166
+ // Test Case 7
167
+ $ikm = \str_repeat("\x0c", 22);
168
+ $length = 42;
169
+ $okm = Encoding::hexToBin(
170
+ '2c91117204d745f3500d636a62f64f0a' .
171
+ 'b3bae548aa53d423b0d1f27ebba6f5e5' .
172
+ '673a081d70cce7acfc48'
173
+ );
174
+ $computed_okm = Core::HKDF('sha1', $ikm, $length, '', null);
175
+ Core::ensureTrue($computed_okm === $okm);
176
+ }
177
+
178
+ /**
179
+ * Test HMAC against test vectors.
180
+ *
181
+ * @throws Ex\EnvironmentIsBrokenException
182
+ * @return void
183
+ */
184
+ private static function HMACTestVector()
185
+ {
186
+ // HMAC test vector From RFC 4231 (Test Case 1)
187
+ $key = \str_repeat("\x0b", 20);
188
+ $data = 'Hi There';
189
+ $correct = 'b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7';
190
+ Core::ensureTrue(
191
+ \hash_hmac(Core::HASH_FUNCTION_NAME, $data, $key) === $correct
192
+ );
193
+ }
194
+
195
+ /**
196
+ * Test AES against test vectors.
197
+ *
198
+ * @throws Ex\EnvironmentIsBrokenException
199
+ * @return void
200
+ */
201
+ private static function AESTestVector()
202
+ {
203
+ // AES CTR mode test vector from NIST SP 800-38A
204
+ $key = Encoding::hexToBin(
205
+ '603deb1015ca71be2b73aef0857d7781' .
206
+ '1f352c073b6108d72d9810a30914dff4'
207
+ );
208
+ $iv = Encoding::hexToBin('f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff');
209
+ $plaintext = Encoding::hexToBin(
210
+ '6bc1bee22e409f96e93d7e117393172a' .
211
+ 'ae2d8a571e03ac9c9eb76fac45af8e51' .
212
+ '30c81c46a35ce411e5fbc1191a0a52ef' .
213
+ 'f69f2445df4f9b17ad2b417be66c3710'
214
+ );
215
+ $ciphertext = Encoding::hexToBin(
216
+ '601ec313775789a5b7a7f504bbf3d228' .
217
+ 'f443e3ca4d62b59aca84e990cacaf5c5' .
218
+ '2b0930daa23de94ce87017ba2d84988d' .
219
+ 'dfc9c58db67aada613c2dd08457941a6'
220
+ );
221
+
222
+ $computed_ciphertext = Crypto::plainEncrypt($plaintext, $key, $iv);
223
+ Core::ensureTrue($computed_ciphertext === $ciphertext);
224
+
225
+ $computed_plaintext = Crypto::plainDecrypt($ciphertext, $key, $iv, Core::CIPHER_METHOD);
226
+ Core::ensureTrue($computed_plaintext === $plaintext);
227
+ }
228
+ }
vendor/firebase/php-jwt/LICENSE ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Copyright (c) 2011, Neuman Vong
2
+
3
+ All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ * Redistributions of source code must retain the above copyright
9
+ notice, this list of conditions and the following disclaimer.
10
+
11
+ * Redistributions in binary form must reproduce the above
12
+ copyright notice, this list of conditions and the following
13
+ disclaimer in the documentation and/or other materials provided
14
+ with the distribution.
15
+
16
+ * Neither the name of Neuman Vong nor the names of other
17
+ contributors may be used to endorse or promote products derived
18
+ from this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
vendor/firebase/php-jwt/README.md ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [![Build Status](https://travis-ci.org/firebase/php-jwt.png?branch=master)](https://travis-ci.org/firebase/php-jwt)
2
+ [![Latest Stable Version](https://poser.pugx.org/firebase/php-jwt/v/stable)](https://packagist.org/packages/firebase/php-jwt)
3
+ [![Total Downloads](https://poser.pugx.org/firebase/php-jwt/downloads)](https://packagist.org/packages/firebase/php-jwt)
4
+ [![License](https://poser.pugx.org/firebase/php-jwt/license)](https://packagist.org/packages/firebase/php-jwt)
5
+
6
+ PHP-JWT
7
+ =======
8
+ A simple library to encode and decode JSON Web Tokens (JWT) in PHP, conforming to [RFC 7519](https://tools.ietf.org/html/rfc7519).
9
+
10
+ Installation
11
+ ------------
12
+
13
+ Use composer to manage your dependencies and download PHP-JWT:
14
+
15
+ ```bash
16
+ composer require firebase/php-jwt
17
+ ```
18
+
19
+ Example
20
+ -------
21
+ ```php
22
+ <?php
23
+ use \Firebase\JWT\JWT;
24
+
25
+ $key = "example_key";
26
+ $payload = array(
27
+ "iss" => "http://example.org",
28
+ "aud" => "http://example.com",
29
+ "iat" => 1356999524,
30
+ "nbf" => 1357000000
31
+ );
32
+
33
+ /**
34
+ * IMPORTANT:
35
+ * You must specify supported algorithms for your application. See
36
+ * https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40
37
+ * for a list of spec-compliant algorithms.
38
+ */
39
+ $jwt = JWT::encode($payload, $key);
40
+ $decoded = JWT::decode($jwt, $key, array('HS256'));
41
+
42
+ print_r($decoded);
43
+
44
+ /*
45
+ NOTE: This will now be an object instead of an associative array. To get
46
+ an associative array, you will need to cast it as such:
47
+ */
48
+
49
+ $decoded_array = (array) $decoded;
50
+
51
+ /**
52
+ * You can add a leeway to account for when there is a clock skew times between
53
+ * the signing and verifying servers. It is recommended that this leeway should
54
+ * not be bigger than a few minutes.
55
+ *
56
+ * Source: http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#nbfDef
57
+ */
58
+ JWT::$leeway = 60; // $leeway in seconds
59
+ $decoded = JWT::decode($jwt, $key, array('HS256'));
60
+
61
+ ?>
62
+ ```
63
+ Example with RS256 (openssl)
64
+ ----------------------------
65
+ ```php
66
+ <?php
67
+ use \Firebase\JWT\JWT;
68
+
69
+ $privateKey = <<<EOD
70
+ -----BEGIN RSA PRIVATE KEY-----
71
+ MIICXAIBAAKBgQC8kGa1pSjbSYZVebtTRBLxBz5H4i2p/llLCrEeQhta5kaQu/Rn
72
+ vuER4W8oDH3+3iuIYW4VQAzyqFpwuzjkDI+17t5t0tyazyZ8JXw+KgXTxldMPEL9
73
+ 5+qVhgXvwtihXC1c5oGbRlEDvDF6Sa53rcFVsYJ4ehde/zUxo6UvS7UrBQIDAQAB
74
+ AoGAb/MXV46XxCFRxNuB8LyAtmLDgi/xRnTAlMHjSACddwkyKem8//8eZtw9fzxz
75
+ bWZ/1/doQOuHBGYZU8aDzzj59FZ78dyzNFoF91hbvZKkg+6wGyd/LrGVEB+Xre0J
76
+ Nil0GReM2AHDNZUYRv+HYJPIOrB0CRczLQsgFJ8K6aAD6F0CQQDzbpjYdx10qgK1
77
+ cP59UHiHjPZYC0loEsk7s+hUmT3QHerAQJMZWC11Qrn2N+ybwwNblDKv+s5qgMQ5
78
+ 5tNoQ9IfAkEAxkyffU6ythpg/H0Ixe1I2rd0GbF05biIzO/i77Det3n4YsJVlDck
79
+ ZkcvY3SK2iRIL4c9yY6hlIhs+K9wXTtGWwJBAO9Dskl48mO7woPR9uD22jDpNSwe
80
+ k90OMepTjzSvlhjbfuPN1IdhqvSJTDychRwn1kIJ7LQZgQ8fVz9OCFZ/6qMCQGOb
81
+ qaGwHmUK6xzpUbbacnYrIM6nLSkXgOAwv7XXCojvY614ILTK3iXiLBOxPu5Eu13k
82
+ eUz9sHyD6vkgZzjtxXECQAkp4Xerf5TGfQXGXhxIX52yH+N2LtujCdkQZjXAsGdm
83
+ B2zNzvrlgRmgBrklMTrMYgm1NPcW+bRLGcwgW2PTvNM=
84
+ -----END RSA PRIVATE KEY-----
85
+ EOD;
86
+
87
+ $publicKey = <<<EOD
88
+ -----BEGIN PUBLIC KEY-----
89
+ MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8kGa1pSjbSYZVebtTRBLxBz5H
90
+ 4i2p/llLCrEeQhta5kaQu/RnvuER4W8oDH3+3iuIYW4VQAzyqFpwuzjkDI+17t5t
91
+ 0tyazyZ8JXw+KgXTxldMPEL95+qVhgXvwtihXC1c5oGbRlEDvDF6Sa53rcFVsYJ4
92
+ ehde/zUxo6UvS7UrBQIDAQAB
93
+ -----END PUBLIC KEY-----
94
+ EOD;
95
+
96
+ $payload = array(
97
+ "iss" => "example.org",
98
+ "aud" => "example.com",
99
+ "iat" => 1356999524,
100
+ "nbf" => 1357000000
101
+ );
102
+
103
+ $jwt = JWT::encode($payload, $privateKey, 'RS256');
104
+ echo "Encode:\n" . print_r($jwt, true) . "\n";
105
+
106
+ $decoded = JWT::decode($jwt, $publicKey, array('RS256'));
107
+
108
+ /*
109
+ NOTE: This will now be an object instead of an associative array. To get
110
+ an associative array, you will need to cast it as such:
111
+ */
112
+
113
+ $decoded_array = (array) $decoded;
114
+ echo "Decode:\n" . print_r($decoded_array, true) . "\n";
115
+ ?>
116
+ ```
117
+
118
+ Changelog
119
+ ---------
120
+
121
+ #### 5.0.0 / 2017-06-26
122
+ - Support RS384 and RS512.
123
+ See [#117](https://github.com/firebase/php-jwt/pull/117). Thanks [@joostfaassen](https://github.com/joostfaassen)!
124
+ - Add an example for RS256 openssl.
125
+ See [#125](https://github.com/firebase/php-jwt/pull/125). Thanks [@akeeman](https://github.com/akeeman)!
126
+ - Detect invalid Base64 encoding in signature.
127
+ See [#162](https://github.com/firebase/php-jwt/pull/162). Thanks [@psignoret](https://github.com/psignoret)!
128
+ - Update `JWT::verify` to handle OpenSSL errors.
129
+ See [#159](https://github.com/firebase/php-jwt/pull/159). Thanks [@bshaffer](https://github.com/bshaffer)!
130
+ - Add `array` type hinting to `decode` method
131
+ See [#101](https://github.com/firebase/php-jwt/pull/101). Thanks [@hywak](https://github.com/hywak)!
132
+ - Add all JSON error types.
133
+ See [#110](https://github.com/firebase/php-jwt/pull/110). Thanks [@gbalduzzi](https://github.com/gbalduzzi)!
134
+ - Bugfix 'kid' not in given key list.
135
+ See [#129](https://github.com/firebase/php-jwt/pull/129). Thanks [@stampycode](https://github.com/stampycode)!
136
+ - Miscellaneous cleanup, documentation and test fixes.
137
+ See [#107](https://github.com/firebase/php-jwt/pull/107), [#115](https://github.com/firebase/php-jwt/pull/115),
138
+ [#160](https://github.com/firebase/php-jwt/pull/160), [#161](https://github.com/firebase/php-jwt/pull/161), and
139
+ [#165](https://github.com/firebase/php-jwt/pull/165). Thanks [@akeeman](https://github.com/akeeman),
140
+ [@chinedufn](https://github.com/chinedufn), and [@bshaffer](https://github.com/bshaffer)!
141
+
142
+ #### 4.0.0 / 2016-07-17
143
+ - Add support for late static binding. See [#88](https://github.com/firebase/php-jwt/pull/88) for details. Thanks to [@chappy84](https://github.com/chappy84)!
144
+ - Use static `$timestamp` instead of `time()` to improve unit testing. See [#93](https://github.com/firebase/php-jwt/pull/93) for details. Thanks to [@josephmcdermott](https://github.com/josephmcdermott)!
145
+ - Fixes to exceptions classes. See [#81](https://github.com/firebase/php-jwt/pull/81) for details. Thanks to [@Maks3w](https://github.com/Maks3w)!
146
+ - Fixes to PHPDoc. See [#76](https://github.com/firebase/php-jwt/pull/76) for details. Thanks to [@akeeman](https://github.com/akeeman)!
147
+
148
+ #### 3.0.0 / 2015-07-22
149
+ - Minimum PHP version updated from `5.2.0` to `5.3.0`.
150
+ - Add `\Firebase\JWT` namespace. See
151
+ [#59](https://github.com/firebase/php-jwt/pull/59) for details. Thanks to
152
+ [@Dashron](https://github.com/Dashron)!
153
+ - Require a non-empty key to decode and verify a JWT. See
154
+ [#60](https://github.com/firebase/php-jwt/pull/60) for details. Thanks to
155
+ [@sjones608](https://github.com/sjones608)!
156
+ - Cleaner documentation blocks in the code. See
157
+ [#62](https://github.com/firebase/php-jwt/pull/62) for details. Thanks to
158
+ [@johanderuijter](https://github.com/johanderuijter)!
159
+
160
+ #### 2.2.0 / 2015-06-22
161
+ - Add support for adding custom, optional JWT headers to `JWT::encode()`. See
162
+ [#53](https://github.com/firebase/php-jwt/pull/53/files) for details. Thanks to
163
+ [@mcocaro](https://github.com/mcocaro)!
164
+
165
+ #### 2.1.0 / 2015-05-20
166
+ - Add support for adding a leeway to `JWT:decode()` that accounts for clock skew
167
+ between signing and verifying entities. Thanks to [@lcabral](https://github.com/lcabral)!
168
+ - Add support for passing an object implementing the `ArrayAccess` interface for
169
+ `$keys` argument in `JWT::decode()`. Thanks to [@aztech-dev](https://github.com/aztech-dev)!
170
+
171
+ #### 2.0.0 / 2015-04-01
172
+ - **Note**: It is strongly recommended that you update to > v2.0.0 to address
173
+ known security vulnerabilities in prior versions when both symmetric and
174
+ asymmetric keys are used together.
175
+ - Update signature for `JWT::decode(...)` to require an array of supported
176
+ algorithms to use when verifying token signatures.
177
+
178
+
179
+ Tests
180
+ -----
181
+ Run the tests using phpunit:
182
+
183
+ ```bash
184
+ $ pear install PHPUnit
185
+ $ phpunit --configuration phpunit.xml.dist
186
+ PHPUnit 3.7.10 by Sebastian Bergmann.
187
+ .....
188
+ Time: 0 seconds, Memory: 2.50Mb
189
+ OK (5 tests, 5 assertions)
190
+ ```
191
+
192
+ New Lines in private keys
193
+ -----
194
+
195
+ If your private key contains `\n` characters, be sure to wrap it in double quotes `""`
196
+ and not single quotes `''` in order to properly interpret the escaped characters.
197
+
198
+ License
199
+ -------
200
+ [3-Clause BSD](http://opensource.org/licenses/BSD-3-Clause).
vendor/firebase/php-jwt/composer.json ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "firebase/php-jwt",
3
+ "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
4
+ "homepage": "https://github.com/firebase/php-jwt",
5
+ "keywords": [
6
+ "php",
7
+ "jwt"
8
+ ],
9
+ "authors": [
10
+ {
11
+ "name": "Neuman Vong",
12
+ "email": "neuman+pear@twilio.com",
13
+ "role": "Developer"
14
+ },
15
+ {
16
+ "name": "Anant Narayanan",
17
+ "email": "anant@php.net",
18
+ "role": "Developer"
19
+ }
20
+ ],
21
+ "license": "BSD-3-Clause",
22
+ "require": {
23
+ "php": ">=5.3.0"
24
+ },
25
+ "autoload": {
26
+ "psr-4": {
27
+ "Firebase\\JWT\\": "src"
28
+ }
29
+ },
30
+ "require-dev": {
31
+ "phpunit/phpunit": ">=4.8 <=9"
32
+ }
33
+ }
vendor/firebase/php-jwt/src/BeforeValidException.php ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <?php
2
+ namespace Firebase\JWT;
3
+
4
+ class BeforeValidException extends \UnexpectedValueException
5
+ {
6
+ }
vendor/firebase/php-jwt/src/ExpiredException.php ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <?php
2
+ namespace Firebase\JWT;
3
+
4
+ class ExpiredException extends \UnexpectedValueException
5
+ {
6
+ }
vendor/firebase/php-jwt/src/JWK.php ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Firebase\JWT;
4
+
5
+ use DomainException;
6
+ use UnexpectedValueException;
7
+
8
+ /**
9
+ * JSON Web Key implementation, based on this spec:
10
+ * https://tools.ietf.org/html/draft-ietf-jose-json-web-key-41
11
+ *
12
+ * PHP version 5
13
+ *
14
+ * @category Authentication
15
+ * @package Authentication_JWT
16
+ * @author Bui Sy Nguyen <nguyenbs@gmail.com>
17
+ * @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
18
+ * @link https://github.com/firebase/php-jwt
19
+ */
20
+ class JWK
21
+ {
22
+ /**
23
+ * Parse a set of JWK keys
24
+ *
25
+ * @param array $jwks The JSON Web Key Set as an associative array
26
+ *
27
+ * @return array An associative array that represents the set of keys
28
+ *
29
+ * @throws InvalidArgumentException Provided JWK Set is empty
30
+ * @throws UnexpectedValueException Provided JWK Set was invalid
31
+ * @throws DomainException OpenSSL failure
32
+ *
33
+ * @uses parseKey
34
+ */
35
+ public static function parseKeySet(array $jwks)
36
+ {
37
+ $keys = array();
38
+
39
+ if (!isset($jwks['keys'])) {
40
+ throw new UnexpectedValueException('"keys" member must exist in the JWK Set');
41
+ }
42
+ if (empty($jwks['keys'])) {
43
+ throw new InvalidArgumentException('JWK Set did not contain any keys');
44
+ }
45
+
46
+ foreach ($jwks['keys'] as $k => $v) {
47
+ $kid = isset($v['kid']) ? $v['kid'] : $k;
48
+ if ($key = self::parseKey($v)) {
49
+ $keys[$kid] = $key;
50
+ }
51
+ }
52
+
53
+ if (0 === \count($keys)) {
54
+ throw new UnexpectedValueException('No supported algorithms found in JWK Set');
55
+ }
56
+
57
+ return $keys;
58
+ }
59
+
60
+ /**
61
+ * Parse a JWK key
62
+ *
63
+ * @param array $jwk An individual JWK
64
+ *
65
+ * @return resource|array An associative array that represents the key
66
+ *
67
+ * @throws InvalidArgumentException Provided JWK is empty
68
+ * @throws UnexpectedValueException Provided JWK was invalid
69
+ * @throws DomainException OpenSSL failure
70
+ *
71
+ * @uses createPemFromModulusAndExponent
72
+ */
73
+ private static function parseKey(array $jwk)
74
+ {
75
+ if (empty($jwk)) {
76
+ throw new InvalidArgumentException('JWK must not be empty');
77
+ }
78
+ if (!isset($jwk['kty'])) {
79
+ throw new UnexpectedValueException('JWK must contain a "kty" parameter');
80
+ }
81
+
82
+ switch ($jwk['kty']) {
83
+ case 'RSA':
84
+ if (\array_key_exists('d', $jwk)) {
85
+ throw new UnexpectedValueException('RSA private keys are not supported');
86
+ }
87
+ if (!isset($jwk['n']) || !isset($jwk['e'])) {
88
+ throw new UnexpectedValueException('RSA keys must contain values for both "n" and "e"');
89
+ }
90
+
91
+ $pem = self::createPemFromModulusAndExponent($jwk['n'], $jwk['e']);
92
+ $publicKey = \openssl_pkey_get_public($pem);
93
+ if (false === $publicKey) {
94
+ throw new DomainException(
95
+ 'OpenSSL error: ' . \openssl_error_string()
96
+ );
97
+ }
98
+ return $publicKey;
99
+ default:
100
+ // Currently only RSA is supported
101
+ break;
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Create a public key represented in PEM format from RSA modulus and exponent information
107
+ *
108
+ * @param string $n The RSA modulus encoded in Base64
109
+ * @param string $e The RSA exponent encoded in Base64
110
+ *
111
+ * @return string The RSA public key represented in PEM format
112
+ *
113
+ * @uses encodeLength
114
+ */
115
+ private static function createPemFromModulusAndExponent($n, $e)
116
+ {
117
+ $modulus = JWT::urlsafeB64Decode($n);
118
+ $publicExponent = JWT::urlsafeB64Decode($e);
119
+
120
+ $components = array(
121
+ 'modulus' => \pack('Ca*a*', 2, self::encodeLength(\strlen($modulus)), $modulus),
122
+ 'publicExponent' => \pack('Ca*a*', 2, self::encodeLength(\strlen($publicExponent)), $publicExponent)
123
+ );
124
+
125
+ $rsaPublicKey = \pack(
126
+ 'Ca*a*a*',
127
+ 48,
128
+ self::encodeLength(\strlen($components['modulus']) + \strlen($components['publicExponent'])),
129
+ $components['modulus'],
130
+ $components['publicExponent']
131
+ );
132
+
133
+ // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
134
+ $rsaOID = \pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
135
+ $rsaPublicKey = \chr(0) . $rsaPublicKey;
136
+ $rsaPublicKey = \chr(3) . self::encodeLength(\strlen($rsaPublicKey)) . $rsaPublicKey;
137
+
138
+ $rsaPublicKey = \pack(
139
+ 'Ca*a*',
140
+ 48,
141
+ self::encodeLength(\strlen($rsaOID . $rsaPublicKey)),
142
+ $rsaOID . $rsaPublicKey
143
+ );
144
+
145
+ $rsaPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
146
+ \chunk_split(\base64_encode($rsaPublicKey), 64) .
147
+ '-----END PUBLIC KEY-----';
148
+
149
+ return $rsaPublicKey;
150
+ }
151
+
152
+ /**
153
+ * DER-encode the length
154
+ *
155
+ * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
156
+ * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
157
+ *
158
+ * @param int $length
159
+ * @return string
160
+ */
161
+ private static function encodeLength($length)
162
+ {
163
+ if ($length <= 0x7F) {
164
+ return \chr($length);
165
+ }
166
+
167
+ $temp = \ltrim(\pack('N', $length), \chr(0));
168
+
169
+ return \pack('Ca*', 0x80 | \strlen($temp), $temp);
170
+ }
171
+ }
vendor/firebase/php-jwt/src/JWT.php ADDED
@@ -0,0 +1,512 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Firebase\JWT;
4
+
5
+ use \DomainException;
6
+ use \InvalidArgumentException;
7
+ use \UnexpectedValueException;
8
+ use \DateTime;
9
+
10
+ /**
11
+ * JSON Web Token implementation, based on this spec:
12
+ * https://tools.ietf.org/html/rfc7519
13
+ *
14
+ * PHP version 5
15
+ *
16
+ * @category Authentication
17
+ * @package Authentication_JWT
18
+ * @author Neuman Vong <neuman@twilio.com>
19
+ * @author Anant Narayanan <anant@php.net>
20
+ * @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
21
+ * @link https://github.com/firebase/php-jwt
22
+ */
23
+ class JWT
24
+ {
25
+ const ASN1_INTEGER = 0x02;
26
+ const ASN1_SEQUENCE = 0x10;
27
+ const ASN1_BIT_STRING = 0x03;
28
+
29
+ /**
30
+ * When checking nbf, iat or expiration times,
31
+ * we want to provide some extra leeway time to
32
+ * account for clock skew.
33
+ */
34
+ public static $leeway = 0;
35
+
36
+ /**
37
+ * Allow the current timestamp to be specified.
38
+ * Useful for fixing a value within unit testing.
39
+ *
40
+ * Will default to PHP time() value if null.
41
+ */
42
+ public static $timestamp = null;
43
+
44
+ public static $supported_algs = array(
45
+ 'ES256' => array('openssl', 'SHA256'),
46
+ 'HS256' => array('hash_hmac', 'SHA256'),
47
+ 'HS384' => array('hash_hmac', 'SHA384'),
48
+ 'HS512' => array('hash_hmac', 'SHA512'),
49
+ 'RS256' => array('openssl', 'SHA256'),
50
+ 'RS384' => array('openssl', 'SHA384'),
51
+ 'RS512' => array('openssl', 'SHA512'),
52
+ );
53
+
54
+ /**
55
+ * Decodes a JWT string into a PHP object.
56
+ *
57
+ * @param string $jwt The JWT
58
+ * @param string|array|resource $key The key, or map of keys.
59
+ * If the algorithm used is asymmetric, this is the public key
60
+ * @param array $allowed_algs List of supported verification algorithms
61
+ * Supported algorithms are 'ES256', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
62
+ *
63
+ * @return object The JWT's payload as a PHP object
64
+ *
65
+ * @throws UnexpectedValueException Provided JWT was invalid
66
+ * @throws SignatureInvalidException Provided JWT was invalid because the signature verification failed
67
+ * @throws BeforeValidException Provided JWT is trying to be used before it's eligible as defined by 'nbf'
68
+ * @throws BeforeValidException Provided JWT is trying to be used before it's been created as defined by 'iat'
69
+ * @throws ExpiredException Provided JWT has since expired, as defined by the 'exp' claim
70
+ *
71
+ * @uses jsonDecode
72
+ * @uses urlsafeB64Decode
73
+ */
74
+ public static function decode($jwt, $key, array $allowed_algs = array())
75
+ {
76
+ $timestamp = \is_null(static::$timestamp) ? \time() : static::$timestamp;
77
+
78
+ if (empty($key)) {
79
+ throw new InvalidArgumentException('Key may not be empty');
80
+ }
81
+ $tks = \explode('.', $jwt);
82
+ if (\count($tks) != 3) {
83
+ throw new UnexpectedValueException('Wrong number of segments');
84
+ }
85
+ list($headb64, $bodyb64, $cryptob64) = $tks;
86
+ if (null === ($header = static::jsonDecode(static::urlsafeB64Decode($headb64)))) {
87
+ throw new UnexpectedValueException('Invalid header encoding');
88
+ }
89
+ if (null === $payload = static::jsonDecode(static::urlsafeB64Decode($bodyb64))) {
90
+ throw new UnexpectedValueException('Invalid claims encoding');
91
+ }
92
+ if (false === ($sig = static::urlsafeB64Decode($cryptob64))) {
93
+ throw new UnexpectedValueException('Invalid signature encoding');
94
+ }
95
+ if (empty($header->alg)) {
96
+ throw new UnexpectedValueException('Empty algorithm');
97
+ }
98
+ if (empty(static::$supported_algs[$header->alg])) {
99
+ throw new UnexpectedValueException('Algorithm not supported');
100
+ }
101
+ if (!\in_array($header->alg, $allowed_algs)) {
102
+ throw new UnexpectedValueException('Algorithm not allowed');
103
+ }
104
+ if ($header->alg === 'ES256') {
105
+ // OpenSSL expects an ASN.1 DER sequence for ES256 signatures
106
+ $sig = self::signatureToDER($sig);
107
+ }
108
+
109
+ if (\is_array($key) || $key instanceof \ArrayAccess) {
110
+ if (isset($header->kid)) {
111
+ if (!isset($key[$header->kid])) {
112
+ throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
113
+ }
114
+ $key = $key[$header->kid];
115
+ } else {
116
+ throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
117
+ }
118
+ }
119
+
120
+ // Check the signature
121
+ if (!static::verify("$headb64.$bodyb64", $sig, $key, $header->alg)) {
122
+ throw new SignatureInvalidException('Signature verification failed');
123
+ }
124
+
125
+ // Check the nbf if it is defined. This is the time that the
126
+ // token can actually be used. If it's not yet that time, abort.
127
+ if (isset($payload->nbf) && $payload->nbf > ($timestamp + static::$leeway)) {
128
+ throw new BeforeValidException(
129
+ 'Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->nbf)
130
+ );
131
+ }
132
+
133
+ // Check that this token has been created before 'now'. This prevents
134
+ // using tokens that have been created for later use (and haven't
135
+ // correctly used the nbf claim).
136
+ if (isset($payload->iat) && $payload->iat > ($timestamp + static::$leeway)) {
137
+ throw new BeforeValidException(
138
+ 'Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->iat)
139
+ );
140
+ }
141
+
142
+ // Check if this token has expired.
143
+ if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) {
144
+ throw new ExpiredException('Expired token');
145
+ }
146
+
147
+ return $payload;
148
+ }
149
+
150
+ /**
151
+ * Converts and signs a PHP object or array into a JWT string.
152
+ *
153
+ * @param object|array $payload PHP object or array
154
+ * @param string $key The secret key.
155
+ * If the algorithm used is asymmetric, this is the private key
156
+ * @param string $alg The signing algorithm.
157
+ * Supported algorithms are 'ES256', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
158
+ * @param mixed $keyId
159
+ * @param array $head An array with header elements to attach
160
+ *
161
+ * @return string A signed JWT
162
+ *
163
+ * @uses jsonEncode
164
+ * @uses urlsafeB64Encode
165
+ */
166
+ public static function encode($payload, $key, $alg = 'HS256', $keyId = null, $head = null)
167
+ {
168
+ $header = array('typ' => 'JWT', 'alg' => $alg);
169
+ if ($keyId !== null) {
170
+ $header['kid'] = $keyId;
171
+ }
172
+ if (isset($head) && \is_array($head)) {
173
+ $header = \array_merge($head, $header);
174
+ }
175
+ $segments = array();
176
+ $segments[] = static::urlsafeB64Encode(static::jsonEncode($header));
177
+ $segments[] = static::urlsafeB64Encode(static::jsonEncode($payload));
178
+ $signing_input = \implode('.', $segments);
179
+
180
+ $signature = static::sign($signing_input, $key, $alg);
181
+ $segments[] = static::urlsafeB64Encode($signature);
182
+
183
+ return \implode('.', $segments);
184
+ }
185
+
186
+ /**
187
+ * Sign a string with a given key and algorithm.
188
+ *
189
+ * @param string $msg The message to sign
190
+ * @param string|resource $key The secret key
191
+ * @param string $alg The signing algorithm.
192
+ * Supported algorithms are 'ES256', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
193
+ *
194
+ * @return string An encrypted message
195
+ *
196
+ * @throws DomainException Unsupported algorithm was specified
197
+ */
198
+ public static function sign($msg, $key, $alg = 'HS256')
199
+ {
200
+ if (empty(static::$supported_algs[$alg])) {
201
+ throw new DomainException('Algorithm not supported');
202
+ }
203
+ list($function, $algorithm) = static::$supported_algs[$alg];
204
+ switch ($function) {
205
+ case 'hash_hmac':
206
+ return \hash_hmac($algorithm, $msg, $key, true);
207
+ case 'openssl':
208
+ $signature = '';
209
+ $success = \openssl_sign($msg, $signature, $key, $algorithm);
210
+ if (!$success) {
211
+ throw new DomainException("OpenSSL unable to sign data");
212
+ } else {
213
+ if ($alg === 'ES256') {
214
+ $signature = self::signatureFromDER($signature, 256);
215
+ }
216
+ return $signature;
217
+ }
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Verify a signature with the message, key and method. Not all methods
223
+ * are symmetric, so we must have a separate verify and sign method.
224
+ *
225
+ * @param string $msg The original message (header and body)
226
+ * @param string $signature The original signature
227
+ * @param string|resource $key For HS*, a string key works. for RS*, must be a resource of an openssl public key
228
+ * @param string $alg The algorithm
229
+ *
230
+ * @return bool
231
+ *
232
+ * @throws DomainException Invalid Algorithm or OpenSSL failure
233
+ */
234
+ private static function verify($msg, $signature, $key, $alg)
235
+ {
236
+ if (empty(static::$supported_algs[$alg])) {
237
+ throw new DomainException('Algorithm not supported');
238
+ }
239
+
240
+ list($function, $algorithm) = static::$supported_algs[$alg];
241
+ switch ($function) {
242
+ case 'openssl':
243
+ $success = \openssl_verify($msg, $signature, $key, $algorithm);
244
+ if ($success === 1) {
245
+ return true;
246
+ } elseif ($success === 0) {
247
+ return false;
248
+ }
249
+ // returns 1 on success, 0 on failure, -1 on error.
250
+ throw new DomainException(
251
+ 'OpenSSL error: ' . \openssl_error_string()
252
+ );
253
+ case 'hash_hmac':
254
+ default:
255
+ $hash = \hash_hmac($algorithm, $msg, $key, true);
256
+ if (\function_exists('hash_equals')) {
257
+ return \hash_equals($signature, $hash);
258
+ }
259
+ $len = \min(static::safeStrlen($signature), static::safeStrlen($hash));
260
+
261
+ $status = 0;
262
+ for ($i = 0; $i < $len; $i++) {
263
+ $status |= (\ord($signature[$i]) ^ \ord($hash[$i]));
264
+ }
265
+ $status |= (static::safeStrlen($signature) ^ static::safeStrlen($hash));
266
+
267
+ return ($status === 0);
268
+ }
269
+ }
270
+
271
+ /**
272
+ * Decode a JSON string into a PHP object.
273
+ *
274
+ * @param string $input JSON string
275
+ *
276
+ * @return object Object representation of JSON string
277
+ *
278
+ * @throws DomainException Provided string was invalid JSON
279
+ */
280
+ public static function jsonDecode($input)
281
+ {
282
+ if (\version_compare(PHP_VERSION, '5.4.0', '>=') && !(\defined('JSON_C_VERSION') && PHP_INT_SIZE > 4)) {
283
+ /** In PHP >=5.4.0, json_decode() accepts an options parameter, that allows you
284
+ * to specify that large ints (like Steam Transaction IDs) should be treated as
285
+ * strings, rather than the PHP default behaviour of converting them to floats.
286
+ */
287
+ $obj = \json_decode($input, false, 512, JSON_BIGINT_AS_STRING);
288
+ } else {
289
+ /** Not all servers will support that, however, so for older versions we must
290
+ * manually detect large ints in the JSON string and quote them (thus converting
291
+ *them to strings) before decoding, hence the preg_replace() call.
292
+ */
293
+ $max_int_length = \strlen((string) PHP_INT_MAX) - 1;
294
+ $json_without_bigints = \preg_replace('/:\s*(-?\d{'.$max_int_length.',})/', ': "$1"', $input);
295
+ $obj = \json_decode($json_without_bigints);
296
+ }
297
+
298
+ if ($errno = \json_last_error()) {
299
+ static::handleJsonError($errno);
300
+ } elseif ($obj === null && $input !== 'null') {
301
+ throw new DomainException('Null result with non-null input');
302
+ }
303
+ return $obj;
304
+ }
305
+
306
+ /**
307
+ * Encode a PHP object into a JSON string.
308
+ *
309
+ * @param object|array $input A PHP object or array
310
+ *
311
+ * @return string JSON representation of the PHP object or array
312
+ *
313
+ * @throws DomainException Provided object could not be encoded to valid JSON
314
+ */
315
+ public static function jsonEncode($input)
316
+ {
317
+ $json = \json_encode($input);
318
+ if ($errno = \json_last_error()) {
319
+ static::handleJsonError($errno);
320
+ } elseif ($json === 'null' && $input !== null) {
321
+ throw new DomainException('Null result with non-null input');
322
+ }
323
+ return $json;
324
+ }
325
+
326
+ /**
327
+ * Decode a string with URL-safe Base64.
328
+ *
329
+ * @param string $input A Base64 encoded string
330
+ *
331
+ * @return string A decoded string
332
+ */
333
+ public static function urlsafeB64Decode($input)
334
+ {
335
+ $remainder = \strlen($input) % 4;
336
+ if ($remainder) {
337
+ $padlen = 4 - $remainder;
338
+ $input .= \str_repeat('=', $padlen);
339
+ }
340
+ return \base64_decode(\strtr($input, '-_', '+/'));
341
+ }
342
+
343
+ /**
344
+ * Encode a string with URL-safe Base64.
345
+ *
346
+ * @param string $input The string you want encoded
347
+ *
348
+ * @return string The base64 encode of what you passed in
349
+ */
350
+ public static function urlsafeB64Encode($input)
351
+ {
352
+ return \str_replace('=', '', \strtr(\base64_encode($input), '+/', '-_'));
353
+ }
354
+
355
+ /**
356
+ * Helper method to create a JSON error.
357
+ *
358
+ * @param int $errno An error number from json_last_error()
359
+ *
360
+ * @return void
361
+ */
362
+ private static function handleJsonError($errno)
363
+ {
364
+ $messages = array(
365
+ JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
366
+ JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON',
367
+ JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
368
+ JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON',
369
+ JSON_ERROR_UTF8 => 'Malformed UTF-8 characters' //PHP >= 5.3.3
370
+ );
371
+ throw new DomainException(
372
+ isset($messages[$errno])
373
+ ? $messages[$errno]
374
+ : 'Unknown JSON error: ' . $errno
375
+ );
376
+ }
377
+
378
+ /**
379
+ * Get the number of bytes in cryptographic strings.
380
+ *
381
+ * @param string $str
382
+ *
383
+ * @return int
384
+ */
385
+ private static function safeStrlen($str)
386
+ {
387
+ if (\function_exists('mb_strlen')) {
388
+ return \mb_strlen($str, '8bit');
389
+ }
390
+ return \strlen($str);
391
+ }
392
+
393
+ /**
394
+ * Convert an ECDSA signature to an ASN.1 DER sequence
395
+ *
396
+ * @param string $sig The ECDSA signature to convert
397
+ * @return string The encoded DER object
398
+ */
399
+ private static function signatureToDER($sig)
400
+ {
401
+ // Separate the signature into r-value and s-value
402
+ list($r, $s) = \str_split($sig, (int) (\strlen($sig) / 2));
403
+
404
+ // Trim leading zeros
405
+ $r = \ltrim($r, "\x00");
406
+ $s = \ltrim($s, "\x00");
407
+
408
+ // Convert r-value and s-value from unsigned big-endian integers to
409
+ // signed two's complement
410
+ if (\ord($r[0]) > 0x7f) {
411
+ $r = "\x00" . $r;
412
+ }
413
+ if (\ord($s[0]) > 0x7f) {
414
+ $s = "\x00" . $s;
415
+ }
416
+
417
+ return self::encodeDER(
418
+ self::ASN1_SEQUENCE,
419
+ self::encodeDER(self::ASN1_INTEGER, $r) .
420
+ self::encodeDER(self::ASN1_INTEGER, $s)
421
+ );
422
+ }
423
+
424
+ /**
425
+ * Encodes a value into a DER object.
426
+ *
427
+ * @param int $type DER tag
428
+ * @param string $value the value to encode
429
+ * @return string the encoded object
430
+ */
431
+ private static function encodeDER($type, $value)
432
+ {
433
+ $tag_header = 0;
434
+ if ($type === self::ASN1_SEQUENCE) {
435
+ $tag_header |= 0x20;
436
+ }
437
+
438
+ // Type
439
+ $der = \chr($tag_header | $type);
440
+
441
+ // Length
442
+ $der .= \chr(\strlen($value));
443
+
444
+ return $der . $value;
445
+ }
446
+
447
+ /**
448
+ * Encodes signature from a DER object.
449
+ *
450
+ * @param string $der binary signature in DER format
451
+ * @param int $keySize the number of bits in the key
452
+ * @return string the signature
453
+ */
454
+ private static function signatureFromDER($der, $keySize)
455
+ {
456
+ // OpenSSL returns the ECDSA signatures as a binary ASN.1 DER SEQUENCE
457
+ list($offset, $_) = self::readDER($der);
458
+ list($offset, $r) = self::readDER($der, $offset);
459
+ list($offset, $s) = self::readDER($der, $offset);
460
+
461
+ // Convert r-value and s-value from signed two's compliment to unsigned
462
+ // big-endian integers
463
+ $r = \ltrim($r, "\x00");
464
+ $s = \ltrim($s, "\x00");
465
+
466
+ // Pad out r and s so that they are $keySize bits long
467
+ $r = \str_pad($r, $keySize / 8, "\x00", STR_PAD_LEFT);
468
+ $s = \str_pad($s, $keySize / 8, "\x00", STR_PAD_LEFT);
469
+
470
+ return $r . $s;
471
+ }
472
+
473
+ /**
474
+ * Reads binary DER-encoded data and decodes into a single object
475
+ *
476
+ * @param string $der the binary data in DER format
477
+ * @param int $offset the offset of the data stream containing the object
478
+ * to decode
479
+ * @return array [$offset, $data] the new offset and the decoded object
480
+ */
481
+ private static function readDER($der, $offset = 0)
482
+ {
483
+ $pos = $offset;
484
+ $size = \strlen($der);
485
+ $constructed = (\ord($der[$pos]) >> 5) & 0x01;
486
+ $type = \ord($der[$pos++]) & 0x1f;
487
+
488
+ // Length
489
+ $len = \ord($der[$pos++]);
490
+ if ($len & 0x80) {
491
+ $n = $len & 0x1f;
492
+ $len = 0;
493
+ while ($n-- && $pos < $size) {
494
+ $len = ($len << 8) | \ord($der[$pos++]);
495
+ }
496
+ }
497
+
498
+ // Value
499
+ if ($type == self::ASN1_BIT_STRING) {
500
+ $pos++; // Skip the first contents octet (padding indicator)
501
+ $data = \substr($der, $pos, $len - 1);
502
+ $pos += $len - 1;
503
+ } elseif (!$constructed) {
504
+ $data = \substr($der, $pos, $len);
505
+ $pos += $len;
506
+ } else {
507
+ $data = null;
508
+ }
509
+
510
+ return array($pos, $data);
511
+ }
512
+ }
vendor/firebase/php-jwt/src/SignatureInvalidException.php ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <?php
2
+ namespace Firebase\JWT;
3
+
4
+ class SignatureInvalidException extends \UnexpectedValueException
5
+ {
6
+ }
vendor/paragonie/random_compat/LICENSE ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Paragon Initiative Enterprises
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
vendor/paragonie/random_compat/build-phar.sh ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+
3
+ basedir=$( dirname $( readlink -f ${BASH_SOURCE[0]} ) )
4
+
5
+ php -dphar.readonly=0 "$basedir/other/build_phar.php" $*
vendor/paragonie/random_compat/composer.json ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "paragonie/random_compat",
3
+ "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
4
+ "keywords": [
5
+ "csprng",
6
+ "random",
7
+ "polyfill",
8
+ "pseudorandom"
9
+ ],
10
+ "license": "MIT",
11
+ "type": "library",
12
+ "authors": [
13
+ {
14
+ "name": "Paragon Initiative Enterprises",
15
+ "email": "security@paragonie.com",
16
+ "homepage": "https://paragonie.com"
17
+ }
18
+ ],
19
+ "support": {
20
+ "issues": "https://github.com/paragonie/random_compat/issues",
21
+ "email": "info@paragonie.com",
22
+ "source": "https://github.com/paragonie/random_compat"
23
+ },
24
+ "require": {
25
+ "php": "^7"
26
+ },
27
+ "require-dev": {
28
+ "vimeo/psalm": "^1",
29
+ "phpunit/phpunit": "4.*|5.*"
30
+ },
31
+ "suggest": {
32
+ "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
33
+ }
34
+ }
vendor/paragonie/random_compat/dist/random_compat.phar.pubkey ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ -----BEGIN PUBLIC KEY-----
2
+ MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEEd+wCqJDrx5B4OldM0dQE0ZMX+lx1ZWm
3
+ pui0SUqD4G29L3NGsz9UhJ/0HjBdbnkhIK5xviT0X5vtjacF6ajgcCArbTB+ds+p
4
+ +h7Q084NuSuIpNb6YPfoUFgC/CL9kAoc
5
+ -----END PUBLIC KEY-----
vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ -----BEGIN PGP SIGNATURE-----
2
+ Version: GnuPG v2.0.22 (MingW32)
3
+
4
+ iQEcBAABAgAGBQJWtW1hAAoJEGuXocKCZATaJf0H+wbZGgskK1dcRTsuVJl9IWip
5
+ QwGw/qIKI280SD6/ckoUMxKDCJiFuPR14zmqnS36k7N5UNPnpdTJTS8T11jttSpg
6
+ 1LCmgpbEIpgaTah+cELDqFCav99fS+bEiAL5lWDAHBTE/XPjGVCqeehyPYref4IW
7
+ NDBIEsvnHPHPLsn6X5jq4+Yj5oUixgxaMPiR+bcO4Sh+RzOVB6i2D0upWfRXBFXA
8
+ NNnsg9/zjvoC7ZW73y9uSH+dPJTt/Vgfeiv52/v41XliyzbUyLalf02GNPY+9goV
9
+ JHG1ulEEBJOCiUD9cE1PUIJwHA/HqyhHIvV350YoEFiHl8iSwm7SiZu5kPjaq74=
10
+ =B6+8
11
+ -----END PGP SIGNATURE-----
vendor/paragonie/random_compat/lib/random.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Random_* Compatibility Library
4
+ * for using the new PHP 7 random_* API in PHP 5 projects
5
+ *
6
+ * @version 2.99.99
7
+ * @released 2018-06-06
8
+ *
9
+ * The MIT License (MIT)
10
+ *
11
+ * Copyright (c) 2015 - 2018 Paragon Initiative Enterprises
12
+ *
13
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ * of this software and associated documentation files (the "Software"), to deal
15
+ * in the Software without restriction, including without limitation the rights
16
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ * copies of the Software, and to permit persons to whom the Software is
18
+ * furnished to do so, subject to the following conditions:
19
+ *
20
+ * The above copyright notice and this permission notice shall be included in
21
+ * all copies or substantial portions of the Software.
22
+ *
23
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ * SOFTWARE.
30
+ */
31
+
32
+ // NOP
vendor/paragonie/random_compat/other/build_phar.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $dist = dirname(__DIR__).'/dist';
3
+ if (!is_dir($dist)) {
4
+ mkdir($dist, 0755);
5
+ }
6
+ if (file_exists($dist.'/random_compat.phar')) {
7
+ unlink($dist.'/random_compat.phar');
8
+ }
9
+ $phar = new Phar(
10
+ $dist.'/random_compat.phar',
11
+ FilesystemIterator::CURRENT_AS_FILEINFO | \FilesystemIterator::KEY_AS_FILENAME,
12
+ 'random_compat.phar'
13
+ );
14
+ rename(
15
+ dirname(__DIR__).'/lib/random.php',
16
+ dirname(__DIR__).'/lib/index.php'
17
+ );
18
+ $phar->buildFromDirectory(dirname(__DIR__).'/lib');
19
+ rename(
20
+ dirname(__DIR__).'/lib/index.php',
21
+ dirname(__DIR__).'/lib/random.php'
22
+ );
23
+
24
+ /**
25
+ * If we pass an (optional) path to a private key as a second argument, we will
26
+ * sign the Phar with OpenSSL.
27
+ *
28
+ * If you leave this out, it will produce an unsigned .phar!
29
+ */
30
+ if ($argc > 1) {
31
+ if (!@is_readable($argv[1])) {
32
+ echo 'Could not read the private key file:', $argv[1], "\n";
33
+ exit(255);
34
+ }
35
+ $pkeyFile = file_get_contents($argv[1]);
36
+
37
+ $private = openssl_get_privatekey($pkeyFile);
38
+ if ($private !== false) {
39
+ $pkey = '';
40
+ openssl_pkey_export($private, $pkey);
41
+ $phar->setSignatureAlgorithm(Phar::OPENSSL, $pkey);
42
+
43
+ /**
44
+ * Save the corresponding public key to the file
45
+ */
46
+ if (!@is_readable($dist.'/random_compat.phar.pubkey')) {
47
+ $details = openssl_pkey_get_details($private);
48
+ file_put_contents(
49
+ $dist.'/random_compat.phar.pubkey',
50
+ $details['key']
51
+ );
52
+ }
53
+ } else {
54
+ echo 'An error occurred reading the private key from OpenSSL.', "\n";
55
+ exit(255);
56
+ }
57
+ }
vendor/paragonie/random_compat/psalm-autoload.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once 'lib/byte_safe_strings.php';
4
+ require_once 'lib/cast_to_int.php';
5
+ require_once 'lib/error_polyfill.php';
6
+ require_once 'other/ide_stubs/libsodium.php';
7
+ require_once 'lib/random.php';
8
+
9
+ $int = random_int(0, 65536);
vendor/paragonie/random_compat/psalm.xml ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <psalm
3
+ autoloader="psalm-autoload.php"
4
+ stopOnFirstError="false"
5
+ useDocblockTypes="true"
6
+ >
7
+ <projectFiles>
8
+ <directory name="lib" />
9
+ </projectFiles>
10
+ <issueHandlers>
11
+ <RedundantConditionGivenDocblockType errorLevel="info" />
12
+ <UnresolvableInclude errorLevel="info" />
13
+ <DuplicateClass errorLevel="info" />
14
+ <InvalidOperand errorLevel="info" />
15
+ <UndefinedConstant errorLevel="info" />
16
+ <MissingReturnType errorLevel="info" />
17
+ <InvalidReturnType errorLevel="info" />
18
+ </issueHandlers>
19
+ </psalm>