diff --git a/biblio.module b/biblio.module
index c8e617d..c1bdcc9 100644
--- a/biblio.module
+++ b/biblio.module
@@ -1340,6 +1340,14 @@ function biblio_form_alter(&$form, $form_state, $form_id) {
       if ($freetagging && $kw_vocab && isset($form['taxonomy']['tags'][$kw_vocab])) {
         unset($form['taxonomy']['tags'][$kw_vocab]);
       }
+
+      // For existing nodes, the DOI should not be editable.
+      if (!empty($form['biblio_doi']['#default_value'])) {
+        $form['biblio_doi_disable'] = $form['biblio_doi'];
+        $form['biblio_doi_disable']['#type'] = 'item';
+        $form['biblio_doi_disable']['#value'] = $form['biblio_doi_disable']['#default_value'];
+        $form['biblio_doi']['#access'] = FALSE;
+      }
     }
   }
   return $form;
@@ -1756,6 +1764,14 @@ function biblio_form_validate($form, &$form_state) {
         ($form['#post']['biblio_type'] > 0 ? t('Save') : '');
   switch ($op) {
     case t('Save'):
+      // Look for DOI conflicts on save.
+      if (!empty($form_state['values']['biblio_doi'])) {
+        $conflict = (int) db_result(db_query("SELECT nid FROM {biblio} INNER JOIN {node} USING (nid, vid) WHERE biblio_doi = '%s' AND nid != %d", $doi, (int) $form['#node']->nid));
+        if ($conflict) {
+          form_set_error('biblio_doi', l(t("Another node already has this DOI."), "node/$conflict"));
+        }
+      }
+
       if ($form_state['storage']['biblio_type'] == $form_state['values']['biblio_type'] ||
          (!empty($form['#node']->biblio_type) && $form['#node']->biblio_type == $form_state['values']['biblio_type'])) {
         unset ($form_state['storage']);
diff --git a/modules/CiteProc/biblio_citeproc.admin.inc b/modules/CiteProc/biblio_citeproc.admin.inc
index c9f6e50..6d4561d 100644
--- a/modules/CiteProc/biblio_citeproc.admin.inc
+++ b/modules/CiteProc/biblio_citeproc.admin.inc
@@ -245,7 +245,7 @@ function _install_csl($name, $csl, $sha = NULL, $all = FALSE) {
       }
     }
     if (!$all && !empty($parent)) {
-      $csl_file_contents = db_result(db_query("SELECT csl FROM biblio_citeproc_styles WHERE id = '%s'", $parent));
+      $csl_file_contents = db_result(db_query("SELECT csl FROM {biblio_citeproc_styles} WHERE id = '%s'", $parent));
       if (!$csl_file_contents) {
         drupal_set_message(t('You do not have the parent style file: !parent_id installed. You must install !parent_id before you can use !csl_id', array('!parent_id' => basename($parent), '!csl_id' => $name)), 'error');
       }
@@ -378,7 +378,7 @@ function biblio_citeproc_csl_editor($form_state, $style) {
     return;
   }
   if (!empty($csl->parent)) {
-    $csl = db_fetch_object(db_query("SELECT id,csl FROM biblio_citeproc_styles WHERE id = '%s'", array(':id' => $csl->parent)));
+    $csl = db_fetch_object(db_query("SELECT id,csl FROM {biblio_citeproc_styles} WHERE id = '%s'", array(':id' => $csl->parent)));
 
   }
   if (isset($csl->csl)) {
diff --git a/modules/CiteProc/biblio_citeproc.module b/modules/CiteProc/biblio_citeproc.module
index ce97b50..23a23a5 100644
--- a/modules/CiteProc/biblio_citeproc.module
+++ b/modules/CiteProc/biblio_citeproc.module
@@ -85,13 +85,13 @@ function theme_biblio_citeproc_style($node, $base = 'biblio', $style_name = NULL
       }
     }
     if (!empty($csl_id)) {
-      $csl = db_fetch_object(db_query("SELECT parent,csl FROM biblio_citeproc_styles WHERE filename = '%s'", $csl_id));
+      $csl = db_fetch_object(db_query("SELECT parent,csl FROM {biblio_citeproc_styles} WHERE filename = '%s'", $csl_id));
       if (!isset($csl->csl)) {
         drupal_set_message(t('Biblio-CiteProc could not fetch the style file: @csl_id from the database.', array('@csl_id' => $csl_id)), 'error');
         return;
       }
       if (!empty($csl->parent)) {
-        $csl_file_contents = db_result(db_query("SELECT csl FROM biblio_citeproc_styles WHERE id = '%s'", $csl->parent));
+        $csl_file_contents = db_result(db_query("SELECT csl FROM {biblio_citeproc_styles} WHERE id = '%s'", $csl->parent));
         if (!$csl_file_contents) {
           drupal_set_message(t('Biblio-CiteProc could not fetch the parent style file: @csl_id from the database.', array('@csl_id' => $csl->parent)), 'error');
           return;
diff --git a/modules/crossref/biblio.crossref.client.php b/modules/crossref/biblio.crossref.client.php
index 5e40fb7..f60b9ea 100644
--- a/modules/crossref/biblio.crossref.client.php
+++ b/modules/crossref/biblio.crossref.client.php
@@ -78,6 +78,50 @@ class BiblioCrossRefClient
     return $this->node;
   }
 
+  function fetchByAuthorTitle($author, $title) {
+    $qdata = simplexml_load_string('<query_batch version="2.0" xsi:schemaLocation="http://www.crossref.org/qschema/2.0 http://www.crossref.org/qschema/crossref_query_input2.0.xsd" xmlns="http://www.crossref.org/qschema/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />');
+    $qdata->head->email_address = $this->pid;
+    $qdata->head->doi_batch_id = uniqid('NA');
+    $qdata->body->query = '';
+    $query = $qdata->body->query;
+    $query['enable-multiple-hits'] = 'exact';
+    $query['key'] = 'key';
+    $query->article_title = $title;
+    $query->article_title['match'] = 'fuzzy';
+    $query->author = $author;
+    $query->author['search-all-authors'] = 'true';
+    $this->query = url($this->url, array(
+      'external' => TRUE,
+      'query' => array(
+        'pid' => $this->pid,
+        'noredirect' => 'true',
+        'format' => 'unixref',
+        'qdata' => (string) $qdata->asXml()
+      )
+    ));
+    $response = drupal_http_request($this->query);
+    if ($response->code != 200) {
+      drupal_set_message(t('Could not open crossref.org for XML author/title query.'),'error');
+      return array();
+    }
+    $xml = simplexml_load_string($response->data);
+    $results = array();
+    foreach ($xml->doi_record as $record) {
+      $result = array(
+        'full' => $record->asXml(),
+        'doi' => $record->xpath('.//doi[1]'),
+        'title' => $record->xpath('.//title[1]'),
+      );
+      $result['full'] = preg_replace('@<[^>]+>@s', '.', $result['full']);
+      $result['full'] = preg_replace('@\.[\s\.]+@', '. ', $result['full']);
+      $result['full'] = trim($result['full'], '. ');
+      $result['doi'] = (string) $result['doi'][0];
+      $result['title'] = (string) $result['title'][0];
+      $results[] = $result;
+    }
+    return $results;
+  }
+
 
   function unixref_startElement($parser, $name, $attrs) {
     switch ($name) {
@@ -172,6 +216,9 @@ class BiblioCrossRefClient
         break;
       case 'journal_issue':
         $this->node['biblio_date'] = (!empty($this->node['month']) ? $this->node['month'] . '/':'') . $this->node['year'];
+        if (empty($this->node['biblio_year'])) {
+          $this->node['biblio_year'] = $this->node['year'];
+        }
         break;
       case 'journal_article':
       case 'conference_paper':
@@ -180,7 +227,9 @@ class BiblioCrossRefClient
       case 'standard_metadata':
       case 'database_date':
       case 'component':
+        if (!empty($this->node['year'])) {
         $this->node['biblio_year'] = $this->node['year'];
+        }
 //        $this->node['biblio_doi']  = $this->node['doi'];
         break;
       case 'issn':
diff --git a/modules/crossref/biblio_crossref.module b/modules/crossref/biblio_crossref.module
index c999616..64f4bb8 100644
--- a/modules/crossref/biblio_crossref.module
+++ b/modules/crossref/biblio_crossref.module
@@ -6,7 +6,7 @@
 
 function biblio_crossref_form_biblio_node_form_alter(&$form, &$form_state) {
   global $user;
-  if (phpversion() > 5 && !$form_state['submitted'] &&
+  if (phpversion() > 5 && (!$form_state['submitted'] || isset($form_state['values']['doi_search_results'])) &&
     !isset($form['#node']->nid))
     {
     $form['biblio_doi_lookup'] = array(
@@ -29,6 +29,27 @@ function biblio_crossref_form_biblio_node_form_alter(&$form, &$form_state) {
       );
     }
 
+    if (isset($form_state['values']['doi_search_results']) && is_array($form_state['values']['doi_search_results'])) {
+      $form['biblio_doi_lookup']['#collapsed'] = FALSE;
+      if (empty($form_state['values']['doi_search_results'])) {
+        // No results.
+      }
+      else {
+        foreach ($form_state['values']['doi_search_results'] as $result) {
+          $info = l($result['doi'], 'http://dx.doi.org/' . $result['doi'], array(
+            'external' => TRUE,
+            'attributes' => array(
+              'onclick' => "$('#edit-doi-data').val($(this).text()); return false"
+            )
+          ));
+          $info .= ': ' . $result['full'];
+          $form['biblio_doi_lookup'][] = array(
+            '#value' => "<div>" . $info . '</div>',
+            '#weight' => -11
+          );
+        }
+      }
+    }
     $form['biblio_doi_lookup']['doi_data'] = array(
         '#type' => 'textfield',
         '#title' => t('DOI'),
@@ -38,13 +59,45 @@ function biblio_crossref_form_biblio_node_form_alter(&$form, &$form_state) {
         '#disabled' => empty($have_pid),
         '#size' => 60,
         '#maxlength' => 255,
-        '#weight' => -4
+        '#weight' => -10
     );
     $form['biblio_doi_lookup']['doi_submit'] = array(
         '#type' => 'submit',
         '#disabled' => empty($have_pid),
-        '#value' => t('Populate using DOI')
+        '#value' => t('Populate using DOI'),
+        '#weight' => -9
+    );
+
+    $form['biblio_doi_lookup']['doi_search_author'] = array(
+        '#type' => 'textfield',
+        '#title' => t('Author'),
+        '#required' => FALSE,
+        '#default_value' => $form_state['values']['doi_search_author'],
+        '#description' => t('Enter the LAST name of the FIRST author.'),
+        '#disabled' => empty($have_pid),
+        '#size' => 60,
+        '#maxlength' => 255,
+        '#weight' => -7
     );
+    $form['biblio_doi_lookup']['doi_search_title'] = array(
+        '#type' => 'textfield',
+        '#title' => t('Title'),
+        '#required' => FALSE,
+        '#default_value' => $form_state['values']['doi_search_title'],
+        '#description' => t('Enter the exact title.'),
+        '#disabled' => empty($have_pid),
+        '#size' => 60,
+        '#maxlength' => 255,
+        '#weight' => -6
+    );
+    $form['biblio_doi_lookup']['doi_search_submit'] = array(
+        '#type' => 'submit',
+        '#disabled' => empty($have_pid),
+        '#value' => t('Search for DOI by Author and Title'),
+        '#weight' => -5
+    );
+    $form_state['values']['doi_search_results'] = NULL;
+
     $form['#validate'] = array_merge(array('biblio_crossref_form_biblio_node_form_validate'), $form['#validate']); // put my validator first
   }
   $biblio_crossref_id = (isset($form_state['values']['biblio_crossref_id'])) ? $form_state['values']['biblio_crossref_id'] : '';
@@ -97,11 +150,22 @@ function biblio_crossref_form_biblio_node_form_validate($form, &$form_state) {
       return;
     }
   }
-  return;
+
+  if (!empty($form_state['values']['doi_search_author']) && !empty($form_state['values']['doi_search_title'])) {
+    $crossref_pid = variable_get('biblio_crossref_pid', '');
+    module_load_include('php', 'biblio_crossref', 'biblio.crossref.client');
+    $client = new BiblioCrossRefClient('', $crossref_pid);
+    $client->setUrl('http://doi.crossref.org/servlet/query');
+    $results = $client->fetchByAuthorTitle($form_state['values']['doi_search_author'], $form_state['values']['doi_search_title']);
+    $form_state['values']['doi_search_results'] = $results;
+    $form_state['rebuild'] = TRUE;
+    $form_state['submitted'] = FALSE;
+    $form_state['values']['biblio_type'] = NULL;
+  }
 }
 
 function biblio_crossref_check_doi($doi) {
-    return db_result(db_query("SELECT nid FROM {biblio_crossref} WHERE biblio_crossref_id = '%s'", $doi));
+    return db_result(db_query("SELECT nid FROM {biblio} INNER JOIN {node} USING (nid, vid) WHERE biblio_doi = '%s'", $doi));
 }
 
 function biblio_crossref_biblio_lookup_link_settings() {
diff --git a/modules/pubmed/EntrezClient.php b/modules/pubmed/EntrezClient.php
index 85ac80c..2d501a3 100644
--- a/modules/pubmed/EntrezClient.php
+++ b/modules/pubmed/EntrezClient.php
@@ -340,7 +340,7 @@ class BiblioEntrezClient
 
     $xml = drupal_http_request($this->query);
 
-    $result = @simplexml_load_string($$xml->data);
+    $result = @simplexml_load_string($xml->data);
 
     if (!$result) {
       throw new Exception('Query ' . $this->query . ' failed.');
diff --git a/modules/pubmed/biblio_pm.module b/modules/pubmed/biblio_pm.module
index ff40f0f..0150a07 100644
--- a/modules/pubmed/biblio_pm.module
+++ b/modules/pubmed/biblio_pm.module
@@ -88,8 +88,8 @@ function biblio_pm_form_biblio_node_form_alter(&$form, &$form_state) {
     );
     $form['#validate'] = array_merge(array('biblio_pm_form_biblio_node_form_validate'), $form['#validate']); // put my validator first
   }
-  $form['biblio_pubmed_id'] = array('#type' => 'value', '#value' => $form_state['values']['biblio_pubmed_id']);
-  $form['biblio_pubmed_md5'] = array('#type' => 'value', '#value' => $form_state['values']['biblio_pubmed_md5']);
+  $form['biblio_pubmed_id'] = array('#type' => 'value', '#value' => $form['#node']->biblio_pubmed_id);
+  $form['biblio_pubmed_md5'] = array('#type' => 'value', '#value' => $form['#node']->biblio_pubmed_md5);
 }
 
 function biblio_pm_form_biblio_node_form_validate($form, &$form_state) {
@@ -230,7 +230,10 @@ function biblio_pm_check_pmid($pmid) {
     return db_result(db_query("SELECT nid FROM {biblio_pubmed} WHERE biblio_pubmed_id = %d", $pmid));
 }
 function biblio_pm_biblio_lookup_link_settings() {
-  return array('pubmed'  => t('PubMed'));
+  return array(
+    'pubmed' => t('PubMed'),
+    'pmc' => t('PubMed Central'),
+  );
 }
 
 function biblio_pm_biblio_lookup_link($node) {
@@ -239,28 +242,45 @@ function biblio_pm_biblio_lookup_link($node) {
 
 function biblio_pm_link($type, $node = NULL, $teaser = FALSE) {
   $show_link = variable_get('biblio_lookup_links', array('pubmed' => TRUE));
-  if (!isset($show_link['pubmed']) ||
-      !$show_link['pubmed'] ||
-      !isset($node) ||
-      $node->type != 'biblio' ||
-      !isset($node->biblio_pubmed_id)) {
-    return array();
-  }
+  $links = array();
 
-  if (isset($node) && $type == 'node' && $node->type == 'biblio' && !empty($node->biblio_pubmed_id)) {
+  if (isset($node) && $type == 'node' && $node->type == 'biblio') {
+    if (!empty($show_link['pubmed']) && !empty($node->biblio_pubmed_id)) {
     $link  = 'http://www.ncbi.nlm.nih.gov/pubmed/'. $node->biblio_pubmed_id .'?dopt=Abstract';
-    $attrs = array('title' => t("Click to view the PubMed listing for this node"));
+      $attrs = array(
+        'title' => t("Click to view the PubMed listing")
+      );
     if (variable_get('biblio_links_target_new_window', null)){
-      $attrs = array_merge($attrs, array('target'=>'_blank'));
+        $attrs = array_merge($attrs, array(
+          'target' => '_blank'
+        ));
     }
 
-    return array('biblio_pubmed' => array(
+      $links['biblio_pubmed'] = array(
         'title'      => t('PubMed'),
         'href'       => $link,
-        'attributes' => $attrs,
+        'attributes' => $attrs
+      );
+    }
+    if (!empty($show_link['pmc']) && !empty($node->biblio_pmcid)) {
+      $link = 'http://www.ncbi.nlm.nih.gov/pmc/articles/PMC' . $node->biblio_pmcid . '/';
+      $attrs = array(
+        'title' => t("Click to view the PubMed Central full text")
+      );
+      if (variable_get('biblio_links_target_new_window', null)) {
+        $attrs = array_merge($attrs, array(
+          'target' => '_blank'
     ));
   }
-  return ;
+
+      $links['biblio_pmc'] = array(
+        'title' => t('PMC Full Text'),
+        'href' => $link,
+        'attributes' => $attrs
+      );
+    }
+  }
+  return $links;
 }
 
 
@@ -297,9 +317,57 @@ function _biblio_pm_update($node) {
   }
 }
 
-function _biblio_pm_load($node) {
-  if ($node->type == 'biblio') {
-    return  db_fetch_array(db_query('SELECT  biblio_pubmed_id  FROM {biblio_pubmed} WHERE nid = %d', $node->nid));
+function _biblio_pm_load(&$node) {
+  if (!isset($node->nid)) return;
+  $fields = (array) db_fetch_array(db_query('SELECT biblio_pubmed_id, biblio_pubmed_md5, biblio_pmcid FROM {biblio_pubmed} WHERE nid = %d', $node->nid));
+  foreach ($fields as $k => $v) {
+    $node->$k = $v;
+  }
+}
+
+function _biblio_pm_presave(&$node, $a, $b) {
+  // If there is no pubmed id, try to locate it by DOI.
+  if (!isset($node->biblio_pubmed_id) || empty($node->biblio_pubmed_id)) {
+    module_load_include('php', 'biblio_pm', 'EntrezClient');
+    module_load_include('php', 'biblio_pm', 'EntrezPubmedArticle');
+    $Eclient = new BiblioEntrezClient;
+    try {
+      $Eclient->setDatabase('pubmed');
+      $Eclient->setTerm($node->biblio_doi . '[AID]');
+      $result = $Eclient->search();
+      if ($Eclient->count() == 1) {
+        $node->biblio_pubmed_id = (string) $result->IdList->Id;
+        drupal_set_message(t("Located PubMed ID: !id", array('!id' => $node->biblio_pubmed_id)));
+        if (isset($node->nid)) {
+          _biblio_pm_insert($node);
+        }
+      }
+    } catch (Exception $e) {
+      drupal_set_message($e->getMessage(), 'warning');
+    }
+  }
+
+  // If there is no pmc id, try to locate it by DOI.
+  if (!isset($node->biblio_pmcid) || empty($node->biblio_pmcid)) {
+    if (isset($node->biblio_doi) && !empty($node->biblio_doi)) {
+      module_load_include('php', 'biblio_pm', 'EntrezClient');
+      module_load_include('php', 'biblio_pm', 'EntrezPubmedArticle');
+      $Eclient = new BiblioEntrezClient;
+      try {
+        $Eclient->setDatabase('pmc');
+        $Eclient->setTerm($node->biblio_doi . '[DOI]');
+        $result = $Eclient->search();
+        if ($Eclient->count() == 1) {
+          $node->biblio_pmcid = (string) $result->IdList->Id;
+          drupal_set_message(t("Located PMC ID: !id", array('!id' => $node->biblio_pmcid)));
+          if (isset($node->nid) && (!isset($node->biblio_pubmed_id) || empty($node->biblio_pubmed_id))) {
+            _biblio_pm_insert($node);
+          }
+        }
+      } catch (Exception $e) {
+        drupal_set_message($e->getMessage(), 'warning');
+      }
+    }
   }
 }
 
