Index: common/common.php
===================================================================
--- common/common.php	(revision 1164)
+++ common/common.php	(working copy)
@@ -369,4 +369,40 @@ if (function_exists("date_default_timezone_set")) {
     date_default_timezone_set("UTC");
 }
 
+/*
+ * Checks weather the current connection is through the https request
+ *
+ * returns bool
+ */
+function is_connection_secure() {
+    $https = getattr($_SERVER, 'HTTPS', null);
+
+    if ($https == 'on' || $https == '1' || $_SERVER['SERVER_PORT'] == 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 1164)
+++ 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,127 @@
+<?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 $ip
+ * @return int
+ */
+function get_tokens($ip = null) {
+    if ($ip == null) {
+        $ip = remote_ip_info();
+    }
+    global $tokens;
+    if (isset($tokens[$ip])) {
+        return $tokens[$ip][0];
+    }
+
+    $query = sprintf("SELECT tokens FROM ia_tokens WHERE `ip` = '%s'",
+            db_escape($ip));
+    $tokens[$ip][0] = $tokens[$ip][1] = db_query_value($query, IA_TOKENS_MAX);
+    return $tokens[$ip][0];
+}
+
+/**
+ * Remove tokens
+ * Returns true on succes or false if not enough tokens
+ * 0 tokens are never enough
+ *
+ * @param int $price
+ * @param string $ip
+ * @return bool
+ */
+function pay_tokens($price, $ip = null) {
+    if ($ip == null) {
+        $ip = remote_ip_info();
+    }
+    global $tokens;
+    // The first element tells us the current tokens and the second the initial ones
+    if (!isset($tokens[$ip])) {
+        get_tokens($ip);
+    }
+
+    if ($tokens[$ip][0] <= $price) {
+        return false;
+    }
+
+    $tokens[$ip][0] -= $price;
+    return true;
+}
+
+/**
+ * Updates the current ip's tokens only if modifications were made to
+ * lower the amount of requests on database
+ *
+ * @param int $amount   optional parameter in case we want something different
+ * @param string $ip
+ */
+function save_tokens($amount = null, $ip = null) {
+    if ($ip == null) {
+        $ip = remote_ip_info();
+    }
+    global $tokens;
+    if ($amount == null) {
+        if (!isset($tokens[$ip]) || $tokens[$ip][0] == $tokens[$ip][1]) {
+            return;
+        }
+
+        $amount = min($tokens[$ip][0], IA_TOKENS_MAX);
+    }
+
+    $query = sprintf("REPLACE INTO `ia_tokens` VALUES('%s', %d)",
+            db_escape($ip), db_escape($amount));
+    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 string $ip
+ * @return string
+ */
+function check_captcha_for_tokens($required = false, $ip = null) {
+    if ($ip == null) {
+        $ip = remote_ip_info();
+    }
+    if (IA_DEVELOPMENT_MODE) {
+        pay_tokens(-IA_TOKENS_MAX, $ip);
+        return 'Confirmati ca sunteti om';
+    }
+
+    $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(-IA_TOKENS_MAX, $ip);
+        return '';
+    }
+    return 'Confirmati ca sunteti om';
+}
+
+?>
Index: common/external_libs/recaptchalib.php
===================================================================
--- common/external_libs/recaptchalib.php	(revision 1164)
+++ common/external_libs/recaptchalib.php	(working copy)
@@ -36,7 +36,7 @@
  * 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_API_SECURE_SERVER", "https://www.google.com/recaptcha/api");
 define("RECAPTCHA_VERIFY_SERVER", "api-verify.recaptcha.net");
 
 /**
Index: scripts/migrate-add-tokens
new file mode 100755
===================================================================
--- scripts/migrate-add-tokens	(revision 0)
+++ scripts/migrate-add-tokens	(working copy)
@@ -0,0 +1,11 @@
+#! /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 (`ip` VARCHAR(64) NOT NULL, `tokens` INT NOT NULL, PRIMARY KEY (ip))";
+db_query($query);
+?>
Index: www/config.php
===================================================================
--- www/config.php	(revision 1164)
+++ www/config.php	(working copy)
@@ -69,6 +69,11 @@ define("IA_BLOG_SIDEBAR", "blog-sidebar");
 // LaTeX support
 define("IA_LATEX_ENABLE", false);
 
+// Token constants
+define('IA_TOKENS_MAX', 1000000);
+define('IA_TOKENS_REGISTER', 1000000);
+define('IA_TOKENS_LOGIN', 200000);
+
 // List of safe MIME types
 // FIXME: add more?
 $IA_SAFE_MIME_TYPES = array('image/jpeg', 'image/pjpeg', 'image/png', 'image/gif', 'image/bmp', 'image/x-ms-bmp');
Index: www/controllers/account_validator.php
===================================================================
--- www/controllers/account_validator.php	(revision 1164)
+++ www/controllers/account_validator.php	(working copy)
@@ -1,19 +1,18 @@
 <?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";
+    if (($captcha_error = check_captcha_for_tokens(true)) == '') {
+        if (count($errors) > 0) {
+            pay_tokens(IA_TOKENS_MAX);
         }
+    } else {
+        $errors['captcha'] = $captcha_error;
     }
 
     return $errors;
Index: www/controllers/login.php
===================================================================
--- www/controllers/login.php	(revision 1164)
+++ 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,13 +42,26 @@ function controller_login() {
             }
         }
 
+        // We won't give any tokens to a wrong login attempt
+        if (($form_errors['captcha'] = check_captcha_for_tokens()) == '') {
+            if ($errors) {
+                pay_tokens(IA_TOKENS_MAX);
+            }
+        };
         // 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 (get_tokens() <= IA_TOKENS_LOGIN) {
+            if (!$errors) {
+                $errors = 'Va rugam confirmati ca sunteti om';
+            }
+        }
+
         // process
         if (!$errors) {
             // persist user to session (login)
@@ -67,14 +87,15 @@ function controller_login() {
             }
         }
         else {
+            // wrong login, pay tokens
+            pay_tokens(IA_TOKENS_LOGIN);
             // save referer so we know where to redirect when login finally
             // succeeds.
             if (!isset($_SESSION['_ia_redirect']) && $referer) {
                 $_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 1164)
+++ 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,9 @@ 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');
-        }
-
         $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,13 +41,13 @@ 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());
             flash("Felicitari! Contul a fost creat. Acum te poti "
                   ."autentifica.");
             redirect(url_login());
         } else {
+            pay_tokens(IA_TOKENS_REGISTER);
             flash_error('Am intalnit probleme. Verifica datele introduse.');
         }
     } else {
@@ -59,17 +56,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/static/css/screen.css
===================================================================
--- www/static/css/screen.css	(revision 1164)
+++ www/static/css/screen.css	(working copy)
@@ -1542,3 +1542,11 @@ ul.tag_filters {
 table.sortable th {
     cursor: pointer;
 }
+
+/* Make form tables have no border */
+table.form {
+    width: 100%;
+}
+table.form td {
+    border: 0px;
+}
Index: www/url.php
===================================================================
--- www/url.php	(revision 1164)
+++ 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 1164)
+++ 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 1164)
+++ 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">
@@ -22,5 +25,33 @@
         </li>
     </ul>
 </fieldset>
+    </td>
+<?php
+if (isset($view['captcha'])) {
+?>
+    <td>
+<fieldset>
+    <legend>Verificare</legend>
+    <ul class="form">
+        <li>
+            <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>
 </form>
 
Index: www/views/register.php
===================================================================
--- www/views/register.php	(revision 1164)
+++ 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 1164)
+++ 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)";
