PageRenderTime 39ms CodeModel.GetById 11ms RepoModel.GetById 1ms app.codeStats 0ms

/comments/post.php

https://github.com/agnesrambaud/yacs
PHP | 315 lines | 137 code | 53 blank | 125 comment | 59 complexity | 947bdb24517059d01b4e39d0e939c488 MD5 | raw file
  1. <?php
  2. /**
  3. * post a comment
  4. *
  5. * This script serves as a back-end for the Comment API and for Post-It notifications.
  6. *
  7. * Using the Comment API, bloggers can easily submit comments from within their preferred RSS reader.
  8. *
  9. * @link http://wellformedweb.org/story/9 The Comment API
  10. * @link http://wellformedweb.org/news/wfw_namespace_elements/ wfw namespace elements
  11. *
  12. * YACS attempts to bind author information to an existing user profile.
  13. *
  14. * If anonymous comments are allowed, YACS uses the value of the author field as the id of a user profile.
  15. * If a password is provided, YACS validates it as well.
  16. *
  17. * To achieve this YACS expects to get credentials in the user name, in the usual form [code]&lt;nick_name&gt;:&lt;password&gt;[/code].
  18. *
  19. * For example, for the user [code]foo[/code] and the password [code]a_password[/code], configure your newsreader
  20. * to send '[code]foo:a_password[/code]' as user name.
  21. *
  22. * YACS allows for several strategies to protect from spam, depending of the settings of global parameters:
  23. * - if no user name has been provided, and if [code]$context['users_with_anonymous_comments'] == 'Y'[/code],
  24. * the comment is accepted
  25. * - if no password has been provided, and if [code]$context['users_with_anonymous_comments'] == 'Y'[/code],
  26. * the comment is accepted
  27. * - if user name and password are those of a valid user profile,
  28. * the comment is accepted
  29. * - else the comment is rejected
  30. *
  31. * @see control/configure.php
  32. *
  33. * Here is an example of Comment API, as submit from [link=RSS Bandit]http://www.rssbandit.org/[/link].
  34. * Note that line breaks have been inserted for readability.
  35. *
  36. * [snippet]
  37. * POST /yacs/comments/post.php/123 HTTP/1.1
  38. * Content-Type: text/xml
  39. *
  40. * <?xml version="1.0" encoding="iso-8859-15"?>
  41. * <item>
  42. * <title>RE: How to create a button from an image? </title>
  43. * <link>http://www.yacs.fr/</link>
  44. * <pubDate>Mon, 04 Oct 2004 12:21:52 GMT</pubDate>
  45. * <description><![CDATA[hello world ]]></description>
  46. * <author>foo.bar@acme.com (Foo)</author>
  47. * <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">foo.bar@acme.com (Foo)</dc:creator>
  48. * </item>
  49. * [/snippet]
  50. *
  51. * Here is an example of Post-It. Note that line breaks have been inserted for readability.
  52. *
  53. * [snippet]
  54. * POST http://www.foo.com/yacs/comments/post.php/123
  55. * Content-Type: application/x-www-form-urlencoded
  56. *
  57. * comment=My+Comment+Comes+Here
  58. * &email=joe@bitworking.org
  59. * &name=Foo+Bar
  60. * &url=http://www.bar.com/
  61. * &agent=send-cb.pl+(Version+0.1)
  62. * [/snippet]
  63. *
  64. * Accepted calls:
  65. * - post.php/&lt;type&gt;/&lt;id&gt; create a new comment for this anchor
  66. * - post.php?anchor=&lt;type&gt;:&lt;id&gt; create a new comment for this anchor
  67. * - post.php/&lt;id&gt; create a new comment for the anchor 'article:&lt;id&gt;'
  68. *
  69. * If the anchor for this item specifies a specific skin (option keyword '[code]skin_xyz[/code]'),
  70. * or a specific variant (option keyword '[code]variant_xyz[/code]'), they are used instead default values.
  71. *
  72. * @author Bernard Paques
  73. * @author GnapZ
  74. * @reference
  75. * @license http://www.gnu.org/copyleft/lesser.txt GNU Lesser General Public License
  76. */
  77. // common definitions and initial processing
  78. include_once '../shared/global.php';
  79. include_once 'comments.php';
  80. // get input data
  81. $raw_data = file_get_contents("php://input");
  82. // save the request if debug mode
  83. if($raw_data && isset($context['debug_comment']) && ($context['debug_comment'] == 'Y'))
  84. Logger::remember('comments/post.php', 'comments post request', $raw_data, 'debug');
  85. // transcode to our internal charset
  86. if($context['charset'] == 'utf-8')
  87. $raw_data = utf8::encode($raw_data);
  88. // look for the anchor reference
  89. $anchor = NULL;
  90. if(isset($_REQUEST['anchor']))
  91. $anchor = $_REQUEST['anchor'];
  92. elseif(isset($context['arguments'][1]))
  93. $anchor = $context['arguments'][0].':'.$context['arguments'][1];
  94. elseif(isset($context['arguments'][0]))
  95. $anchor = 'article:'.$context['arguments'][0];
  96. $anchor = strip_tags($anchor);
  97. // get the related anchor, if any
  98. if($anchor)
  99. $anchor =& Anchors::get($anchor);
  100. // a straightforward implementation of the Comment API
  101. if(isset($_SERVER['CONTENT_TYPE']) && ($_SERVER['CONTENT_TYPE'] == 'text/xml')) {
  102. // description -- escaped or not
  103. $comment = '';
  104. if(preg_match('/<description><!\[CDATA\[(.+)\]\]><\/description>/is', $raw_data, $matches))
  105. $comment = $matches[1];
  106. elseif(preg_match('/<description>(.+)<\/description>/is', $raw_data, $matches))
  107. $comment = $matches[1];
  108. // creator
  109. $name = '';
  110. if(preg_match('/<creator[^>]*>(.+)<\/creator>/is', $raw_data, $matches))
  111. $name = $matches[1];
  112. // dc:creator
  113. elseif(preg_match('/<dc:creator[^>]*>(.+)<\/dc:creator>/is', $raw_data, $matches))
  114. $name = $matches[1];
  115. // title
  116. if(preg_match('/<title>(.+)<\/title>/is', $raw_data, $matches)) {
  117. // only if we are not repeating page title again and again
  118. if($anchor) {
  119. if(!preg_match('/'.preg_quote($anchor->get_title(), '/').'/i', $matches[1]))
  120. $comment = '[b]'.$matches[1].'[/b] '.$comment;
  121. } else
  122. $comment = '[b]'.$matches[1].'[/b] '.$comment;
  123. }
  124. // link
  125. $source = '';
  126. if(preg_match('/<link>(.+)<\/link>/is', $raw_data, $matches))
  127. $source = $matches[1];
  128. // author
  129. if(!$source)
  130. if(preg_match('/<author>(.+)<\/author>/is', $raw_data, $matches))
  131. $source = $matches[1];
  132. // the Post-It API
  133. } elseif(isset($_SERVER['CONTENT_TYPE']) && ($_SERVER['CONTENT_TYPE'] == 'application/x-www-form-urlencoded')) {
  134. // the comment
  135. if(isset($_REQUEST['comment']))
  136. $comment = strip_tags($_REQUEST['comment']);
  137. // poster name
  138. if(isset($_REQUEST['name']))
  139. $name = strip_tags($_REQUEST['name']);
  140. // poster web address
  141. if(isset($_REQUEST['url']))
  142. $source = strip_tags($_REQUEST['url']);
  143. // or poster email address
  144. elseif(isset($_REQUEST['email']))
  145. $source = strip_tags($_REQUEST['email']);
  146. }
  147. // load the skin, maybe with a variant
  148. load_skin('comments', $anchor);
  149. // clear the tab we are in, if any
  150. if(is_object($anchor))
  151. $context['current_focus'] = $anchor->get_focus();
  152. // the path to this page
  153. if(is_object($anchor))
  154. $context['path_bar'] = $anchor->get_path_bar();
  155. else
  156. $context['path_bar'] = array( 'comments/' => i18n::s('Comments') );
  157. // the title of the page
  158. $context['page_title'] = i18n::s('Comment service');
  159. // stop crawlers
  160. if(Surfer::is_crawler()) {
  161. Safe::header('Status: 401 Forbidden', TRUE, 401);
  162. Logger::error(i18n::s('You are not allowed to perform this operation.'));
  163. // process uploaded data
  164. } elseif(isset($_SERVER['REQUEST_METHOD']) && ($_SERVER['REQUEST_METHOD'] == 'POST')) {
  165. // extract nick name if mailbox@server (name)
  166. if(preg_match('/\((.+)\)/', $name, $matches))
  167. $name = $matches[1];
  168. // split name and password, if any
  169. $password = '';
  170. if(preg_match('/^(.+):(.+)$/', $name, $matches)) {
  171. $name = $matches[1];
  172. $password = $matches[2];
  173. }
  174. // make things explicit
  175. if(!$name)
  176. $name = 'anonymous';
  177. // anonymous comments are not allowed
  178. if(($name == 'anonymous') && (!isset($context['users_with_anonymous_comments']) || ($context['users_with_anonymous_comments'] != 'Y')))
  179. $response = array('faultCode' => 49, 'faultString' => 'Anonymous posts are not allowed');
  180. // users have to be authenticated
  181. elseif(!$password && (!isset($context['users_with_anonymous_comments']) || ($context['users_with_anonymous_comments'] != 'Y')))
  182. $response = array('faultCode' => 49, 'faultString' => 'Please authenticate with a valid user name and password');
  183. // do we have a valid target to track?
  184. elseif(!$anchor || !is_object($anchor))
  185. $response = array('faultCode' => 33, 'faultString' => 'Nothing to comment');
  186. // save the comment
  187. else {
  188. // prepare a new comment record
  189. $fields = array();
  190. $fields['anchor'] = $anchor->get_reference();
  191. $fields['description'] = $comment;
  192. $fields['create_name'] = $name;
  193. $fields['create_address'] = $source;
  194. $fields['edit_name'] = $name;
  195. $fields['edit_address'] = $source;
  196. // if user name and/or password are provided, authentication has to be correct
  197. $user = array();
  198. if($name && !$password && (!$user =& Users::get($name)))
  199. $response = array('faultCode' => 49, 'faultString' => 'Unknown user name');
  200. elseif($name && $password && (!$user = Users::login($name, $password)))
  201. $response = array('faultCode' => 49, 'faultString' => 'Invalid user name and password');
  202. // ok, post this comment
  203. else {
  204. // reference user profile if any
  205. if($user['id']) {
  206. $fields['create_id'] = $user['id'];
  207. $fields['create_name'] = $user['nick_name'];
  208. $fields['create_address'] = $user['email'];
  209. $fields['edit_id'] = $user['id'];
  210. $fields['edit_name'] = $user['nick_name'];
  211. $fields['edit_address'] = $user['email'];
  212. }
  213. // save the request if debug mode
  214. if($context['debug_comment'] == 'Y')
  215. Logger::remember('comments/post.php', 'comments post item', $fields, 'debug');
  216. // save in the database
  217. if(!$fields['id'] = Comments::post($fields))
  218. $response = array('faultCode' => 1, 'faultString' => Logger::error_pop());
  219. // post-processing
  220. else {
  221. // touch the related anchor
  222. $anchor->touch('comment:create', $fields['id']);
  223. // clear cache
  224. Comments::clear($fields);
  225. // increment the post counter of the surfer
  226. if($user['id'])
  227. Users::increment_posts($user['id']);
  228. }
  229. }
  230. }
  231. // an error has been encountered
  232. if(is_array($response)) {
  233. $response = '<?xml version="1.0" encoding="'.$context['charset'].'"?>'
  234. ."\n".'<response>'
  235. ."\n".'<error>'.$response['faultCode'].'</error>'
  236. ."\n".'<message>'.$response['faultString'].'</message>'
  237. ."\n".'</response>';
  238. // also sets an error at the HTTP level
  239. Safe::header('Status: 400 Bad Request', TRUE, 400);
  240. // everything's going fine
  241. } else {
  242. $response = '<?xml version="1.0" encoding="'.$context['charset'].'"?>'
  243. ."\n".'<response>'
  244. ."\n".'<error>0</error>'
  245. ."\n".'</response>';
  246. }
  247. // save the response if debug mode
  248. if($context['debug_comment'] == 'Y')
  249. Logger::remember('comments/post.php', 'comments post response', $response, 'debug');
  250. // send the response
  251. Safe::header('Content-Type: text/xml');
  252. Safe::header('Content-Length: '.strlen($response));
  253. echo $response;
  254. return;
  255. // this is not a POST -- assume we have a human being
  256. } else {
  257. // detail usage rule
  258. Logger::error(i18n::s('This script supports Comment API and Post-It updates through HTTP POST requests.'));
  259. }
  260. // render the skin
  261. render_skin();
  262. ?>