b2evolution PHP Cross Reference Blogging Systems

Source: /inc/xmlrpc/model/_xmlrpcs.funcs.php - 2142 lines - 59988 bytes - Summary - Text - Print

   1  <?php
   2  /**
   3   * @see http://phpxmlrpc.sourceforge.net/
   4   * @see http://xmlrpc.usefulinc.com/doc/
   5   * @copyright Edd Dumbill <edd@usefulinc.com> (C) 1999-2002
   6   *
   7   * @package evocore
   8   * @subpackage xmlrpc
   9   */
  10  if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
  11  
  12  if( CANUSEXMLRPC !== TRUE )
  13  {
  14      return;
  15  }
  16  
  17  /**
  18   * Include XML-RPC for PHP SERVER library
  19   */
  20  load_funcs('_ext/xmlrpc/_xmlrpcs.inc.php');
  21  
  22  
  23  // --------------------------------------- SUPPORT FUNCTIONS ----------------------------------------
  24  
  25  
  26  /**
  27   * Used for logging, only if {@link $debug_xmlrpc_logging} is true
  28   *
  29   * @return boolean Have we logged?
  30   */
  31  function logIO( $msg, $newline = false )
  32  {
  33      global $debug_xmlrpc_logging, $basepath, $xmlsrv_subdir;
  34  
  35      if( ! $debug_xmlrpc_logging )
  36      {
  37          return false;
  38      }
  39  
  40      if( $newline )
  41      {
  42          $msg = "\n\n".date('Y-m-d H:i:s')."\n".$msg;
  43      }
  44  
  45      $ok = save_to_file( $msg."\n", $basepath.$xmlsrv_subdir.'xmlrpc.log', 'a+' );
  46  
  47      return (bool) $ok;
  48  }
  49  
  50  
  51  /**
  52   * Returns a string replaced by stars, for passwords.
  53   *
  54   * @param string the source string
  55   * @return string same length, but only stars
  56   */
  57  function starify( $string )
  58  {
  59      return str_repeat( '*', strlen( $string ) );
  60  }
  61  
  62  
  63  /**
  64   * metaWeblog.newMediaObject image upload
  65   * wp.uploadFile
  66   *
  67   * Supplied image is encoded into the struct as bits
  68   *
  69   * @see http://www.xmlrpc.com/metaWeblogApi#metaweblognewmediaobject
  70   * @see http://codex.wordpress.org/XML-RPC_wp#wp.uploadFile
  71   *
  72   * @param xmlrpcmsg XML-RPC Message
  73   *                    0 blogid (string): Unique identifier of the blog the post will be added to.
  74   *                        Currently ignored in b2evo, in favor of the category.
  75   *                    1 username (string): Login for a Blogger user who has permission to edit the given
  76   *                        post (either the user who originally created it or an admin of the blog).
  77   *                    2 password (string): Password for said username.
  78   *                    3 struct (struct)
  79   *                             - name : filename
  80   *                             - type : mimetype
  81   *                             - bits : base64 encoded file
  82   * @return xmlrpcresp XML-RPC Response
  83   */
  84  function _wp_mw_newmediaobject($m)
  85  {
  86      global $Settings, $Plugins, $force_upload_forbiddenext;
  87  
  88      // CHECK LOGIN:
  89      /**
  90       * @var User
  91       */
  92      if( ! $current_User = & xmlrpcs_login( $m, 1, 2 ) )
  93      {    // Login failed, return (last) error:
  94          return xmlrpcs_resperror();
  95      }
  96  
  97      // GET BLOG:
  98      /**
  99       * @var Blog
 100       */
 101      if( ! $Blog = & xmlrpcs_get_Blog( $m, 0 ) )
 102      {    // Login failed, return (last) error:
 103          return xmlrpcs_resperror();
 104      }
 105  
 106      // CHECK PERMISSION:
 107      if( ! $current_User->check_perm( 'files', 'add', false, $Blog->ID ) )
 108      {    // Permission denied
 109          return xmlrpcs_resperror( 3 );    // User error 3
 110      }
 111      logIO( 'Permission granted.' );
 112  
 113      if( ! $Settings->get('upload_enabled') )
 114      {
 115          return xmlrpcs_resperror( 2, 'Object upload not allowed' );
 116      }
 117  
 118      $xcontent = $m->getParam(3);
 119      // Get the main data - and decode it properly for the image - sorry, binary object
 120      logIO( 'Decoding content...' );
 121      $contentstruct = xmlrpc_decode_recurse($xcontent);
 122      $data = $contentstruct['bits'];
 123  
 124      $file_mimetype = isset($contentstruct['type']) ? $contentstruct['type'] : '(none)';
 125      logIO( 'Received MIME type: '.$file_mimetype );
 126  
 127      $overwrite = false;
 128      if( isset($contentstruct['overwrite']) )
 129      {
 130          $overwrite = (bool) $contentstruct['overwrite'];
 131      }
 132      logIO( 'Overwrite if exists: '.($overwrite ? 'yes' : 'no') );
 133  
 134      load_funcs('files/model/_file.funcs.php');
 135  
 136      $filesize = evo_bytes( $data );
 137      if( ( $maxfilesize = $Settings->get('upload_maxkb') * 1024 ) && $filesize > $maxfilesize )
 138      {
 139          return xmlrpcs_resperror( 4, sprintf( T_('The file is too large: %s but the maximum allowed is %s.'),
 140                      bytesreadable($filesize, false), bytesreadable($maxfilesize, false) ) );
 141      }
 142      logIO( 'File size is OK: '.bytesreadable($filesize, false) );
 143  
 144      $FileRootCache = & get_FileRootCache();
 145      $fm_FileRoot = & $FileRootCache->get_by_type_and_ID( 'collection', $Blog->ID, true );
 146      if( ! $fm_FileRoot )
 147      {    // fileRoot not found:
 148          return xmlrpcs_resperror( 14, 'File root not found' );
 149      }
 150  
 151      $rf_filepath = $contentstruct['name'];
 152      logIO( 'Received filepath: '.$rf_filepath );
 153  
 154      // Split into path + name:
 155      $filepath_parts = explode( '/', $rf_filepath );
 156      $filename = array_pop( $filepath_parts );
 157      logIO( 'Original file name: '.$filename );
 158  
 159      // Validate and sanitize filename
 160      if( $error_filename = process_filename( $filename, true ) )
 161      {
 162          return xmlrpcs_resperror( 5, $error_filename );
 163      }
 164      logIO( 'Sanitized file name: '.$filename );
 165  
 166      // Check valid path parts:
 167      $rds_subpath = '';
 168      foreach( $filepath_parts as $filepath_part )
 169      {
 170          if( empty($filepath_part) || $filepath_part == '.' )
 171          {    // self ref not useful
 172              continue;
 173          }
 174  
 175          if( $error = validate_dirname($filepath_part) )
 176          {    // invalid relative path:
 177              logIO( $error );
 178              return xmlrpcs_resperror( 6, $error );
 179          }
 180  
 181          $rds_subpath .= $filepath_part.'/';
 182      }
 183      logIO( 'Subpath: '.$rds_subpath );
 184  
 185      // Create temporary file and insert contents into it.
 186      $tmpfile_name = tempnam(sys_get_temp_dir(), 'fmupload');
 187      if( $tmpfile_name )
 188      {
 189          if( save_to_file( $data, $tmpfile_name, 'wb' ) )
 190          {
 191              $image_info = @getimagesize($tmpfile_name);
 192          }
 193          else
 194          {
 195              return xmlrpcs_resperror( 13, 'Error while writing to temp file.' );
 196          }
 197      }
 198  
 199      if( !empty($image_info) )
 200      {    // This is an image file, let's check mimetype and correct extension
 201          if( $image_info['mime'] != $file_mimetype )
 202          {    // Invalid file type
 203              $FiletypeCache = & get_FiletypeCache();
 204              // Get correct file type based on mime type
 205              $correct_Filetype = $FiletypeCache->get_by_mimetype( $image_info['mime'], false, false );
 206  
 207              $file_mimetype = $image_info['mime'];
 208  
 209              // Check if file type is known by us, and if it is allowed for upload.
 210              // If we don't know this file type or if it isn't allowed we don't change the extension! The current extension is allowed for sure.
 211              if( $correct_Filetype && $correct_Filetype->is_allowed() )
 212              {    // A FileType with the given mime type exists in database and it is an allowed file type for current User
 213                  // The "correct" extension is a plausible one, proceed...
 214                  $correct_extension = array_shift($correct_Filetype->get_extensions());
 215                  $path_info = pathinfo($filename);
 216                  $current_extension = $path_info['extension'];
 217  
 218                  // change file extension to the correct extension, but only if the correct extension is not restricted, this is an extra security check!
 219                  if( strtolower($current_extension) != strtolower($correct_extension) && ( !in_array( $correct_extension, $force_upload_forbiddenext ) ) )
 220                  {    // change the file extension to the correct extension
 221                      $old_filename = $filename;
 222                      $filename = $path_info['filename'].'.'.$correct_extension;
 223                  }
 224              }
 225          }
 226      }
 227  
 228      // Get File object for requested target location:
 229      $FileCache = & get_FileCache();
 230      $newFile = & $FileCache->get_by_root_and_path( $fm_FileRoot->type, $fm_FileRoot->in_type_ID, trailing_slash($rds_subpath).$filename, true );
 231  
 232      if( $newFile->exists() )
 233      {
 234          if( $overwrite && $newFile->unlink() )
 235          {    // OK, file deleted
 236              // Delete thumb caches from old location:
 237              logIO( 'Old file deleted' );
 238              $newFile->rm_cache();
 239          }
 240          else
 241          {
 242              return xmlrpcs_resperror( 8, sprintf( T_('The file &laquo;%s&raquo; already exists.'), $filename ) );
 243          }
 244      }
 245  
 246      // Trigger plugin event
 247      if( $Plugins->trigger_event_first_false( 'AfterFileUpload', array(
 248                  'File' => & $newFile,
 249                  'name' => & $filename,
 250                  'type' => & $file_mimetype,
 251                  'tmp_name' => & $tmpfile_name,
 252                  'size' => & $filesize,
 253              ) ) )
 254      {
 255          // Plugin returned 'false'.
 256          // Abort upload for this file:
 257          @unlink($tmpfile_name);
 258          return xmlrpcs_resperror( 16, 'File upload aborted by a plugin.' );
 259      }
 260  
 261      if( ! mkdir_r( $newFile->get_dir() ) )
 262      {    // Dir didn't already exist and could not be created
 263          return xmlrpcs_resperror( 9, 'Error creating sub directories: '.$newFile->get_rdfs_rel_path() );
 264      }
 265  
 266      if( ! @rename( $tmpfile_name, $newFile->get_full_path() ) )
 267      {
 268          return xmlrpcs_resperror( 13, 'Error while writing to file.' );
 269      }
 270  
 271      // chmod the file
 272      $newFile->chmod();
 273  
 274      // Initializes file properties (type, size, perms...)
 275      $newFile->load_properties();
 276  
 277      // Load meta data AND MAKE SURE IT IS CREATED IN DB:
 278      $newFile->meta == 'unknown';
 279      $newFile->load_meta( true );
 280  
 281      // Resize and rotate
 282      logIO( 'Running file post-processing (resize and rotate)...' );
 283      prepare_uploaded_files( array($newFile) );
 284      logIO( 'Done' );
 285  
 286      $url = $newFile->get_url();
 287      logIO( 'URL of new file: '.$url );
 288  
 289      $struct = new xmlrpcval(array(
 290              'file' => new xmlrpcval($filename, 'string'),
 291              'url' => new xmlrpcval($url, 'string'),
 292              'type' => new xmlrpcval($file_mimetype, 'string'),
 293          ), 'struct');
 294  
 295      logIO( 'OK.' );
 296      return new xmlrpcresp($struct);
 297  }
 298  
 299  
 300  /**
 301   * Used in these methods
 302   *         - metaweblog.getPost
 303   *         - metaWeblog.getRecentPosts
 304   *         - wp.getPages
 305   *         - wp.getPage
 306   *
 307   * Note: a valid Item object must be supplied
 308   *
 309   */
 310  function _wp_mw_get_item_struct( & $Item )
 311  {
 312      global $DB;
 313  
 314      logIO('Item title: '.$Item->title);
 315  
 316      if( is_null($Item->extra_cat_IDs) )
 317      {    // Load extra cats
 318          $Item->extra_cat_IDs = postcats_get_byID($Item->ID);
 319      }
 320  
 321      $cat_ids = $Item->extra_cat_IDs;
 322      array_unshift($cat_ids, $Item->main_cat_ID); // Move to top
 323      $cat_ids = array_unique( $cat_ids );
 324  
 325      $SQL = 'SELECT cat_name FROM T_categories WHERE cat_ID IN ('.$DB->quote($cat_ids).')';
 326      $cat_names = array();
 327      if( $categories = $DB->get_col($SQL) )
 328      {
 329          foreach( $categories as $cat )
 330          {
 331              $cat_names[] = new xmlrpcval($cat);
 332          }
 333          logIO( 'Categories: '.implode(', ', $categories) );
 334      }
 335  
 336      $tag_names_string = '';
 337      if( $tags = $Item->get_tags() )
 338      {
 339          $tag_names_string = implode(', ', $tags);
 340          logIO( 'Tags: '.$tag_names_string );
 341      }
 342  
 343      $SQL = 'SELECT * FROM T_items__item_settings WHERE iset_item_ID = '.$DB->quote($Item->ID);
 344      $item_settings = array();
 345      if( $settings = $DB->get_results($SQL) )
 346      {
 347          foreach( $settings as $setting )
 348          {
 349              $item_settings[] = new xmlrpcval(array(
 350                      'id'    => new xmlrpcval( 0 ),
 351                      'key'   => new xmlrpcval( $setting->iset_name ),
 352                      'value' => new xmlrpcval( $setting->iset_value ),
 353                  ),'struct');
 354          }
 355      }
 356  
 357      // Split item content on before and after <!--more-->
 358      // No tag balancing, no pages, no rendering, just raw text
 359      $content_parts = explode( '<!--more-->', $Item->content, 2 );
 360      if( !isset($content_parts[1]) ) $content_parts[1] = '';
 361  
 362      $parent_title = '';
 363      if( isset($Item->parent_ID) )
 364      {
 365          $ItemCache = & get_ItemCache();
 366          if( $parent_Item = & $ItemCache->get_by_ID( $Item->parent_ID, false, false ) );
 367          {
 368              $parent_title = $parent_Item->title;
 369          }
 370      }
 371  
 372      $item_status = wp_or_b2evo_item_status($Item->status, 'wp');
 373      $url = $Item->get_permanent_url();
 374  
 375      $data = array(
 376              'page_id'                    => new xmlrpcval( $Item->ID, 'int' ),
 377              'postid'                    => new xmlrpcval( $Item->ID, 'int' ),    // mw
 378              'userid'                    => new xmlrpcval( $Item->creator_user_ID, 'int' ),
 379              'page_status'                => new xmlrpcval( $item_status ),
 380              'post_status'                => new xmlrpcval( $item_status ),    // mw
 381              'description'                => new xmlrpcval( $content_parts[0] ),
 382              'text_more'                    => new xmlrpcval( $content_parts[1] ),
 383              'title'                        => new xmlrpcval( $Item->title ),
 384              'link'                        => new xmlrpcval( $url ),
 385              'permalink'                    => new xmlrpcval( $url ),    // mw
 386              'permaLink'                    => new xmlrpcval( $url ),
 387              'categories'                => new xmlrpcval( $cat_names, 'array' ),
 388              'excerpt'                    => new xmlrpcval( $Item->excerpt ),
 389              'mt_excerpt'                => new xmlrpcval( $Item->excerpt ),
 390              'mt_allow_comments'            => new xmlrpcval( ($Item->can_comment(NULL) ? 1 : 0), 'int' ),
 391              'mt_text_more'                => new xmlrpcval( $content_parts[1] ),
 392              'mt_keywords'                => new xmlrpcval( $tag_names_string ),
 393              'wp_slug'                    => new xmlrpcval( $Item->urltitle ),
 394              'wp_author'                    => new xmlrpcval( $Item->get('t_author') ),
 395              'wp_page_parent_id'            => new xmlrpcval( (isset($Item->parent_ID) ? $Item->parent_ID : 0), 'int' ),
 396              'wp_page_parent_title'        => new xmlrpcval( $parent_title ),
 397              'wp_page_order'                => new xmlrpcval( $Item->order ), // We don't use 'int' here because b2evolution "order" is stored as double while WP uses integer values
 398              'wp_author_id'                => new xmlrpcval( $Item->creator_user_ID, 'string' ),
 399              'wp_author_display_name'    => new xmlrpcval( $Item->get('t_author') ),
 400              'wp_post_format'            => new xmlrpcval( $Item->ptyp_ID ),
 401              'date_created_gmt'            => new xmlrpcval( datetime_to_iso8601($Item->issue_date, true), 'dateTime.iso8601' ),
 402              'dateCreated'                => new xmlrpcval( datetime_to_iso8601($Item->issue_date),'dateTime.iso8601' ),
 403              'custom_fields'                => new xmlrpcval( $item_settings, 'array' ),
 404              'wp_page_template'            => new xmlrpcval('default'), // n/a
 405              'mt_allow_pings'            => new xmlrpcval( 0, 'int' ), // n/a
 406              'wp_password'                => new xmlrpcval(''), // n/a
 407          );
 408  
 409      return $data;
 410  }
 411  
 412  
 413  function _wp_or_blogger_getusersblogs( $type, $m )
 414  {
 415      global $xmlsrv_url;
 416  
 417      if ( $type == 'wp')
 418      {
 419          $username_index = 0;
 420          $password_index = 1;
 421      }
 422      else
 423      {
 424          $username_index = 1;
 425          $password_index = 2;
 426      }
 427      // CHECK LOGIN:
 428      if( ! $current_User = & xmlrpcs_login( $m, $username_index, $password_index ) )
 429      {    // Login failed, return (last) error:
 430          return xmlrpcs_resperror();
 431      }
 432  
 433      // LOAD BLOGS the user is a member of:
 434      $BlogCache = & get_BlogCache();
 435      $blog_array = $BlogCache->load_user_blogs( 'blog_ismember', 'view', $current_User->ID );
 436  
 437      $resp_array = array();
 438      foreach( $blog_array as $l_blog_ID )
 439      {    // Loop through all blogs that match the requested permission:
 440  
 441          /**
 442           * @var Blog
 443           */
 444          $l_Blog = & $BlogCache->get_by_ID( $l_blog_ID );
 445  
 446          logIO('Current user IS a member of this blog: '.$l_blog_ID);
 447          $item = array(
 448                      'blogid' => new xmlrpcval( $l_blog_ID ),
 449                      'blogName' => new xmlrpcval( $l_Blog->get('shortname') ),
 450                      'url' => new xmlrpcval( $l_Blog->gen_blogurl() ),
 451                      'isAdmin' => new xmlrpcval( $current_User->check_perm( 'blog_admin', 'edit', false, $l_Blog->ID ), 'boolean') );
 452          if ( $type == 'wp')
 453          {
 454              $item['xmlrpc'] = new xmlrpcval ( $xmlsrv_url.'xmlrpc.php' );
 455          }
 456  
 457          $resp_array[] = new xmlrpcval( $item, 'struct');
 458      }
 459  
 460      logIO( 'OK.' );
 461      return new xmlrpcresp( new xmlrpcval( $resp_array, 'array' ) );
 462  }
 463  
 464  
 465  /**
 466   * Decode the dateCreated
 467   *
 468   * @param struct
 469   * @return string MYSQL date
 470   */
 471  function _mw_decode_date( $contentstruct )
 472  {
 473      global $Settings;
 474  
 475      $postdate = NULL;
 476  
 477      if( !empty($contentstruct['date_created_gmt']) )
 478      {
 479          $postdate = iso8601_to_datetime($contentstruct['date_created_gmt']);
 480  
 481          // Add time difference to GMT date
 482          $postdate = date( 'Y-m-d H:i:s', (mysql2timestamp($postdate, true) + $Settings->get('time_difference')) );
 483  
 484          logIO( 'Using contentstruct date_created_gmt: '.$postdate );
 485      }
 486  
 487      if( empty($postdate) && !empty($contentstruct['dateCreated']) )
 488      {
 489          $postdate = $contentstruct['dateCreated'];
 490          if( strpos($postdate, 'T') > 0 )
 491          {    // Date is in ISO 8601 format
 492              $postdate = iso8601_to_datetime($postdate);
 493          }
 494  
 495          logIO( 'Using contentstruct dateCreated: '.$postdate );
 496      }
 497  
 498      return $postdate;
 499  }
 500  
 501  
 502  /**
 503   * Get IDs for requested categories
 504   *
 505   * @param array struct
 506   * @param integer blog ID
 507   * @param boolean Return empty array (instead of error), if no cats given in struct?
 508   * @return array|xmlrpcresp A list of category IDs or xmlrpcresp in case of error.
 509   */
 510  function _mw_get_cat_IDs( $contentstruct, $Blog, $empty_struct_is_ok = false )
 511  {
 512      global $DB;
 513  
 514      $categories = array();
 515      if( isset($contentstruct['categories']) )
 516      {
 517          foreach( $contentstruct['categories'] as $l_catname )
 518          {
 519              $categories[] = trim(strip_tags($l_catname));
 520          }
 521      }
 522      logIO( 'Categories: '.implode( ', ', $categories ) );
 523  
 524      if( $empty_struct_is_ok && empty($categories) )
 525      {
 526          return $categories;
 527      }
 528  
 529      $cat_IDs = array();
 530      if( ! empty($categories) )
 531      {
 532          // for cross-blog-entries, the cat_blog_ID WHERE clause should be removed (but cats are given by name!)
 533          $SQL = 'SELECT cat_ID
 534                  FROM T_categories
 535                  WHERE cat_blog_ID = '.$DB->quote($Blog->ID).'
 536                  AND cat_name IN (';
 537  
 538          foreach( $categories as $l_cat )
 539          {
 540              $SQL .= '"'.$DB->escape($l_cat).'", ';
 541          }
 542          if( ! empty($categories) )
 543          {
 544              $SQL = substr($SQL, 0, -2); // remove ', '
 545          }
 546          $SQL .= ')';
 547  
 548          logIO('Loading categories by name');
 549          if( ! $cat_IDs = $DB->get_col($SQL) )
 550          {    // DB error
 551              logIO('Couldn\'t find requested categories');
 552          }
 553      }
 554  
 555      if( empty($cat_IDs) )
 556      {
 557          // No category given/valid - use default for this blog:
 558          logIO('Using default category');
 559  
 560          if( ! $default_cat = $Blog->get_default_cat_ID() )
 561          {
 562              logIO( 'There are no categories in this blog yet');
 563              return xmlrpcs_resperror( 5, 'There are no categories in this blog yet' ); // user error 5
 564          }
 565          $cat_IDs = array($default_cat);
 566      }
 567      return $cat_IDs;
 568  }
 569  
 570  
 571  /**
 572   * Helper for {@link b2_getcategories()} and {@link mt_getPostCategories()}, because they differ
 573   * only in the "categoryId" case ("categoryId" (b2) vs "categoryID" (MT))
 574   *
 575   * @param string Type, either "b2" or "mt"
 576   * @param xmlrpcmsg XML-RPC Message
 577   *                    0 blogid (string): Unique identifier of the blog to query
 578   *                    1 username (string): Login for a Blogger user who is member of the blog.
 579   *                    2 password (string): Password for said username.
 580   * @return xmlrpcresp XML-RPC Response
 581   */
 582  function _b2_or_mt_get_categories( $type, $m )
 583  {
 584      global $DB, $Settings;
 585  
 586      /**
 587       * @var User
 588       */
 589      if( ! $current_User = & xmlrpcs_login( $m, 1, 2 ) )
 590      {    // Login failed, return (last) error:
 591          return xmlrpcs_resperror();
 592      }
 593  
 594      // GET BLOG:
 595      /**
 596       * @var Blog
 597       */
 598      if( ! $Blog = & xmlrpcs_get_Blog( $m, 0 ) )
 599      {    // Login failed, return (last) error:
 600          return xmlrpcs_resperror();
 601      }
 602  
 603      $SQL = new SQL();
 604      $SQL->SELECT( 'cat_ID, cat_name, cat_order' );
 605      $SQL->FROM( 'T_categories' );
 606      $SQL->WHERE( $Blog->get_sql_where_aggregate_coll_IDs('cat_blog_ID') );
 607  
 608      if( $Settings->get('chapter_ordering') == 'manual' )
 609      {    // Manual order
 610          $SQL->ORDER_BY( 'cat_order' );
 611      }
 612      else
 613      {    // Alphabetic order
 614          $SQL->ORDER_BY( 'cat_name' );
 615      }
 616  
 617      $rows = $DB->get_results( $SQL->get() );
 618      if( $DB->error )
 619      {    // DB error
 620          return xmlrpcs_resperror( 99, 'DB error: '.$DB->last_error ); // user error 9
 621      }
 622      logIO( 'Categories: '.count($rows) );
 623  
 624      $categoryIdName = ( $type == 'b2' ? 'categoryID' : 'categoryId' );
 625  
 626      $ChapterCache = & get_ChapterCache();
 627      $data = array();
 628      foreach( $rows as $row )
 629      {
 630          $Chapter = & $ChapterCache->get_by_ID( $row->cat_ID, false, false );
 631          if( ! $Chapter )
 632          {
 633              continue;
 634          }
 635          $data[] = new xmlrpcval( array(
 636                  $categoryIdName => new xmlrpcval( $Chapter->ID ),
 637                  'categoryName' => new xmlrpcval( $Chapter->name )
 638              ), 'struct' );
 639      }
 640  
 641      logIO( 'OK.' );
 642      return new xmlrpcresp( new xmlrpcval($data, 'array') );
 643  }
 644  
 645  
 646  /**
 647   * metaWeblog.getCategories
 648   *
 649   * @see http://www.xmlrpc.com/metaWeblogApi#metawebloggetcategories
 650   *
 651   * @param xmlrpcmsg XML-RPC Message
 652   *                    0 blogid (string): Unique identifier of the blog the post will be added to.
 653   *                        Currently ignored in b2evo, in favor of the category.
 654   *                    1 username (string): Login for a Blogger user who has permission to edit the given
 655   *                        post (either the user who originally created it or an admin of the blog).
 656   *                    2 password (string): Password for said username.
 657   * @param array of params to narrow category selection
 658   */
 659  function _wp_mw_getcategories( $m, $params = array() )
 660  {
 661      global $DB, $Settings;
 662  
 663      // CHECK LOGIN:
 664      /**
 665       * @var User
 666       */
 667      if( ! $current_User = & xmlrpcs_login( $m, 1, 2 ) )
 668      {    // Login failed, return (last) error:
 669          return xmlrpcs_resperror();
 670      }
 671  
 672      // GET BLOG:
 673      /**
 674       * @var Blog
 675       */
 676      if( ! $Blog = & xmlrpcs_get_Blog( $m, 0 ) )
 677      {    // Login failed, return (last) error:
 678          return xmlrpcs_resperror();
 679      }
 680  
 681      $SQL = new SQL();
 682      $SQL->SELECT( 'cat_ID, cat_name, cat_order' );
 683      $SQL->FROM( 'T_categories' );
 684      $SQL->WHERE( $Blog->get_sql_where_aggregate_coll_IDs('cat_blog_ID') );
 685  
 686      if( !empty($params['search']) )
 687      {    // Category name starts with 'search'
 688          $SQL->WHERE_and( 'cat_name LIKE "'.$DB->like_escape( $params['search'] ).'%"' );
 689      }
 690      if( $Settings->get('chapter_ordering') == 'manual' )
 691      {    // Manual order
 692          $SQL->ORDER_BY( 'cat_order' );
 693      }
 694      else
 695      {    // Alphabetic order
 696          $SQL->ORDER_BY( 'cat_name' );
 697      }
 698  
 699      $rows = $DB->get_results( $SQL->get() );
 700      if( $DB->error )
 701      {    // DB error
 702          return xmlrpcs_resperror( 99, 'DB error: '.$DB->last_error ); // user error 9
 703      }
 704      $total_rows = count($rows);
 705  
 706      logIO( 'Categories: '.$total_rows );
 707  
 708      $ChapterCache = & get_ChapterCache();
 709      $data = array();
 710      for( $i=0; $i<$total_rows; $i++ )
 711      {
 712          if( !empty($params['limit']) && $i >= $params['limit'] )
 713          {    // We found enough, exit the loop
 714              break;
 715          }
 716  
 717          $Chapter = & $ChapterCache->get_by_ID( $rows[$i]->cat_ID, false, false );
 718          if( ! $Chapter )
 719          {
 720              continue;
 721          }
 722  
 723          if( isset($params['search']) )
 724          {    // wp.suggestCategories
 725              $data[] = new xmlrpcval( array(
 726                      'category_id' => new xmlrpcval( intval($Chapter->ID) ),
 727                      'category_name' => new xmlrpcval( $Chapter->name ),
 728                  ),'struct');
 729          }
 730          else
 731          {
 732              $data[] = new xmlrpcval( array(
 733                      'categoryId' => new xmlrpcval( intval($Chapter->ID) ),                // not in RFC (http://www.xmlrpc.com/metaWeblogApi)
 734                      'parentId' => new xmlrpcval( intval($Chapter->parent_ID) ),            // not in RFC
 735                      'description' => new xmlrpcval( $Chapter->name ),
 736                      'categoryDescription' => new xmlrpcval( $Chapter->description ),    // not in RFC
 737                      'categoryName' => new xmlrpcval( $Chapter->name ),                    // not in RFC
 738                      'htmlUrl' => new xmlrpcval( $Chapter->get_permanent_url() ),
 739                      'rssUrl' => new xmlrpcval( url_add_param($Chapter->get_permanent_url(), 'tempskin=_rss2') )
 740                  ),'struct');
 741          }
 742      }
 743  
 744      logIO( 'OK.' );
 745      return new xmlrpcresp( new xmlrpcval($data, 'array') );
 746  }
 747  
 748  
 749  /**
 750   * Get current_User for an XML-RPC request - Includes login (password) check.
 751   *
 752   * @param xmlrpcmsg XML-RPC Message
 753   * @param integer idx of login param in XML-RPC Message
 754   * @param integer idx of pass param in XML-RPC Message
 755   * @return User or NULL
 756   */
 757  function & xmlrpcs_login( $m, $login_param, $pass_param )
 758  {
 759      global $xmlrpcs_errcode, $xmlrpcs_errmsg, $xmlrpcerruser;
 760  
 761      $username = $m->getParam( $login_param );
 762      $username = $username->scalarval();
 763  
 764      $password = $m->getParam( $pass_param );
 765      $password = $password->scalarval();
 766  
 767      /**
 768       * @var UserCache
 769       */
 770      $UserCache = & get_UserCache();
 771      $current_User = & $UserCache->get_by_login( $username );
 772  
 773      if( empty( $current_User ) || ! $current_User->check_password( $password, false ) )
 774      {    // User not found or password doesn't match
 775          $xmlrpcs_errcode = $xmlrpcerruser+1;
 776          $xmlrpcs_errmsg = 'Wrong username/password combination: '.$username.' / '.starify($password);
 777          $r = NULL;
 778          return $r;
 779      }
 780  
 781      // This may be needed globally for status permissions in ItemList2, etc..
 782      $GLOBALS['current_User'] = & $current_User;
 783  
 784      // Check here ability to use APIs
 785      $group = $current_User->get_Group();
 786      if( ! $group->check_perm('perm_api', 'always') )
 787      {    // Permission denied
 788          $xmlrpcs_errcode = $xmlrpcerruser+1;
 789          $xmlrpcs_errmsg = 'User has no permission to use this API: '.$username.' / '.starify($password);
 790          $r = NULL;
 791          return $r;
 792      }
 793  
 794      logIO( 'Login OK - User: '.$current_User->ID.' - '.$current_User->login );
 795  
 796  
 797      return $current_User;
 798  }
 799  
 800  
 801  /**
 802   * Get current Blog for an XML-RPC request.
 803   *
 804   * @param xmlrpcmsg XML-RPC Message
 805   * @param integer idx of blog ID param
 806   * @return Blog or NULL
 807   */
 808  function & xmlrpcs_get_Blog( $m, $id_param )
 809  {
 810      global $xmlrpcs_errcode, $xmlrpcs_errmsg, $xmlrpcerruser;
 811  
 812      $blog = $m->getParam( $id_param );
 813      $blog = $blog->scalarval();
 814      // waltercruz> qtm: http://qtm.blogistan.co.uk/ inserts some spacing before/after blogID.
 815      $blog = (int) trim($blog);
 816      /**
 817       * @var BlogCache
 818       */
 819      $BlogCache = & get_BlogCache();
 820      /**
 821       * @var Blog
 822       */
 823      $Blog = & $BlogCache->get_by_ID( $blog, false, false );
 824  
 825      if( empty( $Blog ) )
 826      {    // Blog not found
 827          $xmlrpcs_errcode = $xmlrpcerruser+2;
 828          $xmlrpcs_errmsg = 'Requested blog/Collection ('.$blog.') does not exist.';
 829          $r = NULL;
 830          return $r;
 831      }
 832  
 833      logIO( 'Requested Blog: '.$Blog->ID.' - '.$Blog->name );
 834  
 835      return $Blog;
 836  }
 837  
 838  
 839  /**
 840   * Get current Item for an XML-RPC request.
 841   *
 842   * @param xmlrpcmsg XML-RPC Message
 843   * @param integer idx of item ID param
 844   * @return Item or NULL
 845   */
 846  function & xmlrpcs_get_Item( $m, $id_param )
 847  {
 848      global $xmlrpcs_errcode, $xmlrpcs_errmsg, $xmlrpcerruser;
 849  
 850      $postid = $m->getParam( $id_param );
 851      $postid = $postid->scalarval();
 852  
 853      /**
 854       * @var ItemCache
 855       */
 856      $ItemCache = & get_ItemCache();
 857      /**
 858       * @var Item
 859       */
 860      $edited_Item = & $ItemCache->get_by_ID( $postid, false, false );
 861  
 862      if( empty( $edited_Item ) )
 863      {    // Item not found
 864          $xmlrpcs_errcode = $xmlrpcerruser+6;
 865          $xmlrpcs_errmsg = 'Requested post/Item ('.$postid.') does not exist.';
 866          $r = NULL;
 867          return $r;
 868      }
 869  
 870      logIO( 'Requested Item: '.$edited_Item->ID.' - '.$edited_Item->title );
 871  
 872      return $edited_Item;
 873  }
 874  
 875  
 876  /**
 877   * Get current Chapter for an XML-RPC request.
 878   *
 879   * @param xmlrpcmsg XML-RPC Message
 880   * @param integer idx of chapter ID param
 881   * @return Chapter or NULL
 882   */
 883  function & xmlrpcs_get_Chapter( $m, $id_param )
 884  {
 885      global $xmlrpcs_errcode, $xmlrpcs_errmsg, $xmlrpcerruser;
 886  
 887      $id = $m->getParam( $id_param );
 888      $id = $id->scalarval();
 889  
 890      /**
 891       * @var ChapterCache
 892       */
 893      $ChapterCache = & get_ChapterCache();
 894      /**
 895       * @var Chapter
 896       */
 897      $edited_Chapter = & $ChapterCache->get_by_ID( $id, false, false );
 898  
 899      if( empty( $edited_Chapter ) )
 900      {    // Chapter not found
 901          $xmlrpcs_errcode = $xmlrpcerruser+9;
 902          $xmlrpcs_errmsg = 'Requested chapter ('.$id.') does not exist.';
 903          $r = NULL;
 904          return $r;
 905      }
 906  
 907      logIO( 'Requested Chapter: '.$edited_Chapter->ID.' - '.strmaxlen($edited_Chapter->name, 30) );
 908  
 909      return $edited_Chapter;
 910  }
 911  
 912  
 913  /**
 914   * Get current Comment for an XML-RPC request.
 915   *
 916   * @param xmlrpcmsg XML-RPC Message
 917   * @param integer idx of comment ID param
 918   * @return Comment or NULL
 919   */
 920  function & xmlrpcs_get_Comment( $m, $id_param )
 921  {
 922      global $xmlrpcs_errcode, $xmlrpcs_errmsg, $xmlrpcerruser;
 923  
 924      $id = $m->getParam( $id_param );
 925      $id = $id->scalarval();
 926  
 927      /**
 928       * @var CommentCache
 929       */
 930      $CommentCache = & get_CommentCache();
 931      /**
 932       * @var Comment
 933       */
 934      $edited_Comment = & $CommentCache->get_by_ID( $id, false, false );
 935  
 936      if( empty( $edited_Comment ) )
 937      {    // Comment not found
 938          $xmlrpcs_errcode = $xmlrpcerruser+9;
 939          $xmlrpcs_errmsg = 'Requested comment ('.$id.') does not exist.';
 940          $r = NULL;
 941          return $r;
 942      }
 943  
 944      logIO( 'Requested Comment: '.$edited_Comment->ID.' - '.strmaxlen($edited_Comment->content, 30) );
 945  
 946      return $edited_Comment;
 947  }
 948  
 949  
 950  /**
 951   * If no errcode or errmsg given, will use the last one that has been set previously.
 952   *
 953   * @param integer
 954   * @param string
 955   * @return xmlrpcresp
 956   */
 957  function xmlrpcs_resperror( $errcode = NULL, $errmsg = NULL )
 958  {
 959      global $xmlrpcs_errcode, $xmlrpcs_errmsg, $xmlrpcerruser;
 960  
 961      if( !empty($errcode) )
 962      { // Transform into user error code
 963          $xmlrpcs_errcode = $xmlrpcerruser + $errcode;
 964      }
 965  
 966      if( !empty($errmsg) )
 967      {    // Custom message
 968          $xmlrpcs_errmsg = $errmsg;
 969      }
 970      elseif( empty( $xmlrpcs_errmsg ) )
 971      {    // Use a standard messsage
 972          switch( $errcode )
 973          {
 974              case 3:
 975                  $xmlrpcs_errmsg = 'Permission denied.';
 976                  break;
 977  
 978              case 11:
 979                  $xmlrpcs_errmsg = 'Requested category not found in requested blog.';
 980                  break;
 981  
 982              case 12:
 983                  $xmlrpcs_errmsg = 'No default category found for requested blog.';
 984                  break;
 985  
 986              case 21:
 987                  $xmlrpcs_errmsg = 'Invalid post title.';
 988                  break;
 989  
 990              case 22:
 991                  $xmlrpcs_errmsg = 'Invalid post contents.';
 992                  break;
 993  
 994              case 99:
 995                  $xmlrpcs_errmsg = 'Database error.';
 996                  break;
 997  
 998              default:
 999                  $xmlrpcs_errmsg = 'Unknown error.';
1000          }
1001      }
1002  
1003      logIO( 'ERROR: '.$xmlrpcs_errcode.' - '.$xmlrpcs_errmsg );
1004  
1005    return new xmlrpcresp( 0, $xmlrpcs_errcode, $xmlrpcs_errmsg );
1006  }
1007  
1008  
1009  /**
1010   * Create a new Comment and return an XML-RPC response
1011   *
1012   * @param array of params
1013   *            - Item (object)
1014   *            - User (object) Can be NULL for anonymous comments
1015   *            - password (string)
1016   *            - username (string)
1017   *            - comment_parent (int)
1018   *            - content (string)
1019   *            - author (string)
1020   *            - author_url (string)
1021   *            - author_email (string)
1022   * @return xmlrpcmsg
1023   */
1024  function xmlrpcs_new_comment( $params = array(), & $commented_Item )
1025  {
1026      global $DB, $Plugins, $Messages, $Hit, $localtimenow, $require_name_email, $minimum_comment_interval;
1027  
1028      $params = array_merge( array(
1029              'password'            => '',
1030              'username'            => '',
1031              'content'            => '',
1032              'comment_parent'    => 0,
1033              'author'            => '',
1034              'author_url'        => '',
1035              'author_email'        => '',
1036          ), $params);
1037  
1038      $comment = $params['content'] = trim($params['content']);
1039  
1040      if( ! $commented_Item->can_comment(NULL) )
1041      {
1042          return xmlrpcs_resperror( 5, T_('You cannot leave comments on this post!') );
1043      }
1044  
1045      $commented_Item->load_Blog(); // Make sure Blog is loaded (will be needed whether logged in or not)
1046  
1047      if( empty($params['username']) && empty($params['password']) )
1048      {    // Anonymous comment
1049          // NO permission to edit!
1050          $perm_comment_edit = false;
1051          $User = NULL;
1052  
1053          $author = trim($params['author']);
1054          $email = trim($params['author_email']);
1055  
1056          if( $commented_Item->Blog->get_setting('allow_anon_url') )
1057          {
1058              $url = trim($params['author_url']);
1059          }
1060          else
1061          {
1062              $url = NULL;
1063          }
1064  
1065          // we need some id info from the anonymous user:
1066          if( $require_name_email )
1067          { // We want Name and EMail with comments
1068              if( empty($author) )
1069              {
1070                  return xmlrpcs_resperror( 5, T_('Please fill in your name.') );
1071              }
1072              if( empty($email) )
1073              {
1074                  return xmlrpcs_resperror( 5, T_('Please fill in your email.') );
1075              }
1076          }
1077  
1078          if( !empty($author) && antispam_check( $author ) )
1079          {
1080              return xmlrpcs_resperror( 5, T_('Supplied name is invalid.') );
1081          }
1082  
1083          if( !empty($email)
1084              && ( !is_email($email)|| antispam_check( $email ) ) )
1085          {
1086              return xmlrpcs_resperror( 5, T_('Supplied email address is invalid.') );
1087          }
1088  
1089  
1090          if( !stristr($url, '://') && !stristr($url, '@') )
1091          { // add 'http://' if no protocol defined for URL; but not if the user seems to be entering an email address alone
1092              $url = 'http://'.$url;
1093          }
1094  
1095          if( strlen($url) <= 8 )
1096          {    // ex: https:// is 8 chars
1097              $url = '';
1098          }
1099  
1100          // Note: as part of the validation we require the url to be absolute; otherwise we cannot detect bozos typing in
1101          // a title for their comment or whatever...
1102          if( $error = validate_url( $url, 'commenting' ) )
1103          {
1104              return xmlrpcs_resperror( 5, T_('Supplied website address is invalid: ').$error );
1105          }
1106      }
1107      else
1108      {
1109          $User = & $params['User'];
1110  
1111          $perm_comment_edit = $User->check_perm( 'blog_comment!published', 'edit', false, $commented_Item->Blog->ID );
1112  
1113          $author = $User->ID;
1114          $url = $User->url;
1115          $email = $User->email;
1116      }
1117  
1118      // Following call says "WARNING: this does *NOT* (necessarilly) make the HTML code safe.":
1119      $comment = check_html_sanity( $comment, $perm_comment_edit ? 'posting' : 'commenting', $User );
1120      if( $comment === false )
1121      {    // ERROR! Restore original comment for further editing:
1122          $comment = $params['content'];
1123      }
1124  
1125      if( empty($comment) )
1126      { // comment should not be empty!
1127          return xmlrpcs_resperror( 5, T_('Please do not send empty comments.') );
1128      }
1129  
1130      $now = date2mysql($localtimenow);
1131  
1132      /*
1133       * Flood-protection
1134       * NOTE: devs can override the flood protection delay in /conf/_overrides_TEST.php
1135       * TODO: Put time check into query?
1136       * TODO: move that as far !!UP!! as possible! We want to waste minimum resources on Floods
1137       * TODO: have several thresholds. For example:
1138       * 1 comment max every 30 sec + 5 comments max every 10 minutes + 15 comments max every 24 hours
1139       * TODO: factorize with trackback
1140       */
1141      $query = 'SELECT MAX(comment_date)
1142                  FROM T_comments
1143                  WHERE comment_author_IP = '.$DB->quote($Hit->IP).'
1144                  OR comment_author_email = '.$DB->quote($email);
1145      $ok = 1;
1146      if( $then = $DB->get_var( $query ) )
1147      {
1148          $time_lastcomment = mysql2date("U",$then);
1149          $time_newcomment = mysql2date("U",$now);
1150          if( ($time_newcomment - $time_lastcomment) < $minimum_comment_interval )
1151              $ok = 0;
1152      }
1153      if( !$ok )
1154      {
1155          return xmlrpcs_resperror( 5, sprintf( T_('You can only post a new comment every %d seconds.'), $minimum_comment_interval ) );
1156      }
1157      /* end flood-protection */
1158  
1159      /**
1160       * Create comment object. Gets validated, before recording it into DB:
1161       */
1162      $Comment = new Comment();
1163      $Comment->set( 'type', 'comment' );
1164      $Comment->set_Item( $commented_Item );
1165      if( $User )
1166      { // User is logged in, we'll use his ID
1167          $Comment->set_author_User( $User );
1168      }
1169      else
1170      {    // User is not logged in:
1171          $Comment->set( 'author', $author );
1172          $Comment->set( 'author_email', $email );
1173          $Comment->set( 'author_url', $url );
1174      }
1175      if( !empty($params['comment_parent']) )
1176      {
1177          $Comment->set( 'in_reply_to_cmt_ID', intval($params['comment_parent']) );
1178      }
1179      $Comment->set( 'author_IP', $Hit->IP );
1180      $Comment->set( 'date', $now );
1181      $Comment->set( 'content', $comment );
1182  
1183      if( $perm_comment_edit )
1184      {    // User has perm to moderate comments, publish automatically:
1185          $Comment->set( 'status', 'published' );
1186      }
1187      else
1188      { // Assign default status for new comments:
1189          $Comment->set( 'status', $commented_Item->Blog->get_setting('new_feedback_status') );
1190      }
1191  
1192      $action = 'submit_comment_post_'.$commented_Item->ID;
1193  
1194      // Trigger event: a Plugin could add a $category="error" message here..
1195      $Plugins->trigger_event('BeforeCommentFormInsert', array(
1196              'Comment' => & $Comment,
1197              'original_comment' => $params['content'],
1198              'is_preview' => false,
1199              'action' => & $action
1200          ) );
1201  
1202      if( $Messages->has_errors() )
1203      {
1204          return xmlrpcs_resperror( 5, $Messages->get_string( 'Cannot create comment, please correct these errors:'."\n", '', "  //  \n", 'xmlrpc' ) );
1205      }
1206  
1207      $Comment->dbinsert();
1208  
1209      if( $Comment->ID )
1210      {    // comment has not been deleted
1211          // Trigger event: a Plugin should cleanup any temporary data here..
1212          $Plugins->trigger_event( 'AfterCommentFormInsert', array( 'Comment' => & $Comment, 'original_comment' => $params['content'] ) );
1213  
1214          /*
1215           * --------------------------
1216           * New comment notifications:
1217           * --------------------------
1218           */
1219          // TODO: dh> this should only send published feedback probably and should also use "outbound_notifications_mode"
1220          // fp> yes for general users, but comment moderators need to receive notifications for new unpublished comments
1221          // asimo> this handle moderators and general users as well and use "outbound_notifications_mode" in case of general users
1222          // Moderators will get emails about every new comment
1223          // Subscribed user will only get emails about new published comments
1224          $executed_by_userid = empty( $User ) ? NULL : $User->ID;
1225          $Comment->handle_notifications( true, $executed_by_userid );
1226      }
1227      else
1228      {
1229          return xmlrpcs_resperror( 99, 'Error while inserting comment: '.$DB->last_error );
1230      }
1231  
1232      return new xmlrpcresp( new xmlrpcval($Comment->ID, 'int') );
1233  }
1234  
1235  
1236  /**
1237   * Edit a Comment and return an XML-RPC response
1238   *
1239   * @param array of params
1240   *            - status (string)
1241   *            - date_created_gmt (string)
1242   *            - content (string)
1243   *            - author (string)
1244   *            - author_url (string)
1245   *            - author_email (string)
1246   * @return xmlrpcmsg
1247   */
1248  function xmlrpcs_edit_comment( $params = array(), & $edited_Comment )
1249  {
1250      global $DB, $current_User, $Messages;
1251  
1252      $params = array_merge( array(
1253              'status'        => '',
1254              'date'            => '',
1255              'content'        => '',
1256              'author'        => '',
1257              'author_url'    => '',
1258              'author_email'    => '',
1259          ), $params);
1260  
1261      $comment = trim($params['content']);
1262  
1263      $edited_Comment_Item = $edited_Comment->get_Item();
1264      $edited_Comment_Item->load_Blog();
1265      $perm_comment_edit = $current_User->check_perm( 'blog_comment!published', 'edit', false, $edited_Comment_Item->Blog->ID );
1266  
1267      // CHECK HTML SANITY:
1268      // Following call says "WARNING: this does *NOT* (necessarilly) make the HTML code safe.":
1269      $comment = check_html_sanity( $comment, $perm_comment_edit ? 'posting' : 'commenting', $current_User );
1270      if( $comment === false )
1271      {    // ERROR! Restore original comment for further editing:
1272          $comment = trim($params['content']);
1273      }
1274  
1275      if( empty($comment) )
1276      { // comment should not be empty!
1277          return xmlrpcs_resperror( 5, T_('Please do not send empty comments.') );
1278      }
1279  
1280      // UPDATE COMMENT IN DB:
1281      $edited_Comment->set( 'content', $comment );
1282      $edited_Comment->set( 'status', $params['status'] );
1283      if( !empty($date) )
1284      {
1285          $edited_Comment->set( 'date', $date );
1286      }
1287      if( ! $edited_Comment->get_author_User() )
1288      { // If this is not a member comment
1289          $edited_Comment->set( 'author', $params['author'] );
1290          $edited_Comment->set( 'author_url', $params['author_url'] );
1291          $edited_Comment->set( 'author_email', $params['author_email'] );
1292      }
1293      $edited_Comment->dbupdate();
1294  
1295      if( $DB->error )
1296      { // DB error
1297          return xmlrpcs_resperror( 99, 'Error while updating comment: '.$DB->last_error );
1298      }
1299  
1300      // Execute or schedule notifications & pings:
1301      logIO( 'Handling notifications...' );
1302      $edited_Comment->handle_notifications( false, $current_User->ID );
1303  
1304      logIO( 'OK.' );
1305      return new xmlrpcresp( new xmlrpcval( true, 'boolean' ) );
1306  }
1307  
1308  
1309  /**
1310   * Create a new Item and return an XML-RPC response
1311   *
1312   * @param array Item properties
1313   * @param object Blog where we are going to create a new Item
1314   * @return xmlrpcmsg
1315   */
1316  function xmlrpcs_new_item( $params, & $Blog = NULL )
1317  {
1318      global $current_User, $Settings, $Messages, $DB, $posttypes_perms;
1319  
1320      $params = array_merge( array(
1321              'title'                => '',
1322              'content'            => '',
1323              'date'                => '',
1324              'main_cat_ID'        => 0,
1325              'extra_cat_IDs'        => array(),
1326              'cat_IDs'            => array(),    // we may use this to set main and extra cats
1327              'status'            => 'published',
1328              'tags'                => '',
1329              'excerpt'            => '',
1330              'item_typ_ID'        => 1,
1331              'comment_status'    => 'open',
1332              'urltitle'            => '',
1333              'featured'            => 0,
1334              'custom_fields'        => array(),
1335              'order'                => '',
1336              'parent_ID'            => '',
1337          ), $params );
1338  
1339      if( empty($Blog) && !empty($params['main_cat_ID']) )
1340      {    // Get the blog by main category ID
1341  
1342          // Check if category exists and can be used
1343          $ChapterCache = & get_ChapterCache();
1344          $main_Chapter = & $ChapterCache->get_by_ID( $params['main_cat_ID'], false, false );
1345          if( empty($main_Chapter) )
1346          {    // Cat does not exist:
1347              return xmlrpcs_resperror( 11 );    // User error 11
1348          }
1349  
1350          $BlogCache = & get_BlogCache();
1351          $Blog = & $BlogCache->get_by_ID( $main_Chapter->blog_ID, false, false );
1352  
1353          logIO( 'Requested Blog: '.$Blog->ID.' - '.$Blog->name );
1354      }
1355  
1356      if( empty($Blog) )
1357      {    // Blog does not exist:
1358          return xmlrpcs_resperror();
1359      }
1360  
1361      if( empty($params['main_cat_ID']) )
1362      {
1363          if( is_array($params['cat_IDs']) && count($params['cat_IDs']) > 0 )
1364          {    // Let's use first cat for MAIN and others for EXTRA
1365              $params['main_cat_ID'] = array_shift($params['cat_IDs']);
1366              $params['extra_cat_IDs'] = $params['cat_IDs'];
1367          }
1368          else
1369          {
1370              if( ! $main_cat = $Blog->get_default_cat_ID() )
1371              {    // No default category found for requested blog
1372                  return xmlrpcs_resperror( 12 ); // User error 12
1373              }
1374              $params['main_cat_ID'] = $main_cat;
1375          }
1376      }
1377      logIO( 'Main cat ID: '.$params['main_cat_ID'] );
1378      logIO( 'Extra cat IDs: '.implode( ', ', $params['extra_cat_IDs'] ) );
1379  
1380      if( empty($params['main_cat_ID']) )
1381      {    // Main category does not exist:
1382          return xmlrpcs_resperror( 11 );    // User error 11
1383      }
1384  
1385      // Check if category exists and can be used
1386      if( ! xmlrpcs_check_cats( $params['main_cat_ID'], $Blog, $params['extra_cat_IDs'] ) )
1387      {    // Permission denied
1388          return xmlrpcs_resperror( 3 );    // User error 3
1389      }
1390  
1391      /*
1392       * CHECK PERMISSION: (we need perm on all categories, especially if they are in different blogs)
1393       * NOTE: extra_cat_IDs array now includes main_cat_ID too, so we are actually checking ALL categories below
1394       */
1395      if( ! $current_User->check_perm( 'cats_post!'.$params['status'], 'edit', false, $params['extra_cat_IDs'] ) )
1396      {    // Permission denied
1397          return xmlrpcs_resperror( 3 );    // User error 3
1398      }
1399  
1400      if( !empty($params['item_typ_ID']) )
1401      {
1402          if( ! preg_match('~^[0-9]+$~', $params['item_typ_ID']) )
1403          {    // Only accept numeric values, switch to default value
1404              $params['item_typ_ID'] = 1;
1405          }
1406  
1407          foreach( $posttypes_perms as $l_permname => $l_posttypes )
1408          {    // "Reverse" the $posttypes_perms array:
1409              foreach( $l_posttypes as $ll_posttype )
1410              {
1411                  $posttype2perm[$ll_posttype] = $l_permname;
1412              }
1413          }
1414  
1415          if( isset( $posttype2perm[ $params['item_typ_ID'] ] ) )
1416          {    // Check permission for this post type
1417              if( ! $current_User->check_perm( 'cats_'.$posttype2perm[ $params['item_typ_ID'] ], 'edit', false, $params['extra_cat_IDs'] ) )
1418              {    // Permission denied
1419                  return xmlrpcs_resperror( 3 );    // User error 3
1420              }
1421          }
1422      }
1423      logIO( 'Post type: '.$params['item_typ_ID'] );
1424  
1425      logIO( 'Permission granted.' );
1426  
1427      // CHECK HTML SANITY:
1428      if( ($params['title'] = check_html_sanity( $params['title'], 'xmlrpc_posting' )) === false )
1429      {
1430          return xmlrpcs_resperror( 21, $Messages->get_string( 'Invalid post title, please correct these errors:', '' ) );
1431      }
1432      if( ($params['content'] = check_html_sanity( $params['content'], 'xmlrpc_posting' )) === false  )
1433      {
1434          return xmlrpcs_resperror( 22, $Messages->get_string( 'Invalid post contents, please correct these errors:'."\n", '', "  //  \n", 'xmlrpc' ) );
1435      }
1436  
1437      if( empty($params['date']) )
1438      {
1439          $params['date'] = date( 'Y-m-d H:i:s', (time() + $Settings->get('time_difference')) );
1440      }
1441  
1442      // INSERT NEW POST INTO DB:
1443      load_class( 'items/model/_item.class.php', 'Item' );
1444      $edited_Item = new Item();
1445      $edited_Item->set( 'title', $params['title'] );
1446      $edited_Item->set( 'content', $params['content'] );
1447      $edited_Item->set( 'issue_date', $params['date'] );
1448      $edited_Item->set( 'main_cat_ID', $params['main_cat_ID'] );
1449      $edited_Item->set( 'extra_cat_IDs', $params['extra_cat_IDs'] );
1450      $edited_Item->set( 'status', $params['status'] );
1451      $edited_Item->set( 'ptyp_ID', $params['item_typ_ID'] );
1452      $edited_Item->set( 'featured', $params['featured'] );
1453      $edited_Item->set_tags_from_string( $params['tags'] );
1454      $edited_Item->set( 'locale', $current_User->locale );
1455      $edited_Item->set_creator_User( $current_User );
1456  
1457      if( $params['excerpt'] != '' ) $edited_Item->set( 'excerpt', $params['excerpt'] );
1458      if( $params['urltitle'] != '' ) $edited_Item->set( 'urltitle', $params['urltitle'] );
1459      if( $params['parent_ID'] != '' ) $edited_Item->set( 'parent_ID', $params['parent_ID'] );
1460      if( !empty($params['order']) ) $edited_Item->set( 'order', $params['order'] ); // Do not set if order is 0
1461  
1462      if( ($Blog->get_setting('allow_comments') != 'never') && ($Blog->get_setting('disable_comments_bypost')) )
1463      {    // Comment status
1464          $edited_Item->set( 'comment_status', $params['comment_status'] );
1465      }
1466  
1467      $edited_Item->dbinsert( 'through_xmlrpc' );
1468      if( empty($edited_Item->ID) )
1469      {
1470          return xmlrpcs_resperror( 99, 'Error while inserting item: '.$DB->last_error );
1471      }
1472      logIO( 'Posted with ID: '.$edited_Item->ID );
1473  
1474      if( !empty($params['custom_fields']) && is_array($params['custom_fields']) && count($params['custom_fields']) > 0 )
1475      {    // TODO sam2kb> Add custom fields
1476          foreach( $params['custom_fields'] as $field )
1477          {    // id, key, value
1478              logIO( 'Custom field: '.var_export($field, true) );
1479          }
1480      }
1481  
1482      // Execute or schedule notifications & pings:
1483      logIO( 'Handling notifications...' );
1484      $edited_Item->handle_post_processing( true );
1485  
1486       logIO( 'OK.' );
1487      return new xmlrpcresp(new xmlrpcval($edited_Item->ID));
1488  }
1489  
1490  
1491  /**
1492   * Edit an Item and return an XML-RPC response
1493   *
1494   * @param Item
1495   * @param array Item properties
1496   * @param object Blog where we are going to create a new Item
1497   * @return xmlrpcmsg
1498   */
1499  function xmlrpcs_edit_item( & $edited_Item, $params )
1500  {
1501      global $current_User, $Messages, $DB, $posttypes_perms;
1502  
1503      $params = array_merge( array(
1504              'title'                => NULL,
1505              'content'            => NULL,
1506              'date'                => '',
1507              'main_cat_ID'        => NULL,
1508              'extra_cat_IDs'        => NULL,
1509              'cat_IDs'            => array(),    // we may use this to set main and extra cats
1510              'status'            => '',
1511              'tags'                => NULL,
1512              'excerpt'            => NULL,
1513              'item_typ_ID'        => NULL,
1514              'comment_status'    => '',
1515              'urltitle'            => NULL,
1516              'featured'            => NULL,
1517              'custom_fields'        => NULL,
1518              'order'                => NULL,
1519              'parent_ID'            => NULL,
1520              'author_ID'            => NULL,
1521              'locale'            => '',
1522          ), $params );
1523  
1524      $Blog = & $edited_Item->get_Blog();
1525      logIO( 'Requested Blog: '.$Blog->ID.' - '.$Blog->name );
1526  
1527      if( empty($Blog) )
1528      {    // Blog does not exist:
1529          return xmlrpcs_resperror();
1530      }
1531  
1532      if( is_array($params['cat_IDs']) && count($params['cat_IDs']) > 0 )
1533      {    // Let's use first cat for MAIN and others for EXTRA
1534          $params['main_cat_ID'] = array_shift($params['cat_IDs']);
1535          $params['extra_cat_IDs'] = $params['cat_IDs'];
1536      }
1537  
1538      if( !is_null($params['main_cat_ID']) && is_array($params['extra_cat_IDs']) )
1539      {    // Check new categories
1540          logIO( 'Main cat ID: '.$params['main_cat_ID'] );
1541          logIO( 'Extra cat IDs: '.implode( ', ', $params['extra_cat_IDs'] ) );
1542  
1543          // Check if category exists and can be used
1544          if( ! xmlrpcs_check_cats( $params['main_cat_ID'], $Blog, $params['extra_cat_IDs'] ) )
1545          {    // Permission denied
1546              return xmlrpcs_resperror( 3 );    // User error 3
1547          }
1548  
1549          /*
1550           * CHECK PERMISSION: (we need perm on all categories, especially if they are in different blogs)
1551           * NOTE: extra_cat_IDs array now includes main_cat_ID too, so we are actually checking ALL categories below
1552           */
1553          if( ! $current_User->check_perm( 'cats_post!'.$params['status'], 'edit', false, $params['extra_cat_IDs'] ) )
1554          {
1555          }
1556      }
1557  
1558      if( !is_null($params['item_typ_ID']) )
1559      {
1560          if( ! preg_match('~^[0-9]+$~', $params['item_typ_ID']) )
1561          {    // Only accept numeric values, switch to default value
1562              $params['item_typ_ID'] = NULL;
1563          }
1564  
1565          foreach( $posttypes_perms as $l_permname => $l_posttypes )
1566          {    // "Reverse" the $posttypes_perms array:
1567              foreach( $l_posttypes as $ll_posttype )
1568              {
1569                  $posttype2perm[$ll_posttype] = $l_permname;
1570              }
1571          }
1572  
1573          if( isset( $posttype2perm[ $params['item_typ_ID'] ] ) )
1574          {    // Check permission for this post type
1575              if( ! $current_User->check_perm( 'cats_'.$posttype2perm[ $params['item_typ_ID'] ], 'edit', false, $params['extra_cat_IDs'] ) )
1576              {    // Permission denied
1577                  return xmlrpcs_resperror( 3 );    // User error 3
1578              }
1579          }
1580      }
1581      logIO( 'Post type: '.$params['item_typ_ID'] );
1582  
1583      logIO( 'Permission granted.' );
1584  
1585      // CHECK HTML SANITY:
1586      if( ($params['title'] = check_html_sanity( $params['title'], 'xmlrpc_posting' )) === false )
1587      {
1588          return xmlrpcs_resperror( 21, $Messages->get_string( 'Invalid post title, please correct these errors:', '' ) );
1589      }
1590      if( ($params['content'] = check_html_sanity( $params['content'], 'xmlrpc_posting' )) === false  )
1591      {
1592          return xmlrpcs_resperror( 22, $Messages->get_string( 'Invalid post contents, please correct these errors:'."\n", '', "  //  \n", 'xmlrpc' ) );
1593      }
1594  
1595      if( !is_null($params['title']) )
1596      {
1597          $edited_Item->set( 'title', $params['title'] );
1598      }
1599      if( !is_null($params['content']) )
1600      {
1601          $edited_Item->set( 'content', $params['content'] );
1602      }
1603      if( !is_null($params['urltitle']) )
1604      {
1605          $edited_Item->set( 'urltitle', $params['urltitle'] );
1606      }
1607      if( !is_null($params['main_cat_ID']) && !is_null($params['extra_cat_IDs']) )
1608      {
1609          $edited_Item->set('main_cat_ID', $params['main_cat_ID']);
1610          $edited_Item->set('extra_cat_IDs', $params['extra_cat_IDs']);
1611      }
1612      if( !is_null($params['item_typ_ID']) )
1613      {
1614          $edited_Item->set('ptyp_ID', $params['item_typ_ID']);
1615      }
1616      if( !is_null($params['featured']) )
1617      {
1618          $edited_Item->set('featured', $params['featured']);
1619      }
1620      if( !is_null($params['order']) )
1621      {
1622          if( ! (empty($params['order']) && ! $edited_Item->order) )
1623          {    // Do not allow 0 order if there was no order set before
1624              $edited_Item->set('order', $params['order']);
1625          }
1626      }
1627      if( !is_null($params['parent_ID']) )
1628      {
1629          $edited_Item->set('parent_ID', $params['parent_ID']);
1630      }
1631      if( !is_null($params['author_ID']) && $params['author_ID'] != $this->creator_user_ID )
1632      {    // We have already checked perms to edit items created by other users
1633          $edited_Item->set( 'lastedit_user_ID', $params['parent_ID']);
1634      }
1635      if( !is_null($params['tags']) )
1636      {
1637          $edited_Item->set_tags_from_string( $params['tags'] );
1638      }
1639      if( !is_null($params['excerpt']) )
1640      {
1641          $edited_Item->set( 'excerpt', $params['excerpt'] );
1642      }
1643      if( !empty($params['comment_status']) && ($Blog->get_setting('allow_comments') != 'never') && ($Blog->get_setting('disable_comments_bypost')) )
1644      {    // Comment status
1645          $edited_Item->set( 'comment_status', $params['comment_status'] );
1646      }
1647      if( !empty($params['status']) )
1648      {
1649          $edited_Item->set( 'status', $params['status'] );
1650      }
1651      if( !empty($params['date']) )
1652      {
1653          $edited_Item->set( 'issue_date', $params['date'] );
1654      }
1655      if( !empty($params['locale']) )
1656      {
1657          $edited_Item->set('locale', $params['locale']);
1658      }
1659  
1660      logIO( var_export($edited_Item->dbchanges, true) );
1661  
1662      // UPDATE POST IN DB:
1663      $edited_Item->dbupdate();
1664  
1665      if( $DB->error )
1666      {
1667          return xmlrpcs_resperror( 99, 'Error while updating item: '.$DB->last_error );
1668      }
1669  
1670      if( !is_null($params['custom_fields']) )
1671      {    // TODO sam2kb> Add custom fields
1672          if( is_array($params['custom_fields']) && count($params['custom_fields']) > 0 )
1673          {
1674              logIO( 'Modifying custom fields...' );
1675              foreach( $params['custom_fields'] as $field )
1676              {    // id, key, value
1677                  logIO( 'Custom field: '.var_export($field, true) );
1678              }
1679          }
1680          else
1681          {
1682              logIO( 'Deleting custom fields...' );
1683          }
1684      }
1685  
1686      // Execute or schedule notifications & pings:
1687      logIO( 'Handling notifications...' );
1688      $edited_Item->handle_post_processing( false );
1689  
1690      logIO( 'OK.' );
1691      return new xmlrpcresp( new xmlrpcval( 1, 'boolean' ) );
1692  }
1693  
1694  
1695  /**
1696   * Check that an User can view a specific Item.
1697   *
1698   * @param object The Item (by reference).
1699   * @param object The User (by reference).
1700   * @return boolean True if permission granted, false otherwise.
1701   */
1702  function xmlrpcs_can_view_item( & $Item, & $current_User )
1703  {
1704      $can_view_post = false;
1705      switch( $Item->status )
1706      {
1707          case 'published':
1708          case 'redirected':
1709              $can_view_post = true;
1710              break;
1711          case 'protected':
1712          case 'draft':
1713          case 'deprecated':
1714              $can_view_post = $current_User->check_perm( 'blog_ismember', 'view', false, $Item->get_blog_ID() );
1715              break;
1716          case 'private':
1717              $can_view_post = ( $Item->creator_user_ID == $current_User->ID );
1718              break;
1719      }
1720  
1721      logIO( 'xmlrpcs_can_view_item(): Post status: '.$Item->status );
1722      logIO( 'xmlrpcs_can_view_item( Item(#'.$Item->ID.'), User(#'.$current_User->ID.') ): Permission '.( $can_view_post ? 'granted' : 'DENIED' ) );
1723      return $can_view_post;
1724  }
1725  
1726  
1727  /**
1728   * Check whether the main category and the extra categories are valid
1729   * in a Blog's context and try to fix errors.
1730   *
1731   * @author Tilman BLUMENBACH / Tblue
1732   *
1733   * @param integer The main category to check (by reference).
1734   * @param object The Blog to which the category is supposed to belong to (by reference).
1735   * @param array Extra categories for the post (by reference).
1736   *
1737   * @return boolean False on error (use xmlrpcs_resperror() to return it), true on success.
1738   */
1739  function xmlrpcs_check_cats( & $maincat, & $Blog, & $extracats )
1740  {
1741      global $xmlrpcs_errcode, $xmlrpcs_errmsg, $xmlrpcerruser;
1742  
1743      // Trim $maincat and $extracats (qtm sends whitespace before the cat IDs):
1744      $maincat   = trim( $maincat );
1745      $extracats = array_map( 'trim', $extracats );
1746  
1747      $ChapterCache = & get_ChapterCache();
1748  
1749      // ---- CHECK MAIN CATEGORY ----
1750      if( $ChapterCache->get_by_ID( $maincat, false ) === false )
1751      {    // Category does not exist!
1752          // Remove old category from extra cats:
1753          if( ( $key = array_search( $maincat, $extracats ) ) !== false )
1754          {
1755              unset( $extracats[$key] );
1756          }
1757  
1758          // Set new category (blog default):
1759          $maincat = $Blog->get_default_cat_ID();
1760          logIO( 'Invalid main cat ID - new ID: '.$maincat );
1761      }
1762      else if( get_allow_cross_posting() < 2 && get_catblog( $maincat ) != $Blog->ID )
1763      {    // We cannot use a maincat of another blog than the current one:
1764          $xmlrpcs_errcode = $xmlrpcerruser + 11;
1765          $xmlrpcs_errmsg = 'Current crossposting setting does not allow moving posts to a different blog.';
1766          return false;
1767      }
1768  
1769      // ---- CHECK EXTRA CATEGORIES ----
1770      foreach( $extracats as $ecat )
1771      {
1772          if( $ecat == $maincat )
1773          {    // We already checked the maincat above (or reset it):
1774              continue;
1775          }
1776  
1777          logIO( 'Checking extra cat: '.$ecat );
1778          if( $ChapterCache->get_by_ID( $ecat, false ) === false )
1779          {    // Extra cat does not exist:
1780              $xmlrpcs_errcode = $xmlrpcerruser + 11;
1781              $xmlrpcs_errmsg  = 'Extra category '.(int)$ecat.' not found in requested blog.';
1782              return false;
1783          }
1784      }
1785  
1786      if( ! in_array( $maincat, $extracats ) )
1787      {
1788          logIO( '$maincat was not found in $extracats array - adding.' );
1789          $extracats[] = $maincat;
1790      }
1791  
1792      return true;
1793  }
1794  
1795  
1796  /**
1797   * Get array of latest items
1798   *
1799   * @param array of params
1800   *            - limit (int) the number of items to return
1801   *            - post_ID (int) return specified item or NULL to return all available
1802   * @return xmlrpcmsg
1803   */
1804  function xmlrpc_get_items( $params, & $Blog )
1805  {
1806      global $current_User;
1807  
1808      $params = array_merge( array(
1809              'limit' => 0,
1810              'item_ID' => 0,
1811              'types' => '', // all item types
1812          ), $params);
1813  
1814      // Protected and private get checked by statuses_where_clause().
1815      $statuses = array( 'published', 'redirected', 'protected', 'private' );
1816      if( $current_User->check_perm( 'blog_ismember', 'view', false, $Blog->ID ) )
1817      {    // These statuses require member status:
1818          $statuses = array_merge( $statuses, array( 'draft', 'deprecated' ) );
1819      }
1820      logIO( 'Statuses: '.implode(', ', $statuses) );
1821  
1822      if( !empty($params['item_ID']) )
1823      {
1824          logIO('Getting item #'.$params['item_ID']);
1825  
1826          $filters = array(
1827                  'visibility_array'    => $statuses,
1828                  'types'                => NULL,
1829                  'post_ID'            => $params['item_ID'],
1830              );
1831      }
1832      else
1833      {
1834          logIO( sprintf('Trying to get latest items (%s)', ($params['limit'] ? $params['limit'] : 'all')) );
1835  
1836          $filters = array(
1837                  'visibility_array'    => $statuses,
1838                  'types'                => $params['types'],
1839                  'order'                => 'DESC',
1840                  'unit'                => 'posts',
1841              );
1842      }
1843  
1844      // Get the pages to display:
1845      load_class( 'items/model/_itemlist.class.php', 'ItemList2' );
1846      $ItemList = new ItemList2( $Blog, NULL, NULL, $params['limit'] );
1847  
1848      $ItemList->set_filters( $filters, false );
1849  
1850      // Run the query:
1851      $ItemList->query();
1852  
1853      logIO( 'Items found: '.$ItemList->result_num_rows );
1854  
1855      $data = array();
1856      while( $Item = & $ItemList->get_item() )
1857      {
1858          $data[] = _wp_mw_get_item_struct( $Item );
1859      }
1860  
1861      return $data;
1862  }
1863  
1864  
1865  /**
1866   * Get array of latest comments
1867   *
1868   * @param array of params
1869   *            - Blog (object)
1870   *            - User (object)
1871   *            - limit (int) the number of comments to return
1872   *            - comment_ID (int) return specified comment or NULL to return all available
1873   *            - item_ID (int) return comments for specified item only
1874   * @return xmlrpcmsg
1875   */
1876  function xmlrpc_get_comments( $params, & $Blog )
1877  {
1878      global $DB, $current_User;
1879  
1880      $params = array_merge( array(
1881              'limit'            => 0,
1882              'comment_ID'    => 0,
1883              'item_ID'        => 0,
1884              'statuses'        => '',
1885              'types'            => array('comment', 'trackback', 'pingback'),
1886          ), $params );
1887  
1888      $params['comment_ID'] = abs( intval($params['comment_ID']) );
1889      $params['item_ID'] = abs( intval($params['item_ID']) );
1890  
1891      if( empty($params['statuses']) )
1892      {    // Return all except 'trash'
1893          $params['statuses'] = array('published', 'deprecated', 'draft');
1894      }
1895  
1896      if( !empty($params['comment_ID']) )
1897      {
1898          logIO('Getting comment #'.$params['comment_ID']);
1899  
1900          $filters = array(
1901                  'comment_ID'=> $params['comment_ID'],
1902                  'types'        => $params['types'],
1903                  'statuses'    => $params['statuses'],
1904              );
1905      }
1906      elseif( !empty($params['item_ID']) )
1907      {
1908          logIO('Getting comments to item #'.$params['item_ID']);
1909  
1910          $ItemCache = & get_ItemCache();
1911          $Item = & $ItemCache->get_by_ID( $params['item_ID'], false, false );
1912  
1913          if( empty( $Item ) )
1914          {    // Item not found
1915              return xmlrpcs_resperror( 5, 'Requested post/Item ('.$params['item_ID'].') does not exist.' );
1916          }
1917  
1918          if( ! $Item->can_see_comments() )
1919          {    // Cannot see comments
1920              return xmlrpcs_resperror( 5, 'You are not allowed to view comments for this post/Item ('.$params['item_ID'].').' );
1921          }
1922  
1923          $filters = array(
1924                  'post_ID'    => $Item->ID,
1925                  'types'        => $params['types'],
1926                  'statuses'    => $params['statuses'],
1927                  'comments'    => $params['limit'],
1928                  'order'        => 'DESC',
1929              );
1930      }
1931      else
1932      {
1933          logIO( sprintf('Trying to get latest comments (%s)', ($params['limit'] ? $params['limit'] : 'all')) );
1934  
1935          $filters = array(
1936                  'types'        => $params['types'],
1937                  'statuses'    => $params['statuses'],
1938                  'comments'    => $params['limit'],
1939                  'order'        => 'DESC',
1940              );
1941      }
1942  
1943      //logIO( "Filters:\n".var_export($filters, true) );
1944  
1945      $CommentList = new CommentList2( $Blog );
1946  
1947      // Filter list:
1948      $CommentList->set_filters( $filters, false );
1949  
1950      // Get ready for display (runs the query):
1951      $CommentList->display_init();
1952  
1953      logIO( 'Comments found: '.$CommentList->result_num_rows );
1954  
1955      $data = array();
1956      if( $CommentList->result_num_rows )
1957      {
1958          while( $Comment = & $CommentList->get_next() )
1959          { // Loop through comments:
1960              $Comment->get_Item();
1961  
1962              $data[] = array(
1963                      'dateCreated'        => new xmlrpcval( datetime_to_iso8601($Comment->date, true), 'dateTime.iso8601' ), // Force GMT date
1964                      'date_created_gmt'    => new xmlrpcval( datetime_to_iso8601($Comment->date, true), 'dateTime.iso8601' ),
1965                      'user_id'            => new xmlrpcval( intval($Comment->author_user_ID) ),
1966                      'comment_id'        => new xmlrpcval($Comment->ID),
1967                      'parent'            => new xmlrpcval( intval($Comment->in_reply_to_cmt_ID) ),
1968                      'status'            => new xmlrpcval( wp_or_b2evo_comment_status($Comment->status, 'wp') ),
1969                      'content'            => new xmlrpcval($Comment->content),
1970                      'link'                => new xmlrpcval($Comment->get_permanent_url()),
1971                      'post_id'            => new xmlrpcval($Comment->Item->ID),
1972                      'post_title'        => new xmlrpcval($Comment->Item->title),
1973                      'author'            => new xmlrpcval($Comment->get_author_name()),
1974                      'author_url'        => new xmlrpcval($Comment->get_author_url()),
1975                      'author_email'        => new xmlrpcval($Comment->get_author_email()),
1976                      'author_ip'            => new xmlrpcval($Comment->author_IP),
1977                      'type'                => new xmlrpcval( (($Comment->type == 'comment') ? '' : $Comment->type) ) // empty string for 'comment'
1978                  );
1979          }
1980      }
1981  
1982      return $data;
1983  }
1984  
1985  
1986  /**
1987   * Deletes given Item
1988   *
1989   * @return xmlrpcresp XML-RPC Response (bool)
1990   */
1991  function xmlrpcs_delete_item( & $edited_Item )
1992  {
1993      global $current_User, $DB;
1994  
1995      // CHECK PERMISSION:
1996      if( ! $current_User->check_perm( 'item_post!CURSTATUS', 'delete', false, $edited_Item ) )
1997      {    // Permission denied
1998          return xmlrpcs_resperror( 3 );    // User error 3
1999      }
2000      logIO( 'Permission granted.' );
2001  
2002      // DELETE POST FROM DB:
2003      $edited_Item->dbdelete();
2004      if( $DB->error )
2005      {
2006          return xmlrpcs_resperror( 99, 'DB error: '.$DB->last_error ); // user error 9
2007      }
2008  
2009      logIO( 'OK.' );
2010      return new xmlrpcresp(new xmlrpcval(1, 'boolean'));
2011  }
2012  
2013  
2014  function wp_or_b2evo_comment_status( $raw_status, $convert_to = 'b2evo' )
2015  {
2016      $status = '';
2017  
2018      if( $convert_to == 'b2evo' )
2019      {
2020          switch( $raw_status )
2021          {    // Map WP statuses to b2evo
2022  
2023              // Keep native b2evo statuses
2024              case 'published':
2025              case 'deprecated':
2026              case 'draft':
2027              case 'trash':
2028                  $status = $raw_status;
2029                  break;
2030  
2031              case 'hold':
2032                  $status = 'draft';
2033                  break;
2034  
2035              case 'spam':
2036                  $status = 'deprecated';
2037                  break;
2038  
2039              case 'approve':
2040                  $status = 'published';
2041                  break;
2042  
2043              default:
2044                  $status = NULL;
2045          }
2046      }
2047      elseif( $convert_to == 'wp' )
2048      {
2049          switch( $raw_status )
2050          {    // Map b2evo statuses to WP
2051              case 'deprecated':
2052                  $status = 'spam';
2053                  break;
2054  
2055              case 'draft':
2056                  $status = 'hold';
2057                  break;
2058  
2059              case 'trash':
2060                  $status = 'trash';
2061                  break;
2062  
2063              default:
2064                  $status = 'approve';
2065                  break;
2066          }
2067      }
2068  
2069      return $status;
2070  }
2071  
2072  
2073  function wp_or_b2evo_item_status( $raw_status, $convert_to = 'b2evo' )
2074  {
2075      $status = '';
2076  
2077      if( $convert_to == 'b2evo' )
2078      {
2079          switch( $raw_status )
2080          {    // Map WP statuses to b2evo
2081              // Note: we drop 'inherit' status because b2evo doesn't support it
2082  
2083              // Keep native b2evo statuses
2084              case 'published':
2085              case 'deprecated':
2086              case 'protected':
2087              case 'private':
2088              case 'draft':
2089              case 'redirected':
2090                  $status = $raw_status;
2091                  break;
2092  
2093              case 'auto-draft':
2094              case 'pending':
2095                  $status = 'draft';
2096                  break;
2097  
2098              case 'publish':
2099              case 'future':
2100                  $status = 'published';
2101                  break;
2102  
2103              case 'trash':
2104                  $status = 'deprecated';
2105                  break;
2106          }
2107      }
2108      elseif( $convert_to == 'wp' )
2109      {
2110          switch( $raw_status )
2111          {    // Map b2evo statuses to WP
2112              case 'private':
2113              case 'draft':
2114                  $status = $raw_status;
2115                  break;
2116  
2117              case 'deprecated':
2118                  $status = 'trash';
2119                  break;
2120  
2121              case 'protected':
2122                  $status = 'private';
2123                  break;
2124  
2125              case 'published':
2126                  $status = 'publish';
2127                  break;
2128  
2129              case 'redirected':
2130                  $status = 'published';
2131                  break;
2132  
2133              default:
2134                  $status = 'approve';
2135                  break;
2136          }
2137      }
2138  
2139      return $status;
2140  }
2141  
2142  ?>

title

Description

title

Description

title

Description

title

title

Body