Index: /tags/ipp-20141024/PS-IPP-PStamp/lib/PS/IPP/PStamp/Job.pm
===================================================================
--- /tags/ipp-20141024/PS-IPP-PStamp/lib/PS/IPP/PStamp/Job.pm	(revision 38191)
+++ /tags/ipp-20141024/PS-IPP-PStamp/lib/PS/IPP/PStamp/Job.pm	(revision 38192)
@@ -1195,5 +1195,5 @@
     my $command;
     my $using_camtool = 0;
-    if ($release_name or $survey) {
+    if ($release_name or ($survey and $survey ne 'bypass')) {
         $command = "$releasetool -dbname $imagedb -listrelexp";
         if ($survey) {
@@ -1534,6 +1534,6 @@
 
     if ($mjd < 54000) {
-        print STDERR "MJD value too small minimum is 54000\n";
-        return undef; 
+        # print STDERR "MJD value too small minimum is 54000\n";
+        $mjd = 54000;
     }
 
Index: /tags/ipp-20141024/dbconfig/pstamp.md
===================================================================
--- /tags/ipp-20141024/dbconfig/pstamp.md	(revision 38191)
+++ /tags/ipp-20141024/dbconfig/pstamp.md	(revision 38192)
@@ -69,11 +69,34 @@
 END
 
-pstampWebRequest METADATA
+pstampWebRequest        METADATA
     num         S64         0    # Primary Key AUTO_INCREMENT
 END
 
-pstampFile METADATA
+pstampFile              METADATA
     file_id     S64         0   # Primary Key AUTO_INCREMENT
     job_id      S64         0
     path        STR         255
-end
+END
+
+pstampUserDomain        METADATA
+    domainName      STR     64
+    accessLevel     S32     0
+    defaultProduct  STR     255
+    defaultLabel    STR     64
+END
+
+pstampUser              METADATA
+    userName        STR     64  # Primary Key (userName, domainName)
+    domainName      STR     64  
+    accessLevel     S32     0
+    defaultProduct  STR     255
+    defaultLabel    STR     64
+END
+
+pstampAccessLevel       METADATA
+    proj_id         S64     0   # Primary Key (proj_id, accessLevel) fkey(proj_id) 
+    accessLevel     S32     0
+    mjd_min         F32     0
+    mjd_max         F32     0
+END
+
Index: /tags/ipp-20141024/dbconfig/release.md
===================================================================
--- /tags/ipp-20141024/dbconfig/release.md	(revision 38191)
+++ /tags/ipp-20141024/dbconfig/release.md	(revision 38192)
@@ -14,4 +14,5 @@
     dvodb       STR     255
     ubercal_file STR    255
+    accessLevelMin S32  0
 END
 
Index: /tags/ipp-20141024/ippTools/share/Makefile.am
===================================================================
--- /tags/ipp-20141024/ippTools/share/Makefile.am	(revision 38191)
+++ /tags/ipp-20141024/ippTools/share/Makefile.am	(revision 38192)
@@ -335,4 +335,5 @@
 	pstamptool_listjob.sql \
 	pstamptool_listfile.sql \
+	pstamptool_listuser.sql \
 	pstamptool_pendingcleanup.sql \
 	pstamptool_pendingdependent.sql \
@@ -346,4 +347,5 @@
 	pstamptool_stopdependentjob.sql \
 	pstamptool_updatejob.sql \
+	pstamptool_addaccesslevel.sql \
 	pxadmin_create_tables.sql \
 	pxadmin_create_mirror_tables.sql \
Index: /tags/ipp-20141024/ippTools/share/pstamptool_addaccesslevel.sql
===================================================================
--- /tags/ipp-20141024/ippTools/share/pstamptool_addaccesslevel.sql	(revision 38192)
+++ /tags/ipp-20141024/ippTools/share/pstamptool_addaccesslevel.sql	(revision 38192)
@@ -0,0 +1,4 @@
+INSERT 
+    INTO pstampAccessLevel 
+        SELECT proj_id, %d, %f, %f FROM pstampProject
+        WHERE name = '%s'
Index: /tags/ipp-20141024/ippTools/share/pstamptool_listuser.sql
===================================================================
--- /tags/ipp-20141024/ippTools/share/pstamptool_listuser.sql	(revision 38192)
+++ /tags/ipp-20141024/ippTools/share/pstamptool_listuser.sql	(revision 38192)
@@ -0,0 +1,10 @@
+SELECT  userName, 
+        domainName, 
+        pstampUser.accessLevel,
+        IFNULL(pstampUserDomain.accessLevel, 0) AS domainAccessLevel,
+        pstampUser.defaultProduct as userProduct,
+        pstampUser.defaultLabel as userLabel,
+        pstampUserDomain.defaultProduct as domainProduct,
+        pstampUserDomain.defaultLabel as domainLabel
+FROM    pstampUser 
+        LEFT JOIN pstampUserDomain USING(domainName)
Index: /tags/ipp-20141024/ippTools/src/pstamptool.c
===================================================================
--- /tags/ipp-20141024/ippTools/src/pstamptool.c	(revision 38191)
+++ /tags/ipp-20141024/ippTools/src/pstamptool.c	(revision 38192)
@@ -60,4 +60,14 @@
 static bool listfileMode(pxConfig *config);
 static bool deletefileMode(pxConfig *config);
+
+static bool adddomainMode(pxConfig * config);
+static bool updatedomainMode(pxConfig * config);
+static bool listdomainMode(pxConfig *config);
+static bool adduserMode(pxConfig * config);
+static bool updateuserMode(pxConfig * config);
+static bool listuserMode(pxConfig * config);
+static bool addaccesslevelMode(pxConfig * config);
+static bool updateaccesslevelMode(pxConfig * config);
+static bool listaccesslevelMode(pxConfig * config);
 
 # define MODECASE(caseName, func) \
@@ -111,4 +121,14 @@
         MODECASE(PSTAMPTOOL_MODE_DELETEFILE, deletefileMode);
 
+        MODECASE(PSTAMPTOOL_MODE_ADDDOMAIN, adddomainMode);
+        MODECASE(PSTAMPTOOL_MODE_UPDATEDOMAIN, updatedomainMode);
+        MODECASE(PSTAMPTOOL_MODE_LISTDOMAIN, listdomainMode);
+        MODECASE(PSTAMPTOOL_MODE_ADDUSER, adduserMode);
+        MODECASE(PSTAMPTOOL_MODE_UPDATEUSER, updateuserMode);
+        MODECASE(PSTAMPTOOL_MODE_LISTUSER, listuserMode);
+        MODECASE(PSTAMPTOOL_MODE_ADDACCESSLEVEL, addaccesslevelMode);
+        MODECASE(PSTAMPTOOL_MODE_UPDATEACCESSLEVEL, updateaccesslevelMode);
+        MODECASE(PSTAMPTOOL_MODE_LISTACCESSLEVEL, listaccesslevelMode);
+
         default:
             psAbort("invalid option (this should not happen)");
@@ -1180,5 +1200,5 @@
 
     // negative simple so the default is true
-    if (!ippdbPrintMetadatas(stdout, output, "pstampDataStore", !simple)) {
+    if (!ippdbPrintMetadatas(stdout, output, "pstampProject", !simple)) {
         psError(PS_ERR_UNKNOWN, false, "failed to print array");
         psFree(output);
@@ -1702,3 +1722,369 @@
     return true;
 }
+
+static bool adddomainMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_STR(domainName,      config->args, "-set_domain",  true, false);
+    PXOPT_LOOKUP_S32(accessLevel,     config->args, "-set_accessLevel", true, false);
+    PXOPT_LOOKUP_STR(defaultProduct,  config->args, "-set_defaultProduct",  false, false);
+    PXOPT_LOOKUP_STR(defaultLabel,    config->args, "-set_defaultLabel",  false, false);
+
+    if (!pstampUserDomainInsert(config->dbh,
+        domainName,
+        accessLevel,
+        defaultProduct,
+        defaultLabel
+        )) {
+        psError(PS_ERR_UNKNOWN, false, "failed to insert domain");
+        return false;
+    }
+
+    return true;
+}
+static bool updatedomainMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_STR(domainName,      config->args, "-domain",  true, false);
+    PXOPT_LOOKUP_S32(accessLevel,     config->args, "-set_accessLevel", false, false);
+    PXOPT_LOOKUP_STR(defaultProduct,  config->args, "-set_defaultProduct",  false, false);
+    PXOPT_LOOKUP_STR(defaultLabel,    config->args, "-set_defaultLabel",  false, false);
+
+    if (!accessLevel && !defaultProduct && !defaultLabel) {
+        psError(PS_ERR_UNKNOWN, true, "at least one set option is required");
+        return false;
+    }
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-domain",   "domainName", "==");
+    if (!psListLength(where->list)) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+        return false;
+    }
+
+    psString query = psStringCopy("UPDATE pstampUserDomain SET");
+
+    char * sep = "";
+    if (accessLevel) {
+        psStringAppend(&query, "%s accessLevel = '%d'", sep, accessLevel);
+        sep = ", ";
+    }
+    if (defaultProduct) {
+        psStringAppend(&query, "%s  defaultProduct = '%s'", sep, defaultProduct);
+        sep = ", ";
+    }
+    if (defaultLabel) {
+        psStringAppend(&query, "%s defaultLabel = '%s'", sep, defaultLabel);
+        sep = ", ";
+    }
+
+    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+    psStringAppend(&query, "\nWHERE %s", whereClause);
+    psFree(whereClause);
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+
+    // psU64 affected = psDBAffectedRows(config->dbh);
+    // psLogMsg("pstamptool", PS_LOG_INFO, "Updated %" PRIu64 " pstampRequests", affected);
+
+    return true;
+}
+
+static bool listdomainMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-domain", "domainName", "=");
+    PXOPT_COPY_S32(config->args, where, "-accessLevel", "accessLevel", "=");
+
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+
+    psString query = psStringCopy("SELECT * from pstampUserDomain");
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " WHERE %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "pstampUserDomain", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+static bool adduserMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_STR(userName,        config->args, "-set_user",  true, false);
+    PXOPT_LOOKUP_STR(domainName,      config->args, "-set_domain",  true, false);
+    PXOPT_LOOKUP_S32(accessLevel,     config->args, "-set_accessLevel", false, false);
+    PXOPT_LOOKUP_STR(defaultProduct,  config->args, "-set_defaultProduct",  false, false);
+    PXOPT_LOOKUP_STR(defaultLabel,    config->args, "-set_defaultLabel",  false, false);
+
+    if (!pstampUserInsert(config->dbh,
+        userName,
+        domainName,
+        accessLevel,
+        defaultProduct,
+        defaultLabel
+        )) {
+        psError(PS_ERR_UNKNOWN, false, "failed to insert user");
+        return false;
+    }
+
+    return true;
+}
+static bool updateuserMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_STR(userName,        config->args, "-user",  true, false);
+    PXOPT_LOOKUP_STR(domainName,      config->args, "-domain",  true, false);
+    PXOPT_LOOKUP_S32(accessLevel,     config->args, "-set_accessLevel", false, false);
+    PXOPT_LOOKUP_STR(defaultProduct,  config->args, "-set_defaultProduct",  false, false);
+    PXOPT_LOOKUP_STR(defaultLabel,    config->args, "-set_defaultLabel",  false, false);
+
+    if (!accessLevel && !defaultProduct && !defaultLabel) {
+        psError(PS_ERR_UNKNOWN, true, "at least one set option is required");
+        return false;
+    }
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-user",     "userName", "==");
+    PXOPT_COPY_STR(config->args, where, "-domain",   "domainName", "==");
+    if (!psListLength(where->list)) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+        return false;
+    }
+
+    psString query = psStringCopy("UPDATE pstampUser SET");
+
+    char * sep = "";
+    if (accessLevel) {
+        psStringAppend(&query, "%s accessLevel = '%d'", sep, accessLevel);
+        sep = ", ";
+    }
+    if (defaultProduct) {
+        psStringAppend(&query, "%s  defaultProduct = '%s'", sep, defaultProduct);
+        sep = ", ";
+    }
+    if (defaultLabel) {
+        psStringAppend(&query, "%s defaultLabel = '%s'", sep, defaultLabel);
+        sep = ", ";
+    }
+
+    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+    psStringAppend(&query, "\nWHERE %s", whereClause);
+    psFree(whereClause);
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+
+    // psU64 affected = psDBAffectedRows(config->dbh);
+    // psLogMsg("pstamptool", PS_LOG_INFO, "Updated %" PRIu64 " pstampRequests", affected);
+
+    return true;
+}
+static bool listuserMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-user", "userName", "=");
+    PXOPT_COPY_STR(config->args, where, "-domain", "domainName", "=");
+    PXOPT_COPY_S32(config->args, where, "-accessLevel", "pstampUser.accessLevel", "=");
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+
+    if (!psListLength(where->list)) {
+        psError(PS_ERR_UNKNOWN, true, "search paramters are required");
+        return false;
+    }
+
+    psString query = pxDataGet("pstamptool_listuser.sql");
+
+    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+    psStringAppend(&query, " WHERE %s", whereClause);
+    psFree(whereClause);
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "pstampUser", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool addaccesslevelMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(proj_id,         config->args, "-set_proj_id", false, false);
+    PXOPT_LOOKUP_STR(project_name,    config->args, "-set_project_name",  false, false);
+    PXOPT_LOOKUP_S32(accessLevel,     config->args, "-set_accessLevel", true, false);
+    PXOPT_LOOKUP_F32(mjd_min,         config->args, "-set_mjd_min", false, false);
+    PXOPT_LOOKUP_F32(mjd_max,         config->args, "-set_mjd_max", false, false);
+
+    if (proj_id) {
+        if (!pstampAccessLevelInsert(config->dbh,
+            proj_id,
+            accessLevel,
+            mjd_min,
+            mjd_max
+        )) {
+            psError(PS_ERR_UNKNOWN, false, "failed to insert accessLevel");
+            return false;
+        }
+    } else if (project_name) {
+        // use select insert getting proj_id from pstampProject
+        psString query = pxDataGet("pstamptool_addaccesslevel.sql");
+        if (!p_psDBRunQueryF(config->dbh, query, accessLevel, mjd_min, mjd_max, project_name)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(query);
+            return false;
+        }
+        psFree(query);
+    } else {
+        psError(PS_ERR_UNKNOWN, true, "proj_id or project_name is required");
+        return false;
+    }
+
+    return true;
+}
+static bool updateaccesslevelMode(pxConfig *config)
+{
+    return false;
+}
+static bool listaccesslevelMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-project_name", "pstampProject.name", "=");
+    PXOPT_COPY_S64(config->args, where, "-proj_id", "proj_id", "=");
+    PXOPT_COPY_S32(config->args, where, "-accessLevel", "accessLevel", "=");
+
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+
+    psString query = psStringCopy("SELECT * from pstampAccessLevel join pstampProject USING(proj_id)");
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " WHERE %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "pstampAccessLevel", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
 # endif
