Post SMTP Mailer/Email Log - Version 1.7.8

Version Description

  • 2017-11-17 *
Download this release

Release Info

Developer yehudah
Plugin Icon 128x128 Post SMTP Mailer/Email Log
Version 1.7.8
Comparing to
See all releases

Code changes from version 1.7.7 to 1.7.8

Postman/Postman-Configuration/PostmanRegisterConfigurationSettings.php CHANGED
@@ -347,7 +347,7 @@ class PostmanSettingsRegistry {
347
  * Get the settings option array and print one of its values
348
  */
349
  public function log_level_callback() {
350
- $inputDescription = sprintf ( __ ( 'Log Level specifies the level of detail written to the <a target="_new" href="%s">WordPress Debug log</a> - view the log with <a target-"_new" href="%s">Debug</a>.', Postman::TEXT_DOMAIN ), 'https://codex.wordpress.org/Debugging_in_WordPress', 'https://wordpress.org/plugins/debug/' );
351
  printf ( '<select id="input_%2$s" class="input_%2$s" name="%1$s[%2$s]">', PostmanOptions::POSTMAN_OPTIONS, PostmanOptions::LOG_LEVEL );
352
  $currentKey = $this->options->getLogLevel ();
353
  $this->printSelectOption ( __ ( 'Off', Postman::TEXT_DOMAIN ), PostmanLogger::OFF_INT, $currentKey );
347
  * Get the settings option array and print one of its values
348
  */
349
  public function log_level_callback() {
350
+ $inputDescription = sprintf ( __ ( 'Log Level specifies the level of detail written to the <a target="_blank" href="%s">WordPress Debug log</a> - view the log with <a target-"_new" href="%s">Debug</a>.', Postman::TEXT_DOMAIN ), 'https://codex.wordpress.org/Debugging_in_WordPress', 'https://wordpress.org/plugins/debug/' );
351
  printf ( '<select id="input_%2$s" class="input_%2$s" name="%1$s[%2$s]">', PostmanOptions::POSTMAN_OPTIONS, PostmanOptions::LOG_LEVEL );
352
  $currentKey = $this->options->getLogLevel ();
353
  $this->printSelectOption ( __ ( 'Off', Postman::TEXT_DOMAIN ), PostmanLogger::OFF_INT, $currentKey );
Postman/Postman-Connectivity-Test/PostmanConnectivityTestController.php CHANGED
@@ -154,7 +154,7 @@ class PostmanConnectivityTestController {
154
  }
155
  print '</table>';
156
  /* Translators: Where %s is the name of the service providing Internet connectivity test */
157
- printf( '<p class="portquiz" style="display:none; font-size:0.8em">* %s</p>', sprintf( __( 'According to %s', Postman::TEXT_DOMAIN ), '<a target="_new" href="http://ww.downor.me/portquiz.net">portquiz.net</a>' ) );
158
  printf( '<p class="ajax-loader" style="display:none"><img src="%s"/></p>', plugins_url( 'post-smtp/style/ajax-loader.gif' ) );
159
  print '<section id="conclusion" style="display:none">';
160
  print sprintf( '<h3>%s:</h3>', __( 'Summary', Postman::TEXT_DOMAIN ) );
154
  }
155
  print '</table>';
156
  /* Translators: Where %s is the name of the service providing Internet connectivity test */
157
+ printf( '<p class="portquiz" style="display:none; font-size:0.8em">* %s</p>', sprintf( __( 'According to %s', Postman::TEXT_DOMAIN ), '<a target="_blank" href="http://ww.downor.me/portquiz.net">portquiz.net</a>' ) );
158
  printf( '<p class="ajax-loader" style="display:none"><img src="%s"/></p>', plugins_url( 'post-smtp/style/ajax-loader.gif' ) );
159
  print '<section id="conclusion" style="display:none">';
160
  print sprintf( '<h3>%s:</h3>', __( 'Summary', Postman::TEXT_DOMAIN ) );
Postman/Postman-Connectivity-Test/registered-domain-libs-master/generateEffectiveTLDs.php CHANGED
@@ -150,7 +150,7 @@ error_reporting(E_ERROR);
150
  $tldTree = array();
151
  $list = file_get_contents(URL);
152
  // $list = "bg\na.bg\n0.bg\n!c.bg\n";
153
- $lines = split("\n", $list);
154
  $licence = TRUE;
155
 
156
  if ($format == "php") echo "<?php\n";
@@ -176,7 +176,7 @@ foreach ($lines as $line) {
176
  }
177
 
178
  // this must be a TLD
179
- $tldParts = split('\.', $line);
180
  buildSubdomain($tldTree, $tldParts);
181
  }
182
 
150
  $tldTree = array();
151
  $list = file_get_contents(URL);
152
  // $list = "bg\na.bg\n0.bg\n!c.bg\n";
153
+ $lines = explode("\n", $list);
154
  $licence = TRUE;
155
 
156
  if ($format == "php") echo "<?php\n";
176
  }
177
 
178
  // this must be a TLD
179
+ $tldParts = preg_split('\.', $line);
180
  buildSubdomain($tldTree, $tldParts);
181
  }
182
 
Postman/Postman-Email-Log/PostmanEmailLogController.php CHANGED
@@ -5,194 +5,193 @@ require_once 'PostmanEmailLogView.php';
5
  /**
6
  *
7
  * @author jasonhendriks
8
- *
9
  */
10
  class PostmanEmailLogController {
11
  const RESEND_MAIL_AJAX_SLUG = 'postman_resend_mail';
12
  private $rootPluginFilenameAndPath;
13
  private $logger;
14
-
15
  /**
16
  */
17
- function __construct($rootPluginFilenameAndPath) {
18
  $this->rootPluginFilenameAndPath = $rootPluginFilenameAndPath;
19
- $this->logger = new PostmanLogger ( get_class ( $this ) );
20
- if (PostmanOptions::getInstance ()->isMailLoggingEnabled ()) {
21
- add_action ( 'admin_menu', array (
22
  $this,
23
- 'postmanAddMenuItem'
24
- ) );
25
  } else {
26
- $this->logger->trace ( 'not creating PostmanEmailLog admin menu item' );
27
  }
28
- if (PostmanUtils::isCurrentPagePostmanAdmin ( 'postman_email_log' )) {
29
- $this->logger->trace ( 'on postman email log page' );
30
  // $this->logger->debug ( 'Registering ' . $actionName . ' Action Post handler' );
31
- add_action ( 'admin_post_delete', array (
32
  $this,
33
- 'delete_log_item'
34
  ) );
35
- add_action ( 'admin_post_view', array (
36
  $this,
37
- 'view_log_item'
38
  ) );
39
- add_action ( 'admin_post_transcript', array (
40
  $this,
41
- 'view_transcript_log_item'
42
  ) );
43
- add_action ( 'admin_init', array (
44
  $this,
45
- 'on_admin_init'
46
  ) );
47
  }
48
- if (is_admin ()) {
49
  $actionName = self::RESEND_MAIL_AJAX_SLUG;
50
  $fullname = 'wp_ajax_' . $actionName;
51
  // $this->logger->debug ( 'Registering ' . 'wp_ajax_' . $fullname . ' Ajax handler' );
52
- add_action ( $fullname, array (
53
  $this,
54
- 'resendMail'
55
  ) );
56
  }
57
  }
58
-
59
  /**
60
  */
61
  function on_admin_init() {
62
- $this->handleBulkAction ();
63
  // register the stylesheet and javascript external resources
64
- $pluginData = apply_filters ( 'postman_get_plugin_metadata', null );
65
- wp_register_script ( 'postman_resend_email_script', plugins_url ( 'script/postman_resend_email_sript.js', $this->rootPluginFilenameAndPath ), array (
66
  PostmanViewController::JQUERY_SCRIPT,
67
- PostmanViewController::POSTMAN_SCRIPT
68
  ), $pluginData ['version'] );
69
  }
70
-
71
  /**
72
  */
73
  public function resendMail() {
74
  // get the email address of the recipient from the HTTP Request
75
- $postid = $this->getRequestParameter ( 'email' );
76
- if (! empty ( $postid )) {
77
- $post = get_post ( $postid );
78
- $meta_values = get_post_meta ( $postid );
79
-
80
- $success = wp_mail ( $meta_values ['original_to'] [0], $meta_values ['original_subject'] [0], $meta_values ['original_message'] [0], $meta_values ['original_headers'] [0] );
81
-
82
  // Postman API: retrieve the result of sending this message from Postman
83
- $result = apply_filters ( 'postman_wp_mail_result', null );
84
  $transcript = $result ['transcript'];
85
-
86
  // post-handling
87
- if ($success) {
88
- $this->logger->debug ( 'Email was successfully re-sent' );
89
  // the message was sent successfully, generate an appropriate message for the user
90
- $statusMessage = sprintf ( __ ( 'Your message was delivered (%d ms) to the SMTP server! Congratulations :)', Postman::TEXT_DOMAIN ), $result ['time'] );
91
-
92
  // compose the JSON response for the caller
93
- $response = array (
94
  'message' => $statusMessage,
95
- 'transcript' => $transcript
96
  );
97
- $this->logger->trace ( 'AJAX response' );
98
- $this->logger->trace ( $response );
99
  // send the JSON response
100
- wp_send_json_success ( $response );
101
  } else {
102
- $this->logger->error ( 'Email was not successfully re-sent - ' . $result ['exception']->getCode () );
103
  // the message was NOT sent successfully, generate an appropriate message for the user
104
- $statusMessage = $result ['exception']->getMessage ();
105
-
106
  // compose the JSON response for the caller
107
- $response = array (
108
  'message' => $statusMessage,
109
- 'transcript' => $transcript
110
  );
111
- $this->logger->trace ( 'AJAX response' );
112
- $this->logger->trace ( $response );
113
  // send the JSON response
114
- wp_send_json_error ( $response );
115
  }
116
  } else {
117
  // compose the JSON response for the caller
118
- $response = array ();
119
  // send the JSON response
120
- wp_send_json_error ( $response );
121
  }
122
  }
123
-
124
  /**
125
  * TODO move this somewhere reusable
126
  *
127
- * @param unknown $parameterName
128
  * @return unknown
129
  */
130
- private function getRequestParameter($parameterName) {
131
- if (isset ( $_POST [$parameterName] )) {
132
- $value = filter_var( $_POST [$parameterName], FILTER_SANITIZE_STRING );
133
- $this->logger->trace ( sprintf ( 'Found parameter "%s"', $parameterName ) );
134
- $this->logger->trace ( $value );
135
  return $value;
136
  }
137
  }
138
-
139
  /**
140
  * From https://www.skyverge.com/blog/add-custom-bulk-action/
141
  */
142
  function handleBulkAction() {
143
  // only do this for administrators
144
- if (PostmanUtils::isAdmin () && isset ( $_REQUEST ['email_log_entry'] )) {
145
- $this->logger->trace ( 'handling bulk action' );
146
- if (wp_verify_nonce ( $_REQUEST ['_wpnonce'], 'bulk-email_log_entries' )) {
147
- $this->logger->trace ( sprintf ( 'nonce "%s" passed validation', $_REQUEST ['_wpnonce'] ) );
148
- if (isset ( $_REQUEST ['action'] ) && ($_REQUEST ['action'] == 'bulk_delete' || $_REQUEST ['action2'] == 'bulk_delete')) {
149
- $this->logger->trace ( sprintf ( 'handling bulk delete' ) );
150
- $purger = new PostmanEmailLogPurger ();
151
  $postids = $_REQUEST ['email_log_entry'];
152
  foreach ( $postids as $postid ) {
153
- $purger->verifyLogItemExistsAndRemove ( $postid );
154
  }
155
- $mh = new PostmanMessageHandler ();
156
- $mh->addMessage ( __ ( 'Mail Log Entries were deleted.', Postman::TEXT_DOMAIN ) );
157
  } else {
158
- $this->logger->warn ( sprintf ( 'action "%s" not recognized', $_REQUEST ['action'] ) );
159
  }
160
  } else {
161
- $this->logger->warn ( sprintf ( 'nonce "%s" failed validation', $_REQUEST ['_wpnonce'] ) );
162
  }
163
- $this->redirectToLogPage ();
164
  }
165
  }
166
-
167
  /**
168
  */
169
  function delete_log_item() {
170
  // only do this for administrators
171
- if (PostmanUtils::isAdmin ()) {
172
- $this->logger->trace ( 'handling delete item' );
173
  $postid = $_REQUEST ['email'];
174
- if (wp_verify_nonce ( $_REQUEST ['_wpnonce'], 'delete_email_log_item_' . $postid )) {
175
- $this->logger->trace ( sprintf ( 'nonce "%s" passed validation', $_REQUEST ['_wpnonce'] ) );
176
- $purger = new PostmanEmailLogPurger ();
177
- $purger->verifyLogItemExistsAndRemove ( $postid );
178
- $mh = new PostmanMessageHandler ();
179
- $mh->addMessage ( __ ( 'Mail Log Entry was deleted.', Postman::TEXT_DOMAIN ) );
180
  } else {
181
- $this->logger->warn ( sprintf ( 'nonce "%s" failed validation', $_REQUEST ['_wpnonce'] ) );
182
  }
183
- $this->redirectToLogPage ();
184
  }
185
  }
186
-
187
  /**
188
  */
189
  function view_log_item() {
190
  // only do this for administrators
191
- if (PostmanUtils::isAdmin ()) {
192
- $this->logger->trace ( 'handling view item' );
193
  $postid = $_REQUEST ['email'];
194
- $post = get_post ( $postid );
195
- $meta_values = get_post_meta ( $postid );
196
  // https://css-tricks.com/examples/hrs/
197
  print '<html><head><style>body {font-family: monospace;} hr {
198
  border: 0;
@@ -200,110 +199,119 @@ class PostmanEmailLogController {
200
  background: #bbb;
201
  }</style></head><body>';
202
  print '<table>';
203
- if (! empty ( $meta_values ['from_header'] [0] )) {
204
- printf ( '<tr><th style="text-align:right">%s:</th><td>%s</td></tr>', _x ( 'From', 'Who is this message From?', Postman::TEXT_DOMAIN ), esc_html ( $meta_values ['from_header'] [0] ) );
205
  }
206
  // show the To header (it's optional)
207
- if (! empty ( $meta_values ['to_header'] [0] )) {
208
- printf ( '<tr><th style="text-align:right">%s:</th><td>%s</td></tr>', _x ( 'To', 'Who is this message To?', Postman::TEXT_DOMAIN ), esc_html ( $meta_values ['to_header'] [0] ) );
209
  }
210
  // show the Cc header (it's optional)
211
- if (! empty ( $meta_values ['cc_header'] [0] )) {
212
- printf ( '<tr><th style="text-align:right">%s:</th><td>%s</td></tr>', _x ( 'Cc', 'Who is this message Cc\'d to?', Postman::TEXT_DOMAIN ), esc_html ( $meta_values ['cc_header'] [0] ) );
213
  }
214
  // show the Bcc header (it's optional)
215
- if (! empty ( $meta_values ['bcc_header'] [0] )) {
216
- printf ( '<tr><th style="text-align:right">%s:</th><td>%s</td></tr>', _x ( 'Bcc', 'Who is this message Bcc\'d to?', Postman::TEXT_DOMAIN ), esc_html ( $meta_values ['bcc_header'] [0] ) );
217
  }
218
  // show the Reply-To header (it's optional)
219
- if (! empty ( $meta_values ['reply_to_header'] [0] )) {
220
- printf ( '<tr><th style="text-align:right">%s:</th><td>%s</td></tr>', __ ( 'Reply-To', Postman::TEXT_DOMAIN ), esc_html ( $meta_values ['reply_to_header'] [0] ) );
221
  }
222
- printf ( '<tr><th style="text-align:right">%s:</th><td>%s</td></tr>', _x ( 'Date', 'What is the date today?', Postman::TEXT_DOMAIN ), $post->post_date );
223
- printf ( '<tr><th style="text-align:right">%s:</th><td>%s</td></tr>', _x ( 'Subject', 'What is the subject of this message?', Postman::TEXT_DOMAIN ), esc_html ( $post->post_title ) );
224
  // The Transport UI is always there, in more recent versions that is
225
- if (! empty ( $meta_values ['transport_uri'] [0] )) {
226
- printf ( '<tr><th style="text-align:right">%s:</th><td>%s</td></tr>', _x ( 'Delivery-URI', 'What is the unique URI of the configuration?', Postman::TEXT_DOMAIN ), esc_html ( $meta_values ['transport_uri'] [0] ) );
227
  }
228
  print '</table>';
229
  print '<hr/>';
230
  print '<pre>';
231
- print esc_html ( $post->post_content );
232
  print '</pre>';
233
  print '</body></html>';
234
- die ();
235
  }
236
  }
237
-
 
 
 
 
 
 
 
238
  /**
239
  */
240
  function view_transcript_log_item() {
241
  // only do this for administrators
242
- if (PostmanUtils::isAdmin ()) {
243
- $this->logger->trace ( 'handling view transcript item' );
244
  $postid = $_REQUEST ['email'];
245
- $post = get_post ( $postid );
246
- $meta_values = get_post_meta ( $postid );
247
  // https://css-tricks.com/examples/hrs/
248
  print '<html><head><style>body {font-family: monospace;} hr {
249
  border: 0;
250
  border-bottom: 1px dashed #ccc;
251
  background: #bbb;
252
  }</style></head><body>';
253
- printf ( '<p>%s</p>', __ ( 'This is the conversation between Postman and the mail server. It can be useful for diagnosing problems. <b>DO NOT</b> post it on-line, it may contain your account password.', Postman::TEXT_DOMAIN ) );
254
  print '<hr/>';
255
  print '<pre>';
256
- if (! empty ( $meta_values ['session_transcript'] [0] )) {
257
- print esc_html ( $meta_values ['session_transcript'] [0] );
258
  } else {
259
  /* Translators: Meaning "Not Applicable" */
260
- print __ ( 'n/a', Postman::TEXT_DOMAIN );
261
  }
262
  print '</pre>';
263
  print '</body></html>';
264
- die ();
265
  }
266
  }
267
-
268
  /**
269
  * For whatever reason, PostmanUtils::get..url doesn't work here? :(
270
  */
271
  function redirectToLogPage() {
272
- PostmanUtils::redirect ( PostmanUtils::POSTMAN_EMAIL_LOG_PAGE_RELATIVE_URL );
273
- die ();
274
  }
275
-
276
  /**
277
  * Register the page
278
  */
279
  function postmanAddMenuItem() {
280
  // only do this for administrators
281
- if (PostmanUtils::isAdmin ()) {
282
- $this->logger->trace ( 'created PostmanEmailLog admin menu item' );
283
- /* Translators where (%s) is the name of the plugin */
284
- $page = add_management_page ( sprintf ( __ ( '%s Email Log', Postman::TEXT_DOMAIN ), __ ( 'Postman SMTP', Postman::TEXT_DOMAIN ) ), _x ( 'Email Log', 'The log of Emails that have been delivered', Postman::TEXT_DOMAIN ), 'read_private_posts', 'postman_email_log', array (
285
- $this,
286
- 'postman_render_email_page'
287
- ) );
 
 
288
  // When the plugin options page is loaded, also load the stylesheet
289
- add_action ( 'admin_print_styles-' . $page, array (
290
  $this,
291
- 'postman_email_log_enqueue_resources'
292
  ) );
293
  }
294
  }
295
  function postman_email_log_enqueue_resources() {
296
- $pluginData = apply_filters ( 'postman_get_plugin_metadata', null );
297
- wp_register_style ( 'postman_email_log', plugins_url ( 'style/postman-email-log.css', $this->rootPluginFilenameAndPath ), null, $pluginData ['version'] );
298
- wp_enqueue_style ( 'postman_email_log' );
299
- wp_enqueue_script ( 'postman_resend_email_script' );
300
- wp_enqueue_script ( 'sprintf' );
301
- wp_localize_script ( 'postman_resend_email_script', 'postman_js_email_was_resent', __ ( 'Email was successfully resent (but without attachments)', Postman::TEXT_DOMAIN ) );
302
  /* Translators: Where %s is an error message */
303
- wp_localize_script ( 'postman_resend_email_script', 'postman_js_email_not_resent', __ ( 'Email could not be resent. Error: %s', Postman::TEXT_DOMAIN ) );
304
- wp_localize_script ( 'postman_resend_email_script', 'postman_js_resend_label', __ ( 'Resend', Postman::TEXT_DOMAIN ) );
305
  }
306
-
307
  /**
308
  * *************************** RENDER TEST PAGE ********************************
309
  * ******************************************************************************
@@ -315,40 +323,71 @@ class PostmanEmailLogController {
315
  * it's the way the list tables are used in the WordPress core.
316
  */
317
  function postman_render_email_page() {
318
-
319
  // Create an instance of our package class...
320
- $testListTable = new PostmanEmailLogView ();
321
- wp_enqueue_script ( 'postman_resend_email_script' );
322
  // Fetch, prepare, sort, and filter our data...
323
- $testListTable->prepare_items ();
324
-
325
  ?>
326
  <div class="wrap">
327
 
328
  <div id="icon-users" class="icon32">
329
  <br />
330
  </div>
331
- <h2><?php
332
- /* Translators where (%s) is the name of the plugin */
333
- echo sprintf ( __ ( '%s Email Log', Postman::TEXT_DOMAIN ), __ ( 'Postman SMTP', Postman::TEXT_DOMAIN ) )?></h2>
334
 
335
  <div
336
  style="background: #ECECEC; border: 1px solid #CCC; padding: 0 10px; margin-top: 5px; border-radius: 5px; -moz-border-radius: 5px; -webkit-border-radius: 5px;">
337
  <p><?php
338
-
339
- echo __ ( 'This is a record of deliveries made to the mail server. It does not neccessarily indicate sucessful delivery to the recipient.', Postman::TEXT_DOMAIN )?></p>
340
  </div>
341
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
342
  <!-- Forms are NOT created automatically, so you need to wrap the table in one to use features like bulk actions -->
343
  <form id="movies-filter" method="get">
344
  <!-- For plugins, we also need to ensure that the form posts back to our current page -->
345
  <input type="hidden" name="page"
346
  value="<?php echo filter_input( INPUT_GET, 'page', FILTER_SANITIZE_STRING ); ?>" />
 
347
  <!-- Now we can render the completed list table -->
348
- <?php $testListTable->display()?>
349
- </form>
350
-
351
- <?php add_thickbox(); ?>
352
 
353
  </div>
354
  <?php
5
  /**
6
  *
7
  * @author jasonhendriks
 
8
  */
9
  class PostmanEmailLogController {
10
  const RESEND_MAIL_AJAX_SLUG = 'postman_resend_mail';
11
  private $rootPluginFilenameAndPath;
12
  private $logger;
13
+
14
  /**
15
  */
16
+ function __construct( $rootPluginFilenameAndPath ) {
17
  $this->rootPluginFilenameAndPath = $rootPluginFilenameAndPath;
18
+ $this->logger = new PostmanLogger( get_class( $this ) );
19
+ if ( PostmanOptions::getInstance()->isMailLoggingEnabled() ) {
20
+ add_action( 'admin_menu', array(
21
  $this,
22
+ 'postmanAddMenuItem',
23
+ ),20 );
24
  } else {
25
+ $this->logger->trace( 'not creating PostmanEmailLog admin menu item' );
26
  }
27
+ if ( PostmanUtils::isCurrentPagePostmanAdmin( 'postman_email_log' ) ) {
28
+ $this->logger->trace( 'on postman email log page' );
29
  // $this->logger->debug ( 'Registering ' . $actionName . ' Action Post handler' );
30
+ add_action( 'admin_post_delete', array(
31
  $this,
32
+ 'delete_log_item',
33
  ) );
34
+ add_action( 'admin_post_view', array(
35
  $this,
36
+ 'view_log_item',
37
  ) );
38
+ add_action( 'admin_post_transcript', array(
39
  $this,
40
+ 'view_transcript_log_item',
41
  ) );
42
+ add_action( 'admin_init', array(
43
  $this,
44
+ 'on_admin_init',
45
  ) );
46
  }
47
+ if ( is_admin() ) {
48
  $actionName = self::RESEND_MAIL_AJAX_SLUG;
49
  $fullname = 'wp_ajax_' . $actionName;
50
  // $this->logger->debug ( 'Registering ' . 'wp_ajax_' . $fullname . ' Ajax handler' );
51
+ add_action( $fullname, array(
52
  $this,
53
+ 'resendMail',
54
  ) );
55
  }
56
  }
57
+
58
  /**
59
  */
60
  function on_admin_init() {
61
+ $this->handleBulkAction();
62
  // register the stylesheet and javascript external resources
63
+ $pluginData = apply_filters( 'postman_get_plugin_metadata', null );
64
+ wp_register_script( 'postman_resend_email_script', plugins_url( 'script/postman_resend_email_sript.js', $this->rootPluginFilenameAndPath ), array(
65
  PostmanViewController::JQUERY_SCRIPT,
66
+ PostmanViewController::POSTMAN_SCRIPT,
67
  ), $pluginData ['version'] );
68
  }
69
+
70
  /**
71
  */
72
  public function resendMail() {
73
  // get the email address of the recipient from the HTTP Request
74
+ $postid = $this->getRequestParameter( 'email' );
75
+ if ( ! empty( $postid ) ) {
76
+ $post = get_post( $postid );
77
+ $meta_values = get_post_meta( $postid );
78
+
79
+ $success = wp_mail( $meta_values ['original_to'] [0], $meta_values ['original_subject'] [0], $meta_values ['original_message'] [0], $meta_values ['original_headers'] [0] );
80
+
81
  // Postman API: retrieve the result of sending this message from Postman
82
+ $result = apply_filters( 'postman_wp_mail_result', null );
83
  $transcript = $result ['transcript'];
84
+
85
  // post-handling
86
+ if ( $success ) {
87
+ $this->logger->debug( 'Email was successfully re-sent' );
88
  // the message was sent successfully, generate an appropriate message for the user
89
+ $statusMessage = sprintf( __( 'Your message was delivered (%d ms) to the SMTP server! Congratulations :)', Postman::TEXT_DOMAIN ), $result ['time'] );
90
+
91
  // compose the JSON response for the caller
92
+ $response = array(
93
  'message' => $statusMessage,
94
+ 'transcript' => $transcript,
95
  );
96
+ $this->logger->trace( 'AJAX response' );
97
+ $this->logger->trace( $response );
98
  // send the JSON response
99
+ wp_send_json_success( $response );
100
  } else {
101
+ $this->logger->error( 'Email was not successfully re-sent - ' . $result ['exception']->getCode() );
102
  // the message was NOT sent successfully, generate an appropriate message for the user
103
+ $statusMessage = $result ['exception']->getMessage();
104
+
105
  // compose the JSON response for the caller
106
+ $response = array(
107
  'message' => $statusMessage,
108
+ 'transcript' => $transcript,
109
  );
110
+ $this->logger->trace( 'AJAX response' );
111
+ $this->logger->trace( $response );
112
  // send the JSON response
113
+ wp_send_json_error( $response );
114
  }
115
  } else {
116
  // compose the JSON response for the caller
117
+ $response = array();
118
  // send the JSON response
119
+ wp_send_json_error( $response );
120
  }
121
  }
122
+
123
  /**
124
  * TODO move this somewhere reusable
125
  *
126
+ * @param unknown $parameterName
127
  * @return unknown
128
  */
129
+ private function getRequestParameter( $parameterName ) {
130
+ if ( isset( $_POST [ $parameterName ] ) ) {
131
+ $value = filter_var( $_POST [ $parameterName ], FILTER_SANITIZE_STRING );
132
+ $this->logger->trace( sprintf( 'Found parameter "%s"', $parameterName ) );
133
+ $this->logger->trace( $value );
134
  return $value;
135
  }
136
  }
137
+
138
  /**
139
  * From https://www.skyverge.com/blog/add-custom-bulk-action/
140
  */
141
  function handleBulkAction() {
142
  // only do this for administrators
143
+ if ( PostmanUtils::isAdmin() && isset( $_REQUEST ['email_log_entry'] ) ) {
144
+ $this->logger->trace( 'handling bulk action' );
145
+ if ( wp_verify_nonce( $_REQUEST ['_wpnonce'], 'bulk-email_log_entries' ) ) {
146
+ $this->logger->trace( sprintf( 'nonce "%s" passed validation', $_REQUEST ['_wpnonce'] ) );
147
+ if ( isset( $_REQUEST ['action'] ) && ($_REQUEST ['action'] == 'bulk_delete' || $_REQUEST ['action2'] == 'bulk_delete') ) {
148
+ $this->logger->trace( sprintf( 'handling bulk delete' ) );
149
+ $purger = new PostmanEmailLogPurger();
150
  $postids = $_REQUEST ['email_log_entry'];
151
  foreach ( $postids as $postid ) {
152
+ $purger->verifyLogItemExistsAndRemove( $postid );
153
  }
154
+ $mh = new PostmanMessageHandler();
155
+ $mh->addMessage( __( 'Mail Log Entries were deleted.', Postman::TEXT_DOMAIN ) );
156
  } else {
157
+ $this->logger->warn( sprintf( 'action "%s" not recognized', $_REQUEST ['action'] ) );
158
  }
159
  } else {
160
+ $this->logger->warn( sprintf( 'nonce "%s" failed validation', $_REQUEST ['_wpnonce'] ) );
161
  }
162
+ $this->redirectToLogPage();
163
  }
164
  }
165
+
166
  /**
167
  */
168
  function delete_log_item() {
169
  // only do this for administrators
170
+ if ( PostmanUtils::isAdmin() ) {
171
+ $this->logger->trace( 'handling delete item' );
172
  $postid = $_REQUEST ['email'];
173
+ if ( wp_verify_nonce( $_REQUEST ['_wpnonce'], 'delete_email_log_item_' . $postid ) ) {
174
+ $this->logger->trace( sprintf( 'nonce "%s" passed validation', $_REQUEST ['_wpnonce'] ) );
175
+ $purger = new PostmanEmailLogPurger();
176
+ $purger->verifyLogItemExistsAndRemove( $postid );
177
+ $mh = new PostmanMessageHandler();
178
+ $mh->addMessage( __( 'Mail Log Entry was deleted.', Postman::TEXT_DOMAIN ) );
179
  } else {
180
+ $this->logger->warn( sprintf( 'nonce "%s" failed validation', $_REQUEST ['_wpnonce'] ) );
181
  }
182
+ $this->redirectToLogPage();
183
  }
184
  }
185
+
186
  /**
187
  */
188
  function view_log_item() {
189
  // only do this for administrators
190
+ if ( PostmanUtils::isAdmin() ) {
191
+ $this->logger->trace( 'handling view item' );
192
  $postid = $_REQUEST ['email'];
193
+ $post = get_post( $postid );
194
+ $meta_values = get_post_meta( $postid );
195
  // https://css-tricks.com/examples/hrs/
196
  print '<html><head><style>body {font-family: monospace;} hr {
197
  border: 0;
199
  background: #bbb;
200
  }</style></head><body>';
201
  print '<table>';
202
+ if ( ! empty( $meta_values ['from_header'] [0] ) ) {
203
+ printf( '<tr><th style="text-align:right">%s:</th><td>%s</td></tr>', _x( 'From', 'Who is this message From?', Postman::TEXT_DOMAIN ), esc_html( $meta_values ['from_header'] [0] ) );
204
  }
205
  // show the To header (it's optional)
206
+ if ( ! empty( $meta_values ['to_header'] [0] ) ) {
207
+ printf( '<tr><th style="text-align:right">%s:</th><td>%s</td></tr>', _x( 'To', 'Who is this message To?', Postman::TEXT_DOMAIN ), esc_html( $meta_values ['to_header'] [0] ) );
208
  }
209
  // show the Cc header (it's optional)
210
+ if ( ! empty( $meta_values ['cc_header'] [0] ) ) {
211
+ printf( '<tr><th style="text-align:right">%s:</th><td>%s</td></tr>', _x( 'Cc', 'Who is this message Cc\'d to?', Postman::TEXT_DOMAIN ), esc_html( $meta_values ['cc_header'] [0] ) );
212
  }
213
  // show the Bcc header (it's optional)
214
+ if ( ! empty( $meta_values ['bcc_header'] [0] ) ) {
215
+ printf( '<tr><th style="text-align:right">%s:</th><td>%s</td></tr>', _x( 'Bcc', 'Who is this message Bcc\'d to?', Postman::TEXT_DOMAIN ), esc_html( $meta_values ['bcc_header'] [0] ) );
216
  }
217
  // show the Reply-To header (it's optional)
218
+ if ( ! empty( $meta_values ['reply_to_header'] [0] ) ) {
219
+ printf( '<tr><th style="text-align:right">%s:</th><td>%s</td></tr>', __( 'Reply-To', Postman::TEXT_DOMAIN ), esc_html( $meta_values ['reply_to_header'] [0] ) );
220
  }
221
+ printf( '<tr><th style="text-align:right">%s:</th><td>%s</td></tr>', _x( 'Date', 'What is the date today?', Postman::TEXT_DOMAIN ), $post->post_date );
222
+ printf( '<tr><th style="text-align:right">%s:</th><td>%s</td></tr>', _x( 'Subject', 'What is the subject of this message?', Postman::TEXT_DOMAIN ), esc_html( $post->post_title ) );
223
  // The Transport UI is always there, in more recent versions that is
224
+ if ( ! empty( $meta_values ['transport_uri'] [0] ) ) {
225
+ printf( '<tr><th style="text-align:right">%s:</th><td>%s</td></tr>', _x( 'Delivery-URI', 'What is the unique URI of the configuration?', Postman::TEXT_DOMAIN ), esc_html( $meta_values ['transport_uri'] [0] ) );
226
  }
227
  print '</table>';
228
  print '<hr/>';
229
  print '<pre>';
230
+ print $this->sanitize_message( $post->post_content );
231
  print '</pre>';
232
  print '</body></html>';
233
+ die();
234
  }
235
  }
236
+
237
+ function sanitize_message( $message ) {
238
+ $allowed_tags = wp_kses_allowed_html( 'post' );
239
+ $allowed_tags['style'] = array();
240
+
241
+ return wp_kses( $message, $allowed_tags );
242
+ }
243
+
244
  /**
245
  */
246
  function view_transcript_log_item() {
247
  // only do this for administrators
248
+ if ( PostmanUtils::isAdmin() ) {
249
+ $this->logger->trace( 'handling view transcript item' );
250
  $postid = $_REQUEST ['email'];
251
+ $post = get_post( $postid );
252
+ $meta_values = get_post_meta( $postid );
253
  // https://css-tricks.com/examples/hrs/
254
  print '<html><head><style>body {font-family: monospace;} hr {
255
  border: 0;
256
  border-bottom: 1px dashed #ccc;
257
  background: #bbb;
258
  }</style></head><body>';
259
+ printf( '<p>%s</p>', __( 'This is the conversation between Postman and the mail server. It can be useful for diagnosing problems. <b>DO NOT</b> post it on-line, it may contain your account password.', Postman::TEXT_DOMAIN ) );
260
  print '<hr/>';
261
  print '<pre>';
262
+ if ( ! empty( $meta_values ['session_transcript'] [0] ) ) {
263
+ print esc_html( $meta_values ['session_transcript'] [0] );
264
  } else {
265
  /* Translators: Meaning "Not Applicable" */
266
+ print __( 'n/a', Postman::TEXT_DOMAIN );
267
  }
268
  print '</pre>';
269
  print '</body></html>';
270
+ die();
271
  }
272
  }
273
+
274
  /**
275
  * For whatever reason, PostmanUtils::get..url doesn't work here? :(
276
  */
277
  function redirectToLogPage() {
278
+ PostmanUtils::redirect( PostmanUtils::POSTMAN_EMAIL_LOG_PAGE_RELATIVE_URL );
279
+ die();
280
  }
281
+
282
  /**
283
  * Register the page
284
  */
285
  function postmanAddMenuItem() {
286
  // only do this for administrators
287
+ if ( PostmanUtils::isAdmin() ) {
288
+ $this->logger->trace( 'created PostmanEmailLog admin menu item' );
289
+ /*
290
+ Translators where (%s) is the name of the plugin */
291
+ $pageTitle = sprintf( __( '%s Email Log', Postman::TEXT_DOMAIN ), __( 'Postman SMTP', Postman::TEXT_DOMAIN ) );
292
+ $pluginName = _x( 'Email Log', 'The log of Emails that have been delivered', Postman::TEXT_DOMAIN );
293
+
294
+ $page = add_submenu_page( PostmanViewController::POSTMAN_MENU_SLUG, $pageTitle, $pluginName, 'read_private_posts', 'postman_email_log', array( $this, 'postman_render_email_page' ) );
295
+
296
  // When the plugin options page is loaded, also load the stylesheet
297
+ add_action( 'admin_print_styles-' . $page, array(
298
  $this,
299
+ 'postman_email_log_enqueue_resources',
300
  ) );
301
  }
302
  }
303
  function postman_email_log_enqueue_resources() {
304
+ $pluginData = apply_filters( 'postman_get_plugin_metadata', null );
305
+ wp_register_style( 'postman_email_log', plugins_url( 'style/postman-email-log.css', $this->rootPluginFilenameAndPath ), null, $pluginData ['version'] );
306
+ wp_enqueue_style( 'postman_email_log' );
307
+ wp_enqueue_script( 'postman_resend_email_script' );
308
+ wp_enqueue_script( 'sprintf' );
309
+ wp_localize_script( 'postman_resend_email_script', 'postman_js_email_was_resent', __( 'Email was successfully resent (but without attachments)', Postman::TEXT_DOMAIN ) );
310
  /* Translators: Where %s is an error message */
311
+ wp_localize_script( 'postman_resend_email_script', 'postman_js_email_not_resent', __( 'Email could not be resent. Error: %s', Postman::TEXT_DOMAIN ) );
312
+ wp_localize_script( 'postman_resend_email_script', 'postman_js_resend_label', __( 'Resend', Postman::TEXT_DOMAIN ) );
313
  }
314
+
315
  /**
316
  * *************************** RENDER TEST PAGE ********************************
317
  * ******************************************************************************
323
  * it's the way the list tables are used in the WordPress core.
324
  */
325
  function postman_render_email_page() {
326
+
327
  // Create an instance of our package class...
328
+ $testListTable = new PostmanEmailLogView();
329
+ wp_enqueue_script( 'postman_resend_email_script' );
330
  // Fetch, prepare, sort, and filter our data...
331
+ $testListTable->prepare_items();
332
+
333
  ?>
334
  <div class="wrap">
335
 
336
  <div id="icon-users" class="icon32">
337
  <br />
338
  </div>
339
+ <h2><?php
340
+ /* Translators where (%s) is the name of the plugin */
341
+ echo sprintf( __( '%s Email Log', Postman::TEXT_DOMAIN ), __( 'Postman SMTP', Postman::TEXT_DOMAIN ) )?></h2>
342
 
343
  <div
344
  style="background: #ECECEC; border: 1px solid #CCC; padding: 0 10px; margin-top: 5px; border-radius: 5px; -moz-border-radius: 5px; -webkit-border-radius: 5px;">
345
  <p><?php
346
+
347
+ echo __( 'This is a record of deliveries made to the mail server. It does not neccessarily indicate sucessful delivery to the recipient.', Postman::TEXT_DOMAIN )?></p>
348
  </div>
349
 
350
+ <?php
351
+ $from_date = isset( $_POST['from_date'] ) ? sanitize_text_field( $_POST['from_date'] ) : '';
352
+ $to_date = isset( $_POST['to_date'] ) ? sanitize_text_field( $_POST['to_date'] ) : '';
353
+ $search = isset( $_POST['search'] ) ? sanitize_text_field( $_POST['search'] ) : '';
354
+ ?>
355
+
356
+ <form id="postman-email-log-filter" method="post">
357
+ <div id="email-log-filter">
358
+ <div class="form-control">
359
+ <label for="from_date"><?php _e( 'From Date', Postman::TEXT_DOMAIN ); ?></label>
360
+ <input id="from_date" class="email-log-date" value="<?php echo $from_date; ?>" type="text" name="from_date" placeholder="<?php _e( 'From Date', Postman::TEXT_DOMAIN ); ?>">
361
+ </div>
362
+ <div class="form-control">
363
+ <label for="to_date"><?php _e( 'To Date', Postman::TEXT_DOMAIN ); ?></label>
364
+ <input id="to_date" class="email-log-date" value="<?php echo $to_date; ?>" type="text" name="to_date" placeholder="<?php _e( 'To Date', Postman::TEXT_DOMAIN ); ?>">
365
+ </div>
366
+ <div class="form-control">
367
+ <label for="search"><?php _e( 'Search', Postman::TEXT_DOMAIN ); ?></label>
368
+ <input id="search" type="text" name="search" value="<?php echo $search; ?>" placeholder="<?php _e( 'Search', Postman::TEXT_DOMAIN ); ?>">
369
+ </div>
370
+ <div class="form-control">
371
+ <button type="submit" name="filter" class="button button-primary"><?php _e( 'Filter', Postman::TEXT_DOMAIN ); ?></button>
372
+ </div>
373
+
374
+ <div class="form-control">
375
+ <!-- <button type="submit" name="export_email_logs" class="button button-primary">Export To CSV</button> -->
376
+ </div>
377
+ </div>
378
+ </form>
379
+
380
  <!-- Forms are NOT created automatically, so you need to wrap the table in one to use features like bulk actions -->
381
  <form id="movies-filter" method="get">
382
  <!-- For plugins, we also need to ensure that the form posts back to our current page -->
383
  <input type="hidden" name="page"
384
  value="<?php echo filter_input( INPUT_GET, 'page', FILTER_SANITIZE_STRING ); ?>" />
385
+
386
  <!-- Now we can render the completed list table -->
387
+ <?php $testListTable->display()?>
388
+ </form>
389
+
390
+ <?php add_thickbox(); ?>
391
 
392
  </div>
393
  <?php
Postman/Postman-Email-Log/PostmanEmailLogService.php CHANGED
@@ -1,5 +1,5 @@
1
  <?php
2
- if (! class_exists ( 'PostmanEmailLog' )) {
3
  class PostmanEmailLog {
4
  public $sender;
5
  public $toRecipients;
@@ -19,15 +19,15 @@ if (! class_exists ( 'PostmanEmailLog' )) {
19
  }
20
  }
21
 
22
- if (! class_exists ( 'PostmanEmailLogService' )) {
23
-
24
  /**
25
  * This class creates the Custom Post Type for Email Logs and handles writing these posts.
26
  *
27
  * @author jasonhendriks
28
  */
29
  class PostmanEmailLogService {
30
-
31
  /*
32
  * Private content is published only for your eyes, or the eyes of only those with authorization
33
  * permission levels to see private content. Normal users and visitors will not be aware of
@@ -36,170 +36,182 @@ if (! class_exists ( 'PostmanEmailLogService' )) {
36
  * the private content when you are logged into your WordPress blog.
37
  */
38
  const POSTMAN_CUSTOM_POST_STATUS_PRIVATE = 'private';
39
-
40
  // member variables
41
  private $logger;
42
  private $inst;
43
-
44
  /**
45
  * Constructor
46
  */
47
  private function __construct() {
48
- $this->logger = new PostmanLogger ( get_class ( $this ) );
49
  }
50
-
51
  /**
52
  * singleton instance
53
  */
54
  public static function getInstance() {
55
  static $inst = null;
56
- if ($inst === null) {
57
- $inst = new PostmanEmailLogService ();
58
  }
59
  return $inst;
60
  }
61
-
62
  /**
63
  * Logs successful email attempts
64
  *
65
- * @param PostmanMessage $message
66
- * @param unknown $transcript
67
- * @param PostmanModuleTransport $transport
68
  */
69
- public function writeSuccessLog(PostmanEmailLog $log, PostmanMessage $message, $transcript, PostmanModuleTransport $transport) {
70
- if (PostmanOptions::getInstance ()->isMailLoggingEnabled ()) {
71
  $statusMessage = '';
72
  $status = true;
73
- $subject = $message->getSubject ();
74
- if (empty ( $subject )) {
75
- $statusMessage = sprintf ( '%s: %s', __ ( 'Warning', Postman::TEXT_DOMAIN ), __ ( 'An empty subject line can result in delivery failure.', Postman::TEXT_DOMAIN ) );
76
  $status = 'WARN';
77
  }
78
- $this->createLog ( $log, $message, $transcript, $statusMessage, $status, $transport );
79
- $this->writeToEmailLog ( $log );
80
  }
81
  }
82
-
83
  /**
84
  * Logs failed email attempts, requires more metadata so the email can be resent in the future
85
  *
86
- * @param PostmanMessage $message
87
- * @param unknown $transcript
88
- * @param PostmanModuleTransport $transport
89
- * @param unknown $statusMessage
90
- * @param unknown $originalTo
91
- * @param unknown $originalSubject
92
- * @param unknown $originalMessage
93
- * @param unknown $originalHeaders
94
  */
95
- public function writeFailureLog(PostmanEmailLog $log, PostmanMessage $message = null, $transcript, PostmanModuleTransport $transport, $statusMessage) {
96
- if (PostmanOptions::getInstance ()->isMailLoggingEnabled ()) {
97
- $this->createLog ( $log, $message, $transcript, $statusMessage, false, $transport );
98
- $this->writeToEmailLog ( $log );
99
  }
100
  }
101
-
102
  /**
103
  * Writes an email sending attempt to the Email Log
104
  *
105
  * From http://wordpress.stackexchange.com/questions/8569/wp-insert-post-php-function-and-custom-fields
106
  */
107
- private function writeToEmailLog(PostmanEmailLog $log) {
 
 
108
  // nothing here is sanitized as WordPress should take care of
109
  // making database writes safe
110
- $my_post = array (
111
  'post_type' => PostmanEmailLogPostType::POSTMAN_CUSTOM_POST_TYPE_SLUG,
112
  'post_title' => $log->subject,
113
  'post_content' => $log->body,
114
  'post_excerpt' => $log->statusMessage,
115
- 'post_status' => PostmanEmailLogService::POSTMAN_CUSTOM_POST_STATUS_PRIVATE
116
  );
117
-
118
  // Insert the post into the database (WordPress gives us the Post ID)
119
- $post_id = wp_insert_post ( $my_post );
120
- $this->logger->debug ( sprintf ( 'Saved message #%s to the database', $post_id ) );
121
- $this->logger->trace ( $log );
122
-
123
  // Write the meta data related to the email
124
- update_post_meta ( $post_id, 'success', $log->success );
125
- update_post_meta ( $post_id, 'from_header', $log->sender );
126
- if (! empty ( $log->toRecipients )) {
127
- update_post_meta ( $post_id, 'to_header', $log->toRecipients );
128
  }
129
- if (! empty ( $log->ccRecipients )) {
130
- update_post_meta ( $post_id, 'cc_header', $log->ccRecipients );
131
  }
132
- if (! empty ( $log->bccRecipients )) {
133
- update_post_meta ( $post_id, 'bcc_header', $log->bccRecipients );
134
  }
135
- if (! empty ( $log->replyTo )) {
136
- update_post_meta ( $post_id, 'reply_to_header', $log->replyTo );
137
  }
138
- update_post_meta ( $post_id, 'transport_uri', $log->transportUri );
139
-
140
- if (! $log->success || true) {
141
  // alwas add the meta data so we can re-send it
142
- update_post_meta ( $post_id, 'original_to', $log->originalTo );
143
- update_post_meta ( $post_id, 'original_subject', $log->originalSubject );
144
- update_post_meta ( $post_id, 'original_message', $log->originalMessage );
145
- update_post_meta ( $post_id, 'original_headers', $log->originalHeaders );
146
  }
147
-
148
  // we do not sanitize the session transcript - let the reader decide how to handle the data
149
- update_post_meta ( $post_id, 'session_transcript', $log->sessionTranscript );
150
-
151
  // truncate the log (remove older entries)
152
- $purger = new PostmanEmailLogPurger ();
153
- $purger->truncateLogItems ( PostmanOptions::getInstance ()->getMailLoggingMaxEntries () );
 
 
 
 
 
 
 
 
 
 
154
  }
155
-
156
  /**
157
  * Creates a Log object for use by writeToEmailLog()
158
  *
159
- * @param PostmanMessage $message
160
- * @param unknown $transcript
161
- * @param unknown $statusMessage
162
- * @param unknown $success
163
- * @param PostmanModuleTransport $transport
164
  * @return PostmanEmailLog
165
  */
166
- private function createLog(PostmanEmailLog $log, PostmanMessage $message = null, $transcript, $statusMessage, $success, PostmanModuleTransport $transport) {
167
- if ($message) {
168
- $log->sender = $message->getFromAddress ()->format ();
169
- $log->toRecipients = $this->flattenEmails ( $message->getToRecipients () );
170
- $log->ccRecipients = $this->flattenEmails ( $message->getCcRecipients () );
171
- $log->bccRecipients = $this->flattenEmails ( $message->getBccRecipients () );
172
- $log->subject = $message->getSubject ();
173
- $log->body = $message->getBody ();
174
- if (null !== $message->getReplyTo ()) {
175
- $log->replyTo = $message->getReplyTo ()->format ();
176
  }
177
  }
178
  $log->success = $success;
179
  $log->statusMessage = $statusMessage;
180
- $log->transportUri = PostmanTransportRegistry::getInstance ()->getPublicTransportUri ( $transport );
181
  $log->sessionTranscript = $log->transportUri . "\n\n" . $transcript;
182
  return $log;
183
  }
184
-
185
  /**
186
  * Creates a readable "TO" entry based on the recipient header
187
  *
188
- * @param array $addresses
189
  * @return string
190
  */
191
- private static function flattenEmails(array $addresses) {
192
  $flat = '';
193
  $count = 0;
194
  foreach ( $addresses as $address ) {
195
- if ($count >= 3) {
196
- $flat .= sprintf ( __ ( '.. +%d more', Postman::TEXT_DOMAIN ), sizeof ( $addresses ) - $count );
197
  break;
198
  }
199
- if ($count > 0) {
200
  $flat .= ', ';
201
  }
202
- $flat .= $address->format ();
203
  $count ++;
204
  }
205
  return $flat;
@@ -207,18 +219,18 @@ if (! class_exists ( 'PostmanEmailLogService' )) {
207
  }
208
  }
209
 
210
- if (! class_exists ( 'PostmanEmailLogPurger' )) {
211
  class PostmanEmailLogPurger {
212
  private $posts;
213
  private $logger;
214
-
215
  /**
216
  *
217
  * @return unknown
218
  */
219
  function __construct() {
220
- $this->logger = new PostmanLogger ( get_class ( $this ) );
221
- $args = array (
222
  'posts_per_page' => 1000,
223
  'offset' => 0,
224
  'category' => '',
@@ -233,46 +245,46 @@ if (! class_exists ( 'PostmanEmailLogPurger' )) {
233
  'post_mime_type' => '',
234
  'post_parent' => '',
235
  'post_status' => 'private',
236
- 'suppress_filters' => true
237
  );
238
- $this->posts = get_posts ( $args );
239
  }
240
-
241
  /**
242
  *
243
- * @param array $posts
244
- * @param unknown $postid
245
  */
246
- function verifyLogItemExistsAndRemove($postid) {
247
  $force_delete = true;
248
  foreach ( $this->posts as $post ) {
249
- if ($post->ID == $postid) {
250
- $this->logger->debug ( 'deleting log item ' . intval($postid) );
251
- wp_delete_post ( $postid, $force_delete );
252
  return;
253
  }
254
  }
255
- $this->logger->warn ( 'could not find Postman Log Item #' . $postid );
256
  }
257
  function removeAll() {
258
- $this->logger->debug ( sprintf ( 'deleting %d log items ', sizeof ( $this->posts ) ) );
259
  $force_delete = true;
260
  foreach ( $this->posts as $post ) {
261
- wp_delete_post ( $post->ID, $force_delete );
262
  }
263
  }
264
-
265
  /**
266
  *
267
- * @param unknown $size
268
  */
269
- function truncateLogItems($size) {
270
- $index = count ( $this->posts );
271
  $force_delete = true;
272
  while ( $index > $size ) {
273
- $postid = $this->posts [-- $index]->ID;
274
- $this->logger->debug ( 'deleting log item ' . $postid );
275
- wp_delete_post ( $postid, $force_delete );
276
  }
277
  }
278
  }
1
  <?php
2
+ if ( ! class_exists( 'PostmanEmailLog' ) ) {
3
  class PostmanEmailLog {
4
  public $sender;
5
  public $toRecipients;
19
  }
20
  }
21
 
22
+ if ( ! class_exists( 'PostmanEmailLogService' ) ) {
23
+
24
  /**
25
  * This class creates the Custom Post Type for Email Logs and handles writing these posts.
26
  *
27
  * @author jasonhendriks
28
  */
29
  class PostmanEmailLogService {
30
+
31
  /*
32
  * Private content is published only for your eyes, or the eyes of only those with authorization
33
  * permission levels to see private content. Normal users and visitors will not be aware of
36
  * the private content when you are logged into your WordPress blog.
37
  */
38
  const POSTMAN_CUSTOM_POST_STATUS_PRIVATE = 'private';
39
+
40
  // member variables
41
  private $logger;
42
  private $inst;
43
+
44
  /**
45
  * Constructor
46
  */
47
  private function __construct() {
48
+ $this->logger = new PostmanLogger( get_class( $this ) );
49
  }
50
+
51
  /**
52
  * singleton instance
53
  */
54
  public static function getInstance() {
55
  static $inst = null;
56
+ if ( $inst === null ) {
57
+ $inst = new PostmanEmailLogService();
58
  }
59
  return $inst;
60
  }
61
+
62
  /**
63
  * Logs successful email attempts
64
  *
65
+ * @param PostmanMessage $message
66
+ * @param unknown $transcript
67
+ * @param PostmanModuleTransport $transport
68
  */
69
+ public function writeSuccessLog( PostmanEmailLog $log, PostmanMessage $message, $transcript, PostmanModuleTransport $transport ) {
70
+ if ( PostmanOptions::getInstance()->isMailLoggingEnabled() ) {
71
  $statusMessage = '';
72
  $status = true;
73
+ $subject = $message->getSubject();
74
+ if ( empty( $subject ) ) {
75
+ $statusMessage = sprintf( '%s: %s', __( 'Warning', Postman::TEXT_DOMAIN ), __( 'An empty subject line can result in delivery failure.', Postman::TEXT_DOMAIN ) );
76
  $status = 'WARN';
77
  }
78
+ $this->createLog( $log, $message, $transcript, $statusMessage, $status, $transport );
79
+ $this->writeToEmailLog( $log );
80
  }
81
  }
82
+
83
  /**
84
  * Logs failed email attempts, requires more metadata so the email can be resent in the future
85
  *
86
+ * @param PostmanMessage $message
87
+ * @param unknown $transcript
88
+ * @param PostmanModuleTransport $transport
89
+ * @param unknown $statusMessage
90
+ * @param unknown $originalTo
91
+ * @param unknown $originalSubject
92
+ * @param unknown $originalMessage
93
+ * @param unknown $originalHeaders
94
  */
95
+ public function writeFailureLog( PostmanEmailLog $log, PostmanMessage $message = null, $transcript, PostmanModuleTransport $transport, $statusMessage ) {
96
+ if ( PostmanOptions::getInstance()->isMailLoggingEnabled() ) {
97
+ $this->createLog( $log, $message, $transcript, $statusMessage, false, $transport );
98
+ $this->writeToEmailLog( $log );
99
  }
100
  }
101
+
102
  /**
103
  * Writes an email sending attempt to the Email Log
104
  *
105
  * From http://wordpress.stackexchange.com/questions/8569/wp-insert-post-php-function-and-custom-fields
106
  */
107
+ private function writeToEmailLog( PostmanEmailLog $log ) {
108
+
109
+ $this->checkForLogErrors( $log );
110
  // nothing here is sanitized as WordPress should take care of
111
  // making database writes safe
112
+ $my_post = array(
113
  'post_type' => PostmanEmailLogPostType::POSTMAN_CUSTOM_POST_TYPE_SLUG,
114
  'post_title' => $log->subject,
115
  'post_content' => $log->body,
116
  'post_excerpt' => $log->statusMessage,
117
+ 'post_status' => PostmanEmailLogService::POSTMAN_CUSTOM_POST_STATUS_PRIVATE,
118
  );
119
+
120
  // Insert the post into the database (WordPress gives us the Post ID)
121
+ $post_id = wp_insert_post( $my_post );
122
+ $this->logger->debug( sprintf( 'Saved message #%s to the database', $post_id ) );
123
+ $this->logger->trace( $log );
124
+
125
  // Write the meta data related to the email
126
+ update_post_meta( $post_id, 'success', $log->success );
127
+ update_post_meta( $post_id, 'from_header', $log->sender );
128
+ if ( ! empty( $log->toRecipients ) ) {
129
+ update_post_meta( $post_id, 'to_header', $log->toRecipients );
130
  }
131
+ if ( ! empty( $log->ccRecipients ) ) {
132
+ update_post_meta( $post_id, 'cc_header', $log->ccRecipients );
133
  }
134
+ if ( ! empty( $log->bccRecipients ) ) {
135
+ update_post_meta( $post_id, 'bcc_header', $log->bccRecipients );
136
  }
137
+ if ( ! empty( $log->replyTo ) ) {
138
+ update_post_meta( $post_id, 'reply_to_header', $log->replyTo );
139
  }
140
+ update_post_meta( $post_id, 'transport_uri', $log->transportUri );
141
+
142
+ if ( ! $log->success || true ) {
143
  // alwas add the meta data so we can re-send it
144
+ update_post_meta( $post_id, 'original_to', $log->originalTo );
145
+ update_post_meta( $post_id, 'original_subject', $log->originalSubject );
146
+ update_post_meta( $post_id, 'original_message', $log->originalMessage );
147
+ update_post_meta( $post_id, 'original_headers', $log->originalHeaders );
148
  }
149
+
150
  // we do not sanitize the session transcript - let the reader decide how to handle the data
151
+ update_post_meta( $post_id, 'session_transcript', $log->sessionTranscript );
152
+
153
  // truncate the log (remove older entries)
154
+ $purger = new PostmanEmailLogPurger();
155
+ $purger->truncateLogItems( PostmanOptions::getInstance()->getMailLoggingMaxEntries() );
156
+ }
157
+
158
+ private function checkForLogErrors( PostmanEmailLog $log ) {
159
+ if ( $log->statusMessage && ! empty( $log->statusMessage ) ) {
160
+ mail( get_bloginfo( 'admin_email' ), __( 'Post SMTP email error', Postman::TEXT_DOMAIN ), $log->statusMessage );
161
+ }
162
+
163
+ if ( strpos( strtolower( $log->sessionTranscript ), 'error' ) !== false ) {
164
+ mail( get_bloginfo( 'admin_email' ), __( 'Post SMTP session transcript error', Postman::TEXT_DOMAIN ), $log->sessionTranscript );
165
+ }
166
  }
167
+
168
  /**
169
  * Creates a Log object for use by writeToEmailLog()
170
  *
171
+ * @param PostmanMessage $message
172
+ * @param unknown $transcript
173
+ * @param unknown $statusMessage
174
+ * @param unknown $success
175
+ * @param PostmanModuleTransport $transport
176
  * @return PostmanEmailLog
177
  */
178
+ private function createLog( PostmanEmailLog $log, PostmanMessage $message = null, $transcript, $statusMessage, $success, PostmanModuleTransport $transport ) {
179
+ if ( $message ) {
180
+ $log->sender = $message->getFromAddress()->format();
181
+ $log->toRecipients = $this->flattenEmails( $message->getToRecipients() );
182
+ $log->ccRecipients = $this->flattenEmails( $message->getCcRecipients() );
183
+ $log->bccRecipients = $this->flattenEmails( $message->getBccRecipients() );
184
+ $log->subject = $message->getSubject();
185
+ $log->body = $message->getBody();
186
+ if ( null !== $message->getReplyTo() ) {
187
+ $log->replyTo = $message->getReplyTo()->format();
188
  }
189
  }
190
  $log->success = $success;
191
  $log->statusMessage = $statusMessage;
192
+ $log->transportUri = PostmanTransportRegistry::getInstance()->getPublicTransportUri( $transport );
193
  $log->sessionTranscript = $log->transportUri . "\n\n" . $transcript;
194
  return $log;
195
  }
196
+
197
  /**
198
  * Creates a readable "TO" entry based on the recipient header
199
  *
200
+ * @param array $addresses
201
  * @return string
202
  */
203
+ private static function flattenEmails( array $addresses ) {
204
  $flat = '';
205
  $count = 0;
206
  foreach ( $addresses as $address ) {
207
+ if ( $count >= 3 ) {
208
+ $flat .= sprintf( __( '.. +%d more', Postman::TEXT_DOMAIN ), sizeof( $addresses ) - $count );
209
  break;
210
  }
211
+ if ( $count > 0 ) {
212
  $flat .= ', ';
213
  }
214
+ $flat .= $address->format();
215
  $count ++;
216
  }
217
  return $flat;
219
  }
220
  }
221
 
222
+ if ( ! class_exists( 'PostmanEmailLogPurger' ) ) {
223
  class PostmanEmailLogPurger {
224
  private $posts;
225
  private $logger;
226
+
227
  /**
228
  *
229
  * @return unknown
230
  */
231
  function __construct() {
232
+ $this->logger = new PostmanLogger( get_class( $this ) );
233
+ $args = array(
234
  'posts_per_page' => 1000,
235
  'offset' => 0,
236
  'category' => '',
245
  'post_mime_type' => '',
246
  'post_parent' => '',
247
  'post_status' => 'private',
248
+ 'suppress_filters' => true,
249
  );
250
+ $this->posts = get_posts( $args );
251
  }
252
+
253
  /**
254
  *
255
+ * @param array $posts
256
+ * @param unknown $postid
257
  */
258
+ function verifyLogItemExistsAndRemove( $postid ) {
259
  $force_delete = true;
260
  foreach ( $this->posts as $post ) {
261
+ if ( $post->ID == $postid ) {
262
+ $this->logger->debug( 'deleting log item ' . intval( $postid ) );
263
+ wp_delete_post( $postid, $force_delete );
264
  return;
265
  }
266
  }
267
+ $this->logger->warn( 'could not find Postman Log Item #' . $postid );
268
  }
269
  function removeAll() {
270
+ $this->logger->debug( sprintf( 'deleting %d log items ', sizeof( $this->posts ) ) );
271
  $force_delete = true;
272
  foreach ( $this->posts as $post ) {
273
+ wp_delete_post( $post->ID, $force_delete );
274
  }
275
  }
276
+
277
  /**
278
  *
279
+ * @param unknown $size
280
  */
281
+ function truncateLogItems( $size ) {
282
+ $index = count( $this->posts );
283
  $force_delete = true;
284
  while ( $index > $size ) {
285
+ $postid = $this->posts [ -- $index ]->ID;
286
+ $this->logger->debug( 'deleting log item ' . $postid );
287
+ wp_delete_post( $postid, $force_delete );
288
  }
289
  }
290
  }
Postman/Postman-Email-Log/PostmanEmailLogView.php CHANGED
@@ -2,14 +2,13 @@
2
 
3
  /**
4
  * See http://wpengineer.com/2426/wp_list_table-a-step-by-step-guide/
5
- *
6
  */
7
- if (! class_exists ( 'WP_List_Table' )) {
8
- require_once (ABSPATH . 'wp-admin/includes/class-wp-list-table.php');
9
  }
10
  class PostmanEmailLogView extends WP_List_Table {
11
  private $logger;
12
-
13
  /**
14
  * ************************************************************************
15
  * REQUIRED.
@@ -18,16 +17,16 @@ class PostmanEmailLogView extends WP_List_Table {
18
  * *************************************************************************
19
  */
20
  function __construct() {
21
- $this->logger = new PostmanLogger ( get_class ( $this ) );
22
-
23
  // Set parent defaults
24
- parent::__construct ( array (
25
  'singular' => 'email_log_entry', // singular name of the listed records
26
  'plural' => 'email_log_entries', // plural name of the listed records
27
- 'ajax' => false
28
  ) ); // does this table support ajax?
29
  }
30
-
31
  /**
32
  * ************************************************************************
33
  * Recommended.
@@ -54,16 +53,16 @@ class PostmanEmailLogView extends WP_List_Table {
54
  * @return string Text or HTML to be placed inside the column <td>
55
  * ************************************************************************
56
  */
57
- function column_default($item, $column_name) {
58
- switch ($column_name) {
59
  case 'date' :
60
  case 'status' :
61
- return $item [$column_name];
62
  default :
63
- return print_r ( $item, true ); // Show the whole array for troubleshooting purposes
64
  }
65
  }
66
-
67
  /**
68
  * ************************************************************************
69
  * Recommended.
@@ -77,48 +76,47 @@ class PostmanEmailLogView extends WP_List_Table {
77
  * should be an associative array formatted as 'slug'=>'link html' - and you
78
  * will need to generate the URLs yourself. You could even ensure the links
79
  *
80
- *
81
  * @see WP_List_Table::::single_row_columns()
82
  * @param array $item
83
  * A singular item (one full row's worth of data)
84
  * @return string Text to be placed inside the column <td> (movie title only)
85
  * ************************************************************************
86
  */
87
- function column_title($item) {
88
-
89
  // Build row actions
90
  $iframeUri = 'admin-post.php?page=postman_email_log&action=%s&email=%s&TB_iframe=true&width=700&height=550';
91
- $deleteUrl = wp_nonce_url ( admin_url ( sprintf ( 'admin-post.php?page=postman_email_log&action=%s&email=%s', 'delete', $item ['ID'] ) ), 'delete_email_log_item_' . $item ['ID'] );
92
- $viewUrl = admin_url ( sprintf ( $iframeUri, 'view', $item ['ID'] ) );
93
- $transcriptUrl = admin_url ( sprintf ( $iframeUri, 'transcript', $item ['ID'] ) );
94
- $resendUrl = admin_url ( sprintf ( $iframeUri, 'resend', $item ['ID'] ) );
95
-
96
- $meta_values = get_post_meta ( $item ['ID'] );
97
-
98
- $actions = array (
99
- 'delete' => sprintf ( '<a href="%s">%s</a>', $deleteUrl, _x ( 'Delete', 'Delete an item from the email log', Postman::TEXT_DOMAIN ) ),
100
- 'view' => sprintf ( '<a href="%s" class="thickbox">%s</a>', $viewUrl, _x ( 'View', 'View an item from the email log', Postman::TEXT_DOMAIN ) )
101
  );
102
-
103
- if (! empty ( $meta_values ['session_transcript'] [0] )) {
104
- $actions ['transcript'] = sprintf ( '<a href="%1$s" class="thickbox">%2$s</a>', $transcriptUrl, __ ( 'Session Transcript', Postman::TEXT_DOMAIN ) );
105
  } else {
106
- $actions ['transcript'] = sprintf ( '%2$s', $transcriptUrl, __ ( 'Session Transcript', Postman::TEXT_DOMAIN ) );
107
  }
108
- if (! (empty ( $meta_values ['original_to'] [0] ) && empty ( $meta_values ['originalHeaders'] [0] ))) {
109
  // $actions ['resend'] = sprintf ( '<a href="%s">%s</a>', $resendUrl, __ ( 'Resend', Postman::TEXT_DOMAIN ) );
110
- $actions ['resend'] = sprintf ( '<span id="%3$s"><a href="javascript:postman_resend_email(%1$s);">%2$s</a></span>', $item ['ID'], __ ( 'Resend', Postman::TEXT_DOMAIN ), 'resend-' . $item ['ID'] );
111
  } else {
112
- $actions ['resend'] = sprintf ( '%2$s', $resendUrl, __ ( 'Resend', Postman::TEXT_DOMAIN ) );
113
  }
114
-
115
  // Return the title contents
116
- return sprintf ( '%1$s %3$s',
117
- /*$1%s*/ $item ['title'],
118
- /*$2%s*/ $item ['ID'],
119
- /*$3%s*/ $this->row_actions ( $actions ) );
120
  }
121
-
122
  /**
123
  * ************************************************************************
124
  * REQUIRED if displaying checkboxes or using bulk actions! The 'cb' column
@@ -132,13 +130,13 @@ class PostmanEmailLogView extends WP_List_Table {
132
  * @return string Text to be placed inside the column <td> (movie title only)
133
  * ************************************************************************
134
  */
135
- function column_cb($item) {
136
- return sprintf ( '<input type="checkbox" name="%1$s[]" value="%2$s" />',
137
- /*$1%s*/ $this->_args ['singular'], // Let's simply repurpose the table's singular label ("movie")
138
- /* $2%s */
139
  $item ['ID'] ); // The value of the checkbox should be the record's id
140
  }
141
-
142
  /**
143
  * ************************************************************************
144
  * REQUIRED! This method dictates the table's columns and titles.
@@ -156,15 +154,15 @@ class PostmanEmailLogView extends WP_List_Table {
156
  * ************************************************************************
157
  */
158
  function get_columns() {
159
- $columns = array (
160
  'cb' => '<input type="checkbox" />', // Render a checkbox instead of text
161
- 'title' => _x ( 'Subject', 'What is the subject of this message?', Postman::TEXT_DOMAIN ),
162
- 'status' => __ ( 'Status', Postman::TEXT_DOMAIN ),
163
- 'date' => _x ( 'Delivery Time', 'When was this email sent?', Postman::TEXT_DOMAIN )
164
  );
165
  return $columns;
166
  }
167
-
168
  /**
169
  * ************************************************************************
170
  * Optional.
@@ -183,24 +181,24 @@ class PostmanEmailLogView extends WP_List_Table {
183
  * ************************************************************************
184
  */
185
  function get_sortable_columns() {
186
- return array ();
187
- $sortable_columns = array (
188
- 'title' => array (
189
  'title',
190
- false
191
  ), // true means it's already sorted
192
- 'status' => array (
193
  'status',
194
- false
195
  ),
196
- 'date' => array (
197
  'date',
198
- false
199
- )
200
  );
201
  return $sortable_columns;
202
  }
203
-
204
  /**
205
  * ************************************************************************
206
  * Optional.
@@ -219,12 +217,12 @@ class PostmanEmailLogView extends WP_List_Table {
219
  * ************************************************************************
220
  */
221
  function get_bulk_actions() {
222
- $actions = array (
223
- 'bulk_delete' => _x ( 'Delete', 'Delete an item from the email log', Postman::TEXT_DOMAIN )
224
  );
225
  return $actions;
226
  }
227
-
228
  /**
229
  * ************************************************************************
230
  * Optional.
@@ -236,7 +234,7 @@ class PostmanEmailLogView extends WP_List_Table {
236
  */
237
  function process_bulk_action() {
238
  }
239
-
240
  /**
241
  * ************************************************************************
242
  * REQUIRED! This is where you prepare your data for display.
@@ -256,12 +254,12 @@ class PostmanEmailLogView extends WP_List_Table {
256
  * ************************************************************************
257
  */
258
  function prepare_items() {
259
-
260
  /**
261
  * First, lets decide how many records per page to show
262
  */
263
  $per_page = 10;
264
-
265
  /**
266
  * REQUIRED.
267
  * Now we need to define our column headers. This includes a complete
@@ -270,10 +268,10 @@ class PostmanEmailLogView extends WP_List_Table {
270
  * can be defined in another method (as we've done here) before being
271
  * used to build the value for our _column_headers property.
272
  */
273
- $columns = $this->get_columns ();
274
- $hidden = array ();
275
- $sortable = $this->get_sortable_columns ();
276
-
277
  /**
278
  * REQUIRED.
279
  * Finally, we build an array to be used by the class for column
@@ -281,19 +279,19 @@ class PostmanEmailLogView extends WP_List_Table {
281
  * 3 other arrays. One for all columns, one for hidden columns, and one
282
  * for sortable columns.
283
  */
284
- $this->_column_headers = array (
285
  $columns,
286
  $hidden,
287
- $sortable
288
  );
289
-
290
  /**
291
  * Optional.
292
  * You can handle your bulk actions however you see fit. In this
293
  * case, we'll handle them within our package just to keep things clean.
294
  */
295
- $this->process_bulk_action ();
296
-
297
  /**
298
  * Instead of querying a database, we're going to fetch the example data
299
  * property we created for use in this plugin.
@@ -304,44 +302,61 @@ class PostmanEmailLogView extends WP_List_Table {
304
  * use sort and pagination data to build a custom query instead, as you'll
305
  * be able to use your precisely-queried data immediately.
306
  */
307
- $data = array ();
308
- $args = array (
309
  'posts_per_page' => 1000,
310
  'offset' => 0,
311
- 'category' => '',
312
- 'category_name' => '',
313
  'orderby' => 'date',
314
  'order' => 'DESC',
315
- 'include' => '',
316
- 'exclude' => '',
317
- 'meta_key' => '',
318
- 'meta_value' => '',
319
  'post_type' => PostmanEmailLogPostType::POSTMAN_CUSTOM_POST_TYPE_SLUG,
320
- 'post_mime_type' => '',
321
- 'post_parent' => '',
322
  'post_status' => 'private',
323
- 'suppress_filters' => true
324
  );
325
- $posts = get_posts ( $args );
326
- foreach ( $posts as $post ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
327
  $date = $post->post_date;
328
- $humanTime = human_time_diff ( strtotime ( $post->post_date_gmt ) );
329
  // if this PHP system support humanTime, than use it
330
- if (! empty ( $humanTime )) {
331
  /* Translators: where %s indicates the relative time from now */
332
- $date = sprintf ( _x ( '%s ago', 'A relative time as in "five days ago"', Postman::TEXT_DOMAIN ), $humanTime );
333
  }
334
- $flattenedPost = array (
 
335
  // the post title must be escaped as they are displayed in the HTML output
336
- 'title' => esc_html ( $post->post_title ),
337
  // the post status must be escaped as they are displayed in the HTML output
338
- 'status' => ($post->post_excerpt != null ? esc_html ( $post->post_excerpt ) : __ ( 'Sent', Postman::TEXT_DOMAIN )),
339
  'date' => $date,
340
- 'ID' => $post->ID
341
  );
342
- array_push ( $data, $flattenedPost );
343
  }
344
-
345
  /**
346
  * This checks for sorting input and sorts the data in our array accordingly.
347
  *
@@ -350,14 +365,13 @@ class PostmanEmailLogView extends WP_List_Table {
350
  * to a custom query. The returned data will be pre-sorted, and this array
351
  * sorting technique would be unnecessary.
352
  */
353
- function usort_reorder($a, $b) {
354
- $orderby = (! empty ( $_REQUEST ['orderby'] )) ? $_REQUEST ['orderby'] : 'title'; // If no sort, default to title
355
- $order = (! empty ( $_REQUEST ['order'] )) ? $_REQUEST ['order'] : 'asc'; // If no order, default to asc
356
- $result = strcmp ( $a [$orderby], $b [$orderby] ); // Determine sort order
357
  return ($order === 'asc') ? $result : - $result; // Send final sort direction to usort
358
  }
359
  // usort($data, 'usort_reorder');
360
-
361
  /**
362
  * *********************************************************************
363
  * ---------------------------------------------------------------------
@@ -372,15 +386,15 @@ class PostmanEmailLogView extends WP_List_Table {
372
  * ---------------------------------------------------------------------
373
  * ********************************************************************
374
  */
375
-
376
  /**
377
  * REQUIRED for pagination.
378
  * Let's figure out what page the user is currently
379
  * looking at. We'll need this later, so you should always include it in
380
  * your own package classes.
381
  */
382
- $current_page = $this->get_pagenum ();
383
-
384
  /**
385
  * REQUIRED for pagination.
386
  * Let's check how many items are in our data array.
@@ -388,31 +402,31 @@ class PostmanEmailLogView extends WP_List_Table {
388
  * without filtering. We'll need this later, so you should always include it
389
  * in your own package classes.
390
  */
391
- $total_items = count ( $data );
392
-
393
  /**
394
  * The WP_List_Table class does not handle pagination for us, so we need
395
  * to ensure that the data is trimmed to only the current page.
396
  * We can use
397
  * array_slice() to
398
  */
399
- $data = array_slice ( $data, (($current_page - 1) * $per_page), $per_page );
400
-
401
  /**
402
  * REQUIRED.
403
  * Now we can add our *sorted* data to the items property, where
404
  * it can be used by the rest of the class.
405
  */
406
  $this->items = $data;
407
-
408
  /**
409
  * REQUIRED.
410
  * We also have to register our pagination options & calculations.
411
  */
412
- $this->set_pagination_args ( array (
413
  'total_items' => $total_items, // WE have to calculate the total number of items
414
  'per_page' => $per_page, // WE have to determine how many items to show on a page
415
- 'total_pages' => ceil ( $total_items / $per_page )
416
  ) ); // WE have to calculate the total number of pages
417
  }
418
  }
2
 
3
  /**
4
  * See http://wpengineer.com/2426/wp_list_table-a-step-by-step-guide/
 
5
  */
6
+ if ( ! class_exists( 'WP_List_Table' ) ) {
7
+ require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
8
  }
9
  class PostmanEmailLogView extends WP_List_Table {
10
  private $logger;
11
+
12
  /**
13
  * ************************************************************************
14
  * REQUIRED.
17
  * *************************************************************************
18
  */
19
  function __construct() {
20
+ $this->logger = new PostmanLogger( get_class( $this ) );
21
+
22
  // Set parent defaults
23
+ parent::__construct( array(
24
  'singular' => 'email_log_entry', // singular name of the listed records
25
  'plural' => 'email_log_entries', // plural name of the listed records
26
+ 'ajax' => false,
27
  ) ); // does this table support ajax?
28
  }
29
+
30
  /**
31
  * ************************************************************************
32
  * Recommended.
53
  * @return string Text or HTML to be placed inside the column <td>
54
  * ************************************************************************
55
  */
56
+ function column_default( $item, $column_name ) {
57
+ switch ( $column_name ) {
58
  case 'date' :
59
  case 'status' :
60
+ return $item [ $column_name ];
61
  default :
62
+ return print_r( $item, true ); // Show the whole array for troubleshooting purposes
63
  }
64
  }
65
+
66
  /**
67
  * ************************************************************************
68
  * Recommended.
76
  * should be an associative array formatted as 'slug'=>'link html' - and you
77
  * will need to generate the URLs yourself. You could even ensure the links
78
  *
 
79
  * @see WP_List_Table::::single_row_columns()
80
  * @param array $item
81
  * A singular item (one full row's worth of data)
82
  * @return string Text to be placed inside the column <td> (movie title only)
83
  * ************************************************************************
84
  */
85
+ function column_title( $item ) {
86
+
87
  // Build row actions
88
  $iframeUri = 'admin-post.php?page=postman_email_log&action=%s&email=%s&TB_iframe=true&width=700&height=550';
89
+ $deleteUrl = wp_nonce_url( admin_url( sprintf( 'admin-post.php?page=postman_email_log&action=%s&email=%s', 'delete', $item ['ID'] ) ), 'delete_email_log_item_' . $item ['ID'] );
90
+ $viewUrl = admin_url( sprintf( $iframeUri, 'view', $item ['ID'] ) );
91
+ $transcriptUrl = admin_url( sprintf( $iframeUri, 'transcript', $item ['ID'] ) );
92
+ $resendUrl = admin_url( sprintf( $iframeUri, 'resend', $item ['ID'] ) );
93
+
94
+ $meta_values = get_post_meta( $item ['ID'] );
95
+
96
+ $actions = array(
97
+ 'delete' => sprintf( '<a href="%s">%s</a>', $deleteUrl, _x( 'Delete', 'Delete an item from the email log', Postman::TEXT_DOMAIN ) ),
98
+ 'view' => sprintf( '<a href="%s" class="thickbox">%s</a>', $viewUrl, _x( 'View', 'View an item from the email log', Postman::TEXT_DOMAIN ) ),
99
  );
100
+
101
+ if ( ! empty( $meta_values ['session_transcript'] [0] ) ) {
102
+ $actions ['transcript'] = sprintf( '<a href="%1$s" class="thickbox">%2$s</a>', $transcriptUrl, __( 'Session Transcript', Postman::TEXT_DOMAIN ) );
103
  } else {
104
+ $actions ['transcript'] = sprintf( '%2$s', $transcriptUrl, __( 'Session Transcript', Postman::TEXT_DOMAIN ) );
105
  }
106
+ if ( ! (empty( $meta_values ['original_to'] [0] ) && empty( $meta_values ['originalHeaders'] [0] )) ) {
107
  // $actions ['resend'] = sprintf ( '<a href="%s">%s</a>', $resendUrl, __ ( 'Resend', Postman::TEXT_DOMAIN ) );
108
+ $actions ['resend'] = sprintf( '<span id="%3$s"><a href="javascript:postman_resend_email(%1$s);">%2$s</a></span>', $item ['ID'], __( 'Resend', Postman::TEXT_DOMAIN ), 'resend-' . $item ['ID'] );
109
  } else {
110
+ $actions ['resend'] = sprintf( '%2$s', $resendUrl, __( 'Resend', Postman::TEXT_DOMAIN ) );
111
  }
112
+
113
  // Return the title contents
114
+ return sprintf( '%1$s %3$s',
115
+ /*$1%s*/ $item ['title'],
116
+ /*$2%s*/ $item ['ID'],
117
+ /*$3%s*/ $this->row_actions( $actions ) );
118
  }
119
+
120
  /**
121
  * ************************************************************************
122
  * REQUIRED if displaying checkboxes or using bulk actions! The 'cb' column
130
  * @return string Text to be placed inside the column <td> (movie title only)
131
  * ************************************************************************
132
  */
133
+ function column_cb( $item ) {
134
+ return sprintf( '<input type="checkbox" name="%1$s[]" value="%2$s" />',
135
+ /*$1%s*/ $this->_args ['singular'], // Let's simply repurpose the table's singular label ("movie")
136
+ /* $2%s */
137
  $item ['ID'] ); // The value of the checkbox should be the record's id
138
  }
139
+
140
  /**
141
  * ************************************************************************
142
  * REQUIRED! This method dictates the table's columns and titles.
154
  * ************************************************************************
155
  */
156
  function get_columns() {
157
+ $columns = array(
158
  'cb' => '<input type="checkbox" />', // Render a checkbox instead of text
159
+ 'title' => _x( 'Subject', 'What is the subject of this message?', Postman::TEXT_DOMAIN ),
160
+ 'status' => __( 'Status', Postman::TEXT_DOMAIN ),
161
+ 'date' => _x( 'Delivery Time', 'When was this email sent?', Postman::TEXT_DOMAIN ),
162
  );
163
  return $columns;
164
  }
165
+
166
  /**
167
  * ************************************************************************
168
  * Optional.
181
  * ************************************************************************
182
  */
183
  function get_sortable_columns() {
184
+ return array();
185
+ $sortable_columns = array(
186
+ 'title' => array(
187
  'title',
188
+ false,
189
  ), // true means it's already sorted
190
+ 'status' => array(
191
  'status',
192
+ false,
193
  ),
194
+ 'date' => array(
195
  'date',
196
+ false,
197
+ ),
198
  );
199
  return $sortable_columns;
200
  }
201
+
202
  /**
203
  * ************************************************************************
204
  * Optional.
217
  * ************************************************************************
218
  */
219
  function get_bulk_actions() {
220
+ $actions = array(
221
+ 'bulk_delete' => _x( 'Delete', 'Delete an item from the email log', Postman::TEXT_DOMAIN ),
222
  );
223
  return $actions;
224
  }
225
+
226
  /**
227
  * ************************************************************************
228
  * Optional.
234
  */
235
  function process_bulk_action() {
236
  }
237
+
238
  /**
239
  * ************************************************************************
240
  * REQUIRED! This is where you prepare your data for display.
254
  * ************************************************************************
255
  */
256
  function prepare_items() {
257
+
258
  /**
259
  * First, lets decide how many records per page to show
260
  */
261
  $per_page = 10;
262
+
263
  /**
264
  * REQUIRED.
265
  * Now we need to define our column headers. This includes a complete
268
  * can be defined in another method (as we've done here) before being
269
  * used to build the value for our _column_headers property.
270
  */
271
+ $columns = $this->get_columns();
272
+ $hidden = array();
273
+ $sortable = $this->get_sortable_columns();
274
+
275
  /**
276
  * REQUIRED.
277
  * Finally, we build an array to be used by the class for column
279
  * 3 other arrays. One for all columns, one for hidden columns, and one
280
  * for sortable columns.
281
  */
282
+ $this->_column_headers = array(
283
  $columns,
284
  $hidden,
285
+ $sortable,
286
  );
287
+
288
  /**
289
  * Optional.
290
  * You can handle your bulk actions however you see fit. In this
291
  * case, we'll handle them within our package just to keep things clean.
292
  */
293
+ $this->process_bulk_action();
294
+
295
  /**
296
  * Instead of querying a database, we're going to fetch the example data
297
  * property we created for use in this plugin.
302
  * use sort and pagination data to build a custom query instead, as you'll
303
  * be able to use your precisely-queried data immediately.
304
  */
305
+ $data = array();
306
+ $args = array(
307
  'posts_per_page' => 1000,
308
  'offset' => 0,
 
 
309
  'orderby' => 'date',
310
  'order' => 'DESC',
 
 
 
 
311
  'post_type' => PostmanEmailLogPostType::POSTMAN_CUSTOM_POST_TYPE_SLUG,
 
 
312
  'post_status' => 'private',
313
+ 'suppress_filters' => true,
314
  );
315
+
316
+ if ( isset( $_POST['from_date'] ) && ! empty( $_POST['from_date'] ) ) {
317
+ $from_date = sanitize_text_field( $_POST['from_date'] );
318
+
319
+ $args['date_query']['after'] = $from_date;
320
+ $args['date_query']['inclusive'] = false;
321
+ }
322
+
323
+ if ( isset( $_POST['to_date'] ) && ! empty( $_POST['to_date'] ) ) {
324
+ $to_date = sanitize_text_field( $_POST['to_date'] );
325
+
326
+ $args['date_query']['before'] = $to_date;
327
+ $args['date_query']['inclusive'] = true;
328
+ }
329
+
330
+ if ( ! empty( $_POST['search'] ) ) {
331
+
332
+ if ( isset( $args['date_query'] ) ) {
333
+ unset( $args['date_query'] ); }
334
+
335
+ $args['s'] = sanitize_text_field( $_POST['search'] );
336
+ }
337
+
338
+ $posts = new WP_query( $args );
339
+
340
+ foreach ( $posts->posts as $post ) {
341
  $date = $post->post_date;
342
+ $humanTime = human_time_diff( strtotime( $post->post_date_gmt ) );
343
  // if this PHP system support humanTime, than use it
344
+ if ( ! empty( $humanTime ) ) {
345
  /* Translators: where %s indicates the relative time from now */
346
+ $date = sprintf( _x( '%s ago', 'A relative time as in "five days ago"', Postman::TEXT_DOMAIN ), $humanTime );
347
  }
348
+
349
+ $flattenedPost = array(
350
  // the post title must be escaped as they are displayed in the HTML output
351
+ 'title' => esc_html( $post->post_title ),
352
  // the post status must be escaped as they are displayed in the HTML output
353
+ 'status' => ($post->post_excerpt != null ? esc_html( $post->post_excerpt ) : __( 'Sent', Postman::TEXT_DOMAIN )),
354
  'date' => $date,
355
+ 'ID' => $post->ID,
356
  );
357
+ array_push( $data, $flattenedPost );
358
  }
359
+
360
  /**
361
  * This checks for sorting input and sorts the data in our array accordingly.
362
  *
365
  * to a custom query. The returned data will be pre-sorted, and this array
366
  * sorting technique would be unnecessary.
367
  */
368
+ function usort_reorder( $a, $b ) {
369
+ $orderby = ( ! empty( $_REQUEST ['orderby'] )) ? $_REQUEST ['orderby'] : 'title'; // If no sort, default to title
370
+ $order = ( ! empty( $_REQUEST ['order'] )) ? $_REQUEST ['order'] : 'asc'; // If no order, default to asc
371
+ $result = strcmp( $a [ $orderby ], $b [ $orderby ] ); // Determine sort order
372
  return ($order === 'asc') ? $result : - $result; // Send final sort direction to usort
373
  }
374
  // usort($data, 'usort_reorder');
 
375
  /**
376
  * *********************************************************************
377
  * ---------------------------------------------------------------------
386
  * ---------------------------------------------------------------------
387
  * ********************************************************************
388
  */
389
+
390
  /**
391
  * REQUIRED for pagination.
392
  * Let's figure out what page the user is currently
393
  * looking at. We'll need this later, so you should always include it in
394
  * your own package classes.
395
  */
396
+ $current_page = $this->get_pagenum();
397
+
398
  /**
399
  * REQUIRED for pagination.
400
  * Let's check how many items are in our data array.
402
  * without filtering. We'll need this later, so you should always include it
403
  * in your own package classes.
404
  */
405
+ $total_items = count( $data );
406
+
407
  /**
408
  * The WP_List_Table class does not handle pagination for us, so we need
409
  * to ensure that the data is trimmed to only the current page.
410
  * We can use
411
  * array_slice() to
412
  */
413
+ $data = array_slice( $data, (($current_page - 1) * $per_page), $per_page );
414
+
415
  /**
416
  * REQUIRED.
417
  * Now we can add our *sorted* data to the items property, where
418
  * it can be used by the rest of the class.
419
  */
420
  $this->items = $data;
421
+
422
  /**
423
  * REQUIRED.
424
  * We also have to register our pagination options & calculations.
425
  */
426
+ $this->set_pagination_args( array(
427
  'total_items' => $total_items, // WE have to calculate the total number of items
428
  'per_page' => $per_page, // WE have to determine how many items to show on a page
429
+ 'total_pages' => ceil( $total_items / $per_page ),
430
  ) ); // WE have to calculate the total number of pages
431
  }
432
  }
Postman/Postman-Mail/PostmanMandrillTransport.php CHANGED
@@ -235,7 +235,7 @@ class PostmanMandrillTransport extends PostmanAbstractModuleTransport implements
235
  */
236
  public function printMandrillAuthSectionInfo() {
237
  /* Translators: Where (1) is the service URL and (2) is the service name and (3) is a api key URL */
238
- printf ( '<p id="wizard_mandrill_auth_help">%s</p>', sprintf ( __ ( 'Create an account at <a href="%1$s" target="_new">%2$s</a> and enter <a href="%3$s" target="_new">an API key</a> below.', Postman::TEXT_DOMAIN ), 'https://mandrillapp.com', 'Mandrillapp.com', 'https://mandrillapp.com/settings' ) );
239
  }
240
 
241
  /**
235
  */
236
  public function printMandrillAuthSectionInfo() {
237
  /* Translators: Where (1) is the service URL and (2) is the service name and (3) is a api key URL */
238
+ printf ( '<p id="wizard_mandrill_auth_help">%s</p>', sprintf ( __ ( 'Create an account at <a href="%1$s" target="_blank">%2$s</a> and enter <a href="%3$s" target="_blank">an API key</a> below.', Postman::TEXT_DOMAIN ), 'https://mandrillapp.com', 'Mandrillapp.com', 'https://mandrillapp.com/settings' ) );
239
  }
240
 
241
  /**
Postman/Postman-Mail/PostmanSendGridTransport.php CHANGED
@@ -186,7 +186,7 @@ class PostmanSendGridTransport extends PostmanAbstractModuleTransport implements
186
  }
187
  public function printSendGridAuthSectionInfo() {
188
  /* Translators: Where (1) is the service URL and (2) is the service name and (3) is a api key URL */
189
- printf ( '<p id="wizard_sendgrid_auth_help">%s</p>', sprintf ( __ ( 'Create an account at <a href="%1$s" target="_new">%2$s</a> and enter <a href="%3$s" target="_new">an API key</a> below.', Postman::TEXT_DOMAIN ), 'https://sendgrid.com', 'SendGrid.com', 'https://app.sendgrid.com/settings/api_keys' ) );
190
  }
191
 
192
  /**
186
  }
187
  public function printSendGridAuthSectionInfo() {
188
  /* Translators: Where (1) is the service URL and (2) is the service name and (3) is a api key URL */
189
+ printf ( '<p id="wizard_sendgrid_auth_help">%s</p>', sprintf ( __ ( 'Create an account at <a href="%1$s" target="_blank">%2$s</a> and enter <a href="%3$s" target="_blank">an API key</a> below.', Postman::TEXT_DOMAIN ), 'https://sendgrid.com', 'SendGrid.com', 'https://app.sendgrid.com/settings/api_keys' ) );
190
  }
191
 
192
  /**
Postman/Postman-Mail/PostmanSmtpModuleTransport.php CHANGED
@@ -528,7 +528,7 @@ class PostmanSmtpModuleTransport extends PostmanAbstractZendModuleTransport impl
528
  $inputValue = (null !== $this->options->getEnvelopeSender() ? esc_attr( $this->options->getEnvelopeSender() ) : '');
529
  $requiredLabel = __( 'Required', Postman::TEXT_DOMAIN );
530
  $envelopeFromMessage = __( 'This address, like the <b>return address</b> printed on an envelope, identifies the account owner to the SMTP server.', Postman::TEXT_DOMAIN );
531
- $spfMessage = sprintf( __( 'For reliable delivery, this domain must specify an <a target="_new" href="%s">SPF record</a> permitting the use of the SMTP server named above.', Postman::TEXT_DOMAIN ), 'https://www.mail-tester.com/spf/' );
532
  printf( '<input type="email" id="input_envelope_sender_email" name="postman_options[envelope_sender]" value="%s" size="40" class="required" placeholder="%s"/> <br/><span class="postman_input_description">%s %s</span>', $inputValue, $requiredLabel, $envelopeFromMessage, $spfMessage );
533
  }
534
 
528
  $inputValue = (null !== $this->options->getEnvelopeSender() ? esc_attr( $this->options->getEnvelopeSender() ) : '');
529
  $requiredLabel = __( 'Required', Postman::TEXT_DOMAIN );
530
  $envelopeFromMessage = __( 'This address, like the <b>return address</b> printed on an envelope, identifies the account owner to the SMTP server.', Postman::TEXT_DOMAIN );
531
+ $spfMessage = sprintf( __( 'For reliable delivery, this domain must specify an <a target="_blank" href="%s">SPF record</a> permitting the use of the SMTP server named above.', Postman::TEXT_DOMAIN ), 'https://www.mail-tester.com/spf/' );
532
  printf( '<input type="email" id="input_envelope_sender_email" name="postman_options[envelope_sender]" value="%s" size="40" class="required" placeholder="%s"/> <br/><span class="postman_input_description">%s %s</span>', $inputValue, $requiredLabel, $envelopeFromMessage, $spfMessage );
533
  }
534
 
Postman/Postman-Mail/Zend-1.12.10/Mail/Protocol/Smtp.php CHANGED
@@ -203,6 +203,11 @@ class Postman_Zend_Mail_Protocol_Smtp extends Postman_Zend_Mail_Protocol_Abstrac
203
  if ($this->_secure == 'tls') {
204
  $this->_send('STARTTLS');
205
  $this->_expect(220, 180);
 
 
 
 
 
206
  if (!stream_socket_enable_crypto($this->_socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
207
  /**
208
  * @see Postman_Zend_Mail_Protocol_Exception
203
  if ($this->_secure == 'tls') {
204
  $this->_send('STARTTLS');
205
  $this->_expect(220, 180);
206
+
207
+ stream_context_set_option($this->_socket, 'ssl', 'verify_peer', false);
208
+ //stream_context_set_option($this->_socket, 'ssl', 'verify_peer_name', false);
209
+ stream_context_set_option($this->_socket, 'ssl', 'allow_self_signed', true);
210
+
211
  if (!stream_socket_enable_crypto($this->_socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
212
  /**
213
  * @see Postman_Zend_Mail_Protocol_Exception
Postman/Postman-Mail/Zend-1.12.10/Validate/Hostname.php CHANGED
@@ -23,12 +23,10 @@
23
  * @see Postman_Zend_Validate_Abstract
24
  */
25
  // require_once 'Zend/Validate/Abstract.php';
26
-
27
  /**
28
  * @see Postman_Zend_Validate_Ip
29
  */
30
  // require_once 'Zend/Validate/Ip.php';
31
-
32
  /**
33
  * Please note there are two standalone test scripts for testing IDN characters due to problems
34
  * with file encoding.
@@ -46,1618 +44,2316 @@
46
  */
47
  class Postman_Zend_Validate_Hostname extends Postman_Zend_Validate_Abstract
48
  {
49
- const CANNOT_DECODE_PUNYCODE = 'hostnameCannotDecodePunycode';
50
- const INVALID = 'hostnameInvalid';
51
- const INVALID_DASH = 'hostnameDashCharacter';
52
- const INVALID_HOSTNAME = 'hostnameInvalidHostname';
53
- const INVALID_HOSTNAME_SCHEMA = 'hostnameInvalidHostnameSchema';
54
- const INVALID_LOCAL_NAME = 'hostnameInvalidLocalName';
55
- const INVALID_URI = 'hostnameInvalidUri';
56
- const IP_ADDRESS_NOT_ALLOWED = 'hostnameIpAddressNotAllowed';
57
- const LOCAL_NAME_NOT_ALLOWED = 'hostnameLocalNameNotAllowed';
58
- const UNDECIPHERABLE_TLD = 'hostnameUndecipherableTld';
59
- const UNKNOWN_TLD = 'hostnameUnknownTld';
60
-
61
- /**
62
- * @var array
63
- */
64
- protected $_messageTemplates = array(
65
- self::CANNOT_DECODE_PUNYCODE => "'%value%' appears to be a DNS hostname but the given punycode notation cannot be decoded",
66
- self::INVALID => "Invalid type given. String expected",
67
- self::INVALID_DASH => "'%value%' appears to be a DNS hostname but contains a dash in an invalid position",
68
- self::INVALID_HOSTNAME => "'%value%' does not match the expected structure for a DNS hostname",
69
- self::INVALID_HOSTNAME_SCHEMA => "'%value%' appears to be a DNS hostname but cannot match against hostname schema for TLD '%tld%'",
70
- self::INVALID_LOCAL_NAME => "'%value%' does not appear to be a valid local network name",
71
- self::INVALID_URI => "'%value%' does not appear to be a valid URI hostname",
72
- self::IP_ADDRESS_NOT_ALLOWED => "'%value%' appears to be an IP address, but IP addresses are not allowed",
73
- self::LOCAL_NAME_NOT_ALLOWED => "'%value%' appears to be a local network name but local network names are not allowed",
74
- self::UNDECIPHERABLE_TLD => "'%value%' appears to be a DNS hostname but cannot extract TLD part",
75
- self::UNKNOWN_TLD => "'%value%' appears to be a DNS hostname but cannot match TLD against known list",
76
- );
77
-
78
- /**
79
- * @var array
80
- */
81
- protected $_messageVariables = array(
82
- 'tld' => '_tld'
83
- );
84
-
85
- /**
86
- * Allows Internet domain names (e.g., example.com)
87
- */
88
- const ALLOW_DNS = 1;
89
-
90
- /**
91
- * Allows IP addresses
92
- */
93
- const ALLOW_IP = 2;
94
-
95
- /**
96
- * Allows local network names (e.g., localhost, www.localdomain)
97
- */
98
- const ALLOW_LOCAL = 4;
99
-
100
- /**
101
- * Allows all types of hostnames
102
- */
103
- const ALLOW_URI = 8;
104
-
105
- /**
106
- * Allows all types of hostnames
107
- */
108
- const ALLOW_ALL = 15;
109
-
110
- /**
111
- * Array of valid top-level-domains
112
- *
113
- * Version 2014112800, Last Updated Fri Nov 28 07:07:01 2014 UTC
114
- *
115
- * @see http://data.iana.org/TLD/tlds-alpha-by-domain.txt List of all TLDs by domain
116
- * @see http://www.iana.org/domains/root/db/ Official list of supported TLDs
117
- * @var array
118
- */
119
- protected $_validTlds = array(
120
- 'abogado',
121
- 'ac',
122
- 'academy',
123
- 'accountants',
124
- 'active',
125
- 'actor',
126
- 'ad',
127
- 'ae',
128
- 'aero',
129
- 'af',
130
- 'ag',
131
- 'agency',
132
- 'ai',
133
- 'airforce',
134
- 'al',
135
- 'allfinanz',
136
- 'alsace',
137
- 'am',
138
- 'an',
139
- 'android',
140
- 'ao',
141
- 'aq',
142
- 'ar',
143
- 'archi',
144
- 'army',
145
- 'arpa',
146
- 'as',
147
- 'asia',
148
- 'associates',
149
- 'at',
150
- 'attorney',
151
- 'au',
152
- 'auction',
153
- 'audio',
154
- 'autos',
155
- 'aw',
156
- 'ax',
157
- 'axa',
158
- 'az',
159
- 'ba',
160
- 'band',
161
- 'bar',
162
- 'bargains',
163
- 'bayern',
164
- 'bb',
165
- 'bd',
166
- 'be',
167
- 'beer',
168
- 'berlin',
169
- 'best',
170
- 'bf',
171
- 'bg',
172
- 'bh',
173
- 'bi',
174
- 'bid',
175
- 'bike',
176
- 'bio',
177
- 'biz',
178
- 'bj',
179
- 'black',
180
- 'blackfriday',
181
- 'bloomberg',
182
- 'blue',
183
- 'bm',
184
- 'bmw',
185
- 'bn',
186
- 'bnpparibas',
187
- 'bo',
188
- 'boo',
189
- 'boutique',
190
- 'br',
191
- 'brussels',
192
- 'bs',
193
- 'bt',
194
- 'budapest',
195
- 'build',
196
- 'builders',
197
- 'business',
198
- 'buzz',
199
- 'bv',
200
- 'bw',
201
- 'by',
202
- 'bz',
203
- 'bzh',
204
- 'ca',
205
- 'cab',
206
- 'cal',
207
- 'camera',
208
- 'camp',
209
- 'cancerresearch',
210
- 'capetown',
211
- 'capital',
212
- 'caravan',
213
- 'cards',
214
- 'care',
215
- 'career',
216
- 'careers',
217
- 'casa',
218
- 'cash',
219
- 'cat',
220
- 'catering',
221
- 'cc',
222
- 'cd',
223
- 'center',
224
- 'ceo',
225
- 'cern',
226
- 'cf',
227
- 'cg',
228
- 'ch',
229
- 'channel',
230
- 'cheap',
231
- 'christmas',
232
- 'chrome',
233
- 'church',
234
- 'ci',
235
- 'citic',
236
- 'city',
237
- 'ck',
238
- 'cl',
239
- 'claims',
240
- 'cleaning',
241
- 'click',
242
- 'clinic',
243
- 'clothing',
244
- 'club',
245
- 'cm',
246
- 'cn',
247
- 'co',
248
- 'coach',
249
- 'codes',
250
- 'coffee',
251
- 'college',
252
- 'cologne',
253
- 'com',
254
- 'community',
255
- 'company',
256
- 'computer',
257
- 'condos',
258
- 'construction',
259
- 'consulting',
260
- 'contractors',
261
- 'cooking',
262
- 'cool',
263
- 'coop',
264
- 'country',
265
- 'cr',
266
- 'credit',
267
- 'creditcard',
268
- 'cricket',
269
- 'crs',
270
- 'cruises',
271
- 'cu',
272
- 'cuisinella',
273
- 'cv',
274
- 'cw',
275
- 'cx',
276
- 'cy',
277
- 'cymru',
278
- 'cz',
279
- 'dad',
280
- 'dance',
281
- 'dating',
282
- 'day',
283
- 'de',
284
- 'deals',
285
- 'degree',
286
- 'delivery',
287
- 'democrat',
288
- 'dental',
289
- 'dentist',
290
- 'desi',
291
- 'diamonds',
292
- 'diet',
293
- 'digital',
294
- 'direct',
295
- 'directory',
296
- 'discount',
297
- 'dj',
298
- 'dk',
299
- 'dm',
300
- 'dnp',
301
- 'do',
302
- 'domains',
303
- 'durban',
304
- 'dvag',
305
- 'dz',
306
- 'eat',
307
- 'ec',
308
- 'edu',
309
- 'education',
310
- 'ee',
311
- 'eg',
312
- 'email',
313
- 'emerck',
314
- 'energy',
315
- 'engineer',
316
- 'engineering',
317
- 'enterprises',
318
- 'equipment',
319
- 'er',
320
- 'es',
321
- 'esq',
322
- 'estate',
323
- 'et',
324
- 'eu',
325
- 'eus',
326
- 'events',
327
- 'everbank',
328
- 'exchange',
329
- 'expert',
330
- 'exposed',
331
- 'fail',
332
- 'farm',
333
- 'feedback',
334
- 'fi',
335
- 'finance',
336
- 'financial',
337
- 'firmdale',
338
- 'fish',
339
- 'fishing',
340
- 'fitness',
341
- 'fj',
342
- 'fk',
343
- 'flights',
344
- 'florist',
345
- 'flsmidth',
346
- 'fly',
347
- 'fm',
348
- 'fo',
349
- 'foo',
350
- 'forsale',
351
- 'foundation',
352
- 'fr',
353
- 'frl',
354
- 'frogans',
355
- 'fund',
356
- 'furniture',
357
- 'futbol',
358
- 'ga',
359
- 'gal',
360
- 'gallery',
361
- 'gb',
362
- 'gbiz',
363
- 'gd',
364
- 'ge',
365
- 'gent',
366
- 'gf',
367
- 'gg',
368
- 'gh',
369
- 'gi',
370
- 'gift',
371
- 'gifts',
372
- 'gives',
373
- 'gl',
374
- 'glass',
375
- 'gle',
376
- 'global',
377
- 'globo',
378
- 'gm',
379
- 'gmail',
380
- 'gmo',
381
- 'gmx',
382
- 'gn',
383
- 'google',
384
- 'gop',
385
- 'gov',
386
- 'gp',
387
- 'gq',
388
- 'gr',
389
- 'graphics',
390
- 'gratis',
391
- 'green',
392
- 'gripe',
393
- 'gs',
394
- 'gt',
395
- 'gu',
396
- 'guide',
397
- 'guitars',
398
- 'guru',
399
- 'gw',
400
- 'gy',
401
- 'hamburg',
402
- 'haus',
403
- 'healthcare',
404
- 'help',
405
- 'here',
406
- 'hiphop',
407
- 'hiv',
408
- 'hk',
409
- 'hm',
410
- 'hn',
411
- 'holdings',
412
- 'holiday',
413
- 'homes',
414
- 'horse',
415
- 'host',
416
- 'hosting',
417
- 'house',
418
- 'how',
419
- 'hr',
420
- 'ht',
421
- 'hu',
422
- 'ibm',
423
- 'id',
424
- 'ie',
425
- 'il',
426
- 'im',
427
- 'immo',
428
- 'immobilien',
429
- 'in',
430
- 'industries',
431
- 'info',
432
- 'ing',
433
- 'ink',
434
- 'institute',
435
- 'insure',
436
- 'int',
437
- 'international',
438
- 'investments',
439
- 'io',
440
- 'iq',
441
- 'ir',
442
- 'is',
443
- 'it',
444
- 'je',
445
- 'jetzt',
446
- 'jm',
447
- 'jo',
448
- 'jobs',
449
- 'joburg',
450
- 'jp',
451
- 'juegos',
452
- 'kaufen',
453
- 'ke',
454
- 'kg',
455
- 'kh',
456
- 'ki',
457
- 'kim',
458
- 'kitchen',
459
- 'kiwi',
460
- 'km',
461
- 'kn',
462
- 'koeln',
463
- 'kp',
464
- 'kr',
465
- 'krd',
466
- 'kred',
467
- 'kw',
468
- 'ky',
469
- 'kz',
470
- 'la',
471
- 'lacaixa',
472
- 'land',
473
- 'lawyer',
474
- 'lb',
475
- 'lc',
476
- 'lds',
477
- 'lease',
478
- 'legal',
479
- 'lgbt',
480
- 'li',
481
- 'life',
482
- 'lighting',
483
- 'limited',
484
- 'limo',
485
- 'link',
486
- 'lk',
487
- 'loans',
488
- 'london',
489
- 'lotto',
490
- 'lr',
491
- 'ls',
492
- 'lt',
493
- 'ltda',
494
- 'lu',
495
- 'luxe',
496
- 'luxury',
497
- 'lv',
498
- 'ly',
499
- 'ma',
500
- 'madrid',
501
- 'maison',
502
- 'management',
503
- 'mango',
504
- 'market',
505
- 'marketing',
506
- 'mc',
507
- 'md',
508
- 'me',
509
- 'media',
510
- 'meet',
511
- 'melbourne',
512
- 'meme',
513
- 'memorial',
514
- 'menu',
515
- 'mg',
516
- 'mh',
517
- 'miami',
518
- 'mil',
519
- 'mini',
520
- 'mk',
521
- 'ml',
522
- 'mm',
523
- 'mn',
524
- 'mo',
525
- 'mobi',
526
- 'moda',
527
- 'moe',
528
- 'monash',
529
- 'money',
530
- 'mormon',
531
- 'mortgage',
532
- 'moscow',
533
- 'motorcycles',
534
- 'mov',
535
- 'mp',
536
- 'mq',
537
- 'mr',
538
- 'ms',
539
- 'mt',
540
- 'mu',
541
- 'museum',
542
- 'mv',
543
- 'mw',
544
- 'mx',
545
- 'my',
546
- 'mz',
547
- 'na',
548
- 'nagoya',
549
- 'name',
550
- 'navy',
551
- 'nc',
552
- 'ne',
553
- 'net',
554
- 'network',
555
- 'neustar',
556
- 'new',
557
- 'nexus',
558
- 'nf',
559
- 'ng',
560
- 'ngo',
561
- 'nhk',
562
- 'ni',
563
- 'ninja',
564
- 'nl',
565
- 'no',
566
- 'np',
567
- 'nr',
568
- 'nra',
569
- 'nrw',
570
- 'nu',
571
- 'nyc',
572
- 'nz',
573
- 'okinawa',
574
- 'om',
575
- 'ong',
576
- 'onl',
577
- 'ooo',
578
- 'org',
579
- 'organic',
580
- 'otsuka',
581
- 'ovh',
582
- 'pa',
583
- 'paris',
584
- 'partners',
585
- 'parts',
586
- 'party',
587
- 'pe',
588
- 'pf',
589
- 'pg',
590
- 'ph',
591
- 'pharmacy',
592
- 'photo',
593
- 'photography',
594
- 'photos',
595
- 'physio',
596
- 'pics',
597
- 'pictures',
598
- 'pink',
599
- 'pizza',
600
- 'pk',
601
- 'pl',
602
- 'place',
603
- 'plumbing',
604
- 'pm',
605
- 'pn',
606
- 'pohl',
607
- 'poker',
608
- 'post',
609
- 'pr',
610
- 'praxi',
611
- 'press',
612
- 'pro',
613
- 'prod',
614
- 'productions',
615
- 'prof',
616
- 'properties',
617
- 'property',
618
- 'ps',
619
- 'pt',
620
- 'pub',
621
- 'pw',
622
- 'py',
623
- 'qa',
624
- 'qpon',
625
- 'quebec',
626
- 're',
627
- 'realtor',
628
- 'recipes',
629
- 'red',
630
- 'rehab',
631
- 'reise',
632
- 'reisen',
633
- 'reit',
634
- 'ren',
635
- 'rentals',
636
- 'repair',
637
- 'report',
638
- 'republican',
639
- 'rest',
640
- 'restaurant',
641
- 'reviews',
642
- 'rich',
643
- 'rio',
644
- 'rip',
645
- 'ro',
646
- 'rocks',
647
- 'rodeo',
648
- 'rs',
649
- 'rsvp',
650
- 'ru',
651
- 'ruhr',
652
- 'rw',
653
- 'ryukyu',
654
- 'sa',
655
- 'saarland',
656
- 'sarl',
657
- 'sb',
658
- 'sc',
659
- 'sca',
660
- 'scb',
661
- 'schmidt',
662
- 'schule',
663
- 'science',
664
- 'scot',
665
- 'sd',
666
- 'se',
667
- 'services',
668
- 'sexy',
669
- 'sg',
670
- 'sh',
671
- 'shiksha',
672
- 'shoes',
673
- 'si',
674
- 'singles',
675
- 'sj',
676
- 'sk',
677
- 'sl',
678
- 'sm',
679
- 'sn',
680
- 'so',
681
- 'social',
682
- 'software',
683
- 'sohu',
684
- 'solar',
685
- 'solutions',
686
- 'soy',
687
- 'space',
688
- 'spiegel',
689
- 'sr',
690
- 'st',
691
- 'su',
692
- 'supplies',
693
- 'supply',
694
- 'support',
695
- 'surf',
696
- 'surgery',
697
- 'suzuki',
698
- 'sv',
699
- 'sx',
700
- 'sy',
701
- 'sydney',
702
- 'systems',
703
- 'sz',
704
- 'taipei',
705
- 'tatar',
706
- 'tattoo',
707
- 'tax',
708
- 'tc',
709
- 'td',
710
- 'technology',
711
- 'tel',
712
- 'tf',
713
- 'tg',
714
- 'th',
715
- 'tienda',
716
- 'tips',
717
- 'tirol',
718
- 'tj',
719
- 'tk',
720
- 'tl',
721
- 'tm',
722
- 'tn',
723
- 'to',
724
- 'today',
725
- 'tokyo',
726
- 'tools',
727
- 'top',
728
- 'town',
729
- 'toys',
730
- 'tp',
731
- 'tr',
732
- 'trade',
733
- 'training',
734
- 'travel',
735
- 'tt',
736
- 'tui',
737
- 'tv',
738
- 'tw',
739
- 'tz',
740
- 'ua',
741
- 'ug',
742
- 'uk',
743
- 'university',
744
- 'uno',
745
- 'uol',
746
- 'us',
747
- 'uy',
748
- 'uz',
749
- 'va',
750
- 'vacations',
751
- 'vc',
752
- 've',
753
- 'vegas',
754
- 'ventures',
755
- 'versicherung',
756
- 'vet',
757
- 'vg',
758
- 'vi',
759
- 'viajes',
760
- 'villas',
761
- 'vision',
762
- 'vlaanderen',
763
- 'vn',
764
- 'vodka',
765
- 'vote',
766
- 'voting',
767
- 'voto',
768
- 'voyage',
769
- 'vu',
770
- 'wales',
771
- 'wang',
772
- 'watch',
773
- 'webcam',
774
- 'website',
775
- 'wed',
776
- 'wedding',
777
- 'wf',
778
- 'whoswho',
779
- 'wien',
780
- 'wiki',
781
- 'williamhill',
782
- 'wme',
783
- 'work',
784
- 'works',
785
- 'world',
786
- 'ws',
787
- 'wtc',
788
- 'wtf',
789
- 'xn--1qqw23a',
790
- 'xn--3bst00m',
791
- 'xn--3ds443g',
792
- 'xn--3e0b707e',
793
- 'xn--45brj9c',
794
- 'xn--45q11c',
795
- 'xn--4gbrim',
796
- 'xn--55qw42g',
797
- 'xn--55qx5d',
798
- 'xn--6frz82g',
799
- 'xn--6qq986b3xl',
800
- 'xn--80adxhks',
801
- 'xn--80ao21a',
802
- 'xn--80asehdb',
803
- 'xn--80aswg',
804
- 'xn--90a3ac',
805
- 'xn--c1avg',
806
- 'xn--cg4bki',
807
- 'xn--clchc0ea0b2g2a9gcd',
808
- 'xn--czr694b',
809
- 'xn--czru2d',
810
- 'xn--d1acj3b',
811
- 'xn--d1alf',
812
- 'xn--fiq228c5hs',
813
- 'xn--fiq64b',
814
- 'xn--fiqs8s',
815
- 'xn--fiqz9s',
816
- 'xn--flw351e',
817
- 'xn--fpcrj9c3d',
818
- 'xn--fzc2c9e2c',
819
- 'xn--gecrj9c',
820
- 'xn--h2brj9c',
821
- 'xn--i1b6b1a6a2e',
822
- 'xn--io0a7i',
823
- 'xn--j1amh',
824
- 'xn--j6w193g',
825
- 'xn--kprw13d',
826
- 'xn--kpry57d',
827
- 'xn--kput3i',
828
- 'xn--l1acc',
829
- 'xn--lgbbat1ad8j',
830
- 'xn--mgb9awbf',
831
- 'xn--mgba3a4f16a',
832
- 'xn--mgbaam7a8h',
833
- 'xn--mgbab2bd',
834
- 'xn--mgbayh7gpa',
835
- 'xn--mgbbh1a71e',
836
- 'xn--mgbc0a9azcg',
837
- 'xn--mgberp4a5d4ar',
838
- 'xn--mgbx4cd0ab',
839
- 'xn--ngbc5azd',
840
- 'xn--node',
841
- 'xn--nqv7f',
842
- 'xn--nqv7fs00ema',
843
- 'xn--o3cw4h',
844
- 'xn--ogbpf8fl',
845
- 'xn--p1acf',
846
- 'xn--p1ai',
847
- 'xn--pgbs0dh',
848
- 'xn--q9jyb4c',
849
- 'xn--qcka1pmc',
850
- 'xn--rhqv96g',
851
- 'xn--s9brj9c',
852
- 'xn--ses554g',
853
- 'xn--unup4y',
854
- 'xn--vermgensberater-ctb',
855
- 'xn--vermgensberatung-pwb',
856
- 'xn--vhquv',
857
- 'xn--wgbh1c',
858
- 'xn--wgbl6a',
859
- 'xn--xhq521b',
860
- 'xn--xkc2al3hye2a',
861
- 'xn--xkc2dl3a5ee0h',
862
- 'xn--yfro4i67o',
863
- 'xn--ygbi2ammx',
864
- 'xn--zfr164b',
865
- 'xxx',
866
- 'xyz',
867
- 'yachts',
868
- 'yandex',
869
- 'ye',
870
- 'yoga',
871
- 'yokohama',
872
- 'youtube',
873
- 'yt',
874
- 'za',
875
- 'zip',
876
- 'zm',
877
- 'zone',
878
- 'zw',
879
- '测试',
880
- 'परीक्षा',
881
- '佛山',
882
- '集团',
883
- '在线',
884
- '한국',
885
- 'ভারত',
886
- '八卦',
887
- 'موقع',
888
- 'বাংলা',
889
- '公益',
890
- '公司',
891
- '移动',
892
- '我爱你',
893
- 'москва',
894
- 'испытание',
895
- 'қаз',
896
- 'онлайн',
897
- 'сайт',
898
- 'срб',
899
- 'бел',
900
- '테스트',
901
- 'орг',
902
- '삼성',
903
- 'சிங்கப்பூர்',
904
- '商标',
905
- '商城',
906
- 'дети',
907
- 'мкд',
908
- 'טעסט',
909
- '中文网',
910
- '中信',
911
- '中国',
912
- '中國',
913
- '谷歌',
914
- 'భారత్',
915
- 'ලංකා',
916
- '測試',
917
- 'ભારત',
918
- 'भारत',
919
- 'آزمایشی',
920
- 'பரிட்சை',
921
- 'संगठन',
922
- '网络',
923
- 'укр',
924
- '香港',
925
- 'δοκιμή',
926
- 'إختبار',
927
- '台湾',
928
- '台灣',
929
- '手机',
930
- 'мон',
931
- 'الجزائر',
932
- 'عمان',
933
- 'ایران',
934
- 'امارات',
935
- 'بازار',
936
- 'پاکستان',
937
- 'الاردن',
938
- 'بھارت',
939
- 'المغرب',
940
- 'السعودية',
941
- 'سودان',
942
- 'عراق',
943
- 'مليسيا',
944
- 'شبكة',
945
- 'გე',
946
- '机构',
947
- '组织机构',
948
- 'ไทย',
949
- 'سورية',
950
- 'рус',
951
- 'рф',
952
- 'تونس',
953
- 'みんな',
954
- 'グーグル',
955
- '世界',
956
- 'ਭਾਰਤ',
957
- '网址',
958
- '游戏',
959
- 'vermögensberater',
960
- 'vermögensberatung',
961
- '企业',
962
- 'مصر',
963
- 'قطر',
964
- '广东',
965
- 'இலங்கை',
966
- 'இந்தியா',
967
- 'հայ',
968
- '新加坡',
969
- 'فلسطين',
970
- 'テスト',
971
- '政务',
972
- );
973
-
974
- /**
975
- * @var string
976
- */
977
- protected $_tld;
978
-
979
- /**
980
- * Array for valid Idns
981
- * @see http://www.iana.org/domains/idn-tables/ Official list of supported IDN Chars
982
- * (.AC) Ascension Island http://www.nic.ac/pdf/AC-IDN-Policy.pdf
983
- * (.AR) Argentinia http://www.nic.ar/faqidn.html
984
- * (.AS) American Samoa http://www.nic.as/idn/chars.cfm
985
- * (.AT) Austria http://www.nic.at/en/service/technical_information/idn/charset_converter/
986
- * (.BIZ) International http://www.iana.org/domains/idn-tables/
987
- * (.BR) Brazil http://registro.br/faq/faq6.html
988
- * (.BV) Bouvett Island http://www.norid.no/domeneregistrering/idn/idn_nyetegn.en.html
989
- * (.CA) Canada http://www.iana.org/domains/idn-tables/tables/ca_fr_1.0.html
990
- * (.CAT) Catalan http://www.iana.org/domains/idn-tables/tables/cat_ca_1.0.html
991
- * (.CH) Switzerland https://nic.switch.ch/reg/ocView.action?res=EF6GW2JBPVTG67DLNIQXU234MN6SC33JNQQGI7L6#anhang1
992
- * (.CL) Chile http://www.iana.org/domains/idn-tables/tables/cl_latn_1.0.html
993
- * (.COM) International http://www.verisign.com/information-services/naming-services/internationalized-domain-names/index.html
994
- * (.DE) Germany http://www.denic.de/en/domains/idns/liste.html
995
- * (.DK) Danmark http://www.dk-hostmaster.dk/index.php?id=151
996
- * (.ES) Spain https://www.nic.es/media/2008-05/1210147705287.pdf
997
- * (.FI) Finland http://www.ficora.fi/en/index/palvelut/fiverkkotunnukset/aakkostenkaytto.html
998
- * (.GR) Greece https://grweb.ics.forth.gr/CharacterTable1_en.jsp
999
- * (.HU) Hungary http://www.domain.hu/domain/English/szabalyzat/szabalyzat.html
1000
- * (.INFO) International http://www.nic.info/info/idn
1001
- * (.IO) British Indian Ocean Territory http://www.nic.io/IO-IDN-Policy.pdf
1002
- * (.IR) Iran http://www.nic.ir/Allowable_Characters_dot-iran
1003
- * (.IS) Iceland http://www.isnic.is/domain/rules.php
1004
- * (.KR) Korea http://www.iana.org/domains/idn-tables/tables/kr_ko-kr_1.0.html
1005
- * (.LI) Liechtenstein https://nic.switch.ch/reg/ocView.action?res=EF6GW2JBPVTG67DLNIQXU234MN6SC33JNQQGI7L6#anhang1
1006
- * (.LT) Lithuania http://www.domreg.lt/static/doc/public/idn_symbols-en.pdf
1007
- * (.MD) Moldova http://www.register.md/
1008
- * (.MUSEUM) International http://www.iana.org/domains/idn-tables/tables/museum_latn_1.0.html
1009
- * (.NET) International http://www.verisign.com/information-services/naming-services/internationalized-domain-names/index.html
1010
- * (.NO) Norway http://www.norid.no/domeneregistrering/idn/idn_nyetegn.en.html
1011
- * (.NU) Niue http://www.worldnames.net/
1012
- * (.ORG) International http://www.pir.org/index.php?db=content/FAQs&tbl=FAQs_Registrant&id=2
1013
- * (.PE) Peru https://www.nic.pe/nuevas_politicas_faq_2.php
1014
- * (.PL) Poland http://www.dns.pl/IDN/allowed_character_sets.pdf
1015
- * (.PR) Puerto Rico http://www.nic.pr/idn_rules.asp
1016
- * (.PT) Portugal https://online.dns.pt/dns_2008/do?com=DS;8216320233;111;+PAGE(4000058)+K-CAT-CODIGO(C.125)+RCNT(100);
1017
- * (.RU) Russia http://www.iana.org/domains/idn-tables/tables/ru_ru-ru_1.0.html
1018
- * (.RS) Serbia http://www.iana.org/domains/idn-tables/tables/rs_sr-rs_1.0.pdf
1019
- * (.SA) Saudi Arabia http://www.iana.org/domains/idn-tables/tables/sa_ar_1.0.html
1020
- * (.SE) Sweden http://www.iis.se/english/IDN_campaignsite.shtml?lang=en
1021
- * (.SH) Saint Helena http://www.nic.sh/SH-IDN-Policy.pdf
1022
- * (.SJ) Svalbard and Jan Mayen http://www.norid.no/domeneregistrering/idn/idn_nyetegn.en.html
1023
- * (.TH) Thailand http://www.iana.org/domains/idn-tables/tables/th_th-th_1.0.html
1024
- * (.TM) Turkmenistan http://www.nic.tm/TM-IDN-Policy.pdf
1025
- * (.TR) Turkey https://www.nic.tr/index.php
1026
- * (.UA) Ukraine http://www.iana.org/domains/idn-tables/tables/ua_cyrl_1.2.html
1027
- * (.VE) Venice http://www.iana.org/domains/idn-tables/tables/ve_es_1.0.html
1028
- * (.VN) Vietnam http://www.vnnic.vn/english/5-6-300-2-2-04-20071115.htm#1.%20Introduction
1029
- *
1030
- * @var array
1031
- */
1032
- protected $_validIdns = array(
1033
- 'AC' => array(1 => '/^[\x{002d}0-9a-zà-öø-ÿāăąćĉċčďđēėęěĝġģĥħīįĵķĺļľŀłńņňŋőœŕŗřśŝşšţťŧūŭůűųŵŷźżž]{1,63}$/iu'),
1034
- 'AR' => array(1 => '/^[\x{002d}0-9a-zà-ãç-êìíñ-õü]{1,63}$/iu'),
1035
- 'AS' => array(1 => '/^[\x{002d}0-9a-zà-öø-ÿāăąćĉċčďđēĕėęěĝğġģĥħĩīĭįıĵķĸĺļľłńņňŋōŏőœŕŗřśŝşšţťŧũūŭůűųŵŷźż]{1,63}$/iu'),
1036
- 'AT' => array(1 => '/^[\x{002d}0-9a-zà-öø-ÿœšž]{1,63}$/iu'),
1037
- 'BIZ' => 'Hostname/Biz.php',
1038
- 'BR' => array(1 => '/^[\x{002d}0-9a-zà-ãçéíó-õúü]{1,63}$/iu'),
1039
- 'BV' => array(1 => '/^[\x{002d}0-9a-zàáä-éêñ-ôöøüčđńŋšŧž]{1,63}$/iu'),
1040
- 'CA' => array(1 => '/^[\x{002d}0-9a-zàâæçéèêëîïôœùûüÿ\x{00E0}\x{00E2}\x{00E7}\x{00E8}\x{00E9}\x{00EA}\x{00EB}\x{00EE}\x{00EF}\x{00F4}\x{00F9}\x{00FB}\x{00FC}\x{00E6}\x{0153}\x{00FF}]{1,63}$/iu'),
1041
- 'CAT' => array(1 => '/^[\x{002d}0-9a-z·àç-éíïòóúü]{1,63}$/iu'),
1042
- 'CH' => array(1 => '/^[\x{002d}0-9a-zà-öø-ÿœ]{1,63}$/iu'),
1043
- 'CL' => array(1 => '/^[\x{002d}0-9a-záéíñóúü]{1,63}$/iu'),
1044
- 'CN' => 'Hostname/Cn.php',
1045
- 'COM' => 'Hostname/Com.php',
1046
- 'DE' => array(1 => '/^[\x{002d}0-9a-zà-öø-ÿăąāćĉčċďđĕěėęēğĝġģĥħĭĩįīıĵķĺľļłńňņŋŏőōœĸŕřŗśŝšşťţŧŭůűũųūŵŷźžż]{1,63}$/iu'),
1047
- 'DK' => array(1 => '/^[\x{002d}0-9a-zäéöüæøå]{1,63}$/iu'),
1048
- 'ES' => array(1 => '/^[\x{002d}0-9a-zàáçèéíïñòóúü·]{1,63}$/iu'),
1049
- 'EU' => array(1 => '/^[\x{002d}0-9a-zà-öø-ÿ]{1,63}$/iu',
1050
- 2 => '/^[\x{002d}0-9a-zāăąćĉċčďđēĕėęěĝğġģĥħĩīĭįıĵķĺļľŀłńņňʼnŋōŏőœŕŗřśŝšťŧũūŭůűųŵŷźżž]{1,63}$/iu',
1051
- 3 => '/^[\x{002d}0-9a-zșț]{1,63}$/iu',
1052
- 4 => '/^[\x{002d}0-9a-zΐάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύώ]{1,63}$/iu',
1053
- 5 => '/^[\x{002d}0-9a-zабвгдежзийклмнопрстуфхцчшщъыьэюя]{1,63}$/iu',
1054
- 6 => '/^[\x{002d}0-9a-zἀ-ἇἐ-ἕἠ-ἧἰ-ἷὀ-ὅὐ-ὗὠ-ὧὰ-ὼώᾀ-ᾇᾐ-ᾗᾠ-ᾧᾰ-ᾴᾶᾷῂῃῄῆῇῐ-ῒΐῖῗῠ-ῧῲῳῴῶῷ]{1,63}$/iu'),
1055
- 'FI' => array(1 => '/^[\x{002d}0-9a-zäåö]{1,63}$/iu'),
1056
- 'GR' => array(1 => '/^[\x{002d}0-9a-zΆΈΉΊΌΎ-ΡΣ-ώἀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙὛὝὟ-ώᾀ-ᾴᾶ-ᾼῂῃῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲῳῴῶ-ῼ]{1,63}$/iu'),
1057
- 'HK' => 'Hostname/Cn.php',
1058
- 'HU' => array(1 => '/^[\x{002d}0-9a-záéíóöúüőű]{1,63}$/iu'),
1059
- 'IL' => array(1 => '/^[\x{002d}0-9\x{05D0}-\x{05EA}]{1,63}$/iu',
1060
- 2 => '/^[\x{002d}0-9a-z]{1,63}$/i'),
1061
- 'INFO'=> array(1 => '/^[\x{002d}0-9a-zäåæéöøü]{1,63}$/iu',
1062
- 2 => '/^[\x{002d}0-9a-záéíóöúüőű]{1,63}$/iu',
1063
- 3 => '/^[\x{002d}0-9a-záæéíðóöúýþ]{1,63}$/iu',
1064
- 4 => '/^[\x{AC00}-\x{D7A3}]{1,17}$/iu',
1065
- 5 => '/^[\x{002d}0-9a-zāčēģīķļņōŗšūž]{1,63}$/iu',
1066
- 6 => '/^[\x{002d}0-9a-ząčėęįšūųž]{1,63}$/iu',
1067
- 7 => '/^[\x{002d}0-9a-zóąćęłńśźż]{1,63}$/iu',
1068
- 8 => '/^[\x{002d}0-9a-záéíñóúü]{1,63}$/iu'),
1069
- 'IO' => array(1 => '/^[\x{002d}0-9a-zà-öø-ÿăąāćĉčċďđĕěėęēğĝġģĥħĭĩįīıĵķĺľļłńňņŋŏőōœĸŕřŗśŝšşťţŧŭůűũųūŵŷźžż]{1,63}$/iu'),
1070
- 'IS' => array(1 => '/^[\x{002d}0-9a-záéýúíóþæöð]{1,63}$/iu'),
1071
- 'IT' => array(1 => '/^[\x{002d}0-9a-zàâäèéêëìîïòôöùûüæœçÿß-]{1,63}$/iu'),
1072
- 'JP' => 'Hostname/Jp.php',
1073
- 'KR' => array(1 => '/^[\x{AC00}-\x{D7A3}]{1,17}$/iu'),
1074
- 'LI' => array(1 => '/^[\x{002d}0-9a-zà-öø-ÿœ]{1,63}$/iu'),
1075
- 'LT' => array(1 => '/^[\x{002d}0-9ąčęėįšųūž]{1,63}$/iu'),
1076
- 'MD' => array(1 => '/^[\x{002d}0-9ăâîşţ]{1,63}$/iu'),
1077
- 'MUSEUM' => array(1 => '/^[\x{002d}0-9a-zà-öø-ÿāăąćċčďđēėęěğġģħīįıķĺļľłńņňŋōőœŕŗřśşšţťŧūůűųŵŷźżžǎǐǒǔ\x{01E5}\x{01E7}\x{01E9}\x{01EF}ə\x{0292}ẁẃẅỳ]{1,63}$/iu'),
1078
- 'NET' => 'Hostname/Com.php',
1079
- 'NO' => array(1 => '/^[\x{002d}0-9a-zàáä-éêñ-ôöøüčđńŋšŧž]{1,63}$/iu'),
1080
- 'NU' => 'Hostname/Com.php',
1081
- 'ORG' => array(1 => '/^[\x{002d}0-9a-záéíñóúü]{1,63}$/iu',
1082
- 2 => '/^[\x{002d}0-9a-zóąćęłńśźż]{1,63}$/iu',
1083
- 3 => '/^[\x{002d}0-9a-záäåæéëíðóöøúüýþ]{1,63}$/iu',
1084
- 4 => '/^[\x{002d}0-9a-záéíóöúüőű]{1,63}$/iu',
1085
- 5 => '/^[\x{002d}0-9a-ząčėęįšūųž]{1,63}$/iu',
1086
- 6 => '/^[\x{AC00}-\x{D7A3}]{1,17}$/iu',
1087
- 7 => '/^[\x{002d}0-9a-zāčēģīķļņōŗšūž]{1,63}$/iu'),
1088
- 'PE' => array(1 => '/^[\x{002d}0-9a-zñáéíóúü]{1,63}$/iu'),
1089
- 'PL' => array(1 => '/^[\x{002d}0-9a-zāčēģīķļņōŗšūž]{1,63}$/iu',
1090
- 2 => '/^[\x{002d}а-ик-ш\x{0450}ѓѕјљњќџ]{1,63}$/iu',
1091
- 3 => '/^[\x{002d}0-9a-zâîăşţ]{1,63}$/iu',
1092
- 4 => '/^[\x{002d}0-9а-яё\x{04C2}]{1,63}$/iu',
1093
- 5 => '/^[\x{002d}0-9a-zàáâèéêìíîòóôùúûċġħż]{1,63}$/iu',
1094
- 6 => '/^[\x{002d}0-9a-zàäåæéêòóôöøü]{1,63}$/iu',
1095
- 7 => '/^[\x{002d}0-9a-zóąćęłńśźż]{1,63}$/iu',
1096
- 8 => '/^[\x{002d}0-9a-zàáâãçéêíòóôõúü]{1,63}$/iu',
1097
- 9 => '/^[\x{002d}0-9a-zâîăşţ]{1,63}$/iu',
1098
- 10=> '/^[\x{002d}0-9a-záäéíóôúýčďĺľňŕšťž]{1,63}$/iu',
1099
- 11=> '/^[\x{002d}0-9a-zçë]{1,63}$/iu',
1100
- 12=> '/^[\x{002d}0-9а-ик-шђјљњћџ]{1,63}$/iu',
1101
- 13=> '/^[\x{002d}0-9a-zćčđšž]{1,63}$/iu',
1102
- 14=> '/^[\x{002d}0-9a-zâçöûüğış]{1,63}$/iu',
1103
- 15=> '/^[\x{002d}0-9a-záéíñóúü]{1,63}$/iu',
1104
- 16=> '/^[\x{002d}0-9a-zäõöüšž]{1,63}$/iu',
1105
- 17=> '/^[\x{002d}0-9a-zĉĝĥĵŝŭ]{1,63}$/iu',
1106
- 18=> '/^[\x{002d}0-9a-zâäéëîô]{1,63}$/iu',
1107
- 19=> '/^[\x{002d}0-9a-zàáâäåæçèéêëìíîïðñòôöøùúûüýćčłńřśš]{1,63}$/iu',
1108
- 20=> '/^[\x{002d}0-9a-zäåæõöøüšž]{1,63}$/iu',
1109
- 21=> '/^[\x{002d}0-9a-zàáçèéìíòóùú]{1,63}$/iu',
1110
- 22=> '/^[\x{002d}0-9a-zàáéíóöúüőű]{1,63}$/iu',
1111
- 23=> '/^[\x{002d}0-9ΐά-ώ]{1,63}$/iu',
1112
- 24=> '/^[\x{002d}0-9a-zàáâåæçèéêëðóôöøüþœ]{1,63}$/iu',
1113
- 25=> '/^[\x{002d}0-9a-záäéíóöúüýčďěňřšťůž]{1,63}$/iu',
1114
- 26=> '/^[\x{002d}0-9a-z·àçèéíïòóúü]{1,63}$/iu',
1115
- 27=> '/^[\x{002d}0-9а-ъьюя\x{0450}\x{045D}]{1,63}$/iu',
1116
- 28=> '/^[\x{002d}0-9а-яёіў]{1,63}$/iu',
1117
- 29=> '/^[\x{002d}0-9a-ząčėęįšūųž]{1,63}$/iu',
1118
- 30=> '/^[\x{002d}0-9a-záäåæéëíðóöøúüýþ]{1,63}$/iu',
1119
- 31=> '/^[\x{002d}0-9a-zàâæçèéêëîïñôùûüÿœ]{1,63}$/iu',
1120
- 32=> '/^[\x{002d}0-9а-щъыьэюяёєіїґ]{1,63}$/iu',
1121
- 33=> '/^[\x{002d}0-9א-ת]{1,63}$/iu'),
1122
- 'PR' => array(1 => '/^[\x{002d}0-9a-záéíóúñäëïüöâêîôûàèùæçœãõ]{1,63}$/iu'),
1123
- 'PT' => array(1 => '/^[\x{002d}0-9a-záàâãçéêíóôõú]{1,63}$/iu'),
1124
- 'RS' => array(1 => '/^[\x{002D}\x{0030}-\x{0039}\x{0061}-\x{007A}\x{0107}\x{010D}\x{0111}\x{0161}\x{017E}]{1,63}$/iu)'),
1125
- 'RU' => array(1 => '/^[\x{002d}0-9а-яё]{1,63}$/iu'),
1126
- 'SA' => array(1 => '/^[\x{002d}.0-9\x{0621}-\x{063A}\x{0641}-\x{064A}\x{0660}-\x{0669}]{1,63}$/iu'),
1127
- 'SE' => array(1 => '/^[\x{002d}0-9a-zäåéöü]{1,63}$/iu'),
1128
- 'SH' => array(1 => '/^[\x{002d}0-9a-zà-öø-ÿăąāćĉčċďđĕěėęēğĝġģĥħĭĩįīıĵķĺľļłńňņŋŏőōœĸŕřŗśŝšşťţŧŭůűũųūŵŷźžż]{1,63}$/iu'),
1129
- 'SI' => array(
1130
- 1 => '/^[\x{002d}0-9a-zà-öø-ÿ]{1,63}$/iu',
1131
- 2 => '/^[\x{002d}0-9a-zāăąćĉċčďđēĕėęěĝğġģĥħĩīĭįıĵķĺļľŀłńņňʼnŋōŏőœŕŗřśŝšťŧũūŭůűųŵŷźżž]{1,63}$/iu',
1132
- 3 => '/^[\x{002d}0-9a-zșț]{1,63}$/iu'),
1133
- 'SJ' => array(1 => '/^[\x{002d}0-9a-zàáä-éêñ-ôöøüčđńŋšŧž]{1,63}$/iu'),
1134
- 'TH' => array(1 => '/^[\x{002d}0-9a-z\x{0E01}-\x{0E3A}\x{0E40}-\x{0E4D}\x{0E50}-\x{0E59}]{1,63}$/iu'),
1135
- 'TM' => array(1 => '/^[\x{002d}0-9a-zà-öø-ÿāăąćĉċčďđēėęěĝġģĥħīįĵķĺļľŀłńņňŋőœŕŗřśŝşšţťŧūŭůűųŵŷźżž]{1,63}$/iu'),
1136
- 'TW' => 'Hostname/Cn.php',
1137
- 'TR' => array(1 => '/^[\x{002d}0-9a-zğıüşöç]{1,63}$/iu'),
1138
- 'UA' => array(1 => '/^[\x{002d}0-9a-zабвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓєѕіїјљњћќѝўџґӂʼ]{1,63}$/iu'),
1139
- 'VE' => array(1 => '/^[\x{002d}0-9a-záéíóúüñ]{1,63}$/iu'),
1140
- 'VN' => array(1 => '/^[ÀÁÂÃÈÉÊÌÍÒÓÔÕÙÚÝàáâãèéêìíòóôõùúýĂăĐđĨĩŨũƠơƯư\x{1EA0}-\x{1EF9}]{1,63}$/iu'),
1141
- 'мон' => array(1 => '/^[\x{002d}0-9\x{0430}-\x{044F}]{1,63}$/iu'),
1142
- 'срб' => array(1 => '/^[\x{002d}0-9а-ик-шђјљњћџ]{1,63}$/iu'),
1143
- 'сайт' => array(1 => '/^[\x{002d}0-9а-яёіїѝйўґг]{1,63}$/iu'),
1144
- 'онлайн' => array(1 => '/^[\x{002d}0-9а-яёіїѝйўґг]{1,63}$/iu'),
1145
- '中国' => 'Hostname/Cn.php',
1146
- '中國' => 'Hostname/Cn.php',
1147
- 'ලංකා' => array(1 => '/^[\x{0d80}-\x{0dff}]{1,63}$/iu'),
1148
- '香港' => 'Hostname/Cn.php',
1149
- '台湾' => 'Hostname/Cn.php',
1150
- '台灣' => 'Hostname/Cn.php',
1151
- 'امارات' => array(1 => '/^[\x{0621}-\x{0624}\x{0626}-\x{063A}\x{0641}\x{0642}\x{0644}-\x{0648}\x{067E}\x{0686}\x{0698}\x{06A9}\x{06AF}\x{06CC}\x{06F0}-\x{06F9}]{1,30}$/iu'),
1152
- 'الاردن' => array(1 => '/^[\x{0621}-\x{0624}\x{0626}-\x{063A}\x{0641}\x{0642}\x{0644}-\x{0648}\x{067E}\x{0686}\x{0698}\x{06A9}\x{06AF}\x{06CC}\x{06F0}-\x{06F9}]{1,30}$/iu'),
1153
- 'السعودية' => array(1 => '/^[\x{0621}-\x{0624}\x{0626}-\x{063A}\x{0641}\x{0642}\x{0644}-\x{0648}\x{067E}\x{0686}\x{0698}\x{06A9}\x{06AF}\x{06CC}\x{06F0}-\x{06F9}]{1,30}$/iu'),
1154
- 'ไทย' => array(1 => '/^[\x{002d}0-9a-z\x{0E01}-\x{0E3A}\x{0E40}-\x{0E4D}\x{0E50}-\x{0E59}]{1,63}$/iu'),
1155
- 'рф' => array(1 => '/^[\x{002d}0-9а-яё]{1,63}$/iu'),
1156
- 'تونس' => array(1 => '/^[\x{0621}-\x{0624}\x{0626}-\x{063A}\x{0641}\x{0642}\x{0644}-\x{0648}\x{067E}\x{0686}\x{0698}\x{06A9}\x{06AF}\x{06CC}\x{06F0}-\x{06F9}]{1,30}$/iu'),
1157
- 'مصر' => array(1 => '/^[\x{0621}-\x{0624}\x{0626}-\x{063A}\x{0641}\x{0642}\x{0644}-\x{0648}\x{067E}\x{0686}\x{0698}\x{06A9}\x{06AF}\x{06CC}\x{06F0}-\x{06F9}]{1,30}$/iu'),
1158
- 'இலங்கை' => array(1 => '/^[\x{0b80}-\x{0bff}]{1,63}$/iu'),
1159
- 'فلسطين' => array(1 => '/^[\x{0621}-\x{0624}\x{0626}-\x{063A}\x{0641}\x{0642}\x{0644}-\x{0648}\x{067E}\x{0686}\x{0698}\x{06A9}\x{06AF}\x{06CC}\x{06F0}-\x{06F9}]{1,30}$/iu'),
1160
- 'شبكة' => array(1 => '/^[\x{0621}-\x{0624}\x{0626}-\x{063A}\x{0641}\x{0642}\x{0644}-\x{0648}\x{067E}\x{0686}\x{0698}\x{06A9}\x{06AF}\x{06CC}\x{06F0}-\x{06F9}]{1,30}$/iu'),
1161
- );
1162
-
1163
- protected $_idnLength = array(
1164
- 'BIZ' => array(5 => 17, 11 => 15, 12 => 20),
1165
- 'CN' => array(1 => 20),
1166
- 'COM' => array(3 => 17, 5 => 20),
1167
- 'HK' => array(1 => 15),
1168
- 'INFO'=> array(4 => 17),
1169
- 'KR' => array(1 => 17),
1170
- 'NET' => array(3 => 17, 5 => 20),
1171
- 'ORG' => array(6 => 17),
1172
- 'TW' => array(1 => 20),
1173
- 'ایران' => array(1 => 30),
1174
- '中国' => array(1 => 20),
1175
- '公司' => array(1 => 20),
1176
- '网络' => array(1 => 20),
1177
- );
1178
-
1179
- protected $_options = array(
1180
- 'allow' => self::ALLOW_DNS,
1181
- 'idn' => true,
1182
- 'tld' => true,
1183
- 'ip' => null
1184
- );
1185
-
1186
- /**
1187
- * Sets validator options
1188
- *
1189
- * @see http://www.iana.org/cctld/specifications-policies-cctlds-01apr02.htm Technical Specifications for ccTLDs
1190
- * @param array $options Validator options
1191
- */
1192
- public function __construct($options = array())
1193
- {
1194
- if ($options instanceof Postman_Zend_Config) {
1195
- $options = $options->toArray();
1196
- } else if (!is_array($options)) {
1197
- $options = func_get_args();
1198
- $temp['allow'] = array_shift($options);
1199
- if (!empty($options)) {
1200
- $temp['idn'] = array_shift($options);
1201
- }
1202
-
1203
- if (!empty($options)) {
1204
- $temp['tld'] = array_shift($options);
1205
- }
1206
-
1207
- if (!empty($options)) {
1208
- $temp['ip'] = array_shift($options);
1209
- }
1210
-
1211
- $options = $temp;
1212
- }
1213
-
1214
- $options += $this->_options;
1215
- $this->setOptions($options);
1216
- }
1217
-
1218
- /**
1219
- * Returns all set options
1220
- *
1221
- * @return array
1222
- */
1223
- public function getOptions()
1224
- {
1225
- return $this->_options;
1226
- }
1227
-
1228
- /**
1229
- * Sets the options for this validator
1230
- *
1231
- * @param array $options
1232
- * @return Postman_Zend_Validate_Hostname
1233
- */
1234
- public function setOptions($options)
1235
- {
1236
- if (array_key_exists('allow', $options)) {
1237
- $this->setAllow($options['allow']);
1238
- }
1239
-
1240
- if (array_key_exists('idn', $options)) {
1241
- $this->setValidateIdn($options['idn']);
1242
- }
1243
-
1244
- if (array_key_exists('tld', $options)) {
1245
- $this->setValidateTld($options['tld']);
1246
- }
1247
-
1248
- if (array_key_exists('ip', $options)) {
1249
- $this->setIpValidator($options['ip']);
1250
- }
1251
-
1252
- return $this;
1253
- }
1254
-
1255
- /**
1256
- * Returns the set ip validator
1257
- *
1258
- * @return Postman_Zend_Validate_Ip
1259
- */
1260
- public function getIpValidator()
1261
- {
1262
- return $this->_options['ip'];
1263
- }
1264
-
1265
- /**
1266
- * @param Postman_Zend_Validate_Ip $ipValidator OPTIONAL
1267
- * @return Postman_Zend_Validate_Hostname
1268
- */
1269
- public function setIpValidator(Postman_Zend_Validate_Ip $ipValidator = null)
1270
- {
1271
- if ($ipValidator === null) {
1272
- $ipValidator = new Postman_Zend_Validate_Ip();
1273
- }
1274
-
1275
- $this->_options['ip'] = $ipValidator;
1276
- return $this;
1277
- }
1278
-
1279
- /**
1280
- * Returns the allow option
1281
- *
1282
- * @return integer
1283
- */
1284
- public function getAllow()
1285
- {
1286
- return $this->_options['allow'];
1287
- }
1288
-
1289
- /**
1290
- * Sets the allow option
1291
- *
1292
- * @param integer $allow
1293
- * @return Postman_Zend_Validate_Hostname Provides a fluent interface
1294
- */
1295
- public function setAllow($allow)
1296
- {
1297
- $this->_options['allow'] = $allow;
1298
- return $this;
1299
- }
1300
-
1301
- /**
1302
- * Returns the set idn option
1303
- *
1304
- * @return boolean
1305
- */
1306
- public function getValidateIdn()
1307
- {
1308
- return $this->_options['idn'];
1309
- }
1310
-
1311
- /**
1312
- * Set whether IDN domains are validated
1313
- *
1314
- * This only applies when DNS hostnames are validated
1315
- *
1316
- * @param boolean $allowed Set allowed to true to validate IDNs, and false to not validate them
1317
- * @return $this
1318
- */
1319
- public function setValidateIdn ($allowed)
1320
- {
1321
- $this->_options['idn'] = (bool) $allowed;
1322
- return $this;
1323
- }
1324
-
1325
- /**
1326
- * Returns the set tld option
1327
- *
1328
- * @return boolean
1329
- */
1330
- public function getValidateTld()
1331
- {
1332
- return $this->_options['tld'];
1333
- }
1334
-
1335
- /**
1336
- * Set whether the TLD element of a hostname is validated
1337
- *
1338
- * This only applies when DNS hostnames are validated
1339
- *
1340
- * @param boolean $allowed Set allowed to true to validate TLDs, and false to not validate them
1341
- * @return $this
1342
- */
1343
- public function setValidateTld ($allowed)
1344
- {
1345
- $this->_options['tld'] = (bool) $allowed;
1346
- return $this;
1347
- }
1348
-
1349
- /**
1350
- * Defined by Postman_Zend_Validate_Interface
1351
- *
1352
- * Returns true if and only if the $value is a valid hostname with respect to the current allow option
1353
- *
1354
- * @param string $value
1355
- * @throws Postman_Zend_Validate_Exception if a fatal error occurs for validation process
1356
- * @return boolean
1357
- */
1358
- public function isValid($value)
1359
- {
1360
- if (!is_string($value)) {
1361
- $this->_error(self::INVALID);
1362
- return false;
1363
- }
1364
-
1365
- $this->_setValue($value);
1366
- // Check input against IP address schema
1367
- if (preg_match('/^[0-9a-f:.]*$/i', $value) &&
1368
- $this->_options['ip']->setTranslator($this->getTranslator())->isValid($value)) {
1369
- if (!($this->_options['allow'] & self::ALLOW_IP)) {
1370
- $this->_error(self::IP_ADDRESS_NOT_ALLOWED);
1371
- return false;
1372
- } else {
1373
- return true;
1374
- }
1375
- }
1376
-
1377
- // RFC3986 3.2.2 states:
1378
- //
1379
- // The rightmost domain label of a fully qualified domain name
1380
- // in DNS may be followed by a single "." and should be if it is
1381
- // necessary to distinguish between the complete domain name and
1382
- // some local domain.
1383
- //
1384
- // (see ZF-6363)
1385
-
1386
- // Local hostnames are allowed to be partitial (ending '.')
1387
- if ($this->_options['allow'] & self::ALLOW_LOCAL) {
1388
- if (substr($value, -1) === '.') {
1389
- $value = substr($value, 0, -1);
1390
- if (substr($value, -1) === '.') {
1391
- // Empty hostnames (ending '..') are not allowed
1392
- $this->_error(self::INVALID_LOCAL_NAME);
1393
- return false;
1394
- }
1395
- }
1396
- }
1397
-
1398
- $domainParts = explode('.', $value);
1399
-
1400
- // Prevent partitial IP V4 adresses (ending '.')
1401
- if ((count($domainParts) == 4) && preg_match('/^[0-9.a-e:.]*$/i', $value) &&
1402
- $this->_options['ip']->setTranslator($this->getTranslator())->isValid($value)) {
1403
- $this->_error(self::INVALID_LOCAL_NAME);
1404
- }
1405
-
1406
- // Check input against DNS hostname schema
1407
- if ((count($domainParts) > 1) && (strlen($value) >= 4) && (strlen($value) <= 254)) {
1408
- $status = false;
1409
-
1410
- $origenc = PHP_VERSION_ID < 50600
1411
- ? iconv_get_encoding('internal_encoding')
1412
- : ini_get('default_charset');
1413
- if (PHP_VERSION_ID < 50600) {
1414
- iconv_set_encoding('internal_encoding', 'UTF-8');
1415
- } else {
1416
- // jason was here
1417
- @ini_set('default_charset', 'UTF-8');
1418
- }
1419
- do {
1420
- // First check TLD
1421
- $matches = array();
1422
- if (preg_match('/([^.]{2,63})$/iu', end($domainParts), $matches)
1423
- || (array_key_exists(end($domainParts), $this->_validIdns))) {
1424
- reset($domainParts);
1425
-
1426
- // Hostname characters are: *(label dot)(label dot label); max 254 chars
1427
- // label: id-prefix [*ldh{61} id-prefix]; max 63 chars
1428
- // id-prefix: alpha / digit
1429
- // ldh: alpha / digit / dash
1430
-
1431
- // Match TLD against known list
1432
- $this->_tld = $matches[1];
1433
- if ($this->_options['tld']) {
1434
- if (!in_array(strtolower($this->_tld), $this->_validTlds)
1435
- && !in_array($this->_tld, $this->_validTlds)) {
1436
- $this->_error(self::UNKNOWN_TLD);
1437
- $status = false;
1438
- break;
1439
- }
1440
- // We have already validated that the TLD is fine. We don't want it to go through the below
1441
- // checks as new UTF-8 TLDs will incorrectly fail if there is no IDN regex for it.
1442
- array_pop($domainParts);
1443
- }
1444
-
1445
- /**
1446
- * Match against IDN hostnames
1447
- * Note: Keep label regex short to avoid issues with long patterns when matching IDN hostnames
1448
- * @see Postman_Zend_Validate_Hostname_Interface
1449
- */
1450
- $regexChars = array(0 => '/^[a-z0-9\x2d]{1,63}$/i');
1451
- if ($this->_options['idn'] && isset($this->_validIdns[strtoupper($this->_tld)])) {
1452
- if (is_string($this->_validIdns[strtoupper($this->_tld)])) {
1453
- $regexChars += include($this->_validIdns[strtoupper($this->_tld)]);
1454
- } else {
1455
- $regexChars += $this->_validIdns[strtoupper($this->_tld)];
1456
- }
1457
- }
1458
-
1459
- // Check each hostname part
1460
- $check = 0;
1461
- foreach ($domainParts as $domainPart) {
1462
- // If some domain part is empty (i.e. zend..com), it's invalid
1463
- if (empty($domainPart)) {
1464
- $this->_error(self::INVALID_HOSTNAME);
1465
- return false;
1466
- }
1467
-
1468
- // Decode Punycode domainnames to IDN
1469
- if (strpos($domainPart, 'xn--') === 0) {
1470
- $domainPart = $this->decodePunycode(substr($domainPart, 4));
1471
- if ($domainPart === false) {
1472
- return false;
1473
- }
1474
- }
1475
-
1476
- // Check dash (-) does not start, end or appear in 3rd and 4th positions
1477
- if ((strpos($domainPart, '-') === 0)
1478
- || ((strlen($domainPart) > 2) && (strpos($domainPart, '-', 2) == 2) && (strpos($domainPart, '-', 3) == 3))
1479
- || (strpos($domainPart, '-') === (strlen($domainPart) - 1))) {
1480
- $this->_error(self::INVALID_DASH);
1481
- $status = false;
1482
- break 2;
1483
- }
1484
-
1485
- // Check each domain part
1486
- $checked = false;
1487
- foreach($regexChars as $regexKey => $regexChar) {
1488
- $status = preg_match($regexChar, $domainPart);
1489
- if ($status > 0) {
1490
- $length = 63;
1491
- if (array_key_exists(strtoupper($this->_tld), $this->_idnLength)
1492
- && (array_key_exists($regexKey, $this->_idnLength[strtoupper($this->_tld)]))) {
1493
- $length = $this->_idnLength[strtoupper($this->_tld)];
1494
- }
1495
-
1496
- if (iconv_strlen($domainPart, 'UTF-8') > $length) {
1497
- $this->_error(self::INVALID_HOSTNAME);
1498
- } else {
1499
- $checked = true;
1500
- break;
1501
- }
1502
- }
1503
- }
1504
-
1505
- if ($checked) {
1506
- ++$check;
1507
- }
1508
- }
1509
-
1510
- // If one of the labels doesn't match, the hostname is invalid
1511
- if ($check !== count($domainParts)) {
1512
- $this->_error(self::INVALID_HOSTNAME_SCHEMA);
1513
- $status = false;
1514
- }
1515
- } else {
1516
- // Hostname not long enough
1517
- $this->_error(self::UNDECIPHERABLE_TLD);
1518
- $status = false;
1519
- }
1520
- } while (false);
1521
-
1522
- if (PHP_VERSION_ID < 50600) {
1523
- iconv_set_encoding('internal_encoding', $origenc);
1524
- } else {
1525
- // jason was here
1526
- @ini_set('default_charset', $origenc);
1527
- }
1528
- // If the input passes as an Internet domain name, and domain names are allowed, then the hostname
1529
- // passes validation
1530
- if ($status && ($this->_options['allow'] & self::ALLOW_DNS)) {
1531
- return true;
1532
- }
1533
- } else if ($this->_options['allow'] & self::ALLOW_DNS) {
1534
- $this->_error(self::INVALID_HOSTNAME);
1535
- }
1536
-
1537
- // Check for URI Syntax (RFC3986)
1538
- if ($this->_options['allow'] & self::ALLOW_URI) {
1539
- if (preg_match("/^([a-zA-Z0-9-._~!$&\'()*+,;=]|%[[:xdigit:]]{2}){1,254}$/i", $value)) {
1540
- return true;
1541
- } else {
1542
- $this->_error(self::INVALID_URI);
1543
- }
1544
- }
1545
-
1546
- // Check input against local network name schema; last chance to pass validation
1547
- $regexLocal = '/^(([a-zA-Z0-9\x2d]{1,63}\x2e)*[a-zA-Z0-9\x2d]{1,63}[\x2e]{0,1}){1,254}$/';
1548
- $status = @preg_match($regexLocal, $value);
1549
-
1550
- // If the input passes as a local network name, and local network names are allowed, then the
1551
- // hostname passes validation
1552
- $allowLocal = $this->_options['allow'] & self::ALLOW_LOCAL;
1553
- if ($status && $allowLocal) {
1554
- return true;
1555
- }
1556
-
1557
- // If the input does not pass as a local network name, add a message
1558
- if (!$status) {
1559
- $this->_error(self::INVALID_LOCAL_NAME);
1560
- }
1561
-
1562
- // If local network names are not allowed, add a message
1563
- if ($status && !$allowLocal) {
1564
- $this->_error(self::LOCAL_NAME_NOT_ALLOWED);
1565
- }
1566
-
1567
- return false;
1568
- }
1569
-
1570
- /**
1571
- * Decodes a punycode encoded string to it's original utf8 string
1572
- * In case of a decoding failure the original string is returned
1573
- *
1574
- * @param string $encoded Punycode encoded string to decode
1575
- * @return string
1576
- */
1577
- protected function decodePunycode($encoded)
1578
- {
1579
- if (!preg_match('/^[a-z0-9-]+$/i', $encoded)) {
1580
- // no punycode encoded string
1581
- $this->_error(self::CANNOT_DECODE_PUNYCODE);
1582
- return false;
1583
- }
1584
-
1585
- $decoded = array();
1586
- $separator = strrpos($encoded, '-');
1587
- if ($separator > 0) {
1588
- for ($x = 0; $x < $separator; ++$x) {
1589
- // prepare decoding matrix
1590
- $decoded[] = ord($encoded[$x]);
1591
- }
1592
- }
1593
-
1594
- $lengthd = count($decoded);
1595
- $lengthe = strlen($encoded);
1596
-
1597
- // decoding
1598
- $init = true;
1599
- $base = 72;
1600
- $index = 0;
1601
- $char = 0x80;
1602
-
1603
- for ($indexe = ($separator) ? ($separator + 1) : 0; $indexe < $lengthe; ++$lengthd) {
1604
- for ($old_index = $index, $pos = 1, $key = 36; 1 ; $key += 36) {
1605
- $hex = ord($encoded[$indexe++]);
1606
- $digit = ($hex - 48 < 10) ? $hex - 22
1607
- : (($hex - 65 < 26) ? $hex - 65
1608
- : (($hex - 97 < 26) ? $hex - 97
1609
- : 36));
1610
-
1611
- $index += $digit * $pos;
1612
- $tag = ($key <= $base) ? 1 : (($key >= $base + 26) ? 26 : ($key - $base));
1613
- if ($digit < $tag) {
1614
- break;
1615
- }
1616
-
1617
- $pos = (int) ($pos * (36 - $tag));
1618
- }
1619
-
1620
- $delta = intval($init ? (($index - $old_index) / 700) : (($index - $old_index) / 2));
1621
- $delta += intval($delta / ($lengthd + 1));
1622
- for ($key = 0; $delta > 910 / 2; $key += 36) {
1623
- $delta = intval($delta / 35);
1624
- }
1625
-
1626
- $base = intval($key + 36 * $delta / ($delta + 38));
1627
- $init = false;
1628
- $char += (int) ($index / ($lengthd + 1));
1629
- $index %= ($lengthd + 1);
1630
- if ($lengthd > 0) {
1631
- for ($i = $lengthd; $i > $index; $i--) {
1632
- $decoded[$i] = $decoded[($i - 1)];
1633
- }
1634
- }
1635
-
1636
- $decoded[$index++] = $char;
1637
- }
1638
-
1639
- // convert decoded ucs4 to utf8 string
1640
- foreach ($decoded as $key => $value) {
1641
- if ($value < 128) {
1642
- $decoded[$key] = chr($value);
1643
- } elseif ($value < (1 << 11)) {
1644
- $decoded[$key] = chr(192 + ($value >> 6));
1645
- $decoded[$key] .= chr(128 + ($value & 63));
1646
- } elseif ($value < (1 << 16)) {
1647
- $decoded[$key] = chr(224 + ($value >> 12));
1648
- $decoded[$key] .= chr(128 + (($value >> 6) & 63));
1649
- $decoded[$key] .= chr(128 + ($value & 63));
1650
- } elseif ($value < (1 << 21)) {
1651
- $decoded[$key] = chr(240 + ($value >> 18));
1652
- $decoded[$key] .= chr(128 + (($value >> 12) & 63));
1653
- $decoded[$key] .= chr(128 + (($value >> 6) & 63));
1654
- $decoded[$key] .= chr(128 + ($value & 63));
1655
- } else {
1656
- $this->_error(self::CANNOT_DECODE_PUNYCODE);
1657
- return false;
1658
- }
1659
- }
1660
-
1661
- return implode($decoded);
1662
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1663
  }
23
  * @see Postman_Zend_Validate_Abstract
24
  */
25
  // require_once 'Zend/Validate/Abstract.php';
 
26
  /**
27
  * @see Postman_Zend_Validate_Ip
28
  */
29
  // require_once 'Zend/Validate/Ip.php';
 
30
  /**
31
  * Please note there are two standalone test scripts for testing IDN characters due to problems
32
  * with file encoding.
44
  */
45
  class Postman_Zend_Validate_Hostname extends Postman_Zend_Validate_Abstract
46
  {
47
+ const CANNOT_DECODE_PUNYCODE = 'hostnameCannotDecodePunycode';
48
+ const INVALID = 'hostnameInvalid';
49
+ const INVALID_DASH = 'hostnameDashCharacter';
50
+ const INVALID_HOSTNAME = 'hostnameInvalidHostname';
51
+ const INVALID_HOSTNAME_SCHEMA = 'hostnameInvalidHostnameSchema';
52
+ const INVALID_LOCAL_NAME = 'hostnameInvalidLocalName';
53
+ const INVALID_URI = 'hostnameInvalidUri';
54
+ const IP_ADDRESS_NOT_ALLOWED = 'hostnameIpAddressNotAllowed';
55
+ const LOCAL_NAME_NOT_ALLOWED = 'hostnameLocalNameNotAllowed';
56
+ const UNDECIPHERABLE_TLD = 'hostnameUndecipherableTld';
57
+ const UNKNOWN_TLD = 'hostnameUnknownTld';
58
+
59
+ /**
60
+ * @var array
61
+ */
62
+ protected $_messageTemplates = array(
63
+ self::CANNOT_DECODE_PUNYCODE => "'%value%' appears to be a DNS hostname but the given punycode notation cannot be decoded",
64
+ self::INVALID => 'Invalid type given. String expected',
65
+ self::INVALID_DASH => "'%value%' appears to be a DNS hostname but contains a dash in an invalid position",
66
+ self::INVALID_HOSTNAME => "'%value%' does not match the expected structure for a DNS hostname",
67
+ self::INVALID_HOSTNAME_SCHEMA => "'%value%' appears to be a DNS hostname but cannot match against hostname schema for TLD '%tld%'",
68
+ self::INVALID_LOCAL_NAME => "'%value%' does not appear to be a valid local network name",
69
+ self::INVALID_URI => "'%value%' does not appear to be a valid URI hostname",
70
+ self::IP_ADDRESS_NOT_ALLOWED => "'%value%' appears to be an IP address, but IP addresses are not allowed",
71
+ self::LOCAL_NAME_NOT_ALLOWED => "'%value%' appears to be a local network name but local network names are not allowed",
72
+ self::UNDECIPHERABLE_TLD => "'%value%' appears to be a DNS hostname but cannot extract TLD part",
73
+ self::UNKNOWN_TLD => "'%value%' appears to be a DNS hostname but cannot match TLD against known list",
74
+ );
75
+
76
+ /**
77
+ * @var array
78
+ */
79
+ protected $_messageVariables = array(
80
+ 'tld' => '_tld',
81
+ );
82
+
83
+ /**
84
+ * Allows Internet domain names (e.g., example.com)
85
+ */
86
+ const ALLOW_DNS = 1;
87
+
88
+ /**
89
+ * Allows IP addresses
90
+ */
91
+ const ALLOW_IP = 2;
92
+
93
+ /**
94
+ * Allows local network names (e.g., localhost, www.localdomain)
95
+ */
96
+ const ALLOW_LOCAL = 4;
97
+
98
+ /**
99
+ * Allows all types of hostnames
100
+ */
101
+ const ALLOW_URI = 8;
102
+
103
+ /**
104
+ * Allows all types of hostnames
105
+ */
106
+ const ALLOW_ALL = 15;
107
+
108
+ /**
109
+ * Array of valid top-level-domains
110
+ *
111
+ * Version 2014112800, Last Updated Fri Nov 28 07:07:01 2014 UTC
112
+ *
113
+ * @see http://data.iana.org/TLD/tlds-alpha-by-domain.txt List of all TLDs by domain
114
+ * @see http://www.iana.org/domains/root/db/ Official list of supported TLDs
115
+ * @var array
116
+ */
117
+ protected $_validTlds = array(
118
+ 'aaa',
119
+ 'aarp',
120
+ 'abarth',
121
+ 'abb',
122
+ 'abbott',
123
+ 'abbvie',
124
+ 'abc',
125
+ 'able',
126
+ 'abogado',
127
+ 'abudhabi',
128
+ 'ac',
129
+ 'academy',
130
+ 'accenture',
131
+ 'accountant',
132
+ 'accountants',
133
+ 'aco',
134
+ 'active',
135
+ 'actor',
136
+ 'ad',
137
+ 'adac',
138
+ 'ads',
139
+ 'adult',
140
+ 'ae',
141
+ 'aeg',
142
+ 'aero',
143
+ 'aetna',
144
+ 'af',
145
+ 'afamilycompany',
146
+ 'afl',
147
+ 'africa',
148
+ 'ag',
149
+ 'agakhan',
150
+ 'agency',
151
+ 'ai',
152
+ 'aig',
153
+ 'aigo',
154
+ 'airbus',
155
+ 'airforce',
156
+ 'airtel',
157
+ 'akdn',
158
+ 'al',
159
+ 'alfaromeo',
160
+ 'alibaba',
161
+ 'alipay',
162
+ 'allfinanz',
163
+ 'allstate',
164
+ 'ally',
165
+ 'alsace',
166
+ 'alstom',
167
+ 'am',
168
+ 'americanexpress',
169
+ 'americanfamily',
170
+ 'amex',
171
+ 'amfam',
172
+ 'amica',
173
+ 'amsterdam',
174
+ 'analytics',
175
+ 'android',
176
+ 'anquan',
177
+ 'anz',
178
+ 'ao',
179
+ 'aol',
180
+ 'apartments',
181
+ 'app',
182
+ 'apple',
183
+ 'aq',
184
+ 'aquarelle',
185
+ 'ar',
186
+ 'arab',
187
+ 'aramco',
188
+ 'archi',
189
+ 'army',
190
+ 'arpa',
191
+ 'art',
192
+ 'arte',
193
+ 'as',
194
+ 'asda',
195
+ 'asia',
196
+ 'associates',
197
+ 'at',
198
+ 'athleta',
199
+ 'attorney',
200
+ 'au',
201
+ 'auction',
202
+ 'audi',
203
+ 'audible',
204
+ 'audio',
205
+ 'auspost',
206
+ 'author',
207
+ 'auto',
208
+ 'autos',
209
+ 'avianca',
210
+ 'aw',
211
+ 'aws',
212
+ 'ax',
213
+ 'axa',
214
+ 'az',
215
+ 'azure',
216
+ 'ba',
217
+ 'baby',
218
+ 'baidu',
219
+ 'banamex',
220
+ 'bananarepublic',
221
+ 'band',
222
+ 'bank',
223
+ 'bar',
224
+ 'barcelona',
225
+ 'barclaycard',
226
+ 'barclays',
227
+ 'barefoot',
228
+ 'bargains',
229
+ 'baseball',
230
+ 'basketball',
231
+ 'bauhaus',
232
+ 'bayern',
233
+ 'bb',
234
+ 'bbc',
235
+ 'bbt',
236
+ 'bbva',
237
+ 'bcg',
238
+ 'bcn',
239
+ 'bd',
240
+ 'be',
241
+ 'beats',
242
+ 'beauty',
243
+ 'beer',
244
+ 'bentley',
245
+ 'berlin',
246
+ 'best',
247
+ 'bestbuy',
248
+ 'bet',
249
+ 'bf',
250
+ 'bg',
251
+ 'bh',
252
+ 'bharti',
253
+ 'bi',
254
+ 'bible',
255
+ 'bid',
256
+ 'bike',
257
+ 'bing',
258
+ 'bingo',
259
+ 'bio',
260
+ 'biz',
261
+ 'bj',
262
+ 'black',
263
+ 'blackfriday',
264
+ 'blanco',
265
+ 'blockbuster',
266
+ 'blog',
267
+ 'bloomberg',
268
+ 'blue',
269
+ 'bm',
270
+ 'bms',
271
+ 'bmw',
272
+ 'bn',
273
+ 'bnl',
274
+ 'bnpparibas',
275
+ 'bo',
276
+ 'boats',
277
+ 'boehringer',
278
+ 'bofa',
279
+ 'bom',
280
+ 'bond',
281
+ 'boo',
282
+ 'book',
283
+ 'booking',
284
+ 'boots',
285
+ 'bosch',
286
+ 'bostik',
287
+ 'boston',
288
+ 'bot',
289
+ 'boutique',
290
+ 'box',
291
+ 'br',
292
+ 'bradesco',
293
+ 'bridgestone',
294
+ 'broadway',
295
+ 'broker',
296
+ 'brother',
297
+ 'brussels',
298
+ 'bs',
299
+ 'bt',
300
+ 'budapest',
301
+ 'bugatti',
302
+ 'build',
303
+ 'builders',
304
+ 'business',
305
+ 'buy',
306
+ 'buzz',
307
+ 'bv',
308
+ 'bw',
309
+ 'by',
310
+ 'bz',
311
+ 'bzh',
312
+ 'ca',
313
+ 'cab',
314
+ 'cafe',
315
+ 'cal',
316
+ 'call',
317
+ 'calvinklein',
318
+ 'cam',
319
+ 'camera',
320
+ 'camp',
321
+ 'cancerresearch',
322
+ 'canon',
323
+ 'capetown',
324
+ 'capital',
325
+ 'capitalone',
326
+ 'car',
327
+ 'caravan',
328
+ 'cards',
329
+ 'care',
330
+ 'career',
331
+ 'careers',
332
+ 'cars',
333
+ 'cartier',
334
+ 'casa',
335
+ 'case',
336
+ 'caseih',
337
+ 'cash',
338
+ 'casino',
339
+ 'cat',
340
+ 'catering',
341
+ 'catholic',
342
+ 'cba',
343
+ 'cbn',
344
+ 'cbre',
345
+ 'cbs',
346
+ 'cc',
347
+ 'cd',
348
+ 'ceb',
349
+ 'center',
350
+ 'ceo',
351
+ 'cern',
352
+ 'cf',
353
+ 'cfa',
354
+ 'cfd',
355
+ 'cg',
356
+ 'ch',
357
+ 'chanel',
358
+ 'channel',
359
+ 'chase',
360
+ 'chat',
361
+ 'cheap',
362
+ 'chintai',
363
+ 'chloe',
364
+ 'christmas',
365
+ 'chrome',
366
+ 'chrysler',
367
+ 'church',
368
+ 'ci',
369
+ 'cipriani',
370
+ 'circle',
371
+ 'cisco',
372
+ 'citadel',
373
+ 'citi',
374
+ 'citic',
375
+ 'city',
376
+ 'cityeats',
377
+ 'ck',
378
+ 'cl',
379
+ 'claims',
380
+ 'cleaning',
381
+ 'click',
382
+ 'clinic',
383
+ 'clinique',
384
+ 'clothing',
385
+ 'cloud',
386
+ 'club',
387
+ 'clubmed',
388
+ 'cm',
389
+ 'cn',
390
+ 'co',
391
+ 'coach',
392
+ 'codes',
393
+ 'coffee',
394
+ 'college',
395
+ 'cologne',
396
+ 'com',
397
+ 'comcast',
398
+ 'commbank',
399
+ 'community',
400
+ 'company',
401
+ 'compare',
402
+ 'computer',
403
+ 'comsec',
404
+ 'condos',
405
+ 'construction',
406
+ 'consulting',
407
+ 'contact',
408
+ 'contractors',
409
+ 'cooking',
410
+ 'cookingchannel',
411
+ 'cool',
412
+ 'coop',
413
+ 'corsica',
414
+ 'country',
415
+ 'coupon',
416
+ 'coupons',
417
+ 'courses',
418
+ 'cr',
419
+ 'credit',
420
+ 'creditcard',
421
+ 'creditunion',
422
+ 'cricket',
423
+ 'crown',
424
+ 'crs',
425
+ 'cruise',
426
+ 'cruises',
427
+ 'csc',
428
+ 'cu',
429
+ 'cuisinella',
430
+ 'cv',
431
+ 'cw',
432
+ 'cx',
433
+ 'cy',
434
+ 'cymru',
435
+ 'cyou',
436
+ 'cz',
437
+ 'dabur',
438
+ 'dad',
439
+ 'dance',
440
+ 'data',
441
+ 'date',
442
+ 'dating',
443
+ 'datsun',
444
+ 'day',
445
+ 'dclk',
446
+ 'dds',
447
+ 'de',
448
+ 'deal',
449
+ 'dealer',
450
+ 'deals',
451
+ 'degree',
452
+ 'delivery',
453
+ 'dell',
454
+ 'deloitte',
455
+ 'delta',
456
+ 'democrat',
457
+ 'dental',
458
+ 'dentist',
459
+ 'desi',
460
+ 'design',
461
+ 'dev',
462
+ 'dhl',
463
+ 'diamonds',
464
+ 'diet',
465
+ 'digital',
466
+ 'direct',
467
+ 'directory',
468
+ 'discount',
469
+ 'discover',
470
+ 'dish',
471
+ 'diy',
472
+ 'dj',
473
+ 'dk',
474
+ 'dm',
475
+ 'dnp',
476
+ 'do',
477
+ 'docs',
478
+ 'doctor',
479
+ 'dodge',
480
+ 'dog',
481
+ 'doha',
482
+ 'domains',
483
+ 'dot',
484
+ 'download',
485
+ 'drive',
486
+ 'dtv',
487
+ 'dubai',
488
+ 'duck',
489
+ 'dunlop',
490
+ 'duns',
491
+ 'dupont',
492
+ 'durban',
493
+ 'dvag',
494
+ 'dvr',
495
+ 'dz',
496
+ 'earth',
497
+ 'eat',
498
+ 'ec',
499
+ 'eco',
500
+ 'edeka',
501
+ 'edu',
502
+ 'education',
503
+ 'ee',
504
+ 'eg',
505
+ 'email',
506
+ 'emerck',
507
+ 'energy',
508
+ 'engineer',
509
+ 'engineering',
510
+ 'enterprises',
511
+ 'epost',
512
+ 'epson',
513
+ 'equipment',
514
+ 'er',
515
+ 'ericsson',
516
+ 'erni',
517
+ 'es',
518
+ 'esq',
519
+ 'estate',
520
+ 'esurance',
521
+ 'et',
522
+ 'etisalat',
523
+ 'eu',
524
+ 'eurovision',
525
+ 'eus',
526
+ 'events',
527
+ 'everbank',
528
+ 'exchange',
529
+ 'expert',
530
+ 'exposed',
531
+ 'express',
532
+ 'extraspace',
533
+ 'fage',
534
+ 'fail',
535
+ 'fairwinds',
536
+ 'faith',
537
+ 'family',
538
+ 'fan',
539
+ 'fans',
540
+ 'farm',
541
+ 'farmers',
542
+ 'fashion',
543
+ 'fast',
544
+ 'fedex',
545
+ 'feedback',
546
+ 'ferrari',
547
+ 'ferrero',
548
+ 'fi',
549
+ 'fiat',
550
+ 'fidelity',
551
+ 'fido',
552
+ 'film',
553
+ 'final',
554
+ 'finance',
555
+ 'financial',
556
+ 'fire',
557
+ 'firestone',
558
+ 'firmdale',
559
+ 'fish',
560
+ 'fishing',
561
+ 'fit',
562
+ 'fitness',
563
+ 'fj',
564
+ 'fk',
565
+ 'flickr',
566
+ 'flights',
567
+ 'flir',
568
+ 'florist',
569
+ 'flowers',
570
+ 'fly',
571
+ 'fm',
572
+ 'fo',
573
+ 'foo',
574
+ 'food',
575
+ 'foodnetwork',
576
+ 'football',
577
+ 'ford',
578
+ 'forex',
579
+ 'forsale',
580
+ 'forum',
581
+ 'foundation',
582
+ 'fox',
583
+ 'fr',
584
+ 'free',
585
+ 'fresenius',
586
+ 'frl',
587
+ 'frogans',
588
+ 'frontdoor',
589
+ 'frontier',
590
+ 'ftr',
591
+ 'fujitsu',
592
+ 'fujixerox',
593
+ 'fun',
594
+ 'fund',
595
+ 'furniture',
596
+ 'futbol',
597
+ 'fyi',
598
+ 'ga',
599
+ 'gal',
600
+ 'gallery',
601
+ 'gallo',
602
+ 'gallup',
603
+ 'game',
604
+ 'games',
605
+ 'gap',
606
+ 'garden',
607
+ 'gb',
608
+ 'gbiz',
609
+ 'gd',
610
+ 'gdn',
611
+ 'ge',
612
+ 'gea',
613
+ 'gent',
614
+ 'genting',
615
+ 'george',
616
+ 'gf',
617
+ 'gg',
618
+ 'ggee',
619
+ 'gh',
620
+ 'gi',
621
+ 'gift',
622
+ 'gifts',
623
+ 'gives',
624
+ 'giving',
625
+ 'gl',
626
+ 'glade',
627
+ 'glass',
628
+ 'gle',
629
+ 'global',
630
+ 'globo',
631
+ 'gm',
632
+ 'gmail',
633
+ 'gmbh',
634
+ 'gmo',
635
+ 'gmx',
636
+ 'gn',
637
+ 'godaddy',
638
+ 'gold',
639
+ 'goldpoint',
640
+ 'golf',
641
+ 'goo',
642
+ 'goodhands',
643
+ 'goodyear',
644
+ 'goog',
645
+ 'google',
646
+ 'gop',
647
+ 'got',
648
+ 'gov',
649
+ 'gp',
650
+ 'gq',
651
+ 'gr',
652
+ 'grainger',
653
+ 'graphics',
654
+ 'gratis',
655
+ 'green',
656
+ 'gripe',
657
+ 'grocery',
658
+ 'group',
659
+ 'gs',
660
+ 'gt',
661
+ 'gu',
662
+ 'guardian',
663
+ 'gucci',
664
+ 'guge',
665
+ 'guide',
666
+ 'guitars',
667
+ 'guru',
668
+ 'gw',
669
+ 'gy',
670
+ 'hair',
671
+ 'hamburg',
672
+ 'hangout',
673
+ 'haus',
674
+ 'hbo',
675
+ 'hdfc',
676
+ 'hdfcbank',
677
+ 'health',
678
+ 'healthcare',
679
+ 'help',
680
+ 'helsinki',
681
+ 'here',
682
+ 'hermes',
683
+ 'hgtv',
684
+ 'hiphop',
685
+ 'hisamitsu',
686
+ 'hitachi',
687
+ 'hiv',
688
+ 'hk',
689
+ 'hkt',
690
+ 'hm',
691
+ 'hn',
692
+ 'hockey',
693
+ 'holdings',
694
+ 'holiday',
695
+ 'homedepot',
696
+ 'homegoods',
697
+ 'homes',
698
+ 'homesense',
699
+ 'honda',
700
+ 'honeywell',
701
+ 'horse',
702
+ 'hospital',
703
+ 'host',
704
+ 'hosting',
705
+ 'hot',
706
+ 'hoteles',
707
+ 'hotels',
708
+ 'hotmail',
709
+ 'house',
710
+ 'how',
711
+ 'hr',
712
+ 'hsbc',
713
+ 'ht',
714
+ 'htc',
715
+ 'hu',
716
+ 'hughes',
717
+ 'hyatt',
718
+ 'hyundai',
719
+ 'ibm',
720
+ 'icbc',
721
+ 'ice',
722
+ 'icu',
723
+ 'id',
724
+ 'ie',
725
+ 'ieee',
726
+ 'ifm',
727
+ 'ikano',
728
+ 'il',
729
+ 'im',
730
+ 'imamat',
731
+ 'imdb',
732
+ 'immo',
733
+ 'immobilien',
734
+ 'in',
735
+ 'industries',
736
+ 'infiniti',
737
+ 'info',
738
+ 'ing',
739
+ 'ink',
740
+ 'institute',
741
+ 'insurance',
742
+ 'insure',
743
+ 'int',
744
+ 'intel',
745
+ 'international',
746
+ 'intuit',
747
+ 'investments',
748
+ 'io',
749
+ 'ipiranga',
750
+ 'iq',
751
+ 'ir',
752
+ 'irish',
753
+ 'is',
754
+ 'iselect',
755
+ 'ismaili',
756
+ 'ist',
757
+ 'istanbul',
758
+ 'it',
759
+ 'itau',
760
+ 'itv',
761
+ 'iveco',
762
+ 'iwc',
763
+ 'jaguar',
764
+ 'java',
765
+ 'jcb',
766
+ 'jcp',
767
+ 'je',
768
+ 'jeep',
769
+ 'jetzt',
770
+ 'jewelry',
771
+ 'jio',
772
+ 'jlc',
773
+ 'jll',
774
+ 'jm',
775
+ 'jmp',
776
+ 'jnj',
777
+ 'jo',
778
+ 'jobs',
779
+ 'joburg',
780
+ 'jot',
781
+ 'joy',
782
+ 'jp',
783
+ 'jpmorgan',
784
+ 'jprs',
785
+ 'juegos',
786
+ 'juniper',
787
+ 'kaufen',
788
+ 'kddi',
789
+ 'ke',
790
+ 'kerryhotels',
791
+ 'kerrylogistics',
792
+ 'kerryproperties',
793
+ 'kfh',
794
+ 'kg',
795
+ 'kh',
796
+ 'ki',
797
+ 'kia',
798
+ 'kim',
799
+ 'kinder',
800
+ 'kindle',
801
+ 'kitchen',
802
+ 'kiwi',
803
+ 'km',
804
+ 'kn',
805
+ 'koeln',
806
+ 'komatsu',
807
+ 'kosher',
808
+ 'kp',
809
+ 'kpmg',
810
+ 'kpn',
811
+ 'kr',
812
+ 'krd',
813
+ 'kred',
814
+ 'kuokgroup',
815
+ 'kw',
816
+ 'ky',
817
+ 'kyoto',
818
+ 'kz',
819
+ 'la',
820
+ 'lacaixa',
821
+ 'ladbrokes',
822
+ 'lamborghini',
823
+ 'lamer',
824
+ 'lancaster',
825
+ 'lancia',
826
+ 'lancome',
827
+ 'land',
828
+ 'landrover',
829
+ 'lanxess',
830
+ 'lasalle',
831
+ 'lat',
832
+ 'latino',
833
+ 'latrobe',
834
+ 'law',
835
+ 'lawyer',
836
+ 'lb',
837
+ 'lc',
838
+ 'lds',
839
+ 'lease',
840
+ 'leclerc',
841
+ 'lefrak',
842
+ 'legal',
843
+ 'lego',
844
+ 'lexus',
845
+ 'lgbt',
846
+ 'li',
847
+ 'liaison',
848
+ 'lidl',
849
+ 'life',
850
+ 'lifeinsurance',
851
+ 'lifestyle',
852
+ 'lighting',
853
+ 'like',
854
+ 'lilly',
855
+ 'limited',
856
+ 'limo',
857
+ 'lincoln',
858
+ 'linde',
859
+ 'link',
860
+ 'lipsy',
861
+ 'live',
862
+ 'living',
863
+ 'lixil',
864
+ 'lk',
865
+ 'loan',
866
+ 'loans',
867
+ 'locker',
868
+ 'locus',
869
+ 'loft',
870
+ 'lol',
871
+ 'london',
872
+ 'lotte',
873
+ 'lotto',
874
+ 'love',
875
+ 'lpl',
876
+ 'lplfinancial',
877
+ 'lr',
878
+ 'ls',
879
+ 'lt',
880
+ 'ltd',
881
+ 'ltda',
882
+ 'lu',
883
+ 'lundbeck',
884
+ 'lupin',
885
+ 'luxe',
886
+ 'luxury',
887
+ 'lv',
888
+ 'ly',
889
+ 'ma',
890
+ 'macys',
891
+ 'madrid',
892
+ 'maif',
893
+ 'maison',
894
+ 'makeup',
895
+ 'man',
896
+ 'management',
897
+ 'mango',
898
+ 'map',
899
+ 'market',
900
+ 'marketing',
901
+ 'markets',
902
+ 'marriott',
903
+ 'marshalls',
904
+ 'maserati',
905
+ 'mattel',
906
+ 'mba',
907
+ 'mc',
908
+ 'mcd',
909
+ 'mcdonalds',
910
+ 'mckinsey',
911
+ 'md',
912
+ 'me',
913
+ 'med',
914
+ 'media',
915
+ 'meet',
916
+ 'melbourne',
917
+ 'meme',
918
+ 'memorial',
919
+ 'men',
920
+ 'menu',
921
+ 'meo',
922
+ 'merckmsd',
923
+ 'metlife',
924
+ 'mg',
925
+ 'mh',
926
+ 'miami',
927
+ 'microsoft',
928
+ 'mil',
929
+ 'mini',
930
+ 'mint',
931
+ 'mit',
932
+ 'mitsubishi',
933
+ 'mk',
934
+ 'ml',
935
+ 'mlb',
936
+ 'mls',
937
+ 'mm',
938
+ 'mma',
939
+ 'mn',
940
+ 'mo',
941
+ 'mobi',
942
+ 'mobile',
943
+ 'mobily',
944
+ 'moda',
945
+ 'moe',
946
+ 'moi',
947
+ 'mom',
948
+ 'monash',
949
+ 'money',
950
+ 'monster',
951
+ 'montblanc',
952
+ 'mopar',
953
+ 'mormon',
954
+ 'mortgage',
955
+ 'moscow',
956
+ 'moto',
957
+ 'motorcycles',
958
+ 'mov',
959
+ 'movie',
960
+ 'movistar',
961
+ 'mp',
962
+ 'mq',
963
+ 'mr',
964
+ 'ms',
965
+ 'msd',
966
+ 'mt',
967
+ 'mtn',
968
+ 'mtr',
969
+ 'mu',
970
+ 'museum',
971
+ 'mutual',
972
+ 'mv',
973
+ 'mw',
974
+ 'mx',
975
+ 'my',
976
+ 'mz',
977
+ 'na',
978
+ 'nab',
979
+ 'nadex',
980
+ 'nagoya',
981
+ 'name',
982
+ 'nationwide',
983
+ 'natura',
984
+ 'navy',
985
+ 'nba',
986
+ 'nc',
987
+ 'ne',
988
+ 'nec',
989
+ 'net',
990
+ 'netbank',
991
+ 'netflix',
992
+ 'network',
993
+ 'neustar',
994
+ 'new',
995
+ 'newholland',
996
+ 'news',
997
+ 'next',
998
+ 'nextdirect',
999
+ 'nexus',
1000
+ 'nf',
1001
+ 'nfl',
1002
+ 'ng',
1003
+ 'ngo',
1004
+ 'nhk',
1005
+ 'ni',
1006
+ 'nico',
1007
+ 'nike',
1008
+ 'nikon',
1009
+ 'ninja',
1010
+ 'nissan',
1011
+ 'nissay',
1012
+ 'nl',
1013
+ 'no',
1014
+ 'nokia',
1015
+ 'northwesternmutual',
1016
+ 'norton',
1017
+ 'now',
1018
+ 'nowruz',
1019
+ 'nowtv',
1020
+ 'np',
1021
+ 'nr',
1022
+ 'nra',
1023
+ 'nrw',
1024
+ 'ntt',
1025
+ 'nu',
1026
+ 'nyc',
1027
+ 'nz',
1028
+ 'obi',
1029
+ 'observer',
1030
+ 'off',
1031
+ 'office',
1032
+ 'okinawa',
1033
+ 'olayan',
1034
+ 'olayangroup',
1035
+ 'oldnavy',
1036
+ 'ollo',
1037
+ 'om',
1038
+ 'omega',
1039
+ 'one',
1040
+ 'ong',
1041
+ 'onl',
1042
+ 'online',
1043
+ 'onyourside',
1044
+ 'ooo',
1045
+ 'open',
1046
+ 'oracle',
1047
+ 'orange',
1048
+ 'org',
1049
+ 'organic',
1050
+ 'origins',
1051
+ 'osaka',
1052
+ 'otsuka',
1053
+ 'ott',
1054
+ 'ovh',
1055
+ 'pa',
1056
+ 'page',
1057
+ 'pamperedchef',
1058
+ 'panasonic',
1059
+ 'panerai',
1060
+ 'paris',
1061
+ 'pars',
1062
+ 'partners',
1063
+ 'parts',
1064
+ 'party',
1065
+ 'passagens',
1066
+ 'pay',
1067
+ 'pccw',
1068
+ 'pe',
1069
+ 'pet',
1070
+ 'pf',
1071
+ 'pfizer',
1072
+ 'pg',
1073
+ 'ph',
1074
+ 'pharmacy',
1075
+ 'phd',
1076
+ 'philips',
1077
+ 'phone',
1078
+ 'photo',
1079
+ 'photography',
1080
+ 'photos',
1081
+ 'physio',
1082
+ 'piaget',
1083
+ 'pics',
1084
+ 'pictet',
1085
+ 'pictures',
1086
+ 'pid',
1087
+ 'pin',
1088
+ 'ping',
1089
+ 'pink',
1090
+ 'pioneer',
1091
+ 'pizza',
1092
+ 'pk',
1093
+ 'pl',
1094
+ 'place',
1095
+ 'play',
1096
+ 'playstation',
1097
+ 'plumbing',
1098
+ 'plus',
1099
+ 'pm',
1100
+ 'pn',
1101
+ 'pnc',
1102
+ 'pohl',
1103
+ 'poker',
1104
+ 'politie',
1105
+ 'porn',
1106
+ 'post',
1107
+ 'pr',
1108
+ 'pramerica',
1109
+ 'praxi',
1110
+ 'press',
1111
+ 'prime',
1112
+ 'pro',
1113
+ 'prod',
1114
+ 'productions',
1115
+ 'prof',
1116
+ 'progressive',
1117
+ 'promo',
1118
+ 'properties',
1119
+ 'property',
1120
+ 'protection',
1121
+ 'pru',
1122
+ 'prudential',
1123
+ 'ps',
1124
+ 'pt',
1125
+ 'pub',
1126
+ 'pw',
1127
+ 'pwc',
1128
+ 'py',
1129
+ 'qa',
1130
+ 'qpon',
1131
+ 'quebec',
1132
+ 'quest',
1133
+ 'qvc',
1134
+ 'racing',
1135
+ 'radio',
1136
+ 'raid',
1137
+ 're',
1138
+ 'read',
1139
+ 'realestate',
1140
+ 'realtor',
1141
+ 'realty',
1142
+ 'recipes',
1143
+ 'red',
1144
+ 'redstone',
1145
+ 'redumbrella',
1146
+ 'rehab',
1147
+ 'reise',
1148
+ 'reisen',
1149
+ 'reit',
1150
+ 'reliance',
1151
+ 'ren',
1152
+ 'rent',
1153
+ 'rentals',
1154
+ 'repair',
1155
+ 'report',
1156
+ 'republican',
1157
+ 'rest',
1158
+ 'restaurant',
1159
+ 'review',
1160
+ 'reviews',
1161
+ 'rexroth',
1162
+ 'rich',
1163
+ 'richardli',
1164
+ 'ricoh',
1165
+ 'rightathome',
1166
+ 'ril',
1167
+ 'rio',
1168
+ 'rip',
1169
+ 'rmit',
1170
+ 'ro',
1171
+ 'rocher',
1172
+ 'rocks',
1173
+ 'rodeo',
1174
+ 'rogers',
1175
+ 'room',
1176
+ 'rs',
1177
+ 'rsvp',
1178
+ 'ru',
1179
+ 'rugby',
1180
+ 'ruhr',
1181
+ 'run',
1182
+ 'rw',
1183
+ 'rwe',
1184
+ 'ryukyu',
1185
+ 'sa',
1186
+ 'saarland',
1187
+ 'safe',
1188
+ 'safety',
1189
+ 'sakura',
1190
+ 'sale',
1191
+ 'salon',
1192
+ 'samsclub',
1193
+ 'samsung',
1194
+ 'sandvik',
1195
+ 'sandvikcoromant',
1196
+ 'sanofi',
1197
+ 'sap',
1198
+ 'sapo',
1199
+ 'sarl',
1200
+ 'sas',
1201
+ 'save',
1202
+ 'saxo',
1203
+ 'sb',
1204
+ 'sbi',
1205
+ 'sbs',
1206
+ 'sc',
1207
+ 'sca',
1208
+ 'scb',
1209
+ 'schaeffler',
1210
+ 'schmidt',
1211
+ 'scholarships',
1212
+ 'school',
1213
+ 'schule',
1214
+ 'schwarz',
1215
+ 'science',
1216
+ 'scjohnson',
1217
+ 'scor',
1218
+ 'scot',
1219
+ 'sd',
1220
+ 'se',
1221
+ 'search',
1222
+ 'seat',
1223
+ 'secure',
1224
+ 'security',
1225
+ 'seek',
1226
+ 'select',
1227
+ 'sener',
1228
+ 'services',
1229
+ 'ses',
1230
+ 'seven',
1231
+ 'sew',
1232
+ 'sex',
1233
+ 'sexy',
1234
+ 'sfr',
1235
+ 'sg',
1236
+ 'sh',
1237
+ 'shangrila',
1238
+ 'sharp',
1239
+ 'shaw',
1240
+ 'shell',
1241
+ 'shia',
1242
+ 'shiksha',
1243
+ 'shoes',
1244
+ 'shop',
1245
+ 'shopping',
1246
+ 'shouji',
1247
+ 'show',
1248
+ 'showtime',
1249
+ 'shriram',
1250
+ 'si',
1251
+ 'silk',
1252
+ 'sina',
1253
+ 'singles',
1254
+ 'site',
1255
+ 'sj',
1256
+ 'sk',
1257
+ 'ski',
1258
+ 'skin',
1259
+ 'sky',
1260
+ 'skype',
1261
+ 'sl',
1262
+ 'sling',
1263
+ 'sm',
1264
+ 'smart',
1265
+ 'smile',
1266
+ 'sn',
1267
+ 'sncf',
1268
+ 'so',
1269
+ 'soccer',
1270
+ 'social',
1271
+ 'softbank',
1272
+ 'software',
1273
+ 'sohu',
1274
+ 'solar',
1275
+ 'solutions',
1276
+ 'song',
1277
+ 'sony',
1278
+ 'soy',
1279
+ 'space',
1280
+ 'spiegel',
1281
+ 'spot',
1282
+ 'spreadbetting',
1283
+ 'sr',
1284
+ 'srl',
1285
+ 'srt',
1286
+ 'st',
1287
+ 'stada',
1288
+ 'staples',
1289
+ 'star',
1290
+ 'starhub',
1291
+ 'statebank',
1292
+ 'statefarm',
1293
+ 'statoil',
1294
+ 'stc',
1295
+ 'stcgroup',
1296
+ 'stockholm',
1297
+ 'storage',
1298
+ 'store',
1299
+ 'stream',
1300
+ 'studio',
1301
+ 'study',
1302
+ 'style',
1303
+ 'su',
1304
+ 'sucks',
1305
+ 'supplies',
1306
+ 'supply',
1307
+ 'support',
1308
+ 'surf',
1309
+ 'surgery',
1310
+ 'suzuki',
1311
+ 'sv',
1312
+ 'swatch',
1313
+ 'swiftcover',
1314
+ 'swiss',
1315
+ 'sx',
1316
+ 'sy',
1317
+ 'sydney',
1318
+ 'symantec',
1319
+ 'systems',
1320
+ 'sz',
1321
+ 'tab',
1322
+ 'taipei',
1323
+ 'talk',
1324
+ 'taobao',
1325
+ 'target',
1326
+ 'tatamotors',
1327
+ 'tatar',
1328
+ 'tattoo',
1329
+ 'tax',
1330
+ 'taxi',
1331
+ 'tc',
1332
+ 'tci',
1333
+ 'td',
1334
+ 'tdk',
1335
+ 'team',
1336
+ 'tech',
1337
+ 'technology',
1338
+ 'tel',
1339
+ 'telecity',
1340
+ 'telefonica',
1341
+ 'temasek',
1342
+ 'tennis',
1343
+ 'teva',
1344
+ 'tf',
1345
+ 'tg',
1346
+ 'th',
1347
+ 'thd',
1348
+ 'theater',
1349
+ 'theatre',
1350
+ 'tiaa',
1351
+ 'tickets',
1352
+ 'tienda',
1353
+ 'tiffany',
1354
+ 'tips',
1355
+ 'tires',
1356
+ 'tirol',
1357
+ 'tj',
1358
+ 'tjmaxx',
1359
+ 'tjx',
1360
+ 'tk',
1361
+ 'tkmaxx',
1362
+ 'tl',
1363
+ 'tm',
1364
+ 'tmall',
1365
+ 'tn',
1366
+ 'to',
1367
+ 'today',
1368
+ 'tokyo',
1369
+ 'tools',
1370
+ 'top',
1371
+ 'toray',
1372
+ 'toshiba',
1373
+ 'total',
1374
+ 'tours',
1375
+ 'town',
1376
+ 'toyota',
1377
+ 'toys',
1378
+ 'tr',
1379
+ 'trade',
1380
+ 'trading',
1381
+ 'training',
1382
+ 'travel',
1383
+ 'travelchannel',
1384
+ 'travelers',
1385
+ 'travelersinsurance',
1386
+ 'trust',
1387
+ 'trv',
1388
+ 'tt',
1389
+ 'tube',
1390
+ 'tui',
1391
+ 'tunes',
1392
+ 'tushu',
1393
+ 'tv',
1394
+ 'tvs',
1395
+ 'tw',
1396
+ 'tz',
1397
+ 'ua',
1398
+ 'ubank',
1399
+ 'ubs',
1400
+ 'uconnect',
1401
+ 'ug',
1402
+ 'uk',
1403
+ 'unicom',
1404
+ 'university',
1405
+ 'uno',
1406
+ 'uol',
1407
+ 'ups',
1408
+ 'us',
1409
+ 'uy',
1410
+ 'uz',
1411
+ 'va',
1412
+ 'vacations',
1413
+ 'vana',
1414
+ 'vanguard',
1415
+ 'vc',
1416
+ 've',
1417
+ 'vegas',
1418
+ 'ventures',
1419
+ 'verisign',
1420
+ 'versicherung',
1421
+ 'vet',
1422
+ 'vg',
1423
+ 'vi',
1424
+ 'viajes',
1425
+ 'video',
1426
+ 'vig',
1427
+ 'viking',
1428
+ 'villas',
1429
+ 'vin',
1430
+ 'vip',
1431
+ 'virgin',
1432
+ 'visa',
1433
+ 'vision',
1434
+ 'vista',
1435
+ 'vistaprint',
1436
+ 'viva',
1437
+ 'vivo',
1438
+ 'vlaanderen',
1439
+ 'vn',
1440
+ 'vodka',
1441
+ 'volkswagen',
1442
+ 'volvo',
1443
+ 'vote',
1444
+ 'voting',
1445
+ 'voto',
1446
+ 'voyage',
1447
+ 'vu',
1448
+ 'vuelos',
1449
+ 'wales',
1450
+ 'walmart',
1451
+ 'walter',
1452
+ 'wang',
1453
+ 'wanggou',
1454
+ 'warman',
1455
+ 'watch',
1456
+ 'watches',
1457
+ 'weather',
1458
+ 'weatherchannel',
1459
+ 'webcam',
1460
+ 'weber',
1461
+ 'website',
1462
+ 'wed',
1463
+ 'wedding',
1464
+ 'weibo',
1465
+ 'weir',
1466
+ 'wf',
1467
+ 'whoswho',
1468
+ 'wien',
1469
+ 'wiki',
1470
+ 'williamhill',
1471
+ 'win',
1472
+ 'windows',
1473
+ 'wine',
1474
+ 'winners',
1475
+ 'wme',
1476
+ 'wolterskluwer',
1477
+ 'woodside',
1478
+ 'work',
1479
+ 'works',
1480
+ 'world',
1481
+ 'wow',
1482
+ 'ws',
1483
+ 'wtc',
1484
+ 'wtf',
1485
+ 'xbox',
1486
+ 'xerox',
1487
+ 'xfinity',
1488
+ 'xihuan',
1489
+ 'xin',
1490
+ 'कॉम',
1491
+ 'セール',
1492
+ '佛山',
1493
+ 'ಭಾರತ',
1494
+ '慈善',
1495
+ '集团',
1496
+ '在线',
1497
+ '한국',
1498
+ 'ଭାରତ',
1499
+ '大众汽车',
1500
+ '点看',
1501
+ 'คอม',
1502
+ 'ভাৰত',
1503
+ 'ভারত',
1504
+ '八卦',
1505
+ 'موقع',
1506
+ 'বাংলা',
1507
+ '公益',
1508
+ '公司',
1509
+ '香格里拉',
1510
+ '网站',
1511
+ '移动',
1512
+ '我爱你',
1513
+ 'москва',
1514
+ 'қаз',
1515
+ 'католик',
1516
+ 'онлайн',
1517
+ 'сайт',
1518
+ '联通',
1519
+ 'срб',
1520
+ 'бг',
1521
+ 'бел',
1522
+ 'קום',
1523
+ '时尚',
1524
+ '微博',
1525
+ '淡马锡',
1526
+ 'ファッション',
1527
+ 'орг',
1528
+ 'नेट',
1529
+ 'ストア',
1530
+ '삼성',
1531
+ 'சிங்கப்பூர்',
1532
+ '商标',
1533
+ '商店',
1534
+ '商城',
1535
+ 'дети',
1536
+ 'мкд',
1537
+ 'ею',
1538
+ 'ポイント',
1539
+ '新闻',
1540
+ '工行',
1541
+ '家電',
1542
+ 'كوم',
1543
+ '中文网',
1544
+ '中信',
1545
+ '中国',
1546
+ '中國',
1547
+ '娱乐',
1548
+ '谷歌',
1549
+ 'భారత్',
1550
+ 'ලංකා',
1551
+ '電訊盈科',
1552
+ '购物',
1553
+ 'クラウド',
1554
+ 'ભારત',
1555
+ '通販',
1556
+ 'भारतम्',
1557
+ 'भारत',
1558
+ 'भारोत',
1559
+ '网店',
1560
+ 'संगठन',
1561
+ '餐厅',
1562
+ '网络',
1563
+ 'ком',
1564
+ 'укр',
1565
+ '香港',
1566
+ '诺基亚',
1567
+ '食品',
1568
+ '飞利浦',
1569
+ '台湾',
1570
+ '台灣',
1571
+ '手表',
1572
+ '手机',
1573
+ 'мон',
1574
+ 'الجزائر',
1575
+ 'عمان',
1576
+ 'ارامكو',
1577
+ 'ایران',
1578
+ 'العليان',
1579
+ 'اتصالات',
1580
+ 'امارات',
1581
+ 'بازار',
1582
+ 'پاکستان',
1583
+ 'الاردن',
1584
+ 'موبايلي',
1585
+ 'بارت',
1586
+ 'بھارت',
1587
+ 'المغرب',
1588
+ 'ابوظبي',
1589
+ 'السعودية',
1590
+ 'ڀارت',
1591
+ 'كاثوليك',
1592
+ 'سودان',
1593
+ 'همراه',
1594
+ 'عراق',
1595
+ 'مليسيا',
1596
+ '澳門',
1597
+ '닷컴',
1598
+ '政府',
1599
+ 'شبكة',
1600
+ 'بيتك',
1601
+ 'عرب',
1602
+ 'გე',
1603
+ '机构',
1604
+ '组织机构',
1605
+ '健康',
1606
+ 'ไทย',
1607
+ 'سورية',
1608
+ 'рус',
1609
+ 'рф',
1610
+ '珠宝',
1611
+ 'تونس',
1612
+ '大拿',
1613
+ 'みんな',
1614
+ 'グーグル',
1615
+ 'ελ',
1616
+ '世界',
1617
+ '書籍',
1618
+ 'ഭാരതം',
1619
+ 'ਭਾਰਤ',
1620
+ '网址',
1621
+ '닷넷',
1622
+ 'コム',
1623
+ '天主教',
1624
+ '游戏',
1625
+ 'vermögensberater',
1626
+ 'vermögensberatung',
1627
+ '企业',
1628
+ '信息',
1629
+ '嘉里大酒店',
1630
+ '嘉里',
1631
+ 'مصر',
1632
+ 'قطر',
1633
+ '广东',
1634
+ 'இலங்கை',
1635
+ 'இந்தியா',
1636
+ 'հայ',
1637
+ '新加坡',
1638
+ 'فلسطين',
1639
+ '政务',
1640
+ 'xperia',
1641
+ 'xxx',
1642
+ 'xyz',
1643
+ 'yachts',
1644
+ 'yahoo',
1645
+ 'yamaxun',
1646
+ 'yandex',
1647
+ 'ye',
1648
+ 'yodobashi',
1649
+ 'yoga',
1650
+ 'yokohama',
1651
+ 'you',
1652
+ 'youtube',
1653
+ 'yt',
1654
+ 'yun',
1655
+ 'za',
1656
+ 'zappos',
1657
+ 'zara',
1658
+ 'zero',
1659
+ 'zip',
1660
+ 'zippo',
1661
+ 'zm',
1662
+ 'zone',
1663
+ 'zuerich',
1664
+ 'zw',
1665
+ );
1666
+
1667
+ /**
1668
+ * @var string
1669
+ */
1670
+ protected $_tld;
1671
+
1672
+ /**
1673
+ * Array for valid Idns
1674
+ *
1675
+ * @see http://www.iana.org/domains/idn-tables/ Official list of supported IDN Chars
1676
+ * (.AC) Ascension Island http://www.nic.ac/pdf/AC-IDN-Policy.pdf
1677
+ * (.AR) Argentinia http://www.nic.ar/faqidn.html
1678
+ * (.AS) American Samoa http://www.nic.as/idn/chars.cfm
1679
+ * (.AT) Austria http://www.nic.at/en/service/technical_information/idn/charset_converter/
1680
+ * (.BIZ) International http://www.iana.org/domains/idn-tables/
1681
+ * (.BR) Brazil http://registro.br/faq/faq6.html
1682
+ * (.BV) Bouvett Island http://www.norid.no/domeneregistrering/idn/idn_nyetegn.en.html
1683
+ * (.CA) Canada http://www.iana.org/domains/idn-tables/tables/ca_fr_1.0.html
1684
+ * (.CAT) Catalan http://www.iana.org/domains/idn-tables/tables/cat_ca_1.0.html
1685
+ * (.CH) Switzerland https://nic.switch.ch/reg/ocView.action?res=EF6GW2JBPVTG67DLNIQXU234MN6SC33JNQQGI7L6#anhang1
1686
+ * (.CL) Chile http://www.iana.org/domains/idn-tables/tables/cl_latn_1.0.html
1687
+ * (.COM) International http://www.verisign.com/information-services/naming-services/internationalized-domain-names/index.html
1688
+ * (.DE) Germany http://www.denic.de/en/domains/idns/liste.html
1689
+ * (.DK) Danmark http://www.dk-hostmaster.dk/index.php?id=151
1690
+ * (.ES) Spain https://www.nic.es/media/2008-05/1210147705287.pdf
1691
+ * (.FI) Finland http://www.ficora.fi/en/index/palvelut/fiverkkotunnukset/aakkostenkaytto.html
1692
+ * (.GR) Greece https://grweb.ics.forth.gr/CharacterTable1_en.jsp
1693
+ * (.HU) Hungary http://www.domain.hu/domain/English/szabalyzat/szabalyzat.html
1694
+ * (.INFO) International http://www.nic.info/info/idn
1695
+ * (.IO) British Indian Ocean Territory http://www.nic.io/IO-IDN-Policy.pdf
1696
+ * (.IR) Iran http://www.nic.ir/Allowable_Characters_dot-iran
1697
+ * (.IS) Iceland http://www.isnic.is/domain/rules.php
1698
+ * (.KR) Korea http://www.iana.org/domains/idn-tables/tables/kr_ko-kr_1.0.html
1699
+ * (.LI) Liechtenstein https://nic.switch.ch/reg/ocView.action?res=EF6GW2JBPVTG67DLNIQXU234MN6SC33JNQQGI7L6#anhang1
1700
+ * (.LT) Lithuania http://www.domreg.lt/static/doc/public/idn_symbols-en.pdf
1701
+ * (.MD) Moldova http://www.register.md/
1702
+ * (.MUSEUM) International http://www.iana.org/domains/idn-tables/tables/museum_latn_1.0.html
1703
+ * (.NET) International http://www.verisign.com/information-services/naming-services/internationalized-domain-names/index.html
1704
+ * (.NO) Norway http://www.norid.no/domeneregistrering/idn/idn_nyetegn.en.html
1705
+ * (.NU) Niue http://www.worldnames.net/
1706
+ * (.ORG) International http://www.pir.org/index.php?db=content/FAQs&tbl=FAQs_Registrant&id=2
1707
+ * (.PE) Peru https://www.nic.pe/nuevas_politicas_faq_2.php
1708
+ * (.PL) Poland http://www.dns.pl/IDN/allowed_character_sets.pdf
1709
+ * (.PR) Puerto Rico http://www.nic.pr/idn_rules.asp
1710
+ * (.PT) Portugal https://online.dns.pt/dns_2008/do?com=DS;8216320233;111;+PAGE(4000058)+K-CAT-CODIGO(C.125)+RCNT(100);
1711
+ * (.RU) Russia http://www.iana.org/domains/idn-tables/tables/ru_ru-ru_1.0.html
1712
+ * (.RS) Serbia http://www.iana.org/domains/idn-tables/tables/rs_sr-rs_1.0.pdf
1713
+ * (.SA) Saudi Arabia http://www.iana.org/domains/idn-tables/tables/sa_ar_1.0.html
1714
+ * (.SE) Sweden http://www.iis.se/english/IDN_campaignsite.shtml?lang=en
1715
+ * (.SH) Saint Helena http://www.nic.sh/SH-IDN-Policy.pdf
1716
+ * (.SJ) Svalbard and Jan Mayen http://www.norid.no/domeneregistrering/idn/idn_nyetegn.en.html
1717
+ * (.TH) Thailand http://www.iana.org/domains/idn-tables/tables/th_th-th_1.0.html
1718
+ * (.TM) Turkmenistan http://www.nic.tm/TM-IDN-Policy.pdf
1719
+ * (.TR) Turkey https://www.nic.tr/index.php
1720
+ * (.UA) Ukraine http://www.iana.org/domains/idn-tables/tables/ua_cyrl_1.2.html
1721
+ * (.VE) Venice http://www.iana.org/domains/idn-tables/tables/ve_es_1.0.html
1722
+ * (.VN) Vietnam http://www.vnnic.vn/english/5-6-300-2-2-04-20071115.htm#1.%20Introduction
1723
+ *
1724
+ * @var array
1725
+ */
1726
+ protected $_validIdns = array(
1727
+ 'AC' => array( 1 => '/^[\x{002d}0-9a-zà-öø-ÿāăąćĉċčďđēėęěĝġģĥħīįĵķĺļľŀłńņňŋőœŕŗřśŝşšţťŧūŭůűųŵŷźżž]{1,63}$/iu' ),
1728
+ 'AR' => array( 1 => '/^[\x{002d}0-9a-zà-ãç-êìíñ-õü]{1,63}$/iu' ),
1729
+ 'AS' => array( 1 => '/^[\x{002d}0-9a-zà-öø-ÿāăąćĉċčďđēĕėęěĝğġģĥħĩīĭįıĵķĸĺļľłńņňŋōŏőœŕŗřśŝşšţťŧũūŭůűųŵŷźż]{1,63}$/iu' ),
1730
+ 'AT' => array( 1 => '/^[\x{002d}0-9a-zà-öø-ÿœšž]{1,63}$/iu' ),
1731
+ 'BIZ' => 'Hostname/Biz.php',
1732
+ 'BR' => array( 1 => '/^[\x{002d}0-9a-zà-ãçéíó-õúü]{1,63}$/iu' ),
1733
+ 'BV' => array( 1 => '/^[\x{002d}0-9a-zàáä-éêñ-ôöøüčđńŋšŧž]{1,63}$/iu' ),
1734
+ 'CA' => array( 1 => '/^[\x{002d}0-9a-zàâæçéèêëîïôœùûüÿ\x{00E0}\x{00E2}\x{00E7}\x{00E8}\x{00E9}\x{00EA}\x{00EB}\x{00EE}\x{00EF}\x{00F4}\x{00F9}\x{00FB}\x{00FC}\x{00E6}\x{0153}\x{00FF}]{1,63}$/iu' ),
1735
+ 'CAT' => array( 1 => '/^[\x{002d}0-9a-z·àç-éíïòóúü]{1,63}$/iu' ),
1736
+ 'CH' => array( 1 => '/^[\x{002d}0-9a-zà-öø-ÿœ]{1,63}$/iu' ),
1737
+ 'CL' => array( 1 => '/^[\x{002d}0-9a-záéíñóúü]{1,63}$/iu' ),
1738
+ 'CN' => 'Hostname/Cn.php',
1739
+ 'COM' => 'Hostname/Com.php',
1740
+ 'DE' => array( 1 => '/^[\x{002d}0-9a-zà-öø-ÿăąāćĉčċďđĕěėęēğĝġģĥħĭĩįīıĵķĺľļłńňņŋŏőōœĸŕřŗśŝšşťţŧŭůűũųūŵŷźžż]{1,63}$/iu' ),
1741
+ 'DK' => array( 1 => '/^[\x{002d}0-9a-zäéöüæøå]{1,63}$/iu' ),
1742
+ 'ES' => array( 1 => '/^[\x{002d}0-9a-zàáçèéíïñòóúü·]{1,63}$/iu' ),
1743
+ 'EU' => array(
1744
+ 1 => '/^[\x{002d}0-9a-zà-öø-ÿ]{1,63}$/iu',
1745
+ 2 => '/^[\x{002d}0-9a-zāăąćĉċčďđēĕėęěĝğġģĥħĩīĭįıĵķĺļľŀłńņňʼnŋōŏőœŕŗřśŝšťŧũūŭůűųŵŷźżž]{1,63}$/iu',
1746
+ 3 => '/^[\x{002d}0-9a-zșț]{1,63}$/iu',
1747
+ 4 => '/^[\x{002d}0-9a-zΐάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύώ]{1,63}$/iu',
1748
+ 5 => '/^[\x{002d}0-9a-zабвгдежзийклмнопрстуфхцчшщъыьэюя]{1,63}$/iu',
1749
+ 6 => '/^[\x{002d}0-9a-zἀ-ἇἐ-ἕἠ-ἧἰ-ἷὀ-ὅὐ-ὗὠ-ὧὰ-ὼώᾀ-ᾇᾐ-ᾗᾠ-ᾧᾰ-ᾴᾶᾷῂῃῄῆῇῐ-ῒΐῖῗῠ-ῧῲῳῴῶῷ]{1,63}$/iu',
1750
+ ),
1751
+ 'FI' => array( 1 => '/^[\x{002d}0-9a-zäåö]{1,63}$/iu' ),
1752
+ 'GR' => array( 1 => '/^[\x{002d}0-9a-zΆΈΉΊΌΎ-ΡΣ-ώἀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙὛὝὟ-ώᾀ-ᾴᾶ-ᾼῂῃῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲῳῴῶ-ῼ]{1,63}$/iu' ),
1753
+ 'HK' => 'Hostname/Cn.php',
1754
+ 'HU' => array( 1 => '/^[\x{002d}0-9a-záéíóöúüőű]{1,63}$/iu' ),
1755
+ 'IL' => array(
1756
+ 1 => '/^[\x{002d}0-9\x{05D0}-\x{05EA}]{1,63}$/iu',
1757
+ 2 => '/^[\x{002d}0-9a-z]{1,63}$/i',
1758
+ ),
1759
+ 'INFO' => array(
1760
+ 1 => '/^[\x{002d}0-9a-zäåæéöøü]{1,63}$/iu',
1761
+ 2 => '/^[\x{002d}0-9a-záéíóöúüőű]{1,63}$/iu',
1762
+ 3 => '/^[\x{002d}0-9a-záæéíðóöúýþ]{1,63}$/iu',
1763
+ 4 => '/^[\x{AC00}-\x{D7A3}]{1,17}$/iu',
1764
+ 5 => '/^[\x{002d}0-9a-zāčēģīķļņōŗšūž]{1,63}$/iu',
1765
+ 6 => '/^[\x{002d}0-9a-ząčėęįšūųž]{1,63}$/iu',
1766
+ 7 => '/^[\x{002d}0-9a-zóąćęłńśźż]{1,63}$/iu',
1767
+ 8 => '/^[\x{002d}0-9a-záéíñóúü]{1,63}$/iu',
1768
+ ),
1769
+ 'IO' => array( 1 => '/^[\x{002d}0-9a-zà-öø-ÿăąāćĉčċďđĕěėęēğĝġģĥħĭĩįīıĵķĺľļłńňņŋŏőōœĸŕřŗśŝšşťţŧŭůűũųūŵŷźžż]{1,63}$/iu' ),
1770
+ 'IS' => array( 1 => '/^[\x{002d}0-9a-záéýúíóþæöð]{1,63}$/iu' ),
1771
+ 'IT' => array( 1 => '/^[\x{002d}0-9a-zàâäèéêëìîïòôöùûüæœçÿß-]{1,63}$/iu' ),
1772
+ 'JP' => 'Hostname/Jp.php',
1773
+ 'KR' => array( 1 => '/^[\x{AC00}-\x{D7A3}]{1,17}$/iu' ),
1774
+ 'LI' => array( 1 => '/^[\x{002d}0-9a-zà-öø-ÿœ]{1,63}$/iu' ),
1775
+ 'LT' => array( 1 => '/^[\x{002d}0-9ąčęėįšųūž]{1,63}$/iu' ),
1776
+ 'MD' => array( 1 => '/^[\x{002d}0-9ăâîşţ]{1,63}$/iu' ),
1777
+ 'MUSEUM' => array( 1 => '/^[\x{002d}0-9a-zà-öø-ÿāăąćċčďđēėęěğġģħīįıķĺļľłńņňŋōőœŕŗřśşšţťŧūůűųŵŷźżžǎǐǒǔ\x{01E5}\x{01E7}\x{01E9}\x{01EF}ə\x{0292}ẁẃẅỳ]{1,63}$/iu' ),
1778
+ 'NET' => 'Hostname/Com.php',
1779
+ 'NO' => array( 1 => '/^[\x{002d}0-9a-zàáä-éêñ-ôöøüčđńŋšŧž]{1,63}$/iu' ),
1780
+ 'NU' => 'Hostname/Com.php',
1781
+ 'ORG' => array(
1782
+ 1 => '/^[\x{002d}0-9a-záéíñóúü]{1,63}$/iu',
1783
+ 2 => '/^[\x{002d}0-9a-zóąćęłńśźż]{1,63}$/iu',
1784
+ 3 => '/^[\x{002d}0-9a-záäåæéëíðóöøúüýþ]{1,63}$/iu',
1785
+ 4 => '/^[\x{002d}0-9a-záéíóöúüőű]{1,63}$/iu',
1786
+ 5 => '/^[\x{002d}0-9a-ząčėęįšūųž]{1,63}$/iu',
1787
+ 6 => '/^[\x{AC00}-\x{D7A3}]{1,17}$/iu',
1788
+ 7 => '/^[\x{002d}0-9a-zāčēģīķļņōŗšūž]{1,63}$/iu',
1789
+ ),
1790
+ 'PE' => array( 1 => '/^[\x{002d}0-9a-zñáéíóúü]{1,63}$/iu' ),
1791
+ 'PL' => array(
1792
+ 1 => '/^[\x{002d}0-9a-zāčēģīķļņōŗšūž]{1,63}$/iu',
1793
+ 2 => '/^[\x{002d}а-ик-ш\x{0450}ѓѕјљњќџ]{1,63}$/iu',
1794
+ 3 => '/^[\x{002d}0-9a-zâîăşţ]{1,63}$/iu',
1795
+ 4 => '/^[\x{002d}0-9а-яё\x{04C2}]{1,63}$/iu',
1796
+ 5 => '/^[\x{002d}0-9a-zàáâèéêìíîòóôùúûċġħż]{1,63}$/iu',
1797
+ 6 => '/^[\x{002d}0-9a-zàäåæéêòóôöøü]{1,63}$/iu',
1798
+ 7 => '/^[\x{002d}0-9a-zóąćęłńśźż]{1,63}$/iu',
1799
+ 8 => '/^[\x{002d}0-9a-zàáâãçéêíòóôõúü]{1,63}$/iu',
1800
+ 9 => '/^[\x{002d}0-9a-zâîăşţ]{1,63}$/iu',
1801
+ 10 => '/^[\x{002d}0-9a-záäéíóôúýčďĺľňŕšťž]{1,63}$/iu',
1802
+ 11 => '/^[\x{002d}0-9a-zçë]{1,63}$/iu',
1803
+ 12 => '/^[\x{002d}0-9а-ик-шђјљњћџ]{1,63}$/iu',
1804
+ 13 => '/^[\x{002d}0-9a-zćčđšž]{1,63}$/iu',
1805
+ 14 => '/^[\x{002d}0-9a-zâçöûüğış]{1,63}$/iu',
1806
+ 15 => '/^[\x{002d}0-9a-záéíñóúü]{1,63}$/iu',
1807
+ 16 => '/^[\x{002d}0-9a-zäõöüšž]{1,63}$/iu',
1808
+ 17 => '/^[\x{002d}0-9a-zĉĝĥĵŝŭ]{1,63}$/iu',
1809
+ 18 => '/^[\x{002d}0-9a-zâäéëîô]{1,63}$/iu',
1810
+ 19 => '/^[\x{002d}0-9a-zàáâäåæçèéêëìíîïðñòôöøùúûüýćčłńřśš]{1,63}$/iu',
1811
+ 20 => '/^[\x{002d}0-9a-zäåæõöøüšž]{1,63}$/iu',
1812
+ 21 => '/^[\x{002d}0-9a-zàáçèéìíòóùú]{1,63}$/iu',
1813
+ 22 => '/^[\x{002d}0-9a-zàáéíóöúüőű]{1,63}$/iu',
1814
+ 23 => '/^[\x{002d}0-9ΐά-ώ]{1,63}$/iu',
1815
+ 24 => '/^[\x{002d}0-9a-zàáâåæçèéêëðóôöøüþœ]{1,63}$/iu',
1816
+ 25 => '/^[\x{002d}0-9a-záäéíóöúüýčďěňřšťůž]{1,63}$/iu',
1817
+ 26 => '/^[\x{002d}0-9a-z·àçèéíïòóúü]{1,63}$/iu',
1818
+ 27 => '/^[\x{002d}0-9а-ъьюя\x{0450}\x{045D}]{1,63}$/iu',
1819
+ 28 => '/^[\x{002d}0-9а-яёіў]{1,63}$/iu',
1820
+ 29 => '/^[\x{002d}0-9a-ząčėęįšūųž]{1,63}$/iu',
1821
+ 30 => '/^[\x{002d}0-9a-záäåæéëíðóöøúüýþ]{1,63}$/iu',
1822
+ 31 => '/^[\x{002d}0-9a-zàâæçèéêëîïñôùûüÿœ]{1,63}$/iu',
1823
+ 32 => '/^[\x{002d}0-9а-щъыьэюяёєіїґ]{1,63}$/iu',
1824
+ 33 => '/^[\x{002d}0-9א-ת]{1,63}$/iu',
1825
+ ),
1826
+ 'PR' => array( 1 => '/^[\x{002d}0-9a-záéíóúñäëïüöâêîôûàèùæçœãõ]{1,63}$/iu' ),
1827
+ 'PT' => array( 1 => '/^[\x{002d}0-9a-záàâãçéêíóôõú]{1,63}$/iu' ),
1828
+ 'RS' => array( 1 => '/^[\x{002D}\x{0030}-\x{0039}\x{0061}-\x{007A}\x{0107}\x{010D}\x{0111}\x{0161}\x{017E}]{1,63}$/iu)' ),
1829
+ 'RU' => array( 1 => '/^[\x{002d}0-9а-яё]{1,63}$/iu' ),
1830
+ 'SA' => array( 1 => '/^[\x{002d}.0-9\x{0621}-\x{063A}\x{0641}-\x{064A}\x{0660}-\x{0669}]{1,63}$/iu' ),
1831
+ 'SE' => array( 1 => '/^[\x{002d}0-9a-zäåéöü]{1,63}$/iu' ),
1832
+ 'SH' => array( 1 => '/^[\x{002d}0-9a-zà-öø-ÿăąāćĉčċďđĕěėęēğĝġģĥħĭĩįīıĵķĺľļłńňņŋŏőōœĸŕřŗśŝšşťţŧŭůűũųūŵŷźžż]{1,63}$/iu' ),
1833
+ 'SI' => array(
1834
+ 1 => '/^[\x{002d}0-9a-zà-öø-ÿ]{1,63}$/iu',
1835
+ 2 => '/^[\x{002d}0-9a-zāăąćĉċčďđēĕėęěĝğġģĥħĩīĭįıĵķĺļľŀłńņňʼnŋōŏőœŕŗřśŝšťŧũūŭůűųŵŷźżž]{1,63}$/iu',
1836
+ 3 => '/^[\x{002d}0-9a-zșț]{1,63}$/iu',
1837
+ ),
1838
+ 'SJ' => array( 1 => '/^[\x{002d}0-9a-zàáä-éêñ-ôöøüčđńŋšŧž]{1,63}$/iu' ),
1839
+ 'TH' => array( 1 => '/^[\x{002d}0-9a-z\x{0E01}-\x{0E3A}\x{0E40}-\x{0E4D}\x{0E50}-\x{0E59}]{1,63}$/iu' ),
1840
+ 'TM' => array( 1 => '/^[\x{002d}0-9a-zà-öø-ÿāăąćĉċčďđēėęěĝġģĥħīįĵķĺļľŀłńņňŋőœŕŗřśŝşšţťŧūŭůűųŵŷźżž]{1,63}$/iu' ),
1841
+ 'TW' => 'Hostname/Cn.php',
1842
+ 'TR' => array( 1 => '/^[\x{002d}0-9a-zğıüşöç]{1,63}$/iu' ),
1843
+ 'UA' => array( 1 => '/^[\x{002d}0-9a-zабвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓєѕіїјљњћќѝўџґӂʼ]{1,63}$/iu' ),
1844
+ 'VE' => array( 1 => '/^[\x{002d}0-9a-záéíóúüñ]{1,63}$/iu' ),
1845
+ 'VN' => array( 1 => '/^[ÀÁÂÃÈÉÊÌÍÒÓÔÕÙÚÝàáâãèéêìíòóôõùúýĂăĐđĨĩŨũƠơƯư\x{1EA0}-\x{1EF9}]{1,63}$/iu' ),
1846
+ 'мон' => array( 1 => '/^[\x{002d}0-9\x{0430}-\x{044F}]{1,63}$/iu' ),
1847
+ 'срб' => array( 1 => '/^[\x{002d}0-9а-ик-шђјљњћџ]{1,63}$/iu' ),
1848
+ 'сайт' => array( 1 => '/^[\x{002d}0-9а-яёіїѝйўґг]{1,63}$/iu' ),
1849
+ 'онлайн' => array( 1 => '/^[\x{002d}0-9а-яёіїѝйўґг]{1,63}$/iu' ),
1850
+ '中国' => 'Hostname/Cn.php',
1851
+ '中國' => 'Hostname/Cn.php',
1852
+ 'ලංකා' => array( 1 => '/^[\x{0d80}-\x{0dff}]{1,63}$/iu' ),
1853
+ '香港' => 'Hostname/Cn.php',
1854
+ '台湾' => 'Hostname/Cn.php',
1855
+ '台灣' => 'Hostname/Cn.php',
1856
+ 'امارات' => array( 1 => '/^[\x{0621}-\x{0624}\x{0626}-\x{063A}\x{0641}\x{0642}\x{0644}-\x{0648}\x{067E}\x{0686}\x{0698}\x{06A9}\x{06AF}\x{06CC}\x{06F0}-\x{06F9}]{1,30}$/iu' ),
1857
+ 'الاردن' => array( 1 => '/^[\x{0621}-\x{0624}\x{0626}-\x{063A}\x{0641}\x{0642}\x{0644}-\x{0648}\x{067E}\x{0686}\x{0698}\x{06A9}\x{06AF}\x{06CC}\x{06F0}-\x{06F9}]{1,30}$/iu' ),
1858
+ 'السعودية' => array( 1 => '/^[\x{0621}-\x{0624}\x{0626}-\x{063A}\x{0641}\x{0642}\x{0644}-\x{0648}\x{067E}\x{0686}\x{0698}\x{06A9}\x{06AF}\x{06CC}\x{06F0}-\x{06F9}]{1,30}$/iu' ),
1859
+ 'ไทย' => array( 1 => '/^[\x{002d}0-9a-z\x{0E01}-\x{0E3A}\x{0E40}-\x{0E4D}\x{0E50}-\x{0E59}]{1,63}$/iu' ),
1860
+ 'рф' => array( 1 => '/^[\x{002d}0-9а-яё]{1,63}$/iu' ),
1861
+ 'تونس' => array( 1 => '/^[\x{0621}-\x{0624}\x{0626}-\x{063A}\x{0641}\x{0642}\x{0644}-\x{0648}\x{067E}\x{0686}\x{0698}\x{06A9}\x{06AF}\x{06CC}\x{06F0}-\x{06F9}]{1,30}$/iu' ),
1862
+ 'مصر' => array( 1 => '/^[\x{0621}-\x{0624}\x{0626}-\x{063A}\x{0641}\x{0642}\x{0644}-\x{0648}\x{067E}\x{0686}\x{0698}\x{06A9}\x{06AF}\x{06CC}\x{06F0}-\x{06F9}]{1,30}$/iu' ),
1863
+ 'இலங்கை' => array( 1 => '/^[\x{0b80}-\x{0bff}]{1,63}$/iu' ),
1864
+ 'فلسطين' => array( 1 => '/^[\x{0621}-\x{0624}\x{0626}-\x{063A}\x{0641}\x{0642}\x{0644}-\x{0648}\x{067E}\x{0686}\x{0698}\x{06A9}\x{06AF}\x{06CC}\x{06F0}-\x{06F9}]{1,30}$/iu' ),
1865
+ 'شبكة' => array( 1 => '/^[\x{0621}-\x{0624}\x{0626}-\x{063A}\x{0641}\x{0642}\x{0644}-\x{0648}\x{067E}\x{0686}\x{0698}\x{06A9}\x{06AF}\x{06CC}\x{06F0}-\x{06F9}]{1,30}$/iu' ),
1866
+ );
1867
+
1868
+ protected $_idnLength = array(
1869
+ 'BIZ' => array( 5 => 17, 11 => 15, 12 => 20 ),
1870
+ 'CN' => array( 1 => 20 ),
1871
+ 'COM' => array( 3 => 17, 5 => 20 ),
1872
+ 'HK' => array( 1 => 15 ),
1873
+ 'INFO' => array( 4 => 17 ),
1874
+ 'KR' => array( 1 => 17 ),
1875
+ 'NET' => array( 3 => 17, 5 => 20 ),
1876
+ 'ORG' => array( 6 => 17 ),
1877
+ 'TW' => array( 1 => 20 ),
1878
+ 'ایران' => array( 1 => 30 ),
1879
+ '中国' => array( 1 => 20 ),
1880
+ '公司' => array( 1 => 20 ),
1881
+ '网络' => array( 1 => 20 ),
1882
+ );
1883
+
1884
+ protected $_options = array(
1885
+ 'allow' => self::ALLOW_DNS,
1886
+ 'idn' => true,
1887
+ 'tld' => true,
1888
+ 'ip' => null,
1889
+ );
1890
+
1891
+ /**
1892
+ * Sets validator options
1893
+ *
1894
+ * @see http://www.iana.org/cctld/specifications-policies-cctlds-01apr02.htm Technical Specifications for ccTLDs
1895
+ * @param array $options Validator options
1896
+ */
1897
+ public function __construct( $options = array() ) {
1898
+ if ( $options instanceof Postman_Zend_Config ) {
1899
+ $options = $options->toArray();
1900
+ } else if ( ! is_array( $options ) ) {
1901
+ $options = func_get_args();
1902
+ $temp['allow'] = array_shift( $options );
1903
+ if ( ! empty( $options ) ) {
1904
+ $temp['idn'] = array_shift( $options );
1905
+ }
1906
+
1907
+ if ( ! empty( $options ) ) {
1908
+ $temp['tld'] = array_shift( $options );
1909
+ }
1910
+
1911
+ if ( ! empty( $options ) ) {
1912
+ $temp['ip'] = array_shift( $options );
1913
+ }
1914
+
1915
+ $options = $temp;
1916
+ }
1917
+
1918
+ $options += $this->_options;
1919
+ $this->setOptions( $options );
1920
+ }
1921
+
1922
+ /**
1923
+ * Returns all set options
1924
+ *
1925
+ * @return array
1926
+ */
1927
+ public function getOptions() {
1928
+
1929
+ return $this->_options;
1930
+ }
1931
+
1932
+ /**
1933
+ * Sets the options for this validator
1934
+ *
1935
+ * @param array $options
1936
+ * @return Postman_Zend_Validate_Hostname
1937
+ */
1938
+ public function setOptions( $options ) {
1939
+ if ( array_key_exists( 'allow', $options ) ) {
1940
+ $this->setAllow( $options['allow'] );
1941
+ }
1942
+
1943
+ if ( array_key_exists( 'idn', $options ) ) {
1944
+ $this->setValidateIdn( $options['idn'] );
1945
+ }
1946
+
1947
+ if ( array_key_exists( 'tld', $options ) ) {
1948
+ $this->setValidateTld( $options['tld'] );
1949
+ }
1950
+
1951
+ if ( array_key_exists( 'ip', $options ) ) {
1952
+ $this->setIpValidator( $options['ip'] );
1953
+ }
1954
+
1955
+ return $this;
1956
+ }
1957
+
1958
+ /**
1959
+ * Returns the set ip validator
1960
+ *
1961
+ * @return Postman_Zend_Validate_Ip
1962
+ */
1963
+ public function getIpValidator() {
1964
+
1965
+ return $this->_options['ip'];
1966
+ }
1967
+
1968
+ /**
1969
+ * @param Postman_Zend_Validate_Ip $ipValidator OPTIONAL
1970
+ * @return Postman_Zend_Validate_Hostname
1971
+ */
1972
+ public function setIpValidator( Postman_Zend_Validate_Ip $ipValidator = null ) {
1973
+ if ( $ipValidator === null ) {
1974
+ $ipValidator = new Postman_Zend_Validate_Ip();
1975
+ }
1976
+
1977
+ $this->_options['ip'] = $ipValidator;
1978
+ return $this;
1979
+ }
1980
+
1981
+ /**
1982
+ * Returns the allow option
1983
+ *
1984
+ * @return integer
1985
+ */
1986
+ public function getAllow() {
1987
+
1988
+ return $this->_options['allow'];
1989
+ }
1990
+
1991
+ /**
1992
+ * Sets the allow option
1993
+ *
1994
+ * @param integer $allow
1995
+ * @return Postman_Zend_Validate_Hostname Provides a fluent interface
1996
+ */
1997
+ public function setAllow( $allow ) {
1998
+ $this->_options['allow'] = $allow;
1999
+ return $this;
2000
+ }
2001
+
2002
+ /**
2003
+ * Returns the set idn option
2004
+ *
2005
+ * @return boolean
2006
+ */
2007
+ public function getValidateIdn() {
2008
+
2009
+ return $this->_options['idn'];
2010
+ }
2011
+
2012
+ /**
2013
+ * Set whether IDN domains are validated
2014
+ *
2015
+ * This only applies when DNS hostnames are validated
2016
+ *
2017
+ * @param boolean $allowed Set allowed to true to validate IDNs, and false to not validate them
2018
+ * @return $this
2019
+ */
2020
+ public function setValidateIdn( $allowed ) {
2021
+ $this->_options['idn'] = (bool) $allowed;
2022
+ return $this;
2023
+ }
2024
+
2025
+ /**
2026
+ * Returns the set tld option
2027
+ *
2028
+ * @return boolean
2029
+ */
2030
+ public function getValidateTld() {
2031
+
2032
+ return $this->_options['tld'];
2033
+ }
2034
+
2035
+ /**
2036
+ * Set whether the TLD element of a hostname is validated
2037
+ *
2038
+ * This only applies when DNS hostnames are validated
2039
+ *
2040
+ * @param boolean $allowed Set allowed to true to validate TLDs, and false to not validate them
2041
+ * @return $this
2042
+ */
2043
+ public function setValidateTld( $allowed ) {
2044
+ $this->_options['tld'] = (bool) $allowed;
2045
+ return $this;
2046
+ }
2047
+
2048
+ /**
2049
+ * Defined by Postman_Zend_Validate_Interface
2050
+ *
2051
+ * Returns true if and only if the $value is a valid hostname with respect to the current allow option
2052
+ *
2053
+ * @param string $value
2054
+ * @throws Postman_Zend_Validate_Exception if a fatal error occurs for validation process
2055
+ * @return boolean
2056
+ */
2057
+ public function isValid( $value ) {
2058
+ if ( ! is_string( $value ) ) {
2059
+ $this->_error( self::INVALID );
2060
+ return false;
2061
+ }
2062
+
2063
+ $this->_setValue( $value );
2064
+ // Check input against IP address schema
2065
+ if ( preg_match( '/^[0-9a-f:.]*$/i', $value ) &&
2066
+ $this->_options['ip']->setTranslator( $this->getTranslator() )->isValid( $value ) ) {
2067
+ if ( ! ($this->_options['allow'] & self::ALLOW_IP) ) {
2068
+ $this->_error( self::IP_ADDRESS_NOT_ALLOWED );
2069
+ return false;
2070
+ } else {
2071
+ return true;
2072
+ }
2073
+ }
2074
+
2075
+ // RFC3986 3.2.2 states:
2076
+ //
2077
+ // The rightmost domain label of a fully qualified domain name
2078
+ // in DNS may be followed by a single "." and should be if it is
2079
+ // necessary to distinguish between the complete domain name and
2080
+ // some local domain.
2081
+ //
2082
+ // (see ZF-6363)
2083
+ // Local hostnames are allowed to be partitial (ending '.')
2084
+ if ( $this->_options['allow'] & self::ALLOW_LOCAL ) {
2085
+ if ( substr( $value, -1 ) === '.' ) {
2086
+ $value = substr( $value, 0, -1 );
2087
+ if ( substr( $value, -1 ) === '.' ) {
2088
+ // Empty hostnames (ending '..') are not allowed
2089
+ $this->_error( self::INVALID_LOCAL_NAME );
2090
+ return false;
2091
+ }
2092
+ }
2093
+ }
2094
+
2095
+ $domainParts = explode( '.', $value );
2096
+
2097
+ // Prevent partitial IP V4 adresses (ending '.')
2098
+ if ( (count( $domainParts ) == 4) && preg_match( '/^[0-9.a-e:.]*$/i', $value ) &&
2099
+ $this->_options['ip']->setTranslator( $this->getTranslator() )->isValid( $value ) ) {
2100
+ $this->_error( self::INVALID_LOCAL_NAME );
2101
+ }
2102
+
2103
+ // Check input against DNS hostname schema
2104
+ if ( (count( $domainParts ) > 1) && (strlen( $value ) >= 4) && (strlen( $value ) <= 254) ) {
2105
+ $status = false;
2106
+
2107
+ $origenc = PHP_VERSION_ID < 50600
2108
+ ? iconv_get_encoding( 'internal_encoding' )
2109
+ : ini_get( 'default_charset' );
2110
+ if ( PHP_VERSION_ID < 50600 ) {
2111
+ iconv_set_encoding( 'internal_encoding', 'UTF-8' );
2112
+ } else {
2113
+ // jason was here
2114
+ @ini_set( 'default_charset', 'UTF-8' );
2115
+ }
2116
+ do {
2117
+ // First check TLD
2118
+ $matches = array();
2119
+ if ( preg_match( '/([^.]{2,63})$/iu', end( $domainParts ), $matches )
2120
+ || (array_key_exists( end( $domainParts ), $this->_validIdns )) ) {
2121
+ reset( $domainParts );
2122
+
2123
+ // Hostname characters are: *(label dot)(label dot label); max 254 chars
2124
+ // label: id-prefix [*ldh{61} id-prefix]; max 63 chars
2125
+ // id-prefix: alpha / digit
2126
+ // ldh: alpha / digit / dash
2127
+ // Match TLD against known list
2128
+ $this->_tld = $matches[1];
2129
+ if ( $this->_options['tld'] ) {
2130
+ if ( ! in_array( strtolower( $this->_tld ), $this->_validTlds )
2131
+ && ! in_array( $this->_tld, $this->_validTlds ) ) {
2132
+ $this->_error( self::UNKNOWN_TLD );
2133
+ $status = false;
2134
+ break;
2135
+ }
2136
+ // We have already validated that the TLD is fine. We don't want it to go through the below
2137
+ // checks as new UTF-8 TLDs will incorrectly fail if there is no IDN regex for it.
2138
+ array_pop( $domainParts );
2139
+ }
2140
+
2141
+ /**
2142
+ * Match against IDN hostnames
2143
+ * Note: Keep label regex short to avoid issues with long patterns when matching IDN hostnames
2144
+ *
2145
+ * @see Postman_Zend_Validate_Hostname_Interface
2146
+ */
2147
+ $regexChars = array( 0 => '/^[a-z0-9\x2d]{1,63}$/i' );
2148
+ if ( $this->_options['idn'] && isset( $this->_validIdns[ strtoupper( $this->_tld ) ] ) ) {
2149
+ if ( is_string( $this->_validIdns[ strtoupper( $this->_tld ) ] ) ) {
2150
+ $regexChars += include( $this->_validIdns[ strtoupper( $this->_tld ) ] );
2151
+ } else {
2152
+ $regexChars += $this->_validIdns[ strtoupper( $this->_tld ) ];
2153
+ }
2154
+ }
2155
+
2156
+ // Check each hostname part
2157
+ $check = 0;
2158
+ foreach ( $domainParts as $domainPart ) {
2159
+ // If some domain part is empty (i.e. zend..com), it's invalid
2160
+ if ( empty( $domainPart ) ) {
2161
+ $this->_error( self::INVALID_HOSTNAME );
2162
+ return false;
2163
+ }
2164
+
2165
+ // Decode Punycode domainnames to IDN
2166
+ if ( strpos( $domainPart, 'xn--' ) === 0 ) {
2167
+ $domainPart = $this->decodePunycode( substr( $domainPart, 4 ) );
2168
+ if ( $domainPart === false ) {
2169
+ return false;
2170
+ }
2171
+ }
2172
+
2173
+ // Check dash (-) does not start, end or appear in 3rd and 4th positions
2174
+ if ( (strpos( $domainPart, '-' ) === 0)
2175
+ || ((strlen( $domainPart ) > 2) && (strpos( $domainPart, '-', 2 ) == 2) && (strpos( $domainPart, '-', 3 ) == 3))
2176
+ || (strpos( $domainPart, '-' ) === (strlen( $domainPart ) - 1)) ) {
2177
+ $this->_error( self::INVALID_DASH );
2178
+ $status = false;
2179
+ break 2;
2180
+ }
2181
+
2182
+ // Check each domain part
2183
+ $checked = false;
2184
+ foreach ( $regexChars as $regexKey => $regexChar ) {
2185
+ $status = preg_match( $regexChar, $domainPart );
2186
+ if ( $status > 0 ) {
2187
+ $length = 63;
2188
+ if ( array_key_exists( strtoupper( $this->_tld ), $this->_idnLength )
2189
+ && (array_key_exists( $regexKey, $this->_idnLength[ strtoupper( $this->_tld ) ] )) ) {
2190
+ $length = $this->_idnLength[ strtoupper( $this->_tld ) ];
2191
+ }
2192
+
2193
+ if ( iconv_strlen( $domainPart, 'UTF-8' ) > $length ) {
2194
+ $this->_error( self::INVALID_HOSTNAME );
2195
+ } else {
2196
+ $checked = true;
2197
+ break;
2198
+ }
2199
+ }
2200
+ }
2201
+
2202
+ if ( $checked ) {
2203
+ ++$check;
2204
+ }
2205
+ }
2206
+
2207
+ // If one of the labels doesn't match, the hostname is invalid
2208
+ if ( $check !== count( $domainParts ) ) {
2209
+ $this->_error( self::INVALID_HOSTNAME_SCHEMA );
2210
+ $status = false;
2211
+ }
2212
+ } else {
2213
+ // Hostname not long enough
2214
+ $this->_error( self::UNDECIPHERABLE_TLD );
2215
+ $status = false;
2216
+ }
2217
+ } while (false);
2218
+
2219
+ if ( PHP_VERSION_ID < 50600 ) {
2220
+ iconv_set_encoding( 'internal_encoding', $origenc );
2221
+ } else {
2222
+ // jason was here
2223
+ @ini_set( 'default_charset', $origenc );
2224
+ }
2225
+ // If the input passes as an Internet domain name, and domain names are allowed, then the hostname
2226
+ // passes validation
2227
+ if ( $status && ($this->_options['allow'] & self::ALLOW_DNS) ) {
2228
+ return true;
2229
+ }
2230
+ } else if ( $this->_options['allow'] & self::ALLOW_DNS ) {
2231
+ $this->_error( self::INVALID_HOSTNAME );
2232
+ }
2233
+
2234
+ // Check for URI Syntax (RFC3986)
2235
+ if ( $this->_options['allow'] & self::ALLOW_URI ) {
2236
+ if ( preg_match( "/^([a-zA-Z0-9-._~!$&\'()*+,;=]|%[[:xdigit:]]{2}){1,254}$/i", $value ) ) {
2237
+ return true;
2238
+ } else {
2239
+ $this->_error( self::INVALID_URI );
2240
+ }
2241
+ }
2242
+
2243
+ // Check input against local network name schema; last chance to pass validation
2244
+ $regexLocal = '/^(([a-zA-Z0-9\x2d]{1,63}\x2e)*[a-zA-Z0-9\x2d]{1,63}[\x2e]{0,1}){1,254}$/';
2245
+ $status = @preg_match( $regexLocal, $value );
2246
+
2247
+ // If the input passes as a local network name, and local network names are allowed, then the
2248
+ // hostname passes validation
2249
+ $allowLocal = $this->_options['allow'] & self::ALLOW_LOCAL;
2250
+ if ( $status && $allowLocal ) {
2251
+ return true;
2252
+ }
2253
+
2254
+ // If the input does not pass as a local network name, add a message
2255
+ if ( ! $status ) {
2256
+ $this->_error( self::INVALID_LOCAL_NAME );
2257
+ }
2258
+
2259
+ // If local network names are not allowed, add a message
2260
+ if ( $status && ! $allowLocal ) {
2261
+ $this->_error( self::LOCAL_NAME_NOT_ALLOWED );
2262
+ }
2263
+
2264
+ return false;
2265
+ }
2266
+
2267
+ /**
2268
+ * Decodes a punycode encoded string to it's original utf8 string
2269
+ * In case of a decoding failure the original string is returned
2270
+ *
2271
+ * @param string $encoded Punycode encoded string to decode
2272
+ * @return string
2273
+ */
2274
+ protected function decodePunycode( $encoded ) {
2275
+ if ( ! preg_match( '/^[a-z0-9-]+$/i', $encoded ) ) {
2276
+ // no punycode encoded string
2277
+ $this->_error( self::CANNOT_DECODE_PUNYCODE );
2278
+ return false;
2279
+ }
2280
+
2281
+ $decoded = array();
2282
+ $separator = strrpos( $encoded, '-' );
2283
+ if ( $separator > 0 ) {
2284
+ for ( $x = 0; $x < $separator; ++$x ) {
2285
+ // prepare decoding matrix
2286
+ $decoded[] = ord( $encoded[ $x ] );
2287
+ }
2288
+ }
2289
+
2290
+ $lengthd = count( $decoded );
2291
+ $lengthe = strlen( $encoded );
2292
+
2293
+ // decoding
2294
+ $init = true;
2295
+ $base = 72;
2296
+ $index = 0;
2297
+ $char = 0x80;
2298
+
2299
+ for ( $indexe = ($separator) ? ($separator + 1) : 0; $indexe < $lengthe; ++$lengthd ) {
2300
+ for ( $old_index = $index, $pos = 1, $key = 36; 1 ; $key += 36 ) {
2301
+ $hex = ord( $encoded[ $indexe++ ] );
2302
+ $digit = ($hex - 48 < 10) ? $hex - 22
2303
+ : (($hex - 65 < 26) ? $hex - 65
2304
+ : (($hex - 97 < 26) ? $hex - 97
2305
+ : 36));
2306
+
2307
+ $index += $digit * $pos;
2308
+ $tag = ($key <= $base) ? 1 : (($key >= $base + 26) ? 26 : ($key - $base));
2309
+ if ( $digit < $tag ) {
2310
+ break;
2311
+ }
2312
+
2313
+ $pos = (int) ($pos * (36 - $tag));
2314
+ }
2315
+
2316
+ $delta = intval( $init ? (($index - $old_index) / 700) : (($index - $old_index) / 2) );
2317
+ $delta += intval( $delta / ($lengthd + 1) );
2318
+ for ( $key = 0; $delta > 910 / 2; $key += 36 ) {
2319
+ $delta = intval( $delta / 35 );
2320
+ }
2321
+
2322
+ $base = intval( $key + 36 * $delta / ($delta + 38) );
2323
+ $init = false;
2324
+ $char += (int) ($index / ($lengthd + 1));
2325
+ $index %= ($lengthd + 1);
2326
+ if ( $lengthd > 0 ) {
2327
+ for ( $i = $lengthd; $i > $index; $i-- ) {
2328
+ $decoded[ $i ] = $decoded[ ($i - 1) ];
2329
+ }
2330
+ }
2331
+
2332
+ $decoded[ $index++ ] = $char;
2333
+ }
2334
+
2335
+ // convert decoded ucs4 to utf8 string
2336
+ foreach ( $decoded as $key => $value ) {
2337
+ if ( $value < 128 ) {
2338
+ $decoded[ $key ] = chr( $value );
2339
+ } elseif ( $value < (1 << 11) ) {
2340
+ $decoded[ $key ] = chr( 192 + ($value >> 6) );
2341
+ $decoded[ $key ] .= chr( 128 + ($value & 63) );
2342
+ } elseif ( $value < (1 << 16) ) {
2343
+ $decoded[ $key ] = chr( 224 + ($value >> 12) );
2344
+ $decoded[ $key ] .= chr( 128 + (($value >> 6) & 63) );
2345
+ $decoded[ $key ] .= chr( 128 + ($value & 63) );
2346
+ } elseif ( $value < (1 << 21) ) {
2347
+ $decoded[ $key ] = chr( 240 + ($value >> 18) );
2348
+ $decoded[ $key ] .= chr( 128 + (($value >> 12) & 63) );
2349
+ $decoded[ $key ] .= chr( 128 + (($value >> 6) & 63) );
2350
+ $decoded[ $key ] .= chr( 128 + ($value & 63) );
2351
+ } else {
2352
+ $this->_error( self::CANNOT_DECODE_PUNYCODE );
2353
+ return false;
2354
+ }
2355
+ }
2356
+
2357
+ return implode( $decoded );
2358
+ }
2359
  }
Postman/Postman.php CHANGED
@@ -16,7 +16,7 @@
16
  */
17
  class Postman {
18
 
19
- const ADMINISTRATOR_ROLE_NAME = 'administrator';
20
  const MANAGE_POSTMAN_CAPABILITY_NAME = 'manage_postman_smtp';
21
  const TEXT_DOMAIN = 'post-smtp';
22
 
@@ -250,7 +250,26 @@ class Postman {
250
  // on any admin pages, show this error message
251
  // I noticed the wpMandrill and SendGrid plugins have the exact same error message here
252
  // I've adopted their error message as well, for shits and giggles .... :D
 
 
253
  $message = __( 'Postman: wp_mail has been declared by another plugin or theme, so you won\'t be able to use Postman until the conflict is resolved.', Postman::TEXT_DOMAIN );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
254
  $this->messageHandler->addError( $message );
255
  }
256
  } else {
@@ -392,4 +411,3 @@ if ( ! function_exists( 'str_getcsv' ) ) {
392
  return PostmanUtils::postman_strgetcsv_impl( $string );
393
  }
394
  }
395
-
16
  */
17
  class Postman {
18
 
19
+ const ADMINISTRATOR_ROLE_NAME = 'administrator';
20
  const MANAGE_POSTMAN_CAPABILITY_NAME = 'manage_postman_smtp';
21
  const TEXT_DOMAIN = 'post-smtp';
22
 
250
  // on any admin pages, show this error message
251
  // I noticed the wpMandrill and SendGrid plugins have the exact same error message here
252
  // I've adopted their error message as well, for shits and giggles .... :D
253
+ $reflFunc = new ReflectionFunction( 'wp_mail' );
254
+
255
  $message = __( 'Postman: wp_mail has been declared by another plugin or theme, so you won\'t be able to use Postman until the conflict is resolved.', Postman::TEXT_DOMAIN );
256
+ $plugin_full_path = $reflFunc->getFileName();
257
+
258
+ if ( strpos( $plugin_full_path, 'plugins' ) !== false ) {
259
+
260
+ require_once ABSPATH . '/wp-admin/includes/plugin.php';
261
+
262
+ preg_match( '/([a-z]+\/[a-z]+\.php)$/', $plugin_full_path, $output_array );
263
+
264
+ $plugin_file = $output_array[1];
265
+ $plugin_data = get_plugin_data( $plugin_full_path );
266
+
267
+ $deactivate_url = '<a href="' . wp_nonce_url( 'plugins.php?action=deactivate&amp;plugin=' . urlencode( $plugin_file ) . '&amp;plugin_status=active&amp;paged=1&amp;s=deactivate-plugin_' . $plugin_file ) . '" aria-label="' . esc_attr( sprintf( _x( 'Deactivate %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Deactivate' ) . '</a><br>';
268
+ $message .= '<br><strong>Plugin Name:</strong> ' . $plugin_data['Name'];
269
+ $message .= '<br>' . $deactivate_url;
270
+ }
271
+
272
+ $message .= '<br><strong>More info that may help</strong> - ' . $reflFunc->getFileName() . ':' . $reflFunc->getStartLine();
273
  $this->messageHandler->addError( $message );
274
  }
275
  } else {
411
  return PostmanUtils::postman_strgetcsv_impl( $string );
412
  }
413
  }
 
Postman/PostmanAdminController.php CHANGED
@@ -137,6 +137,8 @@ if ( ! class_exists( 'PostmanAdminController' ) ) {
137
  $this,
138
  'postmanModifyLinksOnPluginsListPage',
139
  ) );
 
 
140
  }
141
 
142
  /**
137
  $this,
138
  'postmanModifyLinksOnPluginsListPage',
139
  ) );
140
+
141
+ require_once( 'PostmanPluginFeedback.php' );
142
  }
143
 
144
  /**
Postman/PostmanConfigTextHelper.php CHANGED
@@ -30,10 +30,10 @@ if ( ! class_exists( 'PostmanAbstractConfigTextHelper' ) ) {
30
  public function getOAuthHelp() {
31
  $attention = __( 'Attention' );
32
  /* translators: parameters available are 1=portal-url, 2=portal-name, 3=clientId-name, 4=clientSecret-name, 5=callbackUrl, 6=service-name, 7=portal-application (e.g. Open the Google Developer Console, create a Client ID for web application using the URL's displayed below, and copy the Client ID and Client Secret here.) */
33
- $errorMessage = sprintf( __( 'Open the <a href="%1$s" target="_new">%2$s</a>, create <b>%7$s</b> with the values displayed below, and copy the generated %3$s and %4$s here.', Postman::TEXT_DOMAIN ), $this->getApplicationPortalUrl(), $this->getApplicationPortalName(), $this->getClientIdLabel(), $this->getClientSecretLabel(), $this->getCallbackUrlLabel(), $this->getOwnerName(), $this->getApplicationDescription() );
34
  $text = sprintf( '<b style="color:red">%s!</b> %s', $attention, $errorMessage );
35
  /* translators: parameters available are 1=clientId-name, 2=service-name, 3=FAQ-URL, 4=Video-URL (e.g. See How do I get a Google Client ID? in the F.A.Q.) */
36
- $howToTemplate = __( 'See <a href="%3$s" target="_new">How do I get a %1$s %2$s?</a> in the F.A.Q. or <a href="%4$s" target="_new">watch our How-To video 📺</a>.', Postman::TEXT_DOMAIN );
37
  $text .= sprintf( ' %s', sprintf( $howToTemplate, $this->getOwnerName(), $this->getClientIdLabel(), 'https://wordpress.org/plugins/post-smtp/faq/', 'https://vimeo.com/128589255' ) );
38
  return $text;
39
  }
30
  public function getOAuthHelp() {
31
  $attention = __( 'Attention' );
32
  /* translators: parameters available are 1=portal-url, 2=portal-name, 3=clientId-name, 4=clientSecret-name, 5=callbackUrl, 6=service-name, 7=portal-application (e.g. Open the Google Developer Console, create a Client ID for web application using the URL's displayed below, and copy the Client ID and Client Secret here.) */
33
+ $errorMessage = sprintf( __( 'Open the <a href="%1$s" target="_blank">%2$s</a>, create <b>%7$s</b> with the values displayed below, and copy the generated %3$s and %4$s here.', Postman::TEXT_DOMAIN ), $this->getApplicationPortalUrl(), $this->getApplicationPortalName(), $this->getClientIdLabel(), $this->getClientSecretLabel(), $this->getCallbackUrlLabel(), $this->getOwnerName(), $this->getApplicationDescription() );
34
  $text = sprintf( '<b style="color:red">%s!</b> %s', $attention, $errorMessage );
35
  /* translators: parameters available are 1=clientId-name, 2=service-name, 3=FAQ-URL, 4=Video-URL (e.g. See How do I get a Google Client ID? in the F.A.Q.) */
36
+ $howToTemplate = __( 'See <a href="%3$s" target="_blank">How do I get a %1$s %2$s?</a> in the F.A.Q. or <a href="%4$s" target="_blank">watch our How-To video 📺</a>.', Postman::TEXT_DOMAIN );
37
  $text .= sprintf( ' %s', sprintf( $howToTemplate, $this->getOwnerName(), $this->getClientIdLabel(), 'https://wordpress.org/plugins/post-smtp/faq/', 'https://vimeo.com/128589255' ) );
38
  return $text;
39
  }
Postman/PostmanPluginFeedback.php ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class PostmanPluginFeedback {
4
+ function __construct() {
5
+ add_filter( 'plugin_action_links_' . plugin_basename( POST_BASE ), array( $this, 'insert_deactivate_link_id' ) );
6
+ add_action( 'wp_ajax_post_user_feedback', array( $this, 'post_user_feedback' ) );
7
+
8
+ global $pagenow;
9
+ if ( 'plugins.php' === $pagenow ) {
10
+ add_action( 'admin_enqueue_scripts', array( $this, 'load_scripts' ) );
11
+ add_action( 'admin_head', array( $this, 'admin_head' ) );
12
+ add_action( 'admin_footer', array( $this, 'add_deactivation_dialog' ) );
13
+ }
14
+ }
15
+
16
+ function load_scripts() {
17
+ wp_enqueue_style( 'wp-jquery-ui-dialog' );
18
+ wp_register_script( 'post-feedback', plugins_url( 'script/feedback/feedback.js', POST_BASE ), array( 'jquery', 'jquery-ui-core', 'jquery-ui-dialog' ), fasle, true );
19
+ wp_localize_script( 'post-feedback', 'post_feedback', array( 'admin_ajax' => admin_url( 'admin-ajax.php' ) ) );
20
+ wp_enqueue_script( 'post-feedback' );
21
+ }
22
+
23
+ function post_user_feedback() {
24
+ if ( ! check_ajax_referer() ) {
25
+ die( 'security error' );
26
+ }
27
+
28
+ $payload = array(
29
+ 'reason' => sanitize_text_field( $_POST['reason'] ),
30
+ 'other_input' => isset( $_POST['other_input'] ) ? sanitize_textarea_field( $_POST['other_input'] ) : '',
31
+ );
32
+
33
+ if ( isset( $_POST['support'] ) ) {
34
+ $payload['support']['email'] = sanitize_email( $_POST['support']['email'] );
35
+ $payload['support']['title'] = sanitize_text_field( $_POST['support']['title'] );
36
+ $payload['support']['text'] = sanitize_textarea_field( $_POST['support']['text'] );
37
+ }
38
+
39
+ $args = array(
40
+ 'body' => $payload,
41
+ );
42
+ $result = wp_remote_post( 'https://postmansmtp.com/feedback', $args );
43
+ die( 'success' );
44
+ }
45
+
46
+ function admin_head() {
47
+ ?>
48
+ <style type="text/css">
49
+ .postman-feedback-dialog-form .ui-dialog-buttonset {
50
+ float: none !important;
51
+ }
52
+
53
+ #postman-feedback-dialog-skip {
54
+ float: left;
55
+ }
56
+
57
+ #postman-feedback-dialog-go, #postman-feedback-dialog-cancel {
58
+ float: right;
59
+ }
60
+
61
+ #postman-feedback-dialog-content p {
62
+ font-size: 1.1em;
63
+ }
64
+
65
+ #postman-deactivate-reasons textarea {
66
+ margin-top: 10px;
67
+ width: 100%;
68
+ height: 150px;
69
+ }
70
+ </style>
71
+ <?php
72
+ }
73
+
74
+ function insert_deactivate_link_id( $links ) {
75
+ $links['deactivate'] = str_replace( '<a', '<a id="postman-plugin-disbale-link"', $links['deactivate'] );
76
+
77
+ return $links;
78
+ }
79
+
80
+ function add_deactivation_dialog() {
81
+ ?>
82
+ <div id="postman-feedback-dialog-content" style="display: none;">
83
+ <p>
84
+ I feel bad to see anyone stop using Post SMTP.<br>
85
+ I would love to get a small feedback from you.
86
+ </p>
87
+ <form>
88
+ <?php wp_nonce_field(); ?>
89
+ <ul id="postman-deactivate-reasons">
90
+ <li class="postman-reason postman-custom-input">
91
+ <label>
92
+ <span><input value="Found a better plugin" type="radio" name="reason" /></span>
93
+ <span><?php _e( 'Found a better plugin', 'postman' ); ?></span>
94
+ </label>
95
+ <div class="postman-reason-input" style="display: none;">
96
+ <textarea name="other_input"></textarea>
97
+ </div>
98
+ </li>
99
+ <li class="postman-reason">
100
+ <label>
101
+ <span><input value="The plugin didn't work" type="radio" name="reason" /></span>
102
+ <span><?php _e( 'The plugin didn\'t work', 'postman' ); ?></span>
103
+ </label>
104
+ </li>
105
+ <li class="postman-reason postman-custom-input">
106
+ <label>
107
+ <span><input value="Other Reason" type="radio" name="reason" /></span>
108
+ <span><?php _e( 'Other Reason', 'postman' ); ?></span>
109
+ </label>
110
+ <div class="postman-reason-input" style="display: none;">
111
+ <textarea name="other_input"></textarea>
112
+ </div>
113
+ </li>
114
+ <li class="postman-reason postman-custom-input">
115
+ <label>
116
+ <span><input value="Support Ticket" type="radio" name="reason" /></span>
117
+ <span><?php _e( 'Open A support ticket for me', 'postman' ); ?></span>
118
+ </label>
119
+ <div class="postman-reason-input" style="display: none;">
120
+ <input type="email" name="support[email]" placeholder="Your Email Address" required>
121
+ <input type="text" name="support[title]" placeholder="The Title" required>
122
+ <textarea name="support[text]" placeholder="Describe the issue" required></textarea>
123
+ </div>
124
+ </li>
125
+ </ul>
126
+ </form>
127
+ </div>
128
+ <?php
129
+ }
130
+ }
131
+ new PostmanPluginFeedback;
Postman/PostmanUtils.php CHANGED
@@ -5,107 +5,103 @@ require_once 'PostmanState.php';
5
  /**
6
  *
7
  * @author jasonhendriks
8
- *
9
  */
10
  class PostmanUtils {
11
  private static $logger;
12
  private static $emailValidator;
13
-
14
- //
15
  const POSTMAN_SETTINGS_PAGE_STUB = 'postman';
16
  const REQUEST_OAUTH2_GRANT_SLUG = 'postman/requestOauthGrant';
17
  const POSTMAN_EMAIL_LOG_PAGE_STUB = 'postman_email_log';
18
-
19
  // redirections back to THIS SITE should always be relative because of IIS bug
20
- const POSTMAN_EMAIL_LOG_PAGE_RELATIVE_URL = 'tools.php?page=postman_email_log';
21
- const POSTMAN_HOME_PAGE_RELATIVE_URL = 'options-general.php?page=postman';
22
-
23
  // custom admin post page
24
  const ADMIN_POST_OAUTH2_GRANT_URL_PART = 'admin-post.php?action=postman/requestOauthGrant';
25
-
26
- //
27
- const NO_ECHO = false;
28
-
29
  /**
30
  * Initialize the Logger
31
  */
32
  public static function staticInit() {
33
- PostmanUtils::$logger = new PostmanLogger ( 'PostmanUtils' );
34
  }
35
-
36
  /**
37
  *
38
- * @param unknown $slug
39
  * @return string
40
  */
41
- public static function getPageUrl($slug) {
42
- return get_admin_url () . 'options-general.php?page=' . $slug;
43
  }
44
-
45
  /**
46
  * Returns an escaped URL
47
  */
48
  public static function getGrantOAuthPermissionUrl() {
49
- return get_admin_url () . self::ADMIN_POST_OAUTH2_GRANT_URL_PART;
50
  }
51
-
52
  /**
53
  * Returns an escaped URL
54
  */
55
  public static function getEmailLogPageUrl() {
56
- return menu_page_url ( self::POSTMAN_EMAIL_LOG_PAGE_STUB, self::NO_ECHO );
57
  }
58
-
59
  /**
60
  * Returns an escaped URL
61
  */
62
  public static function getSettingsPageUrl() {
63
- return menu_page_url ( self::POSTMAN_SETTINGS_PAGE_STUB, self::NO_ECHO );
64
  }
65
-
66
- //
67
- public static function isCurrentPagePostmanAdmin($page = 'postman') {
68
- $result = (isset ( $_REQUEST ['page'] ) && substr ( $_REQUEST ['page'], 0, strlen ( $page ) ) == $page);
69
  return $result;
70
  }
71
  /**
72
  * from http://stackoverflow.com/questions/834303/startswith-and-endswith-functions-in-php
73
  *
74
- * @param unknown $haystack
75
- * @param unknown $needle
76
  * @return boolean
77
  */
78
- public static function startsWith($haystack, $needle) {
79
- $length = strlen ( $needle );
80
- return (substr ( $haystack, 0, $length ) === $needle);
81
  }
82
  /**
83
  * from http://stackoverflow.com/questions/834303/startswith-and-endswith-functions-in-php
84
  *
85
- * @param unknown $haystack
86
- * @param unknown $needle
87
  * @return boolean
88
  */
89
- public static function endsWith($haystack, $needle) {
90
- $length = strlen ( $needle );
91
- if ($length == 0) {
92
  return true;
93
  }
94
- return (substr ( $haystack, - $length ) === $needle);
95
  }
96
- public static function obfuscatePassword($password) {
97
- return str_repeat ( '*', strlen ( $password ) );
98
  }
99
  /**
100
  * Detect if the host is NOT a domain name
101
  *
102
- * @param unknown $ipAddress
103
  * @return number
104
  */
105
- public static function isHostAddressNotADomainName($host) {
106
  // IPv4 / IPv6 test from http://stackoverflow.com/a/17871737/4368109
107
- $ipv6Detected = preg_match ( '/(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/', $host );
108
- $ipv4Detected = preg_match ( '/((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])/', $host );
109
  return $ipv4Detected || $ipv6Detected;
110
  // from http://stackoverflow.com/questions/106179/regular-expression-to-match-dns-hostname-or-ip-address
111
  // return preg_match ( '/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9‌​]{2}|2[0-4][0-9]|25[0-5])$/', $ipAddress );
@@ -115,44 +111,44 @@ class PostmanUtils {
115
  * Inside WordPress we can use wp_remote_post().
116
  * Outside WordPress, not so much.
117
  *
118
- * @param unknown $url
119
- * @param unknown $args
120
  * @return the HTML body
121
  */
122
- static function remotePostGetBodyOnly($url, $parameters, array $headers = array()) {
123
- $response = PostmanUtils::remotePost ( $url, $parameters, $headers );
124
- $theBody = wp_remote_retrieve_body ( $response );
125
  return $theBody;
126
  }
127
-
128
  /**
129
  * Makes the outgoing HTTP requests
130
  * Inside WordPress we can use wp_remote_post().
131
  * Outside WordPress, not so much.
132
  *
133
- * @param unknown $url
134
- * @param unknown $args
135
  * @return the HTTP response
136
  */
137
- static function remotePost($url, $parameters = array(), array $headers = array()) {
138
- $args = array (
139
- 'timeout' => PostmanOptions::getInstance ()->getConnectionTimeout (),
140
  'headers' => $headers,
141
- 'body' => $parameters
142
  );
143
- if (PostmanUtils::$logger->isTrace ()) {
144
- PostmanUtils::$logger->trace ( sprintf ( 'Posting to %s', $url ) );
145
- PostmanUtils::$logger->trace ( 'Post header:' );
146
- PostmanUtils::$logger->trace ( $headers );
147
- PostmanUtils::$logger->trace ( 'Posting args:' );
148
- PostmanUtils::$logger->trace ( $parameters );
149
  }
150
- $response = wp_remote_post ( $url, $args );
151
-
152
  // pre-process the response
153
- if (is_wp_error ( $response )) {
154
- PostmanUtils::$logger->error ( $response->get_error_message () );
155
- throw new Exception ( 'Error executing wp_remote_post: ' . $response->get_error_message () );
156
  } else {
157
  return $response;
158
  }
@@ -161,51 +157,51 @@ class PostmanUtils {
161
  * A facade function that handles redirects.
162
  * Inside WordPress we can use wp_redirect(). Outside WordPress, not so much. **Load it before postman-core.php**
163
  *
164
- * @param unknown $url
165
  */
166
- static function redirect($url) {
167
  // redirections back to THIS SITE should always be relative because of IIS bug
168
- if (PostmanUtils::$logger->isTrace ()) {
169
- PostmanUtils::$logger->trace ( sprintf ( "Redirecting to '%s'", $url ) );
170
  }
171
- wp_redirect ( $url );
172
- exit ();
173
  }
174
- static function parseBoolean($var) {
175
- return filter_var ( $var, FILTER_VALIDATE_BOOLEAN );
176
  }
177
- static function logMemoryUse($startingMemory, $description) {
178
- PostmanUtils::$logger->trace ( sprintf ( $description . ' memory used: %s', PostmanUtils::roundBytes ( memory_get_usage () - $startingMemory ) ) );
179
  }
180
-
181
  /**
182
  * Rounds the bytes returned from memory_get_usage to smaller amounts used IEC binary prefixes
183
  * See http://en.wikipedia.org/wiki/Binary_prefix
184
  *
185
- * @param unknown $size
186
  * @return string
187
  */
188
- static function roundBytes($size) {
189
- $unit = array (
190
  'B',
191
  'KiB',
192
  'MiB',
193
  'GiB',
194
  'TiB',
195
- 'PiB'
196
  );
197
- return @round ( $size / pow ( 1024, ($i = floor ( log ( $size, 1024 ) )) ), 2 ) . ' ' . $unit [$i];
198
  }
199
-
200
  /**
201
  * Unblock threads waiting on lock()
202
  */
203
  static function unlock() {
204
- if (PostmanState::getInstance ()->isFileLockingEnabled ()) {
205
- PostmanUtils::deleteLockFile ();
206
  }
207
  }
208
-
209
  /**
210
  * Processes will block on this method until unlock() is called
211
  * Inspired by http://cubicspot.blogspot.ca/2010/10/forget-flock-and-system-v-semaphores.html
@@ -213,56 +209,56 @@ class PostmanUtils {
213
  * @throws Exception
214
  */
215
  static function lock() {
216
- if (PostmanState::getInstance ()->isFileLockingEnabled ()) {
217
  $attempts = 0;
218
  while ( true ) {
219
  // create the semaphore
220
- $lock = PostmanUtils::createLockFile ();
221
- if ($lock) {
222
  // if we got the lock, return
223
  return;
224
  } else {
225
  $attempts ++;
226
- if ($attempts >= 10) {
227
- throw new Exception ( sprintf ( 'Could not create lockfile %s', '/tmp' . '/.postman.lock' ) );
228
  }
229
- sleep ( 1 );
230
  }
231
  }
232
  }
233
  }
234
- static function deleteLockFile($tempDirectory = null) {
235
- $path = PostmanUtils::calculateTemporaryLockPath ( $tempDirectory );
236
- $success = @unlink ( $path );
237
- if (PostmanUtils::$logger->isTrace ()) {
238
- PostmanUtils::$logger->trace ( sprintf ( 'Deleting file %s : %s', $path, $success ) );
239
  }
240
  return $success;
241
  }
242
- static function createLockFile($tempDirectory = null) {
243
- $path = PostmanUtils::calculateTemporaryLockPath ( $tempDirectory );
244
- $success = @fopen ( $path, 'xb' );
245
- if (PostmanUtils::$logger->isTrace ()) {
246
- PostmanUtils::$logger->trace ( sprintf ( 'Creating file %s : %s', $path, $success ) );
247
  }
248
  return $success;
249
  }
250
-
251
  /**
252
  * Creates the pathname of the lockfile
253
  *
254
- * @param unknown $tempDirectory
255
  * @return string
256
  */
257
- private static function calculateTemporaryLockPath($tempDirectory) {
258
- if (empty ( $tempDirectory )) {
259
- $options = PostmanOptions::getInstance ();
260
- $tempDirectory = $options->getTempDirectory ();
261
  }
262
- $fullPath = sprintf ( '%s/.postman_%s.lock', $tempDirectory, self::generateUniqueLockKey () );
263
  return $fullPath;
264
  }
265
-
266
  /**
267
  *
268
  * @return string
@@ -270,24 +266,24 @@ class PostmanUtils {
270
  private static function generateUniqueLockKey() {
271
  // for single sites, use the network_site_url to generate the key because
272
  // it is unique for every wordpress site unlike the blog ID which may be the same
273
- $key = hash ( 'crc32', network_site_url ( '/' ) );
274
  // TODO for multisites
275
  // if the subsite is sharing the config - use the network_site_url of site 0
276
  // if the subsite has its own config - use the network_site_url of the subsite
277
  return $key;
278
  }
279
-
280
  /**
281
  * From http://stackoverflow.com/a/381275/4368109
282
  *
283
- * @param unknown $text
284
  * @return boolean
285
  */
286
- public static function isEmpty($text) {
287
  // Function for basic field validation (present and neither empty nor only white space
288
- return (! isset ( $text ) || trim ( $text ) === '');
289
  }
290
-
291
  /**
292
  * Warning! This can only be called on hook 'init' or later
293
  */
@@ -304,21 +300,21 @@ class PostmanUtils {
304
  *
305
  * Good to know.
306
  */
307
- $logger = PostmanUtils::$logger = new PostmanLogger ( 'PostmanUtils' );
308
- if ($logger->isTrace ()) {
309
- $logger->trace ( 'calling current_user_can' );
310
  }
311
- return current_user_can ( Postman::MANAGE_POSTMAN_CAPABILITY_NAME ) && is_admin ();
312
  }
313
-
314
  /**
315
  * Validate an e-mail address
316
  *
317
- * @param unknown $email
318
  * @return number
319
  */
320
- static function validateEmail($email) {
321
- if (PostmanOptions::getInstance ()->isEmailValidationDisabled ()) {
322
  return true;
323
  }
324
  require_once 'Postman-Mail/Zend-1.12.10/Exception.php';
@@ -329,134 +325,130 @@ class PostmanUtils {
329
  require_once 'Postman-Mail/Zend-1.12.10/Validate/Ip.php';
330
  require_once 'Postman-Mail/Zend-1.12.10/Validate/Hostname.php';
331
  require_once 'Postman-Mail/Zend-1.12.10/Validate/EmailAddress.php';
332
- if (! isset ( PostmanUtils::$emailValidator )) {
333
- PostmanUtils::$emailValidator = new Postman_Zend_Validate_EmailAddress ();
334
  }
335
- return PostmanUtils::$emailValidator->isValid ( $email );
336
  }
337
-
338
  /**
339
  * From http://stackoverflow.com/questions/13430120/str-getcsv-alternative-for-older-php-version-gives-me-an-empty-array-at-the-e
340
  *
341
- * @param unknown $string
342
  * @return multitype:
343
  */
344
- static function postman_strgetcsv_impl($string) {
345
- $fh = fopen ( 'php://temp', 'r+' );
346
- fwrite ( $fh, $string );
347
- rewind ( $fh );
348
-
349
- $row = fgetcsv ( $fh );
350
-
351
- fclose ( $fh );
352
  return $row;
353
  }
354
-
355
  /**
356
  *
357
  * @return Ambigous <string, unknown>
358
  */
359
  static function postmanGetServerName() {
360
- if (! empty ( $_SERVER ['SERVER_NAME'] )) {
361
  $serverName = $_SERVER ['SERVER_NAME'];
362
- } else if (! empty ( $_SERVER ['HTTP_HOST'] )) {
363
  $serverName = $_SERVER ['HTTP_HOST'];
364
  } else {
365
  $serverName = 'localhost.localdomain';
366
  }
367
  return $serverName;
368
  }
369
-
370
  /**
371
  * Does this hostname belong to Google?
372
  *
373
- * @param unknown $hostname
374
  * @return boolean
375
  */
376
- static function isGoogle($hostname) {
377
- return PostmanUtils::endsWith ( $hostname, 'gmail.com' ) || PostmanUtils::endsWith ( $hostname, 'googleapis.com' );
378
  }
379
-
380
  /**
381
  *
382
- * @param unknown $actionName
383
- * @param unknown $callbackName
384
  */
385
- public static function registerAdminMenu($viewController, $callbackName) {
386
  $logger = PostmanUtils::$logger;
387
- if ($logger->isTrace ()) {
388
- $logger->trace ( 'Registering admin menu ' . $callbackName );
389
  }
390
- add_action ( 'admin_menu', array (
391
  $viewController,
392
- $callbackName
393
  ) );
394
  }
395
-
396
  /**
397
  *
398
- * @param unknown $actionName
399
- * @param unknown $callbackName
400
  */
401
- public static function registerAjaxHandler($actionName, $class, $callbackName) {
402
- if (is_admin ()) {
403
  $fullname = 'wp_ajax_' . $actionName;
404
  // $this->logger->debug ( 'Registering ' . 'wp_ajax_' . $fullname . ' Ajax handler' );
405
- add_action ( $fullname, array (
406
  $class,
407
- $callbackName
408
  ) );
409
  }
410
  }
411
-
412
  /**
413
  *
414
- * @param unknown $parameterName
415
  * @return mixed
416
  */
417
- public static function getBooleanRequestParameter($parameterName) {
418
- return filter_var ( $this->getRequestParameter ( $parameterName ), FILTER_VALIDATE_BOOLEAN );
419
  }
420
-
421
  /**
422
  *
423
- * @param unknown $parameterName
424
  * @return unknown
425
  */
426
- public static function getRequestParameter($parameterName) {
427
  $logger = PostmanUtils::$logger;
428
- if (isset ( $_POST [$parameterName] )) {
429
- $value = filter_var( $_POST [$parameterName], FILTER_SANITIZE_STRING );
430
- if ($logger->isTrace ()) {
431
- $logger->trace ( sprintf ( 'Found parameter "%s"', $parameterName ) );
432
- $logger->trace ( $value );
433
  }
434
  return $value;
435
  }
436
  }
437
 
438
  public static function getServerIp() {
439
- $ip = $_SERVER['SERVER_ADDR'];
 
 
 
 
 
 
 
 
 
440
  $serverIp = filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 );
441
 
442
  if ( ! $serverIp ) {
443
- $serverIp = self::get_external_ip();
444
  }
445
 
446
- return $serverIp;
447
  }
448
-
449
- private static function get_external_ip() {
450
- $ch = curl_init("https://postmansmtp.com/ip.php");
451
- curl_setopt($ch, CURLOPT_HEADER, FALSE);
452
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
453
- $result = curl_exec($ch);
454
- curl_close($ch);
455
- if ($result === FALSE) {
456
- return "ERROR";
457
- } else {
458
- return trim($result);
459
- }
460
- }
461
  }
462
- PostmanUtils::staticInit ();
5
  /**
6
  *
7
  * @author jasonhendriks
 
8
  */
9
  class PostmanUtils {
10
  private static $logger;
11
  private static $emailValidator;
12
+
 
13
  const POSTMAN_SETTINGS_PAGE_STUB = 'postman';
14
  const REQUEST_OAUTH2_GRANT_SLUG = 'postman/requestOauthGrant';
15
  const POSTMAN_EMAIL_LOG_PAGE_STUB = 'postman_email_log';
16
+
17
  // redirections back to THIS SITE should always be relative because of IIS bug
18
+ const POSTMAN_EMAIL_LOG_PAGE_RELATIVE_URL = 'admin.php?page=postman_email_log';
19
+ const POSTMAN_HOME_PAGE_RELATIVE_URL = 'admin.php?page=postman';
20
+
21
  // custom admin post page
22
  const ADMIN_POST_OAUTH2_GRANT_URL_PART = 'admin-post.php?action=postman/requestOauthGrant';
23
+
24
+ const NO_ECHO = false;
25
+
 
26
  /**
27
  * Initialize the Logger
28
  */
29
  public static function staticInit() {
30
+ PostmanUtils::$logger = new PostmanLogger( 'PostmanUtils' );
31
  }
32
+
33
  /**
34
  *
35
+ * @param unknown $slug
36
  * @return string
37
  */
38
+ public static function getPageUrl( $slug ) {
39
+ return get_admin_url() . 'admin.php?page=' . $slug;
40
  }
41
+
42
  /**
43
  * Returns an escaped URL
44
  */
45
  public static function getGrantOAuthPermissionUrl() {
46
+ return get_admin_url() . self::ADMIN_POST_OAUTH2_GRANT_URL_PART;
47
  }
48
+
49
  /**
50
  * Returns an escaped URL
51
  */
52
  public static function getEmailLogPageUrl() {
53
+ return menu_page_url( self::POSTMAN_EMAIL_LOG_PAGE_STUB, self::NO_ECHO );
54
  }
55
+
56
  /**
57
  * Returns an escaped URL
58
  */
59
  public static function getSettingsPageUrl() {
60
+ return menu_page_url( self::POSTMAN_SETTINGS_PAGE_STUB, self::NO_ECHO );
61
  }
62
+
63
+ public static function isCurrentPagePostmanAdmin( $page = 'postman' ) {
64
+ $result = (isset( $_REQUEST ['page'] ) && substr( $_REQUEST ['page'], 0, strlen( $page ) ) == $page);
 
65
  return $result;
66
  }
67
  /**
68
  * from http://stackoverflow.com/questions/834303/startswith-and-endswith-functions-in-php
69
  *
70
+ * @param unknown $haystack
71
+ * @param unknown $needle
72
  * @return boolean
73
  */
74
+ public static function startsWith( $haystack, $needle ) {
75
+ $length = strlen( $needle );
76
+ return (substr( $haystack, 0, $length ) === $needle);
77
  }
78
  /**
79
  * from http://stackoverflow.com/questions/834303/startswith-and-endswith-functions-in-php
80
  *
81
+ * @param unknown $haystack
82
+ * @param unknown $needle
83
  * @return boolean
84
  */
85
+ public static function endsWith( $haystack, $needle ) {
86
+ $length = strlen( $needle );
87
+ if ( $length == 0 ) {
88
  return true;
89
  }
90
+ return (substr( $haystack, - $length ) === $needle);
91
  }
92
+ public static function obfuscatePassword( $password ) {
93
+ return str_repeat( '*', strlen( $password ) );
94
  }
95
  /**
96
  * Detect if the host is NOT a domain name
97
  *
98
+ * @param unknown $ipAddress
99
  * @return number
100
  */
101
+ public static function isHostAddressNotADomainName( $host ) {
102
  // IPv4 / IPv6 test from http://stackoverflow.com/a/17871737/4368109
103
+ $ipv6Detected = preg_match( '/(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/', $host );
104
+ $ipv4Detected = preg_match( '/((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])/', $host );
105
  return $ipv4Detected || $ipv6Detected;
106
  // from http://stackoverflow.com/questions/106179/regular-expression-to-match-dns-hostname-or-ip-address
107
  // return preg_match ( '/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9‌​]{2}|2[0-4][0-9]|25[0-5])$/', $ipAddress );
111
  * Inside WordPress we can use wp_remote_post().
112
  * Outside WordPress, not so much.
113
  *
114
+ * @param unknown $url
115
+ * @param unknown $args
116
  * @return the HTML body
117
  */
118
+ static function remotePostGetBodyOnly( $url, $parameters, array $headers = array() ) {
119
+ $response = PostmanUtils::remotePost( $url, $parameters, $headers );
120
+ $theBody = wp_remote_retrieve_body( $response );
121
  return $theBody;
122
  }
123
+
124
  /**
125
  * Makes the outgoing HTTP requests
126
  * Inside WordPress we can use wp_remote_post().
127
  * Outside WordPress, not so much.
128
  *
129
+ * @param unknown $url
130
+ * @param unknown $args
131
  * @return the HTTP response
132
  */
133
+ static function remotePost( $url, $parameters = array(), array $headers = array() ) {
134
+ $args = array(
135
+ 'timeout' => PostmanOptions::getInstance()->getConnectionTimeout(),
136
  'headers' => $headers,
137
+ 'body' => $parameters,
138
  );
139
+ if ( PostmanUtils::$logger->isTrace() ) {
140
+ PostmanUtils::$logger->trace( sprintf( 'Posting to %s', $url ) );
141
+ PostmanUtils::$logger->trace( 'Post header:' );
142
+ PostmanUtils::$logger->trace( $headers );
143
+ PostmanUtils::$logger->trace( 'Posting args:' );
144
+ PostmanUtils::$logger->trace( $parameters );
145
  }
146
+ $response = wp_remote_post( $url, $args );
147
+
148
  // pre-process the response
149
+ if ( is_wp_error( $response ) ) {
150
+ PostmanUtils::$logger->error( $response->get_error_message() );
151
+ throw new Exception( 'Error executing wp_remote_post: ' . $response->get_error_message() );
152
  } else {
153
  return $response;
154
  }
157
  * A facade function that handles redirects.
158
  * Inside WordPress we can use wp_redirect(). Outside WordPress, not so much. **Load it before postman-core.php**
159
  *
160
+ * @param unknown $url
161
  */
162
+ static function redirect( $url ) {
163
  // redirections back to THIS SITE should always be relative because of IIS bug
164
+ if ( PostmanUtils::$logger->isTrace() ) {
165
+ PostmanUtils::$logger->trace( sprintf( "Redirecting to '%s'", $url ) );
166
  }
167
+ wp_redirect( $url );
168
+ exit();
169
  }
170
+ static function parseBoolean( $var ) {
171
+ return filter_var( $var, FILTER_VALIDATE_BOOLEAN );
172
  }
173
+ static function logMemoryUse( $startingMemory, $description ) {
174
+ PostmanUtils::$logger->trace( sprintf( $description . ' memory used: %s', PostmanUtils::roundBytes( memory_get_usage() - $startingMemory ) ) );
175
  }
176
+
177
  /**
178
  * Rounds the bytes returned from memory_get_usage to smaller amounts used IEC binary prefixes
179
  * See http://en.wikipedia.org/wiki/Binary_prefix
180
  *
181
+ * @param unknown $size
182
  * @return string
183
  */
184
+ static function roundBytes( $size ) {
185
+ $unit = array(
186
  'B',
187
  'KiB',
188
  'MiB',
189
  'GiB',
190
  'TiB',
191
+ 'PiB',
192
  );
193
+ return @round( $size / pow( 1024, ($i = floor( log( $size, 1024 ) )) ), 2 ) . ' ' . $unit [ $i ];
194
  }
195
+
196
  /**
197
  * Unblock threads waiting on lock()
198
  */
199
  static function unlock() {
200
+ if ( PostmanState::getInstance()->isFileLockingEnabled() ) {
201
+ PostmanUtils::deleteLockFile();
202
  }
203
  }
204
+
205
  /**
206
  * Processes will block on this method until unlock() is called
207
  * Inspired by http://cubicspot.blogspot.ca/2010/10/forget-flock-and-system-v-semaphores.html
209
  * @throws Exception
210
  */
211
  static function lock() {
212
+ if ( PostmanState::getInstance()->isFileLockingEnabled() ) {
213
  $attempts = 0;
214
  while ( true ) {
215
  // create the semaphore
216
+ $lock = PostmanUtils::createLockFile();
217
+ if ( $lock ) {
218
  // if we got the lock, return
219
  return;
220
  } else {
221
  $attempts ++;
222
+ if ( $attempts >= 10 ) {
223
+ throw new Exception( sprintf( 'Could not create lockfile %s', '/tmp' . '/.postman.lock' ) );
224
  }
225
+ sleep( 1 );
226
  }
227
  }
228
  }
229
  }
230
+ static function deleteLockFile( $tempDirectory = null ) {
231
+ $path = PostmanUtils::calculateTemporaryLockPath( $tempDirectory );
232
+ $success = @unlink( $path );
233
+ if ( PostmanUtils::$logger->isTrace() ) {
234
+ PostmanUtils::$logger->trace( sprintf( 'Deleting file %s : %s', $path, $success ) );
235
  }
236
  return $success;
237
  }
238
+ static function createLockFile( $tempDirectory = null ) {
239
+ $path = PostmanUtils::calculateTemporaryLockPath( $tempDirectory );
240
+ $success = @fopen( $path, 'xb' );
241
+ if ( PostmanUtils::$logger->isTrace() ) {
242
+ PostmanUtils::$logger->trace( sprintf( 'Creating file %s : %s', $path, $success ) );
243
  }
244
  return $success;
245
  }
246
+
247
  /**
248
  * Creates the pathname of the lockfile
249
  *
250
+ * @param unknown $tempDirectory
251
  * @return string
252
  */
253
+ private static function calculateTemporaryLockPath( $tempDirectory ) {
254
+ if ( empty( $tempDirectory ) ) {
255
+ $options = PostmanOptions::getInstance();
256
+ $tempDirectory = $options->getTempDirectory();
257
  }
258
+ $fullPath = sprintf( '%s/.postman_%s.lock', $tempDirectory, self::generateUniqueLockKey() );
259
  return $fullPath;
260
  }
261
+
262
  /**
263
  *
264
  * @return string
266
  private static function generateUniqueLockKey() {
267
  // for single sites, use the network_site_url to generate the key because
268
  // it is unique for every wordpress site unlike the blog ID which may be the same
269
+ $key = hash( 'crc32', network_site_url( '/' ) );
270
  // TODO for multisites
271
  // if the subsite is sharing the config - use the network_site_url of site 0
272
  // if the subsite has its own config - use the network_site_url of the subsite
273
  return $key;
274
  }
275
+
276
  /**
277
  * From http://stackoverflow.com/a/381275/4368109
278
  *
279
+ * @param unknown $text
280
  * @return boolean
281
  */
282
+ public static function isEmpty( $text ) {
283
  // Function for basic field validation (present and neither empty nor only white space
284
+ return ( ! isset( $text ) || trim( $text ) === '');
285
  }
286
+
287
  /**
288
  * Warning! This can only be called on hook 'init' or later
289
  */
300
  *
301
  * Good to know.
302
  */
303
+ $logger = PostmanUtils::$logger = new PostmanLogger( 'PostmanUtils' );
304
+ if ( $logger->isTrace() ) {
305
+ $logger->trace( 'calling current_user_can' );
306
  }
307
+ return current_user_can( Postman::MANAGE_POSTMAN_CAPABILITY_NAME ) && is_admin();
308
  }
309
+
310
  /**
311
  * Validate an e-mail address
312
  *
313
+ * @param unknown $email
314
  * @return number
315
  */
316
+ static function validateEmail( $email ) {
317
+ if ( PostmanOptions::getInstance()->isEmailValidationDisabled() ) {
318
  return true;
319
  }
320
  require_once 'Postman-Mail/Zend-1.12.10/Exception.php';
325
  require_once 'Postman-Mail/Zend-1.12.10/Validate/Ip.php';
326
  require_once 'Postman-Mail/Zend-1.12.10/Validate/Hostname.php';
327
  require_once 'Postman-Mail/Zend-1.12.10/Validate/EmailAddress.php';
328
+ if ( ! isset( PostmanUtils::$emailValidator ) ) {
329
+ PostmanUtils::$emailValidator = new Postman_Zend_Validate_EmailAddress();
330
  }
331
+ return PostmanUtils::$emailValidator->isValid( $email );
332
  }
333
+
334
  /**
335
  * From http://stackoverflow.com/questions/13430120/str-getcsv-alternative-for-older-php-version-gives-me-an-empty-array-at-the-e
336
  *
337
+ * @param unknown $string
338
  * @return multitype:
339
  */
340
+ static function postman_strgetcsv_impl( $string ) {
341
+ $fh = fopen( 'php://temp', 'r+' );
342
+ fwrite( $fh, $string );
343
+ rewind( $fh );
344
+
345
+ $row = fgetcsv( $fh );
346
+
347
+ fclose( $fh );
348
  return $row;
349
  }
350
+
351
  /**
352
  *
353
  * @return Ambigous <string, unknown>
354
  */
355
  static function postmanGetServerName() {
356
+ if ( ! empty( $_SERVER ['SERVER_NAME'] ) ) {
357
  $serverName = $_SERVER ['SERVER_NAME'];
358
+ } else if ( ! empty( $_SERVER ['HTTP_HOST'] ) ) {
359
  $serverName = $_SERVER ['HTTP_HOST'];
360
  } else {
361
  $serverName = 'localhost.localdomain';
362
  }
363
  return $serverName;
364
  }
365
+
366
  /**
367
  * Does this hostname belong to Google?
368
  *
369
+ * @param unknown $hostname
370
  * @return boolean
371
  */
372
+ static function isGoogle( $hostname ) {
373
+ return PostmanUtils::endsWith( $hostname, 'gmail.com' ) || PostmanUtils::endsWith( $hostname, 'googleapis.com' );
374
  }
375
+
376
  /**
377
  *
378
+ * @param unknown $actionName
379
+ * @param unknown $callbackName
380
  */
381
+ public static function registerAdminMenu( $viewController, $callbackName ) {
382
  $logger = PostmanUtils::$logger;
383
+ if ( $logger->isTrace() ) {
384
+ $logger->trace( 'Registering admin menu ' . $callbackName );
385
  }
386
+ add_action( 'admin_menu', array(
387
  $viewController,
388
+ $callbackName,
389
  ) );
390
  }
391
+
392
  /**
393
  *
394
+ * @param unknown $actionName
395
+ * @param unknown $callbackName
396
  */
397
+ public static function registerAjaxHandler( $actionName, $class, $callbackName ) {
398
+ if ( is_admin() ) {
399
  $fullname = 'wp_ajax_' . $actionName;
400
  // $this->logger->debug ( 'Registering ' . 'wp_ajax_' . $fullname . ' Ajax handler' );
401
+ add_action( $fullname, array(
402
  $class,
403
+ $callbackName,
404
  ) );
405
  }
406
  }
407
+
408
  /**
409
  *
410
+ * @param unknown $parameterName
411
  * @return mixed
412
  */
413
+ public static function getBooleanRequestParameter( $parameterName ) {
414
+ return filter_var( self::getRequestParameter( $parameterName ), FILTER_VALIDATE_BOOLEAN );
415
  }
416
+
417
  /**
418
  *
419
+ * @param unknown $parameterName
420
  * @return unknown
421
  */
422
+ public static function getRequestParameter( $parameterName ) {
423
  $logger = PostmanUtils::$logger;
424
+ if ( isset( $_POST [ $parameterName ] ) ) {
425
+ $value = filter_var( $_POST [ $parameterName ], FILTER_SANITIZE_STRING );
426
+ if ( $logger->isTrace() ) {
427
+ $logger->trace( sprintf( 'Found parameter "%s"', $parameterName ) );
428
+ $logger->trace( $value );
429
  }
430
  return $value;
431
  }
432
  }
433
 
434
  public static function getServerIp() {
435
+ $ip = '';
436
+
437
+ if ( strpos( $_SERVER['SERVER_SOFTWARE'], 'iis' ) !== false ) {
438
+ $ip = $_SERVER['LOCAL_ADDR'];
439
+ }
440
+
441
+ if ( empty( $ip ) ) {
442
+ $ip = $_SERVER['SERVER_ADDR'];
443
+ }
444
+
445
  $serverIp = filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 );
446
 
447
  if ( ! $serverIp ) {
448
+ $serverIp = filter_var( gethostbyname( $_SERVER['SERVER_NAME'] ), FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 );
449
  }
450
 
451
+ return $serverIp ? $serverIp : 'localhost';
452
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
453
  }
454
+ PostmanUtils::staticInit();
Postman/PostmanViewController.php CHANGED
@@ -56,7 +56,7 @@ if ( ! class_exists( 'PostmanViewController' ) ) {
56
  $this,
57
  'outputDefaultContent',
58
  );
59
- $mainPostmanSettingsPage = add_options_page( $pageTitle, $pluginName, Postman::MANAGE_POSTMAN_CAPABILITY_NAME, $uniqueId, $pageOptions );
60
  // When the plugin options page is loaded, also load the stylesheet
61
  add_action( 'admin_print_styles-' . $mainPostmanSettingsPage, array(
62
  $this,
@@ -97,7 +97,9 @@ if ( ! class_exists( 'PostmanViewController' ) ) {
97
  wp_register_style( 'jquery_steps_style', plugins_url( 'style/jquery-steps/jquery.steps.css', $this->rootPluginFilenameAndPath ), PostmanViewController::POSTMAN_STYLE, '1.1.0' );
98
 
99
  wp_register_script( PostmanViewController::POSTMAN_SCRIPT, plugins_url( 'script/postman.js', $this->rootPluginFilenameAndPath ), array(
100
- PostmanViewController::JQUERY_SCRIPT
 
 
101
  ), $pluginData ['version'] );
102
  wp_register_script( 'sprintf', plugins_url( 'script/sprintf/sprintf.min.js', $this->rootPluginFilenameAndPath ), null, '1.0.2' );
103
  wp_register_script( 'jquery_steps_script', plugins_url( 'script/jquery-steps/jquery.steps.min.js', $this->rootPluginFilenameAndPath ), array(
@@ -159,7 +161,7 @@ if ( ! class_exists( 'PostmanViewController' ) ) {
159
  if ( PostmanState::getInstance()->isTimeToReviewPostman() && ! PostmanOptions::getInstance()->isNew() ) {
160
  print '</br><hr width="70%"></br>';
161
  /* translators: where %s is the URL to the WordPress.org review and ratings page */
162
- printf( '%s</span></p>', sprintf( __( 'Please consider <a href="%s">leaving a review</a> to help spread the word! :D', Postman::TEXT_DOMAIN ), 'https://wordpress.org/support/view/plugin-reviews/postman-smtp?filter=5' ) );
163
  }
164
  printf( '<p><span>%s :-)</span></p>', sprintf( __( 'Postman needs translators! Please take a moment to <a href="%s">translate a few sentences on-line</a>', Postman::TEXT_DOMAIN ), 'https://translate.wordpress.org/projects/wp-plugins/post-smtp/stable' ) );
165
  }
@@ -292,7 +294,8 @@ if ( ! class_exists( 'PostmanViewController' ) ) {
292
  print '<ul>';
293
  printf( '<li><a href="%s" class="welcome-icon run-port-test">%s</a></li>', $this->getPageUrl( PostmanConnectivityTestController::PORT_TEST_SLUG ), __( 'Connectivity Test', Postman::TEXT_DOMAIN ) );
294
  printf( '<li><a href="%s" class="welcome-icon run-port-test">%s</a></li>', $this->getPageUrl( PostmanDiagnosticTestController::DIAGNOSTICS_SLUG ), __( 'Diagnostic Test', Postman::TEXT_DOMAIN ) );
295
- printf( '<li><a href="https://wordpress.org/support/plugin/postman-smtp" class="welcome-icon postman_support">%s</a></li>', __( 'Online Support', Postman::TEXT_DOMAIN ) );
 
296
  print '</ul></div></div></div></div>';
297
  }
298
  }
56
  $this,
57
  'outputDefaultContent',
58
  );
59
+ $mainPostmanSettingsPage = add_menu_page( $pageTitle, $pluginName, Postman::MANAGE_POSTMAN_CAPABILITY_NAME, $uniqueId, $pageOptions );
60
  // When the plugin options page is loaded, also load the stylesheet
61
  add_action( 'admin_print_styles-' . $mainPostmanSettingsPage, array(
62
  $this,
97
  wp_register_style( 'jquery_steps_style', plugins_url( 'style/jquery-steps/jquery.steps.css', $this->rootPluginFilenameAndPath ), PostmanViewController::POSTMAN_STYLE, '1.1.0' );
98
 
99
  wp_register_script( PostmanViewController::POSTMAN_SCRIPT, plugins_url( 'script/postman.js', $this->rootPluginFilenameAndPath ), array(
100
+ PostmanViewController::JQUERY_SCRIPT,
101
+ 'jquery-ui-core',
102
+ 'jquery-ui-datepicker',
103
  ), $pluginData ['version'] );
104
  wp_register_script( 'sprintf', plugins_url( 'script/sprintf/sprintf.min.js', $this->rootPluginFilenameAndPath ), null, '1.0.2' );
105
  wp_register_script( 'jquery_steps_script', plugins_url( 'script/jquery-steps/jquery.steps.min.js', $this->rootPluginFilenameAndPath ), array(
161
  if ( PostmanState::getInstance()->isTimeToReviewPostman() && ! PostmanOptions::getInstance()->isNew() ) {
162
  print '</br><hr width="70%"></br>';
163
  /* translators: where %s is the URL to the WordPress.org review and ratings page */
164
+ printf( '%s</span></p>', sprintf( __( 'Please consider <a href="%s">leaving a review</a> to help spread the word! :D', Postman::TEXT_DOMAIN ), 'https://wordpress.org/support/view/plugin-reviews/post-smtp?filter=5' ) );
165
  }
166
  printf( '<p><span>%s :-)</span></p>', sprintf( __( 'Postman needs translators! Please take a moment to <a href="%s">translate a few sentences on-line</a>', Postman::TEXT_DOMAIN ), 'https://translate.wordpress.org/projects/wp-plugins/post-smtp/stable' ) );
167
  }
294
  print '<ul>';
295
  printf( '<li><a href="%s" class="welcome-icon run-port-test">%s</a></li>', $this->getPageUrl( PostmanConnectivityTestController::PORT_TEST_SLUG ), __( 'Connectivity Test', Postman::TEXT_DOMAIN ) );
296
  printf( '<li><a href="%s" class="welcome-icon run-port-test">%s</a></li>', $this->getPageUrl( PostmanDiagnosticTestController::DIAGNOSTICS_SLUG ), __( 'Diagnostic Test', Postman::TEXT_DOMAIN ) );
297
+ printf( '<li><a href="https://postmansmtp.com/forums/" class="welcome-icon postman_support">%s</a></li>', __( 'Online Support', Postman::TEXT_DOMAIN ) );
298
+ printf( '<li><img class="align-middle" src="' . plugins_url( 'style/images/new.gif', dirname( __DIR__ ) . '/postman-smtp.php' ) . '"><a class="align-middle" href="https://postmansmtp.com/category/guides/" class="welcome-icon postman_guides">%s</a></li>', __( 'Guides', Postman::TEXT_DOMAIN ) );
299
  print '</ul></div></div></div></div>';
300
  }
301
  }
postman-smtp.php CHANGED
@@ -4,7 +4,7 @@
4
  * Plugin Name: Post SMTP
5
  * Plugin URI: https://wordpress.org/plugins/post-smtp/
6
  * Description: Email not reliable? Post SMTP is the first and only WordPress SMTP plugin to implement OAuth 2.0 for Gmail, Hotmail and Yahoo Mail. Setup is a breeze with the Configuration Wizard and integrated Port Tester. Enjoy worry-free delivery even if your password changes!
7
- * Version: 1.7.7
8
  * Author: Jason Hendriks, Yehuda Hassine
9
  * Text Domain: post-smtp
10
  * Author URI: https://postmansmtp.com
@@ -35,6 +35,7 @@
35
  * DO some check and Start Postman
36
  */
37
 
 
38
 
39
  if ( in_array( 'postman-smtp/postman-smtp.php', (array) get_option( 'active_plugins', array() ) ) ) {
40
  add_action( 'admin_init', 'post_smtp_plugin_deactivate' );
@@ -70,5 +71,5 @@ function post_start( $startingMemory ) {
70
  */
71
  function post_setupPostman() {
72
  require_once 'Postman/Postman.php';
73
- $kevinCostner = new Postman( __FILE__, '1.7.7' );
74
  }
4
  * Plugin Name: Post SMTP
5
  * Plugin URI: https://wordpress.org/plugins/post-smtp/
6
  * Description: Email not reliable? Post SMTP is the first and only WordPress SMTP plugin to implement OAuth 2.0 for Gmail, Hotmail and Yahoo Mail. Setup is a breeze with the Configuration Wizard and integrated Port Tester. Enjoy worry-free delivery even if your password changes!
7
+ * Version: 1.7.8
8
  * Author: Jason Hendriks, Yehuda Hassine
9
  * Text Domain: post-smtp
10
  * Author URI: https://postmansmtp.com
35
  * DO some check and Start Postman
36
  */
37
 
38
+ define( 'POST_BASE', __FILE__ );
39
 
40
  if ( in_array( 'postman-smtp/postman-smtp.php', (array) get_option( 'active_plugins', array() ) ) ) {
41
  add_action( 'admin_init', 'post_smtp_plugin_deactivate' );
71
  */
72
  function post_setupPostman() {
73
  require_once 'Postman/Postman.php';
74
+ $kevinCostner = new Postman( __FILE__, '1.7.8' );
75
  }
readme.txt CHANGED
@@ -1,16 +1,19 @@
1
  === Post SMTP Mailer/Email Log ===
2
  Contributors: yehudah, jasonhendriks
3
- Tags: postman smtp, postman, smtp, email, mail, mailer, email log, oauth2, gmail, google apps, hotmail, yahoo, mandrill api, sendgrid api, sparkpost api
4
  Requires at least: 3.9
5
- Tested up to: 4.8
6
- Stable tag: 1.7.7
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
10
- Send, log and troubleshoot your Outgoing Email easily. Supports everything: SMTP, Gmail, Mandril, SendGrid and OAuth 2.0!
11
 
12
  == Description ==
13
 
 
 
 
14
  = Postman SMTP is back! =
15
  Now under new maintenance, no need to search for another SMTP plugin anymore.
16
 
@@ -22,8 +25,6 @@ https://www.youtube.com/watch?v=z-x1DhcAN0o
22
 
23
  Post is a next-generation [SMTP Mailer](https://wordpress.org/plugins/search.php?q=smtp), software that assists in the delivery of email generated by your WordPress site. Post is the first and only plugin to support the [latest security standards](http://googleonlinesecurity.blogspot.ca/2014/04/new-security-measures-will-affect-older.html). With OAuth 2.0, there is **no need** to [store your email passsword](http://blog.codinghorror.com/youre-probably-storing-passwords-incorrectly/) in the WordPress database where it might be found.
24
 
25
- > Post is one year old! SparkPost API Integration is coming soon as Mandrill is no longer offering a free service.
26
-
27
  The **Connectivity Test** and intelligent **Setup Wizard** scan your SMTP server to detect firewall blocks and eliminate configuration mistakes. The built-in **Email Log** is an invaluable resource for [diagnosing problems](https://wordpress.org/support/topic/ugly-e-mails-no-html-and-no-special-characters?replies=15) with emails. Even hosts that block the standard SMTP ports, like GoDaddy or Bluehost, can't stop your email as **Post can deliver via HTTPS** if it can't use SMTP.
28
 
29
  Post is *not* another WP Mail SMTP clone like WP Bank or Easy SMTP. It replaces the default WordPress SMTP library, PHPMailer, with the heavy-duty Zend_Mail. Never [lose an email to PHP mail()](http://www.jvfconsulting.com/blog/php-mail-function-vs-smtp-guaranteed-delivery/) again.
@@ -49,7 +50,7 @@ Post is *not* another WP Mail SMTP clone like WP Bank or Easy SMTP. It replaces
49
  * COMING SOON: Sparkpost API for sending any email (requires a [Sparkpost](https://www.sparkpost.com) account)
50
  * SendGrid API for sending any email (requires a [SendGrid](https://sendgrid.com) account and PHP 5.3)
51
 
52
- > Post needs [translators](https://translate.wordpress.org/projects/wp-plugins/Post-smtp/stable)! If you are a non-English speaker, please get involved!
53
 
54
  = Compatibile With.. =
55
  * [Woocommerce](https://wordpress.org/plugins/woocommerce/)
@@ -110,7 +111,7 @@ Post is *not* another WP Mail SMTP clone like WP Bank or Easy SMTP. It replaces
110
  1. In 'Outgoing Mail Server Port', enter the SMTP Server's port
111
  1. In 'Security' choose the appropriate type (a good guess is SMTPS for port 465, StartTLS otherwise)
112
  1. In 'Authentication' choose 'OAuth 2.0'
113
- 1. Post will give you a link to the Client ID maintenance page of your email service provider. Create a Client ID for your WordPress site.. [instructions for this are detailed in the FAQ](https://wordpress.org/plugins/Post-smtp/faq/)
114
  1. Copy your generated Client ID and Client secret into the plugin's Settings page.
115
  1. Choose the 'Message' tab.
116
  1. In 'Envelope From Address' enter your email address. This MUST be the same address you login to webmail with.
@@ -280,6 +281,16 @@ To avoid being flagged as spam, you need to prove your email isn't forged. On a
280
 
281
 
282
  == Changelog ==
 
 
 
 
 
 
 
 
 
 
283
  = 1.7.7 - 2017-10-17
284
  * Fixed: Error sending files with sendgrid
285
  * Fixed: Wrong attachments format in Mandrill
1
  === Post SMTP Mailer/Email Log ===
2
  Contributors: yehudah, jasonhendriks
3
+ Tags: postman smtp, postman, smtp, email, mail, mailer, email log, oauth2, gmail, google apps, hotmail, yahoo, mandrill api, sendgrid api, elastic email
4
  Requires at least: 3.9
5
+ Tested up to: 4.9
6
+ Stable tag: 1.7.8
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
10
+ Send, log and troubleshoot your Outgoing Email easily. Supports everything: SMTP, Gmail, Mandril, SendGrid, Elastic Email and OAuth 2.0!
11
 
12
  == Description ==
13
 
14
+ = From now all the support is made on Post SMTP forums =
15
+ https://postmansmtp.com/forums/
16
+
17
  = Postman SMTP is back! =
18
  Now under new maintenance, no need to search for another SMTP plugin anymore.
19
 
25
 
26
  Post is a next-generation [SMTP Mailer](https://wordpress.org/plugins/search.php?q=smtp), software that assists in the delivery of email generated by your WordPress site. Post is the first and only plugin to support the [latest security standards](http://googleonlinesecurity.blogspot.ca/2014/04/new-security-measures-will-affect-older.html). With OAuth 2.0, there is **no need** to [store your email passsword](http://blog.codinghorror.com/youre-probably-storing-passwords-incorrectly/) in the WordPress database where it might be found.
27
 
 
 
28
  The **Connectivity Test** and intelligent **Setup Wizard** scan your SMTP server to detect firewall blocks and eliminate configuration mistakes. The built-in **Email Log** is an invaluable resource for [diagnosing problems](https://wordpress.org/support/topic/ugly-e-mails-no-html-and-no-special-characters?replies=15) with emails. Even hosts that block the standard SMTP ports, like GoDaddy or Bluehost, can't stop your email as **Post can deliver via HTTPS** if it can't use SMTP.
29
 
30
  Post is *not* another WP Mail SMTP clone like WP Bank or Easy SMTP. It replaces the default WordPress SMTP library, PHPMailer, with the heavy-duty Zend_Mail. Never [lose an email to PHP mail()](http://www.jvfconsulting.com/blog/php-mail-function-vs-smtp-guaranteed-delivery/) again.
50
  * COMING SOON: Sparkpost API for sending any email (requires a [Sparkpost](https://www.sparkpost.com) account)
51
  * SendGrid API for sending any email (requires a [SendGrid](https://sendgrid.com) account and PHP 5.3)
52
 
53
+ > Post needs [translators](https://translate.wordpress.org/projects/wp-plugins/post-smtp/stable)! If you are a non-English speaker, please get involved!
54
 
55
  = Compatibile With.. =
56
  * [Woocommerce](https://wordpress.org/plugins/woocommerce/)
111
  1. In 'Outgoing Mail Server Port', enter the SMTP Server's port
112
  1. In 'Security' choose the appropriate type (a good guess is SMTPS for port 465, StartTLS otherwise)
113
  1. In 'Authentication' choose 'OAuth 2.0'
114
+ 1. Post will give you a link to the Client ID maintenance page of your email service provider. Create a Client ID for your WordPress site.. [instructions for this are detailed in the FAQ](https://wordpress.org/plugins/post-smtp/faq/)
115
  1. Copy your generated Client ID and Client secret into the plugin's Settings page.
116
  1. Choose the 'Message' tab.
117
  1. In 'Envelope From Address' enter your email address. This MUST be the same address you login to webmail with.
281
 
282
 
283
  == Changelog ==
284
+ = 1.7.8 - 2017-11-17
285
+ * = Menu Items grouping =
286
+ * Fixed: IP detection error in some web hosts
287
+ * Fixed: Link open in new page attribute = _blank
288
+ * Fixed: Replace deprecated PHP 7 functions.
289
+ * Updated: Validator TLD's list
290
+ * Added: Email log date and search filter.
291
+ * Added: Alert on sending error (Fallback to local mail)
292
+ * Added: Email body preview (not raw)
293
+
294
  = 1.7.7 - 2017-10-17
295
  * Fixed: Error sending files with sendgrid
296
  * Fixed: Wrong attachments format in Mandrill
script/feedback/feedback.js ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).ready(function($) {
2
+
3
+ $( '#the-list #postman-plugin-disbale-link' ).click(function(e) {
4
+ e.preventDefault();
5
+
6
+ var reason = $( '#postman-feedback-dialog-content .postman-reason' ),
7
+ deactivateLink = $( this ).attr( 'href' );
8
+
9
+ $( "#postman-feedback-dialog-content" ).dialog({
10
+ title: 'Post SMTP Feedback Form',
11
+ dialogClass: 'postman-feedback-dialog-form',
12
+ resizable: false,
13
+ minWidth: 400,
14
+ minHeight: 300,
15
+ modal: true,
16
+ buttons: {
17
+ 'skip' : {
18
+ text: 'Skip',
19
+ id: 'postman-feedback-dialog-skip',
20
+ click: function() {
21
+ $( this ).dialog( "close" );
22
+
23
+ location.href = deactivateLink;
24
+ }
25
+ },
26
+ 'go' : {
27
+ text: 'Continue',
28
+ id: 'postman-feedback-dialog-go',
29
+ class: 'button',
30
+ click: function() {
31
+ $( this ).dialog( "close" );
32
+
33
+ var form = $( this ).find( 'form' ).serializeArray(),
34
+ result = {};
35
+
36
+ $.each( form, function() {
37
+ if ( '' !== this.value )
38
+ result[ this.name ] = this.value;
39
+ });
40
+
41
+ if ( ! jQuery.isEmptyObject( result ) ) {
42
+ result.action = 'post_user_feedback';
43
+
44
+ $.post( post_feedback.admin_ajax, result, function(result) {
45
+
46
+ });
47
+ }
48
+
49
+ // Remove this comment to deactivate plugin
50
+ location.href = deactivateLink;
51
+ },
52
+ },
53
+ 'cancel' : {
54
+ text: 'Cancel',
55
+ id: 'postman-feedback-dialog-cancel',
56
+ class: 'button button-primary',
57
+ click: function() {
58
+ $( this ).dialog( "close" );
59
+ }
60
+ }
61
+ }
62
+ });
63
+
64
+ reason.change(function() {
65
+ $( '.postman-reason-input' ).hide();
66
+
67
+ if ( $( this ).hasClass( 'postman-custom-input' ) ) {
68
+ $( this ).find( '.postman-reason-input' ).show();
69
+ }
70
+ });
71
+
72
+ });
73
+ });
script/postman.js CHANGED
@@ -1,3 +1,7 @@
 
 
 
 
1
  var redirectUrlWarning = false;
2
  if (!console)
3
  console = {
1
+ jQuery(document).ready(function($) {
2
+ $( ".email-log-date" ).datepicker();
3
+ });
4
+
5
  var redirectUrlWarning = false;
6
  if (!console)
7
  console = {
style/images/new.gif ADDED
Binary file
style/postman-email-log.css CHANGED
@@ -3,3 +3,366 @@
3
  th#date {
4
  width:15%;
5
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  th#date {
4
  width:15%;
5
  }
6
+
7
+ #email-log-filter {
8
+ padding: 20px;
9
+ display: flex;
10
+ justify-content: space-around;
11
+ align-items: center;
12
+ border-bottom: 1px solid #ddd;
13
+ }
14
+
15
+ #email-log-filter label {
16
+ font-weight: bold;
17
+ font-size: 1em;
18
+ }
19
+
20
+ #email-log-filter input {
21
+ padding: 10px;
22
+ border-radius: 5px;
23
+ }
24
+
25
+
26
+ /* Date Picker Default Styles */
27
+ .ui-datepicker {
28
+ padding: 0;
29
+ margin: 0;
30
+ -webkit-border-radius: 0;
31
+ -moz-border-radius: 0;
32
+ border-radius: 0;
33
+ background-color: #fff;
34
+ border: 1px solid #dfdfdf;
35
+ border-top: none;
36
+ -webkit-box-shadow: 0 3px 6px rgba(0, 0, 0, 0.075);
37
+ box-shadow: 0 3px 6px rgba(0, 0, 0, 0.075);
38
+ min-width: 17em;
39
+ width: auto;
40
+ }
41
+
42
+ .ui-datepicker * {
43
+ padding: 0;
44
+ font-family: "Open Sans", sans-serif;
45
+ -webkit-border-radius: 0;
46
+ -moz-border-radius: 0;
47
+ border-radius: 0;
48
+ }
49
+
50
+ .ui-datepicker table {
51
+ font-size: 13px;
52
+ margin: 0;
53
+ border: none;
54
+ border-collapse: collapse;
55
+ }
56
+
57
+ .ui-datepicker .ui-widget-header,
58
+ .ui-datepicker .ui-datepicker-header {
59
+ background-image: none;
60
+ border: none;
61
+ color: #fff;
62
+ font-weight: normal;
63
+ }
64
+
65
+ .ui-datepicker .ui-datepicker-header .ui-state-hover {
66
+ background: transparent;
67
+ border-color: transparent;
68
+ cursor: pointer;
69
+ }
70
+
71
+ .ui-datepicker .ui-datepicker-title {
72
+ margin: 0;
73
+ padding: 10px 0;
74
+ color: #fff;
75
+ font-size: 14px;
76
+ line-height: 14px;
77
+ text-align: center;
78
+ }
79
+
80
+ .ui-datepicker .ui-datepicker-prev,
81
+ .ui-datepicker .ui-datepicker-next {
82
+ position: relative;
83
+ top: 0;
84
+ height: 34px;
85
+ width: 34px;
86
+ }
87
+
88
+ .ui-datepicker .ui-state-hover.ui-datepicker-prev,
89
+ .ui-datepicker .ui-state-hover.ui-datepicker-next {
90
+ border: none;
91
+ }
92
+
93
+ .ui-datepicker .ui-datepicker-prev,
94
+ .ui-datepicker .ui-datepicker-prev-hover {
95
+ left: 0;
96
+ }
97
+
98
+ .ui-datepicker .ui-datepicker-next,
99
+ .ui-datepicker .ui-datepicker-next-hover {
100
+ right: 0;
101
+ }
102
+
103
+ .ui-datepicker .ui-datepicker-next span,
104
+ .ui-datepicker .ui-datepicker-prev span {
105
+ display: none;
106
+ }
107
+
108
+ .ui-datepicker .ui-datepicker-prev {
109
+ float: left;
110
+ }
111
+
112
+ .ui-datepicker .ui-datepicker-next {
113
+ float: right;
114
+ }
115
+
116
+ .ui-datepicker .ui-datepicker-prev:before,
117
+ .ui-datepicker .ui-datepicker-next:before {
118
+ font: normal 20px/34px 'dashicons';
119
+ padding-left: 7px;
120
+ color: #fff;
121
+ speak: none;
122
+ -webkit-font-smoothing: antialiased;
123
+ -moz-osx-font-smoothing: grayscale;
124
+ width: 34px;
125
+ height: 34px;
126
+ }
127
+
128
+ .ui-datepicker .ui-datepicker-prev:before {
129
+ content: '\f341';
130
+ }
131
+
132
+ .ui-datepicker .ui-datepicker-next:before {
133
+ content: '\f345';
134
+ }
135
+
136
+ .ui-datepicker .ui-datepicker-prev-hover:before,
137
+ .ui-datepicker .ui-datepicker-next-hover:before {
138
+ opacity: 0.7;
139
+ }
140
+
141
+ .ui-datepicker select.ui-datepicker-month,
142
+ .ui-datepicker select.ui-datepicker-year {
143
+ width: 33%;
144
+ }
145
+
146
+ .ui-datepicker thead {
147
+ color: #fff;
148
+ font-weight: 600;
149
+ }
150
+
151
+ .ui-datepicker th {
152
+ padding: 10px;
153
+ }
154
+
155
+ .ui-datepicker td {
156
+ padding: 0;
157
+ border: 1px solid #f4f4f4;
158
+ }
159
+
160
+ .ui-datepicker td.ui-datepicker-other-month {
161
+ border: transparent;
162
+ }
163
+
164
+ .ui-datepicker td.ui-datepicker-week-end {
165
+ background-color: #f4f4f4;
166
+ border: 1px solid #f4f4f4;
167
+ }
168
+
169
+ .ui-datepicker td.ui-datepicker-today {
170
+ background-color: #f0f0c0;
171
+ }
172
+
173
+ .ui-datepicker td.ui-datepicker-current-day {
174
+ background: #bbdd88;
175
+ }
176
+
177
+ .ui-datepicker td .ui-state-default {
178
+ background: transparent;
179
+ border: none;
180
+ text-align: center;
181
+ text-decoration: none;
182
+ width: auto;
183
+ display: block;
184
+ padding: 5px 10px;
185
+ font-weight: normal;
186
+ color: #444;
187
+ }
188
+
189
+ .ui-datepicker td.ui-state-disabled .ui-state-default {
190
+ opacity: 0.5;
191
+ }
192
+
193
+ /* Default Color Scheme */
194
+ .ui-datepicker .ui-widget-header,
195
+ .ui-datepicker .ui-datepicker-header {
196
+ background: #00a0d2;
197
+ }
198
+
199
+ .ui-datepicker thead {
200
+ background: #32373c;
201
+ }
202
+
203
+ .ui-datepicker td .ui-state-hover {
204
+ background: #0073aa;
205
+ color: #fff;
206
+ }
207
+
208
+ /* WordPress Color Schemes */
209
+
210
+ /* Fresh */
211
+ .admin-color-fresh .ui-datepicker .ui-widget-header,
212
+ .admin-color-fresh .ui-datepicker .ui-datepicker-header {
213
+ background: #00a0d2;
214
+ }
215
+
216
+ .admin-color-fresh .ui-datepicker thead {
217
+ background: #32373c;
218
+ }
219
+
220
+ .admin-color-fresh .ui-datepicker td .ui-state-hover {
221
+ background: #0073aa;
222
+ color: #fff;
223
+ }
224
+
225
+ /* Blue */
226
+ .admin-color-blue .ui-datepicker .ui-widget-header,
227
+ .admin-color-blue .ui-datepicker .ui-datepicker-header {
228
+ background: #52accc;
229
+ }
230
+
231
+ .admin-color-blue .ui-datepicker thead {
232
+ background: #4796b3;
233
+ }
234
+
235
+ .admin-color-blue .ui-datepicker td .ui-state-hover {
236
+ background: #096484;
237
+ color: #fff;
238
+ }
239
+
240
+ /* Coffee */
241
+ .admin-color-coffee .ui-datepicker .ui-widget-header,
242
+ .admin-color-coffee .ui-datepicker .ui-datepicker-header {
243
+ background: #59524c;
244
+ }
245
+
246
+ .admin-color-coffee .ui-datepicker thead {
247
+ background: #46403c;
248
+ }
249
+
250
+ .admin-color-coffee .ui-datepicker td .ui-state-hover {
251
+ background: #c7a589;
252
+ color: #fff;
253
+ }
254
+
255
+ /* Ectoplasm */
256
+ .admin-color-ectoplasm .ui-datepicker .ui-widget-header,
257
+ .admin-color-ectoplasm .ui-datepicker .ui-datepicker-header {
258
+ background: #523f6d;
259
+ }
260
+
261
+ .admin-color-ectoplasm .ui-datepicker thead {
262
+ background: #413256;
263
+ }
264
+
265
+ .admin-color-ectoplasm .ui-datepicker td .ui-state-hover {
266
+ background: #a3b745;
267
+ color: #fff;
268
+ }
269
+
270
+ /* Midnight */
271
+ .admin-color-midnight .ui-datepicker .ui-widget-header,
272
+ .admin-color-midnight .ui-datepicker .ui-datepicker-header {
273
+ background: #363b3f;
274
+ }
275
+
276
+ .admin-color-midnight .ui-datepicker thead {
277
+ background: #26292c;
278
+ }
279
+
280
+ .admin-color-midnight .ui-datepicker td .ui-state-hover {
281
+ background: #e14d43;
282
+ color: #fff;
283
+ }
284
+
285
+ /* Ocean */
286
+ .admin-color-ocean .ui-datepicker .ui-widget-header,
287
+ .admin-color-ocean .ui-datepicker .ui-datepicker-header {
288
+ background: #738e96;
289
+ }
290
+
291
+ .admin-color-ocean .ui-datepicker thead {
292
+ background: #627c83;
293
+ }
294
+
295
+ .admin-color-ocean .ui-datepicker td .ui-state-hover {
296
+ background: #9ebaa0;
297
+ color: #fff;
298
+ }
299
+
300
+ /* Sunrise */
301
+ .admin-color-sunrise .ui-datepicker .ui-widget-header,
302
+ .admin-color-sunrise .ui-datepicker .ui-datepicker-header,
303
+ .admin-color-sunrise .ui-datepicker .ui-datepicker-header .ui-state-hover {
304
+ background: #cf4944;
305
+ }
306
+
307
+ .admin-color-sunrise .ui-datepicker th {
308
+ border-color: #be3631;
309
+ background: #be3631;
310
+ }
311
+
312
+ .admin-color-sunrise .ui-datepicker td .ui-state-hover {
313
+ background: #dd823b;
314
+ color: #fff;
315
+ }
316
+
317
+ /* Light */
318
+ .admin-color-light .ui-datepicker .ui-widget-header,
319
+ .admin-color-light .ui-datepicker .ui-datepicker-header {
320
+ background: #e5e5e5;
321
+ }
322
+
323
+ .admin-color-light .ui-datepicker thead {
324
+ background: #888;
325
+ }
326
+
327
+ .admin-color-light .ui-datepicker .ui-datepicker-title,
328
+ .admin-color-light .ui-datepicker td .ui-state-default,
329
+ .admin-color-light .ui-datepicker .ui-datepicker-prev:before,
330
+ .admin-color-light .ui-datepicker .ui-datepicker-next:before {
331
+ color: #555;
332
+ }
333
+
334
+ .admin-color-light .ui-datepicker td .ui-state-hover {
335
+ background: #e5e5e5;
336
+ }
337
+
338
+ /* bbPress Color Schemes */
339
+
340
+ /* Evergreen */
341
+ .admin-color-bbp-evergreen .ui-datepicker .ui-widget-header,
342
+ .admin-color-bbp-evergreen .ui-datepicker .ui-datepicker-header {
343
+ background: #56b274;
344
+ }
345
+
346
+ .admin-color-bbp-evergreen .ui-datepicker thead {
347
+ background: #36533f;
348
+ }
349
+
350
+ .admin-color-bbp-evergreen .ui-datepicker td .ui-state-hover {
351
+ background: #446950;
352
+ color: #fff;
353
+ }
354
+
355
+ /* Mint */
356
+ .admin-color-bbp-mint .ui-datepicker .ui-widget-header,
357
+ .admin-color-bbp-mint .ui-datepicker .ui-datepicker-header {
358
+ background: #4ca26a;
359
+ }
360
+
361
+ .admin-color-bbp-mint .ui-datepicker thead {
362
+ background: #4f6d59;
363
+ }
364
+
365
+ .admin-color-bbp-mint .ui-datepicker td .ui-state-hover {
366
+ background: #5fb37c;
367
+ color: #fff;
368
+ }
style/postman.css CHANGED
@@ -81,4 +81,46 @@ p#back_to_main_menu {
81
 
82
  .welcome-panel-column welcome-panel-last {
83
  padding-bottom: 10px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  }
81
 
82
  .welcome-panel-column welcome-panel-last {
83
  padding-bottom: 10px;
84
+ }
85
+
86
+ .new-item {
87
+ display: inline-block;
88
+ padding: 5px;
89
+ border-radius: 20px;
90
+ background-color: red;
91
+ margin-right: 10px;
92
+ }
93
+
94
+ .new-item span {
95
+ font-weight: bold;
96
+ color: white;
97
+ animation: pulse 2s linear 0s infinite;
98
+ }
99
+
100
+ .align-middle {
101
+ display: inline-block;
102
+ vertical-align: middle;
103
+ }
104
+
105
+
106
+ @keyframes pulse {
107
+ 0% {
108
+ opacity: 0;
109
+ }
110
+
111
+ 25% {
112
+ opacity: 0.25;
113
+ }
114
+
115
+ 50% {
116
+ opacity: 0.5;
117
+ }
118
+
119
+ 75% {
120
+ opacity: 0.75;
121
+ }
122
+
123
+ 100% {
124
+ opacity: 1;
125
+ }
126
  }