Spam protection, AntiSpam, FireWall by CleanTalk - Version 2.19

Version Description

2013-11-08 = * New: Antispam protection from spam bots at the registration form * Changed: Russian localization for admin panel * Changed: PHP code optimizations

Download this release

Release Info

Developer shagimuratov
Plugin Icon 128x128 Spam protection, AntiSpam, FireWall by CleanTalk
Version 2.19
Comparing to
See all releases

Code changes from version 2.5.18 to 2.19

cleantalk.class.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * Cleantalk base class
4
  *
5
- * @version 1.21.9
6
  * @package Cleantalk
7
  * @subpackage Base
8
  * @author Сleantalk team (welcome@cleantalk.ru)
@@ -154,7 +154,7 @@ class CleantalkResponse {
154
  $this->stop_words = isset($obj->stop_words) ? $obj->stop_words : null;
155
  $this->comment = isset($obj->comment) ? utf8_decode($obj->comment) : null;
156
  $this->blacklisted = (isset($obj->blacklisted)) ? $obj->blacklisted : null;
157
- $this->allow = (isset($obj->allow)) ? $obj->allow : null;
158
  $this->id = (isset($obj->id)) ? $obj->id : null;
159
  $this->fast_submit = (isset($obj->fast_submit)) ? $obj->fast_submit : 0;
160
  $this->spam = (isset($obj->spam)) ? $obj->spam : 0;
@@ -508,9 +508,6 @@ class Cleantalk {
508
  break;
509
 
510
  case 'check_newuser':
511
- if (empty($request->sender_nickname)) {
512
- $error_params[] = 'sender_nickname';
513
- }
514
  if (empty($request->sender_email)) {
515
  $error_params[] = 'sender_email';
516
  }
2
  /**
3
  * Cleantalk base class
4
  *
5
+ * @version 1.21.11
6
  * @package Cleantalk
7
  * @subpackage Base
8
  * @author Сleantalk team (welcome@cleantalk.ru)
154
  $this->stop_words = isset($obj->stop_words) ? $obj->stop_words : null;
155
  $this->comment = isset($obj->comment) ? utf8_decode($obj->comment) : null;
156
  $this->blacklisted = (isset($obj->blacklisted)) ? $obj->blacklisted : null;
157
+ $this->allow = (isset($obj->allow)) ? $obj->allow : 0;
158
  $this->id = (isset($obj->id)) ? $obj->id : null;
159
  $this->fast_submit = (isset($obj->fast_submit)) ? $obj->fast_submit : 0;
160
  $this->spam = (isset($obj->spam)) ? $obj->spam : 0;
508
  break;
509
 
510
  case 'check_newuser':
 
 
 
511
  if (empty($request->sender_email)) {
512
  $error_params[] = 'sender_email';
513
  }
cleantalk.php CHANGED
@@ -2,14 +2,15 @@
2
  /*
3
  Plugin Name: Anti-spam by CleanTalk
4
  Plugin URI: http://cleantalk.org/wordpress
5
- Description: Invisible antispam for comments and feedback forms. The plugin doesn't use CAPTCHA, Q&A, math or quiz to stop spam bots.
6
- Version: 2.5.18
7
  Author: СleanTalk <welcome@cleantalk.ru>
8
  Author URI: http://cleantalk.org
9
  */
10
 
11
- $ct_agent_version = 'wordpress-2518';
12
  $ct_checkjs_frm = 'ct_checkjs_frm';
 
13
 
14
  add_action('init', 'ct_init_locale');
15
  add_action('comment_form', 'ct_add_hidden_fields');
@@ -19,6 +20,9 @@ add_filter('preprocess_comment', 'ct_check'); // param - comment data array
19
  add_action('frm_validate_entry', 'ct_frm_validate_entry', 20, 2);
20
  add_action('frm_entries_footer_scripts', 'ct_frm_entries_footer_scripts', 20, 2);
21
 
 
 
 
22
  if (is_admin()) {
23
  add_action('admin_init', 'ct_admin_init');
24
  add_action('admin_menu', 'ct_admin_add_page');
@@ -52,6 +56,7 @@ function ct_def_options() {
52
  'server' => 'http://moderate.cleantalk.ru',
53
  'apikey' => __('enter key', 'cleantalk'),
54
  'autoPubRevelantMess' => '1',
 
55
  'comments_test' => '1',
56
  'formidable_test' => '1',
57
  'remove_old_spam' => '0',
@@ -170,7 +175,7 @@ function ct_init_locale() {
170
  * Public action 'comment_form' - Adds hidden filed to define avaialbility of client's JavaScript
171
  * @param int $post_id Post ID, not used
172
  */
173
- function ct_add_hidden_fields($post_id = 0) {
174
  $ct_checkjs_def = 0;
175
  $ct_checkjs_key = ct_get_checkjs_value();
176
 
@@ -180,10 +185,10 @@ function ct_add_hidden_fields($post_id = 0) {
180
  }
181
 
182
  ?>
183
- <input type="hidden" id="ct_checkjs" name="ct_checkjs" value="0">
184
  <script type="text/javascript">
185
  // <![CDATA[
186
- document.getElementById("ct_checkjs").value = document.getElementById("ct_checkjs").value.replace("<?php echo $ct_checkjs_def ?>", "<?php echo $ct_checkjs_key ?>");
187
  // ]]>
188
  </script>
189
  <?php
@@ -659,6 +664,205 @@ function ct_get_comment_text($current_text) {
659
  return $new_text;
660
  }
661
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
662
  /**
663
  * Admin action 'admin_enqueue_scripts' - Enqueue admin script of reloading admin page after needed AJAX events
664
  * @param string $hook URL of hooked page
@@ -685,6 +889,7 @@ function ct_admin_init() {
685
  add_settings_field('cleantalk_apikey', __('Access key', 'cleantalk'), 'ct_input_apikey', 'cleantalk', 'cleantalk_settings_main');
686
  add_settings_field('cleantalk_autoPubRevelantMess', __('Publish relevant comments', 'cleantalk'), 'ct_input_autoPubRevelantMess', 'cleantalk', 'cleantalk_settings_main');
687
  add_settings_field('cleantalk_remove_old_spam', __('Automatically delete spam comments', 'cleantalk'), 'ct_input_remove_old_spam', 'cleantalk', 'cleantalk_settings_main');
 
688
  add_settings_field('cleantalk_comments_test', __('Comments form', 'cleantalk'), 'ct_input_comments_test', 'cleantalk', 'cleantalk_settings_anti_spam');
689
  add_settings_field('cleantalk_formidable_test', __('Formidable Forms', 'cleantalk'), 'ct_input_formidable_test', 'cleantalk', 'cleantalk_settings_anti_spam');
690
  }
@@ -753,7 +958,6 @@ function ct_input_formidable_test() {
753
  echo "<input type='radio' id='cleantalk_formidable_test1' name='cleantalk_settings[formidable_test]' value='1' " . ($value == '1' ? 'checked' : '') . " /><label for='cleantalk_formidable_test1'> " . __('Yes') . "</label>";
754
  echo '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
755
  echo "<input type='radio' id='cleantalk_formidable_test0' name='cleantalk_settings[formidable_test]' value='0' " . ($value == '0' ? 'checked' : '') . " /><label for='cleantalk_formidable_test0'> " . __('No') . "</label>";
756
- admin_addDescriptionsFields(__('Spam protection for feeback plugin <a href="http://wordpress.org/plugins/formidable/">Formidable Forms</a>', 'cleantalk'));
757
  }
758
 
759
  /**
@@ -765,7 +969,17 @@ function ct_input_comments_test() {
765
  echo "<input type='radio' id='cleantalk_comments_test1' name='cleantalk_settings[comments_test]' value='1' " . ($value == '1' ? 'checked' : '') . " /><label for='cleantalk_comments_test1'> " . __('Yes') . "</label>";
766
  echo '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
767
  echo "<input type='radio' id='cleantalk_comments_test0' name='cleantalk_settings[comments_test]' value='0' " . ($value == '0' ? 'checked' : '') . " /><label for='cleantalk_comments_test0'> " . __('No') . "</label>";
768
- admin_addDescriptionsFields(__('Spam protection WordPress comments', 'cleantalk'));
 
 
 
 
 
 
 
 
 
 
769
  }
770
 
771
 
@@ -796,123 +1010,4 @@ function ct_settings_page() {
796
  <?php
797
  }
798
 
799
- /**
800
- * Mark bad words
801
- * @global string $ct_stop_words
802
- * @param int $comment_id
803
- * @param int $comment_status Not use
804
- */
805
- function ct_mark_red($comment_id, $comment_status) {
806
- global $ct_stop_words;
807
-
808
- $comment = get_comment($comment_id, 'ARRAY_A');
809
- $message = $comment['comment_content'];
810
- foreach (explode(':', $ct_stop_words) as $word) {
811
- $message = preg_replace("/($word)/ui", '<font rel="cleantalk" color="#FF1000">' . "$1" . '</font>', $message);
812
-
813
- }
814
- $comment['comment_content'] = $message;
815
- kses_remove_filters();
816
- wp_update_comment($comment);
817
- }
818
-
819
- /**
820
- * Unmark bad words
821
- * @param string $message
822
- * @return string Cleat comment
823
- */
824
- function ct_unmark_red($message) {
825
- $message = preg_replace("/\<font rel\=\"cleantalk\" color\=\"\#FF1000\"\>(\S+)\<\/font>/iu", '$1', $message);
826
-
827
- return $message;
828
- }
829
-
830
- /**
831
- * Notice blog owner if plugin using without Access key
832
- * @return bool
833
- */
834
- function admin_notice_message(){
835
-
836
- if (ct_active() === false)
837
- return false;
838
-
839
- $options = ct_get_options();
840
- if ($options['apikey'] === 'enter key' || $options['apikey'] === '')
841
- echo '<div class="updated"><p>' . __("Please enter the Access Key in <a href=\"options-general.php?page=cleantalk\">CleanTalk plugin</a> settings to enable protection from spam in comments!", 'cleantalk') . '</p></div>';
842
-
843
- ct_send_feedback();
844
-
845
- delete_spam_comments();
846
-
847
- return true;
848
- }
849
-
850
- /**
851
- * @author Artem Leontiev
852
- *
853
- * Add descriptions for field
854
- */
855
- function admin_addDescriptionsFields($descr = '') {
856
- echo "<p style='color: #666 !important'>$descr</p>";
857
- }
858
-
859
- /**
860
- * Tests plugin activation status
861
- * @return bool
862
- */
863
- function ct_active(){
864
- $ct_active = false;
865
- foreach (get_option('active_plugins') as $k => $v) {
866
- if (preg_match("/cleantalk.php$/", $v))
867
- $ct_active = true;
868
- }
869
-
870
- return $ct_active;
871
- }
872
- /**
873
- * Tests plugin activation status
874
- * @return bool
875
- */
876
- function ct_plugin_active($plugin_name){
877
- $active = false;
878
- foreach (get_option('active_plugins') as $k => $v) {
879
- if ($plugin_name == $v)
880
- $active = true;
881
- }
882
- return $active;
883
- }
884
-
885
- /**
886
- * Get ct_get_checkjs_value
887
- * @return string
888
- */
889
- function ct_get_checkjs_value() {
890
- $options = ct_get_options();
891
-
892
- return md5($options['apikey'] . '+' . get_settings('admin_email'));
893
- }
894
-
895
- /**
896
- * Delete old spam comments
897
- * @return null
898
- */
899
- function delete_spam_comments() {
900
- $options = ct_get_options();
901
-
902
- if ($options['remove_old_spam'] == 1) {
903
- $last_comments = get_comments(array('status' => 'spam', 'number' => 1000, 'order' => 'ASC'));
904
- foreach ($last_comments as $c) {
905
- if (time() - strtotime($c->comment_date_gmt) > 86400 * $options['spam_store_days']) {
906
- // Force deletion old spam comments
907
- wp_delete_comment($c->comment_ID, true);
908
- }
909
- }
910
- }
911
-
912
-
913
- return null;
914
- }
915
-
916
-
917
-
918
  ?>
2
  /*
3
  Plugin Name: Anti-spam by CleanTalk
4
  Plugin URI: http://cleantalk.org/wordpress
5
+ Description: Invisible antispam for comments, registrations and feedbacks. The plugin doesn't use CAPTCHA, Q&A, math or quiz to stop spam bots.
6
+ Version: 2.19
7
  Author: СleanTalk <welcome@cleantalk.ru>
8
  Author URI: http://cleantalk.org
9
  */
10
 
11
+ $ct_agent_version = 'wordpress-219';
12
  $ct_checkjs_frm = 'ct_checkjs_frm';
13
+ $ct_checkjs_register_form = 'ct_checkjs_register_form';
14
 
15
  add_action('init', 'ct_init_locale');
16
  add_action('comment_form', 'ct_add_hidden_fields');
20
  add_action('frm_validate_entry', 'ct_frm_validate_entry', 20, 2);
21
  add_action('frm_entries_footer_scripts', 'ct_frm_entries_footer_scripts', 20, 2);
22
 
23
+ add_action('register_form','ct_register_form');
24
+ add_filter('registration_errors', 'ct_registration_errors', 10, 3);
25
+
26
  if (is_admin()) {
27
  add_action('admin_init', 'ct_admin_init');
28
  add_action('admin_menu', 'ct_admin_add_page');
56
  'server' => 'http://moderate.cleantalk.ru',
57
  'apikey' => __('enter key', 'cleantalk'),
58
  'autoPubRevelantMess' => '1',
59
+ 'registrations_test' => '1',
60
  'comments_test' => '1',
61
  'formidable_test' => '1',
62
  'remove_old_spam' => '0',
175
  * Public action 'comment_form' - Adds hidden filed to define avaialbility of client's JavaScript
176
  * @param int $post_id Post ID, not used
177
  */
178
+ function ct_add_hidden_fields($post_id = 0, $field_name = 'ct_checkjs') {
179
  $ct_checkjs_def = 0;
180
  $ct_checkjs_key = ct_get_checkjs_value();
181
 
185
  }
186
 
187
  ?>
188
+ <input type="hidden" id="<?php echo $field_name; ?>" name="<?php echo $field_name; ?>" value="0">
189
  <script type="text/javascript">
190
  // <![CDATA[
191
+ document.getElementById("<?php echo $field_name; ?>").value = document.getElementById("<?php echo $field_name; ?>").value.replace("<?php echo $ct_checkjs_def ?>", "<?php echo $ct_checkjs_key ?>");
192
  // ]]>
193
  </script>
194
  <?php
664
  return $new_text;
665
  }
666
 
667
+ /**
668
+ * Mark bad words
669
+ * @global string $ct_stop_words
670
+ * @param int $comment_id
671
+ * @param int $comment_status Not use
672
+ */
673
+ function ct_mark_red($comment_id, $comment_status) {
674
+ global $ct_stop_words;
675
+
676
+ $comment = get_comment($comment_id, 'ARRAY_A');
677
+ $message = $comment['comment_content'];
678
+ foreach (explode(':', $ct_stop_words) as $word) {
679
+ $message = preg_replace("/($word)/ui", '<font rel="cleantalk" color="#FF1000">' . "$1" . '</font>', $message);
680
+
681
+ }
682
+ $comment['comment_content'] = $message;
683
+ kses_remove_filters();
684
+ wp_update_comment($comment);
685
+ }
686
+
687
+ /**
688
+ * Unmark bad words
689
+ * @param string $message
690
+ * @return string Cleat comment
691
+ */
692
+ function ct_unmark_red($message) {
693
+ $message = preg_replace("/\<font rel\=\"cleantalk\" color\=\"\#FF1000\"\>(\S+)\<\/font>/iu", '$1', $message);
694
+
695
+ return $message;
696
+ }
697
+
698
+ /**
699
+ * Notice blog owner if plugin using without Access key
700
+ * @return bool
701
+ */
702
+ function admin_notice_message(){
703
+
704
+ if (ct_active() === false)
705
+ return false;
706
+
707
+ $options = ct_get_options();
708
+ if ($options['apikey'] === 'enter key' || $options['apikey'] === '')
709
+ echo '<div class="updated"><p>' . __("Please enter the Access Key in <a href=\"options-general.php?page=cleantalk\">CleanTalk plugin</a> settings to enable protection from spam in comments!", 'cleantalk') . '</p></div>';
710
+
711
+ ct_send_feedback();
712
+
713
+ delete_spam_comments();
714
+
715
+ return true;
716
+ }
717
+
718
+ /**
719
+ * @author Artem Leontiev
720
+ *
721
+ * Add descriptions for field
722
+ */
723
+ function admin_addDescriptionsFields($descr = '') {
724
+ echo "<p style='color: #666 !important'>$descr</p>";
725
+ }
726
+
727
+ /**
728
+ * Tests plugin activation status
729
+ * @return bool
730
+ */
731
+ function ct_active(){
732
+ $ct_active = false;
733
+ foreach (get_option('active_plugins') as $k => $v) {
734
+ if (preg_match("/cleantalk.php$/", $v))
735
+ $ct_active = true;
736
+ }
737
+
738
+ return $ct_active;
739
+ }
740
+ /**
741
+ * Tests plugin activation status
742
+ * @return bool
743
+ */
744
+ function ct_plugin_active($plugin_name){
745
+ $active = false;
746
+ foreach (get_option('active_plugins') as $k => $v) {
747
+ if ($plugin_name == $v)
748
+ $active = true;
749
+ }
750
+ return $active;
751
+ }
752
+
753
+ /**
754
+ * Get ct_get_checkjs_value
755
+ * @return string
756
+ */
757
+ function ct_get_checkjs_value() {
758
+ $options = ct_get_options();
759
+
760
+ return md5($options['apikey'] . '+' . get_settings('admin_email'));
761
+ }
762
+
763
+ /**
764
+ * Delete old spam comments
765
+ * @return null
766
+ */
767
+ function delete_spam_comments() {
768
+ $options = ct_get_options();
769
+
770
+ if ($options['remove_old_spam'] == 1) {
771
+ $last_comments = get_comments(array('status' => 'spam', 'number' => 1000, 'order' => 'ASC'));
772
+ foreach ($last_comments as $c) {
773
+ if (time() - strtotime($c->comment_date_gmt) > 86400 * $options['spam_store_days']) {
774
+ // Force deletion old spam comments
775
+ wp_delete_comment($c->comment_ID, true);
776
+ }
777
+ }
778
+ }
779
+
780
+
781
+ return null;
782
+ }
783
+
784
+
785
+ /**
786
+ * Insert a hidden field to registration form
787
+ * @return null
788
+ */
789
+ function ct_register_form() {
790
+ global $ct_checkjs_register_form;
791
+
792
+ ct_add_hidden_fields(0, $ct_checkjs_register_form);
793
+ return null;
794
+ }
795
+
796
+ /**
797
+ * Test users registration
798
+ * @return array with errors
799
+ */
800
+ function ct_registration_errors($errors, $sanitized_user_login, $user_email) {
801
+ global $ct_agent_version, $ct_checkjs_register_form;
802
+
803
+ $options = ct_get_options();
804
+ if ($options['registrations_test'] == 0) {
805
+ return $errors;
806
+ }
807
+
808
+ $checkjs = 0;
809
+ if (isset($_POST[$ct_checkjs_register_form])) {
810
+ if($_POST[$ct_checkjs_register_form] == ct_get_checkjs_value()) {
811
+ $checkjs = 1;
812
+ }
813
+ }
814
+ require_once('cleantalk.class.php');
815
+
816
+ $blog_lang = substr(get_locale(), 0, 2);
817
+ $user_info = array(
818
+ 'cms_lang' => $blog_lang,
819
+ 'REFFERRER' => @$_SERVER['HTTP_REFERER'],
820
+ 'USER_AGENT' => @$_SERVER['HTTP_USER_AGENT'],
821
+ );
822
+ $user_info = json_encode($user_info);
823
+ if ($user_info === false)
824
+ $user_info = '';
825
+
826
+ $sender_email = $user_email;
827
+
828
+ $config = get_option('cleantalk_server');
829
+
830
+ $ct = new Cleantalk();
831
+ $ct->work_url = $config['ct_work_url'];
832
+ $ct->server_url = $options['server'];
833
+ $ct->server_ttl = $config['ct_server_ttl'];
834
+ $ct->server_changed = $config['ct_server_changed'];
835
+
836
+ $ct_request = new CleantalkRequest();
837
+
838
+ $ct_request->auth_key = $options['apikey'];
839
+ $ct_request->sender_email = $sender_email;
840
+ $ct_request->sender_ip = $ct->ct_session_ip($_SERVER['REMOTE_ADDR']);
841
+ $ct_request->sender_nickname = $sanitized_user_login;
842
+ $ct_request->agent = $ct_agent_version;
843
+ $ct_request->sender_info = $user_info;
844
+ $ct_request->js_on = $checkjs;
845
+
846
+ $ct_result = $ct->isAllowUser($ct_request);
847
+ if ($ct->server_change) {
848
+ update_option(
849
+ 'cleantalk_server', array(
850
+ 'ct_work_url' => $ct->work_url,
851
+ 'ct_server_ttl' => $ct->server_ttl,
852
+ 'ct_server_changed' => time()
853
+ )
854
+ );
855
+ }
856
+
857
+ if ($ct_result->errno == 0 && $ct_result->allow == 0) {
858
+ $wordpress_domain = preg_replace("/^https?:\/\//", "", site_url());
859
+ $errors->add('ct_error', __($ct_result->comment, $wordpress_domain));
860
+ }
861
+
862
+ return $errors;
863
+ }
864
+
865
+
866
  /**
867
  * Admin action 'admin_enqueue_scripts' - Enqueue admin script of reloading admin page after needed AJAX events
868
  * @param string $hook URL of hooked page
889
  add_settings_field('cleantalk_apikey', __('Access key', 'cleantalk'), 'ct_input_apikey', 'cleantalk', 'cleantalk_settings_main');
890
  add_settings_field('cleantalk_autoPubRevelantMess', __('Publish relevant comments', 'cleantalk'), 'ct_input_autoPubRevelantMess', 'cleantalk', 'cleantalk_settings_main');
891
  add_settings_field('cleantalk_remove_old_spam', __('Automatically delete spam comments', 'cleantalk'), 'ct_input_remove_old_spam', 'cleantalk', 'cleantalk_settings_main');
892
+ add_settings_field('cleantalk_registrations_test', __('Registration form', 'cleantalk'), 'ct_input_registrations_test', 'cleantalk', 'cleantalk_settings_anti_spam');
893
  add_settings_field('cleantalk_comments_test', __('Comments form', 'cleantalk'), 'ct_input_comments_test', 'cleantalk', 'cleantalk_settings_anti_spam');
894
  add_settings_field('cleantalk_formidable_test', __('Formidable Forms', 'cleantalk'), 'ct_input_formidable_test', 'cleantalk', 'cleantalk_settings_anti_spam');
895
  }
958
  echo "<input type='radio' id='cleantalk_formidable_test1' name='cleantalk_settings[formidable_test]' value='1' " . ($value == '1' ? 'checked' : '') . " /><label for='cleantalk_formidable_test1'> " . __('Yes') . "</label>";
959
  echo '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
960
  echo "<input type='radio' id='cleantalk_formidable_test0' name='cleantalk_settings[formidable_test]' value='0' " . ($value == '0' ? 'checked' : '') . " /><label for='cleantalk_formidable_test0'> " . __('No') . "</label>";
 
961
  }
962
 
963
  /**
969
  echo "<input type='radio' id='cleantalk_comments_test1' name='cleantalk_settings[comments_test]' value='1' " . ($value == '1' ? 'checked' : '') . " /><label for='cleantalk_comments_test1'> " . __('Yes') . "</label>";
970
  echo '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
971
  echo "<input type='radio' id='cleantalk_comments_test0' name='cleantalk_settings[comments_test]' value='0' " . ($value == '0' ? 'checked' : '') . " /><label for='cleantalk_comments_test0'> " . __('No') . "</label>";
972
+ }
973
+
974
+ /**
975
+ * Admin callback function - Displays inputs of 'comments_test' plugin parameter
976
+ */
977
+ function ct_input_registrations_test() {
978
+ $options = ct_get_options();
979
+ $value = $options['registrations_test'];
980
+ echo "<input type='radio' id='cleantalk_registrations_test1' name='cleantalk_settings[registrations_test]' value='1' " . ($value == '1' ? 'checked' : '') . " /><label for='cleantalk_registrations_test1'> " . __('Yes') . "</label>";
981
+ echo '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
982
+ echo "<input type='radio' id='cleantalk_registrations_test0' name='cleantalk_settings[registrations_test]' value='0' " . ($value == '0' ? 'checked' : '') . " /><label for='cleantalk_registrations_test0'> " . __('No') . "</label>";
983
  }
984
 
985
 
1010
  <?php
1011
  }
1012
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1013
  ?>
cleantalk.xmlrpc.php DELETED
@@ -1,3777 +0,0 @@
1
- <?php
2
- // by Edd Dumbill (C) 1999-2002
3
- // <edd@usefulinc.com>
4
- // $Id: xmlrpc.inc,v 1.174 2009/03/16 19:36:38 ggiunta Exp $
5
-
6
- // Copyright (c) 1999,2000,2002 Edd Dumbill.
7
- // All rights reserved.
8
- //
9
- // Redistribution and use in source and binary forms, with or without
10
- // modification, are permitted provided that the following conditions
11
- // are met:
12
- //
13
- // * Redistributions of source code must retain the above copyright
14
- // notice, this list of conditions and the following disclaimer.
15
- //
16
- // * Redistributions in binary form must reproduce the above
17
- // copyright notice, this list of conditions and the following
18
- // disclaimer in the documentation and/or other materials provided
19
- // with the distribution.
20
- //
21
- // * Neither the name of the "XML-RPC for PHP" nor the names of its
22
- // contributors may be used to endorse or promote products derived
23
- // from this software without specific prior written permission.
24
- //
25
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26
- // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27
- // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28
- // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29
- // REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30
- // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31
- // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32
- // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33
- // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34
- // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35
- // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
36
- // OF THE POSSIBILITY OF SUCH DAMAGE.
37
-
38
- if(!function_exists('xml_parser_create'))
39
- {
40
- // For PHP 4 onward, XML functionality is always compiled-in on windows:
41
- // no more need to dl-open it. It might have been compiled out on *nix...
42
- if(strtoupper(substr(PHP_OS, 0, 3) != 'WIN'))
43
- {
44
- dl('xml.so');
45
- }
46
- }
47
-
48
- // G. Giunta 2005/01/29: declare global these variables,
49
- // so that xmlrpc.inc will work even if included from within a function
50
- // Milosch: 2005/08/07 - explicitly request these via $GLOBALS where used.
51
- $GLOBALS['xmlrpcI4']='i4';
52
- $GLOBALS['xmlrpcInt']='int';
53
- $GLOBALS['xmlrpcBoolean']='boolean';
54
- $GLOBALS['xmlrpcDouble']='double';
55
- $GLOBALS['xmlrpcString']='string';
56
- $GLOBALS['xmlrpcDateTime']='dateTime.iso8601';
57
- $GLOBALS['xmlrpcBase64']='base64';
58
- $GLOBALS['xmlrpcArray']='array';
59
- $GLOBALS['xmlrpcStruct']='struct';
60
- $GLOBALS['xmlrpcValue']='undefined';
61
-
62
- $GLOBALS['xmlrpcTypes']=array(
63
- $GLOBALS['xmlrpcI4'] => 1,
64
- $GLOBALS['xmlrpcInt'] => 1,
65
- $GLOBALS['xmlrpcBoolean'] => 1,
66
- $GLOBALS['xmlrpcString'] => 1,
67
- $GLOBALS['xmlrpcDouble'] => 1,
68
- $GLOBALS['xmlrpcDateTime'] => 1,
69
- $GLOBALS['xmlrpcBase64'] => 1,
70
- $GLOBALS['xmlrpcArray'] => 2,
71
- $GLOBALS['xmlrpcStruct'] => 3
72
- );
73
-
74
- $GLOBALS['xmlrpc_valid_parents'] = array(
75
- 'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT'),
76
- 'BOOLEAN' => array('VALUE'),
77
- 'I4' => array('VALUE'),
78
- 'INT' => array('VALUE'),
79
- 'STRING' => array('VALUE'),
80
- 'DOUBLE' => array('VALUE'),
81
- 'DATETIME.ISO8601' => array('VALUE'),
82
- 'BASE64' => array('VALUE'),
83
- 'MEMBER' => array('STRUCT'),
84
- 'NAME' => array('MEMBER'),
85
- 'DATA' => array('ARRAY'),
86
- 'ARRAY' => array('VALUE'),
87
- 'STRUCT' => array('VALUE'),
88
- 'PARAM' => array('PARAMS'),
89
- 'METHODNAME' => array('METHODCALL'),
90
- 'PARAMS' => array('METHODCALL', 'METHODRESPONSE'),
91
- 'FAULT' => array('METHODRESPONSE'),
92
- 'NIL' => array('VALUE'), // only used when extension activated
93
- 'EX:NIL' => array('VALUE') // only used when extension activated
94
- );
95
-
96
- // define extra types for supporting NULL (useful for json or <NIL/>)
97
- $GLOBALS['xmlrpcNull']='null';
98
- $GLOBALS['xmlrpcTypes']['null']=1;
99
-
100
- // Not in use anymore since 2.0. Shall we remove it?
101
- /// @deprecated
102
- $GLOBALS['xmlEntities']=array(
103
- 'amp' => '&',
104
- 'quot' => '"',
105
- 'lt' => '<',
106
- 'gt' => '>',
107
- 'apos' => "'"
108
- );
109
-
110
- // tables used for transcoding different charsets into us-ascii xml
111
-
112
- $GLOBALS['xml_iso88591_Entities']=array();
113
- $GLOBALS['xml_iso88591_Entities']['in'] = array();
114
- $GLOBALS['xml_iso88591_Entities']['out'] = array();
115
- for ($i = 0; $i < 32; $i++)
116
- {
117
- $GLOBALS['xml_iso88591_Entities']['in'][] = chr($i);
118
- $GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';';
119
- }
120
- for ($i = 160; $i < 256; $i++)
121
- {
122
- $GLOBALS['xml_iso88591_Entities']['in'][] = chr($i);
123
- $GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';';
124
- }
125
-
126
- /// @todo add to iso table the characters from cp_1252 range, i.e. 128 to 159?
127
- /// These will NOT be present in true ISO-8859-1, but will save the unwary
128
- /// windows user from sending junk (though no luck when reciving them...)
129
- /*
130
- $GLOBALS['xml_cp1252_Entities']=array();
131
- for ($i = 128; $i < 160; $i++)
132
- {
133
- $GLOBALS['xml_cp1252_Entities']['in'][] = chr($i);
134
- }
135
- $GLOBALS['xml_cp1252_Entities']['out'] = array(
136
- '&#x20AC;', '?', '&#x201A;', '&#x0192;',
137
- '&#x201E;', '&#x2026;', '&#x2020;', '&#x2021;',
138
- '&#x02C6;', '&#x2030;', '&#x0160;', '&#x2039;',
139
- '&#x0152;', '?', '&#x017D;', '?',
140
- '?', '&#x2018;', '&#x2019;', '&#x201C;',
141
- '&#x201D;', '&#x2022;', '&#x2013;', '&#x2014;',
142
- '&#x02DC;', '&#x2122;', '&#x0161;', '&#x203A;',
143
- '&#x0153;', '?', '&#x017E;', '&#x0178;'
144
- );
145
- */
146
-
147
- $GLOBALS['xmlrpcerr'] = array(
148
- 'unknown_method'=>1,
149
- 'invalid_return'=>2,
150
- 'incorrect_params'=>3,
151
- 'introspect_unknown'=>4,
152
- 'http_error'=>5,
153
- 'no_data'=>6,
154
- 'no_ssl'=>7,
155
- 'curl_fail'=>8,
156
- 'invalid_request'=>15,
157
- 'no_curl'=>16,
158
- 'server_error'=>17,
159
- 'multicall_error'=>18,
160
- 'multicall_notstruct'=>9,
161
- 'multicall_nomethod'=>10,
162
- 'multicall_notstring'=>11,
163
- 'multicall_recursion'=>12,
164
- 'multicall_noparams'=>13,
165
- 'multicall_notarray'=>14,
166
-
167
- 'cannot_decompress'=>103,
168
- 'decompress_fail'=>104,
169
- 'dechunk_fail'=>105,
170
- 'server_cannot_decompress'=>106,
171
- 'server_decompress_fail'=>107
172
- );
173
-
174
- $GLOBALS['xmlrpcstr'] = array(
175
- 'unknown_method'=>'Unknown method',
176
- 'invalid_return'=>'Invalid return payload: enable debugging to examine incoming payload',
177
- 'incorrect_params'=>'Incorrect parameters passed to method',
178
- 'introspect_unknown'=>"Can't introspect: method unknown",
179
- 'http_error'=>"Didn't receive 200 OK from remote server.",
180
- 'no_data'=>'No data received from server.',
181
- 'no_ssl'=>'No SSL support compiled in.',
182
- 'curl_fail'=>'CURL error',
183
- 'invalid_request'=>'Invalid request payload',
184
- 'no_curl'=>'No CURL support compiled in.',
185
- 'server_error'=>'Internal server error',
186
- 'multicall_error'=>'Received from server invalid multicall response',
187
- 'multicall_notstruct'=>'system.multicall expected struct',
188
- 'multicall_nomethod'=>'missing methodName',
189
- 'multicall_notstring'=>'methodName is not a string',
190
- 'multicall_recursion'=>'recursive system.multicall forbidden',
191
- 'multicall_noparams'=>'missing params',
192
- 'multicall_notarray'=>'params is not an array',
193
-
194
- 'cannot_decompress'=>'Received from server compressed HTTP and cannot decompress',
195
- 'decompress_fail'=>'Received from server invalid compressed HTTP',
196
- 'dechunk_fail'=>'Received from server invalid chunked HTTP',
197
- 'server_cannot_decompress'=>'Received from client compressed HTTP request and cannot decompress',
198
- 'server_decompress_fail'=>'Received from client invalid compressed HTTP request'
199
- );
200
-
201
- // The charset encoding used by the server for received messages and
202
- // by the client for received responses when received charset cannot be determined
203
- // or is not supported
204
- $GLOBALS['xmlrpc_defencoding']='UTF-8';
205
-
206
- // The encoding used internally by PHP.
207
- // String values received as xml will be converted to this, and php strings will be converted to xml
208
- // as if having been coded with this
209
- // $GLOBALS['xmlrpc_internalencoding']='ISO-8859-1';
210
- $GLOBALS['xmlrpc_internalencoding']='UTF-8'; // by Denis Shagimuratov
211
-
212
- $GLOBALS['xmlrpcName']='XML-RPC for PHP';
213
- $GLOBALS['xmlrpcVersion']='3.0.0.beta';
214
-
215
- // let user errors start at 800
216
- $GLOBALS['xmlrpcerruser']=800;
217
- // let XML parse errors start at 100
218
- $GLOBALS['xmlrpcerrxml']=100;
219
-
220
- // formulate backslashes for escaping regexp
221
- // Not in use anymore since 2.0. Shall we remove it?
222
- /// @deprecated
223
- $GLOBALS['xmlrpc_backslash']=chr(92).chr(92);
224
-
225
- // set to TRUE to enable correct decoding of <NIL/> and <EX:NIL/> values
226
- $GLOBALS['xmlrpc_null_extension']=false;
227
-
228
- // set to TRUE to enable encoding of php NULL values to <EX:NIL/> instead of <NIL/>
229
- $GLOBALS['xmlrpc_null_apache_encoding']=false;
230
-
231
- // used to store state during parsing
232
- // quick explanation of components:
233
- // ac - used to accumulate values
234
- // isf - used to indicate a parsing fault (2) or xmlrpcresp fault (1)
235
- // isf_reason - used for storing xmlrpcresp fault string
236
- // lv - used to indicate "looking for a value": implements
237
- // the logic to allow values with no types to be strings
238
- // params - used to store parameters in method calls
239
- // method - used to store method name
240
- // stack - array with genealogy of xml elements names:
241
- // used to validate nesting of xmlrpc elements
242
- $GLOBALS['_xh']=null;
243
-
244
- /**
245
- * Convert a string to the correct XML representation in a target charset
246
- * To help correct communication of non-ascii chars inside strings, regardless
247
- * of the charset used when sending requests, parsing them, sending responses
248
- * and parsing responses, an option is to convert all non-ascii chars present in the message
249
- * into their equivalent 'charset entity'. Charset entities enumerated this way
250
- * are independent of the charset encoding used to transmit them, and all XML
251
- * parsers are bound to understand them.
252
- * Note that in the std case we are not sending a charset encoding mime type
253
- * along with http headers, so we are bound by RFC 3023 to emit strict us-ascii.
254
- *
255
- * @todo do a bit of basic benchmarking (strtr vs. str_replace)
256
- * @todo make usage of iconv() or recode_string() or mb_string() where available
257
- */
258
- function xmlrpc_encode_entitites($data, $src_encoding='', $dest_encoding='')
259
- {
260
- if ($src_encoding == '')
261
- {
262
- // lame, but we know no better...
263
- $src_encoding = $GLOBALS['xmlrpc_internalencoding'];
264
- }
265
-
266
- switch(strtoupper($src_encoding.'_'.$dest_encoding))
267
- {
268
- case 'ISO-8859-1_':
269
- case 'ISO-8859-1_US-ASCII':
270
- $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
271
- $escaped_data = str_replace($GLOBALS['xml_iso88591_Entities']['in'], $GLOBALS['xml_iso88591_Entities']['out'], $escaped_data);
272
- break;
273
- case 'ISO-8859-1_UTF-8':
274
- $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
275
- $escaped_data = utf8_encode($escaped_data);
276
- break;
277
- case 'ISO-8859-1_ISO-8859-1':
278
- case 'US-ASCII_US-ASCII':
279
- case 'US-ASCII_UTF-8':
280
- case 'US-ASCII_':
281
- case 'US-ASCII_ISO-8859-1':
282
- case 'UTF-8_UTF-8':
283
- //case 'CP1252_CP1252':
284
- $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
285
- break;
286
- case 'UTF-8_':
287
- case 'UTF-8_US-ASCII':
288
- case 'UTF-8_ISO-8859-1':
289
- // NB: this will choke on invalid UTF-8, going most likely beyond EOF
290
- $escaped_data = '';
291
- // be kind to users creating string xmlrpcvals out of different php types
292
- $data = (string) $data;
293
- $ns = strlen ($data);
294
- for ($nn = 0; $nn < $ns; $nn++)
295
- {
296
- $ch = $data[$nn];
297
- $ii = ord($ch);
298
- //1 7 0bbbbbbb (127)
299
- if ($ii < 128)
300
- {
301
- /// @todo shall we replace this with a (supposedly) faster str_replace?
302
- switch($ii){
303
- case 34:
304
- $escaped_data .= '&quot;';
305
- break;
306
- case 38:
307
- $escaped_data .= '&amp;';
308
- break;
309
- case 39:
310
- $escaped_data .= '&apos;';
311
- break;
312
- case 60:
313
- $escaped_data .= '&lt;';
314
- break;
315
- case 62:
316
- $escaped_data .= '&gt;';
317
- break;
318
- default:
319
- $escaped_data .= $ch;
320
- } // switch
321
- }
322
- //2 11 110bbbbb 10bbbbbb (2047)
323
- else if ($ii>>5 == 6)
324
- {
325
- $b1 = ($ii & 31);
326
- $ii = ord($data[$nn+1]);
327
- $b2 = ($ii & 63);
328
- $ii = ($b1 * 64) + $b2;
329
- $ent = sprintf ('&#%d;', $ii);
330
- $escaped_data .= $ent;
331
- $nn += 1;
332
- }
333
- //3 16 1110bbbb 10bbbbbb 10bbbbbb
334
- else if ($ii>>4 == 14)
335
- {
336
- $b1 = ($ii & 15);
337
- $ii = ord($data[$nn+1]);
338
- $b2 = ($ii & 63);
339
- $ii = ord($data[$nn+2]);
340
- $b3 = ($ii & 63);
341
- $ii = ((($b1 * 64) + $b2) * 64) + $b3;
342
- $ent = sprintf ('&#%d;', $ii);
343
- $escaped_data .= $ent;
344
- $nn += 2;
345
- }
346
- //4 21 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
347
- else if ($ii>>3 == 30)
348
- {
349
- $b1 = ($ii & 7);
350
- $ii = ord($data[$nn+1]);
351
- $b2 = ($ii & 63);
352
- $ii = ord($data[$nn+2]);
353
- $b3 = ($ii & 63);
354
- $ii = ord($data[$nn+3]);
355
- $b4 = ($ii & 63);
356
- $ii = ((((($b1 * 64) + $b2) * 64) + $b3) * 64) + $b4;
357
- $ent = sprintf ('&#%d;', $ii);
358
- $escaped_data .= $ent;
359
- $nn += 3;
360
- }
361
- }
362
- break;
363
- /*
364
- case 'CP1252_':
365
- case 'CP1252_US-ASCII':
366
- $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
367
- $escaped_data = str_replace($GLOBALS['xml_iso88591_Entities']['in'], $GLOBALS['xml_iso88591_Entities']['out'], $escaped_data);
368
- $escaped_data = str_replace($GLOBALS['xml_cp1252_Entities']['in'], $GLOBALS['xml_cp1252_Entities']['out'], $escaped_data);
369
- break;
370
- case 'CP1252_UTF-8':
371
- $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
372
- /// @todo we could use real UTF8 chars here instead of xml entities... (note that utf_8 encode all allone will NOT convert them)
373
- $escaped_data = str_replace($GLOBALS['xml_cp1252_Entities']['in'], $GLOBALS['xml_cp1252_Entities']['out'], $escaped_data);
374
- $escaped_data = utf8_encode($escaped_data);
375
- break;
376
- case 'CP1252_ISO-8859-1':
377
- $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
378
- // we might as well replave all funky chars with a '?' here, but we are kind and leave it to the receiving application layer to decide what to do with these weird entities...
379
- $escaped_data = str_replace($GLOBALS['xml_cp1252_Entities']['in'], $GLOBALS['xml_cp1252_Entities']['out'], $escaped_data);
380
- break;
381
- */
382
- default:
383
- $escaped_data = '';
384
- error_log("Converting from $src_encoding to $dest_encoding: not supported...");
385
- }
386
- return $escaped_data;
387
- }
388
-
389
- /// xml parser handler function for opening element tags
390
- function xmlrpc_se($parser, $name, $attrs, $accept_single_vals=false)
391
- {
392
- // if invalid xmlrpc already detected, skip all processing
393
- if ($GLOBALS['_xh']['isf'] < 2)
394
- {
395
- // check for correct element nesting
396
- // top level element can only be of 2 types
397
- /// @todo optimization creep: save this check into a bool variable, instead of using count() every time:
398
- /// there is only a single top level element in xml anyway
399
- if (count($GLOBALS['_xh']['stack']) == 0)
400
- {
401
- if ($name != 'METHODRESPONSE' && $name != 'METHODCALL' && (
402
- $name != 'VALUE' && !$accept_single_vals))
403
- {
404
- $GLOBALS['_xh']['isf'] = 2;
405
- $GLOBALS['_xh']['isf_reason'] = 'missing top level xmlrpc element';
406
- return;
407
- }
408
- else
409
- {
410
- $GLOBALS['_xh']['rt'] = strtolower($name);
411
- $GLOBALS['_xh']['rt'] = strtolower($name);
412
- }
413
- }
414
- else
415
- {
416
- // not top level element: see if parent is OK
417
- $parent = end($GLOBALS['_xh']['stack']);
418
- if (!array_key_exists($name, $GLOBALS['xmlrpc_valid_parents']) || !in_array($parent, $GLOBALS['xmlrpc_valid_parents'][$name]))
419
- {
420
- $GLOBALS['_xh']['isf'] = 2;
421
- $GLOBALS['_xh']['isf_reason'] = "xmlrpc element $name cannot be child of $parent";
422
- return;
423
- }
424
- }
425
-
426
- switch($name)
427
- {
428
- // optimize for speed switch cases: most common cases first
429
- case 'VALUE':
430
- /// @todo we could check for 2 VALUE elements inside a MEMBER or PARAM element
431
- $GLOBALS['_xh']['vt']='value'; // indicator: no value found yet
432
- $GLOBALS['_xh']['ac']='';
433
- $GLOBALS['_xh']['lv']=1;
434
- $GLOBALS['_xh']['php_class']=null;
435
- break;
436
- case 'I4':
437
- case 'INT':
438
- case 'STRING':
439
- case 'BOOLEAN':
440
- case 'DOUBLE':
441
- case 'DATETIME.ISO8601':
442
- case 'BASE64':
443
- if ($GLOBALS['_xh']['vt']!='value')
444
- {
445
- //two data elements inside a value: an error occurred!
446
- $GLOBALS['_xh']['isf'] = 2;
447
- $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
448
- return;
449
- }
450
- $GLOBALS['_xh']['ac']=''; // reset the accumulator
451
- break;
452
- case 'STRUCT':
453
- case 'ARRAY':
454
- if ($GLOBALS['_xh']['vt']!='value')
455
- {
456
- //two data elements inside a value: an error occurred!
457
- $GLOBALS['_xh']['isf'] = 2;
458
- $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
459
- return;
460
- }
461
- // create an empty array to hold child values, and push it onto appropriate stack
462
- $cur_val = array();
463
- $cur_val['values'] = array();
464
- $cur_val['type'] = $name;
465
- // check for out-of-band information to rebuild php objs
466
- // and in case it is found, save it
467
- if (@isset($attrs['PHP_CLASS']))
468
- {
469
- $cur_val['php_class'] = $attrs['PHP_CLASS'];
470
- }
471
- $GLOBALS['_xh']['valuestack'][] = $cur_val;
472
- $GLOBALS['_xh']['vt']='data'; // be prepared for a data element next
473
- break;
474
- case 'DATA':
475
- if ($GLOBALS['_xh']['vt']!='data')
476
- {
477
- //two data elements inside a value: an error occurred!
478
- $GLOBALS['_xh']['isf'] = 2;
479
- $GLOBALS['_xh']['isf_reason'] = "found two data elements inside an array element";
480
- return;
481
- }
482
- case 'METHODCALL':
483
- case 'METHODRESPONSE':
484
- case 'PARAMS':
485
- // valid elements that add little to processing
486
- break;
487
- case 'METHODNAME':
488
- case 'NAME':
489
- /// @todo we could check for 2 NAME elements inside a MEMBER element
490
- $GLOBALS['_xh']['ac']='';
491
- break;
492
- case 'FAULT':
493
- $GLOBALS['_xh']['isf']=1;
494
- break;
495
- case 'MEMBER':
496
- $GLOBALS['_xh']['valuestack'][count($GLOBALS['_xh']['valuestack'])-1]['name']=''; // set member name to null, in case we do not find in the xml later on
497
- //$GLOBALS['_xh']['ac']='';
498
- // Drop trough intentionally
499
- case 'PARAM':
500
- // clear value type, so we can check later if no value has been passed for this param/member
501
- $GLOBALS['_xh']['vt']=null;
502
- break;
503
- case 'NIL':
504
- case 'EX:NIL':
505
- if ($GLOBALS['xmlrpc_null_extension'])
506
- {
507
- if ($GLOBALS['_xh']['vt']!='value')
508
- {
509
- //two data elements inside a value: an error occurred!
510
- $GLOBALS['_xh']['isf'] = 2;
511
- $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
512
- return;
513
- }
514
- $GLOBALS['_xh']['ac']=''; // reset the accumulator
515
- break;
516
- }
517
- // we do not support the <NIL/> extension, so
518
- // drop through intentionally
519
- default:
520
- /// INVALID ELEMENT: RAISE ISF so that it is later recognized!!!
521
- $GLOBALS['_xh']['isf'] = 2;
522
- $GLOBALS['_xh']['isf_reason'] = "found not-xmlrpc xml element $name";
523
- break;
524
- }
525
-
526
- // Save current element name to stack, to validate nesting
527
- $GLOBALS['_xh']['stack'][] = $name;
528
-
529
- /// @todo optimization creep: move this inside the big switch() above
530
- if($name!='VALUE')
531
- {
532
- $GLOBALS['_xh']['lv']=0;
533
- }
534
- }
535
- }
536
-
537
- /// Used in decoding xml chunks that might represent single xmlrpc values
538
- function xmlrpc_se_any($parser, $name, $attrs)
539
- {
540
- xmlrpc_se($parser, $name, $attrs, true);
541
- }
542
-
543
- /// xml parser handler function for close element tags
544
- function xmlrpc_ee($parser, $name, $rebuild_xmlrpcvals = true)
545
- {
546
- if ($GLOBALS['_xh']['isf'] < 2)
547
- {
548
- // push this element name from stack
549
- // NB: if XML validates, correct opening/closing is guaranteed and
550
- // we do not have to check for $name == $curr_elem.
551
- // we also checked for proper nesting at start of elements...
552
- $curr_elem = array_pop($GLOBALS['_xh']['stack']);
553
-
554
- switch($name)
555
- {
556
- case 'VALUE':
557
- // This if() detects if no scalar was inside <VALUE></VALUE>
558
- if ($GLOBALS['_xh']['vt']=='value')
559
- {
560
- $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
561
- $GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcString'];
562
- }
563
-
564
- if ($rebuild_xmlrpcvals)
565
- {
566
- // build the xmlrpc val out of the data received, and substitute it
567
- $temp = new xmlrpcval($GLOBALS['_xh']['value'], $GLOBALS['_xh']['vt']);
568
- // in case we got info about underlying php class, save it
569
- // in the object we're rebuilding
570
- if (isset($GLOBALS['_xh']['php_class']))
571
- $temp->_php_class = $GLOBALS['_xh']['php_class'];
572
- // check if we are inside an array or struct:
573
- // if value just built is inside an array, let's move it into array on the stack
574
- $vscount = count($GLOBALS['_xh']['valuestack']);
575
- if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY')
576
- {
577
- $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][] = $temp;
578
- }
579
- else
580
- {
581
- $GLOBALS['_xh']['value'] = $temp;
582
- }
583
- }
584
- else
585
- {
586
- /// @todo this needs to treat correctly php-serialized objects,
587
- /// since std deserializing is done by php_xmlrpc_decode,
588
- /// which we will not be calling...
589
- if (isset($GLOBALS['_xh']['php_class']))
590
- {
591
- }
592
-
593
- // check if we are inside an array or struct:
594
- // if value just built is inside an array, let's move it into array on the stack
595
- $vscount = count($GLOBALS['_xh']['valuestack']);
596
- if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY')
597
- {
598
- $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][] = $GLOBALS['_xh']['value'];
599
- }
600
- }
601
- break;
602
- case 'BOOLEAN':
603
- case 'I4':
604
- case 'INT':
605
- case 'STRING':
606
- case 'DOUBLE':
607
- case 'DATETIME.ISO8601':
608
- case 'BASE64':
609
- $GLOBALS['_xh']['vt']=strtolower($name);
610
- /// @todo: optimization creep - remove the if/elseif cycle below
611
- /// since the case() in which we are already did that
612
- if ($name=='STRING')
613
- {
614
- $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
615
- }
616
- elseif ($name=='DATETIME.ISO8601')
617
- {
618
- if (!preg_match('/^[0-9]{8}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $GLOBALS['_xh']['ac']))
619
- {
620
- error_log('XML-RPC: invalid value received in DATETIME: '.$GLOBALS['_xh']['ac']);
621
- }
622
- $GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcDateTime'];
623
- $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
624
- }
625
- elseif ($name=='BASE64')
626
- {
627
- /// @todo check for failure of base64 decoding / catch warnings
628
- $GLOBALS['_xh']['value']=base64_decode($GLOBALS['_xh']['ac']);
629
- }
630
- elseif ($name=='BOOLEAN')
631
- {
632
- // special case here: we translate boolean 1 or 0 into PHP
633
- // constants true or false.
634
- // Strings 'true' and 'false' are accepted, even though the
635
- // spec never mentions them (see eg. Blogger api docs)
636
- // NB: this simple checks helps a lot sanitizing input, ie no
637
- // security problems around here
638
- if ($GLOBALS['_xh']['ac']=='1' || strcasecmp($GLOBALS['_xh']['ac'], 'true') == 0)
639
- {
640
- $GLOBALS['_xh']['value']=true;
641
- }
642
- else
643
- {
644
- // log if receiveing something strange, even though we set the value to false anyway
645
- if ($GLOBALS['_xh']['ac']!='0' && strcasecmp($GLOBALS['_xh']['ac'], 'false') != 0)
646
- error_log('XML-RPC: invalid value received in BOOLEAN: '.$GLOBALS['_xh']['ac']);
647
- $GLOBALS['_xh']['value']=false;
648
- }
649
- }
650
- elseif ($name=='DOUBLE')
651
- {
652
- // we have a DOUBLE
653
- // we must check that only 0123456789-.<space> are characters here
654
- // NOTE: regexp could be much stricter than this...
655
- if (!preg_match('/^[+-eE0123456789 \t.]+$/', $GLOBALS['_xh']['ac']))
656
- {
657
- /// @todo: find a better way of throwing an error than this!
658
- error_log('XML-RPC: non numeric value received in DOUBLE: '.$GLOBALS['_xh']['ac']);
659
- $GLOBALS['_xh']['value']='ERROR_NON_NUMERIC_FOUND';
660
- }
661
- else
662
- {
663
- // it's ok, add it on
664
- $GLOBALS['_xh']['value']=(double)$GLOBALS['_xh']['ac'];
665
- }
666
- }
667
- else
668
- {
669
- // we have an I4/INT
670
- // we must check that only 0123456789-<space> are characters here
671
- if (!preg_match('/^[+-]?[0123456789 \t]+$/', $GLOBALS['_xh']['ac']))
672
- {
673
- /// @todo find a better way of throwing an error than this!
674
- error_log('XML-RPC: non numeric value received in INT: '.$GLOBALS['_xh']['ac']);
675
- $GLOBALS['_xh']['value']='ERROR_NON_NUMERIC_FOUND';
676
- }
677
- else
678
- {
679
- // it's ok, add it on
680
- $GLOBALS['_xh']['value']=(int)$GLOBALS['_xh']['ac'];
681
- }
682
- }
683
- //$GLOBALS['_xh']['ac']=''; // is this necessary?
684
- $GLOBALS['_xh']['lv']=3; // indicate we've found a value
685
- break;
686
- case 'NAME':
687
- $GLOBALS['_xh']['valuestack'][count($GLOBALS['_xh']['valuestack'])-1]['name'] = $GLOBALS['_xh']['ac'];
688
- break;
689
- case 'MEMBER':
690
- //$GLOBALS['_xh']['ac']=''; // is this necessary?
691
- // add to array in the stack the last element built,
692
- // unless no VALUE was found
693
- if ($GLOBALS['_xh']['vt'])
694
- {
695
- $vscount = count($GLOBALS['_xh']['valuestack']);
696
- $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][$GLOBALS['_xh']['valuestack'][$vscount-1]['name']] = $GLOBALS['_xh']['value'];
697
- } else
698
- error_log('XML-RPC: missing VALUE inside STRUCT in received xml');
699
- break;
700
- case 'DATA':
701
- //$GLOBALS['_xh']['ac']=''; // is this necessary?
702
- $GLOBALS['_xh']['vt']=null; // reset this to check for 2 data elements in a row - even if they're empty
703
- break;
704
- case 'STRUCT':
705
- case 'ARRAY':
706
- // fetch out of stack array of values, and promote it to current value
707
- $curr_val = array_pop($GLOBALS['_xh']['valuestack']);
708
- $GLOBALS['_xh']['value'] = $curr_val['values'];
709
- $GLOBALS['_xh']['vt']=strtolower($name);
710
- if (isset($curr_val['php_class']))
711
- {
712
- $GLOBALS['_xh']['php_class'] = $curr_val['php_class'];
713
- }
714
- break;
715
- case 'PARAM':
716
- // add to array of params the current value,
717
- // unless no VALUE was found
718
- if ($GLOBALS['_xh']['vt'])
719
- {
720
- $GLOBALS['_xh']['params'][]=$GLOBALS['_xh']['value'];
721
- $GLOBALS['_xh']['pt'][]=$GLOBALS['_xh']['vt'];
722
- }
723
- else
724
- error_log('XML-RPC: missing VALUE inside PARAM in received xml');
725
- break;
726
- case 'METHODNAME':
727
- $GLOBALS['_xh']['method']=preg_replace('/^[\n\r\t ]+/', '', $GLOBALS['_xh']['ac']);
728
- break;
729
- case 'NIL':
730
- case 'EX:NIL':
731
- if ($GLOBALS['xmlrpc_null_extension'])
732
- {
733
- $GLOBALS['_xh']['vt']='null';
734
- $GLOBALS['_xh']['value']=null;
735
- $GLOBALS['_xh']['lv']=3;
736
- break;
737
- }
738
- // drop through intentionally if nil extension not enabled
739
- case 'PARAMS':
740
- case 'FAULT':
741
- case 'METHODCALL':
742
- case 'METHORESPONSE':
743
- break;
744
- default:
745
- // End of INVALID ELEMENT!
746
- // shall we add an assert here for unreachable code???
747
- break;
748
- }
749
- }
750
- }
751
-
752
- /// Used in decoding xmlrpc requests/responses without rebuilding xmlrpc values
753
- function xmlrpc_ee_fast($parser, $name)
754
- {
755
- xmlrpc_ee($parser, $name, false);
756
- }
757
-
758
- /// xml parser handler function for character data
759
- function xmlrpc_cd($parser, $data)
760
- {
761
- // skip processing if xml fault already detected
762
- if ($GLOBALS['_xh']['isf'] < 2)
763
- {
764
- // "lookforvalue==3" means that we've found an entire value
765
- // and should discard any further character data
766
- if($GLOBALS['_xh']['lv']!=3)
767
- {
768
- // G. Giunta 2006-08-23: useless change of 'lv' from 1 to 2
769
- //if($GLOBALS['_xh']['lv']==1)
770
- //{
771
- // if we've found text and we're just in a <value> then
772
- // say we've found a value
773
- //$GLOBALS['_xh']['lv']=2;
774
- //}
775
- // we always initialize the accumulator before starting parsing, anyway...
776
- //if(!@isset($GLOBALS['_xh']['ac']))
777
- //{
778
- // $GLOBALS['_xh']['ac'] = '';
779
- //}
780
- $GLOBALS['_xh']['ac'].=$data;
781
- }
782
- }
783
- }
784
-
785
- /// xml parser handler function for 'other stuff', ie. not char data or
786
- /// element start/end tag. In fact it only gets called on unknown entities...
787
- function xmlrpc_dh($parser, $data)
788
- {
789
- // skip processing if xml fault already detected
790
- if ($GLOBALS['_xh']['isf'] < 2)
791
- {
792
- if(substr($data, 0, 1) == '&' && substr($data, -1, 1) == ';')
793
- {
794
- // G. Giunta 2006-08-25: useless change of 'lv' from 1 to 2
795
- //if($GLOBALS['_xh']['lv']==1)
796
- //{
797
- // $GLOBALS['_xh']['lv']=2;
798
- //}
799
- $GLOBALS['_xh']['ac'].=$data;
800
- }
801
- }
802
- return true;
803
- }
804
-
805
- class xmlrpc_client
806
- {
807
- var $path;
808
- var $server;
809
- var $port=0;
810
- var $method='http';
811
- var $errno;
812
- var $errstr;
813
- var $debug=0;
814
- var $username='';
815
- var $password='';
816
- var $authtype=1;
817
- var $cert='';
818
- var $certpass='';
819
- var $cacert='';
820
- var $cacertdir='';
821
- var $key='';
822
- var $keypass='';
823
- var $verifypeer=true;
824
- var $verifyhost=1;
825
- var $no_multicall=false;
826
- var $proxy='';
827
- var $proxyport=0;
828
- var $proxy_user='';
829
- var $proxy_pass='';
830
- var $proxy_authtype=1;
831
- var $cookies=array();
832
- var $extracurlopts=array();
833
-
834
- /**
835
- * List of http compression methods accepted by the client for responses.
836
- * NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib
837
- *
838
- * NNB: you can set it to any non-empty array for HTTP11 and HTTPS, since
839
- * in those cases it will be up to CURL to decide the compression methods
840
- * it supports. You might check for the presence of 'zlib' in the output of
841
- * curl_version() to determine wheter compression is supported or not
842
- */
843
- var $accepted_compression = array();
844
- /**
845
- * Name of compression scheme to be used for sending requests.
846
- * Either null, gzip or deflate
847
- */
848
- var $request_compression = '';
849
- /**
850
- * CURL handle: used for keep-alive connections (PHP 4.3.8 up, see:
851
- * http://curl.haxx.se/docs/faq.html#7.3)
852
- */
853
- var $xmlrpc_curl_handle = null;
854
- /// Wheter to use persistent connections for http 1.1 and https
855
- var $keepalive = false;
856
- /// Charset encodings that can be decoded without problems by the client
857
- var $accepted_charset_encodings = array();
858
- /// Charset encoding to be used in serializing request. NULL = use ASCII
859
- var $request_charset_encoding = '';
860
- /**
861
- * Decides the content of xmlrpcresp objects returned by calls to send()
862
- * valid strings are 'xmlrpcvals', 'phpvals' or 'xml'
863
- */
864
- var $return_type = 'xmlrpcvals';
865
- /**
866
- * Sent to servers in http headers
867
- */
868
- var $user_agent;
869
-
870
- /**
871
- * @param string $path either the complete server URL or the PATH part of the xmlrc server URL, e.g. /xmlrpc/server.php
872
- * @param string $server the server name / ip address
873
- * @param integer $port the port the server is listening on, defaults to 80 or 443 depending on protocol used
874
- * @param string $method the http protocol variant: defaults to 'http', 'https' and 'http11' can be used if CURL is installed
875
- */
876
- function xmlrpc_client($path, $server='', $port='', $method='')
877
- {
878
- // allow user to specify all params in $path
879
- if($server == '' and $port == '' and $method == '')
880
- {
881
- $parts = parse_url($path);
882
- $server = $parts['host'];
883
- $path = isset($parts['path']) ? $parts['path'] : '';
884
- if(isset($parts['query']))
885
- {
886
- $path .= '?'.$parts['query'];
887
- }
888
- if(isset($parts['fragment']))
889
- {
890
- $path .= '#'.$parts['fragment'];
891
- }
892
- if(isset($parts['port']))
893
- {
894
- $port = $parts['port'];
895
- }
896
- if(isset($parts['scheme']))
897
- {
898
- $method = $parts['scheme'];
899
- }
900
- if(isset($parts['user']))
901
- {
902
- $this->username = $parts['user'];
903
- }
904
- if(isset($parts['pass']))
905
- {
906
- $this->password = $parts['pass'];
907
- }
908
- }
909
- if($path == '' || $path[0] != '/')
910
- {
911
- $this->path='/'.$path;
912
- }
913
- else
914
- {
915
- $this->path=$path;
916
- }
917
- $this->server=$server;
918
- if($port != '')
919
- {
920
- $this->port=$port;
921
- }
922
- if($method != '')
923
- {
924
- $this->method=$method;
925
- }
926
-
927
- // if ZLIB is enabled, let the client by default accept compressed responses
928
- if(function_exists('gzinflate') || (
929
- function_exists('curl_init') && (($info = curl_version()) &&
930
- ((is_string($info) && strpos($info, 'zlib') !== null) || isset($info['libz_version'])))
931
- ))
932
- {
933
- $this->accepted_compression = array('gzip', 'deflate');
934
- }
935
-
936
- // keepalives: enabled by default
937
- $this->keepalive = true;
938
-
939
- // by default the xml parser can support these 3 charset encodings
940
- $this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII');
941
-
942
- // initialize user_agent string
943
- $this->user_agent = $GLOBALS['xmlrpcName'] . ' ' . $GLOBALS['xmlrpcVersion'];
944
- }
945
-
946
- /**
947
- * Enables/disables the echoing to screen of the xmlrpc responses received
948
- * @param integer $debug values 0, 1 and 2 are supported (2 = echo sent msg too, before received response)
949
- * @access public
950
- */
951
- function setDebug($in)
952
- {
953
- $this->debug=$in;
954
- }
955
-
956
- /**
957
- * Add some http BASIC AUTH credentials, used by the client to authenticate
958
- * @param string $u username
959
- * @param string $p password
960
- * @param integer $t auth type. See curl_setopt man page for supported auth types. Defaults to CURLAUTH_BASIC (basic auth)
961
- * @access public
962
- */
963
- function setCredentials($u, $p, $t=1)
964
- {
965
- $this->username=$u;
966
- $this->password=$p;
967
- $this->authtype=$t;
968
- }
969
-
970
- /**
971
- * Add a client-side https certificate
972
- * @param string $cert
973
- * @param string $certpass
974
- * @access public
975
- */
976
- function setCertificate($cert, $certpass)
977
- {
978
- $this->cert = $cert;
979
- $this->certpass = $certpass;
980
- }
981
-
982
- /**
983
- * Add a CA certificate to verify server with (see man page about
984
- * CURLOPT_CAINFO for more details
985
- * @param string $cacert certificate file name (or dir holding certificates)
986
- * @param bool $is_dir set to true to indicate cacert is a dir. defaults to false
987
- * @access public
988
- */
989
- function setCaCertificate($cacert, $is_dir=false)
990
- {
991
- if ($is_dir)
992
- {
993
- $this->cacertdir = $cacert;
994
- }
995
- else
996
- {
997
- $this->cacert = $cacert;
998
- }
999
- }
1000
-
1001
- /**
1002
- * Set attributes for SSL communication: private SSL key
1003
- * NB: does not work in older php/curl installs
1004
- * Thanks to Daniel Convissor
1005
- * @param string $key The name of a file containing a private SSL key
1006
- * @param string $keypass The secret password needed to use the private SSL key
1007
- * @access public
1008
- */
1009
- function setKey($key, $keypass)
1010
- {
1011
- $this->key = $key;
1012
- $this->keypass = $keypass;
1013
- }
1014
-
1015
- /**
1016
- * Set attributes for SSL communication: verify server certificate
1017
- * @param bool $i enable/disable verification of peer certificate
1018
- * @access public
1019
- */
1020
- function setSSLVerifyPeer($i)
1021
- {
1022
- $this->verifypeer = $i;
1023
- }
1024
-
1025
- /**
1026
- * Set attributes for SSL communication: verify match of server cert w. hostname
1027
- * @param int $i
1028
- * @access public
1029
- */
1030
- function setSSLVerifyHost($i)
1031
- {
1032
- $this->verifyhost = $i;
1033
- }
1034
-
1035
- /**
1036
- * Set proxy info
1037
- * @param string $proxyhost
1038
- * @param string $proxyport Defaults to 8080 for HTTP and 443 for HTTPS
1039
- * @param string $proxyusername Leave blank if proxy has public access
1040
- * @param string $proxypassword Leave blank if proxy has public access
1041
- * @param int $proxyauthtype set to constant CURLAUTH_NTLM to use NTLM auth with proxy
1042
- * @access public
1043
- */
1044
- function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 1)
1045
- {
1046
- $this->proxy = $proxyhost;
1047
- $this->proxyport = $proxyport;
1048
- $this->proxy_user = $proxyusername;
1049
- $this->proxy_pass = $proxypassword;
1050
- $this->proxy_authtype = $proxyauthtype;
1051
- }
1052
-
1053
- /**
1054
- * Enables/disables reception of compressed xmlrpc responses.
1055
- * Note that enabling reception of compressed responses merely adds some standard
1056
- * http headers to xmlrpc requests. It is up to the xmlrpc server to return
1057
- * compressed responses when receiving such requests.
1058
- * @param string $compmethod either 'gzip', 'deflate', 'any' or ''
1059
- * @access public
1060
- */
1061
- function setAcceptedCompression($compmethod)
1062
- {
1063
- if ($compmethod == 'any')
1064
- $this->accepted_compression = array('gzip', 'deflate');
1065
- else
1066
- $this->accepted_compression = array($compmethod);
1067
- }
1068
-
1069
- /**
1070
- * Enables/disables http compression of xmlrpc request.
1071
- * Take care when sending compressed requests: servers might not support them
1072
- * (and automatic fallback to uncompressed requests is not yet implemented)
1073
- * @param string $compmethod either 'gzip', 'deflate' or ''
1074
- * @access public
1075
- */
1076
- function setRequestCompression($compmethod)
1077
- {
1078
- $this->request_compression = $compmethod;
1079
- }
1080
-
1081
- /**
1082
- * Adds a cookie to list of cookies that will be sent to server.
1083
- * NB: setting any param but name and value will turn the cookie into a 'version 1' cookie:
1084
- * do not do it unless you know what you are doing
1085
- * @param string $name
1086
- * @param string $value
1087
- * @param string $path
1088
- * @param string $domain
1089
- * @param int $port
1090
- * @access public
1091
- *
1092
- * @todo check correctness of urlencoding cookie value (copied from php way of doing it...)
1093
- */
1094
- function setCookie($name, $value='', $path='', $domain='', $port=null)
1095
- {
1096
- $this->cookies[$name]['value'] = urlencode($value);
1097
- if ($path || $domain || $port)
1098
- {
1099
- $this->cookies[$name]['path'] = $path;
1100
- $this->cookies[$name]['domain'] = $domain;
1101
- $this->cookies[$name]['port'] = $port;
1102
- $this->cookies[$name]['version'] = 1;
1103
- }
1104
- else
1105
- {
1106
- $this->cookies[$name]['version'] = 0;
1107
- }
1108
- }
1109
-
1110
- /**
1111
- * Directly set cURL options, for extra flexibility
1112
- * It allows eg. to bind client to a specific IP interface / address
1113
- * @param $options array
1114
- */
1115
- function SetCurlOptions( $options )
1116
- {
1117
- $this->extracurlopts = $options;
1118
- }
1119
-
1120
- /**
1121
- * Set user-agent string that will be used by this client instance
1122
- * in http headers sent to the server
1123
- */
1124
- function SetUserAgent( $agentstring )
1125
- {
1126
- $this->user_agent = $agentstring;
1127
- }
1128
-
1129
- /**
1130
- * Send an xmlrpc request
1131
- * @param mixed $msg The message object, or an array of messages for using multicall, or the complete xml representation of a request
1132
- * @param integer $timeout Connection timeout, in seconds, If unspecified, a platform specific timeout will apply
1133
- * @param string $method if left unspecified, the http protocol chosen during creation of the object will be used
1134
- * @return xmlrpcresp
1135
- * @access public
1136
- */
1137
- function& send($msg, $timeout=0, $method='')
1138
- {
1139
- // if user deos not specify http protocol, use native method of this client
1140
- // (i.e. method set during call to constructor)
1141
- if($method == '')
1142
- {
1143
- $method = $this->method;
1144
- }
1145
-
1146
- if(is_array($msg))
1147
- {
1148
- // $msg is an array of xmlrpcmsg's
1149
- $r = $this->multicall($msg, $timeout, $method);
1150
- return $r;
1151
- }
1152
- elseif(is_string($msg))
1153
- {
1154
- $n = new xmlrpcmsg('');
1155
- $n->payload = $msg;
1156
- $msg = $n;
1157
- }
1158
-
1159
- // where msg is an xmlrpcmsg
1160
- $msg->debug=$this->debug;
1161
-
1162
- if($method == 'https')
1163
- {
1164
- $r =& $this->sendPayloadHTTPS(
1165
- $msg,
1166
- $this->server,
1167
- $this->port,
1168
- $timeout,
1169
- $this->username,
1170
- $this->password,
1171
- $this->authtype,
1172
- $this->cert,
1173
- $this->certpass,
1174
- $this->cacert,
1175
- $this->cacertdir,
1176
- $this->proxy,
1177
- $this->proxyport,
1178
- $this->proxy_user,
1179
- $this->proxy_pass,
1180
- $this->proxy_authtype,
1181
- $this->keepalive,
1182
- $this->key,
1183
- $this->keypass
1184
- );
1185
- }
1186
- elseif($method == 'http11')
1187
- {
1188
- $r =& $this->sendPayloadCURL(
1189
- $msg,
1190
- $this->server,
1191
- $this->port,
1192
- $timeout,
1193
- $this->username,
1194
- $this->password,
1195
- $this->authtype,
1196
- null,
1197
- null,
1198
- null,
1199
- null,
1200
- $this->proxy,
1201
- $this->proxyport,
1202
- $this->proxy_user,
1203
- $this->proxy_pass,
1204
- $this->proxy_authtype,
1205
- 'http',
1206
- $this->keepalive
1207
- );
1208
- }
1209
- else
1210
- {
1211
- $r =& $this->sendPayloadHTTP10(
1212
- $msg,
1213
- $this->server,
1214
- $this->port,
1215
- $timeout,
1216
- $this->username,
1217
- $this->password,
1218
- $this->authtype,
1219
- $this->proxy,
1220
- $this->proxyport,
1221
- $this->proxy_user,
1222
- $this->proxy_pass,
1223
- $this->proxy_authtype
1224
- );
1225
- }
1226
-
1227
- return $r;
1228
- }
1229
-
1230
- /**
1231
- * @access private
1232
- */
1233
- function &sendPayloadHTTP10($msg, $server, $port, $timeout=0,
1234
- $username='', $password='', $authtype=1, $proxyhost='',
1235
- $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1)
1236
- {
1237
- if($port==0)
1238
- {
1239
- $port=80;
1240
- }
1241
-
1242
- // Only create the payload if it was not created previously
1243
- if(empty($msg->payload))
1244
- {
1245
- $msg->createPayload($this->request_charset_encoding);
1246
- }
1247
-
1248
- $payload = $msg->payload;
1249
- // Deflate request body and set appropriate request headers
1250
- if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate'))
1251
- {
1252
- if($this->request_compression == 'gzip')
1253
- {
1254
- $a = @gzencode($payload);
1255
- if($a)
1256
- {
1257
- $payload = $a;
1258
- $encoding_hdr = "Content-Encoding: gzip\r\n";
1259
- }
1260
- }
1261
- else
1262
- {
1263
- $a = @gzcompress($payload);
1264
- if($a)
1265
- {
1266
- $payload = $a;
1267
- $encoding_hdr = "Content-Encoding: deflate\r\n";
1268
- }
1269
- }
1270
- }
1271
- else
1272
- {
1273
- $encoding_hdr = '';
1274
- }
1275
-
1276
- // thanks to Grant Rauscher <grant7@firstworld.net> for this
1277
- $credentials='';
1278
- if($username!='')
1279
- {
1280
- $credentials='Authorization: Basic ' . base64_encode($username . ':' . $password) . "\r\n";
1281
- if ($authtype != 1)
1282
- {
1283
- error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth is supported with HTTP 1.0');
1284
- }
1285
- }
1286
-
1287
- $accepted_encoding = '';
1288
- if(is_array($this->accepted_compression) && count($this->accepted_compression))
1289
- {
1290
- $accepted_encoding = 'Accept-Encoding: ' . implode(', ', $this->accepted_compression) . "\r\n";
1291
- }
1292
-
1293
- $proxy_credentials = '';
1294
- if($proxyhost)
1295
- {
1296
- if($proxyport == 0)
1297
- {
1298
- $proxyport = 8080;
1299
- }
1300
- $connectserver = $proxyhost;
1301
- $connectport = $proxyport;
1302
- $uri = 'http://'.$server.':'.$port.$this->path;
1303
- if($proxyusername != '')
1304
- {
1305
- if ($proxyauthtype != 1)
1306
- {
1307
- error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth to proxy is supported with HTTP 1.0');
1308
- }
1309
- $proxy_credentials = 'Proxy-Authorization: Basic ' . base64_encode($proxyusername.':'.$proxypassword) . "\r\n";
1310
- }
1311
- }
1312
- else
1313
- {
1314
- $connectserver = $server;
1315
- $connectport = $port;
1316
- $uri = $this->path;
1317
- }
1318
-
1319
- // Cookie generation, as per rfc2965 (version 1 cookies) or
1320
- // netscape's rules (version 0 cookies)
1321
- $cookieheader='';
1322
- if (count($this->cookies))
1323
- {
1324
- $version = '';
1325
- foreach ($this->cookies as $name => $cookie)
1326
- {
1327
- if ($cookie['version'])
1328
- {
1329
- $version = ' $Version="' . $cookie['version'] . '";';
1330
- $cookieheader .= ' ' . $name . '="' . $cookie['value'] . '";';
1331
- if ($cookie['path'])
1332
- $cookieheader .= ' $Path="' . $cookie['path'] . '";';
1333
- if ($cookie['domain'])
1334
- $cookieheader .= ' $Domain="' . $cookie['domain'] . '";';
1335
- if ($cookie['port'])
1336
- $cookieheader .= ' $Port="' . $cookie['port'] . '";';
1337
- }
1338
- else
1339
- {
1340
- $cookieheader .= ' ' . $name . '=' . $cookie['value'] . ";";
1341
- }
1342
- }
1343
- $cookieheader = 'Cookie:' . $version . substr($cookieheader, 0, -1) . "\r\n";
1344
- }
1345
-
1346
- $op= 'POST ' . $uri. " HTTP/1.0\r\n" .
1347
- 'User-Agent: ' . $this->user_agent . "\r\n" .
1348
- 'Host: '. $server . ':' . $port . "\r\n" .
1349
- $credentials .
1350
- $proxy_credentials .
1351
- $accepted_encoding .
1352
- $encoding_hdr .
1353
- 'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings) . "\r\n" .
1354
- $cookieheader .
1355
- 'Content-Type: ' . $msg->content_type . "\r\nContent-Length: " .
1356
- strlen($payload) . "\r\n\r\n" .
1357
- $payload;
1358
-
1359
- if($this->debug > 1)
1360
- {
1361
- print "<PRE>\n---SENDING---\n" . htmlentities($op) . "\n---END---\n</PRE>";
1362
- // let the client see this now in case http times out...
1363
- flush();
1364
- }
1365
-
1366
- if($timeout>0)
1367
- {
1368
- $fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr, $timeout);
1369
- }
1370
- else
1371
- {
1372
- $fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr);
1373
- }
1374
- if($fp)
1375
- {
1376
- if($timeout>0 && function_exists('stream_set_timeout'))
1377
- {
1378
- stream_set_timeout($fp, $timeout);
1379
- }
1380
- }
1381
- else
1382
- {
1383
- $this->errstr='Connect error: '.$this->errstr;
1384
- $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr . ' (' . $this->errno . ')');
1385
- return $r;
1386
- }
1387
-
1388
- if(!fputs($fp, $op, strlen($op)))
1389
- {
1390
- fclose($fp);
1391
- $this->errstr='Write error';
1392
- $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr);
1393
- return $r;
1394
- }
1395
- else
1396
- {
1397
- // reset errno and errstr on succesful socket connection
1398
- $this->errstr = '';
1399
- }
1400
- // G. Giunta 2005/10/24: close socket before parsing.
1401
- // should yeld slightly better execution times, and make easier recursive calls (e.g. to follow http redirects)
1402
- $ipd='';
1403
- do
1404
- {
1405
- // shall we check for $data === FALSE?
1406
- // as per the manual, it signals an error
1407
- $ipd.=fread($fp, 32768);
1408
- } while(!feof($fp));
1409
- fclose($fp);
1410
- $r =& $msg->parseResponse($ipd, false, $this->return_type);
1411
- return $r;
1412
-
1413
- }
1414
-
1415
- /**
1416
- * @access private
1417
- */
1418
- function &sendPayloadHTTPS($msg, $server, $port, $timeout=0, $username='',
1419
- $password='', $authtype=1, $cert='',$certpass='', $cacert='', $cacertdir='',
1420
- $proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1,
1421
- $keepalive=false, $key='', $keypass='')
1422
- {
1423
- $r =& $this->sendPayloadCURL($msg, $server, $port, $timeout, $username,
1424
- $password, $authtype, $cert, $certpass, $cacert, $cacertdir, $proxyhost, $proxyport,
1425
- $proxyusername, $proxypassword, $proxyauthtype, 'https', $keepalive, $key, $keypass);
1426
- return $r;
1427
- }
1428
-
1429
- /**
1430
- * Contributed by Justin Miller <justin@voxel.net>
1431
- * Requires curl to be built into PHP
1432
- * NB: CURL versions before 7.11.10 cannot use proxy to talk to https servers!
1433
- * @access private
1434
- */
1435
- function &sendPayloadCURL($msg, $server, $port, $timeout=0, $username='',
1436
- $password='', $authtype=1, $cert='', $certpass='', $cacert='', $cacertdir='',
1437
- $proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1, $method='https',
1438
- $keepalive=false, $key='', $keypass='')
1439
- {
1440
- if(!function_exists('curl_init'))
1441
- {
1442
- $this->errstr='CURL unavailable on this install';
1443
- $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_curl'], $GLOBALS['xmlrpcstr']['no_curl']);
1444
- return $r;
1445
- }
1446
- if($method == 'https')
1447
- {
1448
- if(($info = curl_version()) &&
1449
- ((is_string($info) && strpos($info, 'OpenSSL') === null) || (is_array($info) && !isset($info['ssl_version']))))
1450
- {
1451
- $this->errstr='SSL unavailable on this install';
1452
- $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_ssl'], $GLOBALS['xmlrpcstr']['no_ssl']);
1453
- return $r;
1454
- }
1455
- }
1456
-
1457
- if($port == 0)
1458
- {
1459
- if($method == 'http')
1460
- {
1461
- $port = 80;
1462
- }
1463
- else
1464
- {
1465
- $port = 443;
1466
- }
1467
- }
1468
-
1469
- // Only create the payload if it was not created previously
1470
- if(empty($msg->payload))
1471
- {
1472
- $msg->createPayload($this->request_charset_encoding);
1473
- }
1474
-
1475
- // Deflate request body and set appropriate request headers
1476
- $payload = $msg->payload;
1477
- if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate'))
1478
- {
1479
- if($this->request_compression == 'gzip')
1480
- {
1481
- $a = @gzencode($payload);
1482
- if($a)
1483
- {
1484
- $payload = $a;
1485
- $encoding_hdr = 'Content-Encoding: gzip';
1486
- }
1487
- }
1488
- else
1489
- {
1490
- $a = @gzcompress($payload);
1491
- if($a)
1492
- {
1493
- $payload = $a;
1494
- $encoding_hdr = 'Content-Encoding: deflate';
1495
- }
1496
- }
1497
- }
1498
- else
1499
- {
1500
- $encoding_hdr = '';
1501
- }
1502
-
1503
- if($this->debug > 1)
1504
- {
1505
- print "<PRE>\n---SENDING---\n" . htmlentities($payload) . "\n---END---\n</PRE>";
1506
- // let the client see this now in case http times out...
1507
- flush();
1508
- }
1509
-
1510
- if(!$keepalive || !$this->xmlrpc_curl_handle)
1511
- {
1512
- $curl = curl_init($method . '://' . $server . ':' . $port . $this->path);
1513
- if($keepalive)
1514
- {
1515
- $this->xmlrpc_curl_handle = $curl;
1516
- }
1517
- }
1518
- else
1519
- {
1520
- $curl = $this->xmlrpc_curl_handle;
1521
- }
1522
-
1523
- // results into variable
1524
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
1525
-
1526
- if($this->debug)
1527
- {
1528
- curl_setopt($curl, CURLOPT_VERBOSE, 1);
1529
- }
1530
- curl_setopt($curl, CURLOPT_USERAGENT, $this->user_agent);
1531
- // required for XMLRPC: post the data
1532
- curl_setopt($curl, CURLOPT_POST, 1);
1533
- // the data
1534
- curl_setopt($curl, CURLOPT_POSTFIELDS, $payload);
1535
-
1536
- // return the header too
1537
- curl_setopt($curl, CURLOPT_HEADER, 1);
1538
-
1539
- // will only work with PHP >= 5.0
1540
- // NB: if we set an empty string, CURL will add http header indicating
1541
- // ALL methods it is supporting. This is possibly a better option than
1542
- // letting the user tell what curl can / cannot do...
1543
- if(is_array($this->accepted_compression) && count($this->accepted_compression))
1544
- {
1545
- //curl_setopt($curl, CURLOPT_ENCODING, implode(',', $this->accepted_compression));
1546
- // empty string means 'any supported by CURL' (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1547
- if (count($this->accepted_compression) == 1)
1548
- {
1549
- curl_setopt($curl, CURLOPT_ENCODING, $this->accepted_compression[0]);
1550
- }
1551
- else
1552
- curl_setopt($curl, CURLOPT_ENCODING, '');
1553
- }
1554
- // extra headers
1555
- $headers = array('Content-Type: ' . $msg->content_type , 'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings));
1556
- // if no keepalive is wanted, let the server know it in advance
1557
- if(!$keepalive)
1558
- {
1559
- $headers[] = 'Connection: close';
1560
- }
1561
- // request compression header
1562
- if($encoding_hdr)
1563
- {
1564
- $headers[] = $encoding_hdr;
1565
- }
1566
-
1567
- curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
1568
- // timeout is borked
1569
- if($timeout)
1570
- {
1571
- curl_setopt($curl, CURLOPT_TIMEOUT, $timeout == 1 ? 1 : $timeout - 1);
1572
- }
1573
-
1574
- if($username && $password)
1575
- {
1576
- curl_setopt($curl, CURLOPT_USERPWD, $username.':'.$password);
1577
- if (defined('CURLOPT_HTTPAUTH'))
1578
- {
1579
- curl_setopt($curl, CURLOPT_HTTPAUTH, $authtype);
1580
- }
1581
- else if ($authtype != 1)
1582
- {
1583
- error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth is supported by the current PHP/curl install');
1584
- }
1585
- }
1586
-
1587
- if($method == 'https')
1588
- {
1589
- // set cert file
1590
- if($cert)
1591
- {
1592
- curl_setopt($curl, CURLOPT_SSLCERT, $cert);
1593
- }
1594
- // set cert password
1595
- if($certpass)
1596
- {
1597
- curl_setopt($curl, CURLOPT_SSLCERTPASSWD, $certpass);
1598
- }
1599
- // whether to verify remote host's cert
1600
- curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verifypeer);
1601
- // set ca certificates file/dir
1602
- if($cacert)
1603
- {
1604
- curl_setopt($curl, CURLOPT_CAINFO, $cacert);
1605
- }
1606
- if($cacertdir)
1607
- {
1608
- curl_setopt($curl, CURLOPT_CAPATH, $cacertdir);
1609
- }
1610
- // set key file (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1611
- if($key)
1612
- {
1613
- curl_setopt($curl, CURLOPT_SSLKEY, $key);
1614
- }
1615
- // set key password (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1616
- if($keypass)
1617
- {
1618
- curl_setopt($curl, CURLOPT_SSLKEYPASSWD, $keypass);
1619
- }
1620
- // whether to verify cert's common name (CN); 0 for no, 1 to verify that it exists, and 2 to verify that it matches the hostname used
1621
- curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $this->verifyhost);
1622
- }
1623
-
1624
- // proxy info
1625
- if($proxyhost)
1626
- {
1627
- if($proxyport == 0)
1628
- {
1629
- $proxyport = 8080; // NB: even for HTTPS, local connection is on port 8080
1630
- }
1631
- curl_setopt($curl, CURLOPT_PROXY, $proxyhost.':'.$proxyport);
1632
- //curl_setopt($curl, CURLOPT_PROXYPORT,$proxyport);
1633
- if($proxyusername)
1634
- {
1635
- curl_setopt($curl, CURLOPT_PROXYUSERPWD, $proxyusername.':'.$proxypassword);
1636
- if (defined('CURLOPT_PROXYAUTH'))
1637
- {
1638
- curl_setopt($curl, CURLOPT_PROXYAUTH, $proxyauthtype);
1639
- }
1640
- else if ($proxyauthtype != 1)
1641
- {
1642
- error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth to proxy is supported by the current PHP/curl install');
1643
- }
1644
- }
1645
- }
1646
-
1647
- // NB: should we build cookie http headers by hand rather than let CURL do it?
1648
- // the following code does not honour 'expires', 'path' and 'domain' cookie attributes
1649
- // set to client obj the the user...
1650
- if (count($this->cookies))
1651
- {
1652
- $cookieheader = '';
1653
- foreach ($this->cookies as $name => $cookie)
1654
- {
1655
- $cookieheader .= $name . '=' . $cookie['value'] . '; ';
1656
- }
1657
- curl_setopt($curl, CURLOPT_COOKIE, substr($cookieheader, 0, -2));
1658
- }
1659
-
1660
- foreach ($this->extracurlopts as $opt => $val)
1661
- {
1662
- curl_setopt($curl, $opt, $val);
1663
- }
1664
-
1665
- $result = curl_exec($curl);
1666
-
1667
- if ($this->debug > 1)
1668
- {
1669
- print "<PRE>\n---CURL INFO---\n";
1670
- foreach(curl_getinfo($curl) as $name => $val)
1671
- print $name . ': ' . htmlentities($val). "\n";
1672
- print "---END---\n</PRE>";
1673
- }
1674
-
1675
- if(!$result) /// @todo we should use a better check here - what if we get back '' or '0'?
1676
- {
1677
- $this->errstr='no response';
1678
- $resp=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['curl_fail'], $GLOBALS['xmlrpcstr']['curl_fail']. ': '. curl_error($curl));
1679
- curl_close($curl);
1680
- if($keepalive)
1681
- {
1682
- $this->xmlrpc_curl_handle = null;
1683
- }
1684
- }
1685
- else
1686
- {
1687
- if(!$keepalive)
1688
- {
1689
- curl_close($curl);
1690
- }
1691
- $resp =& $msg->parseResponse($result, true, $this->return_type);
1692
- }
1693
- return $resp;
1694
- }
1695
-
1696
- /**
1697
- * Send an array of request messages and return an array of responses.
1698
- * Unless $this->no_multicall has been set to true, it will try first
1699
- * to use one single xmlrpc call to server method system.multicall, and
1700
- * revert to sending many successive calls in case of failure.
1701
- * This failure is also stored in $this->no_multicall for subsequent calls.
1702
- * Unfortunately, there is no server error code universally used to denote
1703
- * the fact that multicall is unsupported, so there is no way to reliably
1704
- * distinguish between that and a temporary failure.
1705
- * If you are sure that server supports multicall and do not want to
1706
- * fallback to using many single calls, set the fourth parameter to FALSE.
1707
- *
1708
- * NB: trying to shoehorn extra functionality into existing syntax has resulted
1709
- * in pretty much convoluted code...
1710
- *
1711
- * @param array $msgs an array of xmlrpcmsg objects
1712
- * @param integer $timeout connection timeout (in seconds)
1713
- * @param string $method the http protocol variant to be used
1714
- * @param boolean fallback When true, upon receiveing an error during multicall, multiple single calls will be attempted
1715
- * @return array
1716
- * @access public
1717
- */
1718
- function multicall($msgs, $timeout=0, $method='', $fallback=true)
1719
- {
1720
- if ($method == '')
1721
- {
1722
- $method = $this->method;
1723
- }
1724
- if(!$this->no_multicall)
1725
- {
1726
- $results = $this->_try_multicall($msgs, $timeout, $method);
1727
- if(is_array($results))
1728
- {
1729
- // System.multicall succeeded
1730
- return $results;
1731
- }
1732
- else
1733
- {
1734
- // either system.multicall is unsupported by server,
1735
- // or call failed for some other reason.
1736
- if ($fallback)
1737
- {
1738
- // Don't try it next time...
1739
- $this->no_multicall = true;
1740
- }
1741
- else
1742
- {
1743
- if (is_a($results, 'xmlrpcresp'))
1744
- {
1745
- $result = $results;
1746
- }
1747
- else
1748
- {
1749
- $result = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['multicall_error'], $GLOBALS['xmlrpcstr']['multicall_error']);
1750
- }
1751
- }
1752
- }
1753
- }
1754
- else
1755
- {
1756
- // override fallback, in case careless user tries to do two
1757
- // opposite things at the same time
1758
- $fallback = true;
1759
- }
1760
-
1761
- $results = array();
1762
- if ($fallback)
1763
- {
1764
- // system.multicall is (probably) unsupported by server:
1765
- // emulate multicall via multiple requests
1766
- foreach($msgs as $msg)
1767
- {
1768
- $results[] =& $this->send($msg, $timeout, $method);
1769
- }
1770
- }
1771
- else
1772
- {
1773
- // user does NOT want to fallback on many single calls:
1774
- // since we should always return an array of responses,
1775
- // return an array with the same error repeated n times
1776
- foreach($msgs as $msg)
1777
- {
1778
- $results[] = $result;
1779
- }
1780
- }
1781
- return $results;
1782
- }
1783
-
1784
- /**
1785
- * Attempt to boxcar $msgs via system.multicall.
1786
- * Returns either an array of xmlrpcreponses, an xmlrpc error response
1787
- * or false (when received response does not respect valid multicall syntax)
1788
- * @access private
1789
- */
1790
- function _try_multicall($msgs, $timeout, $method)
1791
- {
1792
- // Construct multicall message
1793
- $calls = array();
1794
- foreach($msgs as $msg)
1795
- {
1796
- $call['methodName'] = new xmlrpcval($msg->method(),'string');
1797
- $numParams = $msg->getNumParams();
1798
- $params = array();
1799
- for($i = 0; $i < $numParams; $i++)
1800
- {
1801
- $params[$i] = $msg->getParam($i);
1802
- }
1803
- $call['params'] = new xmlrpcval($params, 'array');
1804
- $calls[] = new xmlrpcval($call, 'struct');
1805
- }
1806
- $multicall = new xmlrpcmsg('system.multicall');
1807
- $multicall->addParam(new xmlrpcval($calls, 'array'));
1808
-
1809
- // Attempt RPC call
1810
- $result =& $this->send($multicall, $timeout, $method);
1811
-
1812
- if($result->faultCode() != 0)
1813
- {
1814
- // call to system.multicall failed
1815
- return $result;
1816
- }
1817
-
1818
- // Unpack responses.
1819
- $rets = $result->value();
1820
-
1821
- if ($this->return_type == 'xml')
1822
- {
1823
- return $rets;
1824
- }
1825
- else if ($this->return_type == 'phpvals')
1826
- {
1827
- ///@todo test this code branch...
1828
- $rets = $result->value();
1829
- if(!is_array($rets))
1830
- {
1831
- return false; // bad return type from system.multicall
1832
- }
1833
- $numRets = count($rets);
1834
- if($numRets != count($msgs))
1835
- {
1836
- return false; // wrong number of return values.
1837
- }
1838
-
1839
- $response = array();
1840
- for($i = 0; $i < $numRets; $i++)
1841
- {
1842
- $val = $rets[$i];
1843
- if (!is_array($val)) {
1844
- return false;
1845
- }
1846
- switch(count($val))
1847
- {
1848
- case 1:
1849
- if(!isset($val[0]))
1850
- {
1851
- return false; // Bad value
1852
- }
1853
- // Normal return value
1854
- $response[$i] = new xmlrpcresp($val[0], 0, '', 'phpvals');
1855
- break;
1856
- case 2:
1857
- /// @todo remove usage of @: it is apparently quite slow
1858
- $code = @$val['faultCode'];
1859
- if(!is_int($code))
1860
- {
1861
- return false;
1862
- }
1863
- $str = @$val['faultString'];
1864
- if(!is_string($str))
1865
- {
1866
- return false;
1867
- }
1868
- $response[$i] = new xmlrpcresp(0, $code, $str);
1869
- break;
1870
- default:
1871
- return false;
1872
- }
1873
- }
1874
- return $response;
1875
- }
1876
- else // return type == 'xmlrpcvals'
1877
- {
1878
- $rets = $result->value();
1879
- if($rets->kindOf() != 'array')
1880
- {
1881
- return false; // bad return type from system.multicall
1882
- }
1883
- $numRets = $rets->arraysize();
1884
- if($numRets != count($msgs))
1885
- {
1886
- return false; // wrong number of return values.
1887
- }
1888
-
1889
- $response = array();
1890
- for($i = 0; $i < $numRets; $i++)
1891
- {
1892
- $val = $rets->arraymem($i);
1893
- switch($val->kindOf())
1894
- {
1895
- case 'array':
1896
- if($val->arraysize() != 1)
1897
- {
1898
- return false; // Bad value
1899
- }
1900
- // Normal return value
1901
- $response[$i] = new xmlrpcresp($val->arraymem(0));
1902
- break;
1903
- case 'struct':
1904
- $code = $val->structmem('faultCode');
1905
- if($code->kindOf() != 'scalar' || $code->scalartyp() != 'int')
1906
- {
1907
- return false;
1908
- }
1909
- $str = $val->structmem('faultString');
1910
- if($str->kindOf() != 'scalar' || $str->scalartyp() != 'string')
1911
- {
1912
- return false;
1913
- }
1914
- $response[$i] = new xmlrpcresp(0, $code->scalarval(), $str->scalarval());
1915
- break;
1916
- default:
1917
- return false;
1918
- }
1919
- }
1920
- return $response;
1921
- }
1922
- }
1923
- } // end class xmlrpc_client
1924
-
1925
- class xmlrpcresp
1926
- {
1927
- var $val = 0;
1928
- var $valtyp;
1929
- var $errno = 0;
1930
- var $errstr = '';
1931
- var $payload;
1932
- var $hdrs = array();
1933
- var $_cookies = array();
1934
- var $content_type = 'text/xml';
1935
- var $raw_data = '';
1936
-
1937
- /**
1938
- * @param mixed $val either an xmlrpcval obj, a php value or the xml serialization of an xmlrpcval (a string)
1939
- * @param integer $fcode set it to anything but 0 to create an error response
1940
- * @param string $fstr the error string, in case of an error response
1941
- * @param string $valtyp either 'xmlrpcvals', 'phpvals' or 'xml'
1942
- *
1943
- * @todo add check that $val / $fcode / $fstr is of correct type???
1944
- * NB: as of now we do not do it, since it might be either an xmlrpcval or a plain
1945
- * php val, or a complete xml chunk, depending on usage of xmlrpc_client::send() inside which creator is called...
1946
- */
1947
- function xmlrpcresp($val, $fcode = 0, $fstr = '', $valtyp='')
1948
- {
1949
- if($fcode != 0)
1950
- {
1951
- // error response
1952
- $this->errno = $fcode;
1953
- $this->errstr = $fstr;
1954
- //$this->errstr = htmlspecialchars($fstr); // XXX: encoding probably shouldn't be done here; fix later.
1955
- }
1956
- else
1957
- {
1958
- // successful response
1959
- $this->val = $val;
1960
- if ($valtyp == '')
1961
- {
1962
- // user did not declare type of response value: try to guess it
1963
- if (is_object($this->val) && is_a($this->val, 'xmlrpcval'))
1964
- {
1965
- $this->valtyp = 'xmlrpcvals';
1966
- }
1967
- else if (is_string($this->val))
1968
- {
1969
- $this->valtyp = 'xml';
1970
-
1971
- }
1972
- else
1973
- {
1974
- $this->valtyp = 'phpvals';
1975
- }
1976
- }
1977
- else
1978
- {
1979
- // user declares type of resp value: believe him
1980
- $this->valtyp = $valtyp;
1981
- }
1982
- }
1983
- }
1984
-
1985
- /**
1986
- * Returns the error code of the response.
1987
- * @return integer the error code of this response (0 for not-error responses)
1988
- * @access public
1989
- */
1990
- function faultCode()
1991
- {
1992
- return $this->errno;
1993
- }
1994
-
1995
- /**
1996
- * Returns the error code of the response.
1997
- * @return string the error string of this response ('' for not-error responses)
1998
- * @access public
1999
- */
2000
- function faultString()
2001
- {
2002
- return $this->errstr;
2003
- }
2004
-
2005
- /**
2006
- * Returns the value received by the server.
2007
- * @return mixed the xmlrpcval object returned by the server. Might be an xml string or php value if the response has been created by specially configured xmlrpc_client objects
2008
- * @access public
2009
- */
2010
- function value()
2011
- {
2012
- return $this->val;
2013
- }
2014
-
2015
- /**
2016
- * Returns an array with the cookies received from the server.
2017
- * Array has the form: $cookiename => array ('value' => $val, $attr1 => $val1, $attr2 = $val2, ...)
2018
- * with attributes being e.g. 'expires', 'path', domain'.
2019
- * NB: cookies sent as 'expired' by the server (i.e. with an expiry date in the past)
2020
- * are still present in the array. It is up to the user-defined code to decide
2021
- * how to use the received cookies, and wheter they have to be sent back with the next
2022
- * request to the server (using xmlrpc_client::setCookie) or not
2023
- * @return array array of cookies received from the server
2024
- * @access public
2025
- */
2026
- function cookies()
2027
- {
2028
- return $this->_cookies;
2029
- }
2030
-
2031
- /**
2032
- * Returns xml representation of the response. XML prologue not included
2033
- * @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
2034
- * @return string the xml representation of the response
2035
- * @access public
2036
- */
2037
- function serialize($charset_encoding='')
2038
- {
2039
- if ($charset_encoding != '')
2040
- $this->content_type = 'text/xml; charset=' . $charset_encoding;
2041
- else
2042
- $this->content_type = 'text/xml';
2043
- $result = "<methodResponse>\n";
2044
- if($this->errno)
2045
- {
2046
- // G. Giunta 2005/2/13: let non-ASCII response messages be tolerated by clients
2047
- // by xml-encoding non ascii chars
2048
- $result .= "<fault>\n" .
2049
- "<value>\n<struct><member><name>faultCode</name>\n<value><int>" . $this->errno .
2050
- "</int></value>\n</member>\n<member>\n<name>faultString</name>\n<value><string>" .
2051
- xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding) . "</string></value>\n</member>\n" .
2052
- "</struct>\n</value>\n</fault>";
2053
- }
2054
- else
2055
- {
2056
- if(!is_object($this->val) || !is_a($this->val, 'xmlrpcval'))
2057
- {
2058
- if (is_string($this->val) && $this->valtyp == 'xml')
2059
- {
2060
- $result .= "<params>\n<param>\n" .
2061
- $this->val .
2062
- "</param>\n</params>";
2063
- }
2064
- else
2065
- {
2066
- /// @todo try to build something serializable?
2067
- die('cannot serialize xmlrpcresp objects whose content is native php values');
2068
- }
2069
- }
2070
- else
2071
- {
2072
- $result .= "<params>\n<param>\n" .
2073
- $this->val->serialize($charset_encoding) .
2074
- "</param>\n</params>";
2075
- }
2076
- }
2077
- $result .= "\n</methodResponse>";
2078
- $this->payload = $result;
2079
- return $result;
2080
- }
2081
- }
2082
-
2083
- class xmlrpcmsg
2084
- {
2085
- var $payload;
2086
- var $methodname;
2087
- var $params=array();
2088
- var $debug=0;
2089
- var $content_type = 'text/xml';
2090
-
2091
- /**
2092
- * @param string $meth the name of the method to invoke
2093
- * @param array $pars array of parameters to be paased to the method (xmlrpcval objects)
2094
- */
2095
- function xmlrpcmsg($meth, $pars=0)
2096
- {
2097
- $this->methodname=$meth;
2098
- if(is_array($pars) && count($pars)>0)
2099
- {
2100
- for($i=0; $i<count($pars); $i++)
2101
- {
2102
- $this->addParam($pars[$i]);
2103
- }
2104
- }
2105
- }
2106
-
2107
- /**
2108
- * @access private
2109
- */
2110
- function xml_header($charset_encoding='')
2111
- {
2112
- if ($charset_encoding != '')
2113
- {
2114
- return "<?xml version=\"1.0\" encoding=\"$charset_encoding\" ?" . ">\n<methodCall>\n";
2115
- }
2116
- else
2117
- {
2118
- return "<?xml version=\"1.0\"?" . ">\n<methodCall>\n";
2119
- }
2120
- }
2121
-
2122
- /**
2123
- * @access private
2124
- */
2125
- function xml_footer()
2126
- {
2127
- return '</methodCall>';
2128
- }
2129
-
2130
- /**
2131
- * @access private
2132
- */
2133
- function kindOf()
2134
- {
2135
- return 'msg';
2136
- }
2137
-
2138
- /**
2139
- * @access private
2140
- */
2141
- function createPayload($charset_encoding='')
2142
- {
2143
- if ($charset_encoding != '')
2144
- $this->content_type = 'text/xml; charset=' . $charset_encoding;
2145
- else
2146
- $this->content_type = 'text/xml';
2147
- $this->payload=$this->xml_header($charset_encoding);
2148
- $this->payload.='<methodName>' . $this->methodname . "</methodName>\n";
2149
- $this->payload.="<params>\n";
2150
- for($i=0; $i<count($this->params); $i++)
2151
- {
2152
- $p=$this->params[$i];
2153
- $this->payload.="<param>\n" . $p->serialize($charset_encoding) .
2154
- "</param>\n";
2155
- }
2156
- $this->payload.="</params>\n";
2157
- $this->payload.=$this->xml_footer();
2158
- }
2159
-
2160
- /**
2161
- * Gets/sets the xmlrpc method to be invoked
2162
- * @param string $meth the method to be set (leave empty not to set it)
2163
- * @return string the method that will be invoked
2164
- * @access public
2165
- */
2166
- function method($meth='')
2167
- {
2168
- if($meth!='')
2169
- {
2170
- $this->methodname=$meth;
2171
- }
2172
- return $this->methodname;
2173
- }
2174
-
2175
- /**
2176
- * Returns xml representation of the message. XML prologue included
2177
- * @return string the xml representation of the message, xml prologue included
2178
- * @access public
2179
- */
2180
- function serialize($charset_encoding='')
2181
- {
2182
- $this->createPayload($charset_encoding);
2183
- return $this->payload;
2184
- }
2185
-
2186
- /**
2187
- * Add a parameter to the list of parameters to be used upon method invocation
2188
- * @param xmlrpcval $par
2189
- * @return boolean false on failure
2190
- * @access public
2191
- */
2192
- function addParam($par)
2193
- {
2194
- // add check: do not add to self params which are not xmlrpcvals
2195
- if(is_object($par) && is_a($par, 'xmlrpcval'))
2196
- {
2197
- $this->params[]=$par;
2198
- return true;
2199
- }
2200
- else
2201
- {
2202
- return false;
2203
- }
2204
- }
2205
-
2206
- /**
2207
- * Returns the nth parameter in the message. The index zero-based.
2208
- * @param integer $i the index of the parameter to fetch (zero based)
2209
- * @return xmlrpcval the i-th parameter
2210
- * @access public
2211
- */
2212
- function getParam($i) { return $this->params[$i]; }
2213
-
2214
- /**
2215
- * Returns the number of parameters in the messge.
2216
- * @return integer the number of parameters currently set
2217
- * @access public
2218
- */
2219
- function getNumParams() { return count($this->params); }
2220
-
2221
- /**
2222
- * Given an open file handle, read all data available and parse it as axmlrpc response.
2223
- * NB: the file handle is not closed by this function.
2224
- * NNB: might have trouble in rare cases to work on network streams, as we
2225
- * check for a read of 0 bytes instead of feof($fp).
2226
- * But since checking for feof(null) returns false, we would risk an
2227
- * infinite loop in that case, because we cannot trust the caller
2228
- * to give us a valid pointer to an open file...
2229
- * @access public
2230
- * @return xmlrpcresp
2231
- * @todo add 2nd & 3rd param to be passed to ParseResponse() ???
2232
- */
2233
- function &parseResponseFile($fp)
2234
- {
2235
- $ipd='';
2236
- while($data=fread($fp, 32768))
2237
- {
2238
- $ipd.=$data;
2239
- }
2240
- //fclose($fp);
2241
- $r =& $this->parseResponse($ipd);
2242
- return $r;
2243
- }
2244
-
2245
- /**
2246
- * Parses HTTP headers and separates them from data.
2247
- * @access private
2248
- */
2249
- function &parseResponseHeaders(&$data, $headers_processed=false)
2250
- {
2251
- // Support "web-proxy-tunelling" connections for https through proxies
2252
- if(preg_match('/^HTTP\/1\.[0-1] 200 Connection established/', $data))
2253
- {
2254
- // Look for CR/LF or simple LF as line separator,
2255
- // (even though it is not valid http)
2256
- $pos = strpos($data,"\r\n\r\n");
2257
- if($pos || is_int($pos))
2258
- {
2259
- $bd = $pos+4;
2260
- }
2261
- else
2262
- {
2263
- $pos = strpos($data,"\n\n");
2264
- if($pos || is_int($pos))
2265
- {
2266
- $bd = $pos+2;
2267
- }
2268
- else
2269
- {
2270
- // No separation between response headers and body: fault?
2271
- $bd = 0;
2272
- }
2273
- }
2274
- if ($bd)
2275
- {
2276
- // this filters out all http headers from proxy.
2277
- // maybe we could take them into account, too?
2278
- $data = substr($data, $bd);
2279
- }
2280
- else
2281
- {
2282
- error_log('XML-RPC: '.__METHOD__.': HTTPS via proxy error, tunnel connection possibly failed');
2283
- $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (HTTPS via proxy error, tunnel connection possibly failed)');
2284
- return $r;
2285
- }
2286
- }
2287
-
2288
- // Strip HTTP 1.1 100 Continue header if present
2289
- while(preg_match('/^HTTP\/1\.1 1[0-9]{2} /', $data))
2290
- {
2291
- $pos = strpos($data, 'HTTP', 12);
2292
- // server sent a Continue header without any (valid) content following...
2293
- // give the client a chance to know it
2294
- if(!$pos && !is_int($pos)) // works fine in php 3, 4 and 5
2295
- {
2296
- break;
2297
- }
2298
- $data = substr($data, $pos);
2299
- }
2300
- if(!preg_match('/^HTTP\/[0-9.]+ 200 /', $data))
2301
- {
2302
- $errstr= substr($data, 0, strpos($data, "\n")-1);
2303
- error_log('XML-RPC: '.__METHOD__.': HTTP error, got response: ' .$errstr);
2304
- $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (' . $errstr . ')');
2305
- return $r;
2306
- }
2307
-
2308
- $GLOBALS['_xh']['headers'] = array();
2309
- $GLOBALS['_xh']['cookies'] = array();
2310
-
2311
- // be tolerant to usage of \n instead of \r\n to separate headers and data
2312
- // (even though it is not valid http)
2313
- $pos = strpos($data,"\r\n\r\n");
2314
- if($pos || is_int($pos))
2315
- {
2316
- $bd = $pos+4;
2317
- }
2318
- else
2319
- {
2320
- $pos = strpos($data,"\n\n");
2321
- if($pos || is_int($pos))
2322
- {
2323
- $bd = $pos+2;
2324
- }
2325
- else
2326
- {
2327
- // No separation between response headers and body: fault?
2328
- // we could take some action here instead of going on...
2329
- $bd = 0;
2330
- }
2331
- }
2332
- // be tolerant to line endings, and extra empty lines
2333
- $ar = preg_split("/\r?\n/", trim(substr($data, 0, $pos)));
2334
- while(list(,$line) = @each($ar))
2335
- {
2336
- // take care of multi-line headers and cookies
2337
- $arr = explode(':',$line,2);
2338
- if(count($arr) > 1)
2339
- {
2340
- $header_name = strtolower(trim($arr[0]));
2341
- /// @todo some other headers (the ones that allow a CSV list of values)
2342
- /// do allow many values to be passed using multiple header lines.
2343
- /// We should add content to $GLOBALS['_xh']['headers'][$header_name]
2344
- /// instead of replacing it for those...
2345
- if ($header_name == 'set-cookie' || $header_name == 'set-cookie2')
2346
- {
2347
- if ($header_name == 'set-cookie2')
2348
- {
2349
- // version 2 cookies:
2350
- // there could be many cookies on one line, comma separated
2351
- $cookies = explode(',', $arr[1]);
2352
- }
2353
- else
2354
- {
2355
- $cookies = array($arr[1]);
2356
- }
2357
- foreach ($cookies as $cookie)
2358
- {
2359
- // glue together all received cookies, using a comma to separate them
2360
- // (same as php does with getallheaders())
2361
- if (isset($GLOBALS['_xh']['headers'][$header_name]))
2362
- $GLOBALS['_xh']['headers'][$header_name] .= ', ' . trim($cookie);
2363
- else
2364
- $GLOBALS['_xh']['headers'][$header_name] = trim($cookie);
2365
- // parse cookie attributes, in case user wants to correctly honour them
2366
- // feature creep: only allow rfc-compliant cookie attributes?
2367
- // @todo support for server sending multiple time cookie with same name, but using different PATHs
2368
- $cookie = explode(';', $cookie);
2369
- foreach ($cookie as $pos => $val)
2370
- {
2371
- $val = explode('=', $val, 2);
2372
- $tag = trim($val[0]);
2373
- $val = trim(@$val[1]);
2374
- /// @todo with version 1 cookies, we should strip leading and trailing " chars
2375
- if ($pos == 0)
2376
- {
2377
- $cookiename = $tag;
2378
- $GLOBALS['_xh']['cookies'][$tag] = array();
2379
- $GLOBALS['_xh']['cookies'][$cookiename]['value'] = urldecode($val);
2380
- }
2381
- else
2382
- {
2383
- if ($tag != 'value')
2384
- {
2385
- $GLOBALS['_xh']['cookies'][$cookiename][$tag] = $val;
2386
- }
2387
- }
2388
- }
2389
- }
2390
- }
2391
- else
2392
- {
2393
- $GLOBALS['_xh']['headers'][$header_name] = trim($arr[1]);
2394
- }
2395
- }
2396
- elseif(isset($header_name))
2397
- {
2398
- /// @todo version1 cookies might span multiple lines, thus breaking the parsing above
2399
- $GLOBALS['_xh']['headers'][$header_name] .= ' ' . trim($line);
2400
- }
2401
- }
2402
-
2403
- $data = substr($data, $bd);
2404
-
2405
- if($this->debug && count($GLOBALS['_xh']['headers']))
2406
- {
2407
- print '<PRE>';
2408
- foreach($GLOBALS['_xh']['headers'] as $header => $value)
2409
- {
2410
- print htmlentities("HEADER: $header: $value\n");
2411
- }
2412
- foreach($GLOBALS['_xh']['cookies'] as $header => $value)
2413
- {
2414
- print htmlentities("COOKIE: $header={$value['value']}\n");
2415
- }
2416
- print "</PRE>\n";
2417
- }
2418
-
2419
- // if CURL was used for the call, http headers have been processed,
2420
- // and dechunking + reinflating have been carried out
2421
- if(!$headers_processed)
2422
- {
2423
- // Decode chunked encoding sent by http 1.1 servers
2424
- if(isset($GLOBALS['_xh']['headers']['transfer-encoding']) && $GLOBALS['_xh']['headers']['transfer-encoding'] == 'chunked')
2425
- {
2426
- if(!$data = decode_chunked($data))
2427
- {
2428
- error_log('XML-RPC: '.__METHOD__.': errors occurred when trying to rebuild the chunked data received from server');
2429
- $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['dechunk_fail'], $GLOBALS['xmlrpcstr']['dechunk_fail']);
2430
- return $r;
2431
- }
2432
- }
2433
-
2434
- // Decode gzip-compressed stuff
2435
- // code shamelessly inspired from nusoap library by Dietrich Ayala
2436
- if(isset($GLOBALS['_xh']['headers']['content-encoding']))
2437
- {
2438
- $GLOBALS['_xh']['headers']['content-encoding'] = str_replace('x-', '', $GLOBALS['_xh']['headers']['content-encoding']);
2439
- if($GLOBALS['_xh']['headers']['content-encoding'] == 'deflate' || $GLOBALS['_xh']['headers']['content-encoding'] == 'gzip')
2440
- {
2441
- // if decoding works, use it. else assume data wasn't gzencoded
2442
- if(function_exists('gzinflate'))
2443
- {
2444
- if($GLOBALS['_xh']['headers']['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data))
2445
- {
2446
- $data = $degzdata;
2447
- if($this->debug)
2448
- print "<PRE>---INFLATED RESPONSE---[".strlen($data)." chars]---\n" . htmlentities($data) . "\n---END---</PRE>";
2449
- }
2450
- elseif($GLOBALS['_xh']['headers']['content-encoding'] == 'gzip' && $degzdata = @gzinflate(substr($data, 10)))
2451
- {
2452
- $data = $degzdata;
2453
- if($this->debug)
2454
- print "<PRE>---INFLATED RESPONSE---[".strlen($data)." chars]---\n" . htmlentities($data) . "\n---END---</PRE>";
2455
- }
2456
- else
2457
- {
2458
- error_log('XML-RPC: '.__METHOD__.': errors occurred when trying to decode the deflated data received from server');
2459
- $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['decompress_fail'], $GLOBALS['xmlrpcstr']['decompress_fail']);
2460
- return $r;
2461
- }
2462
- }
2463
- else
2464
- {
2465
- error_log('XML-RPC: '.__METHOD__.': the server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
2466
- $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['cannot_decompress'], $GLOBALS['xmlrpcstr']['cannot_decompress']);
2467
- return $r;
2468
- }
2469
- }
2470
- }
2471
- } // end of 'if needed, de-chunk, re-inflate response'
2472
-
2473
- // real stupid hack to avoid PHP complaining about returning NULL by ref
2474
- $r = null;
2475
- $r =& $r;
2476
- return $r;
2477
- }
2478
-
2479
- /**
2480
- * Parse the xmlrpc response contained in the string $data and return an xmlrpcresp object.
2481
- * @param string $data the xmlrpc response, eventually including http headers
2482
- * @param bool $headers_processed when true prevents parsing HTTP headers for interpretation of content-encoding and consequent decoding
2483
- * @param string $return_type decides return type, i.e. content of response->value(). Either 'xmlrpcvals', 'xml' or 'phpvals'
2484
- * @return xmlrpcresp
2485
- * @access public
2486
- */
2487
- function &parseResponse($data='', $headers_processed=false, $return_type='xmlrpcvals')
2488
- {
2489
- if($this->debug)
2490
- {
2491
- //by maHo, replaced htmlspecialchars with htmlentities
2492
- print "<PRE>---GOT---\n" . htmlentities($data) . "\n---END---\n</PRE>";
2493
- }
2494
-
2495
- if($data == '')
2496
- {
2497
- error_log('XML-RPC: '.__METHOD__.': no response received from server.');
2498
- $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_data'], $GLOBALS['xmlrpcstr']['no_data']);
2499
- return $r;
2500
- }
2501
-
2502
- $GLOBALS['_xh']=array();
2503
-
2504
- $raw_data = $data;
2505
- // parse the HTTP headers of the response, if present, and separate them from data
2506
- if(substr($data, 0, 4) == 'HTTP')
2507
- {
2508
- $r =& $this->parseResponseHeaders($data, $headers_processed);
2509
- if ($r)
2510
- {
2511
- // failed processing of HTTP response headers
2512
- // save into response obj the full payload received, for debugging
2513
- $r->raw_data = $data;
2514
- return $r;
2515
- }
2516
- }
2517
- else
2518
- {
2519
- $GLOBALS['_xh']['headers'] = array();
2520
- $GLOBALS['_xh']['cookies'] = array();
2521
- }
2522
-
2523
- if($this->debug)
2524
- {
2525
- $start = strpos($data, '<!-- SERVER DEBUG INFO (BASE64 ENCODED):');
2526
- if ($start)
2527
- {
2528
- $start += strlen('<!-- SERVER DEBUG INFO (BASE64 ENCODED):');
2529
- $end = strpos($data, '-->', $start);
2530
- $comments = substr($data, $start, $end-$start);
2531
- print "<PRE>---SERVER DEBUG INFO (DECODED) ---\n\t".htmlentities(str_replace("\n", "\n\t", base64_decode($comments)))."\n---END---\n</PRE>";
2532
- }
2533
- }
2534
-
2535
- // be tolerant of extra whitespace in response body
2536
- $data = trim($data);
2537
-
2538
- /// @todo return an error msg if $data=='' ?
2539
-
2540
- // be tolerant of junk after methodResponse (e.g. javascript ads automatically inserted by free hosts)
2541
- // idea from Luca Mariano <luca.mariano@email.it> originally in PEARified version of the lib
2542
- $pos = strrpos($data, '</methodResponse>');
2543
- if($pos !== false)
2544
- {
2545
- $data = substr($data, 0, $pos+17);
2546
- }
2547
-
2548
- // if user wants back raw xml, give it to him
2549
- if ($return_type == 'xml')
2550
- {
2551
- $r = new xmlrpcresp($data, 0, '', 'xml');
2552
- $r->hdrs = $GLOBALS['_xh']['headers'];
2553
- $r->_cookies = $GLOBALS['_xh']['cookies'];
2554
- $r->raw_data = $raw_data;
2555
- return $r;
2556
- }
2557
-
2558
- // try to 'guestimate' the character encoding of the received response
2559
- $resp_encoding = guess_encoding(@$GLOBALS['_xh']['headers']['content-type'], $data);
2560
-
2561
- $GLOBALS['_xh']['ac']='';
2562
- //$GLOBALS['_xh']['qt']=''; //unused...
2563
- $GLOBALS['_xh']['stack'] = array();
2564
- $GLOBALS['_xh']['valuestack'] = array();
2565
- $GLOBALS['_xh']['isf']=0; // 0 = OK, 1 for xmlrpc fault responses, 2 = invalid xmlrpc
2566
- $GLOBALS['_xh']['isf_reason']='';
2567
- $GLOBALS['_xh']['rt']=''; // 'methodcall or 'methodresponse'
2568
-
2569
- // if response charset encoding is not known / supported, try to use
2570
- // the default encoding and parse the xml anyway, but log a warning...
2571
- if (!in_array($resp_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
2572
- // the following code might be better for mb_string enabled installs, but
2573
- // makes the lib about 200% slower...
2574
- //if (!is_valid_charset($resp_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
2575
- {
2576
- error_log('XML-RPC: '.__METHOD__.': invalid charset encoding of received response: '.$resp_encoding);
2577
- $resp_encoding = $GLOBALS['xmlrpc_defencoding'];
2578
- }
2579
- $parser = xml_parser_create($resp_encoding);
2580
- xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
2581
- // G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
2582
- // the xml parser to give us back data in the expected charset.
2583
- // What if internal encoding is not in one of the 3 allowed?
2584
- // we use the broadest one, ie. utf8
2585
- // This allows to send data which is native in various charset,
2586
- // by extending xmlrpc_encode_entitites() and setting xmlrpc_internalencoding
2587
- if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
2588
- {
2589
- xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
2590
- }
2591
- else
2592
- {
2593
- xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
2594
- }
2595
-
2596
- if ($return_type == 'phpvals')
2597
- {
2598
- xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee_fast');
2599
- }
2600
- else
2601
- {
2602
- xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
2603
- }
2604
-
2605
- xml_set_character_data_handler($parser, 'xmlrpc_cd');
2606
- xml_set_default_handler($parser, 'xmlrpc_dh');
2607
-
2608
- // first error check: xml not well formed
2609
- if(!xml_parse($parser, $data, count($data)))
2610
- {
2611
- // thanks to Peter Kocks <peter.kocks@baygate.com>
2612
- if((xml_get_current_line_number($parser)) == 1)
2613
- {
2614
- $errstr = 'XML error at line 1, check URL';
2615
- }
2616
- else
2617
- {
2618
- $errstr = sprintf('XML error: %s at line %d, column %d',
2619
- xml_error_string(xml_get_error_code($parser)),
2620
- xml_get_current_line_number($parser), xml_get_current_column_number($parser));
2621
- }
2622
- error_log($errstr);
2623
- $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'], $GLOBALS['xmlrpcstr']['invalid_return'].' ('.$errstr.')');
2624
- xml_parser_free($parser);
2625
- if($this->debug)
2626
- {
2627
- print $errstr;
2628
- }
2629
- $r->hdrs = $GLOBALS['_xh']['headers'];
2630
- $r->_cookies = $GLOBALS['_xh']['cookies'];
2631
- $r->raw_data = $raw_data;
2632
- return $r;
2633
- }
2634
- xml_parser_free($parser);
2635
- // second error check: xml well formed but not xml-rpc compliant
2636
- if ($GLOBALS['_xh']['isf'] > 1)
2637
- {
2638
- if ($this->debug)
2639
- {
2640
- /// @todo echo something for user?
2641
- }
2642
-
2643
- $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'],
2644
- $GLOBALS['xmlrpcstr']['invalid_return'] . ' ' . $GLOBALS['_xh']['isf_reason']);
2645
- }
2646
- // third error check: parsing of the response has somehow gone boink.
2647
- // NB: shall we omit this check, since we trust the parsing code?
2648
- elseif ($return_type == 'xmlrpcvals' && !is_object($GLOBALS['_xh']['value']))
2649
- {
2650
- // something odd has happened
2651
- // and it's time to generate a client side error
2652
- // indicating something odd went on
2653
- $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'],
2654
- $GLOBALS['xmlrpcstr']['invalid_return']);
2655
- }
2656
- else
2657
- {
2658
- if ($this->debug)
2659
- {
2660
- print "<PRE>---PARSED---\n";
2661
- // somehow htmlentities chokes on var_export, and some full html string...
2662
- //print htmlentitites(var_export($GLOBALS['_xh']['value'], true));
2663
- print htmlspecialchars(var_export($GLOBALS['_xh']['value'], true));
2664
- print "\n---END---</PRE>";
2665
- }
2666
-
2667
- // note that using =& will raise an error if $GLOBALS['_xh']['st'] does not generate an object.
2668
- $v =& $GLOBALS['_xh']['value'];
2669
-
2670
- if($GLOBALS['_xh']['isf'])
2671
- {
2672
- /// @todo we should test here if server sent an int and a string,
2673
- /// and/or coerce them into such...
2674
- if ($return_type == 'xmlrpcvals')
2675
- {
2676
- $errno_v = $v->structmem('faultCode');
2677
- $errstr_v = $v->structmem('faultString');
2678
- $errno = $errno_v->scalarval();
2679
- $errstr = $errstr_v->scalarval();
2680
- }
2681
- else
2682
- {
2683
- $errno = $v['faultCode'];
2684
- $errstr = $v['faultString'];
2685
- }
2686
-
2687
- if($errno == 0)
2688
- {
2689
- // FAULT returned, errno needs to reflect that
2690
- $errno = -1;
2691
- }
2692
-
2693
- $r = new xmlrpcresp(0, $errno, $errstr);
2694
- }
2695
- else
2696
- {
2697
- $r=new xmlrpcresp($v, 0, '', $return_type);
2698
- }
2699
- }
2700
-
2701
- $r->hdrs = $GLOBALS['_xh']['headers'];
2702
- $r->_cookies = $GLOBALS['_xh']['cookies'];
2703
- $r->raw_data = $raw_data;
2704
- return $r;
2705
- }
2706
- }
2707
-
2708
- class xmlrpcval
2709
- {
2710
- var $me=array();
2711
- var $mytype=0;
2712
- var $_php_class=null;
2713
-
2714
- /**
2715
- * @param mixed $val
2716
- * @param string $type any valid xmlrpc type name (lowercase). If null, 'string' is assumed
2717
- */
2718
- function xmlrpcval($val=-1, $type='')
2719
- {
2720
- /// @todo: optimization creep - do not call addXX, do it all inline.
2721
- /// downside: booleans will not be coerced anymore
2722
- if($val!==-1 || $type!='')
2723
- {
2724
- // optimization creep: inlined all work done by constructor
2725
- switch($type)
2726
- {
2727
- case '':
2728
- $this->mytype=1;
2729
- $this->me['string']=$val;
2730
- break;
2731
- case 'i4':
2732
- case 'int':
2733
- case 'double':
2734
- case 'string':
2735
- case 'boolean':
2736
- case 'dateTime.iso8601':
2737
- case 'base64':
2738
- case 'null':
2739
- $this->mytype=1;
2740
- $this->me[$type]=$val;
2741
- break;
2742
- case 'array':
2743
- $this->mytype=2;
2744
- $this->me['array']=$val;
2745
- break;
2746
- case 'struct':
2747
- $this->mytype=3;
2748
- $this->me['struct']=$val;
2749
- break;
2750
- default:
2751
- error_log("XML-RPC: ".__METHOD__.": not a known type ($type)");
2752
- }
2753
- /*if($type=='')
2754
- {
2755
- $type='string';
2756
- }
2757
- if($GLOBALS['xmlrpcTypes'][$type]==1)
2758
- {
2759
- $this->addScalar($val,$type);
2760
- }
2761
- elseif($GLOBALS['xmlrpcTypes'][$type]==2)
2762
- {
2763
- $this->addArray($val);
2764
- }
2765
- elseif($GLOBALS['xmlrpcTypes'][$type]==3)
2766
- {
2767
- $this->addStruct($val);
2768
- }*/
2769
- }
2770
- }
2771
-
2772
- /**
2773
- * Add a single php value to an (unitialized) xmlrpcval
2774
- * @param mixed $val
2775
- * @param string $type
2776
- * @return int 1 or 0 on failure
2777
- */
2778
- function addScalar($val, $type='string')
2779
- {
2780
- $typeof=@$GLOBALS['xmlrpcTypes'][$type];
2781
- if($typeof!=1)
2782
- {
2783
- error_log("XML-RPC: ".__METHOD__.": not a scalar type ($type)");
2784
- return 0;
2785
- }
2786
-
2787
- // coerce booleans into correct values
2788
- // NB: we should either do it for datetimes, integers and doubles, too,
2789
- // or just plain remove this check, implemented on booleans only...
2790
- if($type==$GLOBALS['xmlrpcBoolean'])
2791
- {
2792
- if(strcasecmp($val,'true')==0 || $val==1 || ($val==true && strcasecmp($val,'false')))
2793
- {
2794
- $val=true;
2795
- }
2796
- else
2797
- {
2798
- $val=false;
2799
- }
2800
- }
2801
-
2802
- switch($this->mytype)
2803
- {
2804
- case 1:
2805
- error_log('XML-RPC: '.__METHOD__.': scalar xmlrpcval can have only one value');
2806
- return 0;
2807
- case 3:
2808
- error_log('XML-RPC: '.__METHOD__.': cannot add anonymous scalar to struct xmlrpcval');
2809
- return 0;
2810
- case 2:
2811
- // we're adding a scalar value to an array here
2812
- //$ar=$this->me['array'];
2813
- //$ar[]=new xmlrpcval($val, $type);
2814
- //$this->me['array']=$ar;
2815
- // Faster (?) avoid all the costly array-copy-by-val done here...
2816
- $this->me['array'][]=new xmlrpcval($val, $type);
2817
- return 1;
2818
- default:
2819
- // a scalar, so set the value and remember we're scalar
2820
- $this->me[$type]=$val;
2821
- $this->mytype=$typeof;
2822
- return 1;
2823
- }
2824
- }
2825
-
2826
- /**
2827
- * Add an array of xmlrpcval objects to an xmlrpcval
2828
- * @param array $vals
2829
- * @return int 1 or 0 on failure
2830
- * @access public
2831
- *
2832
- * @todo add some checking for $vals to be an array of xmlrpcvals?
2833
- */
2834
- function addArray($vals)
2835
- {
2836
- if($this->mytype==0)
2837
- {
2838
- $this->mytype=$GLOBALS['xmlrpcTypes']['array'];
2839
- $this->me['array']=$vals;
2840
- return 1;
2841
- }
2842
- elseif($this->mytype==2)
2843
- {
2844
- // we're adding to an array here
2845
- $this->me['array'] = array_merge($this->me['array'], $vals);
2846
- return 1;
2847
- }
2848
- else
2849
- {
2850
- error_log('XML-RPC: '.__METHOD__.': already initialized as a [' . $this->kindOf() . ']');
2851
- return 0;
2852
- }
2853
- }
2854
-
2855
- /**
2856
- * Add an array of named xmlrpcval objects to an xmlrpcval
2857
- * @param array $vals
2858
- * @return int 1 or 0 on failure
2859
- * @access public
2860
- *
2861
- * @todo add some checking for $vals to be an array?
2862
- */
2863
- function addStruct($vals)
2864
- {
2865
- if($this->mytype==0)
2866
- {
2867
- $this->mytype=$GLOBALS['xmlrpcTypes']['struct'];
2868
- $this->me['struct']=$vals;
2869
- return 1;
2870
- }
2871
- elseif($this->mytype==3)
2872
- {
2873
- // we're adding to a struct here
2874
- $this->me['struct'] = array_merge($this->me['struct'], $vals);
2875
- return 1;
2876
- }
2877
- else
2878
- {
2879
- error_log('XML-RPC: '.__METHOD__.': already initialized as a [' . $this->kindOf() . ']');
2880
- return 0;
2881
- }
2882
- }
2883
-
2884
- // poor man's version of print_r ???
2885
- // DEPRECATED!
2886
- function dump($ar)
2887
- {
2888
- foreach($ar as $key => $val)
2889
- {
2890
- echo "$key => $val<br />";
2891
- if($key == 'array')
2892
- {
2893
- while(list($key2, $val2) = each($val))
2894
- {
2895
- echo "-- $key2 => $val2<br />";
2896
- }
2897
- }
2898
- }
2899
- }
2900
-
2901
- /**
2902
- * Returns a string containing "struct", "array" or "scalar" describing the base type of the value
2903
- * @return string
2904
- * @access public
2905
- */
2906
- function kindOf()
2907
- {
2908
- switch($this->mytype)
2909
- {
2910
- case 3:
2911
- return 'struct';
2912
- break;
2913
- case 2:
2914
- return 'array';
2915
- break;
2916
- case 1:
2917
- return 'scalar';
2918
- break;
2919
- default:
2920
- return 'undef';
2921
- }
2922
- }
2923
-
2924
- /**
2925
- * @access private
2926
- */
2927
- function serializedata($typ, $val, $charset_encoding='')
2928
- {
2929
- $rs='';
2930
- switch(@$GLOBALS['xmlrpcTypes'][$typ])
2931
- {
2932
- case 1:
2933
- switch($typ)
2934
- {
2935
- case $GLOBALS['xmlrpcBase64']:
2936
- $rs.="<${typ}>" . base64_encode($val) . "</${typ}>";
2937
- break;
2938
- case $GLOBALS['xmlrpcBoolean']:
2939
- $rs.="<${typ}>" . ($val ? '1' : '0') . "</${typ}>";
2940
- break;
2941
- case $GLOBALS['xmlrpcString']:
2942
- // G. Giunta 2005/2/13: do NOT use htmlentities, since
2943
- // it will produce named html entities, which are invalid xml
2944
- $rs.="<${typ}>" . xmlrpc_encode_entitites($val, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding). "</${typ}>";
2945
- break;
2946
- case $GLOBALS['xmlrpcInt']:
2947
- case $GLOBALS['xmlrpcI4']:
2948
- $rs.="<${typ}>".(int)$val."</${typ}>";
2949
- break;
2950
- case $GLOBALS['xmlrpcDouble']:
2951
- // avoid using standard conversion of float to string because it is locale-dependent,
2952
- // and also because the xmlrpc spec forbids exponential notation.
2953
- // sprintf('%F') could be most likely ok but it fails eg. on 2e-14.
2954
- // The code below tries its best at keeping max precision while avoiding exp notation,
2955
- // but there is of course no limit in the number of decimal places to be used...
2956
- $rs.="<${typ}>".preg_replace('/\\.?0+$/','',number_format((double)$val, 128, '.', ''))."</${typ}>";
2957
- break;
2958
- case $GLOBALS['xmlrpcDateTime']:
2959
- if (is_string($val))
2960
- {
2961
- $rs.="<${typ}>${val}</${typ}>";
2962
- }
2963
- else if(is_a($val, 'DateTime'))
2964
- {
2965
- $rs.="<${typ}>".$val->format('Ymd\TH:i:s')."</${typ}>";
2966
- }
2967
- else if(is_int($val))
2968
- {
2969
- $rs.="<${typ}>".strftime("%Y%m%dT%H:%M:%S", $val)."</${typ}>";
2970
- }
2971
- else
2972
- {
2973
- // not really a good idea here: but what shall we output anyway? left for backward compat...
2974
- $rs.="<${typ}>${val}</${typ}>";
2975
- }
2976
- break;
2977
- case $GLOBALS['xmlrpcNull']:
2978
- if ($GLOBALS['xmlrpc_null_apache_encoding'])
2979
- {
2980
- $rs.="<ex:nil/>";
2981
- }
2982
- else
2983
- {
2984
- $rs.="<nil/>";
2985
- }
2986
- break;
2987
- default:
2988
- // no standard type value should arrive here, but provide a possibility
2989
- // for xmlrpcvals of unknown type...
2990
- $rs.="<${typ}>${val}</${typ}>";
2991
- }
2992
- break;
2993
- case 3:
2994
- // struct
2995
- if ($this->_php_class)
2996
- {
2997
- $rs.='<struct php_class="' . $this->_php_class . "\">\n";
2998
- }
2999
- else
3000
- {
3001
- $rs.="<struct>\n";
3002
- }
3003
- foreach($val as $key2 => $val2)
3004
- {
3005
- $rs.='<member><name>'.xmlrpc_encode_entitites($key2, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding)."</name>\n";
3006
- //$rs.=$this->serializeval($val2);
3007
- $rs.=$val2->serialize($charset_encoding);
3008
- $rs.="</member>\n";
3009
- }
3010
- $rs.='</struct>';
3011
- break;
3012
- case 2:
3013
- // array
3014
- $rs.="<array>\n<data>\n";
3015
- for($i=0; $i<count($val); $i++)
3016
- {
3017
- //$rs.=$this->serializeval($val[$i]);
3018
- $rs.=$val[$i]->serialize($charset_encoding);
3019
- }
3020
- $rs.="</data>\n</array>";
3021
- break;
3022
- default:
3023
- break;
3024
- }
3025
- return $rs;
3026
- }
3027
-
3028
- /**
3029
- * Returns xml representation of the value. XML prologue not included
3030
- * @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
3031
- * @return string
3032
- * @access public
3033
- */
3034
- function serialize($charset_encoding='')
3035
- {
3036
- // add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
3037
- //if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
3038
- //{
3039
- reset($this->me);
3040
- list($typ, $val) = each($this->me);
3041
- return '<value>' . $this->serializedata($typ, $val, $charset_encoding) . "</value>\n";
3042
- //}
3043
- }
3044
-
3045
- // DEPRECATED
3046
- function serializeval($o)
3047
- {
3048
- // add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
3049
- //if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
3050
- //{
3051
- $ar=$o->me;
3052
- reset($ar);
3053
- list($typ, $val) = each($ar);
3054
- return '<value>' . $this->serializedata($typ, $val) . "</value>\n";
3055
- //}
3056
- }
3057
-
3058
- /**
3059
- * Checks wheter a struct member with a given name is present.
3060
- * Works only on xmlrpcvals of type struct.
3061
- * @param string $m the name of the struct member to be looked up
3062
- * @return boolean
3063
- * @access public
3064
- */
3065
- function structmemexists($m)
3066
- {
3067
- return array_key_exists($m, $this->me['struct']);
3068
- }
3069
-
3070
- /**
3071
- * Returns the value of a given struct member (an xmlrpcval object in itself).
3072
- * Will raise a php warning if struct member of given name does not exist
3073
- * @param string $m the name of the struct member to be looked up
3074
- * @return xmlrpcval
3075
- * @access public
3076
- */
3077
- function structmem($m)
3078
- {
3079
- return $this->me['struct'][$m];
3080
- }
3081
-
3082
- /**
3083
- * Reset internal pointer for xmlrpcvals of type struct.
3084
- * @access public
3085
- */
3086
- function structreset()
3087
- {
3088
- reset($this->me['struct']);
3089
- }
3090
-
3091
- /**
3092
- * Return next member element for xmlrpcvals of type struct.
3093
- * @return xmlrpcval
3094
- * @access public
3095
- */
3096
- function structeach()
3097
- {
3098
- return each($this->me['struct']);
3099
- }
3100
-
3101
- // DEPRECATED! this code looks like it is very fragile and has not been fixed
3102
- // for a long long time. Shall we remove it for 2.0?
3103
- function getval()
3104
- {
3105
- // UNSTABLE
3106
- reset($this->me);
3107
- list($a,$b)=each($this->me);
3108
- // contributed by I Sofer, 2001-03-24
3109
- // add support for nested arrays to scalarval
3110
- // i've created a new method here, so as to
3111
- // preserve back compatibility
3112
-
3113
- if(is_array($b))
3114
- {
3115
- @reset($b);
3116
- while(list($id,$cont) = @each($b))
3117
- {
3118
- $b[$id] = $cont->scalarval();
3119
- }
3120
- }
3121
-
3122
- // add support for structures directly encoding php objects
3123
- if(is_object($b))
3124
- {
3125
- $t = get_object_vars($b);
3126
- @reset($t);
3127
- while(list($id,$cont) = @each($t))
3128
- {
3129
- $t[$id] = $cont->scalarval();
3130
- }
3131
- @reset($t);
3132
- while(list($id,$cont) = @each($t))
3133
- {
3134
- @$b->$id = $cont;
3135
- }
3136
- }
3137
- // end contrib
3138
- return $b;
3139
- }
3140
-
3141
- /**
3142
- * Returns the value of a scalar xmlrpcval
3143
- * @return mixed
3144
- * @access public
3145
- */
3146
- function scalarval()
3147
- {
3148
- reset($this->me);
3149
- list(,$b)=each($this->me);
3150
- return $b;
3151
- }
3152
-
3153
- /**
3154
- * Returns the type of the xmlrpcval.
3155
- * For integers, 'int' is always returned in place of 'i4'
3156
- * @return string
3157
- * @access public
3158
- */
3159
- function scalartyp()
3160
- {
3161
- reset($this->me);
3162
- list($a,)=each($this->me);
3163
- if($a==$GLOBALS['xmlrpcI4'])
3164
- {
3165
- $a=$GLOBALS['xmlrpcInt'];
3166
- }
3167
- return $a;
3168
- }
3169
-
3170
- /**
3171
- * Returns the m-th member of an xmlrpcval of struct type
3172
- * @param integer $m the index of the value to be retrieved (zero based)
3173
- * @return xmlrpcval
3174
- * @access public
3175
- */
3176
- function arraymem($m)
3177
- {
3178
- return $this->me['array'][$m];
3179
- }
3180
-
3181
- /**
3182
- * Returns the number of members in an xmlrpcval of array type
3183
- * @return integer
3184
- * @access public
3185
- */
3186
- function arraysize()
3187
- {
3188
- return count($this->me['array']);
3189
- }
3190
-
3191
- /**
3192
- * Returns the number of members in an xmlrpcval of struct type
3193
- * @return integer
3194
- * @access public
3195
- */
3196
- function structsize()
3197
- {
3198
- return count($this->me['struct']);
3199
- }
3200
- }
3201
-
3202
-
3203
- // date helpers
3204
-
3205
- /**
3206
- * Given a timestamp, return the corresponding ISO8601 encoded string.
3207
- *
3208
- * Really, timezones ought to be supported
3209
- * but the XML-RPC spec says:
3210
- *
3211
- * "Don't assume a timezone. It should be specified by the server in its
3212
- * documentation what assumptions it makes about timezones."
3213
- *
3214
- * These routines always assume localtime unless
3215
- * $utc is set to 1, in which case UTC is assumed
3216
- * and an adjustment for locale is made when encoding
3217
- *
3218
- * @param int $timet (timestamp)
3219
- * @param int $utc (0 or 1)
3220
- * @return string
3221
- */
3222
- function iso8601_encode($timet, $utc=0)
3223
- {
3224
- if(!$utc)
3225
- {
3226
- $t=strftime("%Y%m%dT%H:%M:%S", $timet);
3227
- }
3228
- else
3229
- {
3230
- if(function_exists('gmstrftime'))
3231
- {
3232
- // gmstrftime doesn't exist in some versions
3233
- // of PHP
3234
- $t=gmstrftime("%Y%m%dT%H:%M:%S", $timet);
3235
- }
3236
- else
3237
- {
3238
- $t=strftime("%Y%m%dT%H:%M:%S", $timet-date('Z'));
3239
- }
3240
- }
3241
- return $t;
3242
- }
3243
-
3244
- /**
3245
- * Given an ISO8601 date string, return a timet in the localtime, or UTC
3246
- * @param string $idate
3247
- * @param int $utc either 0 or 1
3248
- * @return int (datetime)
3249
- */
3250
- function iso8601_decode($idate, $utc=0)
3251
- {
3252
- $t=0;
3253
- if(preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/', $idate, $regs))
3254
- {
3255
- if($utc)
3256
- {
3257
- $t=gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
3258
- }
3259
- else
3260
- {
3261
- $t=mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
3262
- }
3263
- }
3264
- return $t;
3265
- }
3266
-
3267
- /**
3268
- * Takes an xmlrpc value in PHP xmlrpcval object format and translates it into native PHP types.
3269
- *
3270
- * Works with xmlrpc message objects as input, too.
3271
- *
3272
- * Given proper options parameter, can rebuild generic php object instances
3273
- * (provided those have been encoded to xmlrpc format using a corresponding
3274
- * option in php_xmlrpc_encode())
3275
- * PLEASE NOTE that rebuilding php objects involves calling their constructor function.
3276
- * This means that the remote communication end can decide which php code will
3277
- * get executed on your server, leaving the door possibly open to 'php-injection'
3278
- * style of attacks (provided you have some classes defined on your server that
3279
- * might wreak havoc if instances are built outside an appropriate context).
3280
- * Make sure you trust the remote server/client before eanbling this!
3281
- *
3282
- * @author Dan Libby (dan@libby.com)
3283
- *
3284
- * @param xmlrpcval $xmlrpc_val
3285
- * @param array $options if 'decode_php_objs' is set in the options array, xmlrpc structs can be decoded into php objects; if 'dates_as_objects' is set xmlrpc datetimes are decoded as php DateTime objects (standard is
3286
- * @return mixed
3287
- */
3288
- function php_xmlrpc_decode($xmlrpc_val, $options=array())
3289
- {
3290
- switch($xmlrpc_val->kindOf())
3291
- {
3292
- case 'scalar':
3293
- if (in_array('extension_api', $options))
3294
- {
3295
- reset($xmlrpc_val->me);
3296
- list($typ,$val) = each($xmlrpc_val->me);
3297
- switch ($typ)
3298
- {
3299
- case 'dateTime.iso8601':
3300
- $xmlrpc_val->scalar = $val;
3301
- $xmlrpc_val->xmlrpc_type = 'datetime';
3302
- $xmlrpc_val->timestamp = iso8601_decode($val);
3303
- return $xmlrpc_val;
3304
- case 'base64':
3305
- $xmlrpc_val->scalar = $val;
3306
- $xmlrpc_val->type = $typ;
3307
- return $xmlrpc_val;
3308
- default:
3309
- return $xmlrpc_val->scalarval();
3310
- }
3311
- }
3312
- if (in_array('dates_as_objects', $options) && $xmlrpc_val->scalartyp() == 'dateTime.iso8601')
3313
- {
3314
- // we return a Datetime object instead of a string
3315
- // since now the constructor of xmlrpcval accepts safely strings, ints and datetimes,
3316
- // we cater to all 3 cases here
3317
- $out = $xmlrpc_val->scalarval();
3318
- if (is_string($out))
3319
- {
3320
- $out = strtotime($out);
3321
- }
3322
- if (is_int($out))
3323
- {
3324
- $result = new Datetime();
3325
- $result->setTimestamp($out);
3326
- return $result;
3327
- }
3328
- elseif (is_a($out, 'Datetime'))
3329
- {
3330
- return $out;
3331
- }
3332
- }
3333
- return $xmlrpc_val->scalarval();
3334
- case 'array':
3335
- $size = $xmlrpc_val->arraysize();
3336
- $arr = array();
3337
- for($i = 0; $i < $size; $i++)
3338
- {
3339
- $arr[] = php_xmlrpc_decode($xmlrpc_val->arraymem($i), $options);
3340
- }
3341
- return $arr;
3342
- case 'struct':
3343
- $xmlrpc_val->structreset();
3344
- // If user said so, try to rebuild php objects for specific struct vals.
3345
- /// @todo should we raise a warning for class not found?
3346
- // shall we check for proper subclass of xmlrpcval instead of
3347
- // presence of _php_class to detect what we can do?
3348
- if (in_array('decode_php_objs', $options) && $xmlrpc_val->_php_class != ''
3349
- && class_exists($xmlrpc_val->_php_class))
3350
- {
3351
- $obj = @new $xmlrpc_val->_php_class;
3352
- while(list($key,$value)=$xmlrpc_val->structeach())
3353
- {
3354
- $obj->$key = php_xmlrpc_decode($value, $options);
3355
- }
3356
- return $obj;
3357
- }
3358
- else
3359
- {
3360
- $arr = array();
3361
- while(list($key,$value)=$xmlrpc_val->structeach())
3362
- {
3363
- $arr[$key] = php_xmlrpc_decode($value, $options);
3364
- }
3365
- return $arr;
3366
- }
3367
- case 'msg':
3368
- $paramcount = $xmlrpc_val->getNumParams();
3369
- $arr = array();
3370
- for($i = 0; $i < $paramcount; $i++)
3371
- {
3372
- $arr[] = php_xmlrpc_decode($xmlrpc_val->getParam($i));
3373
- }
3374
- return $arr;
3375
- }
3376
- }
3377
-
3378
- // This constant left here only for historical reasons...
3379
- // it was used to decide if we have to define xmlrpc_encode on our own, but
3380
- // we do not do it anymore
3381
- if(function_exists('xmlrpc_decode'))
3382
- {
3383
- define('XMLRPC_EPI_ENABLED','1');
3384
- }
3385
- else
3386
- {
3387
- define('XMLRPC_EPI_ENABLED','0');
3388
- }
3389
-
3390
- /**
3391
- * Takes native php types and encodes them into xmlrpc PHP object format.
3392
- * It will not re-encode xmlrpcval objects.
3393
- *
3394
- * Feature creep -- could support more types via optional type argument
3395
- * (string => datetime support has been added, ??? => base64 not yet)
3396
- *
3397
- * If given a proper options parameter, php object instances will be encoded
3398
- * into 'special' xmlrpc values, that can later be decoded into php objects
3399
- * by calling php_xmlrpc_decode() with a corresponding option
3400
- *
3401
- * @author Dan Libby (dan@libby.com)
3402
- *
3403
- * @param mixed $php_val the value to be converted into an xmlrpcval object
3404
- * @param array $options can include 'encode_php_objs', 'auto_dates', 'null_extension' or 'extension_api'
3405
- * @return xmlrpcval
3406
- */
3407
- function php_xmlrpc_encode($php_val, $options=array())
3408
- {
3409
- $type = gettype($php_val);
3410
- switch($type)
3411
- {
3412
- case 'string':
3413
- if (in_array('auto_dates', $options) && preg_match('/^[0-9]{8}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $php_val))
3414
- $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcDateTime']);
3415
- else
3416
- $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcString']);
3417
- break;
3418
- case 'integer':
3419
- $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcInt']);
3420
- break;
3421
- case 'double':
3422
- $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcDouble']);
3423
- break;
3424
- // <G_Giunta_2001-02-29>
3425
- // Add support for encoding/decoding of booleans, since they are supported in PHP
3426
- case 'boolean':
3427
- $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcBoolean']);
3428
- break;
3429
- // </G_Giunta_2001-02-29>
3430
- case 'array':
3431
- // PHP arrays can be encoded to either xmlrpc structs or arrays,
3432
- // depending on wheter they are hashes or plain 0..n integer indexed
3433
- // A shorter one-liner would be
3434
- // $tmp = array_diff(array_keys($php_val), range(0, count($php_val)-1));
3435
- // but execution time skyrockets!
3436
- $j = 0;
3437
- $arr = array();
3438
- $ko = false;
3439
- foreach($php_val as $key => $val)
3440
- {
3441
- $arr[$key] = php_xmlrpc_encode($val, $options);
3442
- if(!$ko && $key !== $j)
3443
- {
3444
- $ko = true;
3445
- }
3446
- $j++;
3447
- }
3448
- if($ko)
3449
- {
3450
- $xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcStruct']);
3451
- }
3452
- else
3453
- {
3454
- $xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcArray']);
3455
- }
3456
- break;
3457
- case 'object':
3458
- if(is_a($php_val, 'xmlrpcval'))
3459
- {
3460
- $xmlrpc_val = $php_val;
3461
- }
3462
- else if(is_a($php_val, 'DateTime'))
3463
- {
3464
- $xmlrpc_val = new xmlrpcval($php_val->format('Ymd\TH:i:s'), $GLOBALS['xmlrpcStruct']);
3465
- }
3466
- else
3467
- {
3468
- $arr = array();
3469
- reset($php_val);
3470
- while(list($k,$v) = each($php_val))
3471
- {
3472
- $arr[$k] = php_xmlrpc_encode($v, $options);
3473
- }
3474
- $xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcStruct']);
3475
- if (in_array('encode_php_objs', $options))
3476
- {
3477
- // let's save original class name into xmlrpcval:
3478
- // might be useful later on...
3479
- $xmlrpc_val->_php_class = get_class($php_val);
3480
- }
3481
- }
3482
- break;
3483
- case 'NULL':
3484
- if (in_array('extension_api', $options))
3485
- {
3486
- $xmlrpc_val = new xmlrpcval('', $GLOBALS['xmlrpcString']);
3487
- }
3488
- else if (in_array('null_extension', $options))
3489
- {
3490
- $xmlrpc_val = new xmlrpcval('', $GLOBALS['xmlrpcNull']);
3491
- }
3492
- else
3493
- {
3494
- $xmlrpc_val = new xmlrpcval();
3495
- }
3496
- break;
3497
- case 'resource':
3498
- if (in_array('extension_api', $options))
3499
- {
3500
- $xmlrpc_val = new xmlrpcval((int)$php_val, $GLOBALS['xmlrpcInt']);
3501
- }
3502
- else
3503
- {
3504
- $xmlrpc_val = new xmlrpcval();
3505
- }
3506
- // catch "user function", "unknown type"
3507
- default:
3508
- // giancarlo pinerolo <ping@alt.it>
3509
- // it has to return
3510
- // an empty object in case, not a boolean.
3511
- $xmlrpc_val = new xmlrpcval();
3512
- break;
3513
- }
3514
- return $xmlrpc_val;
3515
- }
3516
-
3517
- /**
3518
- * Convert the xml representation of a method response, method request or single
3519
- * xmlrpc value into the appropriate object (a.k.a. deserialize)
3520
- * @param string $xml_val
3521
- * @param array $options
3522
- * @return mixed false on error, or an instance of either xmlrpcval, xmlrpcmsg or xmlrpcresp
3523
- */
3524
- function php_xmlrpc_decode_xml($xml_val, $options=array())
3525
- {
3526
- $GLOBALS['_xh'] = array();
3527
- $GLOBALS['_xh']['ac'] = '';
3528
- $GLOBALS['_xh']['stack'] = array();
3529
- $GLOBALS['_xh']['valuestack'] = array();
3530
- $GLOBALS['_xh']['params'] = array();
3531
- $GLOBALS['_xh']['pt'] = array();
3532
- $GLOBALS['_xh']['isf'] = 0;
3533
- $GLOBALS['_xh']['isf_reason'] = '';
3534
- $GLOBALS['_xh']['method'] = false;
3535
- $GLOBALS['_xh']['rt'] = '';
3536
- /// @todo 'guestimate' encoding
3537
- $parser = xml_parser_create();
3538
- xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
3539
- // What if internal encoding is not in one of the 3 allowed?
3540
- // we use the broadest one, ie. utf8!
3541
- if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
3542
- {
3543
- xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
3544
- }
3545
- else
3546
- {
3547
- xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
3548
- }
3549
- xml_set_element_handler($parser, 'xmlrpc_se_any', 'xmlrpc_ee');
3550
- xml_set_character_data_handler($parser, 'xmlrpc_cd');
3551
- xml_set_default_handler($parser, 'xmlrpc_dh');
3552
- if(!xml_parse($parser, $xml_val, 1))
3553
- {
3554
- $errstr = sprintf('XML error: %s at line %d, column %d',
3555
- xml_error_string(xml_get_error_code($parser)),
3556
- xml_get_current_line_number($parser), xml_get_current_column_number($parser));
3557
- error_log($errstr);
3558
- xml_parser_free($parser);
3559
- return false;
3560
- }
3561
- xml_parser_free($parser);
3562
- if ($GLOBALS['_xh']['isf'] > 1) // test that $GLOBALS['_xh']['value'] is an obj, too???
3563
- {
3564
- error_log($GLOBALS['_xh']['isf_reason']);
3565
- return false;
3566
- }
3567
- switch ($GLOBALS['_xh']['rt'])
3568
- {
3569
- case 'methodresponse':
3570
- $v =& $GLOBALS['_xh']['value'];
3571
- if ($GLOBALS['_xh']['isf'] == 1)
3572
- {
3573
- $vc = $v->structmem('faultCode');
3574
- $vs = $v->structmem('faultString');
3575
- $r = new xmlrpcresp(0, $vc->scalarval(), $vs->scalarval());
3576
- }
3577
- else
3578
- {
3579
- $r = new xmlrpcresp($v);
3580
- }
3581
- return $r;
3582
- case 'methodcall':
3583
- $m = new xmlrpcmsg($GLOBALS['_xh']['method']);
3584
- for($i=0; $i < count($GLOBALS['_xh']['params']); $i++)
3585
- {
3586
- $m->addParam($GLOBALS['_xh']['params'][$i]);
3587
- }
3588
- return $m;
3589
- case 'value':
3590
- return $GLOBALS['_xh']['value'];
3591
- default:
3592
- return false;
3593
- }
3594
- }
3595
-
3596
- /**
3597
- * decode a string that is encoded w/ "chunked" transfer encoding
3598
- * as defined in rfc2068 par. 19.4.6
3599
- * code shamelessly stolen from nusoap library by Dietrich Ayala
3600
- *
3601
- * @param string $buffer the string to be decoded
3602
- * @return string
3603
- */
3604
- function decode_chunked($buffer)
3605
- {
3606
- // length := 0
3607
- $length = 0;
3608
- $new = '';
3609
-
3610
- // read chunk-size, chunk-extension (if any) and crlf
3611
- // get the position of the linebreak
3612
- $chunkend = strpos($buffer,"\r\n") + 2;
3613
- $temp = substr($buffer,0,$chunkend);
3614
- $chunk_size = hexdec( trim($temp) );
3615
- $chunkstart = $chunkend;
3616
- while($chunk_size > 0)
3617
- {
3618
- $chunkend = strpos($buffer, "\r\n", $chunkstart + $chunk_size);
3619
-
3620
- // just in case we got a broken connection
3621
- if($chunkend == false)
3622
- {
3623
- $chunk = substr($buffer,$chunkstart);
3624
- // append chunk-data to entity-body
3625
- $new .= $chunk;
3626
- $length += strlen($chunk);
3627
- break;
3628
- }
3629
-
3630
- // read chunk-data and crlf
3631
- $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
3632
- // append chunk-data to entity-body
3633
- $new .= $chunk;
3634
- // length := length + chunk-size
3635
- $length += strlen($chunk);
3636
- // read chunk-size and crlf
3637
- $chunkstart = $chunkend + 2;
3638
-
3639
- $chunkend = strpos($buffer,"\r\n",$chunkstart)+2;
3640
- if($chunkend == false)
3641
- {
3642
- break; //just in case we got a broken connection
3643
- }
3644
- $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
3645
- $chunk_size = hexdec( trim($temp) );
3646
- $chunkstart = $chunkend;
3647
- }
3648
- return $new;
3649
- }
3650
-
3651
- /**
3652
- * xml charset encoding guessing helper function.
3653
- * Tries to determine the charset encoding of an XML chunk received over HTTP.
3654
- * NB: according to the spec (RFC 3023), if text/xml content-type is received over HTTP without a content-type,
3655
- * we SHOULD assume it is strictly US-ASCII. But we try to be more tolerant of unconforming (legacy?) clients/servers,
3656
- * which will be most probably using UTF-8 anyway...
3657
- *
3658
- * @param string $httpheaders the http Content-type header
3659
- * @param string $xmlchunk xml content buffer
3660
- * @param string $encoding_prefs comma separated list of character encodings to be used as default (when mb extension is enabled)
3661
- *
3662
- * @todo explore usage of mb_http_input(): does it detect http headers + post data? if so, use it instead of hand-detection!!!
3663
- */
3664
- function guess_encoding($httpheader='', $xmlchunk='', $encoding_prefs=null)
3665
- {
3666
- // discussion: see http://www.yale.edu/pclt/encoding/
3667
- // 1 - test if encoding is specified in HTTP HEADERS
3668
-
3669
- //Details:
3670
- // LWS: (\13\10)?( |\t)+
3671
- // token: (any char but excluded stuff)+
3672
- // quoted string: " (any char but double quotes and cointrol chars)* "
3673
- // header: Content-type = ...; charset=value(; ...)*
3674
- // where value is of type token, no LWS allowed between 'charset' and value
3675
- // Note: we do not check for invalid chars in VALUE:
3676
- // this had better be done using pure ereg as below
3677
- // Note 2: we might be removing whitespace/tabs that ought to be left in if
3678
- // the received charset is a quoted string. But nobody uses such charset names...
3679
-
3680
- /// @todo this test will pass if ANY header has charset specification, not only Content-Type. Fix it?
3681
- $matches = array();
3682
- if(preg_match('/;\s*charset\s*=([^;]+)/i', $httpheader, $matches))
3683
- {
3684
- return strtoupper(trim($matches[1], " \t\""));
3685
- }
3686
-
3687
- // 2 - scan the first bytes of the data for a UTF-16 (or other) BOM pattern
3688
- // (source: http://www.w3.org/TR/2000/REC-xml-20001006)
3689
- // NOTE: actually, according to the spec, even if we find the BOM and determine
3690
- // an encoding, we should check if there is an encoding specified
3691
- // in the xml declaration, and verify if they match.
3692
- /// @todo implement check as described above?
3693
- /// @todo implement check for first bytes of string even without a BOM? (It sure looks harder than for cases WITH a BOM)
3694
- if(preg_match('/^(\x00\x00\xFE\xFF|\xFF\xFE\x00\x00|\x00\x00\xFF\xFE|\xFE\xFF\x00\x00)/', $xmlchunk))
3695
- {
3696
- return 'UCS-4';
3697
- }
3698
- elseif(preg_match('/^(\xFE\xFF|\xFF\xFE)/', $xmlchunk))
3699
- {
3700
- return 'UTF-16';
3701
- }
3702
- elseif(preg_match('/^(\xEF\xBB\xBF)/', $xmlchunk))
3703
- {
3704
- return 'UTF-8';
3705
- }
3706
-
3707
- // 3 - test if encoding is specified in the xml declaration
3708
- // Details:
3709
- // SPACE: (#x20 | #x9 | #xD | #xA)+ === [ \x9\xD\xA]+
3710
- // EQ: SPACE?=SPACE? === [ \x9\xD\xA]*=[ \x9\xD\xA]*
3711
- if (preg_match('/^<\?xml\s+version\s*=\s*'. "((?:\"[a-zA-Z0-9_.:-]+\")|(?:'[a-zA-Z0-9_.:-]+'))".
3712
- '\s+encoding\s*=\s*' . "((?:\"[A-Za-z][A-Za-z0-9._-]*\")|(?:'[A-Za-z][A-Za-z0-9._-]*'))/",
3713
- $xmlchunk, $matches))
3714
- {
3715
- return strtoupper(substr($matches[2], 1, -1));
3716
- }
3717
-
3718
- // 4 - if mbstring is available, let it do the guesswork
3719
- // NB: we favour finding an encoding that is compatible with what we can process
3720
- if(extension_loaded('mbstring'))
3721
- {
3722
- if($encoding_prefs)
3723
- {
3724
- $enc = mb_detect_encoding($xmlchunk, $encoding_prefs);
3725
- }
3726
- else
3727
- {
3728
- $enc = mb_detect_encoding($xmlchunk);
3729
- }
3730
- // NB: mb_detect likes to call it ascii, xml parser likes to call it US_ASCII...
3731
- // IANA also likes better US-ASCII, so go with it
3732
- if($enc == 'ASCII')
3733
- {
3734
- $enc = 'US-'.$enc;
3735
- }
3736
- return $enc;
3737
- }
3738
- else
3739
- {
3740
- // no encoding specified: as per HTTP1.1 assume it is iso-8859-1?
3741
- // Both RFC 2616 (HTTP 1.1) and 1945 (HTTP 1.0) clearly state that for text/xxx content types
3742
- // this should be the standard. And we should be getting text/xml as request and response.
3743
- // BUT we have to be backward compatible with the lib, which always used UTF-8 as default...
3744
- return $GLOBALS['xmlrpc_defencoding'];
3745
- }
3746
- }
3747
-
3748
- /**
3749
- * Checks if a given charset encoding is present in a list of encodings or
3750
- * if it is a valid subset of any encoding in the list
3751
- * @param string $encoding charset to be tested
3752
- * @param mixed $validlist comma separated list of valid charsets (or array of charsets)
3753
- */
3754
- function is_valid_charset($encoding, $validlist)
3755
- {
3756
- $charset_supersets = array(
3757
- 'US-ASCII' => array ('ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4',
3758
- 'ISO-8859-5', 'ISO-8859-6', 'ISO-8859-7', 'ISO-8859-8',
3759
- 'ISO-8859-9', 'ISO-8859-10', 'ISO-8859-11', 'ISO-8859-12',
3760
- 'ISO-8859-13', 'ISO-8859-14', 'ISO-8859-15', 'UTF-8',
3761
- 'EUC-JP', 'EUC-', 'EUC-KR', 'EUC-CN')
3762
- );
3763
- if (is_string($validlist))
3764
- $validlist = explode(',', $validlist);
3765
- if (@in_array(strtoupper($encoding), $validlist))
3766
- return true;
3767
- else
3768
- {
3769
- if (array_key_exists($encoding, $charset_supersets))
3770
- foreach ($validlist as $allowed)
3771
- if (in_array($allowed, $charset_supersets[$encoding]))
3772
- return true;
3773
- return false;
3774
- }
3775
- }
3776
-
3777
- ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
i18n/cleantalk-ru_RU.mo CHANGED
Binary file
readme.txt CHANGED
@@ -1,18 +1,19 @@
1
  === Anti-spam by CleanTalk ===
2
  Contributors: znaeff, shagimuratov
3
- Tags: spam, antispam, anti-spam, spambot, spam-bot, stop spam, spammers, spamfree, captcha, capcha, captha, catcha, recaptcha, comment, comments, math, cloud, blacklist, puzzle, wpmu, network, multisite, akismet, спам, 垃圾邮件, correo no deseado, forms, formidable forms, feedback
4
  Requires at least: 3.0
5
  Tested up to: 3.7.1
6
- Stable tag: 2.5.18
7
  License: GPLv2
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
10
- Invisible anti-spam without CAPTCHA. No spam in the comments and feedback forms.
11
 
12
  == Description ==
13
 
14
  1. Stops spam bots in the comments.
15
- 1. Stops spam bots in the feedback forms.
 
16
  1. Invisible spam protection for visitors.
17
  1. Anti-spam without CAPTCHA, math, puzzles or Q&A.
18
 
@@ -26,6 +27,7 @@ The plugin is WordPress MultiUser (WPMU or WordPress network) compatible. Each b
26
 
27
  = Functions =
28
  * Antispam protection for comments form.
 
29
  * Antispam protection for <a href="http://wordpress.org/plugins/formidable/" target="_blank">Formiadble forms</a>.
30
 
31
  = Requirements =
@@ -79,12 +81,17 @@ Use other antispam plugins not necessarily, because CleanTalk stops 99.99% of sp
79
  == Screenshots ==
80
 
81
  1. The comment from spammer (sender blacklisted by IP/Email, comment text not relevant for the post) prohibited to place in the queue WordPress
82
- 1. Antispam settings to filter spam bots. Just enter Access key and app ready to stop spammers at the blog.
83
  1. Not spam, not relevant to article comment has moved to approval.
 
84
  1. Spam bot stopped at Formidable feedback form
85
 
86
  == Changelog ==
87
 
 
 
 
 
 
88
  = 2.5.18 2013-11-01 =
89
  * Fixed: Bug with selection of the last comments for post
90
  * New: Antispam protection for Formiadble feedback forms
@@ -196,6 +203,11 @@ Use other antispam plugins not necessarily, because CleanTalk stops 99.99% of sp
196
  * First version
197
 
198
  == Upgrade Notice ==
 
 
 
 
 
199
  = 2.5.18 2013-11-01 =
200
  * Fixed: Bug with selection of the last comments for post
201
  * New: Antispam protection for Formiadble feedback forms
1
  === Anti-spam by CleanTalk ===
2
  Contributors: znaeff, shagimuratov
3
+ Tags: spam, antispam, anti-spam, spambot, spam-bot, stop spam, spammers, spamfree, captcha, capcha, captha, catcha, recaptcha, comment, comments, math, cloud, blacklist, puzzle, wpmu, network, multisite, akismet, спам, 垃圾邮件, correo no deseado, forms, formidable forms, feedback, registration
4
  Requires at least: 3.0
5
  Tested up to: 3.7.1
6
+ Stable tag: 2.19
7
  License: GPLv2
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
10
+ Invisible anti-spam without CAPTCHA. No spam in the comments, registration and feedback forms.
11
 
12
  == Description ==
13
 
14
  1. Stops spam bots in the comments.
15
+ 1. Stops spam bots in the registration.
16
+ 1. Stops spam bots at the feedback forms.
17
  1. Invisible spam protection for visitors.
18
  1. Anti-spam without CAPTCHA, math, puzzles or Q&A.
19
 
27
 
28
  = Functions =
29
  * Antispam protection for comments form.
30
+ * Antispam protection for registration form.
31
  * Antispam protection for <a href="http://wordpress.org/plugins/formidable/" target="_blank">Formiadble forms</a>.
32
 
33
  = Requirements =
81
  == Screenshots ==
82
 
83
  1. The comment from spammer (sender blacklisted by IP/Email, comment text not relevant for the post) prohibited to place in the queue WordPress
 
84
  1. Not spam, not relevant to article comment has moved to approval.
85
+ 1. Antispam stoppped spam bot at the registration form
86
  1. Spam bot stopped at Formidable feedback form
87
 
88
  == Changelog ==
89
 
90
+ = 2.19 2013-11-08 =
91
+ * New: Antispam protection from spam bots at the registration form
92
+ * Changed: Russian localization for admin panel
93
+ * Changed: PHP code optimizations
94
+
95
  = 2.5.18 2013-11-01 =
96
  * Fixed: Bug with selection of the last comments for post
97
  * New: Antispam protection for Formiadble feedback forms
203
  * First version
204
 
205
  == Upgrade Notice ==
206
+ = 2.19 2013-11-08 =
207
+ * New: Antispam protection from spam bots at the registration form
208
+ * Changed: Russian localization for admin panel
209
+ * Changed: PHP code optimizations
210
+
211
  = 2.5.18 2013-11-01 =
212
  * Fixed: Bug with selection of the last comments for post
213
  * New: Antispam protection for Formiadble feedback forms
screenshot-2.png CHANGED
Binary file
screenshot-3.png CHANGED
Binary file
screenshot-4.png CHANGED
Binary file