Index: /tags/ipp-20141024/ippTools/src/pstamptool.h
===================================================================
--- /tags/ipp-20141024/ippTools/src/pstamptool.h	(revision 38191)
+++ /tags/ipp-20141024/ippTools/src/pstamptool.h	(revision 38192)
@@ -53,4 +53,17 @@
     PSTAMPTOOL_MODE_LISTFILE,
     PSTAMPTOOL_MODE_DELETEFILE,
+
+    PSTAMPTOOL_MODE_ADDUSER,
+    PSTAMPTOOL_MODE_UPDATEUSER,
+    PSTAMPTOOL_MODE_LISTUSER,
+//    PSTAMPTOOL_MODE_GETUSERINFO,
+
+    PSTAMPTOOL_MODE_ADDDOMAIN,
+    PSTAMPTOOL_MODE_UPDATEDOMAIN,
+    PSTAMPTOOL_MODE_LISTDOMAIN,
+
+    PSTAMPTOOL_MODE_ADDACCESSLEVEL,
+    PSTAMPTOOL_MODE_UPDATEACCESSLEVEL,
+    PSTAMPTOOL_MODE_LISTACCESSLEVEL,
 } pstamptoolMode;
 
Index: /tags/ipp-20141024/ippTools/src/pstamptoolConfig.c
===================================================================
--- /tags/ipp-20141024/ippTools/src/pstamptoolConfig.c	(revision 38191)
+++ /tags/ipp-20141024/ippTools/src/pstamptoolConfig.c	(revision 38192)
@@ -277,5 +277,5 @@
     // -project
     psMetadata *projectArgs = psMetadataAlloc();
