From 1df6f5912754b83a65a86a402fabb79c12736bed Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tobias=20H=C3=B6=C3=9Fl?=
Date: Mon, 9 Apr 2012 09:42:14 +0000
Subject: [PATCH 01/96] Use batch requests when syncing friends; this reduces
the time for synchronization a lot
---
facebook/facebook.php | 2875 +++++++++++++++++++++--------------------
1 file changed, 1445 insertions(+), 1430 deletions(-)
mode change 100755 => 100644 facebook/facebook.php
diff --git a/facebook/facebook.php b/facebook/facebook.php
old mode 100755
new mode 100644
index 70a353dd..0c31da1f
--- a/facebook/facebook.php
+++ b/facebook/facebook.php
@@ -15,38 +15,38 @@
* in the images directory and may be uploaded as a Facebook app icon.
* Use images/friendica-16.jpg for the Icon and images/friendica-128.jpg for the Logo.
* b. The url should be your site URL with a trailing slash.
- * Friendica is a software application and does not require a Privacy Policy
+ * Friendica is a software application and does not require a Privacy Policy
* or Terms of Service, though your installation of it might. Facebook may require
- * that you provide a Privacy Policy, which we find ironic.
+ * that you provide a Privacy Policy, which we find ironic.
* c. Set the following values in your .htconfig.php file
* $a->config['facebook']['appid'] = 'xxxxxxxxxxx';
* $a->config['facebook']['appsecret'] = 'xxxxxxxxxxxxxxx';
* Replace with the settings Facebook gives you.
- * d. Navigate to Set Web->Site URL & Domain -> Website Settings. Set
- * Site URL to yoursubdomain.yourdomain.com. Set Site Domain to your
+ * d. Navigate to Set Web->Site URL & Domain -> Website Settings. Set
+ * Site URL to yoursubdomain.yourdomain.com. Set Site Domain to your
* yourdomain.com.
* 2. Visit the Facebook Settings section of the "Settings->Plugin Settings" page.
* and click 'Install Facebook Connector'.
* 3. Visit the Facebook Settings section of the "Settings->Plugin Settings" page.
* and click 'Install Facebook Connector'.
- * 4. This will ask you to login to Facebook and grant permission to the
- * plugin to do its stuff. Allow it to do so.
+ * 4. This will ask you to login to Facebook and grant permission to the
+ * plugin to do its stuff. Allow it to do so.
* 5. Optional step: If you want to use Facebook Real Time Updates (so new messages
* and new contacts are added ~1min after they are postet / added on FB), go to
* Settings -> plugins -> facebook and press the "Activate Real-Time Updates"-button.
* 6. You're done. To turn it off visit the Plugin Settings page again and
* 'Remove Facebook posting'.
*
- * Vidoes and embeds will not be posted if there is no other content. Links
- * and images will be converted to a format suitable for the Facebook API and
- * long posts truncated - with a link to view the full post.
+ * Vidoes and embeds will not be posted if there is no other content. Links
+ * and images will be converted to a format suitable for the Facebook API and
+ * long posts truncated - with a link to view the full post.
*
* Facebook contacts will not be able to view private photos, as they are not able to
- * authenticate to your site to establish identity. We will address this
+ * authenticate to your site to establish identity. We will address this
* in a future release.
*/
-
- /** TODO
+
+/** TODO
* - Implement a method for the administrator to delete all configuration data the plugin has created,
* e.g. the app_access_token
*/
@@ -61,28 +61,28 @@ define('FACEBOOK_MIN_POLL_INTERVAL', 5);
function facebook_install() {
- register_hook('post_local', 'addon/facebook/facebook.php', 'facebook_post_local');
- register_hook('notifier_normal', 'addon/facebook/facebook.php', 'facebook_post_hook');
- register_hook('jot_networks', 'addon/facebook/facebook.php', 'facebook_jot_nets');
- register_hook('connector_settings', 'addon/facebook/facebook.php', 'facebook_plugin_settings');
- register_hook('cron', 'addon/facebook/facebook.php', 'facebook_cron');
- register_hook('enotify', 'addon/facebook/facebook.php', 'facebook_enotify');
- register_hook('queue_predeliver', 'addon/facebook/facebook.php', 'fb_queue_hook');
+ register_hook('post_local', 'addon/facebook/facebook.php', 'facebook_post_local');
+ register_hook('notifier_normal', 'addon/facebook/facebook.php', 'facebook_post_hook');
+ register_hook('jot_networks', 'addon/facebook/facebook.php', 'facebook_jot_nets');
+ register_hook('connector_settings', 'addon/facebook/facebook.php', 'facebook_plugin_settings');
+ register_hook('cron', 'addon/facebook/facebook.php', 'facebook_cron');
+ register_hook('enotify', 'addon/facebook/facebook.php', 'facebook_enotify');
+ register_hook('queue_predeliver', 'addon/facebook/facebook.php', 'fb_queue_hook');
}
function facebook_uninstall() {
- unregister_hook('post_local', 'addon/facebook/facebook.php', 'facebook_post_local');
- unregister_hook('notifier_normal', 'addon/facebook/facebook.php', 'facebook_post_hook');
- unregister_hook('jot_networks', 'addon/facebook/facebook.php', 'facebook_jot_nets');
- unregister_hook('connector_settings', 'addon/facebook/facebook.php', 'facebook_plugin_settings');
- unregister_hook('cron', 'addon/facebook/facebook.php', 'facebook_cron');
- unregister_hook('enotify', 'addon/facebook/facebook.php', 'facebook_enotify');
- unregister_hook('queue_predeliver', 'addon/facebook/facebook.php', 'fb_queue_hook');
+ unregister_hook('post_local', 'addon/facebook/facebook.php', 'facebook_post_local');
+ unregister_hook('notifier_normal', 'addon/facebook/facebook.php', 'facebook_post_hook');
+ unregister_hook('jot_networks', 'addon/facebook/facebook.php', 'facebook_jot_nets');
+ unregister_hook('connector_settings', 'addon/facebook/facebook.php', 'facebook_plugin_settings');
+ unregister_hook('cron', 'addon/facebook/facebook.php', 'facebook_cron');
+ unregister_hook('enotify', 'addon/facebook/facebook.php', 'facebook_enotify');
+ unregister_hook('queue_predeliver', 'addon/facebook/facebook.php', 'fb_queue_hook');
- // hook moved
- unregister_hook('post_local_end', 'addon/facebook/facebook.php', 'facebook_post_hook');
- unregister_hook('plugin_settings', 'addon/facebook/facebook.php', 'facebook_plugin_settings');
+ // hook moved
+ unregister_hook('post_local_end', 'addon/facebook/facebook.php', 'facebook_post_hook');
+ unregister_hook('plugin_settings', 'addon/facebook/facebook.php', 'facebook_plugin_settings');
}
@@ -96,199 +96,195 @@ function facebook_module() {}
// If $_REQUEST["realtime_cb"] is set, this is a callback from the Real-Time Updates API
function facebook_init(&$a) {
-
- if (x($_REQUEST, "realtime_cb") && x($_REQUEST, "realtime_cb")) {
- logger("facebook_init: Facebook Real-Time callback called", LOGGER_DEBUG);
-
- if (x($_REQUEST, "hub_verify_token")) {
- // this is the verification callback while registering for real time updates
-
- $verify_token = get_config('facebook', 'cb_verify_token');
- if ($verify_token != $_REQUEST["hub_verify_token"]) {
- logger('facebook_init: Wrong Facebook Callback Verifier - expected ' . $verify_token . ', got ' . $_REQUEST["hub_verify_token"]);
- return;
- }
-
- if (x($_REQUEST, "hub_challenge")) {
- logger('facebook_init: Answering Challenge: ' . $_REQUEST["hub_challenge"], LOGGER_DATA);
- echo $_REQUEST["hub_challenge"];
- die();
- }
- }
-
- require_once('include/items.php');
-
- // this is a status update
- $content = file_get_contents("php://input");
- if (is_numeric($content)) $content = file_get_contents("php://input");
- $js = json_decode($content);
- logger(print_r($js, true), LOGGER_DATA);
-
- if (!isset($js->object) || $js->object != "user" || !isset($js->entry)) {
- logger('facebook_init: Could not parse Real-Time Update data', LOGGER_DEBUG);
- return;
- }
-
- $affected_users = array("feed" => array(), "friends" => array());
-
- foreach ($js->entry as $entry) {
- $fbuser = $entry->uid;
- foreach ($entry->changed_fields as $field) {
- if (!isset($affected_users[$field])) {
- logger('facebook_init: Unknown field "' . $field . '"');
- continue;
- }
- if (in_array($fbuser, $affected_users[$field])) continue;
-
- $r = q("SELECT `uid` FROM `pconfig` WHERE `cat` = 'facebook' AND `k` = 'self_id' AND `v` = '%s' LIMIT 1", dbesc($fbuser));
- if(! count($r))
- continue;
- $uid = $r[0]['uid'];
-
- $access_token = get_pconfig($uid,'facebook','access_token');
- if(! $access_token)
- return;
-
- switch ($field) {
- case "feed":
- logger('facebook_init: FB-User ' . $fbuser . ' / feed', LOGGER_DEBUG);
-
- if(! get_pconfig($uid,'facebook','no_wall')) {
- $private_wall = intval(get_pconfig($uid,'facebook','private_wall'));
- $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token);
- if($s) {
- $j = json_decode($s);
- if (isset($j->data)) {
- logger('facebook_init: wall: ' . print_r($j,true), LOGGER_DATA);
- fb_consume_stream($uid,$j,($private_wall) ? false : true);
- } else {
- logger('facebook_init: wall: got no data from Facebook: ' . print_r($j,true), LOGGER_NORMAL);
- }
- }
- }
-
- break;
- case "friends":
- logger('facebook_init: FB-User ' . $fbuser . ' / friends', LOGGER_DEBUG);
-
- fb_get_friends($uid, false);
- set_pconfig($uid,'facebook','friend_check',time());
- break;
- default:
- logger('facebook_init: Unknown callback field for ' . $fbuser, LOGGER_NORMAL);
- }
- $affected_users[$field][] = $fbuser;
- }
- }
- }
-
- if($a->argc != 2)
- return;
- $nick = $a->argv[1];
- if(strlen($nick))
- $r = q("SELECT `uid` FROM `user` WHERE `nickname` = '%s' LIMIT 1",
- dbesc($nick)
- );
- if(! count($r))
- return;
+ if (x($_REQUEST, "realtime_cb") && x($_REQUEST, "realtime_cb")) {
+ logger("facebook_init: Facebook Real-Time callback called", LOGGER_DEBUG);
- $uid = $r[0]['uid'];
- $auth_code = (x($_GET, 'code') ? $_GET['code'] : '');
- $error = (x($_GET, 'error_description') ? $_GET['error_description'] : '');
+ if (x($_REQUEST, "hub_verify_token")) {
+ // this is the verification callback while registering for real time updates
+
+ $verify_token = get_config('facebook', 'cb_verify_token');
+ if ($verify_token != $_REQUEST["hub_verify_token"]) {
+ logger('facebook_init: Wrong Facebook Callback Verifier - expected ' . $verify_token . ', got ' . $_REQUEST["hub_verify_token"]);
+ return;
+ }
+
+ if (x($_REQUEST, "hub_challenge")) {
+ logger('facebook_init: Answering Challenge: ' . $_REQUEST["hub_challenge"], LOGGER_DATA);
+ echo $_REQUEST["hub_challenge"];
+ die();
+ }
+ }
+
+ require_once('include/items.php');
+
+ // this is a status update
+ $content = file_get_contents("php://input");
+ if (is_numeric($content)) $content = file_get_contents("php://input");
+ $js = json_decode($content);
+ logger(print_r($js, true), LOGGER_DATA);
+
+ if (!isset($js->object) || $js->object != "user" || !isset($js->entry)) {
+ logger('facebook_init: Could not parse Real-Time Update data', LOGGER_DEBUG);
+ return;
+ }
+
+ $affected_users = array("feed" => array(), "friends" => array());
+
+ foreach ($js->entry as $entry) {
+ $fbuser = $entry->uid;
+ foreach ($entry->changed_fields as $field) {
+ if (!isset($affected_users[$field])) {
+ logger('facebook_init: Unknown field "' . $field . '"');
+ continue;
+ }
+ if (in_array($fbuser, $affected_users[$field])) continue;
+
+ $r = q("SELECT `uid` FROM `pconfig` WHERE `cat` = 'facebook' AND `k` = 'self_id' AND `v` = '%s' LIMIT 1", dbesc($fbuser));
+ if(! count($r))
+ continue;
+ $uid = $r[0]['uid'];
+
+ $access_token = get_pconfig($uid,'facebook','access_token');
+ if(! $access_token)
+ return;
+
+ switch ($field) {
+ case "feed":
+ logger('facebook_init: FB-User ' . $fbuser . ' / feed', LOGGER_DEBUG);
+
+ if(! get_pconfig($uid,'facebook','no_wall')) {
+ $private_wall = intval(get_pconfig($uid,'facebook','private_wall'));
+ $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token);
+ if($s) {
+ $j = json_decode($s);
+ if (isset($j->data)) {
+ logger('facebook_init: wall: ' . print_r($j,true), LOGGER_DATA);
+ fb_consume_stream($uid,$j,($private_wall) ? false : true);
+ } else {
+ logger('facebook_init: wall: got no data from Facebook: ' . print_r($j,true), LOGGER_NORMAL);
+ }
+ }
+ }
+
+ break;
+ case "friends":
+ logger('facebook_init: FB-User ' . $fbuser . ' / friends', LOGGER_DEBUG);
+
+ fb_get_friends($uid, false);
+ set_pconfig($uid,'facebook','friend_check',time());
+ break;
+ default:
+ logger('facebook_init: Unknown callback field for ' . $fbuser, LOGGER_NORMAL);
+ }
+ $affected_users[$field][] = $fbuser;
+ }
+ }
+ }
- if($error)
- logger('facebook_init: Error: ' . $error);
+ if($a->argc != 2)
+ return;
+ $nick = $a->argv[1];
+ if(strlen($nick))
+ $r = q("SELECT `uid` FROM `user` WHERE `nickname` = '%s' LIMIT 1",
+ dbesc($nick)
+ );
+ if(! count($r))
+ return;
- if($auth_code && $uid) {
+ $uid = $r[0]['uid'];
+ $auth_code = (x($_GET, 'code') ? $_GET['code'] : '');
+ $error = (x($_GET, 'error_description') ? $_GET['error_description'] : '');
- $appid = get_config('facebook','appid');
- $appsecret = get_config('facebook', 'appsecret');
+ if($error)
+ logger('facebook_init: Error: ' . $error);
- $x = fetch_url('https://graph.facebook.com/oauth/access_token?client_id='
- . $appid . '&client_secret=' . $appsecret . '&redirect_uri='
- . urlencode($a->get_baseurl() . '/facebook/' . $nick)
- . '&code=' . $auth_code);
+ if($auth_code && $uid) {
- logger('facebook_init: returned access token: ' . $x, LOGGER_DATA);
+ $appid = get_config('facebook','appid');
+ $appsecret = get_config('facebook', 'appsecret');
- if(strpos($x,'access_token=') !== false) {
- $token = str_replace('access_token=', '', $x);
- if(strpos($token,'&') !== false)
- $token = substr($token,0,strpos($token,'&'));
- set_pconfig($uid,'facebook','access_token',$token);
- set_pconfig($uid,'facebook','post','1');
- if(get_pconfig($uid,'facebook','no_linking') === false)
- set_pconfig($uid,'facebook','no_linking',1);
- fb_get_self($uid);
- fb_get_friends($uid, true);
- fb_consume_all($uid);
+ $x = fetch_url('https://graph.facebook.com/oauth/access_token?client_id='
+ . $appid . '&client_secret=' . $appsecret . '&redirect_uri='
+ . urlencode($a->get_baseurl() . '/facebook/' . $nick)
+ . '&code=' . $auth_code);
- }
+ logger('facebook_init: returned access token: ' . $x, LOGGER_DATA);
- }
+ if(strpos($x,'access_token=') !== false) {
+ $token = str_replace('access_token=', '', $x);
+ if(strpos($token,'&') !== false)
+ $token = substr($token,0,strpos($token,'&'));
+ set_pconfig($uid,'facebook','access_token',$token);
+ set_pconfig($uid,'facebook','post','1');
+ if(get_pconfig($uid,'facebook','no_linking') === false)
+ set_pconfig($uid,'facebook','no_linking',1);
+ fb_get_self($uid);
+ fb_get_friends($uid, true);
+ fb_consume_all($uid);
+
+ }
+
+ }
}
function fb_get_self($uid) {
- $access_token = get_pconfig($uid,'facebook','access_token');
- if(! $access_token)
- return;
- $s = fetch_url('https://graph.facebook.com/me/?access_token=' . $access_token);
- if($s) {
- $j = json_decode($s);
- set_pconfig($uid,'facebook','self_id',(string) $j->id);
- }
+ $access_token = get_pconfig($uid,'facebook','access_token');
+ if(! $access_token)
+ return;
+ $s = fetch_url('https://graph.facebook.com/me/?access_token=' . $access_token);
+ if($s) {
+ $j = json_decode($s);
+ set_pconfig($uid,'facebook','self_id',(string) $j->id);
+ }
}
-function fb_get_friends_sync_new($uid, $access_token, $person) {
- $link = 'http://facebook.com/profile.php?id=' . $person->id;
-
- $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' LIMIT 1",
- intval($uid),
- dbesc($link)
- );
-
- if (count($r) == 0) {
- logger('fb_get_friends: new contact found: ' . $link, LOGGER_DEBUG);
-
- fb_get_friends_sync_full($uid, $access_token, $person);
- }
+function fb_get_friends_sync_new($uid, $access_token, $persons) {
+ $persons_todo = array();
+ foreach ($persons as $person) {
+ $link = 'http://facebook.com/profile.php?id=' . $person->id;
+
+ $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' LIMIT 1",
+ intval($uid),
+ dbesc($link)
+ );
+
+ if (count($r) == 0) {
+ logger('fb_get_friends: new contact found: ' . $link, LOGGER_DEBUG);
+ $persons_todo[] = $person;
+ }
+
+ if (count($persons_todo) > 0) fb_get_friends_sync_full($uid, $access_token, $persons_todo);
+ }
}
-function fb_get_friends_sync_full($uid, $access_token, $person) {
- $s = fetch_url('https://graph.facebook.com/' . $person->id . '?access_token=' . $access_token);
- if($s) {
- $jp = json_decode($s);
- logger('fb_get_friends: info: ' . print_r($jp,true), LOGGER_DATA);
+function fb_get_friends_sync_parsecontact($uid, $contact) {
+ $contact->link = 'http://facebook.com/profile.php?id=' . $contact->id;
- // always use numeric link for consistency
+ // If its a page then set the first name from the username
+ if (!$contact->first_name and $contact->username)
+ $contact->first_name = $contact->username;
- $jp->link = 'http://facebook.com/profile.php?id=' . $person->id;
+ // check if we already have a contact
- // If its a page then set the first name from the username
- if (!$jp->first_name and $jp->username)
- $jp->first_name = $jp->username;
+ $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' LIMIT 1",
+ intval($uid),
+ dbesc($contact->link)
+ );
- // check if we already have a contact
+ if(count($r)) {
- $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' LIMIT 1",
- intval($uid),
- dbesc($jp->link)
- );
+ // check that we have all the photos, this has been known to fail on occasion
- if(count($r)) {
+ if((! $r[0]['photo']) || (! $r[0]['thumb']) || (! $r[0]['micro'])) {
+ require_once("Photo.php");
- // check that we have all the photos, this has been known to fail on occasion
+ $photos = import_profile_photo('https://graph.facebook.com/' . $contact->id . '/picture', $uid, $r[0]['id']);
- if((! $r[0]['photo']) || (! $r[0]['thumb']) || (! $r[0]['micro'])) {
- require_once("Photo.php");
-
- $photos = import_profile_photo('https://graph.facebook.com/' . $jp->id . '/picture', $uid, $r[0]['id']);
-
- $r = q("UPDATE `contact` SET `photo` = '%s',
+ $r = q("UPDATE `contact` SET `photo` = '%s',
`thumb` = '%s',
`micro` = '%s',
`name-date` = '%s',
@@ -296,59 +292,59 @@ function fb_get_friends_sync_full($uid, $access_token, $person) {
`avatar-date` = '%s'
WHERE `id` = %d LIMIT 1
",
- dbesc($photos[0]),
- dbesc($photos[1]),
- dbesc($photos[2]),
- dbesc(datetime_convert()),
- dbesc(datetime_convert()),
- dbesc(datetime_convert()),
- intval($r[0]['id'])
- );
- }
- return;
- }
- else {
+ dbesc($photos[0]),
+ dbesc($photos[1]),
+ dbesc($photos[2]),
+ dbesc(datetime_convert()),
+ dbesc(datetime_convert()),
+ dbesc(datetime_convert()),
+ intval($r[0]['id'])
+ );
+ }
+ return;
+ }
+ else {
- // create contact record
- $r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,
+ // create contact record
+ $r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,
`name`, `nick`, `photo`, `network`, `rel`, `priority`,
`writable`, `blocked`, `readonly`, `pending` )
VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ",
- intval($uid),
- dbesc(datetime_convert()),
- dbesc($jp->link),
- dbesc(normalise_link($jp->link)),
- dbesc(''),
- dbesc(''),
- dbesc($jp->id),
- dbesc('facebook ' . $jp->id),
- dbesc($jp->name),
- dbesc(($jp->nickname) ? $jp->nickname : strtolower($jp->first_name)),
- dbesc('https://graph.facebook.com/' . $jp->id . '/picture'),
- dbesc(NETWORK_FACEBOOK),
- intval(CONTACT_IS_FRIEND),
- intval(1),
- intval(1)
- );
- }
+ intval($uid),
+ dbesc(datetime_convert()),
+ dbesc($contact->link),
+ dbesc(normalise_link($contact->link)),
+ dbesc(''),
+ dbesc(''),
+ dbesc($contact->id),
+ dbesc('facebook ' . $contact->id),
+ dbesc($contact->name),
+ dbesc(($contact->nickname) ? $contact->nickname : strtolower($contact->first_name)),
+ dbesc('https://graph.facebook.com/' . $contact->id . '/picture'),
+ dbesc(NETWORK_FACEBOOK),
+ intval(CONTACT_IS_FRIEND),
+ intval(1),
+ intval(1)
+ );
+ }
- $r = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1",
- dbesc($jp->link),
- intval($uid)
- );
+ $r = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1",
+ dbesc($contact->link),
+ intval($uid)
+ );
- if(! count($r)) {
- return;
- }
+ if(! count($r)) {
+ return;
+ }
- $contact = $r[0];
- $contact_id = $r[0]['id'];
+ $contact = $r[0];
+ $contact_id = $r[0]['id'];
- require_once("Photo.php");
+ require_once("Photo.php");
- $photos = import_profile_photo($r[0]['photo'],$uid,$contact_id);
+ $photos = import_profile_photo($r[0]['photo'],$uid,$contact_id);
- $r = q("UPDATE `contact` SET `photo` = '%s',
+ $r = q("UPDATE `contact` SET `photo` = '%s',
`thumb` = '%s',
`micro` = '%s',
`name-date` = '%s',
@@ -356,49 +352,68 @@ function fb_get_friends_sync_full($uid, $access_token, $person) {
`avatar-date` = '%s'
WHERE `id` = %d LIMIT 1
",
- dbesc($photos[0]),
- dbesc($photos[1]),
- dbesc($photos[2]),
- dbesc(datetime_convert()),
- dbesc(datetime_convert()),
- dbesc(datetime_convert()),
- intval($contact_id)
- );
+ dbesc($photos[0]),
+ dbesc($photos[1]),
+ dbesc($photos[2]),
+ dbesc(datetime_convert()),
+ dbesc(datetime_convert()),
+ dbesc(datetime_convert()),
+ intval($contact_id)
+ );
+}
- }
+function fb_get_friends_sync_full($uid, $access_token, $persons) {
+ if (count($persons) == 0) return;
+ $nums = Ceil(count($persons) / 50);
+ for ($i = 0; $i < $nums; $i++) {
+ $batch_request = array();
+ for ($j = $i * 50; $j < ($i+1) * 50 && $j < count($persons); $j++) $batch_request[] = array('method'=>'GET', 'relative_url'=>$persons[$j]->id);
+ $s = post_url('https://graph.facebook.com/', array('access_token' => $access_token, 'batch' => json_encode($batch_request)));
+ if($s) {
+ $results = json_decode($s);
+ logger('fb_get_friends: info: ' . print_r($results,true), LOGGER_DATA);
+ foreach ($results as $contact) {
+ if ($contact->code != 200) logger('fb_get_friends: not found: ' . print_r($contact,true), LOGGER_DEBUG);
+ else fb_get_friends_sync_parsecontact($uid, json_decode($contact->body));
+ }
+ }
+ }
}
// if $fullsync is true, only new contacts are searched for
function fb_get_friends($uid, $fullsync = true) {
- $r = q("SELECT `uid` FROM `user` WHERE `uid` = %d AND `account_expired` = 0 LIMIT 1",
- intval($uid)
- );
- if(! count($r))
- return;
+ $r = q("SELECT `uid` FROM `user` WHERE `uid` = %d AND `account_expired` = 0 LIMIT 1",
+ intval($uid)
+ );
+ if(! count($r))
+ return;
- $access_token = get_pconfig($uid,'facebook','access_token');
+ $access_token = get_pconfig($uid,'facebook','access_token');
- $no_linking = get_pconfig($uid,'facebook','no_linking');
- if($no_linking)
- return;
+ $no_linking = get_pconfig($uid,'facebook','no_linking');
+ if($no_linking)
+ return;
- if(! $access_token)
- return;
- $s = fetch_url('https://graph.facebook.com/me/friends?access_token=' . $access_token);
- if($s) {
- logger('facebook: fb_get_friends: ' . $s, LOGGER_DATA);
- $j = json_decode($s);
- logger('facebook: fb_get_friends: json: ' . print_r($j,true), LOGGER_DATA);
- if(! $j->data)
- return;
- foreach($j->data as $person)
- if ($fullsync)
- fb_get_friends_sync_full($uid, $access_token, $person);
- else
- fb_get_friends_sync_new($uid, $access_token, $person);
- }
+ if(! $access_token)
+ return;
+ $s = fetch_url('https://graph.facebook.com/me/friends?access_token=' . $access_token);
+ if($s) {
+ logger('facebook: fb_get_friends: ' . $s, LOGGER_DATA);
+ $j = json_decode($s);
+ logger('facebook: fb_get_friends: json: ' . print_r($j,true), LOGGER_DATA);
+ if(! $j->data)
+ return;
+
+ $persons_todo = array();
+ foreach($j->data as $person) $persons_todo[] = $person;
+
+ if ($fullsync)
+ fb_get_friends_sync_full($uid, $access_token, $persons_todo);
+ else
+ fb_get_friends_sync_new($uid, $access_token, $persons_todo);
+ }
}
// This is the POST method to the facebook settings page
@@ -406,232 +421,232 @@ function fb_get_friends($uid, $fullsync = true) {
function facebook_post(&$a) {
- $uid = local_user();
- if($uid){
+ $uid = local_user();
+ if($uid){
- $value = ((x($_POST,'post_by_default')) ? intval($_POST['post_by_default']) : 0);
- set_pconfig($uid,'facebook','post_by_default', $value);
+ $value = ((x($_POST,'post_by_default')) ? intval($_POST['post_by_default']) : 0);
+ set_pconfig($uid,'facebook','post_by_default', $value);
- $no_linking = get_pconfig($uid,'facebook','no_linking');
+ $no_linking = get_pconfig($uid,'facebook','no_linking');
- $no_wall = ((x($_POST,'facebook_no_wall')) ? intval($_POST['facebook_no_wall']) : 0);
- set_pconfig($uid,'facebook','no_wall',$no_wall);
+ $no_wall = ((x($_POST,'facebook_no_wall')) ? intval($_POST['facebook_no_wall']) : 0);
+ set_pconfig($uid,'facebook','no_wall',$no_wall);
- $private_wall = ((x($_POST,'facebook_private_wall')) ? intval($_POST['facebook_private_wall']) : 0);
- set_pconfig($uid,'facebook','private_wall',$private_wall);
-
+ $private_wall = ((x($_POST,'facebook_private_wall')) ? intval($_POST['facebook_private_wall']) : 0);
+ set_pconfig($uid,'facebook','private_wall',$private_wall);
- set_pconfig($uid,'facebook','blocked_apps',escape_tags(trim($_POST['blocked_apps'])));
- $linkvalue = ((x($_POST,'facebook_linking')) ? intval($_POST['facebook_linking']) : 0);
- set_pconfig($uid,'facebook','no_linking', (($linkvalue) ? 0 : 1));
+ set_pconfig($uid,'facebook','blocked_apps',escape_tags(trim($_POST['blocked_apps'])));
- // FB linkage was allowed but has just been turned off - remove all FB contacts and posts
+ $linkvalue = ((x($_POST,'facebook_linking')) ? intval($_POST['facebook_linking']) : 0);
+ set_pconfig($uid,'facebook','no_linking', (($linkvalue) ? 0 : 1));
- if((! intval($no_linking)) && (! intval($linkvalue))) {
- $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `network` = '%s' ",
- intval($uid),
- dbesc(NETWORK_FACEBOOK)
- );
- if(count($r)) {
- require_once('include/Contact.php');
- foreach($r as $rr)
- contact_remove($rr['id']);
- }
- }
- elseif(intval($no_linking) && intval($linkvalue)) {
- // FB linkage is now allowed - import stuff.
- fb_get_self($uid);
- fb_get_friends($uid, true);
- fb_consume_all($uid);
- }
+ // FB linkage was allowed but has just been turned off - remove all FB contacts and posts
- info( t('Settings updated.') . EOL);
- }
+ if((! intval($no_linking)) && (! intval($linkvalue))) {
+ $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `network` = '%s' ",
+ intval($uid),
+ dbesc(NETWORK_FACEBOOK)
+ );
+ if(count($r)) {
+ require_once('include/Contact.php');
+ foreach($r as $rr)
+ contact_remove($rr['id']);
+ }
+ }
+ elseif(intval($no_linking) && intval($linkvalue)) {
+ // FB linkage is now allowed - import stuff.
+ fb_get_self($uid);
+ fb_get_friends($uid, true);
+ fb_consume_all($uid);
+ }
- return;
+ info( t('Settings updated.') . EOL);
+ }
+
+ return;
}
// Facebook settings form
function facebook_content(&$a) {
- if(! local_user()) {
- notice( t('Permission denied.') . EOL);
- return '';
- }
+ if(! local_user()) {
+ notice( t('Permission denied.') . EOL);
+ return '';
+ }
- if($a->argc > 1 && $a->argv[1] === 'remove') {
- del_pconfig(local_user(),'facebook','post');
- info( t('Facebook disabled') . EOL);
- }
+ if($a->argc > 1 && $a->argv[1] === 'remove') {
+ del_pconfig(local_user(),'facebook','post');
+ info( t('Facebook disabled') . EOL);
+ }
- if($a->argc > 1 && $a->argv[1] === 'friends') {
- fb_get_friends(local_user(), true);
- info( t('Updating contacts') . EOL);
- }
+ if($a->argc > 1 && $a->argv[1] === 'friends') {
+ fb_get_friends(local_user(), true);
+ info( t('Updating contacts') . EOL);
+ }
- $o = '';
-
- $fb_installed = false;
- if (get_pconfig(local_user(),'facebook','post')) {
- $access_token = get_pconfig(local_user(),'facebook','access_token');
- if ($access_token) {
- $private_wall = intval(get_pconfig($uid,'facebook','private_wall'));
- $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token);
- if($s) {
- $j = json_decode($s);
- if (isset($j->data)) $fb_installed = true;
- }
- }
- }
-
- $appid = get_config('facebook','appid');
+ $o = '';
- if(! $appid) {
- notice( t('Facebook API key is missing.') . EOL);
- return '';
- }
+ $fb_installed = false;
+ if (get_pconfig(local_user(),'facebook','post')) {
+ $access_token = get_pconfig(local_user(),'facebook','access_token');
+ if ($access_token) {
+ $private_wall = intval(get_pconfig(local_user(),'facebook','private_wall'));
+ $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token);
+ if($s) {
+ $j = json_decode($s);
+ if (isset($j->data)) $fb_installed = true;
+ }
+ }
+ }
- $a->page['htmlhead'] .= ' ' . "\r\n";
+ $appid = get_config('facebook','appid');
- $o .= '' . t('Facebook Connect') . ' ';
+ if(! $appid) {
+ notice( t('Facebook API key is missing.') . EOL);
+ return '';
+ }
- if(! $fb_installed) {
- $o .= '';
- }
+ $o .= '' . t('Facebook Connect') . ' ';
- if($fb_installed) {
- $o .= '';
+ }
- $o .= '';
+ }
- return $o;
+ return $o;
}
function facebook_cron($a,$b) {
- $last = get_config('facebook','last_poll');
-
- $poll_interval = intval(get_config('facebook','poll_interval'));
- if(! $poll_interval)
- $poll_interval = FACEBOOK_DEFAULT_POLL_INTERVAL;
+ $last = get_config('facebook','last_poll');
- if($last) {
- $next = $last + $poll_interval;
- if($next > time())
- return;
- }
+ $poll_interval = intval(get_config('facebook','poll_interval'));
+ if(! $poll_interval)
+ $poll_interval = FACEBOOK_DEFAULT_POLL_INTERVAL;
- logger('facebook_cron');
+ if($last) {
+ $next = $last + $poll_interval;
+ if($next > time())
+ return;
+ }
+
+ logger('facebook_cron');
- // Find the FB users on this site and randomize in case one of them
- // uses an obscene amount of memory. It may kill this queue run
- // but hopefully we'll get a few others through on each run.
+ // Find the FB users on this site and randomize in case one of them
+ // uses an obscene amount of memory. It may kill this queue run
+ // but hopefully we'll get a few others through on each run.
- $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'facebook' AND `k` = 'post' AND `v` = '1' ORDER BY RAND() ");
- if(count($r)) {
- foreach($r as $rr) {
- if(get_pconfig($rr['uid'],'facebook','no_linking'))
- continue;
- $ab = intval(get_config('system','account_abandon_days'));
- if($ab > 0) {
- $z = q("SELECT `uid` FROM `user` WHERE `uid` = %d AND `login_date` > UTC_TIMESTAMP() - INTERVAL %d DAY LIMIT 1",
- intval($rr['uid']),
- intval($ab)
- );
- if(! count($z))
- continue;
- }
+ $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'facebook' AND `k` = 'post' AND `v` = '1' ORDER BY RAND() ");
+ if(count($r)) {
+ foreach($r as $rr) {
+ if(get_pconfig($rr['uid'],'facebook','no_linking'))
+ continue;
+ $ab = intval(get_config('system','account_abandon_days'));
+ if($ab > 0) {
+ $z = q("SELECT `uid` FROM `user` WHERE `uid` = %d AND `login_date` > UTC_TIMESTAMP() - INTERVAL %d DAY LIMIT 1",
+ intval($rr['uid']),
+ intval($ab)
+ );
+ if(! count($z))
+ continue;
+ }
- // check for new friends once a day
- $last_friend_check = get_pconfig($rr['uid'],'facebook','friend_check');
- if($last_friend_check)
- $next_friend_check = $last_friend_check + 86400;
- if($next_friend_check <= time()) {
- fb_get_friends($rr['uid'], true);
- set_pconfig($rr['uid'],'facebook','friend_check',time());
- }
- fb_consume_all($rr['uid']);
- }
- }
-
- if (get_config('facebook', 'realtime_active') == 1) {
- if (!facebook_check_realtime_active()) {
-
- logger('facebook_cron: Facebook is not sending Real-Time Updates any more, although it is supposed to. Trying to fix it...', LOGGER_NORMAL);
- facebook_subscription_add_users();
-
- if (facebook_check_realtime_active())
- logger('facebook_cron: Successful', LOGGER_NORMAL);
- else {
- logger('facebook_cron: Failed', LOGGER_NORMAL);
-
- if(strlen($a->config['admin_email']) && !get_config('facebook', 'realtime_err_mailsent')) {
- $res = mail($a->config['admin_email'], t('Problems with Facebook Real-Time Updates'),
- "Hi!\n\nThere's a problem with the Facebook Real-Time Updates that cannot be solved automatically. Maybe a permission issue?\n\nPlease try to re-activate it on " . $a->config["system"]["url"] . "/admin/plugins/facebook\n\nThis e-mail will only be sent once.",
- 'From: ' . t('Administrator') . '@' . $_SERVER['SERVER_NAME'] . "\n"
- . 'Content-type: text/plain; charset=UTF-8' . "\n"
- . 'Content-transfer-encoding: 8bit'
- );
-
- set_config('facebook', 'realtime_err_mailsent', 1);
- }
- }
- } else { // !facebook_check_realtime_active()
- del_config('facebook', 'realtime_err_mailsent');
- }
- }
-
- set_config('facebook','last_poll', time());
+ // check for new friends once a day
+ $last_friend_check = get_pconfig($rr['uid'],'facebook','friend_check');
+ if($last_friend_check)
+ $next_friend_check = $last_friend_check + 86400;
+ if($next_friend_check <= time()) {
+ fb_get_friends($rr['uid'], true);
+ set_pconfig($rr['uid'],'facebook','friend_check',time());
+ }
+ fb_consume_all($rr['uid']);
+ }
+ }
+
+ if (get_config('facebook', 'realtime_active') == 1) {
+ if (!facebook_check_realtime_active()) {
+
+ logger('facebook_cron: Facebook is not sending Real-Time Updates any more, although it is supposed to. Trying to fix it...', LOGGER_NORMAL);
+ facebook_subscription_add_users();
+
+ if (facebook_check_realtime_active())
+ logger('facebook_cron: Successful', LOGGER_NORMAL);
+ else {
+ logger('facebook_cron: Failed', LOGGER_NORMAL);
+
+ if(strlen($a->config['admin_email']) && !get_config('facebook', 'realtime_err_mailsent')) {
+ $res = mail($a->config['admin_email'], t('Problems with Facebook Real-Time Updates'),
+ "Hi!\n\nThere's a problem with the Facebook Real-Time Updates that cannot be solved automatically. Maybe a permission issue?\n\nPlease try to re-activate it on " . $a->config["system"]["url"] . "/admin/plugins/facebook\n\nThis e-mail will only be sent once.",
+ 'From: ' . t('Administrator') . '@' . $_SERVER['SERVER_NAME'] . "\n"
+ . 'Content-type: text/plain; charset=UTF-8' . "\n"
+ . 'Content-transfer-encoding: 8bit'
+ );
+
+ set_config('facebook', 'realtime_err_mailsent', 1);
+ }
+ }
+ } else { // !facebook_check_realtime_active()
+ del_config('facebook', 'realtime_err_mailsent');
+ }
+ }
+
+ set_config('facebook','last_poll', time());
}
@@ -639,1118 +654,1118 @@ function facebook_cron($a,$b) {
function facebook_plugin_settings(&$a,&$b) {
- $b .= '';
+ $b .= '';
}
function facebook_plugin_admin(&$a, &$o){
- $o = ' ';
-
- $o .= '' . t('Facebook API Key') . ' ';
-
- $appid = get_config('facebook', 'appid' );
- $appsecret = get_config('facebook', 'appsecret' );
- $poll_interval = get_config('facebook', 'poll_interval' );
- if (!$poll_interval) $poll_interval = FACEBOOK_DEFAULT_POLL_INTERVAL;
-
- $ret1 = q("SELECT `v` FROM `config` WHERE `cat` = 'facebook' AND `k` = 'appid' LIMIT 1");
- $ret2 = q("SELECT `v` FROM `config` WHERE `cat` = 'facebook' AND `k` = 'appsecret' LIMIT 1");
- if ((count($ret1) > 0 && $ret1[0]['v'] != $appid) || (count($ret2) > 0 && $ret2[0]['v'] != $appsecret)) $o .= t('Error: it appears that you have specified the App-ID and -Secret in your .htconfig.php file. As long as they are specified there, they cannot be set using this form. ');
-
- $working_connection = false;
- if ($appid && $appsecret) {
- $subs = facebook_subscriptions_get();
- if ($subs === null) $o .= t('Error: the given API Key seems to be incorrect (the application access token could not be retrieved).') . ' ';
- elseif (is_array($subs)) {
- $o .= t('The given API Key seems to work correctly.') . ' ';
- $working_connection = true;
- } else $o .= t('The correctness of the API Key could not be detected. Somthing strange\'s going on.') . ' ';
- }
-
- $o .= '' . t('App-ID / API-Key') . ' ';
- $o .= '' . t('Application secret') . ' ';
- $o .= '' . sprintf(t('Polling Interval (min. %1$s minutes)'), FACEBOOK_MIN_POLL_INTERVAL) . ' ';
- $o .= ' ';
-
- if ($working_connection) {
- $o .= '' . t('Real-Time Updates') . ' ';
-
- $activated = facebook_check_realtime_active();
- if ($activated) {
- $o .= t('Real-Time Updates are activated.') . ' ';
- $o .= ' ';
- } else {
- $o .= t('Real-Time Updates not activated.') . ' ';
- }
- }
+ $o = ' ';
+
+ $o .= '' . t('Facebook API Key') . ' ';
+
+ $appid = get_config('facebook', 'appid' );
+ $appsecret = get_config('facebook', 'appsecret' );
+ $poll_interval = get_config('facebook', 'poll_interval' );
+ if (!$poll_interval) $poll_interval = FACEBOOK_DEFAULT_POLL_INTERVAL;
+
+ $ret1 = q("SELECT `v` FROM `config` WHERE `cat` = 'facebook' AND `k` = 'appid' LIMIT 1");
+ $ret2 = q("SELECT `v` FROM `config` WHERE `cat` = 'facebook' AND `k` = 'appsecret' LIMIT 1");
+ if ((count($ret1) > 0 && $ret1[0]['v'] != $appid) || (count($ret2) > 0 && $ret2[0]['v'] != $appsecret)) $o .= t('Error: it appears that you have specified the App-ID and -Secret in your .htconfig.php file. As long as they are specified there, they cannot be set using this form. ');
+
+ $working_connection = false;
+ if ($appid && $appsecret) {
+ $subs = facebook_subscriptions_get();
+ if ($subs === null) $o .= t('Error: the given API Key seems to be incorrect (the application access token could not be retrieved).') . ' ';
+ elseif (is_array($subs)) {
+ $o .= t('The given API Key seems to work correctly.') . ' ';
+ $working_connection = true;
+ } else $o .= t('The correctness of the API Key could not be detected. Somthing strange\'s going on.') . ' ';
+ }
+
+ $o .= '' . t('App-ID / API-Key') . ' ';
+ $o .= '' . t('Application secret') . ' ';
+ $o .= '' . sprintf(t('Polling Interval (min. %1$s minutes)'), FACEBOOK_MIN_POLL_INTERVAL) . ' ';
+ $o .= ' ';
+
+ if ($working_connection) {
+ $o .= '' . t('Real-Time Updates') . ' ';
+
+ $activated = facebook_check_realtime_active();
+ if ($activated) {
+ $o .= t('Real-Time Updates are activated.') . ' ';
+ $o .= ' ';
+ } else {
+ $o .= t('Real-Time Updates not activated.') . ' ';
+ }
+ }
}
function facebook_plugin_admin_post(&$a, &$o){
- check_form_security_token_redirectOnErr('/admin/plugins/facebook', 'fbsave');
-
- if (x($_REQUEST,'fb_save_keys')) {
- set_config('facebook', 'appid', $_REQUEST['appid']);
- set_config('facebook', 'appsecret', $_REQUEST['appsecret']);
- $poll_interval = IntVal($_REQUEST['poll_interval']);
- if ($poll_interval >= FACEBOOK_MIN_POLL_INTERVAL) set_config('facebook', 'poll_interval', $poll_interval);
- del_config('facebook', 'app_access_token');
- info(t('The new values have been saved.'));
- }
- if (x($_REQUEST,'real_time_activate')) {
- facebook_subscription_add_users();
- }
- if (x($_REQUEST,'real_time_deactivate')) {
- facebook_subscription_del_users();
- }
+ check_form_security_token_redirectOnErr('/admin/plugins/facebook', 'fbsave');
+
+ if (x($_REQUEST,'fb_save_keys')) {
+ set_config('facebook', 'appid', $_REQUEST['appid']);
+ set_config('facebook', 'appsecret', $_REQUEST['appsecret']);
+ $poll_interval = IntVal($_REQUEST['poll_interval']);
+ if ($poll_interval >= FACEBOOK_MIN_POLL_INTERVAL) set_config('facebook', 'poll_interval', $poll_interval);
+ del_config('facebook', 'app_access_token');
+ info(t('The new values have been saved.'));
+ }
+ if (x($_REQUEST,'real_time_activate')) {
+ facebook_subscription_add_users();
+ }
+ if (x($_REQUEST,'real_time_deactivate')) {
+ facebook_subscription_del_users();
+ }
}
function facebook_jot_nets(&$a,&$b) {
- if(! local_user())
- return;
+ if(! local_user())
+ return;
- $fb_post = get_pconfig(local_user(),'facebook','post');
- if(intval($fb_post) == 1) {
- $fb_defpost = get_pconfig(local_user(),'facebook','post_by_default');
- $selected = ((intval($fb_defpost) == 1) ? ' checked="checked" ' : '');
- $b .= ' '
- . t('Post to Facebook') . '
';
- }
+ $fb_post = get_pconfig(local_user(),'facebook','post');
+ if(intval($fb_post) == 1) {
+ $fb_defpost = get_pconfig(local_user(),'facebook','post_by_default');
+ $selected = ((intval($fb_defpost) == 1) ? ' checked="checked" ' : '');
+ $b .= ' '
+ . t('Post to Facebook') . '
';
+ }
}
function facebook_post_hook(&$a,&$b) {
- if($b['deleted'] || ($b['created'] !== $b['edited']))
- return;
+ if($b['deleted'] || ($b['created'] !== $b['edited']))
+ return;
- /**
- * Post to Facebook stream
- */
+ /**
+ * Post to Facebook stream
+ */
- require_once('include/group.php');
- require_once('include/html2plain.php');
+ require_once('include/group.php');
+ require_once('include/html2plain.php');
- logger('Facebook post');
+ logger('Facebook post');
- $reply = false;
- $likes = false;
+ $reply = false;
+ $likes = false;
- $toplevel = (($b['id'] == $b['parent']) ? true : false);
+ $toplevel = (($b['id'] == $b['parent']) ? true : false);
- $linking = ((get_pconfig($b['uid'],'facebook','no_linking')) ? 0 : 1);
+ $linking = ((get_pconfig($b['uid'],'facebook','no_linking')) ? 0 : 1);
- if((! $toplevel) && ($linking)) {
- $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
- intval($b['parent']),
- intval($b['uid'])
- );
- if(count($r) && substr($r[0]['uri'],0,4) === 'fb::')
- $reply = substr($r[0]['uri'],4);
- elseif(count($r) && substr($r[0]['extid'],0,4) === 'fb::')
- $reply = substr($r[0]['extid'],4);
- else
- return;
+ if((! $toplevel) && ($linking)) {
+ $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
+ intval($b['parent']),
+ intval($b['uid'])
+ );
+ if(count($r) && substr($r[0]['uri'],0,4) === 'fb::')
+ $reply = substr($r[0]['uri'],4);
+ elseif(count($r) && substr($r[0]['extid'],0,4) === 'fb::')
+ $reply = substr($r[0]['extid'],4);
+ else
+ return;
- $u = q("SELECT * FROM user where uid = %d limit 1",
- intval($b['uid'])
- );
- if(! count($u))
- return;
+ $u = q("SELECT * FROM user where uid = %d limit 1",
+ intval($b['uid'])
+ );
+ if(! count($u))
+ return;
- // only accept comments from the item owner. Other contacts are unknown to FB.
-
- if(! link_compare($b['author-link'], $a->get_baseurl() . '/profile/' . $u[0]['nickname']))
- return;
-
+ // only accept comments from the item owner. Other contacts are unknown to FB.
- logger('facebook reply id=' . $reply);
- }
+ if(! link_compare($b['author-link'], $a->get_baseurl() . '/profile/' . $u[0]['nickname']))
+ return;
- if(strstr($b['postopts'],'facebook') || ($b['private']) || ($reply)) {
- if($b['private'] && $reply === false) {
- $allow_people = expand_acl($b['allow_cid']);
- $allow_groups = expand_groups(expand_acl($b['allow_gid']));
- $deny_people = expand_acl($b['deny_cid']);
- $deny_groups = expand_groups(expand_acl($b['deny_gid']));
+ logger('facebook reply id=' . $reply);
+ }
- $recipients = array_unique(array_merge($allow_people,$allow_groups));
- $deny = array_unique(array_merge($deny_people,$deny_groups));
+ if(strstr($b['postopts'],'facebook') || ($b['private']) || ($reply)) {
- $allow_str = dbesc(implode(', ',$recipients));
- if($allow_str) {
- $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $allow_str ) AND `network` = 'face'");
- $allow_arr = array();
- if(count($r))
- foreach($r as $rr)
- $allow_arr[] = $rr['notify'];
- }
+ if($b['private'] && $reply === false) {
+ $allow_people = expand_acl($b['allow_cid']);
+ $allow_groups = expand_groups(expand_acl($b['allow_gid']));
+ $deny_people = expand_acl($b['deny_cid']);
+ $deny_groups = expand_groups(expand_acl($b['deny_gid']));
- $deny_str = dbesc(implode(', ',$deny));
- if($deny_str) {
- $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $deny_str ) AND `network` = 'face'");
- $deny_arr = array();
- if(count($r))
- foreach($r as $rr)
- $deny_arr[] = $rr['notify'];
- }
+ $recipients = array_unique(array_merge($allow_people,$allow_groups));
+ $deny = array_unique(array_merge($deny_people,$deny_groups));
- if(count($deny_arr) && (! count($allow_arr))) {
+ $allow_str = dbesc(implode(', ',$recipients));
+ if($allow_str) {
+ $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $allow_str ) AND `network` = 'face'");
+ $allow_arr = array();
+ if(count($r))
+ foreach($r as $rr)
+ $allow_arr[] = $rr['notify'];
+ }
- // One or more FB folks were denied access but nobody on FB was specifically allowed access.
- // This might cause the post to be open to public on Facebook, but only to selected members
- // on another network. Since this could potentially leak a post to somebody who was denied,
- // we will skip posting it to Facebook with a slightly vague but relevant message that will
- // hopefully lead somebody to this code comment for a better explanation of what went wrong.
+ $deny_str = dbesc(implode(', ',$deny));
+ if($deny_str) {
+ $r = q("SELECT `notify` FROM `contact` WHERE `id` IN ( $deny_str ) AND `network` = 'face'");
+ $deny_arr = array();
+ if(count($r))
+ foreach($r as $rr)
+ $deny_arr[] = $rr['notify'];
+ }
- notice( t('Post to Facebook cancelled because of multi-network access permission conflict.') . EOL);
- return;
- }
+ if(count($deny_arr) && (! count($allow_arr))) {
+ // One or more FB folks were denied access but nobody on FB was specifically allowed access.
+ // This might cause the post to be open to public on Facebook, but only to selected members
+ // on another network. Since this could potentially leak a post to somebody who was denied,
+ // we will skip posting it to Facebook with a slightly vague but relevant message that will
+ // hopefully lead somebody to this code comment for a better explanation of what went wrong.
- // if it's a private message but no Facebook members are allowed or denied, skip Facebook post
+ notice( t('Post to Facebook cancelled because of multi-network access permission conflict.') . EOL);
+ return;
+ }
- if((! count($allow_arr)) && (! count($deny_arr)))
- return;
- }
- if($b['verb'] == ACTIVITY_LIKE)
- $likes = true;
+ // if it's a private message but no Facebook members are allowed or denied, skip Facebook post
+ if((! count($allow_arr)) && (! count($deny_arr)))
+ return;
+ }
- $appid = get_config('facebook', 'appid' );
- $secret = get_config('facebook', 'appsecret' );
+ if($b['verb'] == ACTIVITY_LIKE)
+ $likes = true;
- if($appid && $secret) {
- logger('facebook: have appid+secret');
+ $appid = get_config('facebook', 'appid' );
+ $secret = get_config('facebook', 'appsecret' );
- $fb_token = get_pconfig($b['uid'],'facebook','access_token');
+ if($appid && $secret) {
+ logger('facebook: have appid+secret');
- // post to facebook if it's a public post and we've ticked the 'post to Facebook' box,
- // or it's a private message with facebook participants
- // or it's a reply or likes action to an existing facebook post
+ $fb_token = get_pconfig($b['uid'],'facebook','access_token');
- if($fb_token && ($toplevel || $b['private'] || $reply)) {
- logger('facebook: able to post');
- require_once('library/facebook.php');
- require_once('include/bbcode.php');
- $msg = $b['body'];
+ // post to facebook if it's a public post and we've ticked the 'post to Facebook' box,
+ // or it's a private message with facebook participants
+ // or it's a reply or likes action to an existing facebook post
- logger('Facebook post: original msg=' . $msg, LOGGER_DATA);
+ if($fb_token && ($toplevel || $b['private'] || $reply)) {
+ logger('facebook: able to post');
+ require_once('library/facebook.php');
+ require_once('include/bbcode.php');
- // make links readable before we strip the code
+ $msg = $b['body'];
- // unless it's a dislike - just send the text as a comment
+ logger('Facebook post: original msg=' . $msg, LOGGER_DATA);
- if($b['verb'] == ACTIVITY_DISLIKE)
- $msg = trim(strip_tags(bbcode($msg)));
+ // make links readable before we strip the code
- // Old code
- /*$search_str = $a->get_baseurl() . '/search';
+ // unless it's a dislike - just send the text as a comment
- if(preg_match("/\[url=(.*?)\](.*?)\[\/url\]/is",$msg,$matches)) {
+ if($b['verb'] == ACTIVITY_DISLIKE)
+ $msg = trim(strip_tags(bbcode($msg)));
- // don't use hashtags for message link
+ // Old code
+ /*$search_str = $a->get_baseurl() . '/search';
- if(strpos($matches[2],$search_str) === false) {
- $link = $matches[1];
- if(substr($matches[2],0,5) != '[img]')
- $linkname = $matches[2];
- }
- }
+ if(preg_match("/\[url=(.*?)\](.*?)\[\/url\]/is",$msg,$matches)) {
- // strip tag links to avoid link clutter, this really should be
- // configurable because we're losing information
+ // don't use hashtags for message link
- $msg = preg_replace("/\#\[url=(.*?)\](.*?)\[\/url\]/is",'#$2',$msg);
+ if(strpos($matches[2],$search_str) === false) {
+ $link = $matches[1];
+ if(substr($matches[2],0,5) != '[img]')
+ $linkname = $matches[2];
+ }
+ }
- // provide the link separately for normal links
- $msg = preg_replace("/\[url=(.*?)\](.*?)\[\/url\]/is",'$2 $1',$msg);
+ // strip tag links to avoid link clutter, this really should be
+ // configurable because we're losing information
- if(preg_match("/\[img\](.*?)\[\/img\]/is",$msg,$matches))
- $image = $matches[1];
+ $msg = preg_replace("/\#\[url=(.*?)\](.*?)\[\/url\]/is",'#$2',$msg);
- $msg = preg_replace("/\[img\](.*?)\[\/img\]/is", t('Image: ') . '$1', $msg);
+ // provide the link separately for normal links
+ $msg = preg_replace("/\[url=(.*?)\](.*?)\[\/url\]/is",'$2 $1',$msg);
- if((strpos($link,z_root()) !== false) && (! $image))
- $image = $a->get_baseurl() . '/images/friendica-64.jpg';
+ if(preg_match("/\[img\](.*?)\[\/img\]/is",$msg,$matches))
+ $image = $matches[1];
- $msg = trim(strip_tags(bbcode($msg)));*/
+ $msg = preg_replace("/\[img\](.*?)\[\/img\]/is", t('Image: ') . '$1', $msg);
- // New code
+ if((strpos($link,z_root()) !== false) && (! $image))
+ $image = $a->get_baseurl() . '/images/friendica-64.jpg';
- // Looking for the first image
- $image = '';
- if(preg_match("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/is",$b['body'],$matches))
- $image = $matches[3];
+ $msg = trim(strip_tags(bbcode($msg)));*/
- if ($image != '')
- if(preg_match("/\[img\](.*?)\[\/img\]/is",$b['body'],$matches))
- $image = $matches[1];
+ // New code
- // Checking for a bookmark element
- $body = $b['body'];
- if (strpos($body, "[bookmark") !== false) {
- // splitting the text in two parts:
- // before and after the bookmark
- $pos = strpos($body, "[bookmark");
- $body1 = substr($body, 0, $pos);
- $body2 = substr($body, $pos);
+ // Looking for the first image
+ $image = '';
+ if(preg_match("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/is",$b['body'],$matches))
+ $image = $matches[3];
- // Removing the bookmark and all quotes after the bookmark
- // they are mostly only the content after the bookmark.
- $body2 = preg_replace("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism",'',$body2);
- $body2 = preg_replace("/\[quote\=([^\]]*)\](.*?)\[\/quote\]/ism",'',$body2);
- $body2 = preg_replace("/\[quote\](.*?)\[\/quote\]/ism",'',$body2);
+ if ($image != '')
+ if(preg_match("/\[img\](.*?)\[\/img\]/is",$b['body'],$matches))
+ $image = $matches[1];
- $body = $body1.$body2;
- }
+ // Checking for a bookmark element
+ $body = $b['body'];
+ if (strpos($body, "[bookmark") !== false) {
+ // splitting the text in two parts:
+ // before and after the bookmark
+ $pos = strpos($body, "[bookmark");
+ $body1 = substr($body, 0, $pos);
+ $body2 = substr($body, $pos);
- // At first convert the text to html
- $html = bbcode($body);
-
- // Then convert it to plain text
- $msg = trim($b['title']." \n\n".html2plain($html, 0, true));
- $msg = html_entity_decode($msg,ENT_QUOTES,'UTF-8');
+ // Removing the bookmark and all quotes after the bookmark
+ // they are mostly only the content after the bookmark.
+ $body2 = preg_replace("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism",'',$body2);
+ $body2 = preg_replace("/\[quote\=([^\]]*)\](.*?)\[\/quote\]/ism",'',$body2);
+ $body2 = preg_replace("/\[quote\](.*?)\[\/quote\]/ism",'',$body2);
- // Removing multiple newlines
- while (strpos($msg, "\n\n\n") !== false)
- $msg = str_replace("\n\n\n", "\n\n", $msg);
-
- // add any attachments as text urls
- $arr = explode(',',$b['attach']);
-
- if(count($arr)) {
- $msg .= "\n";
- foreach($arr as $r) {
- $matches = false;
- $cnt = preg_match('|\[attach\]href=\"(.*?)\" size=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"\[\/attach\]|',$r,$matches);
- if($cnt) {
- $msg .= "\n".$matches[1];
- }
- }
- }
-
- $link = '';
- $linkname = '';
- // look for bookmark-bbcode and handle it with priority
- if(preg_match("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/is",$b['body'],$matches)) {
- $link = $matches[1];
- $linkname = $matches[2];
- }
-
- // If there is no bookmark element then take the first link
- if ($link == '') {
- $links = collecturls($html);
- if (sizeof($links) > 0) {
- reset($links);
- $link = current($links);
- }
- }
-
- // Remove trailing and leading spaces
- $msg = trim($msg);
-
- // Since facebook increased the maxpostlen massively this never should happen again :)
- if (strlen($msg) > FACEBOOK_MAXPOSTLEN) {
- $shortlink = "";
- require_once('library/slinky.php');
-
- $display_url = $b['plink'];
-
- $slinky = new Slinky( $display_url );
- // setup a cascade of shortening services
- // try to get a short link from these services
- // in the order ur1.ca, trim, id.gd, tinyurl
- $slinky->set_cascade( array( new Slinky_UR1ca(), new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL() ) );
- $shortlink = $slinky->short();
- // the new message will be shortened such that "... $shortlink"
- // will fit into the character limit
- $msg = substr($msg, 0, FACEBOOK_MAXPOSTLEN - strlen($shortlink) - 4);
- $msg .= '... ' . $shortlink;
- }
-
- // Fallback - if message is empty
- if(!strlen($msg))
- $msg = $link;
-
- if(!strlen($msg))
- $msg = $image;
-
- if(!strlen($msg))
- $msg = $linkname;
-
- // If there is nothing to post then exit
- if(!strlen($msg))
- return;
-
- logger('Facebook post: msg=' . $msg, LOGGER_DATA);
-
- if($likes) {
- $postvars = array('access_token' => $fb_token);
- }
- else {
- $postvars = array(
- 'access_token' => $fb_token,
- 'message' => $msg
- );
- if(isset($image))
- $postvars['picture'] = $image;
- if(isset($link))
- $postvars['link'] = $link;
- if(isset($linkname))
- $postvars['name'] = $linkname;
- }
-
- if(($b['private']) && ($toplevel)) {
- $postvars['privacy'] = '{"value": "CUSTOM", "friends": "SOME_FRIENDS"';
- if(count($allow_arr))
- $postvars['privacy'] .= ',"allow": "' . implode(',',$allow_arr) . '"';
- if(count($deny_arr))
- $postvars['privacy'] .= ',"deny": "' . implode(',',$deny_arr) . '"';
- $postvars['privacy'] .= '}';
-
- }
-
- if($reply) {
- $url = 'https://graph.facebook.com/' . $reply . '/' . (($likes) ? 'likes' : 'comments');
- }
- else {
- $url = 'https://graph.facebook.com/me/feed';
- if($b['plink'])
- $postvars['actions'] = '{"name": "' . t('View on Friendica') . '", "link": "' . $b['plink'] . '"}';
- }
-
- logger('facebook: post to ' . $url);
- logger('facebook: postvars: ' . print_r($postvars,true));
-
- // "test_mode" prevents anything from actually being posted.
- // Otherwise, let's do it.
-
- if(! get_config('facebook','test_mode')) {
- $x = post_url($url, $postvars);
- logger('Facebook post returns: ' . $x, LOGGER_DEBUG);
-
- $retj = json_decode($x);
- if($retj->id) {
- q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
- dbesc('fb::' . $retj->id),
- intval($b['id'])
- );
- }
- else {
- if(! $likes) {
- $s = serialize(array('url' => $url, 'item' => $b['id'], 'post' => $postvars));
- require_once('include/queue_fn.php');
- add_to_queue($a->contact,NETWORK_FACEBOOK,$s);
- notice( t('Facebook post failed. Queued for retry.') . EOL);
- }
-
- if (isset($retj->error) && $retj->error->type == "OAuthException" && $retj->error->code == 190) {
- logger('Facebook session has expired due to changed password.', LOGGER_DEBUG);
-
- $last_notification = get_pconfig($b['uid'], 'facebook', 'session_expired_mailsent');
- if (!$last_notification || $last_notification < (time() - FACEBOOK_SESSION_ERR_NOTIFICATION_INTERVAL)) {
- require_once('include/enotify.php');
-
- $r = q("SELECT * FROM `user` WHERE `uid` = %d LIMIT 1", intval($b['uid']) );
- notification(array(
- 'uid' => $b['uid'],
- 'type' => NOTIFY_SYSTEM,
- 'system_type' => 'facebook_connection_invalid',
- 'language' => $r[0]['language'],
- 'to_name' => $r[0]['username'],
- 'to_email' => $r[0]['email'],
- 'source_name' => t('Administrator'),
- 'source_link' => $a->config["system"]["url"],
- 'source_photo' => $a->config["system"]["url"] . '/images/person-80.jpg',
- ));
-
- set_pconfig($b['uid'], 'facebook', 'session_expired_mailsent', time());
- } else logger('Facebook: No notification, as the last one was sent on ' . $last_notification, LOGGER_DEBUG);
- }
- }
- }
- }
- }
- }
+ $body = $body1.$body2;
+ }
+
+ // At first convert the text to html
+ $html = bbcode($body);
+
+ // Then convert it to plain text
+ $msg = trim($b['title']." \n\n".html2plain($html, 0, true));
+ $msg = html_entity_decode($msg,ENT_QUOTES,'UTF-8');
+
+ // Removing multiple newlines
+ while (strpos($msg, "\n\n\n") !== false)
+ $msg = str_replace("\n\n\n", "\n\n", $msg);
+
+ // add any attachments as text urls
+ $arr = explode(',',$b['attach']);
+
+ if(count($arr)) {
+ $msg .= "\n";
+ foreach($arr as $r) {
+ $matches = false;
+ $cnt = preg_match('|\[attach\]href=\"(.*?)\" size=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"\[\/attach\]|',$r,$matches);
+ if($cnt) {
+ $msg .= "\n".$matches[1];
+ }
+ }
+ }
+
+ $link = '';
+ $linkname = '';
+ // look for bookmark-bbcode and handle it with priority
+ if(preg_match("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/is",$b['body'],$matches)) {
+ $link = $matches[1];
+ $linkname = $matches[2];
+ }
+
+ // If there is no bookmark element then take the first link
+ if ($link == '') {
+ $links = collecturls($html);
+ if (sizeof($links) > 0) {
+ reset($links);
+ $link = current($links);
+ }
+ }
+
+ // Remove trailing and leading spaces
+ $msg = trim($msg);
+
+ // Since facebook increased the maxpostlen massively this never should happen again :)
+ if (strlen($msg) > FACEBOOK_MAXPOSTLEN) {
+ $shortlink = "";
+ require_once('library/slinky.php');
+
+ $display_url = $b['plink'];
+
+ $slinky = new Slinky( $display_url );
+ // setup a cascade of shortening services
+ // try to get a short link from these services
+ // in the order ur1.ca, trim, id.gd, tinyurl
+ $slinky->set_cascade( array( new Slinky_UR1ca(), new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL() ) );
+ $shortlink = $slinky->short();
+ // the new message will be shortened such that "... $shortlink"
+ // will fit into the character limit
+ $msg = substr($msg, 0, FACEBOOK_MAXPOSTLEN - strlen($shortlink) - 4);
+ $msg .= '... ' . $shortlink;
+ }
+
+ // Fallback - if message is empty
+ if(!strlen($msg))
+ $msg = $link;
+
+ if(!strlen($msg))
+ $msg = $image;
+
+ if(!strlen($msg))
+ $msg = $linkname;
+
+ // If there is nothing to post then exit
+ if(!strlen($msg))
+ return;
+
+ logger('Facebook post: msg=' . $msg, LOGGER_DATA);
+
+ if($likes) {
+ $postvars = array('access_token' => $fb_token);
+ }
+ else {
+ $postvars = array(
+ 'access_token' => $fb_token,
+ 'message' => $msg
+ );
+ if(isset($image))
+ $postvars['picture'] = $image;
+ if(isset($link))
+ $postvars['link'] = $link;
+ if(isset($linkname))
+ $postvars['name'] = $linkname;
+ }
+
+ if(($b['private']) && ($toplevel)) {
+ $postvars['privacy'] = '{"value": "CUSTOM", "friends": "SOME_FRIENDS"';
+ if(count($allow_arr))
+ $postvars['privacy'] .= ',"allow": "' . implode(',',$allow_arr) . '"';
+ if(count($deny_arr))
+ $postvars['privacy'] .= ',"deny": "' . implode(',',$deny_arr) . '"';
+ $postvars['privacy'] .= '}';
+
+ }
+
+ if($reply) {
+ $url = 'https://graph.facebook.com/' . $reply . '/' . (($likes) ? 'likes' : 'comments');
+ }
+ else {
+ $url = 'https://graph.facebook.com/me/feed';
+ if($b['plink'])
+ $postvars['actions'] = '{"name": "' . t('View on Friendica') . '", "link": "' . $b['plink'] . '"}';
+ }
+
+ logger('facebook: post to ' . $url);
+ logger('facebook: postvars: ' . print_r($postvars,true));
+
+ // "test_mode" prevents anything from actually being posted.
+ // Otherwise, let's do it.
+
+ if(! get_config('facebook','test_mode')) {
+ $x = post_url($url, $postvars);
+ logger('Facebook post returns: ' . $x, LOGGER_DEBUG);
+
+ $retj = json_decode($x);
+ if($retj->id) {
+ q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
+ dbesc('fb::' . $retj->id),
+ intval($b['id'])
+ );
+ }
+ else {
+ if(! $likes) {
+ $s = serialize(array('url' => $url, 'item' => $b['id'], 'post' => $postvars));
+ require_once('include/queue_fn.php');
+ add_to_queue($a->contact,NETWORK_FACEBOOK,$s);
+ notice( t('Facebook post failed. Queued for retry.') . EOL);
+ }
+
+ if (isset($retj->error) && $retj->error->type == "OAuthException" && $retj->error->code == 190) {
+ logger('Facebook session has expired due to changed password.', LOGGER_DEBUG);
+
+ $last_notification = get_pconfig($b['uid'], 'facebook', 'session_expired_mailsent');
+ if (!$last_notification || $last_notification < (time() - FACEBOOK_SESSION_ERR_NOTIFICATION_INTERVAL)) {
+ require_once('include/enotify.php');
+
+ $r = q("SELECT * FROM `user` WHERE `uid` = %d LIMIT 1", intval($b['uid']) );
+ notification(array(
+ 'uid' => $b['uid'],
+ 'type' => NOTIFY_SYSTEM,
+ 'system_type' => 'facebook_connection_invalid',
+ 'language' => $r[0]['language'],
+ 'to_name' => $r[0]['username'],
+ 'to_email' => $r[0]['email'],
+ 'source_name' => t('Administrator'),
+ 'source_link' => $a->config["system"]["url"],
+ 'source_photo' => $a->config["system"]["url"] . '/images/person-80.jpg',
+ ));
+
+ set_pconfig($b['uid'], 'facebook', 'session_expired_mailsent', time());
+ } else logger('Facebook: No notification, as the last one was sent on ' . $last_notification, LOGGER_DEBUG);
+ }
+ }
+ }
+ }
+ }
+ }
}
function facebook_enotify(&$app, &$data) {
- if (x($data, 'params') && $data['params']['type'] == NOTIFY_SYSTEM && x($data['params'], 'system_type') && $data['params']['system_type'] == 'facebook_connection_invalid') {
- $data['itemlink'] = '/facebook';
- $data['epreamble'] = $data['preamble'] = t('Your Facebook connection became invalid. Please Re-authenticate.');
- $data['subject'] = t('Facebook connection became invalid');
- $data['body'] = sprintf( t("Hi %1\$s,\n\nThe connection between your accounts on %2\$s and Facebook became invalid. This usually happens after you change your Facebook-password. To enable the connection again, you have to %3\$sre-authenticate the Facebook-connector%4\$s."), $data['params']['to_name'], "[url=" . $app->config["system"]["url"] . "]" . $app->config["sitename"] . "[/url]", "[url=" . $app->config["system"]["url"] . "/facebook]", "[/url]");
- }
+ if (x($data, 'params') && $data['params']['type'] == NOTIFY_SYSTEM && x($data['params'], 'system_type') && $data['params']['system_type'] == 'facebook_connection_invalid') {
+ $data['itemlink'] = '/facebook';
+ $data['epreamble'] = $data['preamble'] = t('Your Facebook connection became invalid. Please Re-authenticate.');
+ $data['subject'] = t('Facebook connection became invalid');
+ $data['body'] = sprintf( t("Hi %1\$s,\n\nThe connection between your accounts on %2\$s and Facebook became invalid. This usually happens after you change your Facebook-password. To enable the connection again, you have to %3\$sre-authenticate the Facebook-connector%4\$s."), $data['params']['to_name'], "[url=" . $app->config["system"]["url"] . "]" . $app->config["sitename"] . "[/url]", "[url=" . $app->config["system"]["url"] . "/facebook]", "[/url]");
+ }
}
function facebook_post_local(&$a,&$b) {
- // Figure out if Facebook posting is enabled for this post and file it in 'postopts'
- // where we will discover it during background delivery.
+ // Figure out if Facebook posting is enabled for this post and file it in 'postopts'
+ // where we will discover it during background delivery.
- // This can only be triggered by a local user posting to their own wall.
+ // This can only be triggered by a local user posting to their own wall.
- if((local_user()) && (local_user() == $b['uid'])) {
+ if((local_user()) && (local_user() == $b['uid'])) {
- $fb_post = intval(get_pconfig(local_user(),'facebook','post'));
- $fb_enable = (($fb_post && x($_REQUEST,'facebook_enable')) ? intval($_REQUEST['facebook_enable']) : 0);
+ $fb_post = intval(get_pconfig(local_user(),'facebook','post'));
+ $fb_enable = (($fb_post && x($_REQUEST,'facebook_enable')) ? intval($_REQUEST['facebook_enable']) : 0);
- // if API is used, default to the chosen settings
- if($_REQUEST['api_source'] && intval(get_pconfig(local_user(),'facebook','post_by_default')))
- $fb_enable = 1;
+ // if API is used, default to the chosen settings
+ if($_REQUEST['api_source'] && intval(get_pconfig(local_user(),'facebook','post_by_default')))
+ $fb_enable = 1;
- if(! $fb_enable)
- return;
+ if(! $fb_enable)
+ return;
- if(strlen($b['postopts']))
- $b['postopts'] .= ',';
- $b['postopts'] .= 'facebook';
- }
+ if(strlen($b['postopts']))
+ $b['postopts'] .= ',';
+ $b['postopts'] .= 'facebook';
+ }
}
function fb_queue_hook(&$a,&$b) {
- $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'",
- dbesc(NETWORK_FACEBOOK)
- );
- if(! count($qi))
- return;
+ $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'",
+ dbesc(NETWORK_FACEBOOK)
+ );
+ if(! count($qi))
+ return;
- require_once('include/queue_fn.php');
+ require_once('include/queue_fn.php');
- foreach($qi as $x) {
- if($x['network'] !== NETWORK_FACEBOOK)
- continue;
+ foreach($qi as $x) {
+ if($x['network'] !== NETWORK_FACEBOOK)
+ continue;
- logger('facebook_queue: run');
+ logger('facebook_queue: run');
- $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid`
+ $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid`
WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1",
- intval($x['cid'])
- );
- if(! count($r))
- continue;
+ intval($x['cid'])
+ );
+ if(! count($r))
+ continue;
- $user = $r[0];
+ $user = $r[0];
- $appid = get_config('facebook', 'appid' );
- $secret = get_config('facebook', 'appsecret' );
+ $appid = get_config('facebook', 'appid' );
+ $secret = get_config('facebook', 'appsecret' );
- if($appid && $secret) {
- $fb_post = intval(get_pconfig($user['uid'],'facebook','post'));
- $fb_token = get_pconfig($user['uid'],'facebook','access_token');
+ if($appid && $secret) {
+ $fb_post = intval(get_pconfig($user['uid'],'facebook','post'));
+ $fb_token = get_pconfig($user['uid'],'facebook','access_token');
- if($fb_post && $fb_token) {
- logger('facebook_queue: able to post');
- require_once('library/facebook.php');
+ if($fb_post && $fb_token) {
+ logger('facebook_queue: able to post');
+ require_once('library/facebook.php');
- $z = unserialize($x['content']);
- $item = $z['item'];
- $j = post_url($z['url'],$z['post']);
+ $z = unserialize($x['content']);
+ $item = $z['item'];
+ $j = post_url($z['url'],$z['post']);
- $retj = json_decode($j);
- if($retj->id) {
- q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
- dbesc('fb::' . $retj->id),
- intval($item)
- );
- logger('facebook_queue: success: ' . $j);
- remove_queue_item($x['id']);
- }
- else {
- logger('facebook_queue: failed: ' . $j);
- update_queue_time($x['id']);
- }
- }
- }
- }
+ $retj = json_decode($j);
+ if($retj->id) {
+ q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d LIMIT 1",
+ dbesc('fb::' . $retj->id),
+ intval($item)
+ );
+ logger('facebook_queue: success: ' . $j);
+ remove_queue_item($x['id']);
+ }
+ else {
+ logger('facebook_queue: failed: ' . $j);
+ update_queue_time($x['id']);
+ }
+ }
+ }
+ }
}
function fb_get_timeline($access_token, &$since) {
+ $entries = new stdClass();
+ $entries->data = array();
+ $newest = 0;
- $entries->data = array();
- $newest = 0;
+ $url = 'https://graph.facebook.com/me/home?access_token='.$access_token;
- $url = 'https://graph.facebook.com/me/home?access_token='.$access_token;
+ if ($since != 0)
+ $url .= "&since=".$since;
- if ($since != 0)
- $url .= "&since=".$since;
+ do {
+ $s = fetch_url($url);
+ $j = json_decode($s);
+ $oldestdate = time();
+ if (isset($j->data))
+ foreach ($j->data as $entry) {
+ $created = strtotime($entry->created_time);
- do {
- $s = fetch_url($url);
- $j = json_decode($s);
- $oldestdate = time();
- if (isset($j->data))
- foreach ($j->data as $entry) {
- $created = strtotime($entry->created_time);
+ if ($newest < $created)
+ $newest = $created;
- if ($newest < $created)
- $newest = $created;
+ if ($created >= $since)
+ $entries->data[] = $entry;
- if ($created >= $since)
- $entries->data[] = $entry;
+ if ($created <= $oldestdate)
+ $oldestdate = $created;
+ }
+ else
+ break;
- if ($created <= $oldestdate)
- $oldestdate = $created;
- }
- else
- break;
+ $url = $j->paging->next;
- $url = $j->paging->next;
+ } while (($oldestdate > $since) and ($since != 0) and ($url != ''));
- } while (($oldestdate > $since) and ($since != 0) and ($url != ''));
+ if ($newest > $since)
+ $since = $newest;
- if ($newest > $since)
- $since = $newest;
-
- return($entries);
+ return($entries);
}
function fb_consume_all($uid) {
- require_once('include/items.php');
+ require_once('include/items.php');
- $access_token = get_pconfig($uid,'facebook','access_token');
- if(! $access_token)
- return;
-
- if(! get_pconfig($uid,'facebook','no_wall')) {
- $private_wall = intval(get_pconfig($uid,'facebook','private_wall'));
- $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token);
- if($s) {
- $j = json_decode($s);
- if (isset($j->data)) {
- logger('fb_consume_stream: wall: ' . print_r($j,true), LOGGER_DATA);
- fb_consume_stream($uid,$j,($private_wall) ? false : true);
- } else {
- logger('fb_consume_stream: wall: got no data from Facebook: ' . print_r($j,true), LOGGER_NORMAL);
- }
- }
- }
- // Get the last date
- $lastdate = get_pconfig($uid,'facebook','lastdate');
- // fetch all items since the last date
- $j = fb_get_timeline($access_token, &$lastdate);
- if (isset($j->data)) {
- logger('fb_consume_stream: feed: ' . print_r($j,true), LOGGER_DATA);
- fb_consume_stream($uid,$j,false);
+ $access_token = get_pconfig($uid,'facebook','access_token');
+ if(! $access_token)
+ return;
- // Write back the last date
- set_pconfig($uid,'facebook','lastdate', $lastdate);
- } else
- logger('fb_consume_stream: feed: got no data from Facebook: ' . print_r($j,true), LOGGER_NORMAL);
+ if(! get_pconfig($uid,'facebook','no_wall')) {
+ $private_wall = intval(get_pconfig($uid,'facebook','private_wall'));
+ $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token);
+ if($s) {
+ $j = json_decode($s);
+ if (isset($j->data)) {
+ logger('fb_consume_stream: wall: ' . print_r($j,true), LOGGER_DATA);
+ fb_consume_stream($uid,$j,($private_wall) ? false : true);
+ } else {
+ logger('fb_consume_stream: wall: got no data from Facebook: ' . print_r($j,true), LOGGER_NORMAL);
+ }
+ }
+ }
+ // Get the last date
+ $lastdate = get_pconfig($uid,'facebook','lastdate');
+ // fetch all items since the last date
+ $j = fb_get_timeline($access_token, &$lastdate);
+ if (isset($j->data)) {
+ logger('fb_consume_stream: feed: ' . print_r($j,true), LOGGER_DATA);
+ fb_consume_stream($uid,$j,false);
+
+ // Write back the last date
+ set_pconfig($uid,'facebook','lastdate', $lastdate);
+ } else
+ logger('fb_consume_stream: feed: got no data from Facebook: ' . print_r($j,true), LOGGER_NORMAL);
}
function fb_get_photo($uid,$link) {
- $access_token = get_pconfig($uid,'facebook','access_token');
- if(! $access_token || (! stristr($link,'facebook.com/photo.php')))
- return "";
- //return "\n" . '[url=' . $link . ']' . t('link') . '[/url]';
- $ret = preg_match('/fbid=([0-9]*)/',$link,$match);
- if($ret)
- $photo_id = $match[1];
- $x = fetch_url('https://graph.facebook.com/' . $photo_id . '?access_token=' . $access_token);
- $j = json_decode($x);
- if($j->picture)
- return "\n\n" . '[url=' . $link . '][img]' . $j->picture . '[/img][/url]';
- //else
- // return "\n" . '[url=' . $link . ']' . t('link') . '[/url]';
+ $access_token = get_pconfig($uid,'facebook','access_token');
+ if(! $access_token || (! stristr($link,'facebook.com/photo.php')))
+ return "";
+ //return "\n" . '[url=' . $link . ']' . t('link') . '[/url]';
+ $ret = preg_match('/fbid=([0-9]*)/',$link,$match);
+ if($ret)
+ $photo_id = $match[1];
+ $x = fetch_url('https://graph.facebook.com/' . $photo_id . '?access_token=' . $access_token);
+ $j = json_decode($x);
+ if($j->picture)
+ return "\n\n" . '[url=' . $link . '][img]' . $j->picture . '[/img][/url]';
+ //else
+ // return "\n" . '[url=' . $link . ']' . t('link') . '[/url]';
}
function fb_consume_stream($uid,$j,$wall = false) {
- $a = get_app();
+ $a = get_app();
- $user = q("SELECT * FROM `user` WHERE `uid` = %d AND `account_expired` = 0 LIMIT 1",
- intval($uid)
- );
- if(! count($user))
- return;
+ $user = q("SELECT * FROM `user` WHERE `uid` = %d AND `account_expired` = 0 LIMIT 1",
+ intval($uid)
+ );
+ if(! count($user))
+ return;
- $my_local_url = $a->get_baseurl() . '/profile/' . $user[0]['nickname'];
+ $my_local_url = $a->get_baseurl() . '/profile/' . $user[0]['nickname'];
- $no_linking = get_pconfig($uid,'facebook','no_linking');
- if($no_linking)
- return;
+ $no_linking = get_pconfig($uid,'facebook','no_linking');
+ if($no_linking)
+ return;
- $self = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
- intval($uid)
- );
+ $self = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
+ intval($uid)
+ );
- $blocked_apps = get_pconfig($uid,'facebook','blocked_apps');
- $blocked_apps_arr = explode(',',$blocked_apps);
+ $blocked_apps = get_pconfig($uid,'facebook','blocked_apps');
+ $blocked_apps_arr = explode(',',$blocked_apps);
- $self_id = get_pconfig($uid,'facebook','self_id');
- if(! count($j->data) || (! strlen($self_id)))
- return;
+ $self_id = get_pconfig($uid,'facebook','self_id');
+ if(! count($j->data) || (! strlen($self_id)))
+ return;
- foreach($j->data as $entry) {
- logger('fb_consume: entry: ' . print_r($entry,true), LOGGER_DATA);
- $datarray = array();
+ foreach($j->data as $entry) {
+ logger('fb_consume: entry: ' . print_r($entry,true), LOGGER_DATA);
+ $datarray = array();
- $r = q("SELECT * FROM `item` WHERE ( `uri` = '%s' OR `extid` = '%s') AND `uid` = %d LIMIT 1",
- dbesc('fb::' . $entry->id),
- dbesc('fb::' . $entry->id),
- intval($uid)
- );
- if(count($r)) {
- $post_exists = true;
- $orig_post = $r[0];
- $top_item = $r[0]['id'];
- }
- else {
- $post_exists = false;
- $orig_post = null;
- }
+ $r = q("SELECT * FROM `item` WHERE ( `uri` = '%s' OR `extid` = '%s') AND `uid` = %d LIMIT 1",
+ dbesc('fb::' . $entry->id),
+ dbesc('fb::' . $entry->id),
+ intval($uid)
+ );
+ if(count($r)) {
+ $post_exists = true;
+ $orig_post = $r[0];
+ $top_item = $r[0]['id'];
+ }
+ else {
+ $post_exists = false;
+ $orig_post = null;
+ }
- if(! $orig_post) {
- $datarray['gravity'] = 0;
- $datarray['uid'] = $uid;
- $datarray['wall'] = (($wall) ? 1 : 0);
- $datarray['uri'] = $datarray['parent-uri'] = 'fb::' . $entry->id;
- $from = $entry->from;
- if($from->id == $self_id)
- $datarray['contact-id'] = $self[0]['id'];
- else {
- // Looking if user is known - if not he is added
- $access_token = get_pconfig($uid, 'facebook', 'access_token');
- fb_get_friends_sync_new($uid, $access_token, $from);
+ if(! $orig_post) {
+ $datarray['gravity'] = 0;
+ $datarray['uid'] = $uid;
+ $datarray['wall'] = (($wall) ? 1 : 0);
+ $datarray['uri'] = $datarray['parent-uri'] = 'fb::' . $entry->id;
+ $from = $entry->from;
+ if($from->id == $self_id)
+ $datarray['contact-id'] = $self[0]['id'];
+ else {
+ // Looking if user is known - if not he is added
+ $access_token = get_pconfig($uid, 'facebook', 'access_token');
+ fb_get_friends_sync_new($uid, $access_token, $from);
- $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
- dbesc($from->id),
- intval($uid)
- );
- if(count($r))
- $datarray['contact-id'] = $r[0]['id'];
- }
+ $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
+ dbesc($from->id),
+ intval($uid)
+ );
+ if(count($r))
+ $datarray['contact-id'] = $r[0]['id'];
+ }
- // don't store post if we don't have a contact
- if(! x($datarray,'contact-id')) {
- logger('facebook: no contact '.$from->name.' '.$from->id.'. post ignored');
- continue;
- }
+ // don't store post if we don't have a contact
+ if(! x($datarray,'contact-id')) {
+ logger('facebook: no contact '.$from->name.' '.$from->id.'. post ignored');
+ continue;
+ }
- $datarray['verb'] = ACTIVITY_POST;
- if($wall) {
- $datarray['owner-name'] = $self[0]['name'];
- $datarray['owner-link'] = $self[0]['url'];
- $datarray['owner-avatar'] = $self[0]['thumb'];
- }
- if(isset($entry->application) && isset($entry->application->name) && strlen($entry->application->name))
- $datarray['app'] = strip_tags($entry->application->name);
- else
- $datarray['app'] = 'facebook';
+ $datarray['verb'] = ACTIVITY_POST;
+ if($wall) {
+ $datarray['owner-name'] = $self[0]['name'];
+ $datarray['owner-link'] = $self[0]['url'];
+ $datarray['owner-avatar'] = $self[0]['thumb'];
+ }
+ if(isset($entry->application) && isset($entry->application->name) && strlen($entry->application->name))
+ $datarray['app'] = strip_tags($entry->application->name);
+ else
+ $datarray['app'] = 'facebook';
- $found_blocked = false;
+ $found_blocked = false;
- if(count($blocked_apps_arr)) {
- foreach($blocked_apps_arr as $bad_appl) {
- if(strlen(trim($bad_appl)) && (stristr($datarray['app'],trim($bad_appl)))) {
- $found_blocked = true;
- }
- }
- }
-
- if($found_blocked) {
- logger('facebook: blocking application: ' . $datarray['app']);
- continue;
- }
+ if(count($blocked_apps_arr)) {
+ foreach($blocked_apps_arr as $bad_appl) {
+ if(strlen(trim($bad_appl)) && (stristr($datarray['app'],trim($bad_appl)))) {
+ $found_blocked = true;
+ }
+ }
+ }
- $datarray['author-name'] = $from->name;
- $datarray['author-link'] = 'http://facebook.com/profile.php?id=' . $from->id;
- $datarray['author-avatar'] = 'https://graph.facebook.com/' . $from->id . '/picture';
- $datarray['plink'] = $datarray['author-link'] . '&v=wall&story_fbid=' . substr($entry->id,strpos($entry->id,'_') + 1);
+ if($found_blocked) {
+ logger('facebook: blocking application: ' . $datarray['app']);
+ continue;
+ }
- logger('facebook: post '.$entry->id.' from '.$from->name);
+ $datarray['author-name'] = $from->name;
+ $datarray['author-link'] = 'http://facebook.com/profile.php?id=' . $from->id;
+ $datarray['author-avatar'] = 'https://graph.facebook.com/' . $from->id . '/picture';
+ $datarray['plink'] = $datarray['author-link'] . '&v=wall&story_fbid=' . substr($entry->id,strpos($entry->id,'_') + 1);
- $datarray['body'] = escape_tags($entry->message);
+ logger('facebook: post '.$entry->id.' from '.$from->name);
- if($entry->name and $entry->link)
- $datarray['body'] .= "\n\n[bookmark=".$entry->link."]".$entry->name."[/bookmark]";
- elseif ($entry->name)
- $datarray['body'] .= "\n\n[b]" . $entry->name."[/b]";
+ $datarray['body'] = escape_tags($entry->message);
- if($entry->caption) {
- if(!$entry->name and $entry->link)
- $datarray['body'] .= "\n\n[bookmark=".$entry->link."]".$entry->caption."[/bookmark]";
- else
- $datarray['body'] .= "[i]" . $entry->caption."[/i]\n";
- }
+ if($entry->name and $entry->link)
+ $datarray['body'] .= "\n\n[bookmark=".$entry->link."]".$entry->name."[/bookmark]";
+ elseif ($entry->name)
+ $datarray['body'] .= "\n\n[b]" . $entry->name."[/b]";
- if(!$entry->caption and !$entry->name) {
- if ($entry->link)
- $datarray['body'] .= "\n[url]".$entry->link."[/url]\n";
- else
- $datarray['body'] .= "\n";
- }
+ if($entry->caption) {
+ if(!$entry->name and $entry->link)
+ $datarray['body'] .= "\n\n[bookmark=".$entry->link."]".$entry->caption."[/bookmark]";
+ else
+ $datarray['body'] .= "[i]" . $entry->caption."[/i]\n";
+ }
- $quote = "";
- if($entry->description)
- $quote = $entry->description;
+ if(!$entry->caption and !$entry->name) {
+ if ($entry->link)
+ $datarray['body'] .= "\n[url]".$entry->link."[/url]\n";
+ else
+ $datarray['body'] .= "\n";
+ }
- if ($entry->properties)
- foreach ($entry->properties as $property)
- $quote .= "\n".$property->name.": [url=".$property->href."]".$property->text."[/url]";
+ $quote = "";
+ if($entry->description)
+ $quote = $entry->description;
- if ($quote)
- $datarray['body'] .= "\n[quote]".$quote."[/quote]";
+ if ($entry->properties)
+ foreach ($entry->properties as $property)
+ $quote .= "\n".$property->name.": [url=".$property->href."]".$property->text."[/url]";
- // Only import the picture when the message is no video
- // oembed display a picture of the video as well
- if ($entry->type != "video") {
- if($entry->picture && $entry->link) {
- $datarray['body'] .= "\n" . '[url=' . $entry->link . '][img]'.$entry->picture.'[/img][/url]';
- }
- else {
- if($entry->picture)
- $datarray['body'] .= "\n" . '[img]' . $entry->picture . '[/img]';
- // if just a link, it may be a wall photo - check
- if($entry->link)
- $datarray['body'] .= fb_get_photo($uid,$entry->link);
- }
- }
+ if ($quote)
+ $datarray['body'] .= "\n[quote]".$quote."[/quote]";
- // Just as a test - to see if these are the missing entries
- //if(trim($datarray['body']) == '')
- // $datarray['body'] = $entry->story;
+ // Only import the picture when the message is no video
+ // oembed display a picture of the video as well
+ if ($entry->type != "video") {
+ if($entry->picture && $entry->link) {
+ $datarray['body'] .= "\n" . '[url=' . $entry->link . '][img]'.$entry->picture.'[/img][/url]';
+ }
+ else {
+ if($entry->picture)
+ $datarray['body'] .= "\n" . '[img]' . $entry->picture . '[/img]';
+ // if just a link, it may be a wall photo - check
+ if($entry->link)
+ $datarray['body'] .= fb_get_photo($uid,$entry->link);
+ }
+ }
- if(trim($datarray['body']) == '') {
- logger('facebook: empty body '.$entry->id.' '.print_r($entry, true));
- continue;
- }
+ // Just as a test - to see if these are the missing entries
+ //if(trim($datarray['body']) == '')
+ // $datarray['body'] = $entry->story;
- $datarray['body'] .= "\n";
+ if(trim($datarray['body']) == '') {
+ logger('facebook: empty body '.$entry->id.' '.print_r($entry, true));
+ continue;
+ }
- if ($entry->icon)
- $datarray['body'] .= "[img]".$entry->icon."[/img] ";
+ $datarray['body'] .= "\n";
- if ($entry->actions)
- foreach ($entry->actions as $action)
- if (($action->name != "Comment") and ($action->name != "Like"))
- $datarray['body'] .= "[url=".$action->link."]".$action->name."[/url] ";
+ if ($entry->icon)
+ $datarray['body'] .= "[img]".$entry->icon."[/img] ";
- $datarray['body'] = trim($datarray['body']);
+ if ($entry->actions)
+ foreach ($entry->actions as $action)
+ if (($action->name != "Comment") and ($action->name != "Like"))
+ $datarray['body'] .= "[url=".$action->link."]".$action->name."[/url] ";
- //if(($datarray['body'] != '') and ($uid == 1))
- // $datarray['body'] .= "[noparse]".print_r($entry, true)."[/noparse]";
+ $datarray['body'] = trim($datarray['body']);
- if ($entry->place->name)
- $datarray['coord'] = $entry->place->name;
- else if ($entry->place->location->street or $entry->place->location->city or $entry->place->location->Denmark) {
- if ($entry->place->location->street)
- $datarray['coord'] = $entry->place->location->street;
- if ($entry->place->location->city)
- $datarray['coord'] .= " ".$entry->place->location->city;
- if ($entry->place->location->country)
- $datarray['coord'] .= " ".$entry->place->location->country;
- } else if ($entry->place->location->latitude and $entry->place->location->longitude)
- $datarray['coord'] = substr($entry->place->location->latitude, 0, 8)
- .' '.substr($entry->place->location->longitude, 0, 8);
+ //if(($datarray['body'] != '') and ($uid == 1))
+ // $datarray['body'] .= "[noparse]".print_r($entry, true)."[/noparse]";
- $datarray['created'] = datetime_convert('UTC','UTC',$entry->created_time);
- $datarray['edited'] = datetime_convert('UTC','UTC',$entry->updated_time);
+ if ($entry->place->name)
+ $datarray['coord'] = $entry->place->name;
+ else if ($entry->place->location->street or $entry->place->location->city or $entry->place->location->Denmark) {
+ if ($entry->place->location->street)
+ $datarray['coord'] = $entry->place->location->street;
+ if ($entry->place->location->city)
+ $datarray['coord'] .= " ".$entry->place->location->city;
+ if ($entry->place->location->country)
+ $datarray['coord'] .= " ".$entry->place->location->country;
+ } else if ($entry->place->location->latitude and $entry->place->location->longitude)
+ $datarray['coord'] = substr($entry->place->location->latitude, 0, 8)
+ .' '.substr($entry->place->location->longitude, 0, 8);
- // If the entry has a privacy policy, we cannot assume who can or cannot see it,
- // as the identities are from a foreign system. Mark it as private to the owner.
+ $datarray['created'] = datetime_convert('UTC','UTC',$entry->created_time);
+ $datarray['edited'] = datetime_convert('UTC','UTC',$entry->updated_time);
- if($entry->privacy && $entry->privacy->value !== 'EVERYONE') {
- $datarray['private'] = 1;
- $datarray['allow_cid'] = '<' . $self[0]['id'] . '>';
- }
+ // If the entry has a privacy policy, we cannot assume who can or cannot see it,
+ // as the identities are from a foreign system. Mark it as private to the owner.
- $top_item = item_store($datarray);
- $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
- intval($top_item),
- intval($uid)
- );
- if(count($r)) {
- $orig_post = $r[0];
- logger('fb: new top level item posted');
- }
- }
+ if($entry->privacy && $entry->privacy->value !== 'EVERYONE') {
+ $datarray['private'] = 1;
+ $datarray['allow_cid'] = '<' . $self[0]['id'] . '>';
+ }
- if(isset($entry->likes) && isset($entry->likes->data))
- $likers = $entry->likes->data;
- else
- $likers = null;
+ $top_item = item_store($datarray);
+ $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
+ intval($top_item),
+ intval($uid)
+ );
+ if(count($r)) {
+ $orig_post = $r[0];
+ logger('fb: new top level item posted');
+ }
+ }
- if(isset($entry->comments) && isset($entry->comments->data))
- $comments = $entry->comments->data;
- else
- $comments = null;
+ if(isset($entry->likes) && isset($entry->likes->data))
+ $likers = $entry->likes->data;
+ else
+ $likers = null;
- if(is_array($likers)) {
- foreach($likers as $likes) {
+ if(isset($entry->comments) && isset($entry->comments->data))
+ $comments = $entry->comments->data;
+ else
+ $comments = null;
- if(! $orig_post)
- continue;
+ if(is_array($likers)) {
+ foreach($likers as $likes) {
- // If we posted the like locally, it will be found with our url, not the FB url.
+ if(! $orig_post)
+ continue;
- $second_url = (($likes->id == $self_id) ? $self[0]['url'] : 'http://facebook.com/profile.php?id=' . $likes->id);
+ // If we posted the like locally, it will be found with our url, not the FB url.
- $r = q("SELECT * FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `verb` = '%s'
+ $second_url = (($likes->id == $self_id) ? $self[0]['url'] : 'http://facebook.com/profile.php?id=' . $likes->id);
+
+ $r = q("SELECT * FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `verb` = '%s'
AND ( `author-link` = '%s' OR `author-link` = '%s' ) LIMIT 1",
- dbesc($orig_post['uri']),
- intval($uid),
- dbesc(ACTIVITY_LIKE),
- dbesc('http://facebook.com/profile.php?id=' . $likes->id),
- dbesc($second_url)
- );
+ dbesc($orig_post['uri']),
+ intval($uid),
+ dbesc(ACTIVITY_LIKE),
+ dbesc('http://facebook.com/profile.php?id=' . $likes->id),
+ dbesc($second_url)
+ );
- if(count($r))
- continue;
-
- $likedata = array();
- $likedata['parent'] = $top_item;
- $likedata['verb'] = ACTIVITY_LIKE;
- $likedata['gravity'] = 3;
- $likedata['uid'] = $uid;
- $likedata['wall'] = (($wall) ? 1 : 0);
- $likedata['uri'] = item_new_uri($a->get_baseurl(), $uid);
- $likedata['parent-uri'] = $orig_post['uri'];
- if($likes->id == $self_id)
- $likedata['contact-id'] = $self[0]['id'];
- else {
- $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
- dbesc($likes->id),
- intval($uid)
- );
- if(count($r))
- $likedata['contact-id'] = $r[0]['id'];
- }
- if(! x($likedata,'contact-id'))
- $likedata['contact-id'] = $orig_post['contact-id'];
+ if(count($r))
+ continue;
- $likedata['app'] = 'facebook';
- $likedata['verb'] = ACTIVITY_LIKE;
- $likedata['author-name'] = $likes->name;
- $likedata['author-link'] = 'http://facebook.com/profile.php?id=' . $likes->id;
- $likedata['author-avatar'] = 'https://graph.facebook.com/' . $likes->id . '/picture';
-
- $author = '[url=' . $likedata['author-link'] . ']' . $likedata['author-name'] . '[/url]';
- $objauthor = '[url=' . $orig_post['author-link'] . ']' . $orig_post['author-name'] . '[/url]';
- $post_type = t('status');
- $plink = '[url=' . $orig_post['plink'] . ']' . $post_type . '[/url]';
- $likedata['object-type'] = ACTIVITY_OBJ_NOTE;
+ $likedata = array();
+ $likedata['parent'] = $top_item;
+ $likedata['verb'] = ACTIVITY_LIKE;
+ $likedata['gravity'] = 3;
+ $likedata['uid'] = $uid;
+ $likedata['wall'] = (($wall) ? 1 : 0);
+ $likedata['uri'] = item_new_uri($a->get_baseurl(), $uid);
+ $likedata['parent-uri'] = $orig_post['uri'];
+ if($likes->id == $self_id)
+ $likedata['contact-id'] = $self[0]['id'];
+ else {
+ $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d AND `blocked` = 0 AND `readonly` = 0 LIMIT 1",
+ dbesc($likes->id),
+ intval($uid)
+ );
+ if(count($r))
+ $likedata['contact-id'] = $r[0]['id'];
+ }
+ if(! x($likedata,'contact-id'))
+ $likedata['contact-id'] = $orig_post['contact-id'];
- $likedata['body'] = sprintf( t('%1$s likes %2$s\'s %3$s'), $author, $objauthor, $plink);
- $likedata['object'] = '' . ACTIVITY_OBJ_NOTE . ' 1 ' .
- '' . $orig_post['uri'] . ' ' . xmlify(' ') . '' . $orig_post['title'] . ' ' . $orig_post['body'] . ' ';
+ $likedata['app'] = 'facebook';
+ $likedata['verb'] = ACTIVITY_LIKE;
+ $likedata['author-name'] = $likes->name;
+ $likedata['author-link'] = 'http://facebook.com/profile.php?id=' . $likes->id;
+ $likedata['author-avatar'] = 'https://graph.facebook.com/' . $likes->id . '/picture';
- $item = item_store($likedata);
- }
- }
- if(is_array($comments)) {
- foreach($comments as $cmnt) {
+ $author = '[url=' . $likedata['author-link'] . ']' . $likedata['author-name'] . '[/url]';
+ $objauthor = '[url=' . $orig_post['author-link'] . ']' . $orig_post['author-name'] . '[/url]';
+ $post_type = t('status');
+ $plink = '[url=' . $orig_post['plink'] . ']' . $post_type . '[/url]';
+ $likedata['object-type'] = ACTIVITY_OBJ_NOTE;
- if(! $orig_post)
- continue;
+ $likedata['body'] = sprintf( t('%1$s likes %2$s\'s %3$s'), $author, $objauthor, $plink);
+ $likedata['object'] = '' . ACTIVITY_OBJ_NOTE . ' 1 ' .
+ '' . $orig_post['uri'] . ' ' . xmlify(' ') . '' . $orig_post['title'] . ' ' . $orig_post['body'] . ' ';
- $r = q("SELECT * FROM `item` WHERE `uid` = %d AND ( `uri` = '%s' OR `extid` = '%s' ) LIMIT 1",
- intval($uid),
- dbesc('fb::' . $cmnt->id),
- dbesc('fb::' . $cmnt->id)
- );
- if(count($r))
- continue;
+ $item = item_store($likedata);
+ }
+ }
+ if(is_array($comments)) {
+ foreach($comments as $cmnt) {
- $cmntdata = array();
- $cmntdata['parent'] = $top_item;
- $cmntdata['verb'] = ACTIVITY_POST;
- $cmntdata['gravity'] = 6;
- $cmntdata['uid'] = $uid;
- $cmntdata['wall'] = (($wall) ? 1 : 0);
- $cmntdata['uri'] = 'fb::' . $cmnt->id;
- $cmntdata['parent-uri'] = $orig_post['uri'];
- if($cmnt->from->id == $self_id) {
- $cmntdata['contact-id'] = $self[0]['id'];
- }
- else {
- $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d LIMIT 1",
- dbesc($cmnt->from->id),
- intval($uid)
- );
- if(count($r)) {
- $cmntdata['contact-id'] = $r[0]['id'];
- if($r[0]['blocked'] || $r[0]['readonly'])
- continue;
- }
- }
- if(! x($cmntdata,'contact-id'))
- $cmntdata['contact-id'] = $orig_post['contact-id'];
+ if(! $orig_post)
+ continue;
- $cmntdata['app'] = 'facebook';
- $cmntdata['created'] = datetime_convert('UTC','UTC',$cmnt->created_time);
- $cmntdata['edited'] = datetime_convert('UTC','UTC',$cmnt->created_time);
- $cmntdata['verb'] = ACTIVITY_POST;
- $cmntdata['author-name'] = $cmnt->from->name;
- $cmntdata['author-link'] = 'http://facebook.com/profile.php?id=' . $cmnt->from->id;
- $cmntdata['author-avatar'] = 'https://graph.facebook.com/' . $cmnt->from->id . '/picture';
- $cmntdata['body'] = $cmnt->message;
- $item = item_store($cmntdata);
-
- $myconv = q("SELECT `author-link`, `author-avatar`, `parent` FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `parent` != 0 ",
- dbesc($orig_post['uri']),
- intval($uid)
- );
+ $r = q("SELECT * FROM `item` WHERE `uid` = %d AND ( `uri` = '%s' OR `extid` = '%s' ) LIMIT 1",
+ intval($uid),
+ dbesc('fb::' . $cmnt->id),
+ dbesc('fb::' . $cmnt->id)
+ );
+ if(count($r))
+ continue;
- if(count($myconv)) {
- $importer_url = $a->get_baseurl() . '/profile/' . $user[0]['nickname'];
+ $cmntdata = array();
+ $cmntdata['parent'] = $top_item;
+ $cmntdata['verb'] = ACTIVITY_POST;
+ $cmntdata['gravity'] = 6;
+ $cmntdata['uid'] = $uid;
+ $cmntdata['wall'] = (($wall) ? 1 : 0);
+ $cmntdata['uri'] = 'fb::' . $cmnt->id;
+ $cmntdata['parent-uri'] = $orig_post['uri'];
+ if($cmnt->from->id == $self_id) {
+ $cmntdata['contact-id'] = $self[0]['id'];
+ }
+ else {
+ $r = q("SELECT * FROM `contact` WHERE `notify` = '%s' AND `uid` = %d LIMIT 1",
+ dbesc($cmnt->from->id),
+ intval($uid)
+ );
+ if(count($r)) {
+ $cmntdata['contact-id'] = $r[0]['id'];
+ if($r[0]['blocked'] || $r[0]['readonly'])
+ continue;
+ }
+ }
+ if(! x($cmntdata,'contact-id'))
+ $cmntdata['contact-id'] = $orig_post['contact-id'];
- foreach($myconv as $conv) {
+ $cmntdata['app'] = 'facebook';
+ $cmntdata['created'] = datetime_convert('UTC','UTC',$cmnt->created_time);
+ $cmntdata['edited'] = datetime_convert('UTC','UTC',$cmnt->created_time);
+ $cmntdata['verb'] = ACTIVITY_POST;
+ $cmntdata['author-name'] = $cmnt->from->name;
+ $cmntdata['author-link'] = 'http://facebook.com/profile.php?id=' . $cmnt->from->id;
+ $cmntdata['author-avatar'] = 'https://graph.facebook.com/' . $cmnt->from->id . '/picture';
+ $cmntdata['body'] = $cmnt->message;
+ $item = item_store($cmntdata);
- // now if we find a match, it means we're in this conversation
-
- if(! link_compare($conv['author-link'],$importer_url))
- continue;
+ $myconv = q("SELECT `author-link`, `author-avatar`, `parent` FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `parent` != 0 ",
+ dbesc($orig_post['uri']),
+ intval($uid)
+ );
- require_once('include/enotify.php');
-
- $conv_parent = $conv['parent'];
+ if(count($myconv)) {
+ $importer_url = $a->get_baseurl() . '/profile/' . $user[0]['nickname'];
- notification(array(
- 'type' => NOTIFY_COMMENT,
- 'notify_flags' => $user[0]['notify-flags'],
- 'language' => $user[0]['language'],
- 'to_name' => $user[0]['username'],
- 'to_email' => $user[0]['email'],
- 'uid' => $user[0]['uid'],
- 'item' => $cmntdata,
- 'link' => $a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $item,
- 'source_name' => $cmntdata['author-name'],
- 'source_link' => $cmntdata['author-link'],
- 'source_photo' => $cmntdata['author-avatar'],
- 'verb' => ACTIVITY_POST,
- 'otype' => 'item',
- 'parent' => $conv_parent,
- ));
+ foreach($myconv as $conv) {
- // only send one notification
- break;
- }
- }
- }
- }
- }
+ // now if we find a match, it means we're in this conversation
+
+ if(! link_compare($conv['author-link'],$importer_url))
+ continue;
+
+ require_once('include/enotify.php');
+
+ $conv_parent = $conv['parent'];
+
+ notification(array(
+ 'type' => NOTIFY_COMMENT,
+ 'notify_flags' => $user[0]['notify-flags'],
+ 'language' => $user[0]['language'],
+ 'to_name' => $user[0]['username'],
+ 'to_email' => $user[0]['email'],
+ 'uid' => $user[0]['uid'],
+ 'item' => $cmntdata,
+ 'link' => $a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $item,
+ 'source_name' => $cmntdata['author-name'],
+ 'source_link' => $cmntdata['author-link'],
+ 'source_photo' => $cmntdata['author-avatar'],
+ 'verb' => ACTIVITY_POST,
+ 'otype' => 'item',
+ 'parent' => $conv_parent,
+ ));
+
+ // only send one notification
+ break;
+ }
+ }
+ }
+ }
+ }
}
function fb_get_app_access_token() {
-
- $acc_token = get_config('facebook','app_access_token');
-
- if ($acc_token !== false) return $acc_token;
-
- $appid = get_config('facebook','appid');
- $appsecret = get_config('facebook', 'appsecret');
-
- if ($appid === false || $appsecret === false) {
- logger('fb_get_app_access_token: appid and/or appsecret not set', LOGGER_DEBUG);
- return false;
- }
- logger('https://graph.facebook.com/oauth/access_token?client_id=' . $appid . '&client_secret=' . $appsecret . '&grant_type=client_credentials', LOGGER_DATA);
- $x = fetch_url('https://graph.facebook.com/oauth/access_token?client_id=' . $appid . '&client_secret=' . $appsecret . '&grant_type=client_credentials');
-
- if(strpos($x,'access_token=') !== false) {
- logger('fb_get_app_access_token: returned access token: ' . $x, LOGGER_DATA);
-
- $token = str_replace('access_token=', '', $x);
- if(strpos($token,'&') !== false)
- $token = substr($token,0,strpos($token,'&'));
-
- if ($token == "") {
- logger('fb_get_app_access_token: empty token: ' . $x, LOGGER_DEBUG);
- return false;
- }
- set_config('facebook','app_access_token',$token);
- return $token;
- } else {
- logger('fb_get_app_access_token: response did not contain an access_token: ' . $x, LOGGER_DATA);
- return false;
- }
+
+ $acc_token = get_config('facebook','app_access_token');
+
+ if ($acc_token !== false) return $acc_token;
+
+ $appid = get_config('facebook','appid');
+ $appsecret = get_config('facebook', 'appsecret');
+
+ if ($appid === false || $appsecret === false) {
+ logger('fb_get_app_access_token: appid and/or appsecret not set', LOGGER_DEBUG);
+ return false;
+ }
+ logger('https://graph.facebook.com/oauth/access_token?client_id=' . $appid . '&client_secret=' . $appsecret . '&grant_type=client_credentials', LOGGER_DATA);
+ $x = fetch_url('https://graph.facebook.com/oauth/access_token?client_id=' . $appid . '&client_secret=' . $appsecret . '&grant_type=client_credentials');
+
+ if(strpos($x,'access_token=') !== false) {
+ logger('fb_get_app_access_token: returned access token: ' . $x, LOGGER_DATA);
+
+ $token = str_replace('access_token=', '', $x);
+ if(strpos($token,'&') !== false)
+ $token = substr($token,0,strpos($token,'&'));
+
+ if ($token == "") {
+ logger('fb_get_app_access_token: empty token: ' . $x, LOGGER_DEBUG);
+ return false;
+ }
+ set_config('facebook','app_access_token',$token);
+ return $token;
+ } else {
+ logger('fb_get_app_access_token: response did not contain an access_token: ' . $x, LOGGER_DATA);
+ return false;
+ }
}
function facebook_subscription_del_users() {
- $a = get_app();
- $access_token = fb_get_app_access_token();
-
- $url = "https://graph.facebook.com/" . get_config('facebook', 'appid' ) . "/subscriptions?access_token=" . $access_token;
- facebook_delete_url($url);
-
- if (!facebook_check_realtime_active()) del_config('facebook', 'realtime_active');
+ $a = get_app();
+ $access_token = fb_get_app_access_token();
+
+ $url = "https://graph.facebook.com/" . get_config('facebook', 'appid' ) . "/subscriptions?access_token=" . $access_token;
+ facebook_delete_url($url);
+
+ if (!facebook_check_realtime_active()) del_config('facebook', 'realtime_active');
}
function facebook_subscription_add_users($second_try = false) {
- $a = get_app();
- $access_token = fb_get_app_access_token();
-
- $url = "https://graph.facebook.com/" . get_config('facebook', 'appid' ) . "/subscriptions?access_token=" . $access_token;
-
- list($usec, $sec) = explode(" ", microtime());
- $verify_token = sha1($usec . $sec . rand(0, 999999999));
- set_config('facebook', 'cb_verify_token', $verify_token);
-
- $cb = $a->get_baseurl() . '/facebook/?realtime_cb=1';
-
- $j = post_url($url,array(
- "object" => "user",
- "fields" => "feed,friends",
- "callback_url" => $cb,
- "verify_token" => $verify_token,
- ));
- del_config('facebook', 'cb_verify_token');
-
- if ($j) {
- $x = json_decode($j);
- logger("Facebook reponse: " . $j, LOGGER_DATA);
- if (isset($x->error)) {
- logger('facebook_subscription_add_users: got an error: ' . $j);
- if ($x->error->type == "OAuthException" && $x->error->code == 190) {
- del_config('facebook', 'app_access_token');
- if ($second_try === false) facebook_subscription_add_users(true);
- }
- } else {
- logger('facebook_subscription_add_users: sucessful');
- if (facebook_check_realtime_active()) set_config('facebook', 'realtime_active', 1);
- }
- };
+ $a = get_app();
+ $access_token = fb_get_app_access_token();
+
+ $url = "https://graph.facebook.com/" . get_config('facebook', 'appid' ) . "/subscriptions?access_token=" . $access_token;
+
+ list($usec, $sec) = explode(" ", microtime());
+ $verify_token = sha1($usec . $sec . rand(0, 999999999));
+ set_config('facebook', 'cb_verify_token', $verify_token);
+
+ $cb = $a->get_baseurl() . '/facebook/?realtime_cb=1';
+
+ $j = post_url($url,array(
+ "object" => "user",
+ "fields" => "feed,friends",
+ "callback_url" => $cb,
+ "verify_token" => $verify_token,
+ ));
+ del_config('facebook', 'cb_verify_token');
+
+ if ($j) {
+ $x = json_decode($j);
+ logger("Facebook reponse: " . $j, LOGGER_DATA);
+ if (isset($x->error)) {
+ logger('facebook_subscription_add_users: got an error: ' . $j);
+ if ($x->error->type == "OAuthException" && $x->error->code == 190) {
+ del_config('facebook', 'app_access_token');
+ if ($second_try === false) facebook_subscription_add_users(true);
+ }
+ } else {
+ logger('facebook_subscription_add_users: sucessful');
+ if (facebook_check_realtime_active()) set_config('facebook', 'realtime_active', 1);
+ }
+ };
}
function facebook_subscriptions_get() {
-
- $access_token = fb_get_app_access_token();
- if (!$access_token) return null;
-
- $url = "https://graph.facebook.com/" . get_config('facebook', 'appid' ) . "/subscriptions?access_token=" . $access_token;
- $j = fetch_url($url);
- $ret = null;
- if ($j) {
- $x = json_decode($j);
- if (isset($x->data)) $ret = $x->data;
- }
- return $ret;
+
+ $access_token = fb_get_app_access_token();
+ if (!$access_token) return null;
+
+ $url = "https://graph.facebook.com/" . get_config('facebook', 'appid' ) . "/subscriptions?access_token=" . $access_token;
+ $j = fetch_url($url);
+ $ret = null;
+ if ($j) {
+ $x = json_decode($j);
+ if (isset($x->data)) $ret = $x->data;
+ }
+ return $ret;
}
function facebook_check_realtime_active() {
- $ret = facebook_subscriptions_get();
- if (is_null($ret)) return false;
- if (is_array($ret)) foreach ($ret as $re) if (is_object($re) && $re->object == "user") return true;
- return false;
+ $ret = facebook_subscriptions_get();
+ if (is_null($ret)) return false;
+ if (is_array($ret)) foreach ($ret as $re) if (is_object($re) && $re->object == "user") return true;
+ return false;
}
@@ -1759,85 +1774,85 @@ function facebook_check_realtime_active() {
// DELETE-request to $url
if(! function_exists('facebook_delete_url')) {
-function facebook_delete_url($url,$headers = null, &$redirects = 0, $timeout = 0) {
- $a = get_app();
- $ch = curl_init($url);
- if(($redirects > 8) || (! $ch))
- return false;
+ function facebook_delete_url($url,$headers = null, &$redirects = 0, $timeout = 0) {
+ $a = get_app();
+ $ch = curl_init($url);
+ if(($redirects > 8) || (! $ch))
+ return false;
- curl_setopt($ch, CURLOPT_HEADER, true);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
- curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
- curl_setopt($ch, CURLOPT_USERAGENT, "Friendica");
+ curl_setopt($ch, CURLOPT_HEADER, true);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
+ curl_setopt($ch, CURLOPT_USERAGENT, "Friendica");
- if(intval($timeout)) {
- curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
- }
- else {
- $curl_time = intval(get_config('system','curl_timeout'));
- curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== false) ? $curl_time : 60));
- }
-
- if(defined('LIGHTTPD')) {
- if(!is_array($headers)) {
- $headers = array('Expect:');
- } else {
- if(!in_array('Expect:', $headers)) {
- array_push($headers, 'Expect:');
- }
- }
- }
- if($headers)
- curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
-
- $check_cert = get_config('system','verifyssl');
- curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false));
- $prx = get_config('system','proxy');
- if(strlen($prx)) {
- curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);
- curl_setopt($ch, CURLOPT_PROXY, $prx);
- $prxusr = get_config('system','proxyuser');
- if(strlen($prxusr))
- curl_setopt($ch, CURLOPT_PROXYUSERPWD, $prxusr);
- }
-
- $a->set_curl_code(0);
-
- // don't let curl abort the entire application
- // if it throws any errors.
-
- $s = @curl_exec($ch);
-
- $base = $s;
- $curl_info = curl_getinfo($ch);
- $http_code = $curl_info['http_code'];
-
- $header = '';
-
- // Pull out multiple headers, e.g. proxy and continuation headers
- // allow for HTTP/2.x without fixing code
-
- while(preg_match('/^HTTP\/[1-2].+? [1-5][0-9][0-9]/',$base)) {
- $chunk = substr($base,0,strpos($base,"\r\n\r\n")+4);
- $header .= $chunk;
- $base = substr($base,strlen($chunk));
- }
-
- if($http_code == 301 || $http_code == 302 || $http_code == 303) {
- $matches = array();
- preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches);
- $url = trim(array_pop($matches));
- $url_parsed = @parse_url($url);
- if (isset($url_parsed)) {
- $redirects++;
- return delete_url($url,$headers,$redirects,$timeout);
+ if(intval($timeout)) {
+ curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
+ }
+ else {
+ $curl_time = intval(get_config('system','curl_timeout'));
+ curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== false) ? $curl_time : 60));
}
- }
- $a->set_curl_code($http_code);
- $body = substr($s,strlen($header));
- $a->set_curl_headers($header);
+ if(defined('LIGHTTPD')) {
+ if(!is_array($headers)) {
+ $headers = array('Expect:');
+ } else {
+ if(!in_array('Expect:', $headers)) {
+ array_push($headers, 'Expect:');
+ }
+ }
+ }
+ if($headers)
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
- curl_close($ch);
- return($body);
-}}
+ $check_cert = get_config('system','verifyssl');
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false));
+ $prx = get_config('system','proxy');
+ if(strlen($prx)) {
+ curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);
+ curl_setopt($ch, CURLOPT_PROXY, $prx);
+ $prxusr = get_config('system','proxyuser');
+ if(strlen($prxusr))
+ curl_setopt($ch, CURLOPT_PROXYUSERPWD, $prxusr);
+ }
+
+ $a->set_curl_code(0);
+
+ // don't let curl abort the entire application
+ // if it throws any errors.
+
+ $s = @curl_exec($ch);
+
+ $base = $s;
+ $curl_info = curl_getinfo($ch);
+ $http_code = $curl_info['http_code'];
+
+ $header = '';
+
+ // Pull out multiple headers, e.g. proxy and continuation headers
+ // allow for HTTP/2.x without fixing code
+
+ while(preg_match('/^HTTP\/[1-2].+? [1-5][0-9][0-9]/',$base)) {
+ $chunk = substr($base,0,strpos($base,"\r\n\r\n")+4);
+ $header .= $chunk;
+ $base = substr($base,strlen($chunk));
+ }
+
+ if($http_code == 301 || $http_code == 302 || $http_code == 303) {
+ $matches = array();
+ preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches);
+ $url = trim(array_pop($matches));
+ $url_parsed = @parse_url($url);
+ if (isset($url_parsed)) {
+ $redirects++;
+ return delete_url($url,$headers,$redirects,$timeout);
+ }
+ }
+ $a->set_curl_code($http_code);
+ $body = substr($s,strlen($header));
+
+ $a->set_curl_headers($header);
+
+ curl_close($ch);
+ return($body);
+ }}
From 64cbef4f0de5a40a493060261ddb96fd1e467361 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tobias=20H=C3=B6=C3=9Fl?=
Date: Mon, 9 Apr 2012 12:07:24 +0000
Subject: [PATCH 02/96] Provide a link to Martin Farrent's instructions instead
of the obsolete README text; Prevent some Notices when running on E_ALL
---
facebook/README | 40 ++----------------------------
facebook/facebook.php | 57 ++++++++++++-------------------------------
2 files changed, 18 insertions(+), 79 deletions(-)
diff --git a/facebook/README b/facebook/README
index b68ba3da..5f74d2d4 100755
--- a/facebook/README
+++ b/facebook/README
@@ -1,43 +1,7 @@
Installing the Friendica/Facebook connector
-1. Visit https://developers.facebook.com/apps to register an app.
- a) Click "Create a new app"
- b) We'd be very happy if you include "Friendica" in the application name
- to increase name recognition.
- c) Edit your app settings on the setup page. The Friendica icons are present
- in the images directory and may be uploaded as a Facebook app icon. Use
- images/friendica-16.jpg for the Icon and images/Friendica-128.jpg for the logo.
- d) In the App Display name enter the name of your app (this should default to the
- name you chose in part a).
- e) Enter YourDomain.com in the App Domain field and hit return.
- f) In "Select how your app connects with Facebook select "Website" and enter the
- full URL to your Friendica install including HTTPS and a trailing slash.
-
-2. Enable the Facebook plugin by clicking on the icon next to it's name on the plugin
- page of your admin panel.
- b) return to the Facebook plugin page in your admin panel, and fill in the App-ID
- and Application Secret settings you got from Facebook.
- c) Click save.
- d) Finally, return to the Facebook settings page, and activate real-time updates.
-
- i. If you for any reason prefer to use a configuration file instead of the admin panels,
- Activate the plugin by including it in .htconfig.php, e.g.
-
- $a->config['system']['addon'] = 'plugin1,plugin2,facebook';
-
- and set the following values:
- $a->config['facebook']['appid'] = 'xxxxxxxxxxx';
- $a->config['facebook']['appsecret'] = 'xxxxxxxxxxxxxxx';
-
- Replace with the settings Facebook gives you.
-
-
-3. To use the Facebook plugin, visit the "connector settings" area of your settings
- page. Click "Install Facebook Connector".
-4. This will ask you to login to Facebook and allow the plugin to do it's stuff.
- Allow it to do so.
-5. You're done. To turn it off visit the Plugin Settings page again and
- 'Remove Facebook posting'.
+Detailed instructions how to use this plugin can be found at
+https://github.com/friendica/friendica/wiki/How-to:-Friendica%E2%80%99s-Facebook-connector
Vidoes and embeds will not be posted if there is no other content. Links
and images will be converted to a format suitable for the Facebook API and
diff --git a/facebook/facebook.php b/facebook/facebook.php
index 0c31da1f..4e886608 100644
--- a/facebook/facebook.php
+++ b/facebook/facebook.php
@@ -1,7 +1,7 @@
* Tobias Hößl
*/
@@ -9,33 +9,8 @@
/**
* Installing the Friendica/Facebook connector
*
- * 1. register an API key for your site from developer.facebook.com
- * a. We'd be very happy if you include "Friendica" in the application name
- * to increase name recognition. The Friendica icons are also present
- * in the images directory and may be uploaded as a Facebook app icon.
- * Use images/friendica-16.jpg for the Icon and images/friendica-128.jpg for the Logo.
- * b. The url should be your site URL with a trailing slash.
- * Friendica is a software application and does not require a Privacy Policy
- * or Terms of Service, though your installation of it might. Facebook may require
- * that you provide a Privacy Policy, which we find ironic.
- * c. Set the following values in your .htconfig.php file
- * $a->config['facebook']['appid'] = 'xxxxxxxxxxx';
- * $a->config['facebook']['appsecret'] = 'xxxxxxxxxxxxxxx';
- * Replace with the settings Facebook gives you.
- * d. Navigate to Set Web->Site URL & Domain -> Website Settings. Set
- * Site URL to yoursubdomain.yourdomain.com. Set Site Domain to your
- * yourdomain.com.
- * 2. Visit the Facebook Settings section of the "Settings->Plugin Settings" page.
- * and click 'Install Facebook Connector'.
- * 3. Visit the Facebook Settings section of the "Settings->Plugin Settings" page.
- * and click 'Install Facebook Connector'.
- * 4. This will ask you to login to Facebook and grant permission to the
- * plugin to do its stuff. Allow it to do so.
- * 5. Optional step: If you want to use Facebook Real Time Updates (so new messages
- * and new contacts are added ~1min after they are postet / added on FB), go to
- * Settings -> plugins -> facebook and press the "Activate Real-Time Updates"-button.
- * 6. You're done. To turn it off visit the Plugin Settings page again and
- * 'Remove Facebook posting'.
+ * Detailed instructions how to use this plugin can be found at
+ * https://github.com/friendica/friendica/wiki/How-to:-Friendica%E2%80%99s-Facebook-connector
*
* Vidoes and embeds will not be posted if there is no other content. Links
* and images will be converted to a format suitable for the Facebook API and
@@ -1391,32 +1366,32 @@ function fb_consume_stream($uid,$j,$wall = false) {
logger('facebook: post '.$entry->id.' from '.$from->name);
- $datarray['body'] = escape_tags($entry->message);
+ $datarray['body'] = (x($entry, 'message') ? escape_tags($entry->message) : '');
- if($entry->name and $entry->link)
+ if(x($entry, 'name') and x($entry, 'link'))
$datarray['body'] .= "\n\n[bookmark=".$entry->link."]".$entry->name."[/bookmark]";
- elseif ($entry->name)
+ elseif (x($entry, 'name'))
$datarray['body'] .= "\n\n[b]" . $entry->name."[/b]";
- if($entry->caption) {
- if(!$entry->name and $entry->link)
+ if(x($entry, 'caption')) {
+ if(!x($entry, 'name') and x($entry, 'link'))
$datarray['body'] .= "\n\n[bookmark=".$entry->link."]".$entry->caption."[/bookmark]";
else
$datarray['body'] .= "[i]" . $entry->caption."[/i]\n";
}
- if(!$entry->caption and !$entry->name) {
- if ($entry->link)
+ if(!x($entry, 'caption') and !x($entry, 'name')) {
+ if (x($entry, 'link'))
$datarray['body'] .= "\n[url]".$entry->link."[/url]\n";
else
$datarray['body'] .= "\n";
}
- $quote = "";
- if($entry->description)
+ $quote = '';
+ if(x($entry, 'description'))
$quote = $entry->description;
- if ($entry->properties)
+ if (x($entry, 'properties'))
foreach ($entry->properties as $property)
$quote .= "\n".$property->name.": [url=".$property->href."]".$property->text."[/url]";
@@ -1426,14 +1401,14 @@ function fb_consume_stream($uid,$j,$wall = false) {
// Only import the picture when the message is no video
// oembed display a picture of the video as well
if ($entry->type != "video") {
- if($entry->picture && $entry->link) {
+ if(x($entry, 'picture') && x($entry, 'link')) {
$datarray['body'] .= "\n" . '[url=' . $entry->link . '][img]'.$entry->picture.'[/img][/url]';
}
else {
- if($entry->picture)
+ if(x($entry, 'picture'))
$datarray['body'] .= "\n" . '[img]' . $entry->picture . '[/img]';
// if just a link, it may be a wall photo - check
- if($entry->link)
+ if(x($entry, 'link'))
$datarray['body'] .= fb_get_photo($uid,$entry->link);
}
}
From c9ccd91dd96baa61e4c075ab4e099ce436a584ec Mon Sep 17 00:00:00 2001
From: Michael Vogel
Date: Mon, 9 Apr 2012 21:28:11 +0200
Subject: [PATCH 03/96] New addon "convpath": It converts all local links
according to the current scheme (http/https)
---
convpath/README | 7 ++++++
convpath/convpath.php | 50 +++++++++++++++++++++++++++++++++++++++++++
facebook/facebook.php | 10 +++++----
3 files changed, 63 insertions(+), 4 deletions(-)
create mode 100755 convpath/README
create mode 100644 convpath/convpath.php
diff --git a/convpath/README b/convpath/README
new file mode 100755
index 00000000..9d1c3072
--- /dev/null
+++ b/convpath/README
@@ -0,0 +1,7 @@
+convpath
+
+This addon converts all internal paths according to the current scheme.
+
+That means that if a page is called via https then all internal links are also converted into https.
+
+Same happens when you call your page with http.
diff --git a/convpath/convpath.php b/convpath/convpath.php
new file mode 100644
index 00000000..5ad7fbcb
--- /dev/null
+++ b/convpath/convpath.php
@@ -0,0 +1,50 @@
+
+ *
+ */
+
+function convpath_install() {
+ register_hook('page_end', 'addon/convpath/convpath.php', 'convpath_page_end');
+ register_hook('page_header', 'addon/convpath/convpath.php', 'convpath_page_header');
+}
+
+
+function convpath_uninstall() {
+ unregister_hook('page_end', 'addon/convpath/convpath.php', 'convpath_page_end');
+ unregister_hook('page_header', 'addon/convpath/convpath.php', 'convpath_page_header');
+}
+
+function convpath_page_header(&$a, &$o){
+ $o = convpath_convert($o);
+}
+
+function convpath_page_end(&$a, &$o){
+ $o = convpath_convert($o);
+ $a->page['aside'] = convpath_convert($a->page['aside']);
+}
+
+/*
+Converts a given path according to the current scheme
+*/
+function convpath_convert($path) {
+ global $a;
+
+ if ($path == "")
+ return("");
+
+ $ssl = (substr($a->get_baseurl(), 0, 8) == "https://");
+
+ if ($ssl) {
+ $search = "http://".$a->get_hostname();
+ $replace = "https://".$a->get_hostname();
+ } else {
+ $search = "https://".$a->get_hostname();
+ $replace = "http://".$a->get_hostname();
+ }
+ $path = str_replace($search, $replace, $path);
+ return($path);
+}
diff --git a/facebook/facebook.php b/facebook/facebook.php
index 70a353dd..5990a99e 100755
--- a/facebook/facebook.php
+++ b/facebook/facebook.php
@@ -1447,11 +1447,13 @@ function fb_consume_stream($uid,$j,$wall = false) {
//if(($datarray['body'] != '') and ($uid == 1))
// $datarray['body'] .= "[noparse]".print_r($entry, true)."[/noparse]";
- if ($entry->place->name)
- $datarray['coord'] = $entry->place->name;
- else if ($entry->place->location->street or $entry->place->location->city or $entry->place->location->Denmark) {
+ if ($entry->place->name or $entry->place->location->street or
+ $entry->place->location->city or $entry->place->location->Denmark) {
+ $datarray['coord'] = '';
+ if ($entry->place->name)
+ $datarray['coord'] .= $entry->place->name;
if ($entry->place->location->street)
- $datarray['coord'] = $entry->place->location->street;
+ $datarray['coord'] .= $entry->place->location->street;
if ($entry->place->location->city)
$datarray['coord'] .= " ".$entry->place->location->city;
if ($entry->place->location->country)
From acebf88aa5ceb6a613232c4bedbb3822c592f92d Mon Sep 17 00:00:00 2001
From: Michael Vogel
Date: Mon, 9 Apr 2012 22:05:22 +0200
Subject: [PATCH 04/96] =?UTF-8?q?New=20addon=20"fromgplus".=20Should=20imp?=
=?UTF-8?q?ort=20=C3posts=20from=20Google+.=20Not=20working=20by=20now.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
fromgplus/fromgplus.php | 181 ++++++++++++++++++++++++++++++++++++++
fromgplus/tofriendica.php | 128 +++++++++++++++++++++++++++
2 files changed, 309 insertions(+)
create mode 100755 fromgplus/fromgplus.php
create mode 100644 fromgplus/tofriendica.php
diff --git a/fromgplus/fromgplus.php b/fromgplus/fromgplus.php
new file mode 100755
index 00000000..09b6c605
--- /dev/null
+++ b/fromgplus/fromgplus.php
@@ -0,0 +1,181 @@
+
+ *
+ */
+
+function fromgplus_install() {
+ register_hook('plugin_settings', 'addon/fromgplus/fromgplus.php', 'fromgplus_addon_settings');
+ register_hook('plugin_settings_post', 'addon/fromgplus/fromgplus.php', 'fromgplus_addon_settings_post');
+}
+
+function fromgplus_uninstall() {
+ unregister_hook('plugin_settings', 'addon/fromgplus/fromgplus.php', 'fromgplus_addon_settings');
+ unregister_hook('plugin_settings_post', 'addon/fromgplus/fromgplus.php', 'fromgplus_addon_settings_post');
+}
+
+function fromgplus_addon_settings(&$a,&$s) {
+
+ if(! local_user())
+ return;
+
+ $enable_checked = (intval(get_pconfig(local_user(),'fromgplus','enable')) ? ' checked="checked"' : '');
+ $account = get_pconfig(local_user(),'fromgplus','account');
+
+ $s .= '';
+ $s .= '
' . t('Google+ Import Settings').' ';
+ $s .= '
';
+
+ $s .= '
'.t('Enable Google+ Import').' ';
+ $s .= '
';
+ $s .= '
';
+ $s .= '
'.t('Google Account ID').' ';
+ $s .= '
';
+ $s .= '
';
+
+ $s .= '
';
+ $s .= '
';
+
+ return;
+}
+
+function fromgplus_addon_settings_post(&$a,&$b) {
+
+ if(! local_user())
+ return;
+
+ if($_POST['fromgplus-submit']) {
+ set_pconfig(local_user(),'fromgplus','account',trim($_POST['fromgplus-account']));
+ $enable = ((x($_POST,'fromgplus-enable')) ? intval($_POST['fromgplus-enable']) : 0);
+ set_pconfig(local_user(),'fromgplus','enable', $enable);
+ info( t('Google+ Import Settings saved.') . EOL);
+ }
+}
+/*
+function html2bbcode($html) {
+
+ $bbcode = html_entity_decode($html, ENT_QUOTES, 'UTF-8');
+
+ $bbcode = str_replace(array("\n"), array(""), $bbcode);
+ $bbcode = str_replace(array("", " "), array("[b]", "[/b]"), $bbcode);
+ $bbcode = str_replace(array("", " "), array("[i]", "[/i]"), $bbcode);
+ $bbcode = str_replace(array("", " "), array("[s]", "[/s]"), $bbcode);
+ $bbcode = str_replace(array(" "), array("\n"), $bbcode);
+
+ $bbcode = trim(strip_tags($bbcode));
+ return($bbcode);
+}
+
+function friendicapost($post) {
+ global $friendica;
+
+ $api = new Statusnet($friendica["user"], $friendica["pw"], "GooglePlus", $friendica["server"]);
+ $ret = $api->updateStatus($post);
+ $api->endSession();
+}
+
+function handleattachments($item) {
+ $post = "";
+
+ foreach ($item->object->attachments as $attachment) {
+ switch($attachment->objectType) {
+ case "video":
+ //$post .= "\n\n[url=".$attachment->url."]".
+ // "[size=large][b]".html2bbcode($attachment->displayName)."[/b][/size][/url]\n";
+ $post .= "\n\n[bookmark=".$attachment->url."]".html2bbcode($attachment->displayName)."[/bookmark]\n";
+
+ //if (strpos($attachment->embed->url, "youtube.com"))
+ // $post .= "[youtube]".$attachment->url."[/youtube]\n";
+ //else
+ /// $post .= "[url=".$attachment->url."][img]".$attachment->image->url."[/img][/url]\n";
+
+ ///$post .= "[quote]".trim(html2bbcode($attachment->content))."[/quote]";
+ break;
+
+ case "article":
+ //$post .= "\n\n[url=".$attachment->url."]".
+ // "[size=large][b]".html2bbcode($attachment->displayName)."[/b][/size][/url]\n";
+ $post .= "\n\n[bookmark=".$attachment->url."]".html2bbcode($attachment->displayName)."[/bookmark]\n";
+ $post .= "[quote]".trim(html2bbcode($attachment->content))."[/quote]";
+ break;
+
+ case "photo":
+ //$post .= "\n\n[url=".$attachment->fullImage->url."]".
+ // "[img]".$attachment->fullImage->url."[/img][/url]\n";
+ $post .= "\n\n[img]".$attachment->fullImage->url."[/img]\n";
+ if ($attachment->displayName != "")
+ $post .= html2bbcode($attachment->displayName)."\n";
+ break;
+
+ case "photo-album":
+ $post .= "\n\n[url=".$attachment->url."]".
+ "[size=large][b]".html2bbcode($attachment->displayName)."[/b][/size][/url]\n";
+ break;
+
+ default:
+ print_r($attachment);
+ die();
+ break;
+ }
+ }
+ return($post);
+}
+
+$result = file_get_contents("https://www.googleapis.com/plus/v1/people/".$google["id"]."/activities/public?alt=json&pp=1&key=".$google["key"]."&maxResults=".$google["maxfetch"]);
+$activities = json_decode($result);
+
+$state = array("lastid"=>'');
+if (file_exists($statefile))
+ $state = unserialize(file_get_contents($statefile));
+
+$lastid = "";
+
+foreach($activities->items as $item) {
+ if ($item->id == $state["lastid"])
+ break;
+
+ if ($lastid == "")
+ $lastid = $item->id;
+
+ switch($item->object->objectType) {
+ case "note":
+ $post = html2bbcode($item->object->content);
+
+ if (is_array($item->object->attachments))
+ $post .= handleattachments($item);
+ friendicapost($post);
+ break;
+
+ case "activity":
+ $post = html2bbcode($item->annotation)."\n";
+ //$post .= html2bbcode("♲ ");
+ $post .= html2bbcode("♻ ");
+ $post .= "[url=".$item->object->actor->url."]".$item->object->actor->displayName."[/url]";
+ $post .= " \n";
+ //$post .= "[quote]";
+
+ $post .= html2bbcode($item->object->content);
+
+ if (is_array($item->object->attachments))
+ $post .= "\n".trim(handleattachments($item));
+
+ //$post .= "[/quote]";
+
+ friendicapost($post);
+ break;
+
+ default:
+ print_r($item);
+ die();
+ break;
+ }
+}
+
+if ($lastid != "") {
+ $state['lastid'] = $lastid;
+ file_put_contents($statefile, serialize($state));
+}
+*/
diff --git a/fromgplus/tofriendica.php b/fromgplus/tofriendica.php
new file mode 100644
index 00000000..b185ecc1
--- /dev/null
+++ b/fromgplus/tofriendica.php
@@ -0,0 +1,128 @@
+", ""), array("[b]", "[/b]"), $bbcode);
+ $bbcode = str_replace(array("", " "), array("[i]", "[/i]"), $bbcode);
+ $bbcode = str_replace(array("", " "), array("[s]", "[/s]"), $bbcode);
+ $bbcode = str_replace(array(" "), array("\n"), $bbcode);
+
+ $bbcode = trim(strip_tags($bbcode));
+ return($bbcode);
+}
+
+function friendicapost($post) {
+ global $friendica;
+
+ $api = new Statusnet($friendica["user"], $friendica["pw"], "GooglePlus", $friendica["server"]);
+ $ret = $api->updateStatus($post);
+ $api->endSession();
+}
+
+function handleattachments($item) {
+ $post = "";
+
+ foreach ($item->object->attachments as $attachment) {
+ switch($attachment->objectType) {
+ case "video":
+ //$post .= "\n\n[url=".$attachment->url."]".
+ // "[size=large][b]".html2bbcode($attachment->displayName)."[/b][/size][/url]\n";
+ $post .= "\n\n[bookmark=".$attachment->url."]".html2bbcode($attachment->displayName)."[/bookmark]\n";
+
+ //if (strpos($attachment->embed->url, "youtube.com"))
+ // $post .= "[youtube]".$attachment->url."[/youtube]\n";
+ //else
+ /// $post .= "[url=".$attachment->url."][img]".$attachment->image->url."[/img][/url]\n";
+
+ ///$post .= "[quote]".trim(html2bbcode($attachment->content))."[/quote]";
+ break;
+
+ case "article":
+ //$post .= "\n\n[url=".$attachment->url."]".
+ // "[size=large][b]".html2bbcode($attachment->displayName)."[/b][/size][/url]\n";
+ $post .= "\n\n[bookmark=".$attachment->url."]".html2bbcode($attachment->displayName)."[/bookmark]\n";
+ $post .= "[quote]".trim(html2bbcode($attachment->content))."[/quote]";
+ break;
+
+ case "photo":
+ //$post .= "\n\n[url=".$attachment->fullImage->url."]".
+ // "[img]".$attachment->fullImage->url."[/img][/url]\n";
+ $post .= "\n\n[img]".$attachment->fullImage->url."[/img]\n";
+ if ($attachment->displayName != "")
+ $post .= html2bbcode($attachment->displayName)."\n";
+ break;
+
+ case "photo-album":
+ $post .= "\n\n[url=".$attachment->url."]".
+ "[size=large][b]".html2bbcode($attachment->displayName)."[/b][/size][/url]\n";
+ break;
+
+ default:
+ print_r($attachment);
+ die();
+ break;
+ }
+ }
+ return($post);
+}
+
+$result = file_get_contents("https://www.googleapis.com/plus/v1/people/".$google["id"]."/activities/public?alt=json&pp=1&key=".$google["key"]."&maxResults=".$google["maxfetch"]);
+$activities = json_decode($result);
+
+$state = array("lastid"=>'');
+if (file_exists($statefile))
+ $state = unserialize(file_get_contents($statefile));
+
+$lastid = "";
+
+foreach($activities->items as $item) {
+ if ($item->id == $state["lastid"])
+ break;
+
+ if ($lastid == "")
+ $lastid = $item->id;
+
+ switch($item->object->objectType) {
+ case "note":
+ $post = html2bbcode($item->object->content);
+
+ if (is_array($item->object->attachments))
+ $post .= handleattachments($item);
+ friendicapost($post);
+ break;
+
+ case "activity":
+ $post = html2bbcode($item->annotation)."\n";
+ //$post .= html2bbcode("♲ ");
+ $post .= html2bbcode("♻ ");
+ $post .= "[url=".$item->object->actor->url."]".$item->object->actor->displayName."[/url]";
+ $post .= " \n";
+ //$post .= "[quote]";
+
+ $post .= html2bbcode($item->object->content);
+
+ if (is_array($item->object->attachments))
+ $post .= "\n".trim(handleattachments($item));
+
+ //$post .= "[/quote]";
+
+ friendicapost($post);
+ break;
+
+ default:
+ print_r($item);
+ die();
+ break;
+ }
+}
+
+if ($lastid != "") {
+ $state['lastid'] = $lastid;
+ file_put_contents($statefile, serialize($state));
+}
+?>
From 6f27beb728c444c908fdfbea818c612b2ae01a74 Mon Sep 17 00:00:00 2001
From: Michael Vogel
Date: Tue, 10 Apr 2012 23:09:06 +0200
Subject: [PATCH 05/96] Facebook: Don't add "story". Convpath now converts only
pictures
---
convpath/convpath.php | 7 ++++++-
facebook/facebook.php | 9 +++++++++
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/convpath/convpath.php b/convpath/convpath.php
index 5ad7fbcb..e5dc0b36 100644
--- a/convpath/convpath.php
+++ b/convpath/convpath.php
@@ -45,6 +45,11 @@ function convpath_convert($path) {
$search = "https://".$a->get_hostname();
$replace = "http://".$a->get_hostname();
}
- $path = str_replace($search, $replace, $path);
+ $searcharr = array("src='".$search, 'src="'.$search);
+ $replacearr = array("src='".$replace, 'src="'.$replace);
+ $path = str_replace($searcharr, $replacearr, $path);
+
+ //$path = str_replace($search, $replace, $path);
+
return($path);
}
diff --git a/facebook/facebook.php b/facebook/facebook.php
index 5990a99e..1e961fda 100755
--- a/facebook/facebook.php
+++ b/facebook/facebook.php
@@ -1423,10 +1423,19 @@ function fb_consume_stream($uid,$j,$wall = false) {
}
}
+ if (($datarray['app'] == "Events") and $entry->actions)
+ foreach ($entry->actions as $action)
+ if ($action->name == "View")
+ $datarray['body'] .= " [url=".$action->link."]".$entry->story."[/url]";
+
// Just as a test - to see if these are the missing entries
//if(trim($datarray['body']) == '')
// $datarray['body'] = $entry->story;
+ // Adding the "story" text to see if there are useful data in it (testing)
+ //if (($datarray['app'] != "Events") and $entry->story)
+ // $datarray['body'] .= "\n".$entry->story;
+
if(trim($datarray['body']) == '') {
logger('facebook: empty body '.$entry->id.' '.print_r($entry, true));
continue;
From 40ce1054b77fc4df2c9840ffdf04bb32b35996c0 Mon Sep 17 00:00:00 2001
From: Michael Vogel
Date: Thu, 12 Apr 2012 16:39:36 +0200
Subject: [PATCH 06/96] Facebook: Images weren't posted in some cases
---
facebook/facebook.php | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/facebook/facebook.php b/facebook/facebook.php
index b6e40654..6b9ee736 100755
--- a/facebook/facebook.php
+++ b/facebook/facebook.php
@@ -558,6 +558,9 @@ function facebook_content(&$a) {
function facebook_cron($a,$b) {
+//del_config('facebook', 'realtime_active');
+//del_config('facebook', 'realtime_err_mailsent');
+//del_config('facebook', 'cb_verify_token');
$last = get_config('facebook','last_poll');
@@ -896,7 +899,7 @@ function facebook_post_hook(&$a,&$b) {
if(preg_match("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/is",$b['body'],$matches))
$image = $matches[3];
- if ($image != '')
+ if ($image == '')
if(preg_match("/\[img\](.*?)\[\/img\]/is",$b['body'],$matches))
$image = $matches[1];
@@ -1436,8 +1439,8 @@ function fb_consume_stream($uid,$j,$wall = false) {
// $datarray['body'] = $entry->story;
// Adding the "story" text to see if there are useful data in it (testing)
- //if (($datarray['app'] != "Events") and $entry->story)
- // $datarray['body'] .= "\n".$entry->story;
+ if (($datarray['app'] != "Events") and $entry->story)
+ $datarray['body'] .= "\n".$entry->story;
if(trim($datarray['body']) == '') {
logger('facebook: empty body '.$entry->id.' '.print_r($entry, true));
From 977ac72f66fbbb23699767228e67c6051bc5f11e Mon Sep 17 00:00:00 2001
From: Michael Vogel
Date: Fri, 13 Apr 2012 08:32:17 +0200
Subject: [PATCH 07/96] Facebook: Messages without images and links but with a
subject and a text larger than 500 characters will be posted as notes.
---
facebook/facebook.php | 19 +++++++++++++++----
1 file changed, 15 insertions(+), 4 deletions(-)
diff --git a/facebook/facebook.php b/facebook/facebook.php
index 6b9ee736..a90445db 100755
--- a/facebook/facebook.php
+++ b/facebook/facebook.php
@@ -1009,10 +1009,14 @@ function facebook_post_hook(&$a,&$b) {
'access_token' => $fb_token,
'message' => $msg
);
- if(isset($image))
+ if(isset($image)) {
$postvars['picture'] = $image;
- if(isset($link))
+ //$postvars['type'] = "photo";
+ }
+ if(isset($link)) {
$postvars['link'] = $link;
+ //$postvars['type'] = "link";
+ }
if(isset($linkname))
$postvars['name'] = $linkname;
}
@@ -1029,11 +1033,18 @@ function facebook_post_hook(&$a,&$b) {
if($reply) {
$url = 'https://graph.facebook.com/' . $reply . '/' . (($likes) ? 'likes' : 'comments');
- }
- else {
+ } else if (($link != "") or ($image != "") or ($b['title'] == '') or (strlen($msg) < 500)) {
$url = 'https://graph.facebook.com/me/feed';
if($b['plink'])
$postvars['actions'] = '{"name": "' . t('View on Friendica') . '", "link": "' . $b['plink'] . '"}';
+ } else {
+ // if its only a message and a subject and the message is larger than 500 characters then post it as note
+ $postvars = array(
+ 'access_token' => $fb_token,
+ 'message' => bbcode($b['body']),
+ 'subject' => $b['title'],
+ );
+ $url = 'https://graph.facebook.com/me/notes';
}
logger('facebook: post to ' . $url);
From 60a7952688aa41c25106b2a05cad1f9ecf223e48 Mon Sep 17 00:00:00 2001
From: Leberwurscht
Date: Fri, 13 Apr 2012 17:05:16 +0200
Subject: [PATCH 08/96] add jappixmini addon
---
jappixmini/README | 23 +++
jappixmini/jappixmini.php | 382 ++++++++++++++++++++++++++++++++++++++
jappixmini/lib.js | 120 ++++++++++++
3 files changed, 525 insertions(+)
create mode 100644 jappixmini/README
create mode 100644 jappixmini/jappixmini.php
create mode 100644 jappixmini/lib.js
diff --git a/jappixmini/README b/jappixmini/README
new file mode 100644
index 00000000..2ef7c2f1
--- /dev/null
+++ b/jappixmini/README
@@ -0,0 +1,23 @@
+Jappix Mini Plugin
+==================
+
+This quick-and-dirty addon allows you to add a Jabber-based, Facebook-like chat
+to Friendica. It uses Jappix Mini.
+
+It is necessary to use a BOSH proxy - so to use this plugin, you need to know the
+address of a BOSH proxy that works with your account.
+
+The addon has an experimental autosubscribe and autosuggest functionality which tries
+to add your Friendica contacts to your roster automatically.
+
+Installation
+------------
+
+Jappix Mini (AGPL license) is not distributed with this addon. You need to
+install it manually:
+
+* Download latest zip file named friendica-addon-* from
+ https://github.com/Leberwurscht/jappix-friendica-addon/tags and place the zip file
+ in the addon folder. Make sure to name it 'jappix.zip' - the download link required
+ by the AGPL points there.
+* Unpack the zip file, rename the folder to 'jappix'. Do not delete jappix.zip.
diff --git a/jappixmini/jappixmini.php b/jappixmini/jappixmini.php
new file mode 100644
index 00000000..88959448
--- /dev/null
+++ b/jappixmini/jappixmini.php
@@ -0,0 +1,382 @@
+You need to install the Jappix application, adapted for Friendica (see README).
';
+ }
+ else if (!file_exists("addon/jappixmini/jappix.zip")) {
+ $o .= 'The source archive jappix.zip does not exist. This is probably a violation of the Jappix License (see README).
';
+ }
+}
+
+function jappixmini_plugin_admin_post(&$a) {
+}
+
+function jappixmini_module() {}
+function jappixmini_init(&$a) {
+ // Here, other friendica sites can fetch the jabber address of local users.
+ // Because we do not want to publish the addresses publicly, they are encrypted so
+ // that only contacts can read it.
+ $encrypt_for = $_REQUEST["encrypt_for"];
+ if ($encrypt_for) {
+ $r = q("SELECT * FROM `contact` WHERE LENGTH(`pubkey`) AND `dfrn-id` = '%s' LIMIT 1",
+ dbesc($encrypt_for)
+ );
+ if (!count($r)) killme();
+
+ // get public key to encrypt address
+ $pubkey = $r[0]['pubkey'];
+
+ // get jabber address
+ $uid = $r[0]['uid'];
+ $username = get_pconfig($uid, 'jappixmini', 'username');
+ if (!$username) killme();
+ $server = get_pconfig($uid, 'jappixmini', 'server');
+ if (!$server) killme();
+
+ $address = $username."@".$server;
+
+ // encrypt address
+ $encrypted = "";
+ openssl_public_encrypt($address,$encrypted,$pubkey);
+
+ // calculate hex representation of encrypted address
+ $hex = bin2hex($encrypted);
+
+ // construct answer
+ $answer = Array("status"=>"ok", "encrypted_address"=>$hex);
+
+ // return answer as json
+ echo json_encode($answer);
+ killme();
+ }
+
+ // If we have only a private key, other site sends encrypted request, we answer unencrypted.
+ $encrypted_for = $_REQUEST["encrypted_for"];
+ if (!$encrypted_for) killme();
+
+ $encrypted_request_hex = $_REQUEST["encrypted_request"];
+ if (!$encrypted_request_hex) killme();
+ $encrypted_request = hex2bin($encrypted_request_hex);
+
+ $r = q("SELECT * FROM `contact` WHERE LENGTH(`prvkey`) AND `issued-id` = '%s' LIMIT 1",
+ dbesc($encrypted_for)
+ );
+ if (!count($r)) killme();
+
+ // decrypt request, validate it
+ $prvkey = $r[0]['prvkey'];
+ $decrypted_request = "";
+ openssl_private_decrypt($encrypted_request, $decrypted_request, $prvkey);
+
+ if ($decrypted_request!=$encrypted_for) killme();
+
+ // get jabber address
+ $uid = $r[0]['uid'];
+ $username = get_pconfig($uid, 'jappixmini', 'username');
+ if (!$username) killme();
+ $server = get_pconfig($uid, 'jappixmini', 'server');
+ if (!$server) killme();
+
+ $address = $username."@".$server;
+
+ // construct answer
+ $answer = Array("status"=>"ok", "address"=>$address);
+
+ // return answer as json
+ echo json_encode($answer);
+ killme();
+}
+
+function jappixmini_settings(&$a, &$s) {
+ $username = get_pconfig(local_user(),'jappixmini','username');
+ $username = htmlentities($username);
+ $server = get_pconfig(local_user(),'jappixmini','server');
+ $server = htmlentities($server);
+ $bosh = get_pconfig(local_user(),'jappixmini','bosh');
+ $bosh = htmlentities($bosh);
+ $encrypted_password = get_pconfig(local_user(),'jappixmini','encrypted-password');
+ $autosubscribe = get_pconfig(local_user(),'jappixmini','autosubscribe');
+ $autosubscribe = intval($autosubscribe) ? ' checked="checked"' : '';
+ $autoapprove = get_pconfig(local_user(),'jappixmini','autoapprove');
+ $autoapprove = intval($autoapprove) ? ' checked="checked"' : '';
+ $activate = get_pconfig(local_user(),'jappixmini','activate');
+ $activate = intval($activate) ? ' checked="checked"' : '';
+
+ $s .= '';
+ $s .= '
Jappix Mini addon settings ';
+ $s .= '
';
+ $s .= 'Activate addon ';
+ $s .= ' ';
+ $s .= ' ';
+ $s .= 'Jabber username ';
+ $s .= ' ';
+ $s .= ' ';
+ $s .= 'Jabber server ';
+ $s .= ' ';
+ $s .= ' ';
+ $s .= 'Jabber BOSH host ';
+ $s .= ' ';
+ $s .= ' ';
+ $s .= 'Jabber password ';
+ $s .= ' ';
+ $onchange = "document.getElementById('jappixmini-encrypted-password').value = jappixmini_addon_encrypt_password(document.getElementById('jappixmini-password').value);";
+ $s .= ' ';
+ $s .= ' ';
+ $s .= 'Approve subscription requests from Friendica contacts automatically ';
+ $s .= ' ';
+ $s .= ' ';
+ $s .= 'Subscribe to Friendica contacts automatically ';
+ $s .= ' ';
+ $s .= ' ';
+ $s .= 'Purge list of jabber addresses of contacts ';
+ $s .= ' ';
+ $s .= ' ';
+ $s .= ' ';
+ $s .= '
';
+
+ $a->page['htmlhead'] .= "";
+}
+
+function jappixmini_settings_post(&$a,&$b) {
+ if(! local_user()) return;
+
+ if($_POST['jappixmini-submit']) {
+ set_pconfig(local_user(),'jappixmini','username',trim($b['jappixmini-username']));
+ set_pconfig(local_user(),'jappixmini','server',trim($b['jappixmini-server']));
+ set_pconfig(local_user(),'jappixmini','bosh',trim($b['jappixmini-bosh']));
+ set_pconfig(local_user(),'jappixmini','encrypted-password',trim($b['jappixmini-encrypted-password']));
+ set_pconfig(local_user(),'jappixmini','autosubscribe',intval($b['jappixmini-autosubscribe']));
+ set_pconfig(local_user(),'jappixmini','autoapprove',intval($b['jappixmini-autoapprove']));
+ set_pconfig(local_user(),'jappixmini','activate',intval($b['jappixmini-activate']));
+ info( 'Jappix Mini settings saved.' );
+
+ if (intval($b['jappixmini-purge'])) {
+ $uid = local_user();
+ q("DELETE FROM `pconfig` WHERE `uid`=$uid AND `cat`='jappixmini' and `k` LIKE 'id%%'");
+ info( 'List of addresses purged.' );
+ }
+ }
+}
+
+function jappixmini_script(&$a,&$s) {
+ if(! local_user()) return;
+
+ $activate = get_pconfig(local_user(),'jappixmini','activate');
+ if (!$activate) return;
+
+ $a->page['htmlhead'] .= ''."\r\n";
+ $a->page['htmlhead'] .= ''."\r\n";
+
+ $a->page['htmlhead'] .= ''."\r\n";
+ $a->page['htmlhead'] .= ''."\r\n";
+
+ $a->page['htmlhead'] .= ''."\r\n";
+
+ $username = get_pconfig(local_user(),'jappixmini','username');
+ $username = str_replace("'", "\\'", $username);
+ $server = get_pconfig(local_user(),'jappixmini','server');
+ $server = str_replace("'", "\\'", $server);
+ $bosh = get_pconfig(local_user(),'jappixmini','bosh');
+ $bosh = str_replace("'", "\\'", $bosh);
+ $encrypted_password = get_pconfig(local_user(),'jappixmini','encrypted-password');
+ $encrypted_password = str_replace("'", "\\'", $encrypted_password);
+
+ $autoapprove = get_pconfig(local_user(),'jappixmini','autoapprove');
+ $autoapprove = intval($autoapprove);
+ $autosubscribe = get_pconfig(local_user(),'jappixmini','autosubscribe');
+ $autosubscribe = intval($autosubscribe);
+
+ // get a list of jabber accounts of the contacts
+ $contacts = Array();
+ $uid = local_user();
+ $rows = q("SELECT `v` FROM `pconfig` WHERE `uid`=$uid AND `cat`='jappixmini' and `k` LIKE 'id%%'");
+ foreach ($rows as $row) {
+ $value = $row['v'];
+ $pos = strpos($value, ":");
+ $address = substr($value, $pos+1);
+ $contacts[] = $address;
+ }
+ $contacts_json = json_encode($contacts);
+
+ $a->page['htmlhead'] .= "";
+
+ return;
+}
+
+function jappixmini_login(&$a, &$o) {
+ // save hash of password using setDB
+ $o = str_replace("
status != "ok") continue;
+
+ $encrypted_address_hex = $answer->encrypted_address;
+ if (!$encrypted_address_hex) continue;
+ $encrypted_address = hex2bin($encrypted_address_hex);
+
+ $decrypted_address = "";
+ openssl_private_decrypt($encrypted_address, $decrypted_address, $prvkey);
+ if (!$decrypted_address) continue;
+
+ $address = $decrypted_address;
+ } else if ($pubkey) {
+ $encrypted_request = "";
+ openssl_public_encrypt($dfrn_id, $encrypted_request, $pubkey);
+ if (!$encrypted_request) continue;
+ $encrypted_request_hex = bin2hex($encrypted_request);
+
+ $retrieval_address = $base."?encrypted_for=".urlencode($dfrn_id)."&encrypted_request=".urlencode($encrypted_request_hex);
+
+ $answer_json = fetch_url($retrieval_address);
+ $answer = json_decode($answer_json);
+ if ($answer->status != "ok") continue;
+
+ $address = $answer->address;
+ if (!$address) continue;
+ }
+
+ // save address
+ set_pconfig($uid, "jappixmini", "id:$dfrn_id", "$now:$address");
+ }
+ }
+}
+
+function jappixmini_download_source(&$a,&$b) {
+ $b .= 'Jappix Mini ';
+ $b .= 'This site uses Jappix Mini by the Jappix authors , which is distributed under the terms of the GNU Affero General Public License .
';
+ $b .= 'You can download the source code .
';
+}
diff --git a/jappixmini/lib.js b/jappixmini/lib.js
new file mode 100644
index 00000000..dd95695f
--- /dev/null
+++ b/jappixmini/lib.js
@@ -0,0 +1,120 @@
+function jappixmini_addon_xor(str1, str2) {
+ if (str1.length != str2.length) throw "not same length";
+
+ encoded = "";
+
+ for (i=0; iReintroduce your Friendica password for chatting: ');
+ div.append($(" "));
+ input = $(' ')
+ div.append(input);
+ button = $(' ');
+ button.click(function(){
+ password = input.val();
+ jappixmini_addon_set_client_secret(password);
+ div.remove();
+ });
+ div.append(button);
+ $("body").append(div);
+ }
+
+ return client_secret;
+}
+
+function jappixmini_addon_encrypt_password(password) {
+ client_secret = jappixmini_addon_get_client_secret();
+
+ // add \0 to password until it has the same length as secret
+ if (client_secret.length
Date: Fri, 13 Apr 2012 18:53:32 +0200
Subject: [PATCH 09/96] new download path in jappixmini addon README
---
jappixmini/README | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/jappixmini/README b/jappixmini/README
index 2ef7c2f1..cf9eb70b 100644
--- a/jappixmini/README
+++ b/jappixmini/README
@@ -17,7 +17,7 @@ Jappix Mini (AGPL license) is not distributed with this addon. You need to
install it manually:
* Download latest zip file named friendica-addon-* from
- https://github.com/Leberwurscht/jappix-friendica-addon/tags and place the zip file
- in the addon folder. Make sure to name it 'jappix.zip' - the download link required
- by the AGPL points there.
+ https://github.com/Leberwurscht/jappix/tags and place the zip file in the addon
+ folder. Make sure to name it 'jappix.zip' - the download link required by the AGPL
+ points there.
* Unpack the zip file, rename the folder to 'jappix'. Do not delete jappix.zip.
From af99d51a7cc8916c3fe9802a25f82a64b4b3b526 Mon Sep 17 00:00:00 2001
From: Leberwurscht
Date: Sat, 14 Apr 2012 18:23:42 +0200
Subject: [PATCH 10/96] bug fixes and improvements
---
jappixmini/README | 3 +
jappixmini/jappixmini.php | 285 +++++++++++++++++++++++---------------
jappixmini/lib.js | 28 ++--
3 files changed, 196 insertions(+), 120 deletions(-)
diff --git a/jappixmini/README b/jappixmini/README
index cf9eb70b..bd1e2aa1 100644
--- a/jappixmini/README
+++ b/jappixmini/README
@@ -10,6 +10,9 @@ address of a BOSH proxy that works with your account.
The addon has an experimental autosubscribe and autosuggest functionality which tries
to add your Friendica contacts to your roster automatically.
+Limitations:
+ - can only handle Jabber passwords that are at most 39 characters long
+
Installation
------------
diff --git a/jappixmini/jappixmini.php b/jappixmini/jappixmini.php
index 88959448..08ba248d 100644
--- a/jappixmini/jappixmini.php
+++ b/jappixmini/jappixmini.php
@@ -23,7 +23,8 @@ and not to the server (at least as soon as the user is logged in). It can be sto
This encryption key could be the friendica password, but then this password would be stored in the browser in cleartext.
It is better to use a hash of the password.
The server should not be able to reconstruct the password, so we can't take the same hash the server stores. But we can
- use hash("some_prefix"+password). This will however not work with OpenID logins.
+ use hash("some_prefix"+password). This will however not work with OpenID logins, for this type of login the password must
+be queried manually.
Problem:
How to discover the jabber addresses of the friendica contacts?
@@ -37,17 +38,23 @@ We do not want to make the jabber address public.
Solution:
When two friendica users connect using DFRN, the relation gets a DFRN ID and a keypair is generated.
-Using this keypair, we can provide the jabber address only to contacs:
+Using this keypair, we can provide the jabber address only to contacts:
-Case 1: Alice has prvkey, Bob has pubkey.
- Alice encrypts request
- Bob decrypts the request, send jabber address unencrypted
- Alice reads address
+Alice:
+ signed_address = openssl_*_encrypt(alice_jabber_address)
+send signed_address to Bob, who does
+ trusted_address = openssl_*_decrypt(signed_address)
+ save trusted_address
+ encrypted_address = openssl_*_encrypt(bob_jabber_address)
+reply with encrypted_address to Alice, who does
+ decrypted_address = openssl_*_decrypt(encrypted_address)
+ save decrypted_address
-Case 2: Alice has prvkey, Bob has pubkey
- Alice send request
- Bob encrypts jabber address
- Alice decrypts jabber address
+Interface for this:
+GET /jappixmini/?role=%s&signed_address=%s&dfrn_id=%s
+
+Response:
+json({"status":"ok", "encrypted_address":"%s"})
*/
@@ -78,6 +85,8 @@ unregister_hook('about_hook', 'addon/jappixmini/jappixmini.php', 'jappixmini_dow
}
function jappixmini_plugin_admin(&$a, &$o) {
+ // display instructions and warnings on addon settings page for admin
+
if (!file_exists("addon/jappixmini/jappix")) {
$o .= 'You need to install the Jappix application, adapted for Friendica (see README).
';
}
@@ -91,81 +100,80 @@ function jappixmini_plugin_admin_post(&$a) {
function jappixmini_module() {}
function jappixmini_init(&$a) {
- // Here, other friendica sites can fetch the jabber address of local users.
- // Because we do not want to publish the addresses publicly, they are encrypted so
- // that only contacts can read it.
- $encrypt_for = $_REQUEST["encrypt_for"];
- if ($encrypt_for) {
- $r = q("SELECT * FROM `contact` WHERE LENGTH(`pubkey`) AND `dfrn-id` = '%s' LIMIT 1",
- dbesc($encrypt_for)
+ // module page where other Friendica sites can submit Jabber addresses to and also can query Jabber addresses
+ // of local users
+
+ $dfrn_id = $_REQUEST["dfrn_id"];
+ if (!$dfrn_id) killme();
+
+ $role = $_REQUEST["role"];
+ if ($role=="pub") {
+ $r = q("SELECT * FROM `contact` WHERE LENGTH(`pubkey`) AND `dfrn-id`='%s' LIMIT 1",
+ dbesc($dfrn_id)
);
if (!count($r)) killme();
- // get public key to encrypt address
- $pubkey = $r[0]['pubkey'];
+ $encrypt_func = openssl_public_encrypt;
+ $decrypt_func = openssl_public_decrypt;
+ $key = $r[0]["pubkey"];
+ } else if ($role=="prv") {
+ $r = q("SELECT * FROM `contact` WHERE LENGTH(`prvkey`) AND `issued-id`='%s' LIMIT 1",
+ dbesc($dfrn_id)
+ );
+ if (!count($r)) killme();
- // get jabber address
- $uid = $r[0]['uid'];
- $username = get_pconfig($uid, 'jappixmini', 'username');
- if (!$username) killme();
- $server = get_pconfig($uid, 'jappixmini', 'server');
- if (!$server) killme();
-
- $address = $username."@".$server;
-
- // encrypt address
- $encrypted = "";
- openssl_public_encrypt($address,$encrypted,$pubkey);
-
- // calculate hex representation of encrypted address
- $hex = bin2hex($encrypted);
-
- // construct answer
- $answer = Array("status"=>"ok", "encrypted_address"=>$hex);
-
- // return answer as json
- echo json_encode($answer);
+ $encrypt_func = openssl_private_encrypt;
+ $decrypt_func = openssl_private_decrypt;
+ $key = $r[0]["prvkey"];
+ } else {
killme();
}
- // If we have only a private key, other site sends encrypted request, we answer unencrypted.
- $encrypted_for = $_REQUEST["encrypted_for"];
- if (!$encrypted_for) killme();
+ $uid = $r[0]["uid"];
- $encrypted_request_hex = $_REQUEST["encrypted_request"];
- if (!$encrypted_request_hex) killme();
- $encrypted_request = hex2bin($encrypted_request_hex);
+ // save the Jabber address we received
+ try {
+ $signed_address_hex = $_REQUEST["signed_address"];
+ $signed_address = hex2bin($signed_address_hex);
- $r = q("SELECT * FROM `contact` WHERE LENGTH(`prvkey`) AND `issued-id` = '%s' LIMIT 1",
- dbesc($encrypted_for)
- );
- if (!count($r)) killme();
+ $trusted_address = "";
+ $decrypt_func($signed_address, $trusted_address, $key);
- // decrypt request, validate it
- $prvkey = $r[0]['prvkey'];
- $decrypted_request = "";
- openssl_private_decrypt($encrypted_request, $decrypted_request, $prvkey);
+ $now = intval(time());
+ set_pconfig($uid, "jappixmini", "id:$dfrn_id", "$now:$trusted_address");
+ } catch (Exception $e) {
+ }
- if ($decrypted_request!=$encrypted_for) killme();
+ // return the requested Jabber address
+ try {
+ $username = get_pconfig($uid, 'jappixmini', 'username');
+ $server = get_pconfig($uid, 'jappixmini', 'server');
+ $address = "$username@$server";
- // get jabber address
- $uid = $r[0]['uid'];
- $username = get_pconfig($uid, 'jappixmini', 'username');
- if (!$username) killme();
- $server = get_pconfig($uid, 'jappixmini', 'server');
- if (!$server) killme();
+ $encrypted_address = "";
+ $encrypt_func($address, $encrypted_address, $key);
- $address = $username."@".$server;
+ $encrypted_address_hex = bin2hex($encrypted_address);
- // construct answer
- $answer = Array("status"=>"ok", "address"=>$address);
+ $answer = Array(
+ "status"=>"ok",
+ "encrypted_address"=>$encrypted_address_hex
+ );
- // return answer as json
- echo json_encode($answer);
- killme();
+ $answer_json = json_encode($answer);
+ echo $answer_json;
+ killme();
+ } catch (Exception $e) {
+ killme();
+ }
}
function jappixmini_settings(&$a, &$s) {
+ // addon settings for a user
+
+ $activate = get_pconfig(local_user(),'jappixmini','activate');
+ $activate = intval($activate) ? ' checked="checked"' : '';
+
$username = get_pconfig(local_user(),'jappixmini','username');
$username = htmlentities($username);
$server = get_pconfig(local_user(),'jappixmini','server');
@@ -177,8 +185,6 @@ function jappixmini_settings(&$a, &$s) {
$autosubscribe = intval($autosubscribe) ? ' checked="checked"' : '';
$autoapprove = get_pconfig(local_user(),'jappixmini','autoapprove');
$autoapprove = intval($autoapprove) ? ' checked="checked"' : '';
- $activate = get_pconfig(local_user(),'jappixmini','activate');
- $activate = intval($activate) ? ' checked="checked"' : '';
$s .= '';
$s .= '
Jappix Mini addon settings ';
@@ -206,7 +212,7 @@ function jappixmini_settings(&$a, &$s) {
$s .= '
Subscribe to Friendica contacts automatically ';
$s .= '
';
$s .= '
';
- $s .= '
Purge list of jabber addresses of contacts ';
+ $s .= '
Purge internal list of jabber addresses of contacts ';
$s .= '
';
$s .= '
';
$s .= '
';
@@ -220,20 +226,32 @@ function jappixmini_settings(&$a, &$s) {
}
function jappixmini_settings_post(&$a,&$b) {
+ // save addon settings for a user
+
if(! local_user()) return;
+ $uid = local_user();
if($_POST['jappixmini-submit']) {
- set_pconfig(local_user(),'jappixmini','username',trim($b['jappixmini-username']));
- set_pconfig(local_user(),'jappixmini','server',trim($b['jappixmini-server']));
- set_pconfig(local_user(),'jappixmini','bosh',trim($b['jappixmini-bosh']));
- set_pconfig(local_user(),'jappixmini','encrypted-password',trim($b['jappixmini-encrypted-password']));
- set_pconfig(local_user(),'jappixmini','autosubscribe',intval($b['jappixmini-autosubscribe']));
- set_pconfig(local_user(),'jappixmini','autoapprove',intval($b['jappixmini-autoapprove']));
- set_pconfig(local_user(),'jappixmini','activate',intval($b['jappixmini-activate']));
+ $purge = intval($b['jappixmini-purge']);
+
+ $username = trim($b['jappixmini-username']);
+ $old_username = get_pconfig($uid,'jappixmini','username');
+ if ($username!=$old_username) $purge = 1;
+
+ $server = trim($b['jappixmini-server']);
+ $old_server = get_pconfig($uid,'jappixmini','server');
+ if ($server!=$old_server) $purge = 1;
+
+ set_pconfig($uid,'jappixmini','username',$username);
+ set_pconfig($uid,'jappixmini','server',$server);
+ set_pconfig($uid,'jappixmini','bosh',trim($b['jappixmini-bosh']));
+ set_pconfig($uid,'jappixmini','encrypted-password',trim($b['jappixmini-encrypted-password']));
+ set_pconfig($uid,'jappixmini','autosubscribe',intval($b['jappixmini-autosubscribe']));
+ set_pconfig($uid,'jappixmini','autoapprove',intval($b['jappixmini-autoapprove']));
+ set_pconfig($uid,'jappixmini','activate',intval($b['jappixmini-activate']));
info( 'Jappix Mini settings saved.' );
- if (intval($b['jappixmini-purge'])) {
- $uid = local_user();
+ if ($purge) {
q("DELETE FROM `pconfig` WHERE `uid`=$uid AND `cat`='jappixmini' and `k` LIKE 'id%%'");
info( 'List of addresses purged.' );
}
@@ -241,6 +259,8 @@ function jappixmini_settings_post(&$a,&$b) {
}
function jappixmini_script(&$a,&$s) {
+ // adds the script to the page header which starts Jappix Mini
+
if(! local_user()) return;
$activate = get_pconfig(local_user(),'jappixmini','activate');
@@ -251,6 +271,7 @@ function jappixmini_script(&$a,&$s) {
$a->page['htmlhead'] .= ''."\r\n";
$a->page['htmlhead'] .= ''."\r\n";
+ $a->page['htmlhead'] .= ''."\r\n";
$a->page['htmlhead'] .= ''."\r\n";
@@ -271,18 +292,35 @@ function jappixmini_script(&$a,&$s) {
// get a list of jabber accounts of the contacts
$contacts = Array();
$uid = local_user();
- $rows = q("SELECT `v` FROM `pconfig` WHERE `uid`=$uid AND `cat`='jappixmini' and `k` LIKE 'id%%'");
+ $rows = q("SELECT * FROM `pconfig` WHERE `uid`=$uid AND `cat`='jappixmini' and `k` LIKE 'id%%'");
foreach ($rows as $row) {
+ $key = $row['k'];
+ $pos = strpos($key, ":");
+ $dfrn_id = substr($key, $pos+1);
+ $r = q("SELECT `name` FROM `contact` WHERE `uid`=$uid AND `dfrn-id`='%s' OR `issued-id`='%s'",
+ dbesc($dfrn_id),
+ dbesc($dfrn_id)
+ );
+ $name = $r[0]["name"];
+
$value = $row['v'];
$pos = strpos($value, ":");
$address = substr($value, $pos+1);
- $contacts[] = $address;
+ if (!$address) continue;
+ if (!$name) $name = $address;
+
+ $contacts[$address] = $name;
}
$contacts_json = json_encode($contacts);
+ // get nickname
+ $r = q("SELECT `username` FROM `user` WHERE `uid`=$uid");
+ $nickname = json_encode($r[0]["username"]);
+
+ // add javascript to start Jappix Mini
$a->page['htmlhead'] .= "";
@@ -291,7 +329,16 @@ function jappixmini_script(&$a,&$s) {
}
function jappixmini_login(&$a, &$o) {
- // save hash of password using setDB
+ // for setDB, needed by jappixmini_addon_set_client_secret
+ $a->page['htmlhead'] .= ''."\r\n";
+
+ // for str_sha1, needed by jappixmini_addon_set_client_secret
+ $a->page['htmlhead'] .= ''."\r\n";
+
+ // for jappixmini_addon_set_client_secret
+ $a->page['htmlhead'] .= ''."\r\n";
+
+ // save hash of password
$o = str_replace("
status != "ok") continue;
+ if ($answer->status != "ok") throw new Exception();
$encrypted_address_hex = $answer->encrypted_address;
- if (!$encrypted_address_hex) continue;
+ if (!$encrypted_address_hex) throw new Exception();
+
$encrypted_address = hex2bin($encrypted_address_hex);
+ if (!$encrypted_address) throw new Exception();
+ // decrypt address
+ $decrypted_address = "";
+ $decrypt_func($encrypted_address, $decrypted_address, $key);
+ if (!$decrypted_address) throw new Exception();
+ } catch (Exception $e) {
$decrypted_address = "";
- openssl_private_decrypt($encrypted_address, $decrypted_address, $prvkey);
- if (!$decrypted_address) continue;
-
- $address = $decrypted_address;
- } else if ($pubkey) {
- $encrypted_request = "";
- openssl_public_encrypt($dfrn_id, $encrypted_request, $pubkey);
- if (!$encrypted_request) continue;
- $encrypted_request_hex = bin2hex($encrypted_request);
-
- $retrieval_address = $base."?encrypted_for=".urlencode($dfrn_id)."&encrypted_request=".urlencode($encrypted_request_hex);
-
- $answer_json = fetch_url($retrieval_address);
- $answer = json_decode($answer_json);
- if ($answer->status != "ok") continue;
-
- $address = $answer->address;
- if (!$address) continue;
}
// save address
- set_pconfig($uid, "jappixmini", "id:$dfrn_id", "$now:$address");
+ set_pconfig($uid, "jappixmini", "id:$dfrn_id", "$now:$decrypted_address");
}
}
}
function jappixmini_download_source(&$a,&$b) {
+ // Jappix Mini source download link on About page
+
$b .= 'Jappix Mini ';
$b .= 'This site uses Jappix Mini by the Jappix authors , which is distributed under the terms of the GNU Affero General Public License .
';
$b .= 'You can download the source code .
';
diff --git a/jappixmini/lib.js b/jappixmini/lib.js
index dd95695f..6ab4644d 100644
--- a/jappixmini/lib.js
+++ b/jappixmini/lib.js
@@ -15,14 +15,23 @@ function jappixmini_addon_xor(str1, str2) {
}
function jappixmini_addon_set_client_secret(password) {
- client_secret = str_sha1("client_secret:"+password);
+ if (!password) return;
+
+ salt1 = "h8doCRekWto0njyQohKpdx6BN0UTyC6N";
+ salt2 = "jdX8OwFC1kWAq3s9uOyAcE8g3UNNO5t3";
+
+ client_secret1 = str_sha1(salt1+password);
+ client_secret2 = str_sha1(salt2+password);
+ client_secret = client_secret1 + client_secret2;
+
setDB('jappix-mini', 'client_secret', client_secret);
+ console.log("client secret set");
}
function jappixmini_addon_get_client_secret() {
client_secret = getDB('jappix-mini', 'client_secret');
if (client_secret===null) {
- div = $('Reintroduce your Friendica password for chatting:
');
+ div = $('Retype your Friendica password for chatting:
');
div.append($(" "));
input = $(' ')
div.append(input);
@@ -43,7 +52,7 @@ function jappixmini_addon_encrypt_password(password) {
client_secret = jappixmini_addon_get_client_secret();
// add \0 to password until it has the same length as secret
- if (client_secret.lengthclient_secret.length-1) throw "password too long";
while (password.length
Date: Sat, 14 Apr 2012 18:41:11 +0200
Subject: [PATCH 11/96] improve README of jappixmini addon
---
jappixmini/README | 20 ++++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/jappixmini/README b/jappixmini/README
index bd1e2aa1..7818d41a 100644
--- a/jappixmini/README
+++ b/jappixmini/README
@@ -4,14 +4,18 @@ Jappix Mini Plugin
This quick-and-dirty addon allows you to add a Jabber-based, Facebook-like chat
to Friendica. It uses Jappix Mini.
-It is necessary to use a BOSH proxy - so to use this plugin, you need to know the
-address of a BOSH proxy that works with your account.
+It is necessary to use a BOSH proxy - so to use this plugin, you need to know
+the address of a BOSH proxy that works with your account. The BOSH server of
+the Jappix project (https://bind.jappix.com/) is not locked to a specific XMPP
+provider, but keep in mind that only personal usage is approved according to
+http://codingteam.net/project/jappix/doc/BoshServer.
-The addon has an experimental autosubscribe and autosuggest functionality which tries
-to add your Friendica contacts to your roster automatically.
+The addon has an experimental autosubscribe and autosuggest functionality which
+tries to add your Friendica contacts to your roster automatically.
Limitations:
- - can only handle Jabber passwords that are at most 39 characters long
+ - The addon can only handle Jabber passwords that are at most 39 characters
+ long.
Installation
------------
@@ -20,7 +24,7 @@ Jappix Mini (AGPL license) is not distributed with this addon. You need to
install it manually:
* Download latest zip file named friendica-addon-* from
- https://github.com/Leberwurscht/jappix/tags and place the zip file in the addon
- folder. Make sure to name it 'jappix.zip' - the download link required by the AGPL
- points there.
+ https://github.com/Leberwurscht/jappix/tags and place the zip file in the
+ addon folder. Make sure to name it 'jappix.zip' - the download link required
+ by the AGPL points there.
* Unpack the zip file, rename the folder to 'jappix'. Do not delete jappix.zip.
From 011bf47e59f84e2e184b8364a44b9edbdcee6342 Mon Sep 17 00:00:00 2001
From: Leberwurscht
Date: Sat, 14 Apr 2012 18:49:23 +0200
Subject: [PATCH 12/96] add jappixmini/jappix/ and jappixmini/jappix.zip to
.gitignore
---
.gitignore | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 .gitignore
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..77d48406
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+jappixmini/jappix/
+jappixmini/jappix.zip
From c376bc162bf611345d7796e4844e57cc9ae780c5 Mon Sep 17 00:00:00 2001
From: Leberwurscht
Date: Sun, 15 Apr 2012 12:20:53 +0200
Subject: [PATCH 13/96] make bosh proxy optional, add subscribe functionality
and some more improvements of jappixmini addon
---
jappixmini/README | 9 ++-
jappixmini/jappixmini.php | 116 ++++++++++++++++++++++++++++++++++----
jappixmini/lib.js | 24 ++++++--
3 files changed, 128 insertions(+), 21 deletions(-)
diff --git a/jappixmini/README b/jappixmini/README
index 7818d41a..822f4391 100644
--- a/jappixmini/README
+++ b/jappixmini/README
@@ -4,17 +4,20 @@ Jappix Mini Plugin
This quick-and-dirty addon allows you to add a Jabber-based, Facebook-like chat
to Friendica. It uses Jappix Mini.
-It is necessary to use a BOSH proxy - so to use this plugin, you need to know
-the address of a BOSH proxy that works with your account. The BOSH server of
+It is necessary to use a BOSH host - so to use this plugin, each users need to
+know the address of a BOSH host that works with his account. The BOSH server of
the Jappix project (https://bind.jappix.com/) is not locked to a specific XMPP
provider, but keep in mind that only personal usage is approved according to
http://codingteam.net/project/jappix/doc/BoshServer.
+If you have a larger server, it is recommended that you install your own BOSH
+server, add it to the tag in jappix/store/conf/hosts.xml, and disable
+the bosh proxy in jappix/store/conf/main.xml.
The addon has an experimental autosubscribe and autosuggest functionality which
tries to add your Friendica contacts to your roster automatically.
Limitations:
- - The addon can only handle Jabber passwords that are at most 39 characters
+ - Jabber passwords can only be encrypted if they are at most 39 characters
long.
Installation
diff --git a/jappixmini/jappixmini.php b/jappixmini/jappixmini.php
index 08ba248d..3e16a6ab 100644
--- a/jappixmini/jappixmini.php
+++ b/jappixmini/jappixmini.php
@@ -93,6 +93,9 @@ function jappixmini_plugin_admin(&$a, &$o) {
else if (!file_exists("addon/jappixmini/jappix.zip")) {
$o .= 'The source archive jappix.zip does not exist. This is probably a violation of the Jappix License (see README).
';
}
+ else {
+ $o .= 'Jappix is installed.
';
+ }
}
function jappixmini_plugin_admin_post(&$a) {
@@ -103,6 +106,8 @@ function jappixmini_init(&$a) {
// module page where other Friendica sites can submit Jabber addresses to and also can query Jabber addresses
// of local users
+ if (!file_exists("addon/jappixmini/jappix")) killme();
+
$dfrn_id = $_REQUEST["dfrn_id"];
if (!$dfrn_id) killme();
@@ -171,6 +176,8 @@ function jappixmini_init(&$a) {
function jappixmini_settings(&$a, &$s) {
// addon settings for a user
+ if (!file_exists("addon/jappixmini/jappix")) return;
+
$activate = get_pconfig(local_user(),'jappixmini','activate');
$activate = intval($activate) ? ' checked="checked"' : '';
@@ -180,11 +187,26 @@ function jappixmini_settings(&$a, &$s) {
$server = htmlentities($server);
$bosh = get_pconfig(local_user(),'jappixmini','bosh');
$bosh = htmlentities($bosh);
- $encrypted_password = get_pconfig(local_user(),'jappixmini','encrypted-password');
+ $password = get_pconfig(local_user(),'jappixmini','password');
$autosubscribe = get_pconfig(local_user(),'jappixmini','autosubscribe');
$autosubscribe = intval($autosubscribe) ? ' checked="checked"' : '';
$autoapprove = get_pconfig(local_user(),'jappixmini','autoapprove');
$autoapprove = intval($autoapprove) ? ' checked="checked"' : '';
+ $encrypt = intval(get_pconfig(local_user(),'jappixmini','encrypt'));
+ $encrypt_checked = $encrypt ? ' checked="checked"' : '';
+ $encrypt_disabled = $encrypt ? '' : ' disabled="disabled"';
+
+ if (!$activate) {
+ // load scripts if not yet activated so that password can be saved
+ $a->page['htmlhead'] .= ''."\r\n";
+ $a->page['htmlhead'] .= ''."\r\n";
+
+ $a->page['htmlhead'] .= ''."\r\n";
+ $a->page['htmlhead'] .= ''."\r\n";
+ $a->page['htmlhead'] .= ''."\r\n";
+
+ $a->page['htmlhead'] .= ''."\r\n";
+ }
$s .= '';
$s .= '
Jappix Mini addon settings ';
@@ -198,13 +220,25 @@ function jappixmini_settings(&$a, &$s) {
$s .= 'Jabber server ';
$s .= ' ';
$s .= ' ';
- $s .= 'Jabber BOSH host ';
- $s .= ' ';
- $s .= ' ';
+
+ $conf = file_get_contents("addon/jappixmini/jappix/store/conf/main.xml");
+ preg_match("/(.*)<\/bosh_proxy>/", $conf, $matches);
+ if ($matches[1]=="on") {
+ $s .= 'Jabber BOSH host ';
+ $s .= ' ';
+ $s .= ' ';
+ }
+
$s .= 'Jabber password ';
- $s .= ' ';
- $onchange = "document.getElementById('jappixmini-encrypted-password').value = jappixmini_addon_encrypt_password(document.getElementById('jappixmini-password').value);";
- $s .= ' ';
+ $s .= ' ';
+ $s .= ' ';
+ $s .= ' ';
+ $onchange = "document.getElementById('jappixmini-friendica-password').disabled = !this.checked;jappixmini_set_password();";
+ $s .= 'Encrypt Jabber password with Friendica password (recommended) ';
+ $s .= ' ';
+ $s .= ' ';
+ $s .= 'Friendica password ';
+ $s .= ' ';
$s .= ' ';
$s .= 'Approve subscription requests from Friendica contacts automatically ';
$s .= ' ';
@@ -216,11 +250,34 @@ function jappixmini_settings(&$a, &$s) {
$s .= ' ';
$s .= ' ';
$s .= ' ';
+ $s .= ' ';
$s .= ' ';
$a->page['htmlhead'] .= "";
}
@@ -228,10 +285,26 @@ function jappixmini_settings(&$a, &$s) {
function jappixmini_settings_post(&$a,&$b) {
// save addon settings for a user
+ if (!file_exists("addon/jappixmini/jappix")) return;
+
if(! local_user()) return;
$uid = local_user();
if($_POST['jappixmini-submit']) {
+ $encrypt = intval($b['jappixmini-encrypt']);
+ if ($encrypt) {
+ // check that Jabber password was encrypted with correct Friendica password
+ $friendica_password = trim($b['jappixmini-friendica-password']);
+ $encrypted = hash('whirlpool',$friendica_password);
+ $r = q("SELECT * FROM `user` WHERE `uid`=$uid AND `password`='%s'",
+ dbesc($encrypted)
+ );
+ if (!count($r)) {
+ info("Wrong friendica password!");
+ return;
+ }
+ }
+
$purge = intval($b['jappixmini-purge']);
$username = trim($b['jappixmini-username']);
@@ -245,10 +318,11 @@ function jappixmini_settings_post(&$a,&$b) {
set_pconfig($uid,'jappixmini','username',$username);
set_pconfig($uid,'jappixmini','server',$server);
set_pconfig($uid,'jappixmini','bosh',trim($b['jappixmini-bosh']));
- set_pconfig($uid,'jappixmini','encrypted-password',trim($b['jappixmini-encrypted-password']));
+ set_pconfig($uid,'jappixmini','password',trim($b['jappixmini-encrypted-password']));
set_pconfig($uid,'jappixmini','autosubscribe',intval($b['jappixmini-autosubscribe']));
set_pconfig($uid,'jappixmini','autoapprove',intval($b['jappixmini-autoapprove']));
set_pconfig($uid,'jappixmini','activate',intval($b['jappixmini-activate']));
+ set_pconfig($uid,'jappixmini','encrypt',$encrypt);
info( 'Jappix Mini settings saved.' );
if ($purge) {
@@ -261,6 +335,7 @@ function jappixmini_settings_post(&$a,&$b) {
function jappixmini_script(&$a,&$s) {
// adds the script to the page header which starts Jappix Mini
+ if (!file_exists("addon/jappixmini/jappix")) return;
if(! local_user()) return;
$activate = get_pconfig(local_user(),'jappixmini','activate');
@@ -281,14 +356,23 @@ function jappixmini_script(&$a,&$s) {
$server = str_replace("'", "\\'", $server);
$bosh = get_pconfig(local_user(),'jappixmini','bosh');
$bosh = str_replace("'", "\\'", $bosh);
- $encrypted_password = get_pconfig(local_user(),'jappixmini','encrypted-password');
- $encrypted_password = str_replace("'", "\\'", $encrypted_password);
+ $encrypt = get_pconfig(local_user(),'jappixmini','encrypt');
+ $encrypt = intval($encrypt);
+ $password = get_pconfig(local_user(),'jappixmini','password');
+ $password = str_replace("'", "\\'", $password);
$autoapprove = get_pconfig(local_user(),'jappixmini','autoapprove');
$autoapprove = intval($autoapprove);
$autosubscribe = get_pconfig(local_user(),'jappixmini','autosubscribe');
$autosubscribe = intval($autosubscribe);
+ // deactivate bosh host if proxy is off
+ $conf = file_get_contents("addon/jappixmini/jappix/store/conf/main.xml");
+ preg_match("/(.*)<\/bosh_proxy>/", $conf, $matches);
+ if ($matches[1]!="on") {
+ $bosh = '';
+ }
+
// get a list of jabber accounts of the contacts
$contacts = Array();
$uid = local_user();
@@ -320,7 +404,7 @@ function jappixmini_script(&$a,&$s) {
// add javascript to start Jappix Mini
$a->page['htmlhead'] .= "";
@@ -329,6 +413,10 @@ function jappixmini_script(&$a,&$s) {
}
function jappixmini_login(&$a, &$o) {
+ // create client secret on login to be able to encrypt jabber passwords
+
+ if (!file_exists("addon/jappixmini/jappix")) return;
+
// for setDB, needed by jappixmini_addon_set_client_secret
$a->page['htmlhead'] .= ''."\r\n";
@@ -345,6 +433,8 @@ function jappixmini_login(&$a, &$o) {
function jappixmini_cron(&$a, $d) {
// For autosubscribe/autoapprove, we need to maintain a list of jabber addresses of our contacts.
+ if (!file_exists("addon/jappixmini/jappix")) return;
+
// go through list of users with jabber enabled
$users = q("SELECT `uid` FROM `pconfig` WHERE `cat`='jappixmini' AND (`k`='autosubscribe' OR `k`='autoapprove') AND `v`='1'");
@@ -437,6 +527,8 @@ function jappixmini_cron(&$a, $d) {
function jappixmini_download_source(&$a,&$b) {
// Jappix Mini source download link on About page
+ if (!file_exists("addon/jappixmini/jappix")) return;
+
$b .= 'Jappix Mini ';
$b .= 'This site uses Jappix Mini by the Jappix authors , which is distributed under the terms of the GNU Affero General Public License .
';
$b .= 'You can download the source code .
';
diff --git a/jappixmini/lib.js b/jappixmini/lib.js
index 6ab4644d..610a8312 100644
--- a/jappixmini/lib.js
+++ b/jappixmini/lib.js
@@ -108,12 +108,26 @@ function jappixmini_manage_roster(contacts, autoapprove, autosubscribe) {
}
}
-function jappixmini_addon_start(server, username, bosh, encrypted_password, nickname) {
+function jappixmini_addon_subscribe() {
+ if (!window.con) {
+ alert("Not connected.");
+ return;
+ }
+
+ xid = prompt("Jabber address");
+ sendSubscribe(xid, "subscribe");
+}
+
+function jappixmini_addon_start(server, username, bosh, encrypted, password, nickname) {
+ // decrypt password
+ if (encrypted)
+ password = jappixmini_addon_decrypt_password(password);
+
// check if settings have changed, reinitialize jappix mini if this is the case
settings_identifier = str_sha1(server);
settings_identifier += str_sha1(username);
settings_identifier += str_sha1(bosh);
- settings_identifier += str_sha1(encrypted_password);
+ settings_identifier += str_sha1(password);
settings_identifier += str_sha1(nickname);
saved_identifier = getDB("jappix-mini", "settings_identifier");
@@ -121,10 +135,8 @@ function jappixmini_addon_start(server, username, bosh, encrypted_password, nick
setDB("jappix-mini", "settings_identifier", settings_identifier);
// set bosh host
- HOST_BOSH = HOST_BOSH+"?host_bosh="+encodeURI(bosh);
-
- // decrypt password
- password = jappixmini_addon_decrypt_password(encrypted_password);
+ if (bosh)
+ HOST_BOSH = HOST_BOSH+"?host_bosh="+encodeURI(bosh);
// start jappix mini
MINI_NICKNAME = nickname;
From 3064bf55cbb2111d9b3b4c64fdb9792d6709003a Mon Sep 17 00:00:00 2001
From: Leberwurscht
Date: Sun, 15 Apr 2012 13:13:05 +0200
Subject: [PATCH 14/96] add jappixmini.tgz
---
jappixmini.tgz | Bin 0 -> 7397 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 jappixmini.tgz
diff --git a/jappixmini.tgz b/jappixmini.tgz
new file mode 100644
index 0000000000000000000000000000000000000000..6420352647672a99ba8968a766d44b3acd6a684b
GIT binary patch
literal 7397
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
literal 0
HcmV?d00001
From 90289ef561d688844e7929fdb45bc28ebb849d99 Mon Sep 17 00:00:00 2001
From: Leberwurscht
Date: Sun, 15 Apr 2012 13:21:32 +0200
Subject: [PATCH 15/96] add missing
to jappixmini settings
---
jappixmini/jappixmini.php | 3 +++
1 file changed, 3 insertions(+)
diff --git a/jappixmini/jappixmini.php b/jappixmini/jappixmini.php
index 3e16a6ab..0e1cc5df 100644
--- a/jappixmini/jappixmini.php
+++ b/jappixmini/jappixmini.php
@@ -209,6 +209,7 @@ function jappixmini_settings(&$a, &$s) {
}
$s .= '';
+
$s .= '
Jappix Mini addon settings ';
$s .= '
';
$s .= 'Activate addon ';
@@ -253,6 +254,8 @@ function jappixmini_settings(&$a, &$s) {
$s .= ' ';
$s .= '
';
+ $s .= '
';
+
$a->page['htmlhead'] .= "';
+ } else {
+ $use = get_pconfig(local_user(),'mathjax','use');
+ if ($use) {
+ $b .= '';
+ }
+ }
+}
+function mathjax_plugin_admin_post (&$a) {
+ $baseurl = ((x($_POST, 'baseurl')) ? trim($_POST['baseurl']) : 'http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML');
+ set_config('mathjax','baseurl',$baseurl);
+ info( t('Settings updated.'). EOL);
+}
+function mathjax_plugin_admin (&$a, &$o) {
+ $t = file_get_contents( dirname(__file__)."/admin.tpl");
+ if (get_config('mathjax','baseurl','') == '') {
+ set_config('mathjax','baseurl','http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML');
+ }
+ $o = replace_macros( $t, array(
+ '$baseurl' => array('baseurl', t('MathJax Base URL'), get_config('mathjax','baseurl' ), t('The URL for the javascript file that should be included to use MathJax. Can be either the MathJax CDN or another installation of MathJax.')),
+ ));
+}
From 6034ba3263fcfea911e914b38f6cdac188c7a596 Mon Sep 17 00:00:00 2001
From: Leberwurscht
Date: Sun, 15 Apr 2012 22:57:25 +0200
Subject: [PATCH 20/96] jappixmini: wait for client secret to be set
---
jappixmini/jappixmini.php | 8 ++-
jappixmini/lib.js | 126 +++++++++++++++++++++-----------------
2 files changed, 77 insertions(+), 57 deletions(-)
diff --git a/jappixmini/jappixmini.php b/jappixmini/jappixmini.php
index b68114d1..80fc237f 100644
--- a/jappixmini/jappixmini.php
+++ b/jappixmini/jappixmini.php
@@ -270,7 +270,9 @@ function jappixmini_settings(&$a, &$s) {
if (friendica_password) {
jappixmini_addon_set_client_secret(friendica_password.value);
- password.value = jappixmini_addon_encrypt_password(clear_password.value);
+ jappixmini_addon_encrypt_password(clear_password.value, function(encrypted_password){
+ password.value = encrypted_password;
+ });
}
}
else {
@@ -283,7 +285,9 @@ function jappixmini_settings(&$a, &$s) {
password = document.getElementById('jappixmini-password');
clear_password = document.getElementById('jappixmini-clear-password');
if (encrypt) {
- clear_password.value = jappixmini_addon_decrypt_password(password.value);
+ jappixmini_addon_decrypt_password(password.value, function(decrypted_password){
+ clear_password.value = decrypted_password;
+ });
}
else {
clear_password.value = password.value;
diff --git a/jappixmini/lib.js b/jappixmini/lib.js
index 610a8312..94e465d1 100644
--- a/jappixmini/lib.js
+++ b/jappixmini/lib.js
@@ -24,59 +24,71 @@ function jappixmini_addon_set_client_secret(password) {
client_secret2 = str_sha1(salt2+password);
client_secret = client_secret1 + client_secret2;
- setDB('jappix-mini', 'client_secret', client_secret);
+ setDB('jappix-mini', 'client-secret', client_secret);
console.log("client secret set");
}
-function jappixmini_addon_get_client_secret() {
- client_secret = getDB('jappix-mini', 'client_secret');
+function jappixmini_addon_get_client_secret(callback) {
+ client_secret = getDB('jappix-mini', 'client-secret');
if (client_secret===null) {
- div = $('Retype your Friendica password for chatting:
');
- div.append($(" "));
- input = $(' ')
- div.append(input);
- button = $(' ');
+ div = document.getElementById("#jappixmini-password-query-div");
+
+ if (!div) {
+ div = $('Retype your Friendica password for chatting:
');
+
+ input = $(' ')
+ div.append(input);
+
+ button = $(' ');
+ div.append(button);
+
+ $("body").append(div);
+ }
+
button.click(function(){
- password = input.val();
+ password = $("#jappixmini-password-query-input").val();
jappixmini_addon_set_client_secret(password);
div.remove();
+
+ client_secret = getDB('jappix-mini', 'client-secret');
+ callback(client_secret);
});
- div.append(button);
- $("body").append(div);
}
-
- return client_secret;
+ else {
+ callback(client_secret);
+ }
}
-function jappixmini_addon_encrypt_password(password) {
- client_secret = jappixmini_addon_get_client_secret();
+function jappixmini_addon_encrypt_password(password, callback) {
+ jappixmini_addon_get_client_secret(function(client_secret){
+ // add \0 to password until it has the same length as secret
+ if (password.length>client_secret.length-1) throw "password too long";
+ while (password.lengthclient_secret.length-1) throw "password too long";
- while (password.length
Date: Sun, 15 Apr 2012 23:13:37 +0200
Subject: [PATCH 21/96] jappixmini: move jappixmini_manage_roster into callback
---
jappixmini/jappixmini.php | 3 +--
jappixmini/lib.js | 4 +++-
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/jappixmini/jappixmini.php b/jappixmini/jappixmini.php
index 80fc237f..3b03743d 100644
--- a/jappixmini/jappixmini.php
+++ b/jappixmini/jappixmini.php
@@ -418,8 +418,7 @@ function jappixmini_script(&$a,&$s) {
// add javascript to start Jappix Mini
$a->page['htmlhead'] .= "";
diff --git a/jappixmini/lib.js b/jappixmini/lib.js
index 94e465d1..351d03f9 100644
--- a/jappixmini/lib.js
+++ b/jappixmini/lib.js
@@ -130,7 +130,7 @@ function jappixmini_addon_subscribe() {
sendSubscribe(xid, "subscribe");
}
-function jappixmini_addon_start(server, username, bosh, encrypted, password, nickname) {
+function jappixmini_addon_start(server, username, bosh, encrypted, password, nickname, contacts, autoapprove, autosubscribe) {
handler = function(password){
// check if settings have changed, reinitialize jappix mini if this is the case
settings_identifier = str_sha1(server);
@@ -149,7 +149,9 @@ function jappixmini_addon_start(server, username, bosh, encrypted, password, nic
// start jappix mini
MINI_NICKNAME = nickname;
+ console.log("launchMini");
launchMini(true, false, server, username, password);
+ jappixmini_manage_roster(contacts, autoapprove, autosubscribe)
}
// decrypt password if necessary
From bec530c9bb1c09113985a5dd7425bbc786115ddc Mon Sep 17 00:00:00 2001
From: Leberwurscht
Date: Sun, 15 Apr 2012 23:19:37 +0200
Subject: [PATCH 22/96] jappixmini: use setPersistent to avoid password query
window
---
jappixmini/lib.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/jappixmini/lib.js b/jappixmini/lib.js
index 351d03f9..f62e5b8f 100644
--- a/jappixmini/lib.js
+++ b/jappixmini/lib.js
@@ -24,12 +24,12 @@ function jappixmini_addon_set_client_secret(password) {
client_secret2 = str_sha1(salt2+password);
client_secret = client_secret1 + client_secret2;
- setDB('jappix-mini', 'client-secret', client_secret);
+ setPersistent('jappix-mini', 'client-secret', client_secret);
console.log("client secret set");
}
function jappixmini_addon_get_client_secret(callback) {
- client_secret = getDB('jappix-mini', 'client-secret');
+ client_secret = getPersistent('jappix-mini', 'client-secret');
if (client_secret===null) {
div = document.getElementById("#jappixmini-password-query-div");
@@ -50,7 +50,7 @@ function jappixmini_addon_get_client_secret(callback) {
jappixmini_addon_set_client_secret(password);
div.remove();
- client_secret = getDB('jappix-mini', 'client-secret');
+ client_secret = getPersistent('jappix-mini', 'client-secret');
callback(client_secret);
});
}
From e7d4a17428539920553efef56838dba974acb289 Mon Sep 17 00:00:00 2001
From: Leberwurscht
Date: Mon, 16 Apr 2012 01:04:45 +0200
Subject: [PATCH 23/96] jappixmini: improve roster management
---
jappixmini/lib.js | 52 +++++++++++++++++++++++++++++++++++++++++------
1 file changed, 46 insertions(+), 6 deletions(-)
diff --git a/jappixmini/lib.js b/jappixmini/lib.js
index f62e5b8f..d1858ff8 100644
--- a/jappixmini/lib.js
+++ b/jappixmini/lib.js
@@ -111,13 +111,53 @@ function jappixmini_manage_roster(contacts, autoapprove, autosubscribe) {
});
// autosubscribe
- if (autosubscribe) {
- for (i=0; i
Date: Mon, 16 Apr 2012 01:42:00 +0200
Subject: [PATCH 24/96] update jappixmini archive
---
jappixmini.tgz | Bin 7397 -> 8018 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
diff --git a/jappixmini.tgz b/jappixmini.tgz
index 6420352647672a99ba8968a766d44b3acd6a684b..82025d711e9a07a3fe71f36dee60c8af1d8aede9 100644
GIT binary patch
literal 8018
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
Ui4!MIoa`=t1EGej^8la#0J*1!LjV8(
literal 7397
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
From 360502d385b44a4d029e8a7947d5d29a4b7fec6d Mon Sep 17 00:00:00 2001
From: Leberwurscht
Date: Mon, 16 Apr 2012 19:26:44 +0200
Subject: [PATCH 25/96] jappixmini: logging message when cron hook is called
---
jappixmini/jappixmini.php | 1 +
1 file changed, 1 insertion(+)
diff --git a/jappixmini/jappixmini.php b/jappixmini/jappixmini.php
index 3b03743d..a648aba7 100644
--- a/jappixmini/jappixmini.php
+++ b/jappixmini/jappixmini.php
@@ -450,6 +450,7 @@ function jappixmini_cron(&$a, $d) {
// go through list of users with jabber enabled
$users = q("SELECT `uid` FROM `pconfig` WHERE `cat`='jappixmini' AND (`k`='autosubscribe' OR `k`='autoapprove') AND `v`='1'");
+ logger("jappixmini: Update list of contacts' jabber accounts for ".count($users)." users.");
foreach ($users as $row) {
$uid = $row["uid"];
From af0366a9e22629a0050e8144884e6877bf103299 Mon Sep 17 00:00:00 2001
From: Leberwurscht
Date: Mon, 16 Apr 2012 19:28:23 +0200
Subject: [PATCH 26/96] jappixmini: add text field for configuration help
---
jappixmini/jappixmini.php | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/jappixmini/jappixmini.php b/jappixmini/jappixmini.php
index a648aba7..f476753a 100644
--- a/jappixmini/jappixmini.php
+++ b/jappixmini/jappixmini.php
@@ -69,6 +69,15 @@ register_hook('cron', 'addon/jappixmini/jappixmini.php', 'jappixmini_cron');
// Jappix source download as required by AGPL
register_hook('about_hook', 'addon/jappixmini/jappixmini.php', 'jappixmini_download_source');
+
+// set standard info text
+$info_text = get_config("jappixmini", "infotext");
+if (!$info_text) set_config("jappixmini", "infotext",
+ "To get the chat working, you need to know a BOSH host which works with your Jabber account. ".
+ "An example of a BOSH server that works for all accounts is https://bind.jappix.com/, but keep ".
+ "in mind that the BOSH server can read along all chat messages. If you know that your Jabber ".
+ "server also provides an own BOSH server, it is much better to use this one!"
+);
}
@@ -96,9 +105,21 @@ function jappixmini_plugin_admin(&$a, &$o) {
else {
$o .= 'Jappix is installed.
';
}
+
+ // info text field
+ $o .= 'Info text to help users with configuration (important if you want to provide your own BOSH host!): ';
+ $info_text = get_config("jappixmini", "infotext");
+ $o .= ''.htmlentities($info_text).' ';
+ $o .= ' ';
}
function jappixmini_plugin_admin_post(&$a) {
+ // set info text
+ $submit = $_REQUEST['jappixmini-admin-settings'];
+ if ($submit) {
+ $info_text = $_REQUEST['jappixmini-infotext'];
+ set_config("jappixmini", "infotext", $info_text);
+ }
}
function jappixmini_module() {}
@@ -200,6 +221,10 @@ function jappixmini_settings(&$a, &$s) {
$encrypt_checked = $encrypt ? ' checked="checked"' : '';
$encrypt_disabled = $encrypt ? '' : ' disabled="disabled"';
+ $info_text = get_config("jappixmini", "infotext");
+ $info_text = htmlentities($info_text);
+ $info_text = str_replace("\n", " ", $info_text);
+
if (!$activate) {
// load scripts if not yet activated so that password can be saved
$a->page['htmlhead'] .= ''."\r\n";
@@ -254,6 +279,7 @@ function jappixmini_settings(&$a, &$s) {
$s .= 'Purge internal list of jabber addresses of contacts ';
$s .= ' ';
$s .= ' ';
+ if ($info_text) $s .= ' Configuration help:'.$info_text.'
';
$s .= ' ';
$s .= ' ';
$s .= '';
From a53167da1a68b977ba5a67342dfa17fd7e8a2974 Mon Sep 17 00:00:00 2001
From: Leberwurscht
Date: Mon, 16 Apr 2012 21:04:45 +0200
Subject: [PATCH 27/96] jappixmini: warn if cron job does not get executed
---
jappixmini/jappixmini.php | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/jappixmini/jappixmini.php b/jappixmini/jappixmini.php
index f476753a..88233063 100644
--- a/jappixmini/jappixmini.php
+++ b/jappixmini/jappixmini.php
@@ -106,6 +106,10 @@ function jappixmini_plugin_admin(&$a, &$o) {
$o .= 'Jappix is installed.
';
}
+ // warn if cron job has not yet been executed
+ $cron_run = get_config("jappixmini", "last_cron_execution");
+ if (!$cron_run) $o .= "Warning: The cron job has not yet been executed. If this message is still there after some time (usually 10 minutes), this means that autosubscribe and autoaccept will not work.
";
+
// info text field
$o .= 'Info text to help users with configuration (important if you want to provide your own BOSH host!): ';
$info_text = get_config("jappixmini", "infotext");
@@ -472,6 +476,8 @@ function jappixmini_login(&$a, &$o) {
function jappixmini_cron(&$a, $d) {
// For autosubscribe/autoapprove, we need to maintain a list of jabber addresses of our contacts.
+ set_config("jappixmini", "last_cron_execution", $d);
+
if (!file_exists("addon/jappixmini/jappix")) return;
// go through list of users with jabber enabled
From b4aa673c5d4f1ffce367e2d317b12384bf5ba0c7 Mon Sep 17 00:00:00 2001
From: Leberwurscht
Date: Mon, 16 Apr 2012 21:16:24 +0200
Subject: [PATCH 28/96] jappixmini: make BOSH proxy optional
---
jappixmini/README | 5 +++--
jappixmini/jappixmini.php | 43 ++++++++++++++++++++++++---------------
jappixmini/lib.js | 13 +++++++-----
3 files changed, 38 insertions(+), 23 deletions(-)
diff --git a/jappixmini/README b/jappixmini/README
index 822f4391..222b85a5 100644
--- a/jappixmini/README
+++ b/jappixmini/README
@@ -10,8 +10,9 @@ the Jappix project (https://bind.jappix.com/) is not locked to a specific XMPP
provider, but keep in mind that only personal usage is approved according to
http://codingteam.net/project/jappix/doc/BoshServer.
If you have a larger server, it is recommended that you install your own BOSH
-server, add it to the tag in jappix/store/conf/hosts.xml, and disable
-the bosh proxy in jappix/store/conf/main.xml.
+server and recommend it using the configuration help field. If it is on the
+same server, you can also deactivate the BOSH proxy. This should improve the
+performance.
The addon has an experimental autosubscribe and autosuggest functionality which
tries to add your Friendica contacts to your roster automatically.
diff --git a/jappixmini/jappixmini.php b/jappixmini/jappixmini.php
index 88233063..b177507a 100644
--- a/jappixmini/jappixmini.php
+++ b/jappixmini/jappixmini.php
@@ -70,7 +70,7 @@ register_hook('cron', 'addon/jappixmini/jappixmini.php', 'jappixmini_cron');
// Jappix source download as required by AGPL
register_hook('about_hook', 'addon/jappixmini/jappixmini.php', 'jappixmini_download_source');
-// set standard info text
+// set standard configuration
$info_text = get_config("jappixmini", "infotext");
if (!$info_text) set_config("jappixmini", "infotext",
"To get the chat working, you need to know a BOSH host which works with your Jabber account. ".
@@ -78,6 +78,9 @@ if (!$info_text) set_config("jappixmini", "infotext",
"in mind that the BOSH server can read along all chat messages. If you know that your Jabber ".
"server also provides an own BOSH server, it is much better to use this one!"
);
+
+$bosh_proxy = get_config("jappixmini", "bosh_proxy");
+if ($bosh_proxy==="") set_config("jappixmini", "bosh_proxy", 1);
}
@@ -110,10 +113,18 @@ function jappixmini_plugin_admin(&$a, &$o) {
$cron_run = get_config("jappixmini", "last_cron_execution");
if (!$cron_run) $o .= "Warning: The cron job has not yet been executed. If this message is still there after some time (usually 10 minutes), this means that autosubscribe and autoaccept will not work.
";
+ // bosh proxy
+ $bosh_proxy = intval(get_config("jappixmini", "bosh_proxy"));
+ $bosh_proxy = intval($bosh_proxy) ? ' checked="checked"' : '';
+ $o .= 'Activate BOSH proxy ';
+ $o .= ' ';
+
// info text field
- $o .= 'Info text to help users with configuration (important if you want to provide your own BOSH host!): ';
$info_text = get_config("jappixmini", "infotext");
- $o .= ''.htmlentities($info_text).' ';
+ $o .= 'Info text to help users with configuration (important if you want to provide your own BOSH host!): ';
+ $o .= ''.htmlentities($info_text).'
';
+
+ // submit button
$o .= ' ';
}
@@ -122,7 +133,9 @@ function jappixmini_plugin_admin_post(&$a) {
$submit = $_REQUEST['jappixmini-admin-settings'];
if ($submit) {
$info_text = $_REQUEST['jappixmini-infotext'];
+ $bosh_proxy = intval($_REQUEST['jappixmini-proxy']);
set_config("jappixmini", "infotext", $info_text);
+ set_config("jappixmini", "bosh_proxy", $bosh_proxy);
}
}
@@ -255,13 +268,9 @@ function jappixmini_settings(&$a, &$s) {
$s .= ' ';
$s .= ' ';
- $conf = file_get_contents("addon/jappixmini/jappix/store/conf/main.xml");
- preg_match("/(.*)<\/bosh_proxy>/", $conf, $matches);
- if ($matches[1]=="on") {
- $s .= 'Jabber BOSH host ';
- $s .= ' ';
- $s .= ' ';
- }
+ $s .= 'Jabber BOSH host ';
+ $s .= ' ';
+ $s .= ' ';
$s .= 'Jabber password ';
$s .= ' ';
@@ -410,11 +419,13 @@ function jappixmini_script(&$a,&$s) {
$autosubscribe = get_pconfig(local_user(),'jappixmini','autosubscribe');
$autosubscribe = intval($autosubscribe);
- // deactivate bosh host if proxy is off
- $conf = file_get_contents("addon/jappixmini/jappix/store/conf/main.xml");
- preg_match("/(.*)<\/bosh_proxy>/", $conf, $matches);
- if ($matches[1]!="on") {
- $bosh = '';
+ // set proxy if necessary
+ $use_proxy = get_config('jappixmini','bosh_proxy');
+ if ($use_proxy) {
+ $proxy = $a->get_baseurl().'/addon/jappixmini/jappix/php/bosh.php';
+ }
+ else {
+ $proxy = "";
}
// get a list of jabber accounts of the contacts
@@ -448,7 +459,7 @@ function jappixmini_script(&$a,&$s) {
// add javascript to start Jappix Mini
$a->page['htmlhead'] .= "";
diff --git a/jappixmini/lib.js b/jappixmini/lib.js
index d1858ff8..8f4a2669 100644
--- a/jappixmini/lib.js
+++ b/jappixmini/lib.js
@@ -170,7 +170,7 @@ function jappixmini_addon_subscribe() {
sendSubscribe(xid, "subscribe");
}
-function jappixmini_addon_start(server, username, bosh, encrypted, password, nickname, contacts, autoapprove, autosubscribe) {
+function jappixmini_addon_start(server, username, proxy, bosh, encrypted, password, nickname, contacts, autoapprove, autosubscribe) {
handler = function(password){
// check if settings have changed, reinitialize jappix mini if this is the case
settings_identifier = str_sha1(server);
@@ -183,15 +183,18 @@ function jappixmini_addon_start(server, username, bosh, encrypted, password, nic
if (saved_identifier != settings_identifier) removeDB('jappix-mini', 'dom');
setDB("jappix-mini", "settings_identifier", settings_identifier);
- // set bosh host
- if (bosh)
- HOST_BOSH = HOST_BOSH+"?host_bosh="+encodeURI(bosh);
+ // set HOST_BOSH
+ if (proxy)
+ HOST_BOSH = proxy+"?host_bosh="+encodeURI(bosh);
+ else
+ HOST_BOSH = bosh;
// start jappix mini
MINI_NICKNAME = nickname;
+ LOCK_HOST = "off";
console.log("launchMini");
launchMini(true, false, server, username, password);
- jappixmini_manage_roster(contacts, autoapprove, autosubscribe)
+ jappixmini_manage_roster(contacts, autoapprove, autosubscribe)
}
// decrypt password if necessary
From 479d8ad96fc3f9398f80a6b2418a400afce5d767 Mon Sep 17 00:00:00 2001
From: Leberwurscht
Date: Mon, 16 Apr 2012 21:40:23 +0200
Subject: [PATCH 29/96] jappixmini: move modified BOSH proxy out of Jappix
source tree to be able to use vanilla Jappix. This is possible because
bosh.php is under MIT license.
---
jappixmini/README | 11 +--
jappixmini/jappixmini.php | 19 +++-
jappixmini/proxy.php | 178 ++++++++++++++++++++++++++++++++++++++
3 files changed, 201 insertions(+), 7 deletions(-)
create mode 100644 jappixmini/proxy.php
diff --git a/jappixmini/README b/jappixmini/README
index 222b85a5..b833dae4 100644
--- a/jappixmini/README
+++ b/jappixmini/README
@@ -27,8 +27,9 @@ Installation
Jappix Mini (AGPL license) is not distributed with this addon. You need to
install it manually:
-* Download latest zip file named friendica-addon-* from
- https://github.com/Leberwurscht/jappix/tags and place the zip file in the
- addon folder. Make sure to name it 'jappix.zip' - the download link required
- by the AGPL points there.
-* Unpack the zip file, rename the folder to 'jappix'. Do not delete jappix.zip.
+* Download latest jappix version from https://github.com/jappix/jappix/tags to
+ addon/jappixmini/jappix.zip. Really make sure you name it 'jappix.zip' - the
+ download link required by the AGPL points there.
+* Unpack the zip file to addon/jappixmini/jappix/. Do not delete jappix.zip.
+* Delete the file addon/jappixmini/jappix/index.php. This is important, because
+ otherwise the installer of Jappix would be publicly accessible.
diff --git a/jappixmini/jappixmini.php b/jappixmini/jappixmini.php
index b177507a..93689a1e 100644
--- a/jappixmini/jappixmini.php
+++ b/jappixmini/jappixmini.php
@@ -100,7 +100,22 @@ function jappixmini_plugin_admin(&$a, &$o) {
// display instructions and warnings on addon settings page for admin
if (!file_exists("addon/jappixmini/jappix")) {
- $o .= 'You need to install the Jappix application, adapted for Friendica (see README).
';
+ $o .= 'You need to install the Jappix application (see README).
';
+ }
+ else if (file_exists("addon/jappixmini/jappix/index.php")) {
+ // try to delete automatically
+ try {
+ unlink("addon/jappixmini/jappix/index.php");
+ }
+ catch (Exception $e) {}
+
+ // warn admin if this is not possible
+ if (file_exists("addon/jappixmini/jappix/index.php"))
+ $o .= 'You must delete addon/jappixmini/jappix/index.php (see README).
';
+ else {
+ info("Deleted addon/jappixmini/jappix/index.php automatically.");
+ $o .= 'Jappix is installed.
';
+ }
}
else if (!file_exists("addon/jappixmini/jappix.zip")) {
$o .= 'The source archive jappix.zip does not exist. This is probably a violation of the Jappix License (see README).
';
@@ -422,7 +437,7 @@ function jappixmini_script(&$a,&$s) {
// set proxy if necessary
$use_proxy = get_config('jappixmini','bosh_proxy');
if ($use_proxy) {
- $proxy = $a->get_baseurl().'/addon/jappixmini/jappix/php/bosh.php';
+ $proxy = $a->get_baseurl().'/addon/jappixmini/proxy.php';
}
else {
$proxy = "";
diff --git a/jappixmini/proxy.php b/jappixmini/proxy.php
new file mode 100644
index 00000000..72de341c
--- /dev/null
+++ b/jappixmini/proxy.php
@@ -0,0 +1,178 @@
+ array(
+ 'method' => 'POST',
+ 'content' => $data
+ )
+ );
+
+ $parameters['http']['header'] = $headers;
+
+ // Change default timeout
+ ini_set('default_socket_timeout', 30);
+
+ // Create the connection
+ $stream = @stream_context_create($parameters);
+ $connection = @fopen($HOST_BOSH, 'rb', false, $stream);
+
+ // Failed to connect!
+ if($connection == false) {
+ header('Status: 502 Proxy Error', true, 502);
+ exit('HTTP/1.1 502 Proxy Error');
+ }
+
+ // Allow stream blocking to handle incoming BOSH data
+ @stream_set_blocking($connection, true);
+
+ // Get the output content
+ $output = @stream_get_contents($connection);
+}
+
+// Cache headers
+header('Cache-Control: no-cache, must-revalidate');
+header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
+
+// POST output
+if($method == 'POST') {
+ // XML header
+ header('Content-Type: text/xml; charset=utf-8');
+
+ if(!$output)
+ echo('');
+ else
+ echo($output);
+}
+
+// GET output
+if($method == 'GET') {
+ // JSON header
+ header('Content-type: application/json');
+
+ // Encode output to JSON
+ $json_output = json_encode($output);
+
+ if(($output == false) || ($output == '') || ($json_output == 'null'))
+ echo($callback.'({"reply":""});');
+ else
+ echo($callback.'({"reply":'.$json_output.'});');
+}
+
+// Close the connection
+if($use_curl)
+ curl_close($connection);
+else
+ @fclose($connection);
+
+?>
From a656c73bf5f444804ceb55c939929729e60ad5d1 Mon Sep 17 00:00:00 2001
From: Leberwurscht
Date: Mon, 16 Apr 2012 23:38:00 +0200
Subject: [PATCH 30/96] jappixmini: increase client priority
---
jappixmini/lib.js | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/jappixmini/lib.js b/jappixmini/lib.js
index 8f4a2669..d02f5d5c 100644
--- a/jappixmini/lib.js
+++ b/jappixmini/lib.js
@@ -194,6 +194,11 @@ function jappixmini_addon_start(server, username, proxy, bosh, encrypted, passwo
LOCK_HOST = "off";
console.log("launchMini");
launchMini(true, false, server, username, password);
+
+ // increase priority over other Jabber clients
+ priority = 101;
+ sendPresence(null,null,priority);
+
jappixmini_manage_roster(contacts, autoapprove, autosubscribe)
}
From 80a0ccb6c80746bd929b5d2b377c995bb2423606 Mon Sep 17 00:00:00 2001
From: Leberwurscht
Date: Mon, 16 Apr 2012 23:38:21 +0200
Subject: [PATCH 31/96] jappixmini: improve README
---
jappixmini/README | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/jappixmini/README b/jappixmini/README
index b833dae4..95dd7b02 100644
--- a/jappixmini/README
+++ b/jappixmini/README
@@ -32,4 +32,5 @@ install it manually:
download link required by the AGPL points there.
* Unpack the zip file to addon/jappixmini/jappix/. Do not delete jappix.zip.
* Delete the file addon/jappixmini/jappix/index.php. This is important, because
- otherwise the installer of Jappix would be publicly accessible.
+ otherwise the installer of Jappix would be publicly accessible. If you are
+ lucky, the file is deleted automatically by the addon.
From 74799ed141ba4d2da96c1bb20f463a365ba6a72a Mon Sep 17 00:00:00 2001
From: Leberwurscht
Date: Mon, 16 Apr 2012 23:39:21 +0200
Subject: [PATCH 32/96] jappixmini: throw error when decrypt_password fails
---
jappixmini/lib.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/jappixmini/lib.js b/jappixmini/lib.js
index d02f5d5c..8e698654 100644
--- a/jappixmini/lib.js
+++ b/jappixmini/lib.js
@@ -84,7 +84,7 @@ function jappixmini_addon_decrypt_password(encrypted_password, callback) {
// remove \0
first_null = password.indexOf("\0")
- // TODO: check first_null==null
+ if (first_null==-1) throw "Decrypted password does not contain \\0";
password = password.substr(0, first_null);
callback(password);
From 1da347bd864f4ead028e7b5e36d1f224277c2786 Mon Sep 17 00:00:00 2001
From: Leberwurscht
Date: Mon, 16 Apr 2012 23:39:46 +0200
Subject: [PATCH 33/96] jappixmini: reinitialize jappix if proxy changed
---
jappixmini/lib.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/jappixmini/lib.js b/jappixmini/lib.js
index 8e698654..43a6470b 100644
--- a/jappixmini/lib.js
+++ b/jappixmini/lib.js
@@ -175,6 +175,7 @@ function jappixmini_addon_start(server, username, proxy, bosh, encrypted, passwo
// check if settings have changed, reinitialize jappix mini if this is the case
settings_identifier = str_sha1(server);
settings_identifier += str_sha1(username);
+ settings_identifier += str_sha1(proxy);
settings_identifier += str_sha1(bosh);
settings_identifier += str_sha1(password);
settings_identifier += str_sha1(nickname);
From 35090ac2a7392a5f807bdb59b1b11a713f0928e8 Mon Sep 17 00:00:00 2001
From: Leberwurscht
Date: Mon, 16 Apr 2012 23:40:40 +0200
Subject: [PATCH 34/96] jappixmini: do not ask user but wait if a subscriber
claims to be from Friendica
---
jappixmini/lib.js | 29 ++++++++++++++++++++++++++---
1 file changed, 26 insertions(+), 3 deletions(-)
diff --git a/jappixmini/lib.js b/jappixmini/lib.js
index 43a6470b..f11fada6 100644
--- a/jappixmini/lib.js
+++ b/jappixmini/lib.js
@@ -99,10 +99,28 @@ function jappixmini_manage_roster(contacts, autoapprove, autosubscribe) {
var from = fullXID(getStanzaFrom(presence));
var xid = bareXID(from);
+ var pstatus = presence.getStatus();
- approve = true;
- if ((!autoapprove) || contacts[xid]===undefined)
- approve = confirm("Accept "+xid+" for chat?");
+ if (autoapprove && contacts[xid]!==undefined) {
+ // approve known address
+ approve = true;
+ console.log("Approve known Friendica contact "+xid+".");
+ }
+ else if (autoapprove && pstatus && pstatus.indexOf("Friendica")!=-1) {
+ // Unknown address claims to be a Friendica contact.
+ // This is probably because the other side knows our
+ // address, but we do not know the other side yet.
+ // But it's only a matter of time, so wait - do not
+ // approve yet and do not annoy the user by asking.
+ approve = false;
+ console.log("Do not approve unknown Friendica contact "+xid+" - wait instead.");
+ }
+ else {
+ // In all other cases, ask the user.
+ message = "Accept "+xid+" for chat?";
+ if (pstatus) message += "\n\nStatus:\n"+pstatus;
+ approve = confirm(message);
+ }
if (approve) {
acceptSubscribe(xid, contacts[xid]);
@@ -144,6 +162,11 @@ function jappixmini_manage_roster(contacts, autoapprove, autosubscribe) {
var presence = new JSJaCPresence();
presence.setTo(xid);
presence.setType("subscribe");
+
+ // must contain the word "~Friendica" so the other side knows
+ // how to handle this
+ presence.setStatus("I'm "+MINI_NICKNAME+" from ~Friendica.\n[machine-generated message]");
+
con.send(presence);
console.log("subscribed to "+xid);
From 20f1d45d9b7aa57f8e1ad27ed673a03884e8eebb Mon Sep 17 00:00:00 2001
From: Leberwurscht
Date: Tue, 17 Apr 2012 00:01:30 +0200
Subject: [PATCH 35/96] jappixmini: combine included jappix scripts
---
jappixmini/jappixmini.php | 19 ++++---------------
1 file changed, 4 insertions(+), 15 deletions(-)
diff --git a/jappixmini/jappixmini.php b/jappixmini/jappixmini.php
index 93689a1e..6840c1d9 100644
--- a/jappixmini/jappixmini.php
+++ b/jappixmini/jappixmini.php
@@ -260,11 +260,7 @@ function jappixmini_settings(&$a, &$s) {
if (!$activate) {
// load scripts if not yet activated so that password can be saved
$a->page['htmlhead'] .= ''."\r\n";
- $a->page['htmlhead'] .= ''."\r\n";
-
- $a->page['htmlhead'] .= ''."\r\n";
- $a->page['htmlhead'] .= ''."\r\n";
- $a->page['htmlhead'] .= ''."\r\n";
+ $a->page['htmlhead'] .= ''."\r\n";
$a->page['htmlhead'] .= ''."\r\n";
}
@@ -410,11 +406,7 @@ function jappixmini_script(&$a,&$s) {
if (!$activate) return;
$a->page['htmlhead'] .= ''."\r\n";
- $a->page['htmlhead'] .= ''."\r\n";
-
- $a->page['htmlhead'] .= ''."\r\n";
- $a->page['htmlhead'] .= ''."\r\n";
- $a->page['htmlhead'] .= ''."\r\n";
+ $a->page['htmlhead'] .= ''."\r\n";
$a->page['htmlhead'] .= ''."\r\n";
@@ -486,11 +478,8 @@ function jappixmini_login(&$a, &$o) {
if (!file_exists("addon/jappixmini/jappix")) return;
- // for setDB, needed by jappixmini_addon_set_client_secret
- $a->page['htmlhead'] .= ''."\r\n";
-
- // for str_sha1, needed by jappixmini_addon_set_client_secret
- $a->page['htmlhead'] .= ''."\r\n";
+ // for setDB and str_sha1, needed by jappixmini_addon_set_client_secret
+ $a->page['htmlhead'] .= ''."\r\n";
// for jappixmini_addon_set_client_secret
$a->page['htmlhead'] .= ''."\r\n";
From 7ea17b33c8f7ddce61d2e06bea2d14e99826948c Mon Sep 17 00:00:00 2001
From: Leberwurscht
Date: Tue, 17 Apr 2012 00:04:34 +0200
Subject: [PATCH 36/96] jappixmini: reduce unnecessary logging
---
jappixmini/lib.js | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/jappixmini/lib.js b/jappixmini/lib.js
index f11fada6..dd2d7955 100644
--- a/jappixmini/lib.js
+++ b/jappixmini/lib.js
@@ -143,7 +143,6 @@ function jappixmini_manage_roster(contacts, autoapprove, autosubscribe) {
xid = $(this).attr("jid");
name = $(this).attr("name");
subscription = $(this).attr("subscription");
- console.log(xid+" "+subscription);
// ignore accounts not in the list
if (contacts[xid]===undefined) return;
@@ -178,7 +177,7 @@ function jappixmini_manage_roster(contacts, autoapprove, autosubscribe) {
item.setAttribute('name', contacts[xid]);
item.appendChild(iq.buildNode('group', {'xmlns': NS_ROSTER}, "Friendica"));
con.send(iq);
- console.log("added to roster "+xid);
+ console.log("added to roster: "+xid);
}
});
}
@@ -216,7 +215,6 @@ function jappixmini_addon_start(server, username, proxy, bosh, encrypted, passwo
// start jappix mini
MINI_NICKNAME = nickname;
LOCK_HOST = "off";
- console.log("launchMini");
launchMini(true, false, server, username, password);
// increase priority over other Jabber clients
From 0164bdf11e1dd8ca2b89955a1c390057858e3f8e Mon Sep 17 00:00:00 2001
From: Leberwurscht
Date: Tue, 17 Apr 2012 00:41:22 +0200
Subject: [PATCH 37/96] jappixmini: add to Friendica group and authorize
---
jappixmini/lib.js | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/jappixmini/lib.js b/jappixmini/lib.js
index dd2d7955..bbbc7e34 100644
--- a/jappixmini/lib.js
+++ b/jappixmini/lib.js
@@ -147,9 +147,24 @@ function jappixmini_manage_roster(contacts, autoapprove, autosubscribe) {
// ignore accounts not in the list
if (contacts[xid]===undefined) return;
- // TODO: add to Friendica group
+ // add to Friendica group if necessary
+ groups = [];
+ $(this).find('group').each(function() {
+ var group_text = $(this).text();
+ if(group_text) groups.push(group_text);
+ });
- // TODO: unblock and authorize if necessary
+ if ($.inArray("Friendica", groups)==-1) {
+ console.log("Add "+xid+" to Friendica group.");
+ groups.push("Friendica");
+ sendRoster(xid, null, null, groups);
+ console.log("Added "+xid+" to Friendica group.");
+ }
+
+ // authorize if necessary
+ if (subscription=="to") {
+ sendSubscribe(xid, 'subscribed');
+ }
// remove from list
delete contacts[xid];
From 54d98689fab14505149d4dab770ba034990a0920 Mon Sep 17 00:00:00 2001
From: friendica
Date: Mon, 16 Apr 2012 22:57:34 -0700
Subject: [PATCH 38/96] small fix to mathjax.
---
convpath.tgz | Bin 0 -> 814 bytes
facebook.tgz | Bin 17977 -> 18227 bytes
fromgplus/fromgplus.php | 181 --------------------------------------
fromgplus/tofriendica.php | 128 ---------------------------
mathjax.tgz | Bin 0 -> 2169 bytes
mathjax/mathjax.php | 4 +-
6 files changed, 3 insertions(+), 310 deletions(-)
create mode 100644 convpath.tgz
delete mode 100755 fromgplus/fromgplus.php
delete mode 100644 fromgplus/tofriendica.php
create mode 100644 mathjax.tgz
diff --git a/convpath.tgz b/convpath.tgz
new file mode 100644
index 0000000000000000000000000000000000000000..2c43d3d380e73b78e1021da199148676ab1a91ba
GIT binary patch
literal 814
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
literal 17977
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