Version Description
Download this release
Release Info
Developer | constantcontact |
Plugin | Creative Mail – Easier WordPress & WooCommerce Email Marketing |
Version | 1.0.0 |
Comparing to | |
See all releases |
Version 1.0.0
- CHANGELOG.md +6 -0
- LICENSE +339 -0
- README.md +37 -0
- assets/css/admin.css +627 -0
- assets/images/icon.svg +3 -0
- assets/images/logo.svg +18 -0
- assets/images/wp-plugin-marketing.png +0 -0
- composer.json +21 -0
- composer.lock +180 -0
- creative-mail-plugin.php +50 -0
- readme.txt +86 -0
- src/constants/environment-names.php +9 -0
- src/creativemail.php +71 -0
- src/helpers/encryption-helper.php +83 -0
- src/helpers/environment-helper.php +73 -0
- src/helpers/guid-helper.php +18 -0
- src/helpers/options-helper.php +174 -0
- src/helpers/sso-helper.php +61 -0
- src/integrations/integration.php +66 -0
- src/managers/admin-manager.php +143 -0
- src/managers/api-manager.php +214 -0
- src/managers/instance-manager.php +54 -0
- src/managers/integration-manager.php +150 -0
- src/modules/blog/models/BlogAttachment.php +21 -0
- src/modules/blog/models/BlogInformation.php +37 -0
- src/modules/blog/models/BlogPost.php +41 -0
- src/modules/contacts/Handlers/BaseContactFormPluginHandler.php +56 -0
- src/modules/contacts/Handlers/ContactFormSevenPluginHandler.php +156 -0
- src/modules/contacts/Handlers/NewsLetterContactFormPluginHandler.php +124 -0
- src/modules/contacts/Handlers/WooCommercePluginHandler.php +121 -0
- src/modules/contacts/Handlers/WpFormsLitePluginHandler.php +82 -0
- src/modules/contacts/Services/ContactsSyncService.php +115 -0
- src/modules/contacts/models/ContactAddressModel.php +81 -0
- src/modules/contacts/models/ContactFormSevenSubmission.php +11 -0
- src/modules/contacts/models/ContactModel.php +130 -0
- src/modules/contacts/models/OptActionBy.php +11 -0
- src/modules/woocommerce/models/WCProductModel.php +42 -0
- src/modules/woocommerce/models/WCStoreInformation.php +34 -0
- src/views/activated-integrations.php +32 -0
- src/views/available-integrations.php +19 -0
- src/views/consent.php +127 -0
- src/views/dashboard.php +45 -0
- src/views/onboarding.php +132 -0
- src/views/pending-setup.php +1 -0
- src/views/settings-internal.php +30 -0
- src/views/settings.php +71 -0
- src/views/unlink.php +10 -0
- vendor/autoload.php +7 -0
- vendor/bin/generate-defuse-key +14 -0
- vendor/composer/ClassLoader.php +445 -0
- vendor/composer/LICENSE +21 -0
- vendor/composer/autoload_classmap.php +55 -0
- vendor/composer/autoload_namespaces.php +9 -0
- vendor/composer/autoload_psr4.php +17 -0
- vendor/composer/autoload_real.php +55 -0
- vendor/composer/autoload_static.php +122 -0
- vendor/composer/installed.json +166 -0
- vendor/defuse/php-encryption/.gitignore +11 -0
- vendor/defuse/php-encryption/.php_cs +60 -0
- vendor/defuse/php-encryption/LICENSE +21 -0
- vendor/defuse/php-encryption/README.md +102 -0
- vendor/defuse/php-encryption/bin/generate-defuse-key +14 -0
- vendor/defuse/php-encryption/composer.json +35 -0
- vendor/defuse/php-encryption/dist/Makefile +37 -0
- vendor/defuse/php-encryption/dist/box.json +25 -0
- vendor/defuse/php-encryption/dist/signingkey.asc +52 -0
- vendor/defuse/php-encryption/docs/CryptoDetails.md +64 -0
- vendor/defuse/php-encryption/docs/FAQ.md +51 -0
- vendor/defuse/php-encryption/docs/InstallingAndVerifying.md +53 -0
- vendor/defuse/php-encryption/docs/InternalDeveloperDocs.md +166 -0
- vendor/defuse/php-encryption/docs/Tutorial.md +314 -0
- vendor/defuse/php-encryption/docs/UpgradingFromV1.2.md +51 -0
- vendor/defuse/php-encryption/docs/classes/Crypto.md +280 -0
- vendor/defuse/php-encryption/docs/classes/File.md +486 -0
- vendor/defuse/php-encryption/docs/classes/Key.md +117 -0
- vendor/defuse/php-encryption/docs/classes/KeyProtectedByPassword.md +259 -0
- vendor/defuse/php-encryption/psalm.xml +13 -0
- vendor/defuse/php-encryption/src/Core.php +448 -0
- vendor/defuse/php-encryption/src/Crypto.php +445 -0
- vendor/defuse/php-encryption/src/DerivedKeys.php +50 -0
- vendor/defuse/php-encryption/src/Encoding.php +269 -0
- vendor/defuse/php-encryption/src/Exception/BadFormatException.php +7 -0
- vendor/defuse/php-encryption/src/Exception/CryptoException.php +7 -0
- vendor/defuse/php-encryption/src/Exception/EnvironmentIsBrokenException.php +7 -0
- vendor/defuse/php-encryption/src/Exception/IOException.php +7 -0
- vendor/defuse/php-encryption/src/Exception/WrongKeyOrModifiedCiphertextException.php +7 -0
- vendor/defuse/php-encryption/src/File.php +762 -0
- vendor/defuse/php-encryption/src/Key.php +94 -0
- vendor/defuse/php-encryption/src/KeyOrPassword.php +149 -0
- vendor/defuse/php-encryption/src/KeyProtectedByPassword.php +145 -0
- vendor/defuse/php-encryption/src/RuntimeTests.php +228 -0
- vendor/firebase/php-jwt/LICENSE +30 -0
- vendor/firebase/php-jwt/README.md +200 -0
- vendor/firebase/php-jwt/composer.json +33 -0
- vendor/firebase/php-jwt/src/BeforeValidException.php +6 -0
- vendor/firebase/php-jwt/src/ExpiredException.php +6 -0
- vendor/firebase/php-jwt/src/JWK.php +171 -0
- vendor/firebase/php-jwt/src/JWT.php +512 -0
- vendor/firebase/php-jwt/src/SignatureInvalidException.php +6 -0
- vendor/paragonie/random_compat/LICENSE +22 -0
- vendor/paragonie/random_compat/build-phar.sh +5 -0
- vendor/paragonie/random_compat/composer.json +34 -0
- vendor/paragonie/random_compat/dist/random_compat.phar.pubkey +5 -0
- vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc +11 -0
- vendor/paragonie/random_compat/lib/random.php +32 -0
- vendor/paragonie/random_compat/other/build_phar.php +57 -0
- vendor/paragonie/random_compat/psalm-autoload.php +9 -0
- 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>
|