CV-Portfolio 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +53 -0
  4. data/_config.yml +111 -0
  5. data/_includes/backtotop.html +1 -0
  6. data/_includes/clients.html +37 -0
  7. data/_includes/header.html +2 -0
  8. data/_includes/javascripts.html +4 -0
  9. data/_includes/mapbox-init.html +2 -0
  10. data/_includes/mapbox.html +3 -0
  11. data/_includes/meta.html +28 -0
  12. data/_includes/photoswipe.html +40 -0
  13. data/_includes/preloader.html +4 -0
  14. data/_includes/review.html +79 -0
  15. data/_includes/scroll.html +2 -0
  16. data/_includes/sidebar.html +68 -0
  17. data/_includes/skills.html +31 -0
  18. data/_includes/testimonials.html +54 -0
  19. data/_layouts/contact.html +20 -0
  20. data/_layouts/default.html +18 -0
  21. data/_layouts/post.html +21 -0
  22. data/_layouts/project.html +23 -0
  23. data/_layouts/resume.html +18 -0
  24. data/_sass/style.scss +3353 -0
  25. data/assets/css/app/_button.scss +70 -0
  26. data/assets/css/app/_cases.scss +35 -0
  27. data/assets/css/app/_clients.scss +22 -0
  28. data/assets/css/app/_contact.scss +74 -0
  29. data/assets/css/app/_form.scss +264 -0
  30. data/assets/css/app/_general.scss +106 -0
  31. data/assets/css/app/_navbar.scss +98 -0
  32. data/assets/css/app/_news.scss +112 -0
  33. data/assets/css/app/_other.scss +58 -0
  34. data/assets/css/app/_popup.scss +167 -0
  35. data/assets/css/app/_portfolio.scss +269 -0
  36. data/assets/css/app/_sections.scss +265 -0
  37. data/assets/css/app/_sidebar.scss +181 -0
  38. data/assets/css/app/_single-news.scss +445 -0
  39. data/assets/css/app/_single-project.scss +97 -0
  40. data/assets/css/app/_testimonials.scss +147 -0
  41. data/assets/css/app/_timeline.scss +78 -0
  42. data/assets/css/app/_typography.scss +253 -0
  43. data/assets/css/main.scss +9 -0
  44. data/assets/css/style.css.map +30 -0
  45. data/assets/css/style.scss +43 -0
  46. data/assets/css/variables/_media.scss +8 -0
  47. data/assets/css/variables/_mixins.scss +59 -0
  48. data/assets/css/variables/_vars.scss +48 -0
  49. data/assets/css/vendors/bootstrap.css +10819 -0
  50. data/assets/css/vendors/bootstrap.css.map +1 -0
  51. data/assets/css/vendors/bootstrap.min.css +7 -0
  52. data/assets/css/vendors/bootstrap.min.css.map +1 -0
  53. data/assets/css/vendors/default-skin.css +482 -0
  54. data/assets/css/vendors/magnific-popup.css +351 -0
  55. data/assets/css/vendors/photoswipe.css +179 -0
  56. data/assets/css/vendors/simplebar.css +211 -0
  57. data/assets/css/vendors/simplebar.min.css +1 -0
  58. data/assets/css/vendors/swiper.min.css +12 -0
  59. data/assets/icons/dark/icon-app.svg +4 -0
  60. data/assets/icons/dark/icon-design.svg +5 -0
  61. data/assets/icons/dark/icon-dev.svg +12 -0
  62. data/assets/icons/dark/icon-education.svg +10 -0
  63. data/assets/icons/dark/icon-experience.svg +4 -0
  64. data/assets/icons/dark/icon-photo.svg +7 -0
  65. data/assets/icons/dark/icon-quote.svg +3 -0
  66. data/assets/icons/emoji/emoji-angry.svg +15 -0
  67. data/assets/icons/emoji/emoji-bad.svg +15 -0
  68. data/assets/icons/emoji/emoji-crazy.svg +15 -0
  69. data/assets/icons/emoji/emoji-happy-2.svg +12 -0
  70. data/assets/icons/emoji/emoji-happy-3.svg +16 -0
  71. data/assets/icons/emoji/emoji-happy.svg +15 -0
  72. data/assets/icons/emoji/emoji-in-love.svg +15 -0
  73. data/assets/icons/emoji/emoji-laughing.svg +15 -0
  74. data/assets/icons/emoji/emoji-nerd.svg +16 -0
  75. data/assets/icons/emoji/emoji-pressure.svg +17 -0
  76. data/assets/icons/emoji/emoji-sad-2.svg +15 -0
  77. data/assets/icons/emoji/emoji-sad.svg +13 -0
  78. data/assets/icons/emoji/emoji-shocked-2.svg +15 -0
  79. data/assets/icons/emoji/emoji-shocked-3.svg +11 -0
  80. data/assets/icons/emoji/emoji-shocked.svg +14 -0
  81. data/assets/icons/emoji/emoji-sweating.svg +10 -0
  82. data/assets/icons/emoji/emoji-thinking.svg +13 -0
  83. data/assets/icons/emoji/emoji-wink.svg +12 -0
  84. data/assets/icons/feather.css +871 -0
  85. data/assets/icons/feather.woff +0 -0
  86. data/assets/icons/feather.woff2 +0 -0
  87. data/assets/icons/icomoon.svg +45 -0
  88. data/assets/icons/icomoon.ttf +0 -0
  89. data/assets/icons/icomoon.woff +0 -0
  90. data/assets/icons/icon-app.svg +14 -0
  91. data/assets/icons/icon-at.svg +3 -0
  92. data/assets/icons/icon-calendar.svg +3 -0
  93. data/assets/icons/icon-design.svg +27 -0
  94. data/assets/icons/icon-dev.svg +54 -0
  95. data/assets/icons/icon-download.svg +3 -0
  96. data/assets/icons/icon-education.svg +10 -0
  97. data/assets/icons/icon-envelope.svg +3 -0
  98. data/assets/icons/icon-experience.svg +4 -0
  99. data/assets/icons/icon-like-fill.svg +3 -0
  100. data/assets/icons/icon-like.svg +3 -0
  101. data/assets/icons/icon-location.svg +3 -0
  102. data/assets/icons/icon-news.svg +3 -0
  103. data/assets/icons/icon-phone.svg +3 -0
  104. data/assets/icons/icon-photo.svg +45 -0
  105. data/assets/icons/icon-quote.svg +9 -0
  106. data/assets/icons/icon-reply.svg +3 -0
  107. data/assets/icons/icon-send.svg +3 -0
  108. data/assets/icons/icon-skype.svg +3 -0
  109. data/assets/icons/icon-user.svg +6 -0
  110. data/assets/icons/style-custome-icons.css +132 -0
  111. data/assets/images/280x204.jpg +0 -0
  112. data/assets/images/400x550.jpg +0 -0
  113. data/assets/images/433x240.jpg +0 -0
  114. data/assets/images/560x340.jpg +0 -0
  115. data/assets/images/958x400.jpg +0 -0
  116. data/assets/images/_icon-map-pic.svg +15 -0
  117. data/assets/images/apple-store.png +0 -0
  118. data/assets/images/avatar-1.png +0 -0
  119. data/assets/images/avatar-2.png +0 -0
  120. data/assets/images/avatar-3.png +0 -0
  121. data/assets/images/avatar-4.png +0 -0
  122. data/assets/images/clients/logo-1-color.png +0 -0
  123. data/assets/images/clients/logo-1-white.png +0 -0
  124. data/assets/images/clients/logo-2-color.png +0 -0
  125. data/assets/images/clients/logo-2-white.png +0 -0
  126. data/assets/images/clients/logo-3-color.png +0 -0
  127. data/assets/images/clients/logo-3-white.png +0 -0
  128. data/assets/images/clients/logo-4-color.png +0 -0
  129. data/assets/images/clients/logo-4-white.png +0 -0
  130. data/assets/images/clients/logo-5-color.png +0 -0
  131. data/assets/images/clients/logo-5-white.png +0 -0
  132. data/assets/images/clients/logo-6-color.png +0 -0
  133. data/assets/images/clients/logo-6-white.png +0 -0
  134. data/assets/images/clients/logo-7-color.png +0 -0
  135. data/assets/images/clients/logo-7-white.png +0 -0
  136. data/assets/images/clients/logo-8-color.png +0 -0
  137. data/assets/images/clients/logo-8-white.png +0 -0
  138. data/assets/images/clients/logo-9-color.png +0 -0
  139. data/assets/images/clients/logo-9-white.png +0 -0
  140. data/assets/images/cover.png +0 -0
  141. data/assets/images/cv-portfolio.jpg +0 -0
  142. data/assets/images/favicons/apple-touch-icon-114x114.png +0 -0
  143. data/assets/images/favicons/apple-touch-icon-144x144.png +0 -0
  144. data/assets/images/favicons/apple-touch-icon-57x57.png +0 -0
  145. data/assets/images/favicons/apple-touch-icon-72x72.png +0 -0
  146. data/assets/images/favicons/favicon.png +0 -0
  147. data/assets/images/icon-map-pic.svg +30 -0
  148. data/assets/images/icon-quote.svg +3 -0
  149. data/assets/images/logotype.svg +9 -0
  150. data/assets/images/my-avatar.png +0 -0
  151. data/assets/images/photoswiper/default-skin.png +0 -0
  152. data/assets/images/photoswiper/default-skin.svg +1 -0
  153. data/assets/images/photoswiper/preloader.gif +0 -0
  154. data/assets/images/play-store.png +0 -0
  155. data/assets/images/splash.gif +0 -0
  156. data/assets/img/posts/400x550.jpg +0 -0
  157. data/assets/img/posts/958x400.jpg +0 -0
  158. data/assets/js/common.js +737 -0
  159. data/assets/js/jquery-3.4.1.min.js +2 -0
  160. data/assets/js/mapbox.init.js +32 -0
  161. data/assets/js/plugins.js +33367 -0
  162. data/assets/js/plugins.min.js +106 -0
  163. data/assets/php/form-contact.php +95 -0
  164. data/assets/php/phpmailer/COMMITMENT +46 -0
  165. data/assets/php/phpmailer/LICENSE +502 -0
  166. data/assets/php/phpmailer/README.md +218 -0
  167. data/assets/php/phpmailer/SECURITY.md +28 -0
  168. data/assets/php/phpmailer/VERSION +1 -0
  169. data/assets/php/phpmailer/composer.json +55 -0
  170. data/assets/php/phpmailer/get_oauth_token.php +144 -0
  171. data/assets/php/phpmailer/language/phpmailer.lang-am.php +26 -0
  172. data/assets/php/phpmailer/language/phpmailer.lang-ar.php +27 -0
  173. data/assets/php/phpmailer/language/phpmailer.lang-az.php +26 -0
  174. data/assets/php/phpmailer/language/phpmailer.lang-ba.php +26 -0
  175. data/assets/php/phpmailer/language/phpmailer.lang-be.php +26 -0
  176. data/assets/php/phpmailer/language/phpmailer.lang-bg.php +26 -0
  177. data/assets/php/phpmailer/language/phpmailer.lang-ca.php +26 -0
  178. data/assets/php/phpmailer/language/phpmailer.lang-ch.php +26 -0
  179. data/assets/php/phpmailer/language/phpmailer.lang-cs.php +25 -0
  180. data/assets/php/phpmailer/language/phpmailer.lang-da.php +26 -0
  181. data/assets/php/phpmailer/language/phpmailer.lang-de.php +25 -0
  182. data/assets/php/phpmailer/language/phpmailer.lang-el.php +25 -0
  183. data/assets/php/phpmailer/language/phpmailer.lang-eo.php +25 -0
  184. data/assets/php/phpmailer/language/phpmailer.lang-es.php +26 -0
  185. data/assets/php/phpmailer/language/phpmailer.lang-et.php +27 -0
  186. data/assets/php/phpmailer/language/phpmailer.lang-fa.php +27 -0
  187. data/assets/php/phpmailer/language/phpmailer.lang-fi.php +27 -0
  188. data/assets/php/phpmailer/language/phpmailer.lang-fo.php +26 -0
  189. data/assets/php/phpmailer/language/phpmailer.lang-fr.php +29 -0
  190. data/assets/php/phpmailer/language/phpmailer.lang-gl.php +26 -0
  191. data/assets/php/phpmailer/language/phpmailer.lang-he.php +26 -0
  192. data/assets/php/phpmailer/language/phpmailer.lang-hi.php +26 -0
  193. data/assets/php/phpmailer/language/phpmailer.lang-hr.php +26 -0
  194. data/assets/php/phpmailer/language/phpmailer.lang-hu.php +26 -0
  195. data/assets/php/phpmailer/language/phpmailer.lang-id.php +27 -0
  196. data/assets/php/phpmailer/language/phpmailer.lang-it.php +27 -0
  197. data/assets/php/phpmailer/language/phpmailer.lang-ja.php +27 -0
  198. data/assets/php/phpmailer/language/phpmailer.lang-ka.php +26 -0
  199. data/assets/php/phpmailer/language/phpmailer.lang-ko.php +26 -0
  200. data/assets/php/phpmailer/language/phpmailer.lang-lt.php +26 -0
  201. data/assets/php/phpmailer/language/phpmailer.lang-lv.php +26 -0
  202. data/assets/php/phpmailer/language/phpmailer.lang-mg.php +25 -0
  203. data/assets/php/phpmailer/language/phpmailer.lang-ms.php +26 -0
  204. data/assets/php/phpmailer/language/phpmailer.lang-nb.php +25 -0
  205. data/assets/php/phpmailer/language/phpmailer.lang-nl.php +26 -0
  206. data/assets/php/phpmailer/language/phpmailer.lang-pl.php +26 -0
  207. data/assets/php/phpmailer/language/phpmailer.lang-pt.php +26 -0
  208. data/assets/php/phpmailer/language/phpmailer.lang-pt_br.php +29 -0
  209. data/assets/php/phpmailer/language/phpmailer.lang-ro.php +26 -0
  210. data/assets/php/phpmailer/language/phpmailer.lang-ru.php +27 -0
  211. data/assets/php/phpmailer/language/phpmailer.lang-sk.php +27 -0
  212. data/assets/php/phpmailer/language/phpmailer.lang-sl.php +27 -0
  213. data/assets/php/phpmailer/language/phpmailer.lang-sr.php +27 -0
  214. data/assets/php/phpmailer/language/phpmailer.lang-sv.php +26 -0
  215. data/assets/php/phpmailer/language/phpmailer.lang-tl.php +27 -0
  216. data/assets/php/phpmailer/language/phpmailer.lang-tr.php +30 -0
  217. data/assets/php/phpmailer/language/phpmailer.lang-uk.php +27 -0
  218. data/assets/php/phpmailer/language/phpmailer.lang-vi.php +26 -0
  219. data/assets/php/phpmailer/language/phpmailer.lang-zh.php +28 -0
  220. data/assets/php/phpmailer/language/phpmailer.lang-zh_cn.php +28 -0
  221. data/assets/php/phpmailer/src/Exception.php +39 -0
  222. data/assets/php/phpmailer/src/OAuth.php +138 -0
  223. data/assets/php/phpmailer/src/PHPMailer.php +4502 -0
  224. data/assets/php/phpmailer/src/POP3.php +419 -0
  225. data/assets/php/phpmailer/src/SMTP.php +1326 -0
  226. data/assets/videos/video-1.mp4 +0 -0
  227. data/assets/videos/video-2.mp4 +0 -0
  228. metadata +283 -0
