Index: acquia_agent/acquia_agent.module
===================================================================
--- acquia_agent/acquia_agent.module	(revision 5121)
+++ acquia_agent/acquia_agent.module	(working copy)
@@ -19,6 +19,8 @@
 define('SUBSCRIPTION_MESSAGE_INVALID' , 1700);
 define('SUBSCRIPTION_VALIDATION_ERROR', 1800);
 
+define('SUBSCRIPTION_PROVISION_ERROR' , 9000);
+
 /**
  * Subscription message lifetime defined by the Acquia Network.
  */
@@ -35,6 +37,18 @@
     'file' => 'acquia_agent.pages.inc',
     'access arguments' => array('administer site configuration'),
   );
+  $items['admin/settings/acquia-agent/view'] = array(
+    'title' => 'Settings',
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+    'weight' => -1,
+  );
+  $items['admin/settings/acquia-agent/free-trial'] = array(
+    'title' => 'Free trial',
+    'page callback' => 'acquia_agent_freetrial_page',
+    'file' => 'acquia_agent.pages.inc',
+    'access callback' => 'acquia_agent_freetrial_access',
+    'type' => MENU_LOCAL_TASK,
+  );
   $items['admin/settings/acquia-agent/refresh-status'] = array(
     'title' => 'Manual update check',
     'page callback' => 'acquia_agent_refresh_status',
@@ -49,7 +63,7 @@
  */
 function acquia_agent_init() {
   if (empty($_POST) && user_access('administer site configuration') && (!acquia_agent_has_credentials())) {
-    if (arg(0) == 'admin') {
+    if (arg(0) == 'admin' && arg(3) != 'free-trial') {
       drupal_set_message(t('Subscribe to the <a href="@acquia-network">Acquia Network</a> to access Network Services and Technical Support for your Drupal site.<br /> 
 To learn how Acquia can help you manage your Drupal site, visit the <a href="@acquia-ps">Acquia Products &amp; Services page</a>.<br />
 Once subscribed, please enter your subscription key and identifier on the <a href="@settings">settings page</a>.<br />
@@ -61,6 +75,10 @@
   }
 }
 
+function acquia_agent_freetrial_access() {
+  return user_access('administer site configuration') && !acquia_agent_has_credentials();
+}
+
 /**
  * Get subscription status from the Acquia Network, and store the result.
  *
@@ -361,23 +379,7 @@
   $path = drupal_get_path('module', 'acquia_agent');
   require_once $path .'/acquia_agent_streams.inc';
 
-  if (empty($acquia_network_address)) {
-    $acquia_network_address = acquia_agent_settings('acquia_network_address');
-  }
-  // Strip protocol (scheme) from Network address
-  $uri = parse_url($acquia_network_address);
-  $port = isset($uri['port']) ? ':'. $uri['port'] : '';
-  $path = isset($uri['path']) ? $uri['path'] : '';
-  $acquia_network_address = $uri['host'] . $port . $path;
-  // Add a scheme based on PHP's capacity.
-  if (in_array('ssl', stream_get_transports(), TRUE) && !defined('ACQUIA_DEVELOPMENT_NOSSL')) {
-    // OpenSSL is available in PHP
-    $acquia_network_address = 'https://'. $acquia_network_address;
-  }
-  else {
-    $acquia_network_address = 'http://'. $acquia_network_address;
-  }
-  $acquia_network_address .= '/xmlrpc.php';
+  $acquia_network_address = acquia_agent_network_address($acquia_network_address);
   $host = isset($_SERVER["SERVER_ADDR"]) ? $_SERVER["SERVER_ADDR"] : '';
   $data = array(
     'authenticator' => _acquia_agent_authenticator($params, $identifier, $key),
@@ -424,6 +426,30 @@
 }
 
 /**
+ * Helper function to build the xmlrpc target address.
+ */
+function acquia_agent_network_address($acquia_network_address = NULL) {
+  if (empty($acquia_network_address)) {
+    $acquia_network_address = acquia_agent_settings('acquia_network_address');
+  }
+  // Strip protocol (scheme) from Network address
+  $uri = parse_url($acquia_network_address);
+  $port = isset($uri['port']) ? ':'. $uri['port'] : '';
+  $path = isset($uri['path']) ? $uri['path'] : '';
+  $acquia_network_address = $uri['host'] . $port . $path;
+  // Add a scheme based on PHP's capacity.
+  if (in_array('ssl', stream_get_transports(), TRUE) && !defined('ACQUIA_DEVELOPMENT_NOSSL')) {
+    // OpenSSL is available in PHP
+    $acquia_network_address = 'https://'. $acquia_network_address;
+  }
+  else {
+    $acquia_network_address = 'http://'. $acquia_network_address;
+  }
+  $acquia_network_address .= '/xmlrpc.php';
+  return $acquia_network_address;
+}
+
+/**
  * Helper function to check if an identifer and key exist.
  */
 function acquia_agent_has_credentials() {
Index: acquia_agent/acquia_agent.pages.inc
===================================================================
--- acquia_agent/acquia_agent.pages.inc	(revision 5121)
+++ acquia_agent/acquia_agent.pages.inc	(working copy)
@@ -7,6 +7,185 @@
  */
 
 /**
+ * Menu callback for free trial signup page.
+ */
+function acquia_agent_freetrial_page() {
+  return drupal_get_form('acquia_agent_freetrial_form');
+}
+
+/**
+ * Form builder for free trial signup form.
+ */
+function acquia_agent_freetrial_form() {
+
+  $form = array (
+    'customer' => array (
+      '#type' => 'fieldset',
+      '#title' => t('Customer information'),
+      '#description' => t('Enter a valid email address that does not belong to any existing acquia.com user.'),
+      'primary_email' => array (
+        '#type' => 'textfield',
+        '#title' => t('E-mail address'),
+        '#size' => 32,
+        '#maxlength' => 64,
+        '#required' => TRUE,
+      ),
+      'pass' => array(
+        '#type' => 'password_confirm',
+        '#title' => t('Acquia.com password'),
+        '#size' => 25,
+        '#description' => t('If the e-mail matches an existing acquia.com user, you must enter the matching password here'),
+      ),
+
+    ),
+   'billing' => array (
+     '#type' => 'fieldset',
+     '#title' => t('Free trial registration'),
+     '#description' => t('To register for a free trial of the Acquia Network, please provide the following information.'),
+       'billing_first_name' => array (
+         '#type' => 'textfield',
+         '#title' => t('First name'),
+         '#size' => 32,
+         '#maxlength' => 32,
+         '#required' => TRUE,
+       ),
+       'billing_last_name' => array (
+         '#type' => 'textfield',
+         '#title' => t('Last name'),
+         '#size' => 32,
+         '#maxlength' => 32,
+         '#required' => TRUE,
+       ),
+       'billing_phone' => array (
+         '#type' => 'textfield',
+         '#title' => t('Phone'),
+         '#description' => t('A work or office number we can use to contact you.'),
+         '#size' => 32,
+         '#maxlength' => 64,
+         '#required' => TRUE,
+        ),
+       'billing_company' => array (
+         '#type' => 'textfield',
+         '#title' => t('Company'),
+         '#description' => t('What company or organization is this free trial for?'),
+         '#size' => 32,
+         '#maxlength' => 64,
+         '#required' => TRUE,
+        ),
+      )
+  );
+
+  $form['billing']['billing_country_acquia'] = array(
+    '#type' => 'select',
+    '#title' => t('Country'),
+    '#options' => _acquia_agent_countries(),
+    '#required' => TRUE,
+  );
+  $form['bottom'] = array(
+    '#type' => 'fieldset',
+    '#title' => 'Subscription information',
+  );
+  $form['bottom']['subscription_name'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Subscription name (site name or nickname)'),
+    '#size' => 32,
+    '#maxlength' => 64,
+    '#required' => TRUE,
+  );
+  $form['tos'] = array(
+    '#value' => '<p>' . t('<a href="@url">Acquia terms of service</a>', array('@url' => 'http://acquia.com/acquia/agreement')) . '</p>',
+  );
+  $form['accept_terms_checkbox'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('I have read and accepted the Acquia terms of service.'),
+    '#required' => FALSE, // validated by our #validate hook
+  );
+  $form['#validate'][] = 'acquia_agent_accept_terms_form_validate';
+  $form['submit'] = array (
+    '#type' => 'submit',
+    '#value' => t('Get subscription'),
+  );
+
+  return $form;
+}
+
+/**
+ * Validation function for free trial signup form.
+ */
+function acquia_agent_accept_terms_form_validate($form, $form_state) {
+  if (empty($form_state['values']['accept_terms_checkbox'])) {
+    form_set_error('bottom][accept_terms_checkbox', t('Please indicate that you have read and accepted the Acquia terms of service.'));
+  }
+}
+
+function _acquia_agent_subscription_clean_values($data) {
+  $values['subscription_name'] = 1;
+  $values['primary_email'] = 1;
+  $values['billing_first_name'] = 1;
+  $values['billing_last_name'] = 1;
+  $values['billing_company'] = 1;
+  $values['billing_phone'] = 1;
+  $values['billing_country_acquia'] = 1;
+  $values['accept_terms_checkbox'] = 0;
+  // Remove any unexpected keys.
+  $values = array_intersect_key($data, $values);
+  // Clear any whitespace.
+  foreach ($values as $key => $string) {
+    $values[$key] = trim($string);
+  }
+  return $values;
+}
+
+/**
+ * Submit function for free trial signup form.
+ */
+function acquia_agent_freetrial_form_submit($form, &$form_state) {
+  $path = drupal_get_path('module', 'acquia_agent');
+  require_once $path .'/acquia_agent_streams.inc';
+
+  $values['body'] = _acquia_agent_subscription_clean_values($form_state['values']);
+  $time = time();
+  $values['authenticator']['time'] = $time;
+  $nonce = md5(acquia_agent_random_bytes(55));
+  $values['authenticator']['nonce'] = $nonce;
+  if (!empty($form_state['values']['pass'])) {
+    // We need an hmac to authenticate to an acquia.com account.
+    // This will fail if acquia.com moves to D7.
+    $md5_pass = md5($form_state['values']['pass']);
+    $values['authenticator']['hash'] = _acquia_agent_hmac($md5_pass, $values['authenticator']['time'], $values['authenticator']['nonce'], $values['body']);
+  }
+  else {
+    $values['authenticator']['hash'] = '';
+  }
+  $data = xmlrpc(acquia_agent_network_address(), 'acquia.agent.provision.freetrial', $values);
+  if ($errno = xmlrpc_errno()) {
+    drupal_set_message(t('Error getting free trial: @message (@errno)', array('@message' => xmlrpc_error_msg(), '@errno' => xmlrpc_errno())), 'error');
+    watchdog('acquia agent', '@message (@errno): %server - %method - <pre>@data</pre>', array('@message' => xmlrpc_error_msg(), '@errno' => xmlrpc_errno(), '%server' => $url, '%method' => $method, '@data' => print_r($values, TRUE)), WATCHDOG_ERROR);
+  }
+  elseif (!empty($data['body']['nid'])) {
+    if (!empty($data['body']['hashkey']) && !empty($md5_pass)) {
+      // We have a key XOR'd with a hash.
+      $hash = str_pad(_acquia_agent_hmac($md5_pass, $time, $nonce, $data['body']['identifier']), 64, chr(0x00));
+      // Repeat the XOR and remove trailing NUL bytes.
+      $key = rtrim(base64_decode($data['body']['hashkey']) ^ $hash);
+      // Strip off the padding marker character.
+      $key = substr($key, 0, -1);
+    }
+    else {
+      $key = $data['body']['key'];
+    }
+    variable_set('acquia_key', $key);
+    variable_set('acquia_identifier', $data['body']['identifier']);
+    drupal_set_message(t('The Acquia configuration options have been saved.'));
+    // Check subscription and send a heartbeat to Acquia Network via XML-RPC.
+    // Our status gets updated locally via the return data.
+    acquia_agent_check_subscription();
+    cache_clear_all();
+    $form_state['redirect'] = 'admin/settings/acquia-agent';
+  }
+}
+
+/**
  * Menu callback for settings page.
  */
 function acquia_agent_settings_page() {
@@ -281,3 +460,236 @@
   $output .= drupal_render($form);
   return $output;
 }
+
+/**
+ * List of countries in the Acquia Network database.
+ */
+function _acquia_agent_countries() {
+  return array (
+    'United States' => 'United States',
+    'Afghanistan' => 'Afghanistan',
+    'Albania' => 'Albania',
+    'Algeria' => 'Algeria',
+    'American Samoa' => 'American Samoa',
+    'Andorra' => 'Andorra',
+    'Angola' => 'Angola',
+    'Anguilla' => 'Anguilla',
+    'Antigua and Barbuda' => 'Antigua and Barbuda',
+    'Argentina' => 'Argentina',
+    'Armenia' => 'Armenia',
+    'Ascension Island' => 'Ascension Island',
+    'Australia' => 'Australia',
+    'Austria' => 'Austria',
+    'Azerbaijan' => 'Azerbaijan',
+    'Bahamas' => 'Bahamas',
+    'Bahrain' => 'Bahrain',
+    'Bangladesh' => 'Bangladesh',
+    'Barbados' => 'Barbados',
+    'Belarus' => 'Belarus',
+    'Belgium' => 'Belgium',
+    'Belize' => 'Belize',
+    'Benin' => 'Benin',
+    'Bermuda' => 'Bermuda',
+    'Bhutan' => 'Bhutan',
+    'Bolivia' => 'Bolivia',
+    'Bosnia and Herzegovina' => 'Bosnia and Herzegovina',
+    'Botswana' => 'Botswana',
+    'Brazil' => 'Brazil',
+    'British Indian Ocean Territory' => 'British Indian Ocean Territory',
+    'Brunei Darussalam' => 'Brunei Darussalam',
+    'Bulgaria' => 'Bulgaria',
+    'Burkina Faso' => 'Burkina Faso',
+    'Burundi' => 'Burundi',
+    'Cambodia' => 'Cambodia',
+    'Cameroon' => 'Cameroon',
+    'Canada' => 'Canada',
+    'Cape Verde' => 'Cape Verde',
+    'Cayman Islands' => 'Cayman Islands',
+    'Central African Republic' => 'Central African Republic',
+    'Chad' => 'Chad',
+    'Chile' => 'Chile',
+    'China' => 'China',
+    'Colombia' => 'Colombia',
+    'Comoros' => 'Comoros',
+    'Congo' => 'Congo',
+    'Cook Islands' => 'Cook Islands',
+    'Costa Rica' => 'Costa Rica',
+    'Cote D Ivoire' => 'Cote D Ivoire',
+    'Croatia' => 'Croatia',
+    'Cuba' => 'Cuba',
+    'Cyprus' => 'Cyprus',
+    'Czech Republic' => 'Czech Republic',
+    'Denmark' => 'Denmark',
+    'Djibouti' => 'Djibouti',
+    'Dominica' => 'Dominica',
+    'Dominican Republic' => 'Dominican Republic',
+    'Ecuador' => 'Ecuador',
+    'Egypt' => 'Egypt',
+    'El Salvador' => 'El Salvador',
+    'Equatorial Guinea' => 'Equatorial Guinea',
+    'Eritrea' => 'Eritrea',
+    'Estonia' => 'Estonia',
+    'Ethiopia' => 'Ethiopia',
+    'Falkland Islands' => 'Falkland Islands',
+    'Faroe Islands' => 'Faroe Islands',
+    'Federated States of Micronesia' => 'Federated States of Micronesia',
+    'Fiji' => 'Fiji',
+    'Finland' => 'Finland',
+    'France' => 'France',
+    'French Guiana' => 'French Guiana',
+    'French Polynesia' => 'French Polynesia',
+    'Gabon' => 'Gabon',
+    'Georgia' => 'Georgia',
+    'Germany' => 'Germany',
+    'Ghana' => 'Ghana',
+    'Greece' => 'Greece',
+    'Greenland' => 'Greenland',
+    'Grenada' => 'Grenada',
+    'Guadeloupe' => 'Guadeloupe',
+    'Guatemala' => 'Guatemala',
+    'Guam' => 'Guam',
+    'Guinea' => 'Guinea',
+    'Guinea Bissau' => 'Guinea Bissau',
+    'Guyana' => 'Guyana',
+    'Haiti' => 'Haiti',
+    'Holy See (Vatican City)' => 'Holy See (Vatican City)',
+    'Honduras' => 'Honduras',
+    'Hong Kong' => 'Hong Kong',
+    'Hungary' => 'Hungary',
+    'Iceland' => 'Iceland',
+    'India' => 'India',
+    'Indonesia' => 'Indonesia',
+    'Iran' => 'Iran',
+    'Ireland' => 'Ireland',
+    'Isle of Man' => 'Isle of Man',
+    'Israel' => 'Israel',
+    'Italy' => 'Italy',
+    'Jamaica' => 'Jamaica',
+    'Japan' => 'Japan',
+    'Jordan' => 'Jordan',
+    'Kazakhstan' => 'Kazakhstan',
+    'Kenya' => 'Kenya',
+    'Kiribati' => 'Kiribati',
+    'Korea' => 'Korea',
+    'Kuwait' => 'Kuwait',
+    'Kyrgyzstan' => 'Kyrgyzstan',
+    'Laos' => 'Laos',
+    'Latvia' => 'Latvia',
+    'Lebanon' => 'Lebanon',
+    'Lesotho' => 'Lesotho',
+    'Liberia' => 'Liberia',
+    'Libya' => 'Libya',
+    'Liechtenstein' => 'Liechtenstein',
+    'Lithuania' => 'Lithuania',
+    'Luxembourg' => 'Luxembourg',
+    'Macau' => 'Macau',
+    'Macedonia' => 'Macedonia',
+    'Madagascar' => 'Madagascar',
+    'Malawi' => 'Malawi',
+    'Malaysia' => 'Malaysia',
+    'Maldives' => 'Maldives',
+    'Mali' => 'Mali',
+    'Malta' => 'Malta',
+    'Marshall Islands' => 'Marshall Islands',
+    'Martinique' => 'Martinique',
+    'Mauritius' => 'Mauritius',
+    'Mayotte' => 'Mayotte',
+    'Mexico' => 'Mexico',
+    'Moldova' => 'Moldova',
+    'Monaco' => 'Monaco',
+    'Mongolia' => 'Mongolia',
+    'Montenegro' => 'Montenegro',
+    'Montserrat' => 'Montserrat',
+    'Morocco' => 'Morocco',
+    'Mozambique' => 'Mozambique',
+    'Myanmar' => 'Myanmar',
+    'Namibia' => 'Namibia',
+    'Nauru' => 'Nauru',
+    'Nepal' => 'Nepal',
+    'Netherlands' => 'Netherlands',
+    'Netherlands Antilles' => 'Netherlands Antilles',
+    'New Caledonia' => 'New Caledonia',
+    'New Zealand' => 'New Zealand',
+    'Nicaragua' => 'Nicaragua',
+    'Niger' => 'Niger',
+    'Nigeria' => 'Nigeria',
+    'Niue' => 'Niue',
+    'Norfolk Island' => 'Norfolk Island',
+    'Northern Mariana Islands' => 'Northern Mariana Islands',
+    'Norway' => 'Norway',
+    'Oman' => 'Oman',
+    'Pakistan' => 'Pakistan',
+    'Palau' => 'Palau',
+    'Palestine' => 'Palestine',
+    'Panama' => 'Panama',
+    'Papua New Guinea' => 'Papua New Guinea',
+    'Paraguay' => 'Paraguay',
+    'Peru' => 'Peru',
+    'Philippines' => 'Philippines',
+    'Pitcairn' => 'Pitcairn',
+    'Poland' => 'Poland',
+    'Portugal' => 'Portugal',
+    'Puerto Rico' => 'Puerto Rico',
+    'Qatar' => 'Qatar',
+    'Reunion' => 'Reunion',
+    'Romania' => 'Romania',
+    'Russian Federation' => 'Russian Federation',
+    'Rwanda' => 'Rwanda',
+    'Saint Vincent and the Grenadines' => 'Saint Vincent and the Grenadines',
+    'San Marino' => 'San Marino',
+    'Sao Tome and Principe' => 'Sao Tome and Principe',
+    'Saudi Arabia' => 'Saudi Arabia',
+    'Senegal' => 'Senegal',
+    'Serbia' => 'Serbia',
+    'Seychelles' => 'Seychelles',
+    'Sierra Leone' => 'Sierra Leone',
+    'Singapore' => 'Singapore',
+    'Slovakia' => 'Slovakia',
+    'Slovenia' => 'Slovenia',
+    'Solomon Islands' => 'Solomon Islands',
+    'Somalia' => 'Somalia',
+    'South Africa' => 'South Africa',
+    'South Georgia' => 'South Georgia',
+    'Spain' => 'Spain',
+    'Sri Lanka' => 'Sri Lanka',
+    'St. Kitts and Nevis' => 'St. Kitts and Nevis',
+    'St. Lucia' => 'St. Lucia',
+    'St. Pierre and Miquelon' => 'St. Pierre and Miquelon',
+    'Sudan' => 'Sudan',
+    'Suriname' => 'Suriname',
+    'Swaziland' => 'Swaziland',
+    'Sweden' => 'Sweden',
+    'Switzerland' => 'Switzerland',
+    'Syrian Arab Republic' => 'Syrian Arab Republic',
+    'Taiwan' => 'Taiwan',
+    'Tajikistan' => 'Tajikistan',
+    'Tanzania' => 'Tanzania',
+    'Thailand' => 'Thailand',
+    'The Gambia' => 'The Gambia',
+    'Togo' => 'Togo',
+    'Tokelau' => 'Tokelau',
+    'Tonga' => 'Tonga',
+    'Trinidad and Tobago' => 'Trinidad and Tobago',
+    'Tunisia' => 'Tunisia',
+    'Turkey' => 'Turkey',
+    'Turkmenistan' => 'Turkmenistan',
+    'Turks and Caicos Islands' => 'Turks and Caicos Islands',
+    'Tuvalu' => 'Tuvalu',
+    'Uganda' => 'Uganda',
+    'Ukraine' => 'Ukraine',
+    'United Arab Emirates' => 'United Arab Emirates',
+    'United Kingdom' => 'United Kingdom',
+    'Uruguay' => 'Uruguay',
+    'Uzbekistan' => 'Uzbekistan',
+    'Vanuatu' => 'Vanuatu',
+    'Venezuela' => 'Venezuela',
+    'Viet Nam' => 'Viet Nam',
+    'Virgin Islands' => 'Virgin Islands',
+    'Western Samoa' => 'Western Samoa',
+    'Yemen' => 'Yemen',
+    'Yugoslavia' => 'Yugoslavia',
+    'Zaire' => 'Zaire',
+    'Zambia' => 'Zambia',
+    'Zimbabwe' => 'Zimbabwe',
+  );
+};
