Index: common/common.php
===================================================================
--- common/common.php	(revision 1172)
+++ common/common.php	(working copy)
@@ -369,4 +369,41 @@ if (function_exists("date_default_timezone_set")) {
     date_default_timezone_set("UTC");
 }
 
+/*
+ * Checks whether the current connection is through the https request
+ *
+ * returns bool
+ */
+function is_connection_secure() {
+    $https = getattr($_SERVER, 'HTTPS', null);
+
+    if ($https == 'on' || $https == '1' ||
+            getattr($_SERVER, 'SERVER_PORT', 80) == 443) {
+        return true;
+    }
+    return false;
+}
+
+/*
+ *Return information about the remote IP address. Useful for logging.
+ * This isn't necessarily just an IP address. It might contain proxy
+ * information when available.
+ *
+ * @return string
+ */
+function remote_ip_info() {
+    $ip_address = getattr($_SERVER, 'REMOTE_ADDR');
+    if ($ip_address && !is_valid_ip_address($ip_address)) {
+        log_warn("Invalid IP address: {$ip_address}", true, 1);
+    }
+    // FIXME: Also validate XFF header.
+    if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
+        return getattr($_SERVER, 'REMOTE_ADDR')."; "
+                .$_SERVER['HTTP_X_FORWARDED_FOR'];
+    } else {
+        return getattr($_SERVER, 'REMOTE_ADDR');
+    }
+}
+
+
 ?>