@@ -0,0 +1,1326 @@
1
+ <?php
2
+ /**
3
+ * PHPMailer RFC821 SMTP email transport class.
4
+ * PHP Version 5.5.
5
+ *
6
+ * @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
7
+ *
8
+ * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
9
+ * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
10
+ * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
11
+ * @author Brent R. Matzelle (original founder)
12
+ * @copyright 2012 - 2017 Marcus Bointon
13
+ * @copyright 2010 - 2012 Jim Jagielski
14
+ * @copyright 2004 - 2009 Andy Prevost
15
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
16
+ * @note This program is distributed in the hope that it will be useful - WITHOUT
17
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18
+ * FITNESS FOR A PARTICULAR PURPOSE.
19
+ */
20
+
21
+ namespace PHPMailer\PHPMailer;
22
+
23
+ /**
24
+ * PHPMailer RFC821 SMTP email transport class.
25
+ * Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server.
26
+ *
27
+ * @author Chris Ryan
28
+ * @author Marcus Bointon <phpmailer@synchromedia.co.uk>
29
+ */
30
+ class SMTP
31
+ {
32
+ /**
33
+ * The PHPMailer SMTP version number.
34
+ *
35
+ * @var string
36
+ */
37
+ const VERSION = '6.0.7';
38
+
39
+ /**
40
+ * SMTP line break constant.
41
+ *
42
+ * @var string
43
+ */
44
+ const LE = "\r\n";
45
+
46
+ /**
47
+ * The SMTP port to use if one is not specified.
48
+ *
49
+ * @var int
50
+ */
51
+ const DEFAULT_PORT = 25;
52
+
53
+ /**
54
+ * The maximum line length allowed by RFC 2822 section 2.1.1.
55
+ *
56
+ * @var int
57
+ */
58
+ const MAX_LINE_LENGTH = 998;
59
+
60
+ /**
61
+ * Debug level for no output.
62
+ */
63
+ const DEBUG_OFF = 0;
64
+
65
+ /**
66
+ * Debug level to show client -> server messages.
67
+ */
68
+ const DEBUG_CLIENT = 1;
69
+
70
+ /**
71
+ * Debug level to show client -> server and server -> client messages.
72
+ */
73
+ const DEBUG_SERVER = 2;
74
+
75
+ /**
76
+ * Debug level to show connection status, client -> server and server -> client messages.
77
+ */
78
+ const DEBUG_CONNECTION = 3;
79
+
80
+ /**
81
+ * Debug level to show all messages.
82
+ */
83
+ const DEBUG_LOWLEVEL = 4;
84
+
85
+ /**
86
+ * Debug output level.
87
+ * Options:
88
+ * * self::DEBUG_OFF (`0`) No debug output, default
89
+ * * self::DEBUG_CLIENT (`1`) Client commands
90
+ * * self::DEBUG_SERVER (`2`) Client commands and server responses
91
+ * * self::DEBUG_CONNECTION (`3`) As DEBUG_SERVER plus connection status
92
+ * * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages.
93
+ *
94
+ * @var int
95
+ */
96
+ public $do_debug = self::DEBUG_OFF;
97
+
98
+ /**
99
+ * How to handle debug output.
100
+ * Options:
101
+ * * `echo` Output plain-text as-is, appropriate for CLI
102
+ * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output
103
+ * * `error_log` Output to error log as configured in php.ini
104
+ * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
105
+ *
106
+ * ```php
107
+ * $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
108
+ * ```
109
+ *
110
+ * Alternatively, you can pass in an instance of a PSR-3 compatible logger, though only `debug`
111
+ * level output is used:
112
+ *
113
+ * ```php
114
+ * $mail->Debugoutput = new myPsr3Logger;
115
+ * ```
116
+ *
117
+ * @var string|callable|\Psr\Log\LoggerInterface
118
+ */
119
+ public $Debugoutput = 'echo';
120
+
121
+ /**
122
+ * Whether to use VERP.
123
+ *
124
+ * @see http://en.wikipedia.org/wiki/Variable_envelope_return_path
125
+ * @see http://www.postfix.org/VERP_README.html Info on VERP
126
+ *
127
+ * @var bool
128
+ */
129
+ public $do_verp = false;
130
+
131
+ /**
132
+ * The timeout value for connection, in seconds.
133
+ * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2.
134
+ * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure.
135
+ *
136
+ * @see http://tools.ietf.org/html/rfc2821#section-4.5.3.2
137
+ *
138
+ * @var int
139
+ */
140
+ public $Timeout = 300;
141
+
142
+ /**
143
+ * How long to wait for commands to complete, in seconds.
144
+ * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2.
145
+ *
146
+ * @var int
147
+ */
148
+ public $Timelimit = 300;
149
+
150
+ /**
151
+ * Patterns to extract an SMTP transaction id from reply to a DATA command.
152
+ * The first capture group in each regex will be used as the ID.
153
+ * MS ESMTP returns the message ID, which may not be correct for internal tracking.
154
+ *
155
+ * @var string[]
156
+ */
157
+ protected $smtp_transaction_id_patterns = [
158
+ 'exim' => '/[\d]{3} OK id=(.*)/',
159
+ 'sendmail' => '/[\d]{3} 2.0.0 (.*) Message/',
160
+ 'postfix' => '/[\d]{3} 2.0.0 Ok: queued as (.*)/',
161
+ 'Microsoft_ESMTP' => '/[0-9]{3} 2.[\d].0 (.*)@(?:.*) Queued mail for delivery/',
162
+ 'Amazon_SES' => '/[\d]{3} Ok (.*)/',
163
+ 'SendGrid' => '/[\d]{3} Ok: queued as (.*)/',
164
+ 'CampaignMonitor' => '/[\d]{3} 2.0.0 OK:([a-zA-Z\d]{48})/',
165
+ ];
166
+
167
+ /**
168
+ * The last transaction ID issued in response to a DATA command,
169
+ * if one was detected.
170
+ *
171
+ * @var string|bool|null
172
+ */
173
+ protected $last_smtp_transaction_id;
174
+
175
+ /**
176
+ * The socket for the server connection.
177
+ *
178
+ * @var ?resource
179
+ */
180
+ protected $smtp_conn;
181
+
182
+ /**
183
+ * Error information, if any, for the last SMTP command.
184
+ *
185
+ * @var array
186
+ */
187
+ protected $error = [
188
+ 'error' => '',
189
+ 'detail' => '',
190
+ 'smtp_code' => '',
191
+ 'smtp_code_ex' => '',
192
+ ];
193
+
194
+ /**
195
+ * The reply the server sent to us for HELO.
196
+ * If null, no HELO string has yet been received.
197
+ *
198
+ * @var string|null
199
+ */
200
+ protected $helo_rply = null;
201
+
202
+ /**
203
+ * The set of SMTP extensions sent in reply to EHLO command.
204
+ * Indexes of the array are extension names.
205
+ * Value at index 'HELO' or 'EHLO' (according to command that was sent)
206
+ * represents the server name. In case of HELO it is the only element of the array.
207
+ * Other values can be boolean TRUE or an array containing extension options.
208
+ * If null, no HELO/EHLO string has yet been received.
209
+ *
210
+ * @var array|null
211
+ */
212
+ protected $server_caps = null;
213
+
214
+ /**
215
+ * The most recent reply received from the server.
216
+ *
217
+ * @var string
218
+ */
219
+ protected $last_reply = '';
220
+
221
+ /**
222
+ * Output debugging info via a user-selected method.
223
+ *
224
+ * @param string $str Debug string to output
225
+ * @param int $level The debug level of this message; see DEBUG_* constants
226
+ *
227
+ * @see SMTP::$Debugoutput
228
+ * @see SMTP::$do_debug
229
+ */
230
+ protected function edebug($str, $level = 0)
231
+ {
232
+ if ($level > $this->do_debug) {
233
+ return;
234
+ }
235
+ //Is this a PSR-3 logger?
236
+ if ($this->Debugoutput instanceof \Psr\Log\LoggerInterface) {
237
+ $this->Debugoutput->debug($str);
238
+
239
+ return;
240
+ }
241
+ //Avoid clash with built-in function names
242
+ if (!in_array($this->Debugoutput, ['error_log', 'html', 'echo']) and is_callable($this->Debugoutput)) {
243
+ call_user_func($this->Debugoutput, $str, $level);
244
+
245
+ return;
246
+ }
247
+ switch ($this->Debugoutput) {
248
+ case 'error_log':
249
+ //Don't output, just log
250
+ error_log($str);
251
+ break;
252
+ case 'html':
253
+ //Cleans up output a bit for a better looking, HTML-safe output
254
+ echo gmdate('Y-m-d H:i:s'), ' ', htmlentities(
255
+ preg_replace('/[\r\n]+/', '', $str),
256
+ ENT_QUOTES,
257
+ 'UTF-8'
258
+ ), "<br>\n";
259
+ break;
260
+ case 'echo':
261
+ default:
262
+ //Normalize line breaks
263
+ $str = preg_replace('/\r\n|\r/ms', "\n", $str);
264
+ echo gmdate('Y-m-d H:i:s'),
265
+ "\t",
266
+ //Trim trailing space
267
+ trim(
268
+ //Indent for readability, except for trailing break
269
+ str_replace(
270
+ "\n",
271
+ "\n \t ",
272
+ trim($str)
273
+ )
274
+ ),
275
+ "\n";
276
+ }
277
+ }
278
+
279
+ /**
280
+ * Connect to an SMTP server.
281
+ *
282
+ * @param string $host SMTP server IP or host name
283
+ * @param int $port The port number to connect to
284
+ * @param int $timeout How long to wait for the connection to open
285
+ * @param array $options An array of options for stream_context_create()
286
+ *
287
+ * @return bool
288
+ */
289
+ public function connect($host, $port = null, $timeout = 30, $options = [])
290
+ {
291
+ static $streamok;
292
+ //This is enabled by default since 5.0.0 but some providers disable it
293
+ //Check this once and cache the result
294
+ if (null === $streamok) {
295
+ $streamok = function_exists('stream_socket_client');
296
+ }
297
+ // Clear errors to avoid confusion
298
+ $this->setError('');
299
+ // Make sure we are __not__ connected
300
+ if ($this->connected()) {
301
+ // Already connected, generate error
302
+ $this->setError('Already connected to a server');
303
+
304
+ return false;
305
+ }
306
+ if (empty($port)) {
307
+ $port = self::DEFAULT_PORT;
308
+ }
309
+ // Connect to the SMTP server
310
+ $this->edebug(
311
+ "Connection: opening to $host:$port, timeout=$timeout, options=" .
312
+ (count($options) > 0 ? var_export($options, true) : 'array()'),
313
+ self::DEBUG_CONNECTION
314
+ );
315
+ $errno = 0;
316
+ $errstr = '';
317
+ if ($streamok) {
318
+ $socket_context = stream_context_create($options);
319
+ set_error_handler([$this, 'errorHandler']);
320
+ $this->smtp_conn = stream_socket_client(
321
+ $host . ':' . $port,
322
+ $errno,
323
+ $errstr,
324
+ $timeout,
325
+ STREAM_CLIENT_CONNECT,
326
+ $socket_context
327
+ );
328
+ restore_error_handler();
329
+ } else {
330
+ //Fall back to fsockopen which should work in more places, but is missing some features
331
+ $this->edebug(
332
+ 'Connection: stream_socket_client not available, falling back to fsockopen',
333
+ self::DEBUG_CONNECTION
334
+ );
335
+ set_error_handler([$this, 'errorHandler']);
336
+ $this->smtp_conn = fsockopen(
337
+ $host,
338
+ $port,
339
+ $errno,
340
+ $errstr,
341
+ $timeout
342
+ );
343
+ restore_error_handler();
344
+ }
345
+ // Verify we connected properly
346
+ if (!is_resource($this->smtp_conn)) {
347
+ $this->setError(
348
+ 'Failed to connect to server',
349
+ '',
350
+ (string) $errno,
351
+ (string) $errstr
352
+ );
353
+ $this->edebug(
354
+ 'SMTP ERROR: ' . $this->error['error']
355
+ . ": $errstr ($errno)",
356
+ self::DEBUG_CLIENT
357
+ );
358
+
359
+ return false;
360
+ }
361
+ $this->edebug('Connection: opened', self::DEBUG_CONNECTION);
362
+ // SMTP server can take longer to respond, give longer timeout for first read
363
+ // Windows does not have support for this timeout function
364
+ if (substr(PHP_OS, 0, 3) != 'WIN') {
365
+ $max = ini_get('max_execution_time');
366
+ // Don't bother if unlimited
367
+ if (0 != $max and $timeout > $max) {
368
+ @set_time_limit($timeout);
369
+ }
370
+ stream_set_timeout($this->smtp_conn, $timeout, 0);
371
+ }
372
+ // Get any announcement
373
+ $announce = $this->get_lines();
374
+ $this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER);
375
+
376
+ return true;
377
+ }
378
+
379
+ /**
380
+ * Initiate a TLS (encrypted) session.
381
+ *
382
+ * @return bool
383
+ */
384
+ public function startTLS()
385
+ {
386
+ if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) {
387
+ return false;
388
+ }
389
+
390
+ //Allow the best TLS version(s) we can
391
+ $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
392
+
393
+ //PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT
394
+ //so add them back in manually if we can
395
+ if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) {
396
+ $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
397
+ $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
398
+ }
399
+
400
+ // Begin encrypted connection
401
+ set_error_handler([$this, 'errorHandler']);
402
+ $crypto_ok = stream_socket_enable_crypto(
403
+ $this->smtp_conn,
404
+ true,
405
+ $crypto_method
406
+ );
407
+ restore_error_handler();
408
+
409
+ return (bool) $crypto_ok;
410
+ }
411
+
412
+ /**
413
+ * Perform SMTP authentication.
414
+ * Must be run after hello().
415
+ *
416
+ * @see hello()
417
+ *
418
+ * @param string $username The user name
419
+ * @param string $password The password
420
+ * @param string $authtype The auth type (CRAM-MD5, PLAIN, LOGIN, XOAUTH2)
421
+ * @param OAuth $OAuth An optional OAuth instance for XOAUTH2 authentication
422
+ *
423
+ * @return bool True if successfully authenticated
424
+ */
425
+ public function authenticate(
426
+ $username,
427
+ $password,
428
+ $authtype = null,
429
+ $OAuth = null
430
+ ) {
431
+ if (!$this->server_caps) {
432
+ $this->setError('Authentication is not allowed before HELO/EHLO');
433
+
434
+ return false;
435
+ }
436
+
437
+ if (array_key_exists('EHLO', $this->server_caps)) {
438
+ // SMTP extensions are available; try to find a proper authentication method
439
+ if (!array_key_exists('AUTH', $this->server_caps)) {
440
+ $this->setError('Authentication is not allowed at this stage');
441
+ // 'at this stage' means that auth may be allowed after the stage changes
442
+ // e.g. after STARTTLS
443
+
444
+ return false;
445
+ }
446
+
447
+ $this->edebug('Auth method requested: ' . ($authtype ? $authtype : 'UNSPECIFIED'), self::DEBUG_LOWLEVEL);
448
+ $this->edebug(
449
+ 'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']),
450
+ self::DEBUG_LOWLEVEL
451
+ );
452
+
453
+ //If we have requested a specific auth type, check the server supports it before trying others
454
+ if (null !== $authtype and !in_array($authtype, $this->server_caps['AUTH'])) {
455
+ $this->edebug('Requested auth method not available: ' . $authtype, self::DEBUG_LOWLEVEL);
456
+ $authtype = null;
457
+ }
458
+
459
+ if (empty($authtype)) {
460
+ //If no auth mechanism is specified, attempt to use these, in this order
461
+ //Try CRAM-MD5 first as it's more secure than the others
462
+ foreach (['CRAM-MD5', 'LOGIN', 'PLAIN', 'XOAUTH2'] as $method) {
463
+ if (in_array($method, $this->server_caps['AUTH'])) {
464
+ $authtype = $method;
465
+ break;
466
+ }
467
+ }
468
+ if (empty($authtype)) {
469
+ $this->setError('No supported authentication methods found');
470
+
471
+ return false;
472
+ }
473
+ self::edebug('Auth method selected: ' . $authtype, self::DEBUG_LOWLEVEL);
474
+ }
475
+
476
+ if (!in_array($authtype, $this->server_caps['AUTH'])) {
477
+ $this->setError("The requested authentication method \"$authtype\" is not supported by the server");
478
+
479
+ return false;
480
+ }
481
+ } elseif (empty($authtype)) {
482
+ $authtype = 'LOGIN';
483
+ }
484
+ switch ($authtype) {
485
+ case 'PLAIN':
486
+ // Start authentication
487
+ if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) {
488
+ return false;
489
+ }
490
+ // Send encoded username and password
491
+ if (!$this->sendCommand(
492
+ 'User & Password',
493
+ base64_encode("\0" . $username . "\0" . $password),
494
+ 235
495
+ )
496
+ ) {
497
+ return false;
498
+ }
499
+ break;
500
+ case 'LOGIN':
501
+ // Start authentication
502
+ if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) {
503
+ return false;
504
+ }
505
+ if (!$this->sendCommand('Username', base64_encode($username), 334)) {
506
+ return false;
507
+ }
508
+ if (!$this->sendCommand('Password', base64_encode($password), 235)) {
509
+ return false;
510
+ }
511
+ break;
512
+ case 'CRAM-MD5':
513
+ // Start authentication
514
+ if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) {
515
+ return false;
516
+ }
517
+ // Get the challenge
518
+ $challenge = base64_decode(substr($this->last_reply, 4));
519
+
520
+ // Build the response
521
+ $response = $username . ' ' . $this->hmac($challenge, $password);
522
+
523
+ // send encoded credentials
524
+ return $this->sendCommand('Username', base64_encode($response), 235);
525
+ case 'XOAUTH2':
526
+ //The OAuth instance must be set up prior to requesting auth.
527
+ if (null === $OAuth) {
528
+ return false;
529
+ }
530
+ $oauth = $OAuth->getOauth64();
531
+
532
+ // Start authentication
533
+ if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) {
534
+ return false;
535
+ }
536
+ break;
537
+ default:
538
+ $this->setError("Authentication method \"$authtype\" is not supported");
539
+
540
+ return false;
541
+ }
542
+
543
+ return true;
544
+ }
545
+
546
+ /**
547
+ * Calculate an MD5 HMAC hash.
548
+ * Works like hash_hmac('md5', $data, $key)
549
+ * in case that function is not available.
550
+ *
551
+ * @param string $data The data to hash
552
+ * @param string $key The key to hash with
553
+ *
554
+ * @return string
555
+ */
556
+ protected function hmac($data, $key)
557
+ {
558
+ if (function_exists('hash_hmac')) {
559
+ return hash_hmac('md5', $data, $key);
560
+ }
561
+
562
+ // The following borrowed from
563
+ // http://php.net/manual/en/function.mhash.php#27225
564
+
565
+ // RFC 2104 HMAC implementation for php.
566
+ // Creates an md5 HMAC.
567
+ // Eliminates the need to install mhash to compute a HMAC
568
+ // by Lance Rushing
569
+
570
+ $bytelen = 64; // byte length for md5
571
+ if (strlen($key) > $bytelen) {
572
+ $key = pack('H*', md5($key));
573
+ }
574
+ $key = str_pad($key, $bytelen, chr(0x00));
575
+ $ipad = str_pad('', $bytelen, chr(0x36));
576
+ $opad = str_pad('', $bytelen, chr(0x5c));
577
+ $k_ipad = $key ^ $ipad;
578
+ $k_opad = $key ^ $opad;
579
+
580
+ return md5($k_opad . pack('H*', md5($k_ipad . $data)));
581
+ }
582
+
583
+ /**
584
+ * Check connection state.
585
+ *
586
+ * @return bool True if connected
587
+ */
588
+ public function connected()
589
+ {
590
+ if (is_resource($this->smtp_conn)) {
591
+ $sock_status = stream_get_meta_data($this->smtp_conn);
592
+ if ($sock_status['eof']) {
593
+ // The socket is valid but we are not connected
594
+ $this->edebug(
595
+ 'SMTP NOTICE: EOF caught while checking if connected',
596
+ self::DEBUG_CLIENT
597
+ );
598
+ $this->close();
599
+
600
+ return false;
601
+ }
602
+
603
+ return true; // everything looks good
604
+ }
605
+
606
+ return false;
607
+ }
608
+
609
+ /**
610
+ * Close the socket and clean up the state of the class.
611
+ * Don't use this function without first trying to use QUIT.
612
+ *
613
+ * @see quit()
614
+ */
615
+ public function close()
616
+ {
617
+ $this->setError('');
618
+ $this->server_caps = null;
619
+ $this->helo_rply = null;
620
+ if (is_resource($this->smtp_conn)) {
621
+ // close the connection and cleanup
622
+ fclose($this->smtp_conn);
623
+ $this->smtp_conn = null; //Makes for cleaner serialization
624
+ $this->edebug('Connection: closed', self::DEBUG_CONNECTION);
625
+ }
626
+ }
627
+
628
+ /**
629
+ * Send an SMTP DATA command.
630
+ * Issues a data command and sends the msg_data to the server,
631
+ * finializing the mail transaction. $msg_data is the message
632
+ * that is to be send with the headers. Each header needs to be
633
+ * on a single line followed by a <CRLF> with the message headers
634
+ * and the message body being separated by an additional <CRLF>.
635
+ * Implements RFC 821: DATA <CRLF>.
636
+ *
637
+ * @param string $msg_data Message data to send
638
+ *
639
+ * @return bool
640
+ */
641
+ public function data($msg_data)
642
+ {
643
+ //This will use the standard timelimit
644
+ if (!$this->sendCommand('DATA', 'DATA', 354)) {
645
+ return false;
646
+ }
647
+
648
+ /* The server is ready to accept data!
649
+ * According to rfc821 we should not send more than 1000 characters on a single line (including the LE)
650
+ * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into
651
+ * smaller lines to fit within the limit.
652
+ * We will also look for lines that start with a '.' and prepend an additional '.'.
653
+ * NOTE: this does not count towards line-length limit.
654
+ */
655
+
656
+ // Normalize line breaks before exploding
657
+ $lines = explode("\n", str_replace(["\r\n", "\r"], "\n", $msg_data));
658
+
659
+ /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field
660
+ * of the first line (':' separated) does not contain a space then it _should_ be a header and we will
661
+ * process all lines before a blank line as headers.
662
+ */
663
+
664
+ $field = substr($lines[0], 0, strpos($lines[0], ':'));
665
+ $in_headers = false;
666
+ if (!empty($field) and strpos($field, ' ') === false) {
667
+ $in_headers = true;
668
+ }
669
+
670
+ foreach ($lines as $line) {
671
+ $lines_out = [];
672
+ if ($in_headers and $line == '') {
673
+ $in_headers = false;
674
+ }
675
+ //Break this line up into several smaller lines if it's too long
676
+ //Micro-optimisation: isset($str[$len]) is faster than (strlen($str) > $len),
677
+ while (isset($line[self::MAX_LINE_LENGTH])) {
678
+ //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on
679
+ //so as to avoid breaking in the middle of a word
680
+ $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' ');
681
+ //Deliberately matches both false and 0
682
+ if (!$pos) {
683
+ //No nice break found, add a hard break
684
+ $pos = self::MAX_LINE_LENGTH - 1;
685
+ $lines_out[] = substr($line, 0, $pos);
686
+ $line = substr($line, $pos);
687
+ } else {
688
+ //Break at the found point
689
+ $lines_out[] = substr($line, 0, $pos);
690
+ //Move along by the amount we dealt with
691
+ $line = substr($line, $pos + 1);
692
+ }
693
+ //If processing headers add a LWSP-char to the front of new line RFC822 section 3.1.1
694
+ if ($in_headers) {
695
+ $line = "\t" . $line;
696
+ }
697
+ }
698
+ $lines_out[] = $line;
699
+
700
+ //Send the lines to the server
701
+ foreach ($lines_out as $line_out) {
702
+ //RFC2821 section 4.5.2
703
+ if (!empty($line_out) and $line_out[0] == '.') {
704
+ $line_out = '.' . $line_out;
705
+ }
706
+ $this->client_send($line_out . static::LE, 'DATA');
707
+ }
708
+ }
709
+
710
+ //Message data has been sent, complete the command
711
+ //Increase timelimit for end of DATA command
712
+ $savetimelimit = $this->Timelimit;
713
+ $this->Timelimit = $this->Timelimit * 2;
714
+ $result = $this->sendCommand('DATA END', '.', 250);
715
+ $this->recordLastTransactionID();
716
+ //Restore timelimit
717
+ $this->Timelimit = $savetimelimit;
718
+
719
+ return $result;
720
+ }
721
+
722
+ /**
723
+ * Send an SMTP HELO or EHLO command.
724
+ * Used to identify the sending server to the receiving server.
725
+ * This makes sure that client and server are in a known state.
726
+ * Implements RFC 821: HELO <SP> <domain> <CRLF>
727
+ * and RFC 2821 EHLO.
728
+ *
729
+ * @param string $host The host name or IP to connect to
730
+ *
731
+ * @return bool
732
+ */
733
+ public function hello($host = '')
734
+ {
735
+ //Try extended hello first (RFC 2821)
736
+ return $this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host);
737
+ }
738
+
739
+ /**
740
+ * Send an SMTP HELO or EHLO command.
741
+ * Low-level implementation used by hello().
742
+ *
743
+ * @param string $hello The HELO string
744
+ * @param string $host The hostname to say we are
745
+ *
746
+ * @return bool
747
+ *
748
+ * @see hello()
749
+ */
750
+ protected function sendHello($hello, $host)
751
+ {
752
+ $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250);
753
+ $this->helo_rply = $this->last_reply;
754
+ if ($noerror) {
755
+ $this->parseHelloFields($hello);
756
+ } else {
757
+ $this->server_caps = null;
758
+ }
759
+
760
+ return $noerror;
761
+ }
762
+
763
+ /**
764
+ * Parse a reply to HELO/EHLO command to discover server extensions.
765
+ * In case of HELO, the only parameter that can be discovered is a server name.
766
+ *
767
+ * @param string $type `HELO` or `EHLO`
768
+ */
769
+ protected function parseHelloFields($type)
770
+ {
771
+ $this->server_caps = [];
772
+ $lines = explode("\n", $this->helo_rply);
773
+
774
+ foreach ($lines as $n => $s) {
775
+ //First 4 chars contain response code followed by - or space
776
+ $s = trim(substr($s, 4));
777
+ if (empty($s)) {
778
+ continue;
779
+ }
780
+ $fields = explode(' ', $s);
781
+ if (!empty($fields)) {
782
+ if (!$n) {
783
+ $name = $type;
784
+ $fields = $fields[0];
785
+ } else {
786
+ $name = array_shift($fields);
787
+ switch ($name) {
788
+ case 'SIZE':
789
+ $fields = ($fields ? $fields[0] : 0);
790
+ break;
791
+ case 'AUTH':
792
+ if (!is_array($fields)) {
793
+ $fields = [];
794
+ }
795
+ break;
796
+ default:
797
+ $fields = true;
798
+ }
799
+ }
800
+ $this->server_caps[$name] = $fields;
801
+ }
802
+ }
803
+ }
804
+
805
+ /**
806
+ * Send an SMTP MAIL command.
807
+ * Starts a mail transaction from the email address specified in
808
+ * $from. Returns true if successful or false otherwise. If True
809
+ * the mail transaction is started and then one or more recipient
810
+ * commands may be called followed by a data command.
811
+ * Implements RFC 821: MAIL <SP> FROM:<reverse-path> <CRLF>.
812
+ *
813
+ * @param string $from Source address of this message
814
+ *
815
+ * @return bool
816
+ */
817
+ public function mail($from)
818
+ {
819
+ $useVerp = ($this->do_verp ? ' XVERP' : '');
820
+
821
+ return $this->sendCommand(
822
+ 'MAIL FROM',
823
+ 'MAIL FROM:<' . $from . '>' . $useVerp,
824
+ 250
825
+ );
826
+ }
827
+
828
+ /**
829
+ * Send an SMTP QUIT command.
830
+ * Closes the socket if there is no error or the $close_on_error argument is true.
831
+ * Implements from RFC 821: QUIT <CRLF>.
832
+ *
833
+ * @param bool $close_on_error Should the connection close if an error occurs?
834
+ *
835
+ * @return bool
836
+ */
837
+ public function quit($close_on_error = true)
838
+ {
839
+ $noerror = $this->sendCommand('QUIT', 'QUIT', 221);
840
+ $err = $this->error; //Save any error
841
+ if ($noerror or $close_on_error) {
842
+ $this->close();
843
+ $this->error = $err; //Restore any error from the quit command
844
+ }
845
+
846
+ return $noerror;
847
+ }
848
+
849
+ /**
850
+ * Send an SMTP RCPT command.
851
+ * Sets the TO argument to $toaddr.
852
+ * Returns true if the recipient was accepted false if it was rejected.
853
+ * Implements from RFC 821: RCPT <SP> TO:<forward-path> <CRLF>.
854
+ *
855
+ * @param string $address The address the message is being sent to
856
+ *
857
+ * @return bool
858
+ */
859
+ public function recipient($address)
860
+ {
861
+ return $this->sendCommand(
862
+ 'RCPT TO',
863
+ 'RCPT TO:<' . $address . '>',
864
+ [250, 251]
865
+ );
866
+ }
867
+
868
+ /**
869
+ * Send an SMTP RSET command.
870
+ * Abort any transaction that is currently in progress.
871
+ * Implements RFC 821: RSET <CRLF>.
872
+ *
873
+ * @return bool True on success
874
+ */
875
+ public function reset()
876
+ {
877
+ return $this->sendCommand('RSET', 'RSET', 250);
878
+ }
879
+
880
+ /**
881
+ * Send a command to an SMTP server and check its return code.
882
+ *
883
+ * @param string $command The command name - not sent to the server
884
+ * @param string $commandstring The actual command to send
885
+ * @param int|array $expect One or more expected integer success codes
886
+ *
887
+ * @return bool True on success
888
+ */
889
+ protected function sendCommand($command, $commandstring, $expect)
890
+ {
891
+ if (!$this->connected()) {
892
+ $this->setError("Called $command without being connected");
893
+
894
+ return false;
895
+ }
896
+ //Reject line breaks in all commands
897
+ if (strpos($commandstring, "\n") !== false or strpos($commandstring, "\r") !== false) {
898
+ $this->setError("Command '$command' contained line breaks");
899
+
900
+ return false;
901
+ }
902
+ $this->client_send($commandstring . static::LE, $command);
903
+
904
+ $this->last_reply = $this->get_lines();
905
+ // Fetch SMTP code and possible error code explanation
906
+ $matches = [];
907
+ if (preg_match('/^([0-9]{3})[ -](?:([0-9]\\.[0-9]\\.[0-9]) )?/', $this->last_reply, $matches)) {
908
+ $code = $matches[1];
909
+ $code_ex = (count($matches) > 2 ? $matches[2] : null);
910
+ // Cut off error code from each response line
911
+ $detail = preg_replace(
912
+ "/{$code}[ -]" .
913
+ ($code_ex ? str_replace('.', '\\.', $code_ex) . ' ' : '') . '/m',
914
+ '',
915
+ $this->last_reply
916
+ );
917
+ } else {
918
+ // Fall back to simple parsing if regex fails
919
+ $code = substr($this->last_reply, 0, 3);
920
+ $code_ex = null;
921
+ $detail = substr($this->last_reply, 4);
922
+ }
923
+
924
+ $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER);
925
+
926
+ if (!in_array($code, (array) $expect)) {
927
+ $this->setError(
928
+ "$command command failed",
929
+ $detail,
930
+ $code,
931
+ $code_ex
932
+ );
933
+ $this->edebug(
934
+ 'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply,
935
+ self::DEBUG_CLIENT
936
+ );
937
+
938
+ return false;
939
+ }
940
+
941
+ $this->setError('');
942
+
943
+ return true;
944
+ }
945
+
946
+ /**
947
+ * Send an SMTP SAML command.
948
+ * Starts a mail transaction from the email address specified in $from.
949
+ * Returns true if successful or false otherwise. If True
950
+ * the mail transaction is started and then one or more recipient
951
+ * commands may be called followed by a data command. This command
952
+ * will send the message to the users terminal if they are logged
953
+ * in and send them an email.
954
+ * Implements RFC 821: SAML <SP> FROM:<reverse-path> <CRLF>.
955
+ *
956
+ * @param string $from The address the message is from
957
+ *
958
+ * @return bool
959
+ */
960
+ public function sendAndMail($from)
961
+ {
962
+ return $this->sendCommand('SAML', "SAML FROM:$from", 250);
963
+ }
964
+
965
+ /**
966
+ * Send an SMTP VRFY command.
967
+ *
968
+ * @param string $name The name to verify
969
+ *
970
+ * @return bool
971
+ */
972
+ public function verify($name)
973
+ {
974
+ return $this->sendCommand('VRFY', "VRFY $name", [250, 251]);
975
+ }
976
+
977
+ /**
978
+ * Send an SMTP NOOP command.
979
+ * Used to keep keep-alives alive, doesn't actually do anything.
980
+ *
981
+ * @return bool
982
+ */
983
+ public function noop()
984
+ {
985
+ return $this->sendCommand('NOOP', 'NOOP', 250);
986
+ }
987
+
988
+ /**
989
+ * Send an SMTP TURN command.
990
+ * This is an optional command for SMTP that this class does not support.
991
+ * This method is here to make the RFC821 Definition complete for this class
992
+ * and _may_ be implemented in future.
993
+ * Implements from RFC 821: TURN <CRLF>.
994
+ *
995
+ * @return bool
996
+ */
997
+ public function turn()
998
+ {
999
+ $this->setError('The SMTP TURN command is not implemented');
1000
+ $this->edebug('SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT);
1001
+
1002
+ return false;
1003
+ }
1004
+
1005
+ /**
1006
+ * Send raw data to the server.
1007
+ *
1008
+ * @param string $data The data to send
1009
+ * @param string $command Optionally, the command this is part of, used only for controlling debug output
1010
+ *
1011
+ * @return int|bool The number of bytes sent to the server or false on error
1012
+ */
1013
+ public function client_send($data, $command = '')
1014
+ {
1015
+ //If SMTP transcripts are left enabled, or debug output is posted online
1016
+ //it can leak credentials, so hide credentials in all but lowest level
1017
+ if (self::DEBUG_LOWLEVEL > $this->do_debug and
1018
+ in_array($command, ['User & Password', 'Username', 'Password'], true)) {
1019
+ $this->edebug('CLIENT -> SERVER: <credentials hidden>', self::DEBUG_CLIENT);
1020
+ } else {
1021
+ $this->edebug('CLIENT -> SERVER: ' . $data, self::DEBUG_CLIENT);
1022
+ }
1023
+ set_error_handler([$this, 'errorHandler']);
1024
+ $result = fwrite($this->smtp_conn, $data);
1025
+ restore_error_handler();
1026
+
1027
+ return $result;
1028
+ }
1029
+
1030
+ /**
1031
+ * Get the latest error.
1032
+ *
1033
+ * @return array
1034
+ */
1035
+ public function getError()
1036
+ {
1037
+ return $this->error;
1038
+ }
1039
+
1040
+ /**
1041
+ * Get SMTP extensions available on the server.
1042
+ *
1043
+ * @return array|null
1044
+ */
1045
+ public function getServerExtList()
1046
+ {
1047
+ return $this->server_caps;
1048
+ }
1049
+
1050
+ /**
1051
+ * Get metadata about the SMTP server from its HELO/EHLO response.
1052
+ * The method works in three ways, dependent on argument value and current state:
1053
+ * 1. HELO/EHLO has not been sent - returns null and populates $this->error.
1054
+ * 2. HELO has been sent -
1055
+ * $name == 'HELO': returns server name
1056
+ * $name == 'EHLO': returns boolean false
1057
+ * $name == any other string: returns null and populates $this->error
1058
+ * 3. EHLO has been sent -
1059
+ * $name == 'HELO'|'EHLO': returns the server name
1060
+ * $name == any other string: if extension $name exists, returns True
1061
+ * or its options (e.g. AUTH mechanisms supported). Otherwise returns False.
1062
+ *
1063
+ * @param string $name Name of SMTP extension or 'HELO'|'EHLO'
1064
+ *
1065
+ * @return mixed
1066
+ */
1067
+ public function getServerExt($name)
1068
+ {
1069
+ if (!$this->server_caps) {
1070
+ $this->setError('No HELO/EHLO was sent');
1071
+
1072
+ return;
1073
+ }
1074
+
1075
+ if (!array_key_exists($name, $this->server_caps)) {
1076
+ if ('HELO' == $name) {
1077
+ return $this->server_caps['EHLO'];
1078
+ }
1079
+ if ('EHLO' == $name || array_key_exists('EHLO', $this->server_caps)) {
1080
+ return false;
1081
+ }
1082
+ $this->setError('HELO handshake was used; No information about server extensions available');
1083
+
1084
+ return;
1085
+ }
1086
+
1087
+ return $this->server_caps[$name];
1088
+ }
1089
+
1090
+ /**
1091
+ * Get the last reply from the server.
1092
+ *
1093
+ * @return string
1094
+ */
1095
+ public function getLastReply()
1096
+ {
1097
+ return $this->last_reply;
1098
+ }
1099
+
1100
+ /**
1101
+ * Read the SMTP server's response.
1102
+ * Either before eof or socket timeout occurs on the operation.
1103
+ * With SMTP we can tell if we have more lines to read if the
1104
+ * 4th character is '-' symbol. If it is a space then we don't
1105
+ * need to read anything else.
1106
+ *
1107
+ * @return string
1108
+ */
1109
+ protected function get_lines()
1110
+ {
1111
+ // If the connection is bad, give up straight away
1112
+ if (!is_resource($this->smtp_conn)) {
1113
+ return '';
1114
+ }
1115
+ $data = '';
1116
+ $endtime = 0;
1117
+ stream_set_timeout($this->smtp_conn, $this->Timeout);
1118
+ if ($this->Timelimit > 0) {
1119
+ $endtime = time() + $this->Timelimit;
1120
+ }
1121
+ $selR = [$this->smtp_conn];
1122
+ $selW = null;
1123
+ while (is_resource($this->smtp_conn) and !feof($this->smtp_conn)) {
1124
+ //Must pass vars in here as params are by reference
1125
+ if (!stream_select($selR, $selW, $selW, $this->Timelimit)) {
1126
+ $this->edebug(
1127
+ 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)',
1128
+ self::DEBUG_LOWLEVEL
1129
+ );
1130
+ break;
1131
+ }
1132
+ //Deliberate noise suppression - errors are handled afterwards
1133
+ $str = @fgets($this->smtp_conn, 515);
1134
+ $this->edebug('SMTP INBOUND: "' . trim($str) . '"', self::DEBUG_LOWLEVEL);
1135
+ $data .= $str;
1136
+ // If response is only 3 chars (not valid, but RFC5321 S4.2 says it must be handled),
1137
+ // or 4th character is a space, we are done reading, break the loop,
1138
+ // string array access is a micro-optimisation over strlen
1139
+ if (!isset($str[3]) or (isset($str[3]) and $str[3] == ' ')) {
1140
+ break;
1141
+ }
1142
+ // Timed-out? Log and break
1143
+ $info = stream_get_meta_data($this->smtp_conn);
1144
+ if ($info['timed_out']) {
1145
+ $this->edebug(
1146
+ 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)',
1147
+ self::DEBUG_LOWLEVEL
1148
+ );
1149
+ break;
1150
+ }
1151
+ // Now check if reads took too long
1152
+ if ($endtime and time() > $endtime) {
1153
+ $this->edebug(
1154
+ 'SMTP -> get_lines(): timelimit reached (' .
1155
+ $this->Timelimit . ' sec)',
1156
+ self::DEBUG_LOWLEVEL
1157
+ );
1158
+ break;
1159
+ }
1160
+ }
1161
+
1162
+ return $data;
1163
+ }
1164
+
1165
+ /**
1166
+ * Enable or disable VERP address generation.
1167
+ *
1168
+ * @param bool $enabled
1169
+ */
1170
+ public function setVerp($enabled = false)
1171
+ {
1172
+ $this->do_verp = $enabled;
1173
+ }
1174
+
1175
+ /**
1176
+ * Get VERP address generation mode.
1177
+ *
1178
+ * @return bool
1179
+ */
1180
+ public function getVerp()
1181
+ {
1182
+ return $this->do_verp;
1183
+ }
1184
+
1185
+ /**
1186
+ * Set error messages and codes.
1187
+ *
1188
+ * @param string $message The error message
1189
+ * @param string $detail Further detail on the error
1190
+ * @param string $smtp_code An associated SMTP error code
1191
+ * @param string $smtp_code_ex Extended SMTP code
1192
+ */
1193
+ protected function setError($message, $detail = '', $smtp_code = '', $smtp_code_ex = '')
1194
+ {
1195
+ $this->error = [
1196
+ 'error' => $message,
1197
+ 'detail' => $detail,
1198
+ 'smtp_code' => $smtp_code,
1199
+ 'smtp_code_ex' => $smtp_code_ex,
1200
+ ];
1201
+ }
1202
+
1203
+ /**
1204
+ * Set debug output method.
1205
+ *
1206
+ * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it
1207
+ */
1208
+ public function setDebugOutput($method = 'echo')
1209
+ {
1210
+ $this->Debugoutput = $method;
1211
+ }
1212
+
1213
+ /**
1214
+ * Get debug output method.
1215
+ *
1216
+ * @return string
1217
+ */
1218
+ public function getDebugOutput()
1219
+ {
1220
+ return $this->Debugoutput;
1221
+ }
1222
+
1223
+ /**
1224
+ * Set debug output level.
1225
+ *
1226
+ * @param int $level
1227
+ */
1228
+ public function setDebugLevel($level = 0)
1229
+ {
1230
+ $this->do_debug = $level;
1231
+ }
1232
+
1233
+ /**
1234
+ * Get debug output level.
1235
+ *
1236
+ * @return int
1237
+ */
1238
+ public function getDebugLevel()
1239
+ {
1240
+ return $this->do_debug;
1241
+ }
1242
+
1243
+ /**
1244
+ * Set SMTP timeout.
1245
+ *
1246
+ * @param int $timeout The timeout duration in seconds
1247
+ */
1248
+ public function setTimeout($timeout = 0)
1249
+ {
1250
+ $this->Timeout = $timeout;
1251
+ }
1252
+
1253
+ /**
1254
+ * Get SMTP timeout.
1255
+ *
1256
+ * @return int
1257
+ */
1258
+ public function getTimeout()
1259
+ {
1260
+ return $this->Timeout;
1261
+ }
1262
+
1263
+ /**
1264
+ * Reports an error number and string.
1265
+ *
1266
+ * @param int $errno The error number returned by PHP
1267
+ * @param string $errmsg The error message returned by PHP
1268
+ * @param string $errfile The file the error occurred in
1269
+ * @param int $errline The line number the error occurred on
1270
+ */
1271
+ protected function errorHandler($errno, $errmsg, $errfile = '', $errline = 0)
1272
+ {
1273
+ $notice = 'Connection failed.';
1274
+ $this->setError(
1275
+ $notice,
1276
+ $errmsg,
1277
+ (string) $errno
1278
+ );
1279
+ $this->edebug(
1280
+ "$notice Error #$errno: $errmsg [$errfile line $errline]",
1281
+ self::DEBUG_CONNECTION
1282
+ );
1283
+ }
1284
+
1285
+ /**
1286
+ * Extract and return the ID of the last SMTP transaction based on
1287
+ * a list of patterns provided in SMTP::$smtp_transaction_id_patterns.
1288
+ * Relies on the host providing the ID in response to a DATA command.
1289
+ * If no reply has been received yet, it will return null.
1290
+ * If no pattern was matched, it will return false.
1291
+ *
1292
+ * @return bool|null|string
1293
+ */
1294
+ protected function recordLastTransactionID()
1295
+ {
1296
+ $reply = $this->getLastReply();
1297
+
1298
+ if (empty($reply)) {
1299
+ $this->last_smtp_transaction_id = null;
1300
+ } else {
1301
+ $this->last_smtp_transaction_id = false;
1302
+ foreach ($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) {
1303
+ if (preg_match($smtp_transaction_id_pattern, $reply, $matches)) {
1304
+ $this->last_smtp_transaction_id = trim($matches[1]);
1305
+ break;
1306
+ }
1307
+ }
1308
+ }
1309
+
1310
+ return $this->last_smtp_transaction_id;
1311
+ }
1312
+
1313
+ /**
1314
+ * Get the queue/transaction ID of the last SMTP transaction
1315
+ * If no reply has been received yet, it will return null.
1316
+ * If no pattern was matched, it will return false.
1317
+ *
1318
+ * @return bool|null|string
1319
+ *
1320
+ * @see recordLastTransactionID()
1321
+ */
1322
+ public function getLastTransactionID()
1323
+ {
1324
+ return $this->last_smtp_transaction_id;
1325
+ }
1326
+ }