-    psMetadataAddStr(projectArgs, PS_LIST_TAIL, "-name", 0, "define project name to list (required)", NULL);
+    psMetadataAddStr(projectArgs, PS_LIST_TAIL, "-name", 0, "define project name to list", NULL);
     psMetadataAddBool(projectArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
 
@@ -303,4 +303,75 @@
     psMetadataAddS64(deletefileArgs, PS_LIST_TAIL, "-job_id",      0, "select by job ID (required)", 0);
 
+    // Access control tables
+
+    // -adddomain
+    psMetadata *adddomainArgs = psMetadataAlloc();
+    psMetadataAddStr(adddomainArgs, PS_LIST_TAIL, "-set_domain",   0, "define domain name (required)", NULL);
+    psMetadataAddS32(adddomainArgs, PS_LIST_TAIL, "-set_accessLevel", 0, "define access level for this domain (required)", 0);
+    psMetadataAddStr(adddomainArgs, PS_LIST_TAIL, "-set_defaultProduct",   0, "define default product", NULL);
+    psMetadataAddStr(adddomainArgs, PS_LIST_TAIL, "-set_defaultLabel",   0, "define default Label", NULL);
+
+    // -updatedomain
+    psMetadata *updatedomainArgs = psMetadataAlloc();
+    psMetadataAddStr(updatedomainArgs, PS_LIST_TAIL, "-domain",   0, "define domain name (required)", NULL);
+    psMetadataAddS32(updatedomainArgs, PS_LIST_TAIL, "-set_accessLevel", 0, "define access level for this domain", 0);
+    psMetadataAddStr(updatedomainArgs, PS_LIST_TAIL, "-set_defaultProduct",   0, "define default product", NULL);
+    psMetadataAddStr(updatedomainArgs, PS_LIST_TAIL, "-set_defaultLabel",   0, "define default Label", NULL);
+
+    // -listdomain
+    psMetadata *listdomainArgs = psMetadataAlloc();
+    psMetadataAddStr(listdomainArgs, PS_LIST_TAIL, "-domain",   0, "define domain name", NULL);
+    psMetadataAddS32(listdomainArgs, PS_LIST_TAIL, "-accessLevel", 0, "select by  access level", 0);
+    psMetadataAddU64(listdomainArgs, PS_LIST_TAIL, "-limit",       0, "limit result set to N items", 0);
+    psMetadataAddBool(listdomainArgs, PS_LIST_TAIL, "-simple",     0, "use the simple output format", false);
+
+    // -adduser
+    psMetadata *adduserArgs = psMetadataAlloc();
+    psMetadataAddStr(adduserArgs, PS_LIST_TAIL, "-set_user",   0, "define user name (required)", NULL);
+    psMetadataAddStr(adduserArgs, PS_LIST_TAIL, "-set_domain",   0, "define user's domain name (required)", NULL);
+    psMetadataAddS32(adduserArgs, PS_LIST_TAIL, "-set_accessLevel", 0, "define maximum access level for this user", 0);
+    psMetadataAddStr(adduserArgs, PS_LIST_TAIL, "-set_defaultProduct",      0, "define default data store product for this user", NULL);
+    psMetadataAddStr(adduserArgs, PS_LIST_TAIL, "-set_defaultLabel",      0, "define default label for requests from this user", NULL);
+
+    // -updateuser
+    psMetadata *updateuserArgs = psMetadataAlloc();
+    psMetadataAddStr(updateuserArgs, PS_LIST_TAIL, "-user",   0, "select user to update (required)", NULL);
+    psMetadataAddStr(updateuserArgs, PS_LIST_TAIL, "-domain",   0, "select domain of user to update (required)", NULL);
+    psMetadataAddS32(updateuserArgs, PS_LIST_TAIL, "-set_accessLevel", 0, "define access level for this domain", 0);
+    psMetadataAddStr(updateuserArgs, PS_LIST_TAIL, "-set_defaultProduct",   0, "define default product", NULL);
+    psMetadataAddStr(updateuserArgs, PS_LIST_TAIL, "-set_defaultLabel",   0, "define default Label", NULL);
+
+    // -listuser
+    psMetadata *listuserArgs = psMetadataAlloc();
+    psMetadataAddStr(listuserArgs, PS_LIST_TAIL, "-user",   0, "select by user name", NULL);
+    psMetadataAddStr(listuserArgs, PS_LIST_TAIL, "-domain",   0, "select by user's domain name", NULL);
+    psMetadataAddS32(listuserArgs, PS_LIST_TAIL, "-accessLevel", 0, "select by maximum access level", 0);
+    psMetadataAddU64(listuserArgs, PS_LIST_TAIL, "-limit",       0, "limit result set to N items", 0);
+    psMetadataAddBool(listuserArgs, PS_LIST_TAIL, "-simple",     0, "use the simple output format", false);
+
+    // -addaccesslevel
+    psMetadata *addaccesslevelArgs = psMetadataAlloc();
+    psMetadataAddStr(addaccesslevelArgs, PS_LIST_TAIL, "-set_project_name",   0, "define project name", NULL);
+    psMetadataAddS64(addaccesslevelArgs, PS_LIST_TAIL, "-set_proj_id", 0, "define project ID", 0);
+    psMetadataAddS32(addaccesslevelArgs, PS_LIST_TAIL, "-set_accessLevel", 0, "define access level (required)", 0);
+    psMetadataAddF32(addaccesslevelArgs, PS_LIST_TAIL, "-set_mjd_min", 0, "define mjd_min", 0.0);
+    psMetadataAddF32(addaccesslevelArgs, PS_LIST_TAIL, "-set_mjd_max", 0, "define mjd_max", 0.0);
+
+    // -listaccesslevel
+    psMetadata *listaccesslevelArgs = psMetadataAlloc();
+    psMetadataAddStr(listaccesslevelArgs, PS_LIST_TAIL, "-project_name",   0, "search by project name", NULL);
+    psMetadataAddS64(listaccesslevelArgs, PS_LIST_TAIL, "-proj_id", 0, "select by  project ID", 0);
+    psMetadataAddS32(listaccesslevelArgs, PS_LIST_TAIL, "-accessLevel", 0, "select by  access level", 0);
+    psMetadataAddU64(listaccesslevelArgs, PS_LIST_TAIL, "-limit",       0, "limit result set to N items", 0);
+    psMetadataAddBool(listaccesslevelArgs, PS_LIST_TAIL, "-simple",     0, "use the simple output format", false);
+
+    // -updateaccesslevel
+    psMetadata *updateaccesslevelArgs = psMetadataAlloc();
+    psMetadataAddStr(updateaccesslevelArgs, PS_LIST_TAIL, "-project_name",   0, "select by project name", NULL);
+    psMetadataAddS64(updateaccesslevelArgs, PS_LIST_TAIL, "-proj_id", 0, "select by project ID", 0);
+    psMetadataAddS32(updateaccesslevelArgs, PS_LIST_TAIL, "-accessLevel", 0, "select by access level (required)", 0);
+    psMetadataAddF32(updateaccesslevelArgs, PS_LIST_TAIL, "-set_mjd_min", 0, "define new mjd_min", NAN);
+    psMetadataAddF32(updateaccesslevelArgs, PS_LIST_TAIL, "-set_mjd_max", 0, "define new mjd_max", NAN);
+
     PXOPT_ADD_MODE("-addreq",          "", PSTAMPTOOL_MODE_ADDREQ,       addreqArgs);
     PXOPT_ADD_MODE("-pendingreq",      "", PSTAMPTOOL_MODE_PENDINGREQ,   pendingreqArgs);
@@ -334,4 +405,13 @@
     PXOPT_ADD_MODE("-listfile",        "", PSTAMPTOOL_MODE_LISTFILE,   listfileArgs);
     PXOPT_ADD_MODE("-deletefile",      "", PSTAMPTOOL_MODE_DELETEFILE,   deletefileArgs);
+
+    PXOPT_ADD_MODE("-adddomain",       "", PSTAMPTOOL_MODE_ADDDOMAIN, adddomainArgs);
+    PXOPT_ADD_MODE("-updatedomain",    "", PSTAMPTOOL_MODE_UPDATEDOMAIN, updatedomainArgs);
+    PXOPT_ADD_MODE("-listdomain",      "", PSTAMPTOOL_MODE_LISTDOMAIN, listdomainArgs);
+    PXOPT_ADD_MODE("-adduser",         "", PSTAMPTOOL_MODE_ADDUSER, adduserArgs);
+    PXOPT_ADD_MODE("-updateuser",      "", PSTAMPTOOL_MODE_UPDATEUSER, updateuserArgs);
+    PXOPT_ADD_MODE("-listuser",        "", PSTAMPTOOL_MODE_LISTUSER, listuserArgs);
+    PXOPT_ADD_MODE("-addaccesslevel",  "", PSTAMPTOOL_MODE_ADDACCESSLEVEL, addaccesslevelArgs);
+    PXOPT_ADD_MODE("-listaccesslevel", "", PSTAMPTOOL_MODE_LISTACCESSLEVEL, listaccesslevelArgs);
 
     if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
Index: /tags/ipp-20141024/ippTools/src/releasetool.c
===================================================================
--- /tags/ipp-20141024/ippTools/src/releasetool.c	(revision 38191)
+++ /tags/ipp-20141024/ippTools/src/releasetool.c	(revision 38192)
@@ -195,4 +195,5 @@
     PXOPT_LOOKUP_STR(dvodb, config->args,         "-set_dvodb",  false, false);
     PXOPT_LOOKUP_STR(ubercal_file, config->args,  "-set_ubercal_file",  false, false);
+    PXOPT_LOOKUP_S32(accessLevelMin,    config->args,   "-set_accessLevelMin", false, false);
 
     if (!ippReleaseInsert(config->dbh,
@@ -204,5 +205,6 @@
         priority,
         dvodb,
-        ubercal_file
+        ubercal_file,
+        accessLevelMin
     )) {
         psError(PS_ERR_UNKNOWN, false, "failed to insert ippRelease");
@@ -229,5 +231,6 @@
     PXOPT_LOOKUP_STR(dvodb, config->args, "-set_dvodb",  false, false);
     PXOPT_LOOKUP_STR(ubercal_file, config->args, "-set_ubercal_file",  false, false);
-    if (!release_state && priority == 0 && dataRelease == -1 && !dvodb && !ubercal_file) {
+    PXOPT_LOOKUP_S32(accessLevelMin,    config->args,   "-set_accessLevelMin", false, false);
+    if (!release_state && priority == 0 && dataRelease == -1 && !dvodb && !ubercal_file && accessLevelMin < 0) {
         psError(PXTOOLS_ERR_CONFIG, true, "at least one of -set_release_state -set_priority -set_dvodb -set_ubercal_file and -set_dataRelease is required\n");
         return false;
@@ -252,4 +255,8 @@
     if (dataRelease != -1) {
         psStringAppend(&query, "%s dataRelease = %d", sep, dataRelease);
+        sep = comma;
+    }
+    if (accessLevelMin != -1) {
+        psStringAppend(&query, "%s accessLevelMin = %d", sep, accessLevelMin);
         sep = comma;
     }
Index: /tags/ipp-20141024/ippTools/src/releasetoolConfig.c
===================================================================
--- /tags/ipp-20141024/ippTools/src/releasetoolConfig.c	(revision 38191)
+++ /tags/ipp-20141024/ippTools/src/releasetoolConfig.c	(revision 38192)
@@ -62,4 +62,5 @@
     psMetadataAddStr(definereleaseArgs, PS_LIST_TAIL, "-set_release_name", 0,   "define release name (required)", NULL);
     psMetadataAddS32(definereleaseArgs, PS_LIST_TAIL, "-set_dataRelease", 0,   "define dataRelease", -1);
+    psMetadataAddS32(definereleaseArgs, PS_LIST_TAIL, "-set_accessLevelMin", 0,   "define accessLevelMin", 0);
     psMetadataAddStr(definereleaseArgs, PS_LIST_TAIL, "-set_release_state", 0, "define release state", NULL);
     psMetadataAddS32(definereleaseArgs, PS_LIST_TAIL, "-set_priority", 0,      "define release priority", 0);
@@ -77,4 +78,5 @@
     psMetadataAddS32(updatereleaseArgs, PS_LIST_TAIL, "-set_priority", 0,       "define new release priority", 0);
     psMetadataAddS32(updatereleaseArgs, PS_LIST_TAIL, "-set_dataRelease", 0,    "define data release", -1);
+    psMetadataAddS32(updatereleaseArgs, PS_LIST_TAIL, "-set_accessLevelMin", 0,   "define accessLevelMin", -1);
     psMetadataAddStr(updatereleaseArgs, PS_LIST_TAIL, "-set_dvodb", 0,         "define new dvo db name", NULL);
     psMetadataAddStr(updatereleaseArgs, PS_LIST_TAIL, "-set_ubercal_file", 0,  "define new ubercal file name", NULL);
Index: /tags/ipp-20141024/pstamp/scripts/psmkreq
===================================================================
--- /tags/ipp-20141024/pstamp/scripts/psmkreq	(revision 38191)
+++ /tags/ipp-20141024/pstamp/scripts/psmkreq	(revision 38192)
@@ -1,3 +1,3 @@
-#!/bin/env perl
+#!/usr/bin/env perl
 ###
 ### psmkreq
@@ -30,4 +30,5 @@
 
 my ($image, $mask, $variance, $jpeg, $cmf, $psf, $backmdl, $inverse, $restorebackground);
+my ($exptime, $expnum, $exptimejpeg, $expnumjpeg);
 my ($convolved, $unconvolved, $uncompressed, $use_imfile_id, $no_wait);
 
@@ -116,4 +117,8 @@
     'psf'               => \$psf,
     'backmdl'           => \$backmdl,
+    'exptime'           => \$exptime,
+    'expnum'            => \$expnum,
+    'exptimejpeg'       => \$exptimejpeg,
+    'expnumjpeg'        => \$expnumjpeg,
     'inverse'           => \$inverse,
     'unconvolved'       => \$unconvolved,
@@ -217,8 +222,11 @@
         $option_mask = $PSTAMP_SELECT_IMAGE    if $option_mask == 0;
 
-
         $option_mask |= $PSTAMP_SELECT_CMF      if $cmf;
         $option_mask |= $PSTAMP_SELECT_PSF      if $psf;
         $option_mask |= $PSTAMP_SELECT_BACKMDL  if $backmdl;
+        $option_mask |= $PSTAMP_SELECT_EXP      if $exptime;
+        $option_mask |= $PSTAMP_SELECT_NUM      if $expnum;
+        $option_mask |= $PSTAMP_SELECT_EXPJPEG  if $exptimejpeg;
+        $option_mask |= $PSTAMP_SELECT_NUMJPEG  if $expnumjpeg;
         $option_mask |= $PSTAMP_SELECT_UNCOMPRESSED if $uncompressed;
         $option_mask |= $PSTAMP_SELECT_UNCONV   if $unconvolved;
Index: /tags/ipp-20141024/pstamp/scripts/pstampparse.pl
===================================================================
--- /tags/ipp-20141024/pstamp/scripts/pstampparse.pl	(revision 38191)
+++ /tags/ipp-20141024/pstamp/scripts/pstampparse.pl	(revision 38192)
@@ -18,4 +18,5 @@
 use POSIX;
 use Time::HiRes qw(gettimeofday);
+use Time::Local;
 
 my $verbose;
@@ -32,4 +33,7 @@
 my $dest_requires_magic;
 my $dump_params = 0;
+my $new_email;
+
+my %accessLevelCache;
 
 # set this to true to disable update processing
@@ -42,4 +46,5 @@
     'product=s' =>  \$product,
     'label=s'   =>  \$label,
+    'new_email=s' => \$new_email,
     'mode=s'    =>  \$mode,
     'need_magic'=>  \$dest_requires_magic,
@@ -54,15 +59,9 @@
 die "invalid mode '$mode'" unless ($mode eq "list_uri") or ($mode eq "queue_job");
 die "--file is required"   unless defined($request_file_name);
-
-if ($mode ne "list_uri") {
-    die "req_id is required"  if !$req_id;
+die "req_id is required"  unless defined $req_id;
+if ($mode ne 'list_uri') {
     die "outdir is required"  if !$outdir;
-    die "product is required" if !$product;
 } else {
-    # mode eq 'list_uri' (used for parser testing)
-    # these values won't be used for anything but should be defined to avoid errors
-    $req_id  = 0;
-    $outdir  = "nowhere";
-    $product = "dummy";
+    $outdir = "somewhere" if !$outdir
 }
 
@@ -94,4 +93,28 @@
 # do not update the database
 $no_update = 1 if $mode ne "queue_job";
+
+$product = 'NULL' unless $product;
+
+
+# look up some defaults
+my $defaultAccessLevel = metadataLookupS32($ipprc->{_siteConfig}, 'PSTAMP_DEFAULT_ACCESS_LEVEL');
+if (!defined $defaultAccessLevel) {
+    warn("cannot find 'PSTAMP_DEFAULT_ACCESS_LEVEL' in site config");
+    exit ($PS_EXIT_CONFIG_ERROR);
+}
+
+# look up the default data store product.
+my $defaultDSProduct = metadataLookupStr($ipprc->{_siteConfig}, 'PSTAMP_DATA_STORE_PRODUCT');
+if (!defined $defaultDSProduct) {
+    # Don't panic yet that it's not defined.
+    #  exit ($PS_EXIT_CONFIG_ERROR);
+    $defaultDSProduct = ''; # set to empty string to simplify some comparisions below
+    print STDERR "Warning PSTAMP_DATA_STORE_PRODUCT not found in the site config.\n" if  $product eq 'NULL';
+}
+
+my $productIsDefault = 0;
+if ($product and $defaultDSProduct) {
+    $productIsDefault = ($product eq $defaultDSProduct);
+}
 
 my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
@@ -123,7 +146,11 @@
     print STDERR "Note that EMAIL will not be optional\n";
 
+    # if need be we can hack Peter Veres' email in here if label = 'WEB.UP' and req_name like 'Peter_Veres%'
+
 } elsif (!$email) {
     my_die("ERROR: Required parameter EMAIL not found in request file header.\n", $PSTAMP_INVALID_REQUEST) 
 }
+
+
     
 
@@ -139,6 +166,4 @@
     my_die("email not supplied in version $extver request file $request_file_name\n", $PSTAMP_INVALID_REQUEST)
         unless $email;
-
-    # XXX check for "valid" $email
 } else {
     # for version 1 file the action is process and email is not used
@@ -148,4 +173,10 @@
 
 print "Request Header Keywords EXTVER: $extver REQ_NAME: $req_name ACTION: $action EMAIL: $email\n";
+
+if ($new_email) {
+    # this is primarily for ease in testing
+    print "Setting email to command line provided value $new_email\n";
+    $email = $new_email
+}
 
 # check for duplicate request name
@@ -158,5 +189,5 @@
     my $exitStatus = $error_code >> 8;
     if ($success) {
-        # -listreq succeeded duplicate request name
+        # -listreq succeeded there is already a request in the database with this name
         print STDERR "REQ_NAME $req_name has already been used\n";
         insertFakeJobForRow(undef, 0, $PSTAMP_DUP_REQUEST);
@@ -169,7 +200,10 @@
 
 
-# Adjust the label for requests coming in over the web interface
 
 my $label_changed = 0;
+if (0) {
+# Adjust the label for requests coming in over the web upload interface
+# XXX: do this later based on the user information
+# We now use the access tabels to set the label.
 if ($label and $label eq "WEB.UP") {
     my $lcname = lc($req_name);
@@ -198,9 +232,49 @@
     print "Setting label for $req_name to $label\n" if $label_changed;
 }
-
+}
+
+# based on the user's email address get the accessLevel, default label and data store product
+my $productForUser;
+my $labelForUser = $label;  # XXX: for now I pass the current label for use in hacking permissions for V1 requests.
+
+my $accessLevel = getUsersAccessLevel($email, \$labelForUser, \$productForUser);
+if ($accessLevel < 0) {
+    print STDERR "Data access is forbidden for $email.\n";
+    insertFakeJobForRow(undef, 0, $PSTAMP_NOT_AUTHORIZED);
+}
+
+# if the request came through the request table upload channel, change it to the user's specific label
+# if any is known. Do we want to do this for any other channels?
+# requests from data store will have those values.
+if ($label and $label eq 'WEB.UP') {
+    if ($labelForUser) {
+        $label = $labelForUser;
+        $label_changed = 1;
+    }
+}
+
+# If the product is the system default, set it to the product for the user@domain. That way 
+# if the request came through a data store that target will be used.
+# We really only need to redirect the results with greater access level than the default.
+# note that for most users productForUser will be undef so the default will be used.
+if ($productIsDefault or $product eq 'NULL') {
+    if ($productForUser) {
+        $product = $productForUser;
+    }
+} else {
+    # if we don't have a product use default
+    if ($product eq 'NULL') {
+        $product = $defaultDSProduct;
+    }
+}
+
+if (!$product or $product eq 'NULL') {
+    # this will only happen if the site.config variable is missing.
+    my_die("No product found.\n", $PS_EXIT_CONFIG_ERROR);
+}
 
 {
-    # update the database with the request name. This will be used as the
-    # the fileset name in the output data store
+    # update the request's row with the new parameters.
+    # The requset name will be used to set the output fileset name in the output data store.
     my $command = "$pstamptool -updatereq -req_id $req_id  -set_name $req_name";
     $command .= " -set_username $email" if $email ne 'null';
@@ -218,7 +292,13 @@
 }
 
+# in these two error conditions we are done. The invoking script will finish up.
 if ($duplicate_req_name) {
     exit 0;
 }
+
+if ($accessLevel < 0) {
+    exit 0;
+}
+
 
 #
@@ -270,4 +350,5 @@
 my $num_jobs = 0;
 
+
 foreach my $row (@$rows) {
 
@@ -281,4 +362,5 @@
     $row->{job_num} = 0;
     $row->{error_code} = 0;
+    $row->{accessLevel} = $accessLevel;
 
     $num_jobs += processRow($action, $row);
@@ -292,5 +374,5 @@
 
 if (($action eq 'LIST' or $mode eq "queue_job") and ($num_jobs eq 0)) {
-    # this should not happen. The function above is required to insert a fake job for any
+    # this should not happen. The processRow is required to insert a fake job for any
     # rows that did not yield any jobs.
     print STDERR "ERROR: zero jobs created for $req_name\n";
@@ -508,4 +590,13 @@
     my $rownum      = $row->{ROWNUM};
 
+    my $mjd_max;
+    my $adjustedDateCuts = adjustDateCuts($row, \$mjd_max);
+    if ($adjustedDateCuts and $mjd_max < 0) {
+        print STDERR "User is not authorized for data from this project.\n";
+        insertFakeJobForRow($row, 1, $PSTAMP_NOT_AUTHORIZED);
+        $num_jobs++;
+        return $num_jobs;
+    }
+
     # Since user can get unmagicked data "by coordinate" requests can go back in time
     # to dredge unusable data from the "dark days"...
@@ -519,4 +610,22 @@
         # avoid error from print below if $id isn't needed
         $row->{ID} = "" if !$row->{ID};
+
+        # if this user does not have unlimited access rights adjust the date cut
+        # to avoid more recent data
+        # We do not adjust dates for stacks since they are not well defined enough.
+        # We'll need to filter them by accessLevel during the lookup
+        my $stage = $row->{IMG_TYPE};
+        if ($stage ne 'stack' and $adjustedDateCuts) {
+            # if lower limit was supplied by user we can leave the value alone
+            if ($row->{MJD_MAX}) {
+                if ($row->{MJD_MAX} > $mjd_max) {
+                    print STDERR "Access rights require limiting MJD_MAX to $mjd_max\n";
+                    $row->{MJD_MAX} = $mjd_max;
+                }
+            } else {
+                # no limit supplied we silently adjust it
+                $row->{MJD_MAX} = $mjd_max;
+            }
+        }
     }
     
@@ -547,102 +656,4 @@
 }
 
-sub processRows {
-    my $action = shift;
-    my $rowList = shift;
-    my $num_jobs = 0;
-
-    if ($action eq 'LIST') {
-        # LIST is not allowed by caller. Can't get here.
-        return list_targets($rowList);
-    }
-
-    # all rows in the list are compatible
-    my $row = $rowList->[0];
-
-    my $project  = $row->{PROJECT};
-
-    # note: resolve_project avoids running pstamptool every time by remembering the
-    # last project resolved
-    my $proj_hash = resolve_project($ipprc, $project, $dbname, $dbserver);
-    if (!$proj_hash) {
-        foreach my $r (@$rowList) {
-            insertFakeJobForRow($r, 1, $PSTAMP_UNKNOWN_PRODUCT);
-            $num_jobs++;
-        }
-        return $num_jobs;
-    }
-    my $req_type  = $row->{REQ_TYPE};
-    my $stage     = $row->{IMG_TYPE};
-    my $id        = $row->{ID};
-    my $component = $row->{COMPONENT};
-    my $tess_id   = $row->{TESS_ID};
-
-    my $filter    = $row->{REQFILT};
-    my $mjd_min   = $row->{MJD_MIN};
-    my $mjd_max   = $row->{MJD_MAX};
-    my $data_group = $row->{DATA_GROUP};
-
-    my $rownum     = $row->{ROWNUM};
-    my $job_type   = $row->{JOB_TYPE};
-    my $option_mask= $row->{OPTION_MASK};
-    
-    my $image_db   = $proj_hash->{dbname};
-    my $camera     = $proj_hash->{camera};
-    my $need_magic = $proj_hash->{need_magic};
-    # Since user can get unmagicked data "by coordinate" requests can go back in time
-    # to dredge unusable data from the "dark days"...
-    if ($req_type eq 'bycoord' and $mjd_min eq 0) {
-	    # ... so unless the user sets mjd_min clamp it to 2009-04-01
-            # XXX: This value should live in the pstampProject table not be hardcoded here
-            $mjd_min = 54922;
-    }
-
-    $need_magic = 0 if $stage eq 'stack';
-
-    # XXX: magic is dead
-    $need_magic = 0;
-
-    my $numRows = scalar @$rowList;
-
-    if (($req_type eq 'byskycell') or ($req_type eq 'bycoord')) {
-        # avoid error from print below if $id isn't needed
-        $id = "" if !$id;
-    }
-    print "Collected $numRows rows beginning with row $rownum. $req_type $stage $id $tess_id $component\n";
-    
-    # Call PS::IPP::PStamp::Job locate_images subroutine to get the images for this
-    # request specification. An array reference is returned.
-    my $start_locate = gettimeofday();
-
-    # XXX: perhaps we should get rid of most of this argument list.
-    # Now that we are passing down compatible rows all of the
-    # information required is contained there
-
-    my $imageList = locate_images($ipprc, $image_db, $rowList, $req_type, $stage, $id, $tess_id, $component,
-                $option_mask, $need_magic, $mjd_min, $mjd_max, $filter, $data_group, $verbose);
-
-    my $dtime_locate = gettimeofday() - $start_locate;
-    print "Time to locate_images for row $rownum $dtime_locate\n";
-
-    # handle this 
-    # XXX: what did I mean by that comment?
-    $row->{need_magic} = $need_magic;
-
-    $num_jobs += queueJobs($action, $rowList, $imageList);
-
-    # if a row slipped through with no jobs add a faulted one
-    foreach my $row (@$rowList) {
-        if ($row->{job_num} == 0) {
-            print "row $row->{ROWNUM} produced no jobs\n";
-            print STDERR "row $row->{ROWNUM} produced no jobs\n";
-            my $error_code = $row->{error_code};
-            $error_code =  $PSTAMP_NO_IMAGE_MATCH if !$error_code;
-            insertFakeJobForRow($row, ++$row->{job_num}, $error_code);
-        }
-    }
-
-    return $num_jobs;
-}
-
 sub queueJobForImage
 {
@@ -658,4 +669,8 @@
 
     $option_mask |= $PSTAMP_NO_WAIT_FOR_UPDATE if $no_updates_allowed;
+
+    # determine if there is a cut off date for data access for this user
+    my $mjd_max;
+    my $adjustedDateCuts = adjustDateCuts($row, \$mjd_max);
 
     my $roi_string;
@@ -702,15 +717,6 @@
 
     my $imagefile = $image->{image};
-    if (($stage ne "stack") and ($need_magic and !$image->{magicked})) {
-        # XXX: should we add a faulted job so the client can know what happened if no images come back?
-        # The test for destreaked is made in locate_images now so this code never runs. This leads to no feedback
-        # to users, but speeds up processing significantly
-        print STDERR "skipping non-magicked image $imagefile\n" if $verbose;
-
-        # for now assume yes.
-
-        insertFakeJobForRow($row, $job_num, $PSTAMP_NOT_DESTREAKED);
-        return 1;
-    } elsif ($stage eq "stack") {
+
+    if ($stage eq "stack") {
         # unconvolved stack images weren't available prior to some point in time.
         # XXX: handle this more correctly by examining the stack run's config dump file.
@@ -723,4 +729,45 @@
             insertFakeJobForRow($row, $job_num, $PSTAMP_NOT_AVAILABLE);
             return 1;
+        }
+    } else { 
+        if ($need_magic and !$image->{magicked}) {
+            # XXX: should we add a faulted job so the client can know what happened if no images come back?
+            # The test for destreaked is made in locate_images now so this code never runs. This leads to no feedback
+            # to users, but speeds up processing significantly
+            print STDERR "skipping non-magicked image $imagefile\n" if $verbose;
+
+            # for now assume yes.
+
+            insertFakeJobForRow($row, $job_num, $PSTAMP_NOT_DESTREAKED);
+            return 1;
+        }
+
+        if ($adjustedDateCuts) {
+            my $mjd = 0;
+            my $exp_id;
+            if ($stage eq 'diff') {
+                # diff stage doesn't have a dateobs so we need to hack it.
+                # Extract dateobs from the exp_name_1
+                if ($row->{PROJECT} eq 'gpc1') {
+                    my $exp_name = $image->{exp_name_1};
+                    if ($exp_name ne 'NULL') {
+                        $mjd = 50000 + (substr($exp_name, 1, 4));
+                        $exp_id = $image->{exp_id_1};
+                    } else {
+                        # it's stack-stack diff just let it go with mjd = 0
+                        # XXX: deal with this
+                    }
+                }
+            } else {
+                $exp_id = $image->{exp_id};
+                $mjd = dateobsToMJD($image->{dateobs});
+            }
+
+            if ($mjd > $mjd_max) {
+                print "User is not authorized for stamps from this exposure exp_id $exp_id.\n";
+                print STDERR "User is not authorized for stamps from this exposure exp_id $exp_id.\n";
+                insertFakeJobForRow($row, $job_num, $PSTAMP_NOT_AUTHORIZED, $exp_id);
+                return 1;
+            }
         }
     }
@@ -953,4 +1000,5 @@
     my $job_num = shift;
     my $fault = shift;
+    my $exp_id = shift;
 
     my ($job_type, $rownum);
@@ -975,4 +1023,5 @@
     my $command = "$pstamptool -addjob  -req_id $req_id -job_type $job_type"
                         . " -rownum $rownum -state stop -fault $fault";
+    $command .= " -exp_id $exp_id" if $exp_id and $exp_id ne 'NULL';
 
     if (!$no_update) {
@@ -1230,4 +1279,136 @@
 }
 
+# get user's access level default label and output product
+sub getUsersAccessLevel {
+    my $email = shift;
+    my $r_label = shift;
+    my $r_product = shift;
+
+    # $r_label is a copy of the initial label. We change it to a user specific one
+    # if one is found.
+    my $label = $$r_label;
+
+    # default settings
+    $$r_label   =  undef;
+    $$r_product =  undef;
+    my $level = -1;
+
+    if (!$email) {
+        # No email provided. 
+        # For now in order to temporarily continue to support the version 1 request format.
+        # set the access rights based on the assigned label.
+        # Before deployment this will be forbidden.
+        if ($label eq 'IFA' or $label eq 'QUB' or $label =~ 'MOPS') {
+            $level = 2;
+        } else {
+            $level = $defaultAccessLevel;
+        }
+        return $level
+    }
+
+    # we've got an email check the domain
+    my ($user, $domain) = split '@', $email;
+    unless ($user and $domain) {
+        print STDERR "Error $email is not an acceptable email adddress.\n";
+        return -1;
+    } 
+
+    my $cmd = "$pstamptool -listuser -user $user -domain $domain";
+    my $results = runToolAndParse($cmd, $verbose);
+    my $userinfo = $results->[0];
+    if ($userinfo) {
+        $level = $userinfo->{accessLevel};
+        if ($userinfo->{userProduct}) {
+            $$r_product = $userinfo->{userProduct};
+        } elsif ($userinfo->{domainProduct}) {
+            $$r_product = $userinfo->{domainProduct};
+        }
+        if ($userinfo->{userLabel}) {
+            $$r_label = $userinfo->{userLabel};
+        } elsif ($userinfo->{domainLabel}) {
+            $$r_label = $userinfo->{domainLabel};
+        }
+    } else {
+        my $cmd = "$pstamptool -listdomain -domain $domain";
+        my $results = runToolAndParse($cmd, $verbose);
+        my $domaininfo = $results->[0];
+        if ($domaininfo) {
+            $level = $domaininfo->{accessLevel};
+            if ($domaininfo->{defaultProduct}) {
+                $$r_product = $domaininfo->{defaultProduct};
+            }
+            if ($domaininfo->{defaultLabel}) {
+                $$r_label = $domaininfo->{defaultLabel};
+            }
+    } else {
+            # no specific user or domain information found in the database. Use the defaults
+            $level = $defaultAccessLevel;
+            $$r_label =  undef;
+            $$r_product =  undef;
+        }
+    }
+
+    return $level;
+}
+
+
+# determine the latest exposure date that a user may have access to
+# based on the user's accessLevel.
+sub adjustDateCuts {
+    my $row = shift;
+    my $r_mjd_max = shift;
+    my $adjusted = 0;
+
+    my $accessLevel = $row->{accessLevel};
+    my $project = $row->{PROJECT};
+    my $mjd_max_user = $row->{MJD_MAX};
+
+    my $key = "$project.$accessLevel";
+
+    my $levelInfo = $accessLevelCache{$key};
+
+    if (!$levelInfo) {
+        my $cmd = "$pstamptool -listaccesslevel -accessLevel $accessLevel -project_name $project";
+        my $results = runToolAndParse($cmd, $verbose);
+        if (!$results or !$results->[0]) {
+            my_die("failed to find accessLevel info for project: $project level: $accessLevel\n", $PS_EXIT_CONFIG_ERROR);
+        }
+        $levelInfo = $results->[0];
+        $accessLevelCache{$key} = $levelInfo;
+    }
+
+    my $mjd_max_thisLevel = $levelInfo->{mjd_max};
+    if ($mjd_max_thisLevel)  {
+        # This project has a limit for this level
+        # adjust the user supplied limit unless it is less than the maximum
+
+        if (!$mjd_max_user or ($mjd_max_user > $mjd_max_thisLevel)) {
+            $$r_mjd_max = $mjd_max_thisLevel;
+            $adjusted = 1;
+        }
+    }
+
+    return $adjusted;
+}
+
+sub dateobsToMJD {
+    my $dateobs = shift;
+
+    my ($date, $time) = split "T", $dateobs;
+
+    my ($year, $mon, $day) = split "-", $date;
+
+    my ($hr, $min, $sec) = split ":",   $time;
+    my $fraction = $sec - int($sec);
+    $sec = int($sec);
+
+    my $ticks = timegm($sec, $min, $hr, $day, $mon-1, $year-1900);
+    $ticks += $fraction;
+
+    # I'm not allowing for leap seconds here but for our purposes here that is ok
+    my $mjd = 40587.0 + ($ticks / 86400.);
+    return $mjd;
+}
+
 sub my_die
 {