Index: common/db/db.php
===================================================================
--- common/db/db.php	(revision 1172)
+++ common/db/db.php	(working copy)
@@ -84,7 +84,7 @@ function db_query_value($query, $default_value = null) {
 
     $rows = db_fetch_all($query);
 
-    if (is_null($rows)) {
+    if (count($rows) == 0) {
         return $default_value;
     }
 
Index: common/db/tokens.php
new file mode 100644
===================================================================
--- common/db/tokens.php	(revision 0)
+++ common/db/tokens.php	(working copy)
@@ -0,0 +1,155 @@
+<?php
+
+require_once(IA_ROOT_DIR . 'common/db/db.php');
+require_once(IA_ROOT_DIR . 'common/external_libs/recaptchalib.php');
+
+/**
+ * Get the current tokens
+ * If we already asked for it do not do an extra SQL request
+ * FIXME: should we find a different method rather than global variables
+ * to hold the value?
+ *
+ * @param string $identifier
+ * @return int
+ */
+function get_tokens($identifier = null) {
+    if ($identifier == null) {
+        $identifier = remote_ip_info();
+    }
+    global $tokens;
+    if (isset($tokens[$identifier])) {
+        return $tokens[$identifier][0];
+    }
+
+    // If we are working in development mode we always get double the
+    // maximum tokens so captchas become useless. Left like this so we
+    // can track token errors in development mode
+    // We may have actions which require captcha all the time(such as register)
+    // which would have the cost IA_TOKENS_MAX + 1
+    if (IA_DEVELOPMENT_MODE) {
+        $tokens[$identifier][0] = $tokens[$identifier][1] =
+            2 * IA_TOKENS_MAX;
+        return 2 * IA_TOKENS_MAX;
+    }
+    $query = sprintf("SELECT tokens, timestamp FROM ia_tokens WHERE "
+            . "`identifier` = '%s'", db_escape($identifier));
+    $result = db_fetch($query);
+
+    // Receive the tokens and add the regenerated ones
+    if (count($result) > 0) {
+        $amount = min(IA_TOKENS_MAX, $result['tokens'] +
+            (time() - db_date_parse($result['timestamp'])) / IA_TOKENS_REGEN);
+    } else {
+        $amount = IA_TOKENS_MAX;
+    }
+
+    // Receive tokens for idle time
+    $tokens[$identifier][0] = $tokens[$identifier][1] = $amount;
+    return $tokens[$identifier][0];
+}
+
+/**
+ * Remove tokens
+ * Returns true on succes or false if not enough tokens
+ * 0 tokens are never enough
+ * if not enough tokens pay what we have
+ *
+ * @param int $price
+ * @param string $identifier
+ * @return bool
+ */
+function pay_tokens($price, $identifier = null) {
+    if ($identifier == null) {
+        $identifier = remote_ip_info();
+    }
+    global $tokens;
+    // The first element tells us the current tokens and the second the initial
+    // ones
+    if (!isset($tokens[$identifier])) {
+        get_tokens($identifier);
+    }
+
+    if ($tokens[$identifier][0] < $price) {
+        $tokens[$identifier][0] = 0;
+        return false;
+    }
+
+    $tokens[$identifier][0] -= $price;
+    return true;
+}
+
+/**
+ * Updates the current identifier's tokens only if modifications were made to
+ * lower the amount of requests on database
+ *
+ * @param string $identifier
+ * @param int $amount   optional parameter in case we want something different
+ */
+function save_tokens($identifier = null, $amount = null) {
+    if ($identifier == null) {
+        $identifier = remote_ip_info();
+    }
+    global $tokens;
+    if ($amount == null) {
+        if (!isset($tokens[$identifier]) ||
+                $tokens[$identifier][0] == $tokens[$identifier][1]) {
+            return;
+        }
+
+        $amount = min($tokens[$identifier][0], IA_TOKENS_MAX);
+    }
+
+    $query = sprintf("REPLACE INTO `ia_tokens` VALUES('%s', %d, '%s')",
+            db_escape($identifier), db_escape($amount),
+            db_escape(db_date_format()));
+    db_query($query);
+}
+
+/**
+ * Receives tokens on a correct recaptcha
+ * It adds the maximum amount of tokens thus surpassing the limit,
+ * no problem though because we don't push anything bigger than the
+ * limit
+ * FIXME: should these be function parameters?
+ * It searches for recaptcha information in $_POST
+ * FIXME: is it okay to use pay_tokens with negative parameter?
+ *
+ * Returns an error if a captcha is submitted
+ * @param bool $required   weather we need to check for errors even if there
+ *                         is no request
+ * @param int $amount
+ * @param string $identifier
+ * @return string
+ */
+function check_captcha_for_tokens($amount = IA_TOKENS_CAPTCHA,
+                                  $required = false, $identifier = null) {
+    if ($identifier == null) {
+        $identifier = remote_ip_info();
+    }
+    if (IA_DEVELOPMENT_MODE) {
+        pay_tokens(-IA_TOKENS_MAX, $identifier);
+        return '';
+    }
+
+    $challenge = request('recaptcha_challenge_field');
+    $response = request('recaptcha_response_field');
+
+    if (($challenge && $response) || $required == true) {
+        $captcha =  recaptcha_check_answer(IA_CAPTCHA_PRIVATE_KEY,
+                        $_SERVER["REMOTE_ADDR"],
+                        $challenge,
+                        $response);
+        if (!$captcha -> is_valid) {
+            if ($challenge === null && $response === null) {
+                return 'Confirmati ca sunteti om';
+            } else {
+                return 'Cuvintele introduse de tine sunt incorecte';
+            }
+        }
+        pay_tokens(-$amount, $identifier);
+        return '';
+    }
+    return 'Confirmati ca sunteti om';
+}
+
+?>
Index: common/external_libs/recaptchalib.php
===================================================================
--- common/external_libs/recaptchalib.php	(revision 1172)
+++ common/external_libs/recaptchalib.php	(working copy)
@@ -4,7 +4,7 @@
  *    - Documentation and latest version
  *          http://recaptcha.net/plugins/php/
  *    - Get a reCAPTCHA API Key
- *          http://recaptcha.net/api/getkey
+ *          https://www.google.com/recaptcha/admin/create
  *    - Discussion group
  *          http://groups.google.com/group/recaptcha
  *
@@ -35,9 +35,9 @@
 /**
  * The reCAPTCHA server URL's
  */
-define("RECAPTCHA_API_SERVER", "http://api.recaptcha.net");
-define("RECAPTCHA_API_SECURE_SERVER", "https://api-secure.recaptcha.net");
-define("RECAPTCHA_VERIFY_SERVER", "api-verify.recaptcha.net");
+define("RECAPTCHA_API_SERVER", "http://www.google.com/recaptcha/api");
+define("RECAPTCHA_API_SECURE_SERVER", "https://www.google.com/recaptcha/api");
+define("RECAPTCHA_VERIFY_SERVER", "www.google.com");
 
 /**
  * Encodes the given data into a query string format
@@ -106,7 +106,7 @@ function _recaptcha_http_post($host, $path, $data, $port = 80) {
 function recaptcha_get_html ($pubkey, $error = null, $use_ssl = false)
 {
 	if ($pubkey == null || $pubkey == '') {
-		die ("To use reCAPTCHA you must get an API key from <a href='http://recaptcha.net/api/getkey'>http://recaptcha.net/api/getkey</a>");
+		die ("To use reCAPTCHA you must get an API key from <a href='https://www.google.com/recaptcha/admin/create'>https://www.google.com/recaptcha/admin/create</a>");
 	}
 	
 	if ($use_ssl) {
@@ -152,7 +152,7 @@ class ReCaptchaResponse {
 function recaptcha_check_answer ($privkey, $remoteip, $challenge, $response, $extra_params = array())
 {
 	if ($privkey == null || $privkey == '') {
-		die ("To use reCAPTCHA you must get an API key from <a href='http://recaptcha.net/api/getkey'>http://recaptcha.net/api/getkey</a>");
+		die ("To use reCAPTCHA you must get an API key from <a href='https://www.google.com/recaptcha/admin/create'>https://www.google.com/recaptcha/admin/create</a>");
 	}
 
 	if ($remoteip == null || $remoteip == '') {
@@ -169,7 +169,7 @@ function recaptcha_check_answer ($privkey, $remoteip, $challenge, $response, $ex
                 return $recaptcha_response;
         }
 
-        $response = _recaptcha_http_post (RECAPTCHA_VERIFY_SERVER, "/verify",
+        $response = _recaptcha_http_post (RECAPTCHA_VERIFY_SERVER, "/recaptcha/api/verify",
                                           array (
                                                  'privatekey' => $privkey,
                                                  'remoteip' => $remoteip,
@@ -200,7 +200,7 @@ function recaptcha_check_answer ($privkey, $remoteip, $challenge, $response, $ex
  * @param string $appname The name of your application
  */
 function recaptcha_get_signup_url ($domain = null, $appname = null) {
-	return "http://recaptcha.net/api/getkey?" .  _recaptcha_qsencode (array ('domain' => $domain, 'app' => $appname));
+	return "https://www.google.com/recaptcha/admin/create?" .  _recaptcha_qsencode (array ('domains' => $domain, 'app' => $appname));
 }
 
 function _recaptcha_aes_pad($val) {
@@ -230,14 +230,14 @@ function _recaptcha_mailhide_urlbase64 ($x) {
 function recaptcha_mailhide_url($pubkey, $privkey, $email) {
 	if ($pubkey == '' || $pubkey == null || $privkey == "" || $privkey == null) {
 		die ("To use reCAPTCHA Mailhide, you have to sign up for a public and private key, " .
-		     "you can do so at <a href='http://mailhide.recaptcha.net/apikey'>http://mailhide.recaptcha.net/apikey</a>");
+		     "you can do so at <a href='http://www.google.com/recaptcha/mailhide/apikey'>http://www.google.com/recaptcha/mailhide/apikey</a>");
 	}
 	
 
 	$ky = pack('H*', $privkey);
 	$cryptmail = _recaptcha_aes_encrypt ($email, $ky);
 	
-	return "http://mailhide.recaptcha.net/d?k=" . $pubkey . "&c=" . _recaptcha_mailhide_urlbase64 ($cryptmail);
+	return "http://www.google.com/recaptcha/mailhide/d?k=" . $pubkey . "&c=" . _recaptcha_mailhide_urlbase64 ($cryptmail);
 }
 
 /**
@@ -262,7 +262,7 @@ function _recaptcha_mailhide_email_parts ($email) {
  * Gets html to display an email address given a public an private key.
  * to get a key, go to:
  *
- * http://mailhide.recaptcha.net/apikey
+ * http://www.google.com/recaptcha/mailhide/apikey
  */
 function recaptcha_mailhide_html($pubkey, $privkey, $email) {
 	$emailparts = _recaptcha_mailhide_email_parts ($email);
@@ -275,3 +275,4 @@ function recaptcha_mailhide_html($pubkey, $privkey, $email) {
 
 
 ?>
+
Index: scripts/migrate-add-tokens
new file mode 100755
===================================================================
--- scripts/migrate-add-tokens	(revision 0)
+++ scripts/migrate-add-tokens	(working copy)
@@ -0,0 +1,13 @@
+#! /usr/bin/env php
+<?php
+
+require_once(dirname($argv[0]) . '/utilities.php');
+require_once(IA_ROOT_DIR . 'common/db/db.php');
+
+db_connect();
+
+$query = "CREATE TABLE IF NOT EXISTS ia_tokens (`identifier` VARCHAR(64) "
+       . "NOT NULL, `tokens` INT NOT NULL, `timestamp` DATETIME, PRIMARY KEY "
+       . "(identifier))";
+db_query($query);
+?>
Index: www/config.php
===================================================================
--- www/config.php	(revision 1172)
+++ www/config.php	(working copy)
@@ -69,6 +69,12 @@ define("IA_BLOG_SIDEBAR", "blog-sidebar");
 // LaTeX support
 define("IA_LATEX_ENABLE", !IA_DEVELOPMENT_MODE);
 
+// Token constants
+define('IA_TOKENS_MAX', 60);
+define('IA_TOKENS_REGISTER', 61);
+define('IA_TOKENS_CAPTCHA', 5);
+define('IA_TOKENS_LOGIN', 20);
+define('IA_TOKENS_REGEN', 300);
 // List of safe MIME types
 // FIXME: add more?
 $IA_SAFE_MIME_TYPES = array('image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/x-ms-bmp');
Index: www/controllers/account_validator.php
===================================================================
--- www/controllers/account_validator.php	(revision 1172)
+++ www/controllers/account_validator.php	(working copy)
@@ -1,19 +1,16 @@
 <?php
 
-require_once(IA_ROOT_DIR."common/tags.php");
+require_once(IA_ROOT_DIR . 'common/tags.php');
+require_once(IA_ROOT_DIR . 'common/db/tokens.php');
 
 // validates registration input data (wrapper for validate_data)
 function validate_register_data($data) {
     $errors = validate_user_data($data, true, null);
 
-    if (!IA_DEVELOPMENT_MODE) {
-        $resp = recaptcha_check_answer(IA_CAPTCHA_PRIVATE_KEY,
-                                       $_SERVER["REMOTE_ADDR"],
-                                       $data['recaptcha_challenge_field'],
-                                       $data['recaptcha_response_field']);
-        if (!$resp->is_valid) {
-            $errors['captcha'] = "Cuvintele introduse de tine sunt incorecte";
-        }
+    // Give enough tokens back for a login
+    $errors['captcha'] = check_captcha_for_tokens(IA_TOKENS_CAPTCHA, true);
+    if (!$errors['captcha']) {
+        unset($errors['captcha']);
     }
 
     return $errors;
Index: www/controllers/login.php
===================================================================
--- www/controllers/login.php	(revision 1172)
+++ www/controllers/login.php	(working copy)
@@ -3,15 +3,21 @@
 require_once(IA_ROOT_DIR . "common/db/user.php");
 
 function controller_login() {
+    if (!is_connection_secure()) {
+        redirect(url_login());
+    }
+
     global $identity_user;
 
     // `data` dictionary is a dictionary with data to be displayed by form view
     // when displaying the form for the first time, this is filled with
     $data = array();
 
-    // here we store validation errors.
-    // It is a dictionary, indexed by field names
-    $errors = array();
+    // array for the captcha error
+    $form_errors = array();
+
+    // The flash error
+    $errors = '';
 
     // process input?
     $submit = request_is_post();
@@ -25,7 +31,8 @@ function controller_login() {
         if (!$user) {
             $user = user_test_ia1_password($data['username'], $data['password']);
             if (!$user) {
-                $errors = true;
+                $errors = 'Numele de utilizator inexistent sau parola ' .
+                          'incorecta. Incearca din nou.';
             }
             else {
                 // update password to the SHA1 algorithm
@@ -35,15 +42,30 @@ function controller_login() {
             }
         }
 
+        // Get some tokens from the captcha
+        // It's not like that matters in any way because we do not receive
+        // enough for the login cost
+        $form_errors['captcha'] = check_captcha_for_tokens();
+
         // obtain referer
         $referer = getattr($_SERVER, 'HTTP_REFERER', '');
-        if ($referer == url_absolute(url_login())) {
+        if ($referer == url_login()) {
             // we don't care about the login page
             $referer = null;
         }
 
+        // pay tokens for loging in
+        if (!pay_tokens(IA_TOKENS_LOGIN)) {
+            if ($form_errors['captcha']) {
+                $errors = 'Va rugam confirmati ca sunteti om';
+                unset($form_errors['captcha']);
+            }
+        }
+
         // process
         if (!$errors) {
+            // good user receives some tokens back enough to logout and login as a different user
+            pay_tokens(-IA_TOKENS_LOGIN);
             // persist user to session (login)
             $remember_user = ($data['remember'] ? true : false);
             identity_start_session($user, $remember_user);
@@ -73,8 +95,7 @@ function controller_login() {
                 $_SESSION['_ia_redirect'] = $_SERVER['HTTP_REFERER'];
             }
 
-            flash_error('Numele de utilizator inexistent sau parola ' .
-                        'incorecta. Incearca din nou.');
+            flash_error($errors);
         }
     }
 
@@ -84,10 +105,15 @@ function controller_login() {
     $view['page_name'] = "login";
     $view['title'] = "Autentificare";
     $view['form_values'] = $data;
-    $view['form_errors'] = array();
+    $view['form_errors'] = $form_errors;
     $view['topnav_select'] = 'login';
     $view['no_sidebar_login'] = true;
 
+    if (get_tokens() < IA_TOKENS_LOGIN) {
+        $view['captcha'] = recaptcha_get_html(IA_CAPTCHA_PUBLIC_KEY, null,
+                true);
+    }
+
     execute_view_die('views/login.php', $view);
 }
 
Index: www/controllers/register.php
===================================================================
--- www/controllers/register.php	(revision 1172)
+++ www/controllers/register.php	(working copy)
@@ -1,11 +1,14 @@
 <?php
 
-require_once(IA_ROOT_DIR."common/db/user.php");
-require_once(IA_ROOT_DIR."common/db/smf.php");
-require_once(IA_ROOT_DIR."common/external_libs/recaptchalib.php");
-require_once(IA_ROOT_DIR."www/controllers/account_validator.php");
+require_once(IA_ROOT_DIR . 'common/db/user.php');
+require_once(IA_ROOT_DIR . 'common/db/smf.php');
+require_once(IA_ROOT_DIR . 'www/controllers/account_validator.php');
+require_once(IA_ROOT_DIR . 'common/db/tokens.php');
 
 function controller_register() {
+    if (!is_connection_secure()) {
+        redirect(url_register());
+    }
     $submit = request_is_post();
 
     // Initialize view parameters.
@@ -28,15 +31,10 @@ function controller_register() {
         $data['newsletter'] = (request('newsletter') ? 1 : 0);
         $data['tnc'] = (request('tnc') ? 1 : 0);
 
-        if(!IA_DEVELOPMENT_MODE) {
-            $data['recaptcha_challenge_field'] = request('recaptcha_challenge_field');
-            $data['recaptcha_response_field'] = request('recaptcha_response_field');
-        }
-
+        pay_tokens(IA_TOKENS_REGISTER);
         $errors = validate_register_data($data);
-
         // 2. process
-        if (!$errors) {
+        if (count($errors) == 0) {
             // create database entry for user
             $user = user_init();
             $user['username'] = $data['username'];
@@ -44,9 +42,11 @@ function controller_register() {
             $user['full_name'] = $data['full_name'];
             $user['email'] = $data['email'];
             $user['newsletter'] = $data['newsletter'];
-
             // There are no acceptable errors in user_create.
             user_create($user, remote_ip_info());
+
+            // give user enough tokens to pass login without captcha
+            pay_tokens(-IA_TOKENS_LOGIN);
             flash("Felicitari! Contul a fost creat. Acum te poti "
                   ."autentifica.");
             redirect(url_login());
@@ -59,17 +59,19 @@ function controller_register() {
         $data['tnc'] = 1;
     }
 
-    if(!IA_DEVELOPMENT_MODE) {
-        $view['captcha'] = recaptcha_get_html(IA_CAPTCHA_PUBLIC_KEY);
+    if(get_tokens() < IA_TOKENS_REGISTER) {
+        $view['captcha'] = recaptcha_get_html(IA_CAPTCHA_PUBLIC_KEY, null,
+                true);
     }
 
     // attach form is displayed for the first time or a validation error occured
+
     $view['title'] = 'Inregistreaza-te!';
     $view['page_name'] = 'register';
     $view['form_errors'] = $errors;
     $view['form_values'] = $data;
     $view['topnav_select'] = 'register';
-    $view['action'] = 'register';
+    $view['action'] = url_register();
     $view['no_sidebar_login'] = true;
     execute_view_die('views/register.php', $view);
 }
Index: www/controllers/resetpass.php
===================================================================
--- www/controllers/resetpass.php	(revision 1172)
+++ www/controllers/resetpass.php	(working copy)
@@ -127,7 +127,7 @@ Parola noua: {$new_password}
 Numele contului: {$user['username']}
 
 Te poti autentifica aici:
-".url_absolute(url_login())."
+".url_login()."
 
 Echipa infoarena
 ".IA_URL."\n";
Index: www/static/css/screen.css
===================================================================
--- www/static/css/screen.css	(revision 1172)
+++ www/static/css/screen.css	(working copy)
@@ -1549,3 +1549,11 @@ div.social_buttons {
     margin-left: 5px;
     height: 65px;
 }
+
+/* Make form tables have no border */
+table.form {
+    width: 100%;
+}
+table.form td {
+    border: 0px;
+}
Index: www/url.php
===================================================================
--- www/url.php	(revision 1172)
+++ www/url.php	(working copy)
@@ -1,6 +1,7 @@
 <?php
 
-require_once(IA_ROOT_DIR.'www/config.php');
+require_once(IA_ROOT_DIR . 'www/config.php');
+require_once(IA_ROOT_DIR . 'common/common.php');
 
 // Creates URLs to various parts of the infoarena website.
 // Please avoid hard-coding URLs throughout the code.
@@ -16,25 +17,34 @@ require_once(IA_ROOT_DIR.'www/config.php');
 //
 // If $absolute is true(default false) then IA_URL_HOST will be included in
 // the url.
-function url_complex($document = '', $args = array(), $absolute = false) {
+function url_complex($document = '', $args = array(), $absolute = false,
+        $secure_connection = false) {
     log_assert(false === strpos($document, '?'), 'Page name contains ?');
     log_assert(is_array($args), "Argument list must be an array");
     log_assert(!array_key_exists("page", $args), "Argument list contains page");
-
+    if ($secure_connection == true) {
+        log_assert($absolute == true);
+    }
     $args['page'] = $document;
     $url = url_from_args($args, $absolute);
     if ($absolute) {
-        return url_absolute($url);
+        return url_absolute($url, $secure_connection);
     } else  {
         return $url;
     }
 }
 
 // Makes an url absolute. It just prepends IA_URL_HOST
-function url_absolute($url)
+function url_absolute($url, $force_secure_connection = false)
 {
     log_assert(strpos($url, 'http') !== 0, "Url begins with http");
-    return IA_URL_HOST . $url;
+    $url = IA_URL_HOST . $url;
+    if ($force_secure_connection == true || is_connection_secure()) {
+        $count = 1;
+        $url = str_replace('http', 'https', $url, $count);
+    }
+
+    return $url;
 }
 
 // Construct an URL from an argument list.
@@ -190,7 +200,7 @@ function url_image_resize($page, $file, $resize)
 // User stuff
 
 function url_login() {
-    return url_complex("login");
+    return url_complex("login", array(), true, true);
 }
 
 function url_logout() {
@@ -206,7 +216,7 @@ function url_account($user = false) {
 }
 
 function url_register() {
-    return url_complex("register");
+    return url_complex("register", array(), true, true);
 }
 
 function url_resetpass($username = false) {
Index: www/utilities.php
===================================================================
--- www/utilities.php	(revision 1172)
+++ www/utilities.php	(working copy)
@@ -1,7 +1,7 @@
 <?php
 
-require_once(IA_ROOT_DIR."www/url.php");
-
+require_once(IA_ROOT_DIR . 'www/url.php');
+require_once(IA_ROOT_DIR . 'common/db/tokens.php');
 // Wrapper around htmlentities which defaults charset to UTF-8
 function html_escape($string, $quote_style = ENT_COMPAT, $charset = "UTF-8")
 {
@@ -77,6 +77,7 @@ function request_is_post() {
 function redirect($absolute_url) {
     header("Location: {$absolute_url}\n\n");
     session_write_close();
+    save_tokens();
     die();
 }
 
@@ -250,24 +251,8 @@ function execute_view_die($view_file_name, $view) {
         log_execution_stats();
     }
     session_write_close();
+    save_tokens();
     die();
 }
 
-// Return information about the remote IP address. Useful for logging.
-// This isn't necessarily just an IP address. It might contain proxy
-// information when available.
-function remote_ip_info() {
-    $ip_address = getattr($_SERVER, 'REMOTE_ADDR');
-    if ($ip_address && !is_valid_ip_address($ip_address)) {
-        log_warn("Invalid IP address: {$ip_address}", true, 1);
-    }
-    // FIXME: Also validate XFF header.
-    if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
-        return getattr($_SERVER, 'REMOTE_ADDR')."; "
-                .$_SERVER['HTTP_X_FORWARDED_FOR'];
-    } else {
-        return getattr($_SERVER, 'REMOTE_ADDR');
-    }
-}
-
 ?>
Index: www/views/form_login.php
===================================================================
--- www/views/form_login.php	(revision 1172)
+++ www/views/form_login.php	(working copy)
@@ -1,4 +1,7 @@
 <form action="<?= html_escape(url_login()) ?>" method="post" class="login">
+<table class="form">
+  <tr>
+    <td>
 <fieldset>
     <legend><img src="<?= html_escape(url_static('images/icons/login.png')) ?>" alt="!" /> Autentificare</legend>
     <ul class="form">
@@ -17,10 +20,40 @@
             <input type="checkbox" value="on" id="form_remember" name="remember" class="checkbox"<?= fval('remember') ? ' checked="checked"' : '' ?>/>
             <label class="checkbox" for="form_remember">Pastreaza-ma autentificat 5 zile</label>
         </li>
+    </ul>
+</fieldset>
+    </td>
+<?php
+if (isset($view['captcha'])) {
+?>
+    <td>
+<fieldset>
+    <legend>Verificare</legend>
+    <ul class="form">
         <li>
-            <input type="submit" value="Autentificare" id="form_submit" class="button important" />
+            <script type="text/javascript">
+                var RecaptchaOptions = {
+                theme : 'clean',
+                };
+            </script>
+
+            <label>Scrieti cuvintele de mai jos:</label>
+            <?= ferr_span('captcha') ?>
+            <?= $view['captcha'] ?>
+            <span class="fieldHelp">Va rugam sa transcrieti cuvintele de mai sus in aceasta casuta pentru verificare</span>
         </li>
     </ul>
 </fieldset>
+    </td>
+<?php
+    }
+?>
+</tr>
+</table>
+<ul class="form clear">
+  <li>
+    <input type="submit" value="Autentificare" id="form_submit" class="button important" />
+  </li>
+</ul>
 </form>
 
Index: www/views/register.php
===================================================================
--- www/views/register.php	(revision 1172)
+++ www/views/register.php	(working copy)
@@ -75,7 +75,7 @@
 </fieldset>
 
 <?php
-    if(!IA_DEVELOPMENT_MODE) {
+    if(isset($view['captcha'])) {
 ?>
 <fieldset>
     <legend>Verificare</legend>
Index: www/wiki/MyTextile.php
===================================================================
--- www/wiki/MyTextile.php	(revision 1172)
+++ www/wiki/MyTextile.php	(working copy)
@@ -5,7 +5,7 @@ require_once(IA_ROOT_DIR."common/attachment.php");
 require_once(IA_ROOT_DIR."common/string.php");
 require_once(IA_ROOT_DIR."www/wiki/latex.php");
 require_once(IA_ROOT_DIR."www/utilities.php");
-
+require_once(IA_ROOT_DIR . 'www/url.php');
 class MyTextile extends Textile {
     // FIXME: If you see a pointless textile error try tweaking this value.
     private $my_error_reporting = 0xF7F7;
@@ -112,7 +112,7 @@ class MyTextile extends Textile {
             if (preg_match("/^ ([^\?]+) \? (".IA_RE_ATTACHMENT_NAME.") $/sxi", $url, $matches)) {
                 $args['url'] = url_attachment($matches[1], $matches[2]);
             } else {
-                $args['url'] = IA_URL . $url;
+                $args['url'] = IA_URL_PREFIX . $url;
             }
         } else {
             $args['clsty'] .= "(wiki_link_external)";
