static void push_item(array_header *arr, char *type, char *to, char *path, char *data) { struct item *p = (struct item *) ap_push_array(arr); if (!to) { to = ""; } if (!path) { path = ""; } p->type = type; p->data = data ? ap_pstrdup(arr->pool, data) : NULL; p->apply_path = ap_pstrcat(arr->pool, path, "*", NULL); if ((type == BY_PATH) && (!ap_is_matchexp(to))) { p->apply_to = ap_pstrcat(arr->pool, "*", to, NULL); } else if (to) { p->apply_to = ap_pstrdup(arr->pool, to); } else { p->apply_to = NULL; } }
/* See the INTERNET-DRAFT document "Tunneling SSL Through a WWW Proxy" * currently at http://www.mcom.com/newsref/std/tunneling_ssl.html * for the format of the "CONNECT host:port HTTP/1.0" request */ API_EXPORT(int) ap_parse_hostinfo_components(pool *p, const char *hostinfo, uri_components * uptr) { const char *s; char *endstr; /* Initialize the structure. parse_uri() and parse_uri_components() * can be called more than once per request. */ memset(uptr, '\0', sizeof(*uptr)); uptr->is_initialized = 1; uptr->hostinfo = ap_pstrdup(p, hostinfo); /* We expect hostinfo to point to the first character of * the hostname. There must be a port, separated by a colon */ s = strchr(hostinfo, ':'); if (s == NULL) { return HTTP_BAD_REQUEST; } uptr->hostname = ap_pstrndup(p, hostinfo, s - hostinfo); ++s; uptr->port_str = ap_pstrdup(p, s); if (*s != '\0') { uptr->port = (unsigned short)ap_strtol(uptr->port_str, &endstr, 10); if (*endstr == '\0') { return HTTP_OK; } /* Invalid characters after ':' found */ } return HTTP_BAD_REQUEST; }
static const char *suphp_handle_cmd_user_group(cmd_parms *cmd, void *mconfig, const char *arg1, const char *arg2) { suphp_conf *cfg; if (mconfig) cfg = (suphp_conf *) mconfig; else cfg = ap_get_module_config(cmd->server->module_config, &suphp_module); cfg->target_user = ap_pstrdup(cmd->pool, arg1); cfg->target_group = ap_pstrdup(cmd->pool, arg2); return NULL; }
/* * The next group of routines are used to capture the path to the * OpenSSOAgentBootstrap.properties file and the directory where the shared libraries * needed by the DSAME agent are stored during module configuration. */ static const char *set_properties_file(cmd_parms *cmd, agent_config_rec_t *config_rec_ptr, const char *arg) { config_rec_ptr->properties_file = ap_pstrdup(cmd->pool, arg); return NULL; }
static const char *add_custom_log(cmd_parms *cmd, void *dummy, char *fn, char *fmt, char *envclause) { const char *err_string = NULL; multi_log_state *mls = ap_get_module_config(cmd->server->module_config, &config_log_module); config_log_state *cls; cls = (config_log_state *) ap_push_array(mls->config_logs); cls->condition_var = NULL; if (envclause != NULL) { if (strncasecmp(envclause, "env=", 4) != 0) { return "error in condition clause"; } if ((envclause[4] == '\0') || ((envclause[4] == '!') && (envclause[5] == '\0'))) { return "missing environment variable name"; } cls->condition_var = ap_pstrdup(cmd->pool, &envclause[4]); } cls->fname = fn; cls->format_string = fmt; if (fmt == NULL) { cls->format = NULL; } else { cls->format = parse_log_string(cmd->pool, fmt, &err_string); } cls->log_fd = -1; return err_string; }
/** * Handler for "AddClientEncoding" directive. * * This registers regex pattern of UserAgent: header and expected * encoding(s) from that useragent. */ static const char * add_client_encoding(cmd_parms *cmd, encoding_config *conf, char *args) { array_header *encs; char *arg; LOG(APLOG_DEBUG, cmd->server, "add_client_encoding: entered"); LOG(APLOG_DEBUG, cmd->server, "add_client_encoding: args == %s", args); if (! cmd->path) { conf = ap_get_module_config(cmd->server->module_config, &encoding_module); } encs = ap_make_array(cmd->pool, 1, sizeof(void *)); /* register useragent with UserAgent: pattern */ if (*args && (arg = ap_getword_conf_nc(cmd->pool, &args))) { LOG(APLOG_DEBUG, cmd->server, "add_client_encoding: agent: %s", arg); *(void **)ap_push_array(conf->client_encoding) = ap_pregcomp(cmd->pool, arg, REG_EXTENDED|REG_ICASE|REG_NOSUB); } /* register list of possible encodings from above useragent */ while (*args && (arg = ap_getword_conf_nc(cmd->pool, &args))) { LOG(APLOG_DEBUG, cmd->server, "add_client_encoding: encname: %s", arg); *(void **)ap_push_array(encs) = ap_pstrdup(cmd->pool, arg); } *(void **)ap_push_array(conf->client_encoding) = encs; return NULL; }
static const char *add_icon(cmd_parms *cmd, void *d, char *icon, char *to) { char *iconbak = ap_pstrdup(cmd->pool, icon); if (icon[0] == '(') { char *alt; char *cl = strchr(iconbak, ')'); if (cl == NULL) { return "missing closing paren"; } alt = ap_getword_nc(cmd->pool, &iconbak, ','); *cl = '\0'; /* Lose closing paren */ add_alt(cmd, d, &alt[1], to); } if (cmd->info == BY_PATH) { if (!strcmp(to, "**DIRECTORY**")) { to = "^^DIRECTORY^^"; } } if (cmd->info == BY_ENCODING) { ap_str_tolower(to); } push_item(((autoindex_config_rec *) d)->icon_list, cmd->info, to, cmd->path, iconbak); return NULL; }
static PyObject * req_get_all_dirs(requestobject *self, PyObject *args) { table *all; py_dir_config *conf = (py_dir_config *) ap_get_module_config(self->request_rec->per_dir_config, &python_module); all = ap_copy_table(self->request_rec->pool, conf->dirs); if (ap_table_get(self->request_rec->notes, "py_more_directives")) { array_header *ah = ap_table_elts(self->request_rec->notes); table_entry *elts = (table_entry *)ah->elts; int i = ah->nelts; while (i--) { if (elts[i].key) { /* chop off _dir */ char *s = ap_pstrdup(self->request_rec->pool, elts[i].key); if (valid_handler(s)) { s[strlen(s)-4] = 0; ap_table_set(all, s, elts[i].val); } } } } return MpTable_FromTable(all); }
static char *get_hash(request_rec *r, char *user, char *auth_pwfile) { configfile_t *f; char l[MAX_STRING_LEN]; const char *rpw; char *w, *x; if (!(f = ap_pcfg_openfile(r->pool, auth_pwfile))) { ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "Could not open password file: %s", auth_pwfile); return NULL; } while (!(ap_cfg_getline(l, MAX_STRING_LEN, f))) { if ((l[0] == '#') || (!l[0])) continue; rpw = l; w = ap_getword(r->pool, &rpw, ':'); x = ap_getword(r->pool, &rpw, ':'); if (x && w && !strcmp(user, w) && !strcmp(ap_auth_name(r), x)) { ap_cfg_closefile(f); return ap_pstrdup(r->pool, rpw); } } ap_cfg_closefile(f); return NULL; }
/* * This function gets called to merge two per-server configuration * records. This is typically done to cope with things like virtual hosts and * the default server configuration The routine has the responsibility of * creating a new record and merging the contents of the other two into it * appropriately. If the module doesn't declare a merge routine, the more * specific existing record is used exclusively. * * The routine MUST NOT modify any of its arguments! * * The return value is a pointer to the created module-specific structure * containing the merged values. */ static void *example_merge_server_config(pool *p, void *server1_conf, void *server2_conf) { excfg *merged_config = (excfg *) ap_pcalloc(p, sizeof(excfg)); excfg *s1conf = (excfg *) server1_conf; excfg *s2conf = (excfg *) server2_conf; char *note; /* * Our inheritance rules are our own, and part of our module's semantics. * Basically, just note whence we came. */ merged_config->cmode = (s1conf->cmode == s2conf->cmode) ? s1conf->cmode : CONFIG_MODE_COMBO; merged_config->local = s2conf->local; merged_config->congenital = (s1conf->congenital | s1conf->local); merged_config->loc = ap_pstrdup(p, s2conf->loc); /* * Trace our call, including what we were asked to merge. */ note = ap_pstrcat(p, "example_merge_server_config(\"", s1conf->loc, "\",\"", s2conf->loc, "\")", NULL); trace_add(NULL, NULL, merged_config, note); return (void *) merged_config; }
API_EXPORT(piped_log *) ap_open_piped_log (pool *p, const char *program) { piped_log *pl; pl = ap_palloc (p, sizeof (*pl)); pl->p = p; pl->program = ap_pstrdup (p, program); pl->pid = -1; ap_block_alarms (); if (pipe (pl->fds) == -1) { int save_errno = errno; ap_unblock_alarms(); errno = save_errno; return NULL; } ap_register_cleanup (p, pl, piped_log_cleanup, piped_log_cleanup_for_exec); if (piped_log_spawn (pl) == -1) { int save_errno = errno; ap_kill_cleanup (p, pl, piped_log_cleanup); close (pl->fds[0]); close (pl->fds[1]); ap_unblock_alarms (); errno = save_errno; return NULL; } ap_unblock_alarms (); return pl; }
static const char *log_request_time(request_rec *r, char *a) { int timz; struct tm *t; char tstr[MAX_STRING_LEN]; t = ap_get_gmtoff(&timz); if (a && *a) { /* Custom format */ strftime(tstr, MAX_STRING_LEN, a, t); } else { /* CLF format */ char sign = (timz < 0 ? '-' : '+'); if (timz < 0) { timz = -timz; } ap_snprintf(tstr, sizeof(tstr), "[%02d/%s/%d:%02d:%02d:%02d %c%.2d%.2d]", t->tm_mday, ap_month_snames[t->tm_mon], t->tm_year+1900, t->tm_hour, t->tm_min, t->tm_sec, sign, timz / 60, timz % 60); } return ap_pstrdup(r->pool, tstr); }
/** * Return the list of encoding(s) (defaults to (list "UTF-8")) * which named client is expected to send. * * @param r Apache request object structure * @param encmap Table of UA-to-encoding(s) * @param lookup Name of the useragent to look for */ static array_header * get_client_encoding(request_rec *r, array_header *encmap, const char *lookup) { void **list = (void **)encmap->elts; array_header *encs = ap_make_array(r->pool, 1, sizeof(char *)); int i; LOG(APLOG_DEBUG, r->server, "get_client_encoding: entered"); /* push UTF-8 as the first candidate of expected encoding */ *((char **)ap_push_array(encs)) = ap_pstrdup(r->pool, "UTF-8"); if (! lookup) return encs; LOG(APLOG_DEBUG, r->server, "get_client_encoding: lookup == %s", lookup); for (i = 0 ; i < encmap->nelts ; i += 2) { if (ap_regexec((regex_t *)list[i], lookup, 0, NULL, 0) == 0) { LOG(APLOG_DEBUG, r->server, "get_client_encoding: entry found"); ap_array_cat(encs, (array_header *)list[i + 1]); return encs; } } LOG(APLOG_DEBUG, r->server, "get_client_encoding: entry not found"); return encs; }
/* Create a copy of a "struct hostent" record; it was presumably returned * from a call to gethostbyname() and lives in static storage. * By creating a copy we can tuck it away for later use. */ API_EXPORT(struct hostent *) ap_pduphostent(pool *p, const struct hostent *hp) { struct hostent *newent; char **ptrs; char **aliases; struct in_addr *addrs; int i = 0, j = 0; if (hp == NULL) return NULL; /* Count number of alias entries */ if (hp->h_aliases != NULL) for (; hp->h_aliases[j] != NULL; ++j) continue; /* Count number of in_addr entries */ if (hp->h_addr_list != NULL) for (; hp->h_addr_list[i] != NULL; ++i) continue; /* Allocate hostent structure, alias ptrs, addr ptrs, addrs */ newent = (struct hostent *) ap_palloc(p, sizeof(*hp)); aliases = (char **) ap_palloc(p, (j + 1) * sizeof(char *)); ptrs = (char **) ap_palloc(p, (i + 1) * sizeof(char *)); addrs = (struct in_addr *) ap_palloc(p, (i + 1) * sizeof(struct in_addr)); *newent = *hp; newent->h_name = ap_pstrdup(p, hp->h_name); newent->h_aliases = aliases; newent->h_addr_list = (char **) ptrs; /* Copy Alias Names: */ for (j = 0; hp->h_aliases[j] != NULL; ++j) { aliases[j] = ap_pstrdup(p, hp->h_aliases[j]); } aliases[j] = NULL; /* Copy address entries */ for (i = 0; hp->h_addr_list[i] != NULL; ++i) { ptrs[i] = (char *) &addrs[i]; addrs[i] = *(struct in_addr *) hp->h_addr_list[i]; } ptrs[i] = NULL; return newent; }
static const char *suphp_handle_cmd_config(cmd_parms *cmd, void *mconfig, const char *arg) { suphp_conf *cfg = (suphp_conf *) mconfig; cfg->php_config = ap_pstrdup(cmd->pool, arg); return NULL; }
static const char *suphp_handle_cmd_phppath(cmd_parms *cmd, void* mconfig, const char *arg) { suphp_conf *cfg; cfg = (suphp_conf *) ap_get_module_config(cmd->server->module_config, &suphp_module); cfg->php_path = ap_pstrdup(cmd->pool, arg); return NULL; }
static int request_setattr(requestobject *self, char *name, PyObject *value) { if (value == NULL) { PyErr_SetString(PyExc_AttributeError, "can't delete request attributes"); return -1; } else if (strcmp(name, "content_type") == 0) { self->request_rec->content_type = ap_pstrdup(self->request_rec->pool, PyString_AsString(value)); self->content_type_set = 1; return 0; } else if (strcmp(name, "filename") == 0) { self->request_rec->filename = ap_pstrdup(self->request_rec->pool, PyString_AsString(value)); return 0; } else if (strcmp(name, "hstack") == 0) { self->hstack = ap_pstrdup(self->request_rec->pool, PyString_AsString(value)); return 0; } else if (strcmp(name, "_Request") == 0) { /* it's ok to assign None */ if (value == Py_None) { Py_XDECREF(self->Request); self->Request = NULL; return 0; } /* but anything else has to be an instance */ if (! PyInstance_Check(value)) { PyErr_SetString(PyExc_AttributeError, "special attribute _Request must be an instance"); return -1; } Py_INCREF(value); self->Request = value; return 0; } else return PyMember_Set((char *)self->request_rec, request_memberlist, name, value); }
int td_set_val(ap_pool *pool, twodim *td, int row, int col, char* str ) { if (!td || !td->a ) return 0; if ( row < 0 || col < 0 || row >= td->r || col >= td->c ) return 0; if (str) { td->a[ row*td->c + col] = (char*) ap_pstrdup(pool, str); } return 1; }
/** * Handler for "SetServerEncoding" directive. */ static const char * set_server_encoding(cmd_parms *cmd, encoding_config *conf, char *arg) { LOG(APLOG_DEBUG, cmd->server, "set_server_encoding: entered"); LOG(APLOG_DEBUG, cmd->server, "set_server_encoding: arg == %s", arg); if (! cmd->path) { conf = ap_get_module_config(cmd->server->module_config, &encoding_module); } conf->server_encoding = ap_pstrdup(cmd->pool, arg); return NULL; }
bool qEnvApache::SetHeader(const char *str, const char *val) { if (!IsFlushed()) { ap_table_set(GetRequest()->headers_out, str, val); if (!stricmp(str,"content-type")) { myReq->content_type = ap_pstrdup(myReq->pool, val); } else if (!stricmp(str,"set-cookie")) ap_table_set(GetRequest()->err_headers_out, str, val); return true; } return false; }
/******************************************************************************* * Configure uid, gid, user, group, username for wrapper. */ const char * fcgi_util_fs_set_uid_n_gid(pool *p, fcgi_server *s, uid_t uid, gid_t gid) { #ifndef WIN32 struct passwd *pw; struct group *gr; if (fcgi_wrapper == NULL) return NULL; if (uid == 0 || gid == 0) { return "invalid uid or gid, see the -user and -group options"; } s->uid = uid; pw = getpwuid(uid); if (pw == NULL) { return ap_psprintf(p, "getpwuid() couldn't determine the username for uid '%ld', " "you probably need to modify the User directive: %s", (long)uid, strerror(errno)); } s->user = ap_pstrdup(p, pw->pw_name); s->username = s->user; s->gid = gid; gr = getgrgid(gid); if (gr == NULL) { return ap_psprintf(p, "getgrgid() couldn't determine the group name for gid '%ld', " "you probably need to modify the Group directive: %s", (long)gid, strerror(errno)); } s->group = ap_pstrdup(p, gr->gr_name); #endif /* !WIN32 */ return NULL; }
static void *suphp_merge_server_config(pool *p, void *base, void *overrides) { suphp_conf *parent = (suphp_conf *) base; suphp_conf *child = (suphp_conf *) overrides; suphp_conf *merged = (suphp_conf *) ap_pcalloc(p, sizeof(suphp_conf)); if (child->engine != SUPHP_ENGINE_UNDEFINED) merged->engine = child->engine; else merged->engine = parent->engine; if (child->php_path != NULL) merged->php_path = ap_pstrdup(p, child->php_path); else merged->php_path = ap_pstrdup(p, parent->php_path); #ifdef SUPHP_USE_USERGROUP if (child->target_user) merged->target_user = ap_pstrdup(p, child->target_user); else if (parent->target_user) merged->target_user = ap_pstrdup(p, parent->target_user); else merged->target_user = NULL; if (child->target_group) merged->target_group = ap_pstrdup(p, child->target_group); else if (parent->target_group) merged->target_group = ap_pstrdup(p, parent->target_group); else merged->target_group = NULL; #endif merged->handlers = ap_overlay_tables(p, child->handlers, parent->handlers); return (void *) merged; }
int suphp_source_child(void *rp, child_info *cinfo) { request_rec *r = (request_rec *) rp; suphp_conf *conf; pool *p = r->main ? r->main->pool : r->pool; char **argv, **env; table *empty_table = ap_make_table(p, 0); conf = ap_get_module_config(r->server->module_config, &suphp_module); /* We want to log output written to stderr */ ap_error_log2stderr(r->server); /* prepare argv for new process */ argv = ap_palloc(p, 4 * sizeof(char *)); argv[0] = ap_pstrdup(p, conf->php_path); argv[1] = "-s"; argv[2] = ap_pstrdup(p, r->filename); argv[3] = NULL; /* prepare environment */ env = ap_create_environment(p, empty_table); /* We cannot use ap_call_exec because of the interference with suExec */ /* So we do everything ourselves */ /* mandatory cleanup before execution */ ap_cleanup_for_exec(); execve(ap_pstrdup(p, conf->php_path), argv, env); /* We are still here? Okay - exec failed */ ap_log_error(APLOG_MARK, APLOG_ERR, NULL, "exec of %s failed", conf->php_path); exit(0); /* NOT REACHED */ return (0); }
static const char *add_desc(cmd_parms *cmd, void *d, char *desc, char *to) { autoindex_config_rec *dcfg = (autoindex_config_rec *) d; ai_desc_t *desc_entry; char *prefix = ""; desc_entry = (ai_desc_t *) ap_push_array(dcfg->desc_list); desc_entry->full_path = (strchr(to, '/') == NULL) ? 0 : 1; desc_entry->wildcards = (WILDCARDS_REQUIRED || desc_entry->full_path || ap_is_fnmatch(to)); if (desc_entry->wildcards) { prefix = desc_entry->full_path ? "*/" : "*"; desc_entry->pattern = ap_pstrcat(dcfg->desc_list->pool, prefix, to, "*", NULL); } else { desc_entry->pattern = ap_pstrdup(dcfg->desc_list->pool, to); } desc_entry->description = ap_pstrdup(dcfg->desc_list->pool, desc); return NULL; }
API_EXPORT(char *)ap_os_case_canonical_filename(pool *pPool, const char *szFile) { char *buf; char buf2[CCHMAXPATH]; int rc, len; char *pos; /* Remove trailing slash unless it's a root directory */ len = strlen(szFile); buf = ap_pstrndup(pPool, szFile, len); if (len > 3 && buf[len-1] == '/') buf[--len] = 0; if (buf[0] == '/' && buf[1] == '/') { /* A UNC path */ if (strchr(buf+2, '/') == NULL) { /* Allow // or //server */ return ap_pstrdup(pPool, buf); } } rc = DosQueryPathInfo(buf, FIL_QUERYFULLNAME, buf2, sizeof(buf2)); if (rc) { if ( rc != ERROR_INVALID_NAME ) { ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, NULL, "OS/2 error %d for file %s", rc, szFile); } return ap_pstrdup(pPool, szFile); } /* Switch backslashes to forward */ for (pos=buf2; *pos; pos++) if (*pos == '\\') *pos = '/'; return ap_pstrdup(pPool, buf2); }
static int change_remote_ip(request_rec *r) { const char *fwdvalue; char *val; removeip_server_cfg *cfg = (removeip_server_cfg *)ap_get_module_config(r->server->module_config, &removeip_module); if (!cfg->enable) return DECLINED; r->connection->remote_ip = ap_pstrdup(r->connection->pool, "localhost"); r->connection->remote_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); return DECLINED; }
static int asis_handler(request_rec *r) { FILE *f; const char *location; r->allowed |= (1 << M_GET); if (r->method_number != M_GET) return DECLINED; if (r->finfo.st_mode == 0) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "File does not exist: %s", r->filename); return NOT_FOUND; } f = ap_pfopen(r->pool, r->filename, "r"); if (f == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "file permissions deny server access: %s", r->filename); return FORBIDDEN; } ap_scan_script_header_err(r, f, NULL); location = ap_table_get(r->headers_out, "Location"); if (location && location[0] == '/' && ((r->status == HTTP_OK) || ap_is_HTTP_REDIRECT(r->status))) { ap_pfclose(r->pool, f); /* Internal redirect -- fake-up a pseudo-request */ r->status = HTTP_OK; /* This redirect needs to be a GET no matter what the original * method was. */ r->method = ap_pstrdup(r->pool, "GET"); r->method_number = M_GET; ap_internal_redirect_handler(location, r); return OK; } ap_send_http_header(r); if (!r->header_only) ap_send_fd(f, r); ap_pfclose(r->pool, f); return OK; }
/* It stores the account name for later use */ const char *os_set_account(pool *p, const char *account) { char account_temp[ACCT_LEN+1]; ap_cpystrn(account_temp, account, sizeof account_temp); /* Make account all upper case */ ap_str_toupper(account_temp); /* Pad to length 8 */ ap_pad(account_temp, sizeof account_temp, ' '); bs2000_account = ap_pstrdup(p, account_temp); return NULL; }
/* * This really wants to be a nested function * but C is too feeble to support them. */ static ap_inline void vhost_alias_checkspace(request_rec *r, char *buf, char **pdest, int size) { /* XXX: what if size > HUGE_STRING_LEN? */ if (*pdest + size > buf + HUGE_STRING_LEN) { **pdest = '\0'; if (r->filename) { r->filename = ap_pstrcat(r->pool, r->filename, buf, NULL); } else { r->filename = ap_pstrdup(r->pool, buf); } *pdest = buf; } }
/* ** dav_fs_remove_locknull_member: Removes filename from the locknull list ** for directory path. */ static dav_error * dav_fs_remove_locknull_member(pool *p, const char *filename, dav_buffer *pbuf) { dav_error *err; size_t len; size_t scanlen; char *scan; const char *scanend; char *dirpath = ap_pstrdup(p, filename); char *fname = strrchr(dirpath, '/'); int dirty = 0; if (fname != NULL) *fname++ = '\0'; else fname = dirpath; len = strlen(fname) + 1; if ((err = dav_fs_load_locknull_list(p, dirpath, pbuf)) != NULL) { /* ### add a higher level description? */ return err; } for (scan = pbuf->buf, scanend = scan + pbuf->cur_len; scan < scanend; scan += scanlen) { scanlen = strlen(scan) + 1; if (len == scanlen && memcmp(fname, scan, scanlen) == 0) { pbuf->cur_len -= scanlen; memmove(scan, scan + scanlen, scanend - (scan + scanlen)); dirty = 1; break; } } if (dirty) { if ((err = dav_fs_save_locknull_list(p, dirpath, pbuf)) != NULL) { /* ### add a higher level description? */ return err; } } return NULL; }