diff --git API.txt API.txt
new file mode 0
index 0000000..bd74512 0
--- /dev/null
+++ API.txt
@@ -0,0 +1,28 @@
+
+
+Overview
+========
+
+Import users into Drupal from a csv file (comma separated file).
+
+See files in user_import/supported/ for examples of the API in use.
+
+hook_user_import_form_field_match()
+===========================================
+Add options to list of Drupal fields to match against column of data being imported.
+
+
+hook_user_import_form_update_user()
+===========================================
+Add options to the import settings form.
+
+
+hook_user_import_data()
+===========================================
+Manipulate imported data before creating or amending user accounts.
+
+
+hook_user_import_after_save()
+===========================================
+Take action(s) after each user account is created or amended.
+ 
diff --git user_import.drush.inc user_import.drush.inc
new file mode 0
index 0000000..745234e 0
--- /dev/null
+++ user_import.drush.inc
@@ -0,0 +1,82 @@
+<?php
+
+function user_import_drush_help($command) {
+  switch ($command) {
+    case 'drush:user-import':
+      return dt('Queue an import of users from a CSV file');
+    case 'error:template-not-found':
+      return dt('Template not found');
+    case 'error:file-not-found':
+      return dt('File not found');
+    case 'error:no-default-template':
+      return dt('There is no default template configured');
+  }
+}
+
+function user_import_drush_command() {
+  $commands = array();
+  $commands['user-import'] = array(
+    'description' => dt('Queue an import of users'),
+    'arguments' => array(
+      'file' => dt('The CSV file with user data'),
+      'template' => dt('Name of the template to use (optional, if not provided the default template is used)'),
+    ),
+    'examples' => array(
+      dt('standard example') => 'drush user-import users.csv',
+    ),
+  );
+  return $commands;
+}
+
+function drush_user_import_user_import($original_file = NULL, $template_name = NULL) {
+  if (!file_exists($original_file)) {
+    return drush_set_error('file-not-found');
+  }
+  $original_file = realpath($original_file);
+  if ($template_name) {
+    $template = db_fetch_object(db_query("SELECT * FROM {user_import} WHERE name = '%s' AND setting = 'template'", $template_name));
+    if (!$template) {
+      return drush_set_error('template-not-found');
+    }
+  }
+  else {
+    $template_id = variable_get('user_import_settings', '0');
+    if (!$template_id) {
+      return drush_set_error('no-default-template');
+    }
+    $template = db_fetch_object(db_query("SELECT * FROM {user_import} WHERE import_id = %d AND setting = 'template'", $template_id));
+    if (!$template) {
+      return drush_set_error('template-not-found');
+    }
+    drush_print(dt('Using default settings template "!template"', array('!template' => $template->name)));
+  }
+  $template->options = unserialize($template->options);
+  $template->field_match = unserialize($template->field_match);
+  $template->roles = unserialize($template->roles);
+  $file = new stdClass();
+  $file->filepath = $original_file;
+  $file->filename = basename($original_file);
+  file_copy($file, file_directory_temp(), FILE_EXISTS_RENAME);
+  $import = new stdClass();
+  // initialize import from template
+  $import->first_line_skip = $template->first_line_skip;
+  $import->contact = $template->contact;
+  $import->username_space = $template->username_space;
+  $import->send_email = $template->send_email;
+  $import->field_match = $template->field_match;
+  $import->roles = $template->roles;
+  $import->options = $template->options;
+  $import->options['ftp'] = 0;
+  $import->oldfilename = basename($original_file);
+  $import->filename = $file->filename;
+  $import->filepath = $file->filepath;
+  $import->setting = 'tested';
+  $import->started = time();
+  $result = drupal_write_record('user_import', $import);
+  if ($result == SAVED_NEW) {
+    drush_print(dt('Succesfully queued user import. The original CSV file !file has been copied to the Drupal installation and can be removed.', array('!file' => $original_file)));
+  }
+  else {
+    drush_set_error('import_failed', dt('Unable to queue the user import.'));
+  }
+}
diff --git user_import.module user_import.module
index a185e06..407d8c5 100644
--- user_import.module
+++ user_import.module
@@ -119,16 +119,26 @@
  * Implementation of hook_cron().
  */
 function user_import_cron() {
-    
-    $imports = _user_import_settings_select();    
-    if (!$imports) return;
-    
-    foreach ($imports as $import) {
-        
-        if ($import['setting'] == 'test' || $import['setting'] == 'import') _user_import_process($import);
+  $imports = _user_import_settings_select();    
+  if (!$imports) return;
+
+  // For each cron run, allow 2 seconds for each row
+  $timelimit = variable_get('user_import_max', 250) * 2;
+
+  foreach ($imports as $import) {
+    if ($import['setting'] == 'test' || $import['setting'] == 'import') {
+      watchdog('user_import', 'Cron for file @filename started', array('@filename' => $import['filename']), WATCHDOG_INFO);
+
+      // Allow each import to run for 2 seconds per user
+      _user_import_set_time_limit($timelimit);
+      _user_import_process($import);
+
+      watchdog('user_import', 'Cron for file @filename ended', array('@filename' => $import['filename']), WATCHDOG_INFO);
     }
-        
-    return;
+  }
+
+  // Set the default cron time limit
+  _user_import_set_time_limit(240);
 }
 
 // - - - - - - - -  FORMS - - - - - - - - 
@@ -1637,6 +1647,18 @@
   
   return $settings_updated;
 }
+
+/**
+ * Sets the time limit if this is allowed by the server
+ * configuration.
+ * 
+ * @param int $timelimit New time limit in seconds
+ */
+function _user_import_set_time_limit($timelimit) {
+  if (function_exists('set_time_limit') && !ini_get('safe_mode')) {
+    set_time_limit($timelimit);
+  }
+}
 
 /**
  * Loads the hooks for the supported modules.
