Enter your database connection information below to initialize the database.
-StatusNet bundles a number of libraries for ease of installation. You can see what bundled libraries you are using, versus what libraries are installed on your server.
'.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'
',$cas_url); + $this->printHTMLFooter(); + phpCAS::traceExit(); + exit(); + } + +// /** +// * This method is used to logout from CAS. +// * @param $url a URL that will be transmitted to the CAS server (to come back to when logged out) +// * @public +// */ +// function logout($url = "") { +// phpCAS::traceBegin(); +// $cas_url = $this->getServerLogoutURL(); +// // v0.4.14 sebastien.gougeon at univ-rennes1.fr +// // header('Location: '.$cas_url); +// if ( $url != "" ) { +// // Adam Moore 1.0.0RC2 +// $url = '?service=' . $url . '&url=' . $url; +// } +// header('Location: '.$cas_url . $url); +// session_unset(); +// session_destroy(); +// $this->printHTMLHeader($this->getString(CAS_STR_LOGOUT)); +// printf(''.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'
',$cas_url); +// $this->printHTMLFooter(); +// phpCAS::traceExit(); +// exit(); +// } + + /** + * This method is used to logout from CAS. + * @params $params an array that contains the optional url and service parameters that will be passed to the CAS server + * @public + */ + function logout($params) { + phpCAS::traceBegin(); + $cas_url = $this->getServerLogoutURL(); + $paramSeparator = '?'; + if (isset($params['url'])) { + $cas_url = $cas_url . $paramSeparator . "url=" . urlencode($params['url']); + $paramSeparator = '&'; + } + if (isset($params['service'])) { + $cas_url = $cas_url . $paramSeparator . "service=" . urlencode($params['service']); + } + header('Location: '.$cas_url); + session_unset(); + session_destroy(); + $this->printHTMLHeader($this->getString(CAS_STR_LOGOUT)); + printf(''.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'
',$cas_url); + $this->printHTMLFooter(); + phpCAS::traceExit(); + exit(); + } + + /** + * @return true if the current request is a logout request. + * @private + */ + function isLogoutRequest() { + return !empty($_POST['logoutRequest']); + } + + /** + * @return true if a logout request is allowed. + * @private + */ + function isLogoutRequestAllowed() { + } + + /** + * This method handles logout requests. + * @param $check_client true to check the client bofore handling the request, + * false not to perform any access control. True by default. + * @param $allowed_clients an array of host names allowed to send logout requests. + * By default, only the CAs server (declared in the constructor) will be allowed. + * @public + */ + function handleLogoutRequests($check_client=true, $allowed_clients=false) { + phpCAS::traceBegin(); + if (!$this->isLogoutRequest()) { + phpCAS::log("Not a logout request"); + phpCAS::traceEnd(); + return; + } + phpCAS::log("Logout requested"); + phpCAS::log("SAML REQUEST: ".$_POST['logoutRequest']); + if ($check_client) { + if (!$allowed_clients) { + $allowed_clients = array( $this->getServerHostname() ); + } + $client_ip = $_SERVER['REMOTE_ADDR']; + $client = gethostbyaddr($client_ip); + phpCAS::log("Client: ".$client); + $allowed = false; + foreach ($allowed_clients as $allowed_client) { + if ($client == $allowed_client) { + phpCAS::log("Allowed client '".$allowed_client."' matches, logout request is allowed"); + $allowed = true; + break; + } else { + phpCAS::log("Allowed client '".$allowed_client."' does not match"); + } + } + if (!$allowed) { + phpCAS::error("Unauthorized logout request from client '".$client."'"); + printf("Unauthorized!"); + phpCAS::traceExit(); + exit(); + } + } else { + phpCAS::log("No access control set"); + } + // Extract the ticket from the SAML Request + preg_match("|Storing PGT `'.$pgt.'\' (id=`'.$pgt_iou.'\').
'; + $this->storePGT($pgt,$pgt_iou); + $this->printHTMLFooter(); + phpCAS::traceExit(); + } + + /** @} */ + + // ######################################################################## + // PGT STORAGE + // ######################################################################## + /** + * @addtogroup internalPGTStorage + * @{ + */ + + /** + * an instance of a class inheriting of PGTStorage, used to deal with PGT + * storage. Created by CASClient::setPGTStorageFile() or CASClient::setPGTStorageDB(), used + * by CASClient::setPGTStorageFile(), CASClient::setPGTStorageDB() and CASClient::initPGTStorage(). + * + * @hideinitializer + * @private + */ + var $_pgt_storage = null; + + /** + * This method is used to initialize the storage of PGT's. + * Halts on error. + * + * @private + */ + function initPGTStorage() + { + // if no SetPGTStorageXxx() has been used, default to file + if ( !is_object($this->_pgt_storage) ) { + $this->setPGTStorageFile(); + } + + // initializes the storage + $this->_pgt_storage->init(); + } + + /** + * This method stores a PGT. Halts on error. + * + * @param $pgt the PGT to store + * @param $pgt_iou its corresponding Iou + * + * @private + */ + function storePGT($pgt,$pgt_iou) + { + // ensure that storage is initialized + $this->initPGTStorage(); + // writes the PGT + $this->_pgt_storage->write($pgt,$pgt_iou); + } + + /** + * This method reads a PGT from its Iou and deletes the corresponding storage entry. + * + * @param $pgt_iou the PGT Iou + * + * @return The PGT corresponding to the Iou, FALSE when not found. + * + * @private + */ + function loadPGT($pgt_iou) + { + // ensure that storage is initialized + $this->initPGTStorage(); + // read the PGT + return $this->_pgt_storage->read($pgt_iou); + } + + /** + * This method is used to tell phpCAS to store the response of the + * CAS server to PGT requests onto the filesystem. + * + * @param $format the format used to store the PGT's (`plain' and `xml' allowed) + * @param $path the path where the PGT's should be stored + * + * @public + */ + function setPGTStorageFile($format='', + $path='') + { + // check that the storage has not already been set + if ( is_object($this->_pgt_storage) ) { + phpCAS::error('PGT storage already defined'); + } + + // create the storage object + $this->_pgt_storage = &new PGTStorageFile($this,$format,$path); + } + + /** + * This method is used to tell phpCAS to store the response of the + * CAS server to PGT requests into a database. + * @note The connection to the database is done only when needed. + * As a consequence, bad parameters are detected only when + * initializing PGT storage. + * + * @param $user the user to access the data with + * @param $password the user's password + * @param $database_type the type of the database hosting the data + * @param $hostname the server hosting the database + * @param $port the port the server is listening on + * @param $database the name of the database + * @param $table the name of the table storing the data + * + * @public + */ + function setPGTStorageDB($user, + $password, + $database_type, + $hostname, + $port, + $database, + $table) + { + // check that the storage has not already been set + if ( is_object($this->_pgt_storage) ) { + phpCAS::error('PGT storage already defined'); + } + + // warn the user that he should use file storage... + trigger_error('PGT storage into database is an experimental feature, use at your own risk',E_USER_WARNING); + + // create the storage object + $this->_pgt_storage = & new PGTStorageDB($this,$user,$password,$database_type,$hostname,$port,$database,$table); + } + + // ######################################################################## + // PGT VALIDATION + // ######################################################################## + /** + * This method is used to validate a PGT; halt on failure. + * + * @param $validate_url the URL of the request to the CAS server. + * @param $text_response the response of the CAS server, as is (XML text); result + * of CASClient::validateST() or CASClient::validatePT(). + * @param $tree_response the response of the CAS server, as a DOM XML tree; result + * of CASClient::validateST() or CASClient::validatePT(). + * + * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError(). + * + * @private + */ + function validatePGT(&$validate_url,$text_response,$tree_response) + { + phpCAS::traceBegin(); + if ( sizeof($arr = $tree_response->get_elements_by_tagname("proxyGrantingTicket")) == 0) { + phpCAS::trace('No estàs autentificat.
Pots tornar a intentar-ho fent click aquí.
Si el problema persisteix hauría de contactar amb l\'administrador d\'aquest llocc.
', + CAS_STR_SERVICE_UNAVAILABLE + => 'El servei `%s\' no està disponible (%s).' +); + +?> diff --git a/plugins/CasAuthentication/extlib/CAS/languages/english.php b/plugins/CasAuthentication/extlib/CAS/languages/english.php new file mode 100644 index 0000000000..c143450314 --- /dev/null +++ b/plugins/CasAuthentication/extlib/CAS/languages/english.php @@ -0,0 +1,27 @@ + + * @sa @link internalLang Internationalization @endlink + * @ingroup internalLang + */ + +$this->_strings = array( + CAS_STR_USING_SERVER + => 'using server', + CAS_STR_AUTHENTICATION_WANTED + => 'CAS Authentication wanted!', + CAS_STR_LOGOUT + => 'CAS logout wanted!', + CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED + => 'You should already have been redirected to the CAS server. Click here to continue.', + CAS_STR_AUTHENTICATION_FAILED + => 'CAS Authentication failed!', + CAS_STR_YOU_WERE_NOT_AUTHENTICATED + => 'You were not authenticated.
You may submit your request again by clicking here.
If the problem persists, you may contact the administrator of this site.
', + CAS_STR_SERVICE_UNAVAILABLE + => 'The service `%s\' is not available (%s).' +); + +?> \ No newline at end of file diff --git a/plugins/CasAuthentication/extlib/CAS/languages/french.php b/plugins/CasAuthentication/extlib/CAS/languages/french.php new file mode 100644 index 0000000000..675a7fc04e --- /dev/null +++ b/plugins/CasAuthentication/extlib/CAS/languages/french.php @@ -0,0 +1,28 @@ + + * @sa @link internalLang Internationalization @endlink + * @ingroup internalLang + */ + +$this->_strings = array( + CAS_STR_USING_SERVER + => 'utilisant le serveur', + CAS_STR_AUTHENTICATION_WANTED + => 'Authentication CAS ncessaire !', + CAS_STR_LOGOUT + => 'Dconnexion demande !', + CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED + => 'Vous auriez du etre redirig(e) vers le serveur CAS. Cliquez ici pour continuer.', + CAS_STR_AUTHENTICATION_FAILED + => 'Authentification CAS infructueuse !', + CAS_STR_YOU_WERE_NOT_AUTHENTICATED + => 'Vous n\'avez pas t authentifi(e).
Vous pouvez soumettre votre requete nouveau en cliquant ici.
Si le problme persiste, vous pouvez contacter l\'administrateur de ce site.
', + CAS_STR_SERVICE_UNAVAILABLE + => 'Le service `%s\' est indisponible (%s)' + +); + +?> \ No newline at end of file diff --git a/plugins/CasAuthentication/extlib/CAS/languages/german.php b/plugins/CasAuthentication/extlib/CAS/languages/german.php new file mode 100644 index 0000000000..29daeb35dd --- /dev/null +++ b/plugins/CasAuthentication/extlib/CAS/languages/german.php @@ -0,0 +1,27 @@ + + * @sa @link internalLang Internationalization @endlink + * @ingroup internalLang + */ + +$this->_strings = array( + CAS_STR_USING_SERVER + => 'via Server', + CAS_STR_AUTHENTICATION_WANTED + => 'CAS Authentifizierung erforderlich!', + CAS_STR_LOGOUT + => 'CAS Abmeldung!', + CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED + => 'eigentlich häten Sie zum CAS Server weitergeleitet werden sollen. Drücken Sie hier um fortzufahren.', + CAS_STR_AUTHENTICATION_FAILED + => 'CAS Anmeldung fehlgeschlagen!', + CAS_STR_YOU_WERE_NOT_AUTHENTICATED + => 'Sie wurden nicht angemeldet.
Um es erneut zu versuchen klicken Sie hier.
Wenn das Problem bestehen bleibt, kontkatieren Sie den Administrator dieser Seite.
', + CAS_STR_SERVICE_UNAVAILABLE + => 'Der Dienst `%s\' ist nicht verfügbar (%s).' +); + +?> \ No newline at end of file diff --git a/plugins/CasAuthentication/extlib/CAS/languages/greek.php b/plugins/CasAuthentication/extlib/CAS/languages/greek.php new file mode 100644 index 0000000000..c17b1d6637 --- /dev/null +++ b/plugins/CasAuthentication/extlib/CAS/languages/greek.php @@ -0,0 +1,27 @@ + + * @sa @link internalLang Internationalization @endlink + * @ingroup internalLang + */ + +$this->_strings = array( + CAS_STR_USING_SERVER + => ' ', + CAS_STR_AUTHENTICATION_WANTED + => ' CAS!', + CAS_STR_LOGOUT + => ' CAS!', + CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED + => ' CAS. .', + CAS_STR_AUTHENTICATION_FAILED + => ' CAS !', + CAS_STR_YOU_WERE_NOT_AUTHENTICATED + => '.
', + CAS_STR_SERVICE_UNAVAILABLE + => ' `%s\' (%s).' +); + +?> \ No newline at end of file diff --git a/plugins/CasAuthentication/extlib/CAS/languages/japanese.php b/plugins/CasAuthentication/extlib/CAS/languages/japanese.php new file mode 100644 index 0000000000..333bb17b6a --- /dev/null +++ b/plugins/CasAuthentication/extlib/CAS/languages/japanese.php @@ -0,0 +1,27 @@ +_strings = array( + CAS_STR_USING_SERVER + => 'using server', + CAS_STR_AUTHENTICATION_WANTED + => 'CASˤǧڤԤޤ', + CAS_STR_LOGOUT + => 'CASȤޤ!', + CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED + => 'CASФ˹ԤɬפޤưŪžʤ å³Ԥޤ', + CAS_STR_AUTHENTICATION_FAILED + => 'CASˤǧڤ˼Ԥޤ', + CAS_STR_YOU_WERE_NOT_AUTHENTICATED + => 'ǧڤǤޤǤ.
꤬褷ʤ ΥȤδ䤤碌Ƥ.
', + CAS_STR_SERVICE_UNAVAILABLE + => 'ӥ `%s\' ѤǤޤ (%s).' +); + +?> \ No newline at end of file diff --git a/plugins/CasAuthentication/extlib/CAS/languages/languages.php b/plugins/CasAuthentication/extlib/CAS/languages/languages.php new file mode 100644 index 0000000000..2c6f8bb3b3 --- /dev/null +++ b/plugins/CasAuthentication/extlib/CAS/languages/languages.php @@ -0,0 +1,24 @@ + + * @sa @link internalLang Internationalization @endlink + * @ingroup internalLang + */ + +//@{ +/** + * a phpCAS string index + */ +define("CAS_STR_USING_SERVER", 1); +define("CAS_STR_AUTHENTICATION_WANTED", 2); +define("CAS_STR_LOGOUT", 3); +define("CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED", 4); +define("CAS_STR_AUTHENTICATION_FAILED", 5); +define("CAS_STR_YOU_WERE_NOT_AUTHENTICATED", 6); +define("CAS_STR_SERVICE_UNAVAILABLE", 7); +//@} + +?> \ No newline at end of file diff --git a/plugins/CasAuthentication/extlib/CAS/languages/spanish.php b/plugins/CasAuthentication/extlib/CAS/languages/spanish.php new file mode 100644 index 0000000000..3a8ffc2535 --- /dev/null +++ b/plugins/CasAuthentication/extlib/CAS/languages/spanish.php @@ -0,0 +1,27 @@ + + * @sa @link internalLang Internationalization @endlink + * @ingroup internalLang + */ + +$this->_strings = array( + CAS_STR_USING_SERVER + => 'usando servidor', + CAS_STR_AUTHENTICATION_WANTED + => '¡Autentificación CAS necesaria!', + CAS_STR_LOGOUT + => '¡Salida CAS necesaria!', + CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED + => 'Ya debería haber sido redireccionado al servidor CAS. Haga click aquí para continuar.', + CAS_STR_AUTHENTICATION_FAILED + => '¡Autentificación CAS fallida!', + CAS_STR_YOU_WERE_NOT_AUTHENTICATED + => 'No estás autentificado.
Puedes volver a intentarlo haciendo click aquí.
Si el problema persiste debería contactar con el administrador de este sitio.
', + CAS_STR_SERVICE_UNAVAILABLE + => 'El servicio `%s\' no está disponible (%s).' +); + +?> diff --git a/plugins/Facebook/FacebookPlugin.php b/plugins/Facebook/FacebookPlugin.php index 39b2ef2876..de91bf24a1 100644 --- a/plugins/Facebook/FacebookPlugin.php +++ b/plugins/Facebook/FacebookPlugin.php @@ -32,6 +32,7 @@ if (!defined('STATUSNET')) { } define("FACEBOOK_CONNECT_SERVICE", 3); +define('FACEBOOKPLUGIN_VERSION', '0.9'); require_once INSTALLDIR . '/plugins/Facebook/facebookutil.php'; @@ -554,4 +555,18 @@ class FacebookPlugin extends Plugin return true; } + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'Facebook', + 'version' => FACEBOOKPLUGIN_VERSION, + 'author' => 'Zach Copley', + 'homepage' => 'http://status.net/wiki/Plugin:Facebook', + 'rawdescription' => + _m('The Facebook plugin allows you to integrate ' . + 'your StatusNet instance with ' . + 'Facebook ' . + 'and Facebook Connect.')); + return true; + } + } diff --git a/plugins/Facebook/facebook/facebook.php b/plugins/Facebook/facebook/facebook.php index 016e8e8e0d..440706cbc3 100644 --- a/plugins/Facebook/facebook/facebook.php +++ b/plugins/Facebook/facebook/facebook.php @@ -82,7 +82,8 @@ class Facebook { if (isset($this->fb_params['friends'])) { - $this->api_client->friends_list = explode(',', $this->fb_params['friends']); + $this->api_client->friends_list = + array_filter(explode(',', $this->fb_params['friends'])); } if (isset($this->fb_params['added'])) { $this->api_client->added = $this->fb_params['added']; @@ -215,11 +216,15 @@ class Facebook { // Invalidate the session currently being used, and clear any state associated // with it. Note that the user will still remain logged into Facebook. public function expire_session() { - if ($this->api_client->auth_expireSession()) { + try { + if ($this->api_client->auth_expireSession()) { + $this->clear_cookie_state(); + return true; + } else { + return false; + } + } catch (Exception $e) { $this->clear_cookie_state(); - return true; - } else { - return false; } } @@ -249,10 +254,14 @@ class Facebook { if (!$this->in_fb_canvas() && isset($_COOKIE[$this->api_key . '_user'])) { $cookies = array('user', 'session_key', 'expires', 'ss'); foreach ($cookies as $name) { - setcookie($this->api_key . '_' . $name, false, time() - 3600); + setcookie($this->api_key . '_' . $name, + false, + time() - 3600, + '', + $this->base_domain); unset($_COOKIE[$this->api_key . '_' . $name]); } - setcookie($this->api_key, false, time() - 3600); + setcookie($this->api_key, false, time() - 3600, '', $this->base_domain); unset($_COOKIE[$this->api_key]); } diff --git a/plugins/Facebook/facebook/facebook_desktop.php b/plugins/Facebook/facebook/facebook_desktop.php index e79a2ca343..ed4762215b 100644 --- a/plugins/Facebook/facebook/facebook_desktop.php +++ b/plugins/Facebook/facebook/facebook_desktop.php @@ -60,7 +60,7 @@ class FacebookDesktop extends Facebook { public function set_session_secret($session_secret) { $this->secret = $session_secret; - $this->api_client->secret = $session_secret; + $this->api_client->use_session_secret($session_secret); } public function require_login() { diff --git a/plugins/Facebook/facebook/facebook_mobile.php b/plugins/Facebook/facebook/facebook_mobile.php new file mode 100644 index 0000000000..5ee7f4ed5b --- /dev/null +++ b/plugins/Facebook/facebook/facebook_mobile.php @@ -0,0 +1,260 @@ + $val) { + if (!$val) { + unset($params[$key]); + } + } + return $page . '?' . http_build_query($params); + } + + public function get_www_url($action, $params) { + $page = parent::get_facebook_url('www'). '/' .$action; + foreach($params as $key => $val) { + if (!$val) { + unset($params[$key]); + } + } + return $page . '?' . http_build_query($params); + } + + public function get_add_url($next=null) { + + return $this->get_m_url('add.php', array('api_key' => $this->api_key, + 'next' => $next)); + } + + public function get_tos_url($next=null, $cancel = null, $canvas=null) { + return $this->get_m_url('tos.php', array('api_key' => $this->api_key, + 'v' => '1.0', + 'next' => $next, + 'canvas' => $canvas, + 'cancel' => $cancel)); + } + + public function get_logout_url($next=null) { + $params = array('api_key' => $this->api_key, + 'session_key' => $this->api_client->session_key, + ); + + if ($next) { + $params['connect_next'] = 1; + $params['next'] = $next; + } + + return $this->get_m_url('logout.php', $params); + } + public function get_register_url($next=null, $cancel_url=null) { + return $this->get_m_url('r.php', + array('fbconnect' => 1, + 'api_key' => $this->api_key, + 'next' => $next ? $next : parent::current_url(), + 'cancel_url' => $cancel_url ? $cancel_url : parent::current_url())); + } + /** + * These set of fbconnect style url redirect back to the application current + * page when the action is done. Developer can also use the non fbconnect + * style url and provide their own redirect link by giving the right parameter + * to $next and/or $cancel_url + */ + public function get_fbconnect_register_url() { + return $this->get_register_url(parent::current_url(), parent::current_url()); + } + public function get_fbconnect_tos_url() { + return $this->get_tos_url(parent::current_url(), parent::current_url(), $this->in_frame()); + } + + public function get_fbconnect_logout_url() { + return $this->get_logout_url(parent::current_url()); + } + + public function logout_user() { + $this->user = null; + } + + public function get_prompt_permissions_url($ext_perm, + $next=null, + $cancel_url=null) { + + return $this->get_www_url('connect/prompt_permissions.php', + array('api_key' => $this->api_key, + 'ext_perm' => $ext_perm, + 'next' => $next ? $next : parent::current_url(), + 'cancel' => $cancel_url ? $cancel_url : parent::current_url(), + 'display' => 'wap')); + + } + + /** + * support both prompt_permissions.php and authorize.php for now. + * authorized.php is to be deprecate though. + */ + public function get_extended_permission_url($ext_perm, + $next=null, + $cancel_url=null) { + $next = $next ? $next : parent::current_url(); + $cancel_url = $cancel_url ? $cancel_url : parent::current_url(); + + return $this->get_m_url('authorize.php', + array('api_key' => $this->api_key, + 'ext_perm' => $ext_perm, + 'next' => $next, + 'cancel_url' => $cancel_url)); + + } + + public function render_prompt_feed_url($action_links=NULL, + $target_id=NULL, + $message='', + $user_message_prompt='', + $caption=NULL, + $callback ='', + $cancel='', + $attachment=NULL, + $preview=true) { + + $params = array('api_key' => $this->api_key, + 'session_key' => $this->api_client->session_key, + ); + if (!empty($attachment)) { + $params['attachment'] = urlencode(json_encode($attachment)); + } else { + $attachment = new stdClass(); + $app_display_info = $this->api_client->admin_getAppProperties(array('application_name', + 'callback_url', + 'description', + 'logo_url')); + $app_display_info = $app_display_info; + $attachment->name = $app_display_info['application_name']; + $attachment->caption = !empty($caption) ? $caption : 'Just see what\'s new!'; + $attachment->description = $app_display_info['description']; + $attachment->href = $app_display_info['callback_url']; + if (!empty($app_display_info['logo_url'])) { + $logo = new stdClass(); + $logo->type = 'image'; + $logo->src = $app_display_info['logo_url']; + $logo->href = $app_display_info['callback_url']; + $attachment->media = array($logo); + } + $params['attachment'] = urlencode(json_encode($attachment)); + } + $params['preview'] = $preview; + $params['message'] = $message; + $params['user_message_prompt'] = $user_message_prompt; + if (!empty($callback)) { + $params['callback'] = $callback; + } else { + $params['callback'] = $this->current_url(); + } + if (!empty($cancel)) { + $params['cancel'] = $cancel; + } else { + $params['cancel'] = $this->current_url(); + } + + if (!empty($target_id)) { + $params['target_id'] = $target_id; + } + if (!empty($action_links)) { + $params['action_links'] = urlencode(json_encode($action_links)); + } + + $params['display'] = 'wap'; + header('Location: '. $this->get_www_url('connect/prompt_feed.php', $params)); + } + +//use template_id + public function render_feed_form_url($template_id=NULL, + $template_data=NULL, + $user_message=NULL, + $body_general=NULL, + $user_message_prompt=NULL, + $target_id=NULL, + $callback=NULL, + $cancel=NULL, + $preview=true) { + + $params = array('api_key' => $this->api_key); + $params['preview'] = $preview; + if (isset($template_id) && $template_id) { + $params['template_id'] = $template_id; + } + $params['message'] = $user_message ? $user_message['value'] : ''; + if (isset($body_general) && $body_general) { + $params['body_general'] = $body_general; + } + if (isset($user_message_prompt) && $user_message_prompt) { + $params['user_message_prompt'] = $user_message_prompt; + } + if (isset($callback) && $callback) { + $params['callback'] = $callback; + } else { + $params['callback'] = $this->current_url(); + } + if (isset($cancel) && $cancel) { + $params['cancel'] = $cancel; + } else { + $params['cancel'] = $this->current_url(); + } + if (isset($template_data) && $template_data) { + $params['template_data'] = $template_data; + } + if (isset($target_id) && $target_id) { + $params['to_ids'] = $target_id; + } + $params['display'] = 'wap'; + header('Location: '. $this->get_www_url('connect/prompt_feed.php', $params)); + } +} diff --git a/plugins/Facebook/facebook/facebookapi_php5_restlib.php b/plugins/Facebook/facebook/facebookapi_php5_restlib.php index 55cb7fb86a..fa1088cd00 100755 --- a/plugins/Facebook/facebook/facebookapi_php5_restlib.php +++ b/plugins/Facebook/facebook/facebookapi_php5_restlib.php @@ -56,6 +56,8 @@ class FacebookRestClient { private $call_as_apikey; private $use_curl_if_available; private $format = null; + private $using_session_secret = false; + private $rawData = null; const BATCH_MODE_DEFAULT = 0; const BATCH_MODE_SERVER_PARALLEL = 0; @@ -76,7 +78,10 @@ class FacebookRestClient { $this->last_call_id = 0; $this->call_as_apikey = ''; $this->use_curl_if_available = true; - $this->server_addr = Facebook::get_facebook_url('api') . '/restserver.php'; + $this->server_addr = + Facebook::get_facebook_url('api') . '/restserver.php'; + $this->photo_server_addr = + Facebook::get_facebook_url('api-photo') . '/restserver.php'; if (!empty($GLOBALS['facebook_config']['debug'])) { $this->cur_id = 0; @@ -128,6 +133,16 @@ function toggleDisplay(id, type) { $this->user = $uid; } + + /** + * Switch to use the session secret instead of the app secret, + * for desktop and unsecured environment + */ + public function use_session_secret($session_secret) { + $this->secret = $session_secret; + $this->using_session_secret = true; + } + /** * Normally, if the cURL library/PHP extension is available, it is used for * HTTP transactions. This allows that behavior to be overridden, falling @@ -270,25 +285,35 @@ function toggleDisplay(id, type) { /** * Returns the session information available after current user logs in. * - * @param string $auth_token the token returned by - * auth_createToken or passed back to - * your callback_url. - * @param bool $generate_session_secret whether the session returned should - * include a session secret + * @param string $auth_token the token returned by auth_createToken or + * passed back to your callback_url. + * @param bool $generate_session_secret whether the session returned should + * include a session secret + * @param string $host_url the connect site URL for which the session is + * being generated. This parameter is optional, unless + * you want Facebook to determine which of several base domains + * to choose from. If this third argument isn't provided but + * there are several base domains, the first base domain is + * chosen. * * @return array An assoc array containing session_key, uid */ - public function auth_getSession($auth_token, $generate_session_secret=false) { + public function auth_getSession($auth_token, + $generate_session_secret = false, + $host_url = null) { if (!$this->pending_batch()) { - $result = $this->call_method('facebook.auth.getSession', - array('auth_token' => $auth_token, - 'generate_session_secret' => $generate_session_secret)); + $result = $this->call_method( + 'facebook.auth.getSession', + array('auth_token' => $auth_token, + 'generate_session_secret' => $generate_session_secret, + 'host_url' => $host_url)); $this->session_key = $result['session_key']; - if (!empty($result['secret']) && !$generate_session_secret) { - // desktop apps have a special secret - $this->secret = $result['secret']; - } + if (!empty($result['secret']) && !$generate_session_secret) { + // desktop apps have a special secret + $this->secret = $result['secret']; + } + return $result; } } @@ -519,13 +544,34 @@ function toggleDisplay(id, type) { return $this->call_upload_method('facebook.events.create', array('event_info' => $event_info), $file, - Facebook::get_facebook_url('api-photo') . '/restserver.php'); + $this->photo_server_addr); } else { return $this->call_method('facebook.events.create', array('event_info' => $event_info)); } } + /** + * Invites users to an event. If a session user exists, the session user + * must have permissions to invite friends to the event and $uids must contain + * a list of friend ids. Otherwise, the event must have been + * created by the app and $uids must contain users of the app. + * This method requires the 'create_event' extended permission to + * invite people on behalf of a user. + * + * @param $eid the event id + * @param $uids an array of users to invite + * @param $personal_message a string containing the user's message + * (text only) + * + */ + public function events_invite($eid, $uids, $personal_message) { + return $this->call_method('facebook.events.invite', + array('eid' => $eid, + 'uids' => $uids, + 'personal_message', $personal_message)); + } + /** * Edits an existing event. Only works for events where application is admin. * @@ -540,7 +586,7 @@ function toggleDisplay(id, type) { return $this->call_upload_method('facebook.events.edit', array('eid' => $eid, 'event_info' => $event_info), $file, - Facebook::get_facebook_url('api-photo') . '/restserver.php'); + $this->photo_server_addr); } else { return $this->call_method('facebook.events.edit', array('eid' => $eid, @@ -576,21 +622,7 @@ function toggleDisplay(id, type) { array('url' => $url)); } - /** - * Lets you insert text strings in their native language into the Facebook - * Translations database so they can be translated. - * - * @param array $native_strings An array of maps, where each map has a 'text' - * field and a 'description' field. - * - * @return int Number of strings uploaded. - */ - public function &fbml_uploadNativeStrings($native_strings) { - return $this->call_method('facebook.fbml.uploadNativeStrings', - array('native_strings' => json_encode($native_strings))); - } - - /** + /** * Associates a given "handle" with FBML markup so that the handle can be * used within the fb:ref FBML tag. A handle is unique within an application * and allows an application to publish identical FBML to many user profiles @@ -668,7 +700,44 @@ function toggleDisplay(id, type) { array('tag_names' => json_encode($tag_names))); } + /** + * Gets the best translations for native strings submitted by an application + * for translation. If $locale is not specified, only native strings and their + * descriptions are returned. If $all is true, then unapproved translations + * are returned as well, otherwise only approved translations are returned. + * + * A mapping of locale codes -> language names is available at + * http://wiki.developers.facebook.com/index.php/Facebook_Locales + * + * @param string $locale the locale to get translations for, or 'all' for all + * locales, or 'en_US' for native strings + * @param bool $all whether to return all or only approved translations + * + * @return array (locale, array(native_strings, array('best translation + * available given enough votes or manual approval', approval + * status))) + * @error API_EC_PARAM + * @error API_EC_PARAM_BAD_LOCALE + */ + public function &intl_getTranslations($locale = 'en_US', $all = false) { + return $this->call_method('facebook.intl.getTranslations', + array('locale' => $locale, + 'all' => $all)); + } + /** + * Lets you insert text strings in their native language into the Facebook + * Translations database so they can be translated. + * + * @param array $native_strings An array of maps, where each map has a 'text' + * field and a 'description' field. + * + * @return int Number of strings uploaded. + */ + public function &intl_uploadNativeStrings($native_strings) { + return $this->call_method('facebook.intl.uploadNativeStrings', + array('native_strings' => json_encode($native_strings))); + } /** * This method is deprecated for calls made on behalf of users. This method @@ -1248,6 +1317,87 @@ function toggleDisplay(id, type) { 'test_mode' => $test_mode)), true); } + /** + * Gifts API + */ + + /** + * Get Gifts associated with an app + * + * @return array of gifts + */ + public function gifts_get() { + return json_decode( + $this->call_method('facebook.gifts.get', + array()), + true + ); + } + + /* + * Update gifts stored by an app + * + * @param array containing gift_id => gift_data to be updated + * @return array containing gift_id => true/false indicating success + * in updating that gift + */ + public function gifts_update($update_array) { + return json_decode( + $this->call_method('facebook.gifts.update', + array('update_str' => json_encode($update_array)) + ), + true + ); + } + + /** + * Dashboard API + */ + + /** + * Set the news for the specified user. + * + * @param int $uid The user for whom you are setting news for + * @param string $news Text of news to display + * + * @return bool Success + */ + public function dashboard_setNews($uid, $news) { + return $this->call_method('facebook.dashboard.setNews', + array('uid' => $uid, + 'news' => $news) + ); + } + + /** + * Get the current news of the specified user. + * + * @param int $uid The user to get the news of + * + * @return string The text of the current news for the user + */ + public function dashboard_getNews($uid) { + return json_decode( + $this->call_method('facebook.dashboard.getNews', + array('uid' => $uid) + ), true); + } + + /** + * Set the news for the specified user. + * + * @param int $uid The user you are clearing the news of + * + * @return bool Success + */ + public function dashboard_clearNews($uid) { + return $this->call_method('facebook.dashboard.clearNews', + array('uid' => $uid) + ); + } + + + /** * Creates a note with the specified title and content. * @@ -1795,14 +1945,20 @@ function toggleDisplay(id, type) { $start_time = 0, $end_time = 0, $limit = 30, - $filter_key = '') { + $filter_key = '', + $exportable_only = false, + $metadata = null, + $post_ids = null) { $args = array( 'viewer_id' => $viewer_id, 'source_ids' => $source_ids, 'start_time' => $start_time, 'end_time' => $end_time, 'limit' => $limit, - 'filter_key' => $filter_key); + 'filter_key' => $filter_key, + 'exportable_only' => $exportable_only, + 'metadata' => $metadata, + 'post_ids' => $post_ids); return $this->call_method('facebook.stream.get', $args); } @@ -1949,97 +2105,6 @@ function toggleDisplay(id, type) { 'options' => json_encode($options))); } - /** - * Get all the marketplace categories. - * - * @return array A list of category names - */ - function marketplace_getCategories() { - return $this->call_method('facebook.marketplace.getCategories', - array()); - } - - /** - * Get all the marketplace subcategories for a particular category. - * - * @param category The category for which we are pulling subcategories - * - * @return array A list of subcategory names - */ - function marketplace_getSubCategories($category) { - return $this->call_method('facebook.marketplace.getSubCategories', - array('category' => $category)); - } - - /** - * Get listings by either listing_id or user. - * - * @param listing_ids An array of listing_ids (optional) - * @param uids An array of user ids (optional) - * - * @return array The data for matched listings - */ - function marketplace_getListings($listing_ids, $uids) { - return $this->call_method('facebook.marketplace.getListings', - array('listing_ids' => $listing_ids, 'uids' => $uids)); - } - - /** - * Search for Marketplace listings. All arguments are optional, though at - * least one must be filled out to retrieve results. - * - * @param category The category in which to search (optional) - * @param subcategory The subcategory in which to search (optional) - * @param query A query string (optional) - * - * @return array The data for matched listings - */ - function marketplace_search($category, $subcategory, $query) { - return $this->call_method('facebook.marketplace.search', - array('category' => $category, - 'subcategory' => $subcategory, - 'query' => $query)); - } - - /** - * Remove a listing from Marketplace. - * - * @param listing_id The id of the listing to be removed - * @param status 'SUCCESS', 'NOT_SUCCESS', or 'DEFAULT' - * - * @return bool True on success - */ - function marketplace_removeListing($listing_id, - $status='DEFAULT', - $uid=null) { - return $this->call_method('facebook.marketplace.removeListing', - array('listing_id' => $listing_id, - 'status' => $status, - 'uid' => $uid)); - } - - /** - * Create/modify a Marketplace listing for the loggedinuser. - * - * @param int listing_id The id of a listing to be modified, 0 - * for a new listing. - * @param show_on_profile bool Should we show this listing on the - * user's profile - * @param listing_attrs array An array of the listing data - * - * @return int The listing_id (unchanged if modifying an existing listing). - */ - function marketplace_createListing($listing_id, - $show_on_profile, - $attrs, - $uid=null) { - return $this->call_method('facebook.marketplace.createListing', - array('listing_id' => $listing_id, - 'show_on_profile' => $show_on_profile, - 'listing_attrs' => json_encode($attrs), - 'uid' => $uid)); - } - ///////////////////////////////////////////////////////////////////////////// // Data Store API @@ -2875,6 +2940,35 @@ function toggleDisplay(id, type) { array('properties' => json_encode($properties))); } + /** + * Sets href and text for a Live Stream Box xid's via link + * + * @param string $xid xid of the Live Stream + * @param string $via_href Href for the via link + * @param string $via_text Text for the via link + * + * @return boolWhether the set was successful + */ + public function admin_setLiveStreamViaLink($xid, $via_href, $via_text) { + return $this->call_method('facebook.admin.setLiveStreamViaLink', + array('xid' => $xid, + 'via_href' => $via_href, + 'via_text' => $via_text)); + } + + /** + * Gets href and text for a Live Stream Box xid's via link + * + * @param string $xid xid of the Live Stream + * + * @return Array Associative array with keys 'via_href' and 'via_text' + * False if there was an error. + */ + public function admin_getLiveStreamViaLink($xid) { + return $this->call_method('facebook.admin.getLiveStreamViaLink', + array('xid' => $xid)); + } + /** * Returns the allocation limit value for a specified integration point name * Integration point names are defined in lib/api/karma/constants.php in the @@ -3012,6 +3106,7 @@ function toggleDisplay(id, type) { $params['call_as_apikey'] = $this->call_as_apikey; } $data = $this->post_request($method, $params); + $this->rawData = $data; $result = $this->convert_result($data, $method, $params); if (is_array($result) && isset($result['error_code'])) { throw new FacebookRestClientException($result['error_msg'], @@ -3053,6 +3148,16 @@ function toggleDisplay(id, type) { return $this->format; } + /** + * Returns the raw JSON or XML output returned by the server in the most + * recent API call. + * + * @return string + */ + public function getRawData() { + return $this->rawData; + } + /** * Calls the specified file-upload POST method with the specified parameters * @@ -3144,6 +3249,10 @@ function toggleDisplay(id, type) { if ($this->call_as_apikey) { $get['call_as_apikey'] = $this->call_as_apikey; } + if ($this->using_session_secret) { + $get['ss'] = '1'; + } + $get['method'] = $method; $get['session_key'] = $this->session_key; $get['api_key'] = $this->api_key; @@ -3241,7 +3350,7 @@ function toggleDisplay(id, type) { return $result; } - private function post_upload_request($method, $params, $file, $server_addr = null) { + protected function post_upload_request($method, $params, $file, $server_addr = null) { $server_addr = $server_addr ? $server_addr : $this->server_addr; list($get, $post) = $this->finalize_params($method, $params); $get_string = $this->create_url_string($get); @@ -3345,6 +3454,8 @@ class FacebookAPIErrorCodes { const API_EC_VERSION = 12; const API_EC_INTERNAL_FQL_ERROR = 13; const API_EC_HOST_PUP = 14; + const API_EC_SESSION_SECRET_NOT_ALLOWED = 15; + const API_EC_HOST_READONLY = 16; /* * PARAMETER ERRORS @@ -3372,6 +3483,8 @@ class FacebookAPIErrorCodes { const API_EC_PARAM_BAD_EID = 150; const API_EC_PARAM_UNKNOWN_CITY = 151; const API_EC_PARAM_BAD_PAGE_TYPE = 152; + const API_EC_PARAM_BAD_LOCALE = 170; + const API_EC_PARAM_BLOCKED_NOTIFICATION = 180; /* * USER PERMISSIONS ERRORS @@ -3394,6 +3507,7 @@ class FacebookAPIErrorCodes { const API_EC_PERMISSION_EVENT = 290; const API_EC_PERMISSION_LARGE_FBML_TEMPLATE = 291; const API_EC_PERMISSION_LIVEMESSAGE = 292; + const API_EC_PERMISSION_CREATE_EVENT = 296; const API_EC_PERMISSION_RSVP_EVENT = 299; /* @@ -3469,6 +3583,8 @@ class FacebookAPIErrorCodes { const FQL_EC_EXTENDED_PERMISSION = 612; const FQL_EC_RATE_LIMIT_EXCEEDED = 613; const FQL_EC_UNRESOLVED_DEPENDENCY = 614; + const FQL_EC_INVALID_SEARCH = 615; + const FQL_EC_CONTAINS_ERROR = 616; const API_EC_REF_SET_FAILED = 700; @@ -3506,6 +3622,7 @@ class FacebookAPIErrorCodes { * EVENT API ERRORS */ const API_EC_EVENT_INVALID_TIME = 1000; + const API_EC_EVENT_NAME_LOCKED = 1001; /* * INFO BOX ERRORS @@ -3566,6 +3683,21 @@ class FacebookAPIErrorCodes { const API_EC_COMMENTS_INVALID_POST = 1705; const API_EC_COMMENTS_INVALID_REMOVE = 1706; + /* + * GIFTS + */ + const API_EC_GIFTS_UNKNOWN = 1900; + + /* + * APPLICATION MORATORIUM ERRORS + */ + const API_EC_DISABLED_ALL = 2000; + const API_EC_DISABLED_STATUS = 2001; + const API_EC_DISABLED_FEED_STORIES = 2002; + const API_EC_DISABLED_NOTIFICATIONS = 2003; + const API_EC_DISABLED_REQUESTS = 2004; + const API_EC_DISABLED_EMAIL = 2005; + /** * This array is no longer maintained; to view the description of an error * code, please look at the message element of the API response or visit diff --git a/plugins/Facebook/facebookaction.php b/plugins/Facebook/facebookaction.php index 3e8c5cf412..bf9c037a57 100644 --- a/plugins/Facebook/facebookaction.php +++ b/plugins/Facebook/facebookaction.php @@ -294,63 +294,7 @@ class FacebookAction extends Action $app_props = $this->facebook->api_client->Admin_getAppProperties(array('icon_url')); $icon_url = $app_props['icon_url']; - $style = ''; + $style = ''; $this->xw->openMemory(); diff --git a/plugins/FeedSub/FeedSubPlugin.php b/plugins/FeedSub/FeedSubPlugin.php index 857a9794d5..e49e2a648a 100644 --- a/plugins/FeedSub/FeedSubPlugin.php +++ b/plugins/FeedSub/FeedSubPlugin.php @@ -105,12 +105,11 @@ class FeedSubPlugin extends Plugin return true; } - /* - // auto increment seems to be broken function onCheckSchema() { + // warning: the autoincrement doesn't seem to set. + // alter table feedinfo change column id id int(11) not null auto_increment; $schema = Schema::get(); - $schema->ensureDataObject('Feedinfo'); + $schema->ensureTable('feedinfo', Feedinfo::schemaDef()); return true; } - */ } diff --git a/plugins/FeedSub/feedinfo.php b/plugins/FeedSub/feedinfo.php index fff66afe97..b166bd6e12 100644 --- a/plugins/FeedSub/feedinfo.php +++ b/plugins/FeedSub/feedinfo.php @@ -31,7 +31,7 @@ class FeedDBException extends FeedSubException } } -class Feedinfo extends Plugin_DataObject +class Feedinfo extends Memcached_DataObject { public $__table = 'feedinfo'; @@ -56,34 +56,90 @@ class Feedinfo extends Plugin_DataObject return parent::staticGet(__CLASS__, $k, $v); } - function tableDef() + /** + * return table definition for DB_DataObject + * + * DB_DataObject needs to know something about the table to manipulate + * instances. This method provides all the DB_DataObject needs to know. + * + * @return array array of column definitions + */ + + function table() { - class_exists('Schema'); // autoload hack - // warning: the autoincrement doesn't seem to set. - // alter table feedinfo change column id id int(11) not null auto_increment; - return new TableDef($this->__table, - array(new ColumnDef('id', 'integer', - null, false, 'PRI', '0', null, true), - new ColumnDef('profile_id', 'integer', - null, false), - new ColumnDef('feeduri', 'varchar', - 255, false, 'UNI'), - new ColumnDef('homeuri', 'varchar', - 255, false), - new ColumnDef('huburi', 'varchar', - 255, false), - new ColumnDef('verify_token', 'varchar', - 32, true), - new ColumnDef('sub_start', 'datetime', - null, true), - new ColumnDef('sub_end', 'datetime', - null, true), - new ColumnDef('created', 'datetime', - null, false), - new ColumnDef('lastupdate', 'datetime', - null, false))); + return array('id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL, + 'profile_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL, + 'feeduri' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL, + 'homeuri' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL, + 'huburi' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL, + 'verify_token' => DB_DATAOBJECT_STR, + 'sub_start' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME, + 'sub_end' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME, + 'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL, + 'lastupdate' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL); + } + + static function schemaDef() + { + return array(new ColumnDef('id', 'integer', + /*size*/ null, + /*nullable*/ false, + /*key*/ 'PRI', + /*default*/ '0', + /*extra*/ null, + /*auto_increment*/ true), + new ColumnDef('profile_id', 'integer', + null, false), + new ColumnDef('feeduri', 'varchar', + 255, false, 'UNI'), + new ColumnDef('homeuri', 'varchar', + 255, false), + new ColumnDef('huburi', 'varchar', + 255, false), + new ColumnDef('verify_token', 'varchar', + 32, true), + new ColumnDef('sub_start', 'datetime', + null, true), + new ColumnDef('sub_end', 'datetime', + null, true), + new ColumnDef('created', 'datetime', + null, false), + new ColumnDef('lastupdate', 'datetime', + null, false)); } + /** + * return key definitions for DB_DataObject + * + * DB_DataObject needs to know about keys that the table has; this function + * defines them. + * + * @return array key definitions + */ + + function keys() + { + return array('id' => 'P'); //? + } + + /** + * return key definitions for Memcached_DataObject + * + * Our caching system uses the same key definitions, but uses a different + * method to get them. + * + * @return array key definitions + */ + + function keyTypes() + { + return $this->keys(); + } + + /** + * Fetch the StatusNet-side profile for this feed + * @return Profile + */ public function getProfile() { return Profile::staticGet('id', $this->profile_id); diff --git a/plugins/GeoURLPlugin.php b/plugins/GeoURLPlugin.php index 30ff2c2788..01178f39c0 100644 --- a/plugins/GeoURLPlugin.php +++ b/plugins/GeoURLPlugin.php @@ -116,4 +116,16 @@ class GeoURLPlugin extends Plugin return true; } + + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'GeoURL', + 'version' => STATUSNET_VERSION, + 'author' => 'Evan Prodromou', + 'homepage' => 'http://status.net/wiki/Plugin:GeoURL', + 'rawdescription' => + _m('Ping GeoURL when '. + 'new geolocation-enhanced notices are posted.')); + return true; + } } diff --git a/plugins/GeonamesPlugin.php b/plugins/GeonamesPlugin.php index df99c7849b..52cc9c97f9 100644 --- a/plugins/GeonamesPlugin.php +++ b/plugins/GeonamesPlugin.php @@ -76,38 +76,25 @@ class GeonamesPlugin extends Plugin return false; } - $client = HTTPClient::start(); - - // XXX: break down a name by commas, narrow by each - - $result = $client->get($this->wsUrl('search', - array('maxRows' => 1, - 'q' => $name, - 'lang' => $language, - 'type' => 'json'))); - - if (!$result->isOk()) { - $this->log(LOG_WARNING, "Error code " . $result->code . - " from " . $this->host . " for $name"); + try { + $geonames = $this->getGeonames('search', + array('maxRows' => 1, + 'q' => $name, + 'lang' => $language, + 'type' => 'xml')); + } catch (Exception $e) { + $this->log(LOG_WARNING, "Error for $name: " . $e->getMessage()); return true; } - $rj = json_decode($result->getBody()); - - if (count($rj->geonames) <= 0) { - $this->log(LOG_WARNING, "No results in response from " . - $this->host . " for $name"); - return true; - } - - $n = $rj->geonames[0]; + $n = $geonames[0]; $location = new Location(); - $location->lat = $n->lat; - $location->lon = $n->lng; - $location->names[$language] = $n->name; - $location->location_id = $n->geonameId; + $location->lat = (string)$n->lat; + $location->lon = (string)$n->lng; + $location->names[$language] = (string)$n->name; + $location->location_id = (string)$n->geonameId; $location->location_ns = self::LOCATION_NS; $this->setCache(array('name' => $name, @@ -143,54 +130,41 @@ class GeonamesPlugin extends Plugin return false; } - $client = HTTPClient::start(); - - $result = $client->get($this->wsUrl('hierarchyJSON', - array('geonameId' => $id, - 'lang' => $language))); - - if (!$result->isOk()) { - $this->log(LOG_WARNING, - "Error code " . $result->code . - " from " . $this->host . " for ID $id"); - return false; - } - - $rj = json_decode($result->getBody()); - - if (count($rj->geonames) <= 0) { - $this->log(LOG_WARNING, - "No results in response from " . - $this->host . " for ID $id"); + try { + $geonames = $this->getGeonames('hierarchy', + array('geonameId' => $id, + 'lang' => $language)); + } catch (Exception $e) { + $this->log(LOG_WARNING, "Error for ID $id: " . $e->getMessage()); return false; } $parts = array(); - foreach ($rj->geonames as $level) { + foreach ($geonames as $level) { if (in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { - $parts[] = $level->name; + $parts[] = (string)$level->name; } } - $last = $rj->geonames[count($rj->geonames)-1]; + $last = $geonames[count($geonames)-1]; if (!in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { - $parts[] = $last->name; + $parts[] = (string)$last->name; } $location = new Location(); - $location->location_id = $last->geonameId; + $location->location_id = (string)$last->geonameId; $location->location_ns = self::LOCATION_NS; - $location->lat = $last->lat; - $location->lon = $last->lng; + $location->lat = (string)$last->lat; + $location->lon = (string)$last->lng; $location->names[$language] = implode(', ', array_reverse($parts)); - $this->setCache(array('id' => $last->geonameId), + $this->setCache(array('id' => (string)$last->geonameId), $location); - // We're responsible for this NAMESPACE; nobody else + // We're responsible for this namespace; nobody else // can resolve it return false; @@ -223,50 +197,36 @@ class GeonamesPlugin extends Plugin return false; } - $client = HTTPClient::start(); - - $result = - $client->get($this->wsUrl('findNearbyPlaceNameJSON', - array('lat' => $lat, - 'lng' => $lon, - 'lang' => $language))); - - if (!$result->isOk()) { - $this->log(LOG_WARNING, - "Error code " . $result->code . - " from " . $this->host . " for coords $lat, $lon"); + try { + $geonames = $this->getGeonames('findNearbyPlaceName', + array('lat' => $lat, + 'lng' => $lon, + 'lang' => $language)); + } catch (Exception $e) { + $this->log(LOG_WARNING, "Error for coords $lat, $lon: " . $e->getMessage()); return true; } - $rj = json_decode($result->getBody()); - - if (count($rj->geonames) <= 0) { - $this->log(LOG_WARNING, - "No results in response from " . - $this->host . " for coords $lat, $lon"); - return true; - } - - $n = $rj->geonames[0]; + $n = $geonames[0]; $parts = array(); $location = new Location(); - $parts[] = $n->name; + $parts[] = (string)$n->name; if (!empty($n->adminName1)) { - $parts[] = $n->adminName1; + $parts[] = (string)$n->adminName1; } if (!empty($n->countryName)) { - $parts[] = $n->countryName; + $parts[] = (string)$n->countryName; } - $location->location_id = $n->geonameId; + $location->location_id = (string)$n->geonameId; $location->location_ns = self::LOCATION_NS; - $location->lat = $lat; - $location->lon = $lon; + $location->lat = (string)$lat; + $location->lon = (string)$lon; $location->names[$language] = implode(', ', $parts); @@ -299,7 +259,9 @@ class GeonamesPlugin extends Plugin return true; } - $n = $this->getCache(array('id' => $location->location_id, + $id = $location->location_id; + + $n = $this->getCache(array('id' => $id, 'language' => $language)); if (!empty($n)) { @@ -307,45 +269,32 @@ class GeonamesPlugin extends Plugin return false; } - $client = HTTPClient::start(); - - $result = $client->get($this->wsUrl('hierarchyJSON', - array('geonameId' => $location->location_id, - 'lang' => $language))); - - if (!$result->isOk()) { - $this->log(LOG_WARNING, - "Error code " . $result->code . - " from " . $this->host . " for ID " . $location->location_id); - return false; - } - - $rj = json_decode($result->getBody()); - - if (count($rj->geonames) <= 0) { - $this->log(LOG_WARNING, - "No results " . - " from " . $this->host . " for ID " . $location->location_id); + try { + $geonames = $this->getGeonames('hierarchy', + array('geonameId' => $id, + 'lang' => $language)); + } catch (Exception $e) { + $this->log(LOG_WARNING, "Error for ID $id: " . $e->getMessage()); return false; } $parts = array(); - foreach ($rj->geonames as $level) { + foreach ($geonames as $level) { if (in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { - $parts[] = $level->name; + $parts[] = (string)$level->name; } } - $last = $rj->geonames[count($rj->geonames)-1]; + $last = $geonames[count($geonames)-1]; if (!in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { - $parts[] = $last->name; + $parts[] = (string)$last->name; } if (count($parts)) { $name = implode(', ', array_reverse($parts)); - $this->setCache(array('id' => $location->location_id, + $this->setCache(array('id' => $id, 'language' => $language), $name); } @@ -354,7 +303,7 @@ class GeonamesPlugin extends Plugin } /** - * Human-readable name for a location + * Human-readable URL for a location * * Given a location, we try to retrieve a geonames.org URL. * @@ -452,4 +401,41 @@ class GeonamesPlugin extends Plugin return 'http://'.$this->host.'/'.$method.'?'.$str; } + + function getGeonames($method, $params) + { + $client = HTTPClient::start(); + + $result = $client->get($this->wsUrl($method, $params)); + + if (!$result->isOk()) { + throw new Exception("HTTP error code " . $result->code); + } + + $document = new SimpleXMLElement($result->getBody()); + + if (empty($document)) { + throw new Exception("No results in response"); + } + + if (isset($document->status)) { + throw new Exception("Error #".$document->status['value']." ('".$document->status['message']."')"); + } + + // Array of elements + + return $document->geoname; + } + + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'Geonames', + 'version' => STATUSNET_VERSION, + 'author' => 'Evan Prodromou', + 'homepage' => 'http://status.net/wiki/Plugin:Geonames', + 'rawdescription' => + _m('Uses Geonames service to get human-readable '. + 'names for locations based on user-provided lat/long pairs.')); + return true; + } } diff --git a/plugins/GoogleAnalyticsPlugin.php b/plugins/GoogleAnalyticsPlugin.php index 6891ee6a7b..c646bf113b 100644 --- a/plugins/GoogleAnalyticsPlugin.php +++ b/plugins/GoogleAnalyticsPlugin.php @@ -70,4 +70,16 @@ class GoogleAnalyticsPlugin extends Plugin $action->inlineScript($js1); $action->inlineScript($js2); } + + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'GoogleAnalytics', + 'version' => STATUSNET_VERSION, + 'author' => 'Evan Prodromou', + 'homepage' => 'http://status.net/wiki/Plugin:GoogleAnalytics', + 'rawdescription' => + _m('Use Google Analytics'. + ' to track Web access.')); + return true; + } } diff --git a/plugins/Imap/ImapPlugin.php b/plugins/Imap/ImapPlugin.php new file mode 100644 index 0000000000..0344442222 --- /dev/null +++ b/plugins/Imap/ImapPlugin.php @@ -0,0 +1,85 @@ +. + * + * @category Plugin + * @package StatusNet + * @author Zach Copley%s', $this->xmlrpc_errno(), $server, $this->xmlrpc_error_msg(), $method, print_r($data, TRUE))); + + if ($errno == MOLLOM_REFRESH) { + // Retrieve a list of valid Mollom servers from mollom.com: + $servers = $this->xmlrpc('http://xmlrpc.mollom.com/'. MOLLOM_API_VERSION, 'mollom.getServerList', $this->authentication()); + + // Store the updated list of servers in the database: + //tODO variable_set('mollom_servers', $servers); + } + else if ($errno == MOLLOM_ERROR) { + return $result; + } + else if ($errno == MOLLOM_REDIRECT) { + // Do nothing, we select the next client automatically. + } + + // Reset the XMLRPC error: + $this->xmlrpc_error(0); // FIXME: this is crazy. + } + else { + common_debug("Result = " . print_r($result, TRUE)); + return $result; + } + } + } + + // If none of the servers worked, activate the fallback mechanism: + common_debug("none of the servers worked"); + // _mollom_fallback(); + + // If everything failed, we reset the server list to force Mollom to request a new list: + //TODO variable_set('mollom_servers', array()); + } + + /** + * This function generate an array with all the information required to + * authenticate against Mollom. To prevent that requests are forged and + * that you are impersonated, each request is signed with a hash computed + * based on a private key and a timestamp. + * + * Both the client and the server share the secret key that is used to + * create the authentication hash based on a timestamp. They both hash + * the timestamp with the secret key, and if the hashes match, the + * authenticity of the message has been validated. + * + * To avoid that someone can intercept a (hash, timestamp)-pair and + * use that to impersonate a client, Mollom will reject the request + * when the timestamp is more than 15 minutes off. + * + * Make sure your server's time is synchronized with the world clocks, + * and that you don't share your private key with anyone else. + */ + private function authentication() { + + $public_key = $this->public_key; + $private_key = $this->private_key; + + // Generate a timestamp according to the dateTime format (http://www.w3.org/TR/xmlschema-2/#dateTime): + $time = gmdate("Y-m-d\TH:i:s.\\0\\0\\0O", time()); + + // Calculate a HMAC-SHA1 according to RFC2104 (http://www.ietf.org/rfc/rfc2104.txt): + $hash = base64_encode( + pack("H*", sha1((str_pad($private_key, 64, chr(0x00)) ^ (str_repeat(chr(0x5c), 64))) . + pack("H*", sha1((str_pad($private_key, 64, chr(0x00)) ^ (str_repeat(chr(0x36), 64))) . + $time)))) + ); + + // Store everything in an array. Elsewhere in the code, we'll add the + // acutal data before we pass it onto the XML-RPC library: + $data['public_key'] = $public_key; + $data['time'] = $time; + $data['hash'] = $hash; + + return $data; + } + + + function xmlrpc($url) { + //require_once './includes/xmlrpc.inc'; + $args = func_get_args(); + return call_user_func_array(array('MollomPlugin', '_xmlrpc'), $args); + } + + /** + * Recursively turn a data structure into objects with 'data' and 'type' attributes. + * + * @param $data + * The data structure. + * @param $type + * Optional type assign to $data. + * @return + * Object. + */ + function xmlrpc_value($data, $type = FALSE) { + $xmlrpc_value = new stdClass(); + $xmlrpc_value->data = $data; + if (!$type) { + $type = $this->xmlrpc_value_calculate_type($xmlrpc_value); + } + $xmlrpc_value->type = $type; + if ($type == 'struct') { + // Turn all the values in the array into new xmlrpc_values + foreach ($xmlrpc_value->data as $key => $value) { + $xmlrpc_value->data[$key] = $this->xmlrpc_value($value); + } + } + if ($type == 'array') { + for ($i = 0, $j = count($xmlrpc_value->data); $i < $j; $i++) { + $xmlrpc_value->data[$i] = $this->xmlrpc_value($xmlrpc_value->data[$i]); + } + } + return $xmlrpc_value; + } + + /** + * Map PHP type to XML-RPC type. + * + * @param $xmlrpc_value + * Variable whose type should be mapped. + * @return + * XML-RPC type as string. + * @see + * http://www.xmlrpc.com/spec#scalars + */ + function xmlrpc_value_calculate_type(&$xmlrpc_value) { + // http://www.php.net/gettype: Never use gettype() to test for a certain type [...] Instead, use the is_* functions. + if (is_bool($xmlrpc_value->data)) { + return 'boolean'; + } + if (is_double($xmlrpc_value->data)) { + return 'double'; + } + if (is_int($xmlrpc_value->data)) { + return 'int'; + } + if (is_array($xmlrpc_value->data)) { + // empty or integer-indexed arrays are 'array', string-indexed arrays 'struct' + return empty($xmlrpc_value->data) || range(0, count($xmlrpc_value->data) - 1) === array_keys($xmlrpc_value->data) ? 'array' : 'struct'; + } + if (is_object($xmlrpc_value->data)) { + if ($xmlrpc_value->data->is_date) { + return 'date'; + } + if ($xmlrpc_value->data->is_base64) { + return 'base64'; + } + $xmlrpc_value->data = get_object_vars($xmlrpc_value->data); + return 'struct'; + } + // default + return 'string'; + } + +/** + * Generate XML representing the given value. + * + * @param $xmlrpc_value + * @return + * XML representation of value. + */ +function xmlrpc_value_get_xml($xmlrpc_value) { + switch ($xmlrpc_value->type) { + case 'boolean': + return '