/* =========================================================================== = (C) Copyright 1997,1998 Michael Stenns = = = = Permission to use, copy, modify, and distribute this program for = = non-commercial use and without fee is hereby granted. = = = = This software is distributed in the hope that it will be useful, = = but WITHOUT ANY WARRANTY; without even the implied warranty of = = MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. = = = =========================================================================== = = = (C) Copyright 1991-1994 The Trustees of Indiana University = = = = Permission to use, copy, modify, and distribute this program for = = non-commercial use and without fee is hereby granted, provided that = = this copyright and permission notice appear on all copies and = = supporting documentation, the name of Indiana University not be used = = in advertising or publicity pertaining to distribution of the program = = without specific prior permission, and notice be given in supporting = = documentation that copying and distribution is by permission of = = Indiana University. = = = = Indiana University makes no representations about the suitability of = = this software for any purpose. It is provided "as is" without express = = or implied warranty. = = = =========================================================================== = = = File: = = IUPOP3_COMMANDS.C - Version 2.0 = = = = Synopsis: = = This file contains functions that implement the pop3 commands. = = = = Author: = = Michael Stenns = = Institute for Technical Chemistry, University of Hanover, Germany = = = = Authors of Version 1.8: = = Jacob Levanon & Larry Hughes = = Indiana University = = University Computing Services, Network Applications = = = = Credits: = = This software is based on the Post Office Protocol version 3, = = as implemented by the University of California at Berkeley. = = = =========================================================================== */ /************************************************************************* ** Include files **************************************************************************/ #include #include #include #include #include #include #include #include "iupop3_general.h" #include "iupop3_vms.h" #include "iupop3_global.h" #include "version.h" /* ======================================================================== */ /* Prototypes look into iupop3_general.h */ /* ======================================================================== */ /**************************************************************************** ** External Declarations ****************************************************************************/ static state_table states[] = { {auth1, "user", 1, 1, pop_user, {auth1, auth2}}, {auth2, "pass", 1, 1, pop_pass, {auth1, trans}}, {auth1, "apop", 2, 2, pop_apop, {auth1, trans}}, {auth1, "auth", 1, 1, pop_auth, {auth1, trans}}, {trans, "stat", 0, 0, pop_stat, {trans, trans}}, {trans, "list", 0, 1, pop_list, {trans, trans}}, {trans, "uidl", 0, 1, pop_uidl, {trans, trans}}, {trans, "retr", 1, 1, pop_send, {trans, trans}}, {trans, "dele", 1, 1, pop_dele, {trans, trans}}, {trans, "noop", 0, 0, pop_noop, {trans, trans}}, {trans, "top", 2, 2, pop_send, {trans, trans}}, {trans, "rset", 0, 0, pop_rset, {trans, trans}}, {trans, "last", 0, 0, pop_last, {trans, trans}}, {trans, "xtnd", 1, 99, pop_xtnd, {trans, trans}}, {trans, "quit", 0, 0, pop_updt, {halt, halt} }, {auth1, "quit", 0, 0, pop_quit, {halt, halt} }, {auth2, "quit", 0, 0, pop_quit, {halt, halt} }, {error, "quit", 0, 0, pop_quit, {halt, halt} }, {(state)0, NULL, 0, 0, NULL, {halt, halt} }, }; /***************************************************************************** ** Pop_get_command: Extract the command from an input line form a POP client *****************************************************************************/ state_table *pop_get_command (POP *p, char *mp) { state_table *s; char buf[MAXMSGLINELEN]; p->arg_count = pop_parse(p,mp); /* ** Search for the POP command in the command/state table */ for (s = states; s->command; s++) { /* ** current operating state? */ if ((strcmp(s->command, p->pop_command) == 0) && (s->ValidCurrentState == p->CurrentState)) { /* ** Make sure command line is syntacticly correct */ if (p->arg_count < s->min_args) return((state_table *)pop_msg(p, POP_FAILURE, "Too few arguments for the %s command.",p->pop_command)); if (p->arg_count > s->max_args) return((state_table *)pop_msg(p, POP_FAILURE, "Too many arguments for the %s command.",p->pop_command)); /* ** Return a pointer to command in the command/state table */ return (s); } } pop_log (LOG_ERROR, p, "unknown command: \"%.20s\"", p->pop_command); pop_msg (p, POP_FAILURE, "Unknown command: \"%.20s\".", p->pop_command); return NULL; } /************************************************************************** ** Pop_dele: Delete a message from the VMS mail file ***************************************************************************/ int pop_dele (POP *p) { Message *mp; int msg_num; msg_num = atoi(p->pop_args[1]); /* ** Is message requested for deletetion out of range? */ if ((msg_num < 1) || (msg_num > p->msg_count)) { pop_log(LOG_ERROR, p, "Message %d does not exist.", msg_num); return(pop_msg(p,POP_FAILURE,"Message %d does not exist.",msg_num)); } /* ** Get a pointer to the message in the message list */ mp = &(p->mptr[msg_num-1]); /* ** If the message already flagged for deletion, signal error */ if (mp->msg_flags & MSG_DEL_FLAG) { pop_log(LOG_ERROR, p, "Message %d has already been deleted.", msg_num); return(pop_msg(p,POP_FAILURE,"Message %d has already been deleted.", msg_num)); } mp->msg_flags |= MSG_DEL_FLAG; p->msgs_deleted++; p->bytes_deleted += mp->length; if (p->last_msg < msg_num) p->last_msg = msg_num; return(pop_msg (p,POP_SUCCESS,"Message %d has been deleted.",msg_num)); } /************************************************************************ ** Pop_last: Return the last message accessed *************************************************************************/ int pop_last(POP *p) { return(pop_msg (p, POP_SUCCESS, " %d was last message accessed", p->last_msg)); } /************************************************************************ ** Pop_list: List the contents of the VMS mail folder *************************************************************************/ int pop_list (POP *p) { Message *mp; int i; int msg_num; if (!(p->retrieve.flags & RETR_IN_PROGRESS_FLAG) && (p->arg_count > 0)) { msg_num = atoi(p->pop_args[1]); /* ** Is the requested message out of range? */ if ((msg_num < 1) || (msg_num > p->msg_count)) return(pop_msg (p,POP_FAILURE, "Message %d does not exist.",msg_num)); /* ** Get a pointer to the message in the message list */ mp = &p->mptr[msg_num-1]; /* ** Is the message already flagged for deletion? */ if (mp->msg_flags & (MSG_DEL_FLAG + MSG_UNAVAILABLE_FLAG)) { if (mp->msg_flags & MSG_DEL_FLAG) return pop_msg (p,POP_FAILURE, "Message %d has been deleted.",msg_num); else return pop_msg (p,POP_FAILURE, "Message %d is unavailable.",msg_num); } /* ** Display message information */ return(pop_msg(p,POP_SUCCESS,"%u %u", msg_num, mp->length)); } /* ** Message number was not supplied - display the entire list of messages */ /* ** Walk through the message information list. ** Don't show messages marked as deleted. */ if (!allocate_send_buffer (p)) return POP_FAILURE; if (!(p->retrieve.flags & RETR_IN_PROGRESS_FLAG)) { pop_msg(p,POP_SUCCESS, "%u messages (%u octets)", p->msg_count - p->msgs_deleted, p->newmail_size - p->bytes_deleted); p->retrieve.lines_sent = 0; p->retrieve.bufsize = 0; p->retrieve.flags |= RETR_IN_PROGRESS_FLAG; } p->retrieve.send_bufsize = 0; for (mp = &p->mptr[p->retrieve.lines_sent]; p->retrieve.lines_sent < p->msg_count; p->retrieve.lines_sent++, mp++) { if (!(mp->msg_flags & (MSG_DEL_FLAG + MSG_UNAVAILABLE_FLAG))) { if (!netbprintf(p,"%u %u\r\n",mp->number,mp->length)) break; } } if (p->retrieve.lines_sent == p->msg_count) { netb_end (p); p->retrieve.function = NULL; } else { p->retrieve.function = pop_list; } netwrite_async (p, p->retrieve.send_buffer, p->retrieve.send_bufsize); return POP_SUCCESS; } /************************************************************************ ** Pop_uidl: give unique-id to messages ** M. Stenns 2-jun-1996 *************************************************************************/ int pop_uidl (POP *p) { Message *mp; int i; int msg_num; unsigned int *bindate; if (!(p->retrieve.flags & RETR_IN_PROGRESS_FLAG) && (p->arg_count > 0)) { msg_num = atoi(p->pop_args[1]); /* ** Is the requested message out of range? */ if ((msg_num < 1) || (msg_num > p->msg_count)) return(pop_msg (p,POP_FAILURE, "Message %d does not exist.",msg_num)); /* ** Get a pointer to the message in the message list */ mp = &p->mptr[msg_num-1]; /* ** Is the message already flagged for deletion? */ if (mp->msg_flags & (MSG_DEL_FLAG + MSG_UNAVAILABLE_FLAG)) { if (mp->msg_flags & MSG_DEL_FLAG) return pop_msg (p,POP_FAILURE, "Message %d has been deleted.",msg_num); else return pop_msg (p,POP_FAILURE, "Message %d is unavailable.",msg_num); } /* ** Display message information */ bindate = (unsigned int *) &mp->bindate; return(pop_msg(p,POP_SUCCESS,"%u %s_%u%u", mp->number, p->user , bindate[1], bindate[0])); } /* ** Message number was not supplied - display the entire list of messages */ /* ** Walk through the message information list. ** Don't show messages marked as deleted. */ if (!allocate_send_buffer (p)) return POP_FAILURE; if (!(p->retrieve.flags & RETR_IN_PROGRESS_FLAG)) { pop_msg(p,POP_SUCCESS," "); p->retrieve.lines_sent = 0; p->retrieve.bufsize = 0; p->retrieve.flags |= RETR_IN_PROGRESS_FLAG; } p->retrieve.send_bufsize = 0; for (mp = &p->mptr[p->retrieve.lines_sent]; p->retrieve.lines_sent < p->msg_count; p->retrieve.lines_sent++, mp++) { if (!(mp->msg_flags & (MSG_DEL_FLAG + MSG_UNAVAILABLE_FLAG))) { bindate = (unsigned int *) &mp->bindate; if (!netbprintf (p,"%u %s_%u%u\r\n", mp->number, p->user , bindate[1], bindate[0])) break; } } if (p->retrieve.lines_sent == p->msg_count) { netb_end (p); p->retrieve.function = NULL; } else { p->retrieve.function = pop_uidl; } netwrite_async (p, p->retrieve.send_buffer, p->retrieve.send_bufsize); return POP_SUCCESS; } /***************************************************************************** ** Pop_parse: Parse an input line from a POP client ** into null-delimited tokens ** Return: the number of tokens extracted minus the command itself *****************************************************************************/ int pop_parse (POP *p, char *buf) { char *tok; int numtok; /* ** Loop through the POP command array */ for (numtok = 0; numtok < MAXARGCOUNT; numtok++) p->pop_args[numtok] = ""; for (tok = strtok(buf, " \r\n"), numtok = 0; tok && (numtok < MAXARGCOUNT); tok=strtok(NULL, " \r\n"), numtok++) { p->pop_args[numtok] = tok; } lower(p->pop_command); return numtok - 1; } /*************************************************************************** ** Pop_pass - Verify user's password in the VMS authorization file ** Return - POP_SUCCESS or POP_FAILURE ***************************************************************************/ int pop_pass (POP *p) { int status; char password[33],username[13]; short passlen,userlen; strncpy(password, p->pop_args[1], sizeof (password) - 1); strncpy(username, p->user, sizeof(username) - 1); passlen = strnlen(password,sizeof(password) - 1); password[passlen] = '\0'; userlen = strnlen(username,sizeof(username) - 1); username[userlen] = '\0'; if ( !(1 & (status = rad_auth (username,userlen,password,passlen))) ) pop_log (LOG_ERROR, p, "rad_auth: %s",vms_message(status)); else status = POP_SUCCESS; return pop_authcheck (p, status); } /*************************************************************************** ** Pop_authcheck - make final checks ** Return - POP_SUCCESS or POP_FAILURE ***************************************************************************/ int pop_authcheck (POP *p, int status) { switch (status) { case 0: authentication_failures++; p->authentication_attempts++; pop_log(LOG_ERROR, p, "user account \"%s\" not accessible.", p->user); return(pop_msg(p,POP_FAILURE, "user account \"%s\" not accessible.", p->user)); break; case 1: status = mail_open_user_context(p); if (vms_error(status)) return(pop_msg(p,POP_FAILURE,"Opening mail user context: %s", vms_message(status))); else { status = mail_user_info(p); if (vms_error(status)) return(pop_msg(p,POP_FAILURE,"Getting mail user info: %s", vms_message(status))); else { status = mail_open_file_context(p); if (p->has_no_mail_file) { pop_msg(p,POP_SUCCESS,"Username/password combination ok"); p->folder_name = "empty"; return(POP_SUCCESS); } else if (vms_error(status)) return(pop_msg(p,POP_FAILURE,"Opening mail file context: %s", vms_message(status))); else { status = mail_open_message_context(p); if (vms_error(status)) return(pop_msg(p,POP_FAILURE, "Opening mail message context: %s", vms_message(status))); else { int messages = 0; if (!p->newmail_selected) { p->folder_name = NEWMAIL_FOLDER; status = mail_folder_select(p,NEWMAIL_FOLDER,&messages); if (use_mail_folder) { if (vms_error(status)) return(pop_msg(p,POP_FAILURE, "Selecting mail folder: %s",vms_message(status))); /* * if compiled with the MAIL_FOLDER option, IUPOP3 moves * all new messages into the mail folder and presents the * mail folder to the client instead of the newmail folder. */ if (messages) pop_log (LOG_DEBUG, p, "moving %d messages to the mail folder", messages); status = mail_message_new_count (p, messages); while ((status == SS$_NORMAL) && messages) { status = mail_message_move (p, MAIL_FOLDER, messages); messages--; } status = mail_folder_select (p, MAIL_FOLDER, &messages); p->folder_name = MAIL_FOLDER; } /* limit the number of newmail messages per connection */ p->msg_count = Min(messages, max_messages); p->msgs_deleted = 0; } if (vms_error(status)) return(pop_msg(p,POP_FAILURE, "Selecting mail folder: %s", vms_message(status))); else { double cpu_time = 0.0; cpu_time = get_cpu (); status = pop_build_info(p); cpu_time = get_cpu () - cpu_time; pop_log (LOG_DEBUG,p,"init. time %8.4f sec",cpu_time); if (status&1) return pop_msg(p,POP_SUCCESS,"Username/password combination ok"); else return pop_msg(p,POP_FAILURE,"%s",vms_message(status)); } } } } } break; default: pop_log(LOG_ERROR, p, "error determining password validity"); return(pop_msg(p,POP_FAILURE, "Error determining password validity.")); break; } } /*************************************************************************** ** Pop_quit: Disconnect from server ** Return: Currently assumes POP_SUCCESS always... ****************************************************************************/ int pop_quit(POP *p) { int status = POP_SUCCESS; if (p->connected) { status = pop_msg(p, POP_SUCCESS, "IUPOP3 server at %s signing off.", myhostname); p->connected = FALSE; } return status; } /*************************************************************************** ** Pop_rset: Undelete all messages flagged for deletion ** Return: Message count and Total nuber of bytes ****************************************************************************/ int pop_rset (POP *p) { Message *mp; int i; for (i = p->msg_count, mp = p->mptr; i > 0; i--, mp++) { if (mp->msg_flags & MSG_DEL_FLAG) mp->msg_flags ^= MSG_DEL_FLAG; if (mp->msg_flags & MSG_RETR_FLAG) mp->msg_flags ^= MSG_RETR_FLAG; } p->msgs_deleted = 0; p->bytes_deleted = 0; p->last_msg = 0; /* Reset the last-message-access flag */ return (pop_msg(p,POP_SUCCESS,"%s Folder has %u messages (%u octets)", p->folder_name,p->msg_count, p->newmail_size)); } /**************************************************************************** ** Pop_send: Send the header and a specified number of lines ** from a mail message to a POP client. ** Return: POP_SUCCESS or POP_FAILURE ****************************************************************************/ int pop_send (POP *p) { Message *mp; int msg_num=0; int msg_lines=0; int status; p->retrieve.flags = 0; msg_num = atoi(p->pop_args[1]); /* ** Is requested message out of range? */ if ((msg_num < 1) || (msg_num > p->msg_count)) { pop_log(LOG_ERROR, p, "Message %d does not exist.",msg_num); return(pop_msg(p,POP_FAILURE,"Message %d does not exist.",msg_num)); } /* ** Get a pointer to the message in the message list */ mp = &p->mptr[msg_num-1]; if (mp->msg_flags & (MSG_DEL_FLAG + MSG_UNAVAILABLE_FLAG)) { /* The message is flagged for deletion */ if (mp->msg_flags & MSG_DEL_FLAG) { pop_log(LOG_ERROR, p, "Message %d has been deleted.", msg_num); status = pop_msg(p,POP_FAILURE,"Message %d has been deleted.",msg_num); } else { pop_log(LOG_ERROR, p, "Message %d is out of synch.", msg_num); status = pop_msg(p,POP_FAILURE,"Message %d temporarily unavailable.",msg_num); } return status; } /* ** If this is a TOP command, get the number of lines to send */ if (strcmp(p->pop_command,"top") == 0) { msg_lines = abs(atoi(p->pop_args[2])); if (msg_lines >= mp->lines) /* Sanity check */ msg_lines = mp->lines - 1; } else /* Assume RETR (retrieve) was issued */ { p->retrieve.flags |= RETR_IS_RETRIEVE_FLAG; msg_lines = mp->lines - 1; } /* ** Retrieve all the lines for this message. ** VMS MAIL failure logged in the log file */ p->retrieve.send_bufsize = 0; p->retrieve.lines_sent = 0; p->retrieve.mp = mp; p->retrieve.message_lines = msg_lines; status = mail_retrieve_message(p); return POP_SUCCESS; } /*************************************************************************** ** Pop_stat: Summary of NEW (unread) mail in the NEWMAIL folder ** Return: Message count and Total number of bytes for NEWMAIL folder. ****************************************************************************/ int pop_stat (POP *p) { int messages, bytes; messages = p->msg_count - p->msgs_deleted; bytes = p->newmail_size - p->bytes_deleted; pop_log(LOG_THREAD, p, "%u messages, %u bytes", messages, bytes); return pop_msg(p,POP_SUCCESS, "%u %u", messages, bytes); } /*************************************************************************** ** Pop_noop: no action ** Return: Success Message. ****************************************************************************/ int pop_noop (POP *p) { return pop_msg(p,POP_SUCCESS,"no action taken"); } /************************************************************************** ** Pop_updt: Update user's mail file. ** ** Return: POP_SUCCESS ***************************************************************************/ int pop_updt (POP *p) { int status; pop_log(LOG_DEBUG, p, "updating %s\'s mail file", p->user); /* * tell the client to sign off */ status = pop_quit(p); p->retrieve.send_count = 0; /* used in update_maildrop() */ p->retrieve.message_lines = p->msg_count; /* used in update_maildrop() */ p->attn_state = ATTN_UPDATE; return POP_SUCCESS; } /************************************************************************* ** Pop_user: verify existance of user in the authorization file ** currently only a dummy routine, the real checks are done ** in pop_pass() ** Return: POP_SUCCESS or POP_FAILURE *************************************************************************/ int pop_user(POP *p) { strncpy(p->user, p->pop_args[1], sizeof p->user); return(pop_msg(p,POP_SUCCESS,"Password required for \"%s\"",lower(p->user))); } /************************************************************************* ** Pop_auth: POP3 AUTHentication command ** Currently none of the authentifications mechanism of ** specified in RFC1734 are supported. ** If anybody wishes to implement KERBEROS_V4, this would ** be the right place. ** Return: POP_FAILURE *************************************************************************/ int pop_auth (POP *p) { pop_log (LOG_THREAD,p,"Auth method %s not supported",upper(p->pop_args[1])); return pop_msg(p, POP_FAILURE, "Unrecognized authentication type"); } /************************************************************************* ** Pop_apop: verify existance of user in the authorization file ** and check the password against the md5 digest sent by client ** Return: POP_SUCCESS or POP_FAILURE *************************************************************************/ int pop_apop (POP *p) { return POP_FAILURE; } /*************************************************************************** ** Pop_xtnd_client - acknowledge client type and version ****************************************************************************/ int pop_xtnd_client (POP *p) { pop_msg(p, POP_SUCCESS, "thanks"); return(POP_SUCCESS); } /*************************************************************************** ** Pop_xtnd_shutdown - prepare to shutdown server ****************************************************************************/ int pop_xtnd_shutdown (POP *p) { pop_log (LOG_INFO, p, "shutdown requested"); pop_msg (p, POP_SUCCESS, "will shutdown when no threads are connected"); server_shutdown = SS$_NORMAL; return POP_SUCCESS; } /*************************************************************************** ** Pop_xtnd_restart - prepare to restart server ****************************************************************************/ int pop_xtnd_restart (POP *p) { pop_log (LOG_INFO, p, "restart requested"); pop_msg (p, POP_SUCCESS, "will restart when no threads are connected"); server_shutdown = SS$_OPRABORT; return POP_SUCCESS; } /*************************************************************************** ** Pop_xtnd_netstats - show pop server internals statistics ****************************************************************************/ int pop_xtnd_netstats (POP *p) { int status = SS$_NORMAL; double cpu; pop_msg (p, POP_SUCCESS, "Statistics follow"); netbprintf(p, " Current Time : %s\r\n", logtime(NULL)); netbprintf(p, " Start Time : %s\r\n", logtime(&server_start_time)); cpu = get_cpu(); netbprintf(p, " CPU Seconds : %-8.2f (%d mins, %d secs)\r\n", cpu, (int)(cpu/60), (int)((int)cpu%60)); netbprintf(p, " Main loop count : %lu\r\n", main_loop_count); netbprintf(p, " Hibernation count : %lu\r\n", hibernation_count); netbprintf(p, " Connections accepted : %d\r\n", total_threads); netbprintf(p, " Commands processed : %lu\r\n", commands_processed); netbprintf(p, " Network bytes written: %lu\r\n", net_bytes_written); netbprintf(p, " Network bytes read : %lu\r\n", net_bytes_read); netbprintf(p, " Network write count : %lu\r\n", net_write_count); netbprintf(p, " Write AST count : %d\r\n", blocked_count); netbprintf(p, " Network read count : %lu\r\n", net_read_count); netb_end (p); p->retrieve.function = NULL; /* do not return to this routine */ netwrite_async (p, p->retrieve.send_buffer, p->retrieve.send_bufsize); return POP_SUCCESS; } /*************************************************************************** ** Pop_xtnd_stats - show pop server statistics ****************************************************************************/ int pop_xtnd_stats (POP *p) { float cpu; int count; POP *pop = get_pop_array (); POP * pp; pop_msg(p, POP_SUCCESS, "Statistics follow"); netbprintf(p, " Version Number : %s\r\n", VERSION); switch (master_log_level) { case LOG_ERROR: netbprintf(p, " Logging Level : ERROR\r\n"); break; case LOG_INFO: netbprintf(p, " Logging Level : INFO\r\n"); break; case LOG_THREAD: netbprintf(p, " Logging Level : THREAD\r\n"); break; case LOG_DEBUG: netbprintf(p, " Logging Level : DEBUG\r\n"); break; } netbprintf(p, " Current Time : %s\r\n", logtime(NULL)); netbprintf(p, " Start Time : %s\r\n", logtime(&server_start_time)); cpu = get_cpu(); netbprintf(p, " CPU Seconds : %-8.2f (%d mins, %d secs)\r\n", cpu, (int)(cpu/60), (int)((int)cpu%60)); netbprintf(p, " Current Threads : %d\r\n", current_threads); netbprintf(p, " Total Threads : %d\r\n", total_threads); netbprintf(p, " Max Threads : %d\r\n", maximum_threads); netbprintf(p, " Too Many Threads : %d\r\n", too_many_threads); netbprintf(p, " Normal Disconnects : %d\r\n", normal_disconnects); netbprintf(p, " Abnormal Disconnects : %d\r\n", abnormal_disconnects); netbprintf(p, " Client Timeouts : %d\r\n", timeouts); netbprintf(p, " Retrieved Messages : %d\r\n", retrieved_messages); netbprintf(p, " Retrieved Octets : %d\r\n", retrieved_octets); netbprintf(p, " Average Octets : %d\r\n", (retrieved_messages <= 0) ? 0 : (retrieved_octets/retrieved_messages)); netbprintf(p, " Minimum Octets : %d\r\n", minimum_octets); netbprintf(p, " Maximum Octets : %d\r\n", maximum_octets); netbprintf(p, " Auth Failures : %d\r\n", authentication_failures); netbprintf(p, " Current Users : %d\r\n", current_threads); for (count=0,pp = &pop[0]; count < MAX_THREADS; count++, pp++) { if (pp->in_use) { netbprintf(p, " %2d. %-14s, since %s %s %8.8s %s\r\n", count, (*pp->user) ? pp->user : "", logtime(&pp->start_time), !BINTIME_IS_ZERO(pp->timeout) ? ",timeouts" : "", !BINTIME_IS_ZERO(pp->timeout) ? time_from_bintime(&pp->timeout,1) : "", (pp->retrieve.flags & RETR_IN_PROGRESS_FLAG) ? ",(blocked)" : "" ); } } netb_end (p); p->retrieve.function = NULL; /* do not return to this routine */ netwrite_async (p, p->retrieve.send_buffer, p->retrieve.send_bufsize); return POP_SUCCESS; } /*************************************************************************** ** pop_xtnd_loglevel - modify server logging level ****************************************************************************/ int pop_xtnd_loglevel (POP *p) { int retval = POP_SUCCESS; char *level; level = p->pop_args[2]; lower(level); if (strcmp(level, "info") == 0) master_log_level = LOG_INFO; else if (strcmp(level, "error") == 0) master_log_level = LOG_ERROR; else if (strcmp(level, "thread") == 0) master_log_level = LOG_THREAD; else if (strcmp(level, "debug") == 0) master_log_level = LOG_DEBUG; else retval = POP_FAILURE; if (retval == POP_SUCCESS) { pop_log(LOG_INFO, p, "logging level changed to %s", level); pop_msg(p, POP_SUCCESS, "logging level changed to %s", level); } else { pop_log(LOG_INFO, p, "invalid logging level %s", level); pop_msg(p, POP_FAILURE, "invalid logging level %s", level); } return retval; } /*************************************************************************** ** pop_xtnd_info - display process quotas ****************************************************************************/ int pop_xtnd_info (POP *p) { static char *info_text[] = { "remaining AST quota" , /* JPI_ASTCNT */ "remaining buffered I/O quota" , /* JPI_BIOCNT */ "remaining buffered I/O byte count quota" , /* JPI_BYTCNT */ "remaining direct I/O quota" , /* JPI_DIOCNT */ "remaining lock request quota" , /* JPI_ENQCNT */ "remaining timer queue entry quota" , /* JPI_TQCNT */ "remaining open file quota" , /* JPI_FILCNT */ "process's global page count in the ws" , /* JPI_GPGCNT */ "remaining paging file quota" , /* JPI_PAGFILCNT */ "number of working set pages or pagelets" /* JPI_PPGCNT */ }; proc_info_type * info = get_process_information (); float cpu; int i; pop_msg (p, POP_SUCCESS, "Displaying process informations after %d measurements",info->count); netbprintf (p, "\r\n description | cur | min | max\r\n"); netbprintf (p, "==========================================+==========+==========+==========\r\n"); for (i = 0; i < JPI_MAX; i++) { netbprintf (p," %-40.40s | %8d | %8d | %8d\r\n", info_text[i], info->cur[i], info->min[i], info->max[i]); } cpu = get_cpu(); netbprintf(p, "\r\n CPU Seconds (image) : %-8.2f (%d mins, %d secs)\r\n", cpu, (int)(cpu/60), (int)((int)cpu%60)); netbprintf(p, " CPU Seconds (process) : %-8.2f (%d mins, %d secs)\r\n\r\n", info->cpu, (int)(info->cpu/60), (int)((int)info->cpu%60)); netb_end (p); p->retrieve.function = NULL; /* do not return to this routine */ netwrite_async (p, p->retrieve.send_buffer, p->retrieve.send_bufsize); return TRUE; } /*************************************************************************** ** pop_xtnd_options - shows or sets options ****************************************************************************/ int pop_xtnd_options (POP *p) { int retval = TRUE; char * option = ""; if (p->arg_count == 2) option = p->pop_args[2]; lower(option); if ((p->arg_count < 2) || !strcmp(option,"show")) { pop_msg(p, POP_SUCCESS, "Displaying current options"); display_runtime_options (p); } else if (strstr (option, "set")) { reset_runtime_options = TRUE; pop_log(LOG_INFO, p, "option reset requested"); pop_msg(p, POP_SUCCESS, "will reset options when no threads are connected"); } else { pop_log (LOG_INFO,p, "invalid options parameter \"%s\"", option); pop_msg (p,POP_FAILURE,"invalid options parameter \"%s\"", option); retval = FALSE; } return retval; } /*************************************************************************** ** display_runtime_options - displays or sets options ****************************************************************************/ #define opt(a) ((a)?"TRUE":"FALSE") int display_runtime_options (POP *p) { const environ_var_type *env = get_environment_list(); int i; netbprintf(p, "\r\n Current Options:\r\n"); netbprintf(p, "=============================+===========================\r\n"); for (i=0; env[i].name; i++) { switch (env[i].type) { case ENV_BOOL : netbprintf(p, " %-26.26s : %s\r\n",env[i].name,opt(*(int *)env[i].varptr)); break; case ENV_NUMERICAL : netbprintf(p, " %-26.26s : %d\r\n",env[i].name,*(int *)env[i].varptr); break; case ENV_STRING : netbprintf(p, " %-26.26s : %s\r\n",env[i].name,*(char **)env[i].varptr); break; case ENV_DELTATIME : netbprintf(p, " %-26.26s : %s\r\n",env[i].name,time_from_bintime(env[i].varptr,0)); default: break; } } netb_end (p); p->retrieve.function = NULL; /* do not return to this routine */ netwrite_async (p, p->retrieve.send_buffer, p->retrieve.send_bufsize); return TRUE; } /*************************************************************************** ** pop_xtnd_help gives overview of the available xtnd commands. ** The xtnd command table is passed via p->pop_args[2]. ****************************************************************************/ int pop_xtnd_help (POP *p) { xtnd_table *s; pop_msg (p, POP_SUCCESS, "Displaying command summery"); /* Search for the POP command in the command/state table */ netbprintf (p,"\r\n XTND command | min. arg. | max. arg. | validation\r\n"); netbprintf (p,"===============+===========+===========+============\r\n"); for (s = (xtnd_table *)p->pop_args[2]; s->subcommand; s++) { netbprintf (p," %-13.13s | %6d | %6d | %-20.20s\r\n", s->subcommand, s->min_args, s->max_args, s->validate ? s->validate : "(none)"); } netb_end (p); p->retrieve.function = NULL; /* do not return to this routine */ netwrite_async (p, p->retrieve.send_buffer, p->retrieve.send_bufsize); return TRUE; } /*************************************************************************** ** pop_xtnd_validate - validate user for accessing server's special functions ****************************************************************************/ int pop_xtnd_validate (char *user, char *command) { FILE *valid_file; int validated = FALSE; char buffer[512]; char *tokens[100]; int count; int length; int number_tokens; if (xtnd_filename == NULL) return FALSE; if ((valid_file = fopen(xtnd_filename ,"r")) != NULL) { while (fgets(buffer,sizeof(buffer)-1,valid_file) && !validated) { length = strlen(buffer) - 1; buffer[length] = '\0'; if (length > 0) { lower(buffer); make_argv(buffer,&number_tokens,tokens," ,\t"); if (strcmp(tokens[0],command) == 0) { for (count=1; countsubcommand; s++) { if (strcmp(s->subcommand,p->pop_subcommand) == 0) { /* Were too few parameters passed to the subcommand? */ if ((p->arg_count-1) < s->min_args) return((xtnd_table *)pop_msg(p,POP_FAILURE, "Too few arguments for the %s %s command.", p->pop_command,p->pop_subcommand)); /* Were too many parameters passed to the subcommand? */ if ((p->arg_count-1) > s->max_args) return((xtnd_table *)pop_msg(p,POP_FAILURE, "Too many arguments for the %s %s command.", p->pop_command,p->pop_subcommand)); /* Validate the user if necessary */ if (s->validate) { if (!pop_xtnd_validate(p->user,s->validate)) return((xtnd_table *)pop_msg(p,POP_FAILURE, "Not validated for the %s %s command.", p->pop_command,p->pop_subcommand)); } if (!strcmp(p->pop_subcommand,"help")) { /* put a pointer to the command table into the argument list */ p->pop_args[2] = (char *) subcommands; } /* Return a pointer to entry for subcommand in the XTND table */ return(s); } } /* The client subcommand was not located in the XTND command table */ pop_log(LOG_ERROR, p, "unknown sub-command:\"%s %s\".", p->pop_command, p->pop_subcommand); return((xtnd_table *)pop_msg(p,POP_FAILURE, "Unknown sub-command: \"%s %s\".",p->pop_command,p->pop_subcommand)); } /*************************************************************************** ** Pop_xtnd ****************************************************************************/ int pop_xtnd(POP *p) { xtnd_table *x; /* Convert the XTND subcommand to lower case */ lower(p->pop_subcommand); /* Search for the subcommand in the XTND command table */ if ((x = get_pop_subcommand(p)) == NULL) return(POP_FAILURE); /* Call the function associated with this subcommand */ if (x->function) return((*x->function)(p)); /* Otherwise assume NOOP */ return (pop_msg(p,POP_SUCCESS,NULL)); } /***************************************************************************** ** set_auth_commands enables or disables the commands ** for user/pass and apop authorization. *****************************************************************************/ void set_auth_commands (int option) { state_table *s; for (s = states; s->command; s++) { if (!strcmp(s->command,"user")) { if ((option == AUTH_ALL) || (option == AUTH_USER)) s->ValidCurrentState = auth1; else if (option == AUTH_NONE) s->ValidCurrentState = disabled; } else if (!strcmp(s->command,"pass")) { if ((option == AUTH_ALL) || (option == AUTH_USER)) s->ValidCurrentState = auth2; else if (option == AUTH_NONE) s->ValidCurrentState = disabled; } else if (!strcmp(s->command,"apop")) { if ((option == AUTH_ALL) || (option == AUTH_APOP)) s->ValidCurrentState = auth1; else if (option == AUTH_NONE) s->ValidCurrentState = disabled; } } }