Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1188 lines
37 KiB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
11 years ago
10 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
9 years ago
11 years ago
10 years ago
11 years ago
10 years ago
10 years ago
10 years ago
11 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
  1. <?php
  2. /**
  3. *
  4. * This is the POST destination for most all locally posted
  5. * text stuff. This function handles status, wall-to-wall status,
  6. * local comments, and remote coments that are posted on this site
  7. * (as opposed to being delivered in a feed).
  8. * Also processed here are posts and comments coming through the
  9. * statusnet/twitter API.
  10. * All of these become an "item" which is our basic unit of
  11. * information.
  12. * Posts that originate externally or do not fall into the above
  13. * posting categories go through item_store() instead of this function.
  14. *
  15. */
  16. require_once('include/crypto.php');
  17. require_once('include/enotify.php');
  18. require_once('include/email.php');
  19. require_once('library/langdet/Text/LanguageDetect.php');
  20. require_once('include/tags.php');
  21. function item_post(&$a) {
  22. if((! local_user()) && (! remote_user()) && (! x($_REQUEST,'commenter')))
  23. return;
  24. require_once('include/security.php');
  25. $uid = local_user();
  26. if(x($_REQUEST,'dropitems')) {
  27. require_once('include/items.php');
  28. $arr_drop = explode(',',$_REQUEST['dropitems']);
  29. drop_items($arr_drop);
  30. $json = array('success' => 1);
  31. echo json_encode($json);
  32. killme();
  33. }
  34. call_hooks('post_local_start', $_REQUEST);
  35. // logger('postinput ' . file_get_contents('php://input'));
  36. logger('postvars ' . print_r($_REQUEST,true), LOGGER_DATA);
  37. $api_source = ((x($_REQUEST,'api_source') && $_REQUEST['api_source']) ? true : false);
  38. $message_id = ((x($_REQUEST,'message_id') && $api_source) ? strip_tags($_REQUEST['message_id']) : '');
  39. $return_path = ((x($_REQUEST,'return')) ? $_REQUEST['return'] : '');
  40. $preview = ((x($_REQUEST,'preview')) ? intval($_REQUEST['preview']) : 0);
  41. // Check for doubly-submitted posts, and reject duplicates
  42. // Note that we have to ignore previews, otherwise nothing will post
  43. // after it's been previewed
  44. if(!$preview && x($_REQUEST['post_id_random'])) {
  45. if(x($_SESSION['post-random']) && $_SESSION['post-random'] == $_REQUEST['post_id_random']) {
  46. logger("item post: duplicate post", LOGGER_DEBUG);
  47. item_post_return($a->get_baseurl(), $api_source, $return_path);
  48. }
  49. else
  50. $_SESSION['post-random'] = $_REQUEST['post_id_random'];
  51. }
  52. /**
  53. * Is this a reply to something?
  54. */
  55. $parent = ((x($_REQUEST,'parent')) ? intval($_REQUEST['parent']) : 0);
  56. $parent_uri = ((x($_REQUEST,'parent_uri')) ? trim($_REQUEST['parent_uri']) : '');
  57. $parent_item = null;
  58. $parent_contact = null;
  59. $thr_parent = '';
  60. $parid = 0;
  61. $r = false;
  62. if($parent || $parent_uri) {
  63. if(! x($_REQUEST,'type'))
  64. $_REQUEST['type'] = 'net-comment';
  65. if($parent) {
  66. $r = q("SELECT * FROM `item` WHERE `id` = %d LIMIT 1",
  67. intval($parent)
  68. );
  69. }
  70. elseif($parent_uri && local_user()) {
  71. // This is coming from an API source, and we are logged in
  72. $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
  73. dbesc($parent_uri),
  74. intval(local_user())
  75. );
  76. }
  77. // if this isn't the real parent of the conversation, find it
  78. if($r !== false && count($r)) {
  79. $parid = $r[0]['parent'];
  80. $parent_uri = $r[0]['uri'];
  81. if($r[0]['id'] != $r[0]['parent']) {
  82. $r = q("SELECT * FROM `item` WHERE `id` = `parent` AND `parent` = %d LIMIT 1",
  83. intval($parid)
  84. );
  85. }
  86. }
  87. if(($r === false) || (! count($r))) {
  88. notice( t('Unable to locate original post.') . EOL);
  89. if(x($_REQUEST,'return'))
  90. goaway($a->get_baseurl() . "/" . $return_path );
  91. killme();
  92. }
  93. $parent_item = $r[0];
  94. $parent = $r[0]['id'];
  95. // multi-level threading - preserve the info but re-parent to our single level threading
  96. //if(($parid) && ($parid != $parent))
  97. $thr_parent = $parent_uri;
  98. if($parent_item['contact-id'] && $uid) {
  99. $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1",
  100. intval($parent_item['contact-id']),
  101. intval($uid)
  102. );
  103. if(count($r))
  104. $parent_contact = $r[0];
  105. }
  106. }
  107. if($parent) logger('mod_item: item_post parent=' . $parent);
  108. $profile_uid = ((x($_REQUEST,'profile_uid')) ? intval($_REQUEST['profile_uid']) : 0);
  109. $post_id = ((x($_REQUEST,'post_id')) ? intval($_REQUEST['post_id']) : 0);
  110. $app = ((x($_REQUEST,'source')) ? strip_tags($_REQUEST['source']) : '');
  111. $allow_moderated = false;
  112. // here is where we are going to check for permission to post a moderated comment.
  113. // First check that the parent exists and it is a wall item.
  114. if((x($_REQUEST,'commenter')) && ((! $parent) || (! $parent_item['wall']))) {
  115. notice( t('Permission denied.') . EOL) ;
  116. if(x($_REQUEST,'return'))
  117. goaway($a->get_baseurl() . "/" . $return_path );
  118. killme();
  119. }
  120. // Now check that it is a page_type of PAGE_BLOG, and that valid personal details
  121. // have been provided, and run any anti-spam plugins
  122. // TODO
  123. if((! can_write_wall($a,$profile_uid)) && (! $allow_moderated)) {
  124. notice( t('Permission denied.') . EOL) ;
  125. if(x($_REQUEST,'return'))
  126. goaway($a->get_baseurl() . "/" . $return_path );
  127. killme();
  128. }
  129. // is this an edited post?
  130. $orig_post = null;
  131. if($post_id) {
  132. $i = q("SELECT * FROM `item` WHERE `uid` = %d AND `id` = %d LIMIT 1",
  133. intval($profile_uid),
  134. intval($post_id)
  135. );
  136. if(! count($i))
  137. killme();
  138. $orig_post = $i[0];
  139. }
  140. $user = null;
  141. $r = q("SELECT * FROM `user` WHERE `uid` = %d LIMIT 1",
  142. intval($profile_uid)
  143. );
  144. if(count($r))
  145. $user = $r[0];
  146. if($orig_post) {
  147. $str_group_allow = $orig_post['allow_gid'];
  148. $str_contact_allow = $orig_post['allow_cid'];
  149. $str_group_deny = $orig_post['deny_gid'];
  150. $str_contact_deny = $orig_post['deny_cid'];
  151. $location = $orig_post['location'];
  152. $coord = $orig_post['coord'];
  153. $verb = $orig_post['verb'];
  154. $emailcc = $orig_post['emailcc'];
  155. $app = $orig_post['app'];
  156. $categories = $orig_post['file'];
  157. $title = notags(trim($_REQUEST['title']));
  158. $body = escape_tags(trim($_REQUEST['body']));
  159. $private = $orig_post['private'];
  160. $pubmail_enable = $orig_post['pubmail'];
  161. }
  162. else {
  163. // if coming from the API and no privacy settings are set,
  164. // use the user default permissions - as they won't have
  165. // been supplied via a form.
  166. if(($api_source)
  167. && (! array_key_exists('contact_allow',$_REQUEST))
  168. && (! array_key_exists('group_allow',$_REQUEST))
  169. && (! array_key_exists('contact_deny',$_REQUEST))
  170. && (! array_key_exists('group_deny',$_REQUEST))) {
  171. $str_group_allow = $user['allow_gid'];
  172. $str_contact_allow = $user['allow_cid'];
  173. $str_group_deny = $user['deny_gid'];
  174. $str_contact_deny = $user['deny_cid'];
  175. }
  176. else {
  177. // use the posted permissions
  178. $str_group_allow = perms2str($_REQUEST['group_allow']);
  179. $str_contact_allow = perms2str($_REQUEST['contact_allow']);
  180. $str_group_deny = perms2str($_REQUEST['group_deny']);
  181. $str_contact_deny = perms2str($_REQUEST['contact_deny']);
  182. }
  183. $title = notags(trim($_REQUEST['title']));
  184. $location = notags(trim($_REQUEST['location']));
  185. $coord = notags(trim($_REQUEST['coord']));
  186. $verb = notags(trim($_REQUEST['verb']));
  187. $emailcc = notags(trim($_REQUEST['emailcc']));
  188. $body = escape_tags(trim($_REQUEST['body']));
  189. $naked_body = preg_replace('/\[(.+?)\]/','',$body);
  190. if (version_compare(PHP_VERSION, '5.3.0', '>=')) {
  191. $l = new Text_LanguageDetect;
  192. //$lng = $l->detectConfidence($naked_body);
  193. //$postopts = (($lng['language']) ? 'lang=' . $lng['language'] . ';' . $lng['confidence'] : '');
  194. $lng = $l->detect($naked_body, 3);
  195. if (sizeof($lng) > 0) {
  196. $postopts = "";
  197. foreach ($lng as $language => $score) {
  198. if ($postopts == "")
  199. $postopts = "lang=";
  200. else
  201. $postopts .= ":";
  202. $postopts .= $language.";".$score;
  203. }
  204. }
  205. logger('mod_item: detect language' . print_r($lng,true) . $naked_body, LOGGER_DATA);
  206. }
  207. else
  208. $postopts = '';
  209. $private = ((strlen($str_group_allow) || strlen($str_contact_allow) || strlen($str_group_deny) || strlen($str_contact_deny)) ? 1 : 0);
  210. if($user['hidewall'])
  211. $private = 2;
  212. // If this is a comment, set the permissions from the parent.
  213. if($parent_item) {
  214. $private = 0;
  215. if(($parent_item['private'])
  216. || strlen($parent_item['allow_cid'])
  217. || strlen($parent_item['allow_gid'])
  218. || strlen($parent_item['deny_cid'])
  219. || strlen($parent_item['deny_gid'])) {
  220. $private = (($parent_item['private']) ? $parent_item['private'] : 1);
  221. }
  222. $str_contact_allow = $parent_item['allow_cid'];
  223. $str_group_allow = $parent_item['allow_gid'];
  224. $str_contact_deny = $parent_item['deny_cid'];
  225. $str_group_deny = $parent_item['deny_gid'];
  226. }
  227. $pubmail_enable = ((x($_REQUEST,'pubmail_enable') && intval($_REQUEST['pubmail_enable']) && (! $private)) ? 1 : 0);
  228. // if using the API, we won't see pubmail_enable - figure out if it should be set
  229. if($api_source && $profile_uid && $profile_uid == local_user() && (! $private)) {
  230. $mail_disabled = ((function_exists('imap_open') && (! get_config('system','imap_disabled'))) ? 0 : 1);
  231. if(! $mail_disabled) {
  232. $r = q("SELECT * FROM `mailacct` WHERE `uid` = %d AND `server` != '' LIMIT 1",
  233. intval(local_user())
  234. );
  235. if(count($r) && intval($r[0]['pubmail']))
  236. $pubmail_enabled = true;
  237. }
  238. }
  239. if(! strlen($body)) {
  240. if($preview)
  241. killme();
  242. info( t('Empty post discarded.') . EOL );
  243. if(x($_REQUEST,'return'))
  244. goaway($a->get_baseurl() . "/" . $return_path );
  245. killme();
  246. }
  247. }
  248. if(strlen($categories)) {
  249. // get the "fileas" tags for this post
  250. $filedas = file_tag_file_to_list($categories, 'file');
  251. }
  252. // save old and new categories, so we can determine what needs to be deleted from pconfig
  253. $categories_old = $categories;
  254. $categories = file_tag_list_to_file(trim($_REQUEST['category']), 'category');
  255. $categories_new = $categories;
  256. if(strlen($filedas)) {
  257. // append the fileas stuff to the new categories list
  258. $categories .= file_tag_list_to_file($filedas, 'file');
  259. }
  260. // Work around doubled linefeeds in Tinymce 3.5b2
  261. // First figure out if it's a status post that would've been
  262. // created using tinymce. Otherwise leave it alone.
  263. /* $plaintext = (local_user() ? intval(get_pconfig(local_user(),'system','plaintext')) || !feature_enabled($profile_uid,'richtext') : 0);
  264. if((! $parent) && (! $api_source) && (! $plaintext)) {
  265. $body = fix_mce_lf($body);
  266. }*/
  267. $plaintext = (local_user() ? !feature_enabled($profile_uid,'richtext') : 0);
  268. if((! $parent) && (! $api_source) && (! $plaintext)) {
  269. $body = fix_mce_lf($body);
  270. }
  271. // get contact info for poster
  272. $author = null;
  273. $self = false;
  274. $contact_id = 0;
  275. if((local_user()) && (local_user() == $profile_uid)) {
  276. $self = true;
  277. $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` = 1 LIMIT 1",
  278. intval($_SESSION['uid'])
  279. );
  280. }
  281. elseif(remote_user()) {
  282. if(is_array($_SESSION['remote'])) {
  283. foreach($_SESSION['remote'] as $v) {
  284. if($v['uid'] == $profile_uid) {
  285. $contact_id = $v['cid'];
  286. break;
  287. }
  288. }
  289. }
  290. if($contact_id) {
  291. $r = q("SELECT * FROM `contact` WHERE `id` = %d LIMIT 1",
  292. intval($contact_id)
  293. );
  294. }
  295. }
  296. if(count($r)) {
  297. $author = $r[0];
  298. $contact_id = $author['id'];
  299. }
  300. // get contact info for owner
  301. if($profile_uid == local_user()) {
  302. $contact_record = $author;
  303. }
  304. else {
  305. $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` = 1 LIMIT 1",
  306. intval($profile_uid)
  307. );
  308. if(count($r))
  309. $contact_record = $r[0];
  310. }
  311. $post_type = notags(trim($_REQUEST['type']));
  312. if($post_type === 'net-comment') {
  313. if($parent_item !== null) {
  314. if($parent_item['wall'] == 1)
  315. $post_type = 'wall-comment';
  316. else
  317. $post_type = 'remote-comment';
  318. }
  319. }
  320. /**
  321. *
  322. * When a photo was uploaded into the message using the (profile wall) ajax
  323. * uploader, The permissions are initially set to disallow anybody but the
  324. * owner from seeing it. This is because the permissions may not yet have been
  325. * set for the post. If it's private, the photo permissions should be set
  326. * appropriately. But we didn't know the final permissions on the post until
  327. * now. So now we'll look for links of uploaded messages that are in the
  328. * post and set them to the same permissions as the post itself.
  329. *
  330. */
  331. $match = null;
  332. if((! $preview) && preg_match_all("/\[img([\=0-9x]*?)\](.*?)\[\/img\]/",$body,$match)) {
  333. $images = $match[2];
  334. if(count($images)) {
  335. foreach($images as $image) {
  336. if(! stristr($image,$a->get_baseurl() . '/photo/'))
  337. continue;
  338. $image_uri = substr($image,strrpos($image,'/') + 1);
  339. $image_uri = substr($image_uri,0, strpos($image_uri,'-'));
  340. if(! strlen($image_uri))
  341. continue;
  342. $srch = '<' . intval($contact_id) . '>';
  343. $r = q("SELECT `id` FROM `photo` WHERE `allow_cid` = '%s' AND `allow_gid` = '' AND `deny_cid` = '' AND `deny_gid` = ''
  344. AND `resource-id` = '%s' AND `uid` = %d LIMIT 1",
  345. dbesc($srch),
  346. dbesc($image_uri),
  347. intval($profile_uid)
  348. );
  349. if(! count($r))
  350. continue;
  351. $r = q("UPDATE `photo` SET `allow_cid` = '%s', `allow_gid` = '%s', `deny_cid` = '%s', `deny_gid` = '%s'
  352. WHERE `resource-id` = '%s' AND `uid` = %d AND `album` = '%s' ",
  353. dbesc($str_contact_allow),
  354. dbesc($str_group_allow),
  355. dbesc($str_contact_deny),
  356. dbesc($str_group_deny),
  357. dbesc($image_uri),
  358. intval($profile_uid),
  359. dbesc( t('Wall Photos'))
  360. );
  361. }
  362. }
  363. }
  364. /**
  365. * Next link in any attachment references we find in the post.
  366. */
  367. $match = false;
  368. if((! $preview) && preg_match_all("/\[attachment\](.*?)\[\/attachment\]/",$body,$match)) {
  369. $attaches = $match[1];
  370. if(count($attaches)) {
  371. foreach($attaches as $attach) {
  372. $r = q("SELECT * FROM `attach` WHERE `uid` = %d AND `id` = %d LIMIT 1",
  373. intval($profile_uid),
  374. intval($attach)
  375. );
  376. if(count($r)) {
  377. $r = q("UPDATE `attach` SET `allow_cid` = '%s', `allow_gid` = '%s', `deny_cid` = '%s', `deny_gid` = '%s'
  378. WHERE `uid` = %d AND `id` = %d",
  379. dbesc($str_contact_allow),
  380. dbesc($str_group_allow),
  381. dbesc($str_contact_deny),
  382. dbesc($str_group_deny),
  383. intval($profile_uid),
  384. intval($attach)
  385. );
  386. }
  387. }
  388. }
  389. }
  390. // embedded bookmark in post? set bookmark flag
  391. $bookmark = 0;
  392. if(preg_match_all("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism",$body,$match,PREG_SET_ORDER)) {
  393. $bookmark = 1;
  394. }
  395. $body = bb_translate_video($body);
  396. /**
  397. * Fold multi-line [code] sequences
  398. */
  399. $body = preg_replace('/\[\/code\]\s*\[code\]/ism',"\n",$body);
  400. $body = scale_external_images($body,false);
  401. /**
  402. * Look for any tags and linkify them
  403. */
  404. $str_tags = '';
  405. $inform = '';
  406. $tags = get_tags($body);
  407. /**
  408. * add a statusnet style reply tag if the original post was from there
  409. * and we are replying, and there isn't one already
  410. */
  411. if(($parent_contact) && ($parent_contact['network'] === NETWORK_OSTATUS)
  412. && ($parent_contact['nick']) && (! in_array('@' . $parent_contact['nick'],$tags))) {
  413. $body = '@' . $parent_contact['nick'] . ' ' . $body;
  414. $tags[] = '@' . $parent_contact['nick'];
  415. }
  416. $tagged = array();
  417. $private_forum = false;
  418. if(count($tags)) {
  419. foreach($tags as $tag) {
  420. // If we already tagged 'Robert Johnson', don't try and tag 'Robert'.
  421. // Robert Johnson should be first in the $tags array
  422. $fullnametagged = false;
  423. for($x = 0; $x < count($tagged); $x ++) {
  424. if(stristr($tagged[$x],$tag . ' ')) {
  425. $fullnametagged = true;
  426. break;
  427. }
  428. }
  429. if($fullnametagged)
  430. continue;
  431. $success = handle_tag($a, $body, $inform, $str_tags, (local_user()) ? local_user() : $profile_uid , $tag);
  432. if($success['replaced'])
  433. $tagged[] = $tag;
  434. if(is_array($success['contact']) && intval($success['contact']['prv'])) {
  435. $private_forum = true;
  436. $private_id = $success['contact']['id'];
  437. }
  438. }
  439. }
  440. if(($private_forum) && (! $parent) && (! $private)) {
  441. // we tagged a private forum in a top level post and the message was public.
  442. // Restrict it.
  443. $private = 1;
  444. $str_contact_allow = '<' . $private_id . '>';
  445. }
  446. $attachments = '';
  447. $match = false;
  448. if(preg_match_all('/(\[attachment\]([0-9]+)\[\/attachment\])/',$body,$match)) {
  449. foreach($match[2] as $mtch) {
  450. $r = q("SELECT `id`,`filename`,`filesize`,`filetype` FROM `attach` WHERE `uid` = %d AND `id` = %d LIMIT 1",
  451. intval($profile_uid),
  452. intval($mtch)
  453. );
  454. if(count($r)) {
  455. if(strlen($attachments))
  456. $attachments .= ',';
  457. $attachments .= '[attach]href="' . $a->get_baseurl() . '/attach/' . $r[0]['id'] . '" length="' . $r[0]['filesize'] . '" type="' . $r[0]['filetype'] . '" title="' . (($r[0]['filename']) ? $r[0]['filename'] : '') . '"[/attach]';
  458. }
  459. $body = str_replace($match[1],'',$body);
  460. }
  461. }
  462. $wall = 0;
  463. if($post_type === 'wall' || $post_type === 'wall-comment')
  464. $wall = 1;
  465. if(! strlen($verb))
  466. $verb = ACTIVITY_POST ;
  467. $gravity = (($parent) ? 6 : 0 );
  468. // even if the post arrived via API we are considering that it
  469. // originated on this site by default for determining relayability.
  470. $origin = ((x($_REQUEST,'origin')) ? intval($_REQUEST['origin']) : 1);
  471. $notify_type = (($parent) ? 'comment-new' : 'wall-new' );
  472. $uri = (($message_id) ? $message_id : item_new_uri($a->get_hostname(),$profile_uid));
  473. // Fallback so that we alway have a thr-parent
  474. if(!$thr_parent)
  475. $thr_parent = $uri;
  476. $datarray = array();
  477. $datarray['uid'] = $profile_uid;
  478. $datarray['type'] = $post_type;
  479. $datarray['wall'] = $wall;
  480. $datarray['gravity'] = $gravity;
  481. $datarray['contact-id'] = $contact_id;
  482. $datarray['owner-name'] = $contact_record['name'];
  483. $datarray['owner-link'] = $contact_record['url'];
  484. $datarray['owner-avatar'] = $contact_record['thumb'];
  485. $datarray['author-name'] = $author['name'];
  486. $datarray['author-link'] = $author['url'];
  487. $datarray['author-avatar'] = $author['thumb'];
  488. $datarray['created'] = datetime_convert();
  489. $datarray['edited'] = datetime_convert();
  490. $datarray['commented'] = datetime_convert();
  491. $datarray['received'] = datetime_convert();
  492. $datarray['changed'] = datetime_convert();
  493. $datarray['uri'] = $uri;
  494. $datarray['title'] = $title;
  495. $datarray['body'] = $body;
  496. $datarray['app'] = $app;
  497. $datarray['location'] = $location;
  498. $datarray['coord'] = $coord;
  499. $datarray['tag'] = $str_tags;
  500. $datarray['file'] = $categories;
  501. $datarray['inform'] = $inform;
  502. $datarray['verb'] = $verb;
  503. $datarray['allow_cid'] = $str_contact_allow;
  504. $datarray['allow_gid'] = $str_group_allow;
  505. $datarray['deny_cid'] = $str_contact_deny;
  506. $datarray['deny_gid'] = $str_group_deny;
  507. $datarray['private'] = $private;
  508. $datarray['pubmail'] = $pubmail_enable;
  509. $datarray['attach'] = $attachments;
  510. $datarray['bookmark'] = intval($bookmark);
  511. $datarray['thr-parent'] = $thr_parent;
  512. $datarray['postopts'] = $postopts;
  513. $datarray['origin'] = $origin;
  514. $datarray['moderated'] = $allow_moderated;
  515. /**
  516. * These fields are for the convenience of plugins...
  517. * 'self' if true indicates the owner is posting on their own wall
  518. * If parent is 0 it is a top-level post.
  519. */
  520. $datarray['parent'] = $parent;
  521. $datarray['self'] = $self;
  522. // $datarray['prvnets'] = $user['prvnets'];
  523. if($orig_post)
  524. $datarray['edit'] = true;
  525. else
  526. $datarray['guid'] = get_guid();
  527. // preview mode - prepare the body for display and send it via json
  528. if($preview) {
  529. require_once('include/conversation.php');
  530. $o = conversation($a,array(array_merge($contact_record,$datarray)),'search', false, true);
  531. logger('preview: ' . $o);
  532. echo json_encode(array('preview' => $o));
  533. killme();
  534. }
  535. call_hooks('post_local',$datarray);
  536. if(x($datarray,'cancel')) {
  537. logger('mod_item: post cancelled by plugin.');
  538. if($return_path) {
  539. goaway($a->get_baseurl() . "/" . $return_path);
  540. }
  541. $json = array('cancel' => 1);
  542. if(x($_REQUEST,'jsreload') && strlen($_REQUEST['jsreload']))
  543. $json['reload'] = $a->get_baseurl() . '/' . $_REQUEST['jsreload'];
  544. echo json_encode($json);
  545. killme();
  546. }
  547. if($orig_post) {
  548. $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `tag` = '%s', `attach` = '%s', `file` = '%s', `edited` = '%s' WHERE `id` = %d AND `uid` = %d",
  549. dbesc($datarray['title']),
  550. dbesc($datarray['body']),
  551. dbesc($datarray['tag']),
  552. dbesc($datarray['attach']),
  553. dbesc($datarray['file']),
  554. dbesc(datetime_convert()),
  555. intval($post_id),
  556. intval($profile_uid)
  557. );
  558. create_tags_from_itemuri($post_id, $profile_uid);
  559. // update filetags in pconfig
  560. file_tag_update_pconfig($uid,$categories_old,$categories_new,'category');
  561. proc_run('php', "include/notifier.php", 'edit_post', "$post_id");
  562. if((x($_REQUEST,'return')) && strlen($return_path)) {
  563. logger('return: ' . $return_path);
  564. goaway($a->get_baseurl() . "/" . $return_path );
  565. }
  566. killme();
  567. }
  568. else
  569. $post_id = 0;
  570. $r = q("INSERT INTO `item` (`guid`, `uid`,`type`,`wall`,`gravity`,`contact-id`,`owner-name`,`owner-link`,`owner-avatar`,
  571. `author-name`, `author-link`, `author-avatar`, `created`, `edited`, `commented`, `received`, `changed`, `uri`, `thr-parent`, `title`, `body`, `app`, `location`, `coord`,
  572. `tag`, `inform`, `verb`, `postopts`, `allow_cid`, `allow_gid`, `deny_cid`, `deny_gid`, `private`, `pubmail`, `attach`, `bookmark`,`origin`, `moderated`, `file` )
  573. VALUES( '%s', %d, '%s', %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, %d, %d, '%s' )",
  574. dbesc($datarray['guid']),
  575. intval($datarray['uid']),
  576. dbesc($datarray['type']),
  577. intval($datarray['wall']),
  578. intval($datarray['gravity']),
  579. intval($datarray['contact-id']),
  580. dbesc($datarray['owner-name']),
  581. dbesc($datarray['owner-link']),
  582. dbesc($datarray['owner-avatar']),
  583. dbesc($datarray['author-name']),
  584. dbesc($datarray['author-link']),
  585. dbesc($datarray['author-avatar']),
  586. dbesc($datarray['created']),
  587. dbesc($datarray['edited']),
  588. dbesc($datarray['commented']),
  589. dbesc($datarray['received']),
  590. dbesc($datarray['changed']),
  591. dbesc($datarray['uri']),
  592. dbesc($datarray['thr-parent']),
  593. dbesc($datarray['title']),
  594. dbesc($datarray['body']),
  595. dbesc($datarray['app']),
  596. dbesc($datarray['location']),
  597. dbesc($datarray['coord']),
  598. dbesc($datarray['tag']),
  599. dbesc($datarray['inform']),
  600. dbesc($datarray['verb']),
  601. dbesc($datarray['postopts']),
  602. dbesc($datarray['allow_cid']),
  603. dbesc($datarray['allow_gid']),
  604. dbesc($datarray['deny_cid']),
  605. dbesc($datarray['deny_gid']),
  606. intval($datarray['private']),
  607. intval($datarray['pubmail']),
  608. dbesc($datarray['attach']),
  609. intval($datarray['bookmark']),
  610. intval($datarray['origin']),
  611. intval($datarray['moderated']),
  612. dbesc($datarray['file'])
  613. );
  614. $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' LIMIT 1",
  615. dbesc($datarray['uri']));
  616. if(count($r)) {
  617. $post_id = $r[0]['id'];
  618. logger('mod_item: saved item ' . $post_id);
  619. create_tags_from_item($post_id);
  620. // update filetags in pconfig
  621. file_tag_update_pconfig($uid,$categories_old,$categories_new,'category');
  622. // Store the fresh generated item into the cache
  623. $cachefile = get_cachefile($datarray["guid"]."-".hash("md5", $datarray['body']));
  624. if (($cachefile != '') AND !file_exists($cachefile)) {
  625. $s = prepare_text($datarray['body']);
  626. $stamp1 = microtime(true);
  627. file_put_contents($cachefile, $s);
  628. $a->save_timestamp($stamp1, "file");
  629. logger('mod_item: put item '.$r[0]['id'].' into cachefile '.$cachefile);
  630. }
  631. if($parent) {
  632. // This item is the last leaf and gets the comment box, clear any ancestors
  633. $r = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent` = %d ",
  634. dbesc(datetime_convert()),
  635. intval($parent)
  636. );
  637. // Inherit ACL's from the parent item.
  638. $r = q("UPDATE `item` SET `allow_cid` = '%s', `allow_gid` = '%s', `deny_cid` = '%s', `deny_gid` = '%s', `private` = %d
  639. WHERE `id` = %d",
  640. dbesc($parent_item['allow_cid']),
  641. dbesc($parent_item['allow_gid']),
  642. dbesc($parent_item['deny_cid']),
  643. dbesc($parent_item['deny_gid']),
  644. intval($parent_item['private']),
  645. intval($post_id)
  646. );
  647. if($contact_record != $author) {
  648. notification(array(
  649. 'type' => NOTIFY_COMMENT,
  650. 'notify_flags' => $user['notify-flags'],
  651. 'language' => $user['language'],
  652. 'to_name' => $user['username'],
  653. 'to_email' => $user['email'],
  654. 'uid' => $user['uid'],
  655. 'item' => $datarray,
  656. 'link' => $a->get_baseurl() . '/display/' . $user['nickname'] . '/' . $post_id,
  657. 'source_name' => $datarray['author-name'],
  658. 'source_link' => $datarray['author-link'],
  659. 'source_photo' => $datarray['author-avatar'],
  660. 'verb' => ACTIVITY_POST,
  661. 'otype' => 'item',
  662. 'parent' => $parent,
  663. 'parent_uri' => $parent_item['uri']
  664. ));
  665. }
  666. // Store the comment signature information in case we need to relay to Diaspora
  667. store_diaspora_comment_sig($datarray, $author, ($self ? $a->user['prvkey'] : false), $parent_item, $post_id);
  668. }
  669. else {
  670. $parent = $post_id;
  671. if($contact_record != $author) {
  672. notification(array(
  673. 'type' => NOTIFY_WALL,
  674. 'notify_flags' => $user['notify-flags'],
  675. 'language' => $user['language'],
  676. 'to_name' => $user['username'],
  677. 'to_email' => $user['email'],
  678. 'uid' => $user['uid'],
  679. 'item' => $datarray,
  680. 'link' => $a->get_baseurl() . '/display/' . $user['nickname'] . '/' . $post_id,
  681. 'source_name' => $datarray['author-name'],
  682. 'source_link' => $datarray['author-link'],
  683. 'source_photo' => $datarray['author-avatar'],
  684. 'verb' => ACTIVITY_POST,
  685. 'otype' => 'item'
  686. ));
  687. }
  688. }
  689. // fallback so that parent always gets set to non-zero.
  690. if(! $parent)
  691. $parent = $post_id;
  692. $r = q("UPDATE `item` SET `parent` = %d, `parent-uri` = '%s', `plink` = '%s', `changed` = '%s', `last-child` = 1, `visible` = 1
  693. WHERE `id` = %d",
  694. intval($parent),
  695. dbesc(($parent == $post_id) ? $uri : $parent_item['uri']),
  696. dbesc($a->get_baseurl() . '/display/' . $user['nickname'] . '/' . $post_id),
  697. dbesc(datetime_convert()),
  698. intval($post_id)
  699. );
  700. // photo comments turn the corresponding item visible to the profile wall
  701. // This way we don't see every picture in your new photo album posted to your wall at once.
  702. // They will show up as people comment on them.
  703. if(! $parent_item['visible']) {
  704. $r = q("UPDATE `item` SET `visible` = 1 WHERE `id` = %d",
  705. intval($parent_item['id'])
  706. );
  707. }
  708. }
  709. else {
  710. logger('mod_item: unable to retrieve post that was just stored.');
  711. notice( t('System error. Post not saved.') . EOL);
  712. goaway($a->get_baseurl() . "/" . $return_path );
  713. // NOTREACHED
  714. }
  715. // update the commented timestamp on the parent
  716. q("UPDATE `item` set `commented` = '%s', `changed` = '%s' WHERE `id` = %d",
  717. dbesc(datetime_convert()),
  718. dbesc(datetime_convert()),
  719. intval($parent)
  720. );
  721. $datarray['id'] = $post_id;
  722. $datarray['plink'] = $a->get_baseurl() . '/display/' . $user['nickname'] . '/' . $post_id;
  723. call_hooks('post_local_end', $datarray);
  724. if(strlen($emailcc) && $profile_uid == local_user()) {
  725. $erecips = explode(',', $emailcc);
  726. if(count($erecips)) {
  727. foreach($erecips as $recip) {
  728. $addr = trim($recip);
  729. if(! strlen($addr))
  730. continue;
  731. $disclaimer = '<hr />' . sprintf( t('This message was sent to you by %s, a member of the Friendica social network.'),$a->user['username'])
  732. . '<br />';
  733. $disclaimer .= sprintf( t('You may visit them online at %s'), $a->get_baseurl() . '/profile/' . $a->user['nickname']) . EOL;
  734. $disclaimer .= t('Please contact the sender by replying to this post if you do not wish to receive these messages.') . EOL;
  735. if (!$datarray['title']=='') {
  736. $subject = email_header_encode($datarray['title'],'UTF-8');
  737. } else {
  738. $subject = email_header_encode('[Friendica]' . ' ' . sprintf( t('%s posted an update.'),$a->user['username']),'UTF-8');
  739. }
  740. $link = '<a href="' . $a->get_baseurl() . '/profile/' . $a->user['nickname'] . '"><img src="' . $author['thumb'] . '" alt="' . $a->user['username'] . '" /></a><br /><br />';
  741. $html = prepare_body($datarray);
  742. $message = '<html><body>' . $link . $html . $disclaimer . '</body></html>';
  743. include_once('include/html2plain.php');
  744. $params = array (
  745. 'fromName' => $a->user['username'],
  746. 'fromEmail' => $a->user['email'],
  747. 'toEmail' => $addr,
  748. 'replyTo' => $a->user['email'],
  749. 'messageSubject' => $subject,
  750. 'htmlVersion' => $message,
  751. 'textVersion' => html2plain($html.$disclaimer),
  752. );
  753. enotify::send($params);
  754. }
  755. }
  756. }
  757. // This is a real juggling act on shared hosting services which kill your processes
  758. // e.g. dreamhost. We used to start delivery to our native delivery agents in the background
  759. // and then run our plugin delivery from the foreground. We're now doing plugin delivery first,
  760. // because as soon as you start loading up a bunch of remote delivey processes, *this* page is
  761. // likely to get killed off. If you end up looking at an /item URL and a blank page,
  762. // it's very likely the delivery got killed before all your friends could be notified.
  763. // Currently the only realistic fixes are to use a reliable server - which precludes shared hosting,
  764. // or cut back on plugins which do remote deliveries.
  765. proc_run('php', "include/notifier.php", $notify_type, "$post_id");
  766. logger('post_complete');
  767. item_post_return($a->get_baseurl(), $api_source, $return_path);
  768. // NOTREACHED
  769. }
  770. function item_post_return($baseurl, $api_source, $return_path) {
  771. // figure out how to return, depending on from whence we came
  772. if($api_source)
  773. return;
  774. if($return_path) {
  775. goaway($baseurl . "/" . $return_path);
  776. }
  777. $json = array('success' => 1);
  778. if(x($_REQUEST,'jsreload') && strlen($_REQUEST['jsreload']))
  779. $json['reload'] = $baseurl . '/' . $_REQUEST['jsreload'];
  780. logger('post_json: ' . print_r($json,true), LOGGER_DEBUG);
  781. echo json_encode($json);
  782. killme();
  783. }
  784. function item_content(&$a) {
  785. if((! local_user()) && (! remote_user()))
  786. return;
  787. require_once('include/security.php');
  788. $o = '';
  789. if(($a->argc == 3) && ($a->argv[1] === 'drop') && intval($a->argv[2])) {
  790. require_once('include/items.php');
  791. $o = drop_item($a->argv[2], !is_ajax());
  792. if (is_ajax()){
  793. // ajax return: [<item id>, 0 (no perm) | <owner id>]
  794. echo json_encode(array(intval($a->argv[2]), intval($o)));
  795. killme();
  796. }
  797. }
  798. return $o;
  799. }
  800. /**
  801. * This function removes the tag $tag from the text $body and replaces it with
  802. * the appropiate link.
  803. *
  804. * @param unknown_type $body the text to replace the tag in
  805. * @param unknown_type $inform a comma-seperated string containing everybody to inform
  806. * @param unknown_type $str_tags string to add the tag to
  807. * @param unknown_type $profile_uid
  808. * @param unknown_type $tag the tag to replace
  809. *
  810. * @return boolean true if replaced, false if not replaced
  811. */
  812. function handle_tag($a, &$body, &$inform, &$str_tags, $profile_uid, $tag) {
  813. $replaced = false;
  814. $r = null;
  815. //is it a hash tag?
  816. if(strpos($tag,'#') === 0) {
  817. //if the tag is replaced...
  818. if(strpos($tag,'[url='))
  819. //...do nothing
  820. return $replaced;
  821. //base tag has the tags name only
  822. $basetag = str_replace('_',' ',substr($tag,1));
  823. //create text for link
  824. $newtag = '#[url=' . $a->get_baseurl() . '/search?tag=' . rawurlencode($basetag) . ']' . $basetag . '[/url]';
  825. //replace tag by the link
  826. $body = str_replace($tag, $newtag, $body);
  827. $replaced = true;
  828. //is the link already in str_tags?
  829. if(! stristr($str_tags,$newtag)) {
  830. //append or set str_tags
  831. if(strlen($str_tags))
  832. $str_tags .= ',';
  833. $str_tags .= $newtag;
  834. }
  835. return $replaced;
  836. }
  837. //is it a person tag?
  838. if(strpos($tag,'@') === 0) {
  839. //is it already replaced?
  840. if(strpos($tag,'[url='))
  841. return $replaced;
  842. $stat = false;
  843. //get the person's name
  844. $name = substr($tag,1);
  845. //is it a link or a full dfrn address?
  846. if((strpos($name,'@')) || (strpos($name,'http://'))) {
  847. $newname = $name;
  848. //get the profile links
  849. $links = @lrdd($name);
  850. if(count($links)) {
  851. //for all links, collect how is to inform and how's profile is to link
  852. foreach($links as $link) {
  853. if($link['@attributes']['rel'] === 'http://webfinger.net/rel/profile-page')
  854. $profile = $link['@attributes']['href'];
  855. if($link['@attributes']['rel'] === 'salmon') {
  856. if(strlen($inform))
  857. $inform .= ',';
  858. $inform .= 'url:' . str_replace(',','%2c',$link['@attributes']['href']);
  859. }
  860. }
  861. }
  862. } else { //if it is a name rather than an address
  863. $newname = $name;
  864. $alias = '';
  865. $tagcid = 0;
  866. //is it some generated name?
  867. if(strrpos($newname,'+')) {
  868. //get the id
  869. $tagcid = intval(substr($newname,strrpos($newname,'+') + 1));
  870. //remove the next word from tag's name
  871. if(strpos($name,' ')) {
  872. $name = substr($name,0,strpos($name,' '));
  873. }
  874. }
  875. if($tagcid) { //if there was an id
  876. //select contact with that id from the logged in user's contact list
  877. $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1",
  878. intval($tagcid),
  879. intval($profile_uid)
  880. );
  881. }
  882. else {
  883. $newname = str_replace('_',' ',$name);
  884. //select someone from this user's contacts by name
  885. $r = q("SELECT * FROM `contact` WHERE `name` = '%s' AND `uid` = %d LIMIT 1",
  886. dbesc($newname),
  887. intval($profile_uid)
  888. );
  889. if(! $r) {
  890. //select someone by attag or nick and the name passed in
  891. $r = q("SELECT * FROM `contact` WHERE `attag` = '%s' OR `nick` = '%s' AND `uid` = %d ORDER BY `attag` DESC LIMIT 1",
  892. dbesc($name),
  893. dbesc($name),
  894. intval($profile_uid)
  895. );
  896. }
  897. }
  898. /* } elseif(strstr($name,'_') || strstr($name,' ')) { //no id
  899. //get the real name
  900. $newname = str_replace('_',' ',$name);
  901. //select someone from this user's contacts by name
  902. $r = q("SELECT * FROM `contact` WHERE `name` = '%s' AND `uid` = %d LIMIT 1",
  903. dbesc($newname),
  904. intval($profile_uid)
  905. );
  906. } else {
  907. //select someone by attag or nick and the name passed in
  908. $r = q("SELECT * FROM `contact` WHERE `attag` = '%s' OR `nick` = '%s' AND `uid` = %d ORDER BY `attag` DESC LIMIT 1",
  909. dbesc($name),
  910. dbesc($name),
  911. intval($profile_uid)
  912. );
  913. }*/
  914. //$r is set, if someone could be selected
  915. if(count($r)) {
  916. $profile = $r[0]['url'];
  917. //set newname to nick, find alias
  918. if($r[0]['network'] === 'stat') {
  919. $newname = $r[0]['nick'];
  920. $stat = true;
  921. if($r[0]['alias'])
  922. $alias = $r[0]['alias'];
  923. }
  924. else
  925. $newname = $r[0]['name'];
  926. //add person's id to $inform
  927. if(strlen($inform))
  928. $inform .= ',';
  929. $inform .= 'cid:' . $r[0]['id'];
  930. }
  931. }
  932. //if there is an url for this persons profile
  933. if(isset($profile)) {
  934. $replaced = true;
  935. //create profile link
  936. $profile = str_replace(',','%2c',$profile);
  937. $newtag = '@[url=' . $profile . ']' . $newname . '[/url]';
  938. $body = str_replace('@' . $name, $newtag, $body);
  939. //append tag to str_tags
  940. if(! stristr($str_tags,$newtag)) {
  941. if(strlen($str_tags))
  942. $str_tags .= ',';
  943. $str_tags .= $newtag;
  944. }
  945. // Status.Net seems to require the numeric ID URL in a mention if the person isn't
  946. // subscribed to you. But the nickname URL is OK if they are. Grrr. We'll tag both.
  947. if(strlen($alias)) {
  948. $newtag = '@[url=' . $alias . ']' . $newname . '[/url]';
  949. if(! stristr($str_tags,$newtag)) {
  950. if(strlen($str_tags))
  951. $str_tags .= ',';
  952. $str_tags .= $newtag;
  953. }
  954. }
  955. }
  956. }
  957. return array('replaced' => $replaced, 'contact' => $r[0]);
  958. }
  959. function store_diaspora_comment_sig($datarray, $author, $uprvkey, $parent_item, $post_id) {
  960. // We won't be able to sign Diaspora comments for authenticated visitors - we don't have their private key
  961. $enabled = intval(get_config('system','diaspora_enabled'));
  962. if(! $enabled) {
  963. logger('mod_item: diaspora support disabled, not storing comment signature', LOGGER_DEBUG);
  964. return;
  965. }
  966. logger('mod_item: storing diaspora comment signature');
  967. require_once('include/bb2diaspora.php');
  968. $signed_body = html_entity_decode(bb2diaspora($datarray['body']));
  969. // Only works for NETWORK_DFRN
  970. $contact_baseurl_start = strpos($author['url'],'://') + 3;
  971. $contact_baseurl_length = strpos($author['url'],'/profile') - $contact_baseurl_start;
  972. $contact_baseurl = substr($author['url'], $contact_baseurl_start, $contact_baseurl_length);
  973. $diaspora_handle = $author['nick'] . '@' . $contact_baseurl;
  974. $signed_text = $datarray['guid'] . ';' . $parent_item['guid'] . ';' . $signed_body . ';' . $diaspora_handle;
  975. if( $uprvkey !== false )
  976. $authorsig = base64_encode(rsa_sign($signed_text,$uprvkey,'sha256'));
  977. else
  978. $authorsig = '';
  979. q("insert into sign (`iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ",
  980. intval($post_id),
  981. dbesc($signed_text),
  982. dbesc(base64_encode($authorsig)),
  983. dbesc($diaspora_handle)
  984. );
  985. return;
  986. }