/* =========================================================================== = = = (C) Copyright 1991 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 = = = = Synopsis: = = This file contains functions that implement the pop3 commands. = = = = Authors: = = 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 #ifdef MULTINET #include #else /* WINS || UCX */ #include #endif #include #include "iupop3_general.h" #include "iupop3_global.h" #include "iupop3_vms.h" /************************************************************************* ** Prototypes /************************************************************************* state_table *get_pop_command(); xtnd_table *get_pop_subcommand(); int pop_dele(); int pop_last(); int pop_list(); void pop_log(); int pop_parse(); int pop_pass(); int pop_quit(); int pop_rset(); int pop_send(); int pop_stat(); int pop_updt(); int pop_user(); int pop_xtnd(); int pop_xtnd_shutdown(); int pop_xtnd_stats(); /**************************************************************************** ** External Declarations ****************************************************************************/ extern int PWDcheck(); extern void make_argv(); extern float get_cpu(); /************************************************************************** ** 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)) return (pop_msg (p,POP_FAILURE,"Message %d does not exist.",msg_num, 0)); /* ** 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->del_flag) return (pop_msg (p,POP_FAILURE,"Message %d has already been deleted.", msg_num, 0)); mp->del_flag = TRUE; 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, 0)); } /***************************************************************************** ** Pop_get_command: Extract the command from an input line form a POP client *****************************************************************************/ static state_table states[] = { auth1, "user", 1, 1, pop_user, {auth1, auth2}, auth2, "pass", 1, 1, pop_pass, {auth1, trans}, auth1, "quit", 0, 0, pop_quit, {halt, halt}, auth2, "quit", 0, 0, pop_quit, {halt, halt}, trans, "stat", 0, 0, pop_stat, {trans, trans}, trans, "list", 0, 1, pop_list, {trans, trans}, trans, "retr", 1, 1, pop_send, {trans, trans}, trans, "dele", 1, 1, pop_dele, {trans, trans}, trans, "noop", 0, 0, NULL, {trans, trans}, trans, "rset", 0, 0, pop_rset, {trans, trans}, trans, "top", 2, 2, pop_send, {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}, (state)0, NULL, 0, 0, NULL, {halt, halt}, }; state_table *pop_get_command(POP *p, register char *mp) { state_table *s; char buf[MAXMSGLINELEN]; if ((p->arg_count = pop_parse(p,mp)) < 0) return(NULL); /* ** 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,0)); 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,0)); /* ** Return a pointer to command in the command/state table */ return (s); } } pop_log(p,"unknown command: \"%s\"",p->pop_command,0); return((state_table *)pop_msg(p, POP_FAILURE, "Unknown command: \"%s\".", p->pop_command, 0)); } /************************************************************************ ** 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, 0)); } /************************************************************************ ** Pop_list: List the contents of the VMS NEWMAIL folder *************************************************************************/ int pop_list(POP *p) { Message *mp; register int status, len, i; register int flag = 0; register int msg_num; char buffer[MAXLINELEN]; if (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, 0)); /* ** 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->del_flag) return (pop_msg (p,POP_FAILURE, "Message %d has been deleted.",msg_num, 0)); /* ** Display message information */ return (pop_msg(p,POP_SUCCESS,"%u %u", msg_num, mp->length, 0)); } /* ** Message number was not supplied - display the entire list of messages */ pop_msg(p,POP_SUCCESS, "%u messages (%u octets)", p->msg_count - p->msgs_deleted, p->newmail_size - p->bytes_deleted); /* ** Walk through the message information list. ** Don't show messages marked as deleted. */ for (i = p->msg_count, mp = p->mptr; i > 0; i--, mp++) { if (!mp->del_flag) { (void)sprintf(buffer, "%u %u\r\n\0", mp->number, mp->length); len = strlen(buffer); status = send(p->sockfd, buffer, len, flag); } } sprintf(buffer, "%s\0", ENDMULTLINTRANS); len = strlen(buffer); status = send(p->sockfd, buffer, len, flag); 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 *mp; register int i; /* ** Loop through the POP command array */ for (mp = buf, i = 0; ; i++) { while (isspace(*mp)) mp++; if (*mp == 0) break; if (i >= MAXARGCOUNT) { pop_log(p,"too many command arguments (%d)",i,0); return(-1); } p->pop_args[i] = mp; while (!isspace(*mp) && *mp) mp++; if (*mp) *mp++ = 0; } if (i == 0) return (NEGATIVE); /* No parameters were */ lower(p->pop_command); return (i-1); } /*************************************************************************** ** Pop_pass - Verify user's password in the VMS authorization file ** Return - POP_SUCCESS or POP_FAILURE ***************************************************************************/ int pop_pass(POP *p) { char password[33]; char username[13]; int status; strcpy(password,p->pop_args[1]); upper(password); strcpy(username,p->user); upper(username); status = PWDcheck(username,password); memset(password,0,sizeof(password)); switch (status) { case 0: pop_log(p,"password supplied for \"%s\" is incorrect.", p->user, 0); return (pop_msg(p,POP_FAILURE, "Password supplied for \"%s\" is incorrect.", p->user, 0)); 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),0)); else { status = mail_user_info(p); if (vms_error(status)) return(pop_msg(p,POP_FAILURE,"Getting mail user info: %s", vms_message(status),0)); else { status = mail_open_file_context(p); if (vms_error(status)) return(pop_msg(p,POP_FAILURE,"Opening mail file context: %s", vms_message(status),0)); 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),0)); else { if (!p->newmail_selected) status = mail_folder_select(p,newmail_folder); if (vms_error(status)) return(pop_msg(p,POP_FAILURE, "Selecting mail folder: %s", vms_message(status),0)); else { pop_msg(p,POP_SUCCESS,"Username/password combination ok",0); pop_build_info(p); return(POP_SUCCESS); } } } } } break; default: pop_log(p,"error determining password validity",0); return (pop_msg(p,POP_FAILURE, "Error determining password validity.",0)); break; } } /*************************************************************************** ** Pop_quit: Disconnect from server ** Return: Currently assumes POP_SUCCESS always... ****************************************************************************/ int pop_quit(POP *p) { int status; status = mail_close_user_context(p); if (p->connected) return(pop_msg(p, POP_SUCCESS, "Pop server at %s signing off.",p->server, 0)); else return(POP_SUCCESS); } /*************************************************************************** ** Pop_rset: Undelete all messages flagged for deletion ** Return: Message count and Total nuber of bytes ****************************************************************************/ int pop_rset(POP *p) { Message *mp; register int i; for (i = p->msg_count, mp = p->mptr; i > 0; i--, mp++) { mp->del_flag = FALSE; mp->retr_flag = FALSE; } p->msgs_deleted = 0; p->bytes_deleted = 0; p->last_msg = 0; /* Reset the last-message-access flag */ return (pop_msg(p,POP_SUCCESS,"NEWMAIL Folder has %u messages (%u octets)", p->msg_count, p->newmail_size, 0)); } /**************************************************************************** ** Pop_send: Send the header and a possibly 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; register int msg_num=0; register int msg_lines=0; char buffer[MAXMSGLINELEN]; int status, len, flag; msg_num = atoi(p->pop_args[1]); /* ** Is 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, 0)); /* ** Get a pointer to the message in the message list */ mp = &p->mptr[msg_num-1]; if (mp->del_flag) /* Is the message flagged for deletion? */ return (pop_msg(p,POP_FAILURE, "Message %d has been deleted.",msg_num, 0)); /* ** If this is a TOP command, get the number of lines to send */ if (strcmp(p->pop_command,"top") == 0) { msg_lines = atoi(p->pop_args[2]); if (msg_lines > mp->lines) /* Sanity check */ msg_lines = mp->lines; } else /* Assume RETR (retrieve) was issued */ { msg_lines = mp->lines; } pop_msg(p,POP_SUCCESS,"%u octets",mp->length, 0); /* ** Retrieve all the lines for this message. ** VMS MAIL failure logged in the log file */ status = mail_retrieve_messages(p, msg_num, msg_lines); /* ** Mark the message as retrieved only if still connected, ** and if the command was "retr". */ if ((strcmp(p->pop_command,"retr") == 0) && p->connected) mp->retr_flag = TRUE; sprintf(buffer, "%s\0", ENDMULTLINTRANS); len = strlen(buffer); status = send(p->sockfd, buffer, len, flag); 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 status; int message_id; unsigned int bytes = 0; unsigned int messages_bytes = 0; for (message_id=1; message_id<=p->msg_count; message_id++,bytes=0) { status = mail_message_bytes(p,&message_id,&bytes); messages_bytes += bytes; } p->newmail_size = messages_bytes; return (pop_msg (p,POP_SUCCESS, "%u %u", p->msg_count - p->msgs_deleted, p->newmail_size - p->bytes_deleted, 0)); } /************************************************************************** ** Pop_updt: Update user's mail file - delete (with purging) messages ** marked for deletion and move retrieved messages to the ** MAIL folder. ** Return: status of pop_quit call... ***************************************************************************/ int pop_updt(POP *p) { char buffer[BUFSIZ]; /* Read buffer */ Message *mp; int msg_num; /* Current message counter */ int msg_cnt, status, PURGE=0; pop_log(p,"updating %s\'s VMS Mail file",p->user,0); msg_cnt = p->msg_count; for (msg_num = 1; msg_num <= msg_cnt; ++msg_num) { /* ** Get a pointer to the message information list */ mp = &p->mptr[msg_num-1]; if (mp->del_flag) { status = mail_delete_message(p, &msg_num); p->msg_count--; status = mail_message_new_count(p); PURGE = TRUE; } else if (mp->retr_flag) { pop_log(p,"pop_updt: moving message #%d to MAIL folder",msg_num,0); mail_message_move(p, msg_num); p->msg_count--; status = mail_message_new_count(p); } } /* --for loop */ if (PURGE) mail_purge_waste(p); /* delete wastebasket folder & compress */ return(pop_quit(p)); } /************************************************************************* ** Pop_user: verify existance of user in the authorization file ** Return: POP_SUCCESS or POP_FAILURE *************************************************************************/ int pop_user(POP *p) { strcpy(p->user, p->pop_args[1]); if (user_exists(p->user)) return(pop_msg(p,POP_SUCCESS,"Password required for \"%s\"",p->user, 0)); else { pop_log(p,"username \"%s\" is invalid", p->user, 0); return(pop_msg(p,POP_FAILURE,"Username \"%s\" is invalid", p->user, 0)); } } /*************************************************************************** ** Pop_xtnd_stats - show pop server statistics ****************************************************************************/ int pop_xtnd_shutdown(POP *p) { pop_log(p, "shutdown requested", 0); pop_msg(p, POP_SUCCESS, "will shutdown when no threads are connected", 0); shutdown = TRUE; return(POP_SUCCESS); } /*************************************************************************** ** Pop_xtnd_stats - show pop server statistics ****************************************************************************/ int pop_xtnd_stats(POP *p) { char buffer[128]; char date_str[11]; char time_str[9]; float cpu; int count; int status; extern POP pop[]; pop_msg(p,POP_SUCCESS,"Statistics follow", 0); get_time(time_str); get_date(date_str); sprintf(buffer, " Current Time : %s %s\n", date_str, time_str); status = send(p->sockfd, buffer, strlen(buffer), 0); sprintf(buffer, " Start Time : %s\n", start_time); status = send(p->sockfd, buffer, strlen(buffer), 0); cpu = get_cpu(); sprintf(buffer, " CPU Seconds : %-8.2f (%d mins, %d secs)\n", cpu, (int)(cpu/60), (int)((int)cpu%60)); status = send(p->sockfd, buffer, strlen(buffer), 0); sprintf(buffer, " Current Threads : %d\n", current_threads); status = send(p->sockfd, buffer, strlen(buffer), 0); sprintf(buffer, " Total Threads : %d\n", total_threads); status = send(p->sockfd, buffer, strlen(buffer), 0); sprintf(buffer, " Max Threads : %d\n", maximum_threads); status = send(p->sockfd, buffer, strlen(buffer), 0); sprintf(buffer, " Too Many Threads : %d\n", too_many_threads); status = send(p->sockfd, buffer, strlen(buffer), 0); sprintf(buffer, " Retrieved Messages : %d\n", retrieved_messages); status = send(p->sockfd, buffer, strlen(buffer), 0); sprintf(buffer, " 'From' Parse Errors : %d\n", from_parse_errors); status = send(p->sockfd, buffer, strlen(buffer), 0); sprintf(buffer, " Normal Disconnects : %d\n", normal_disconnects); status = send(p->sockfd, buffer, strlen(buffer), 0); sprintf(buffer, " Abnormal Disconnects : %d\n", abnormal_disconnects); status = send(p->sockfd, buffer, strlen(buffer), 0); sprintf(buffer, " Client Timeouts : %d\n", timeouts); status = send(p->sockfd, buffer, strlen(buffer), 0); sprintf(buffer, " Current Users :\n"); status = send(p->sockfd, buffer, strlen(buffer), 0); for (count=0; countsockfd, buffer, strlen(buffer), 0); } sprintf(buffer, "%s", ENDMULTLINTRANS); status = send(p->sockfd, buffer, strlen(buffer), 0); return(POP_SUCCESS); } /*************************************************************************** ** pop_xtnd_validate - validate user for accessing server's special functions ****************************************************************************/ int pop_xtnd_validate(char *user, char *command) { #define VALID_FILE "POP_XTND_LIST" FILE *valid_file; int validated = FALSE; char buffer[256]; char *tokens[100]; int count; int length; int number_tokens; if ((valid_file = fopen(VALID_FILE,"r")) != NULL) { while (!feof(valid_file) && !validated) { if (fgets(buffer,sizeof(buffer)-1,valid_file) == NULL) break; else { length = strlen(buffer); if (buffer[length-1] == '\n') { length--; 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,0)); /* 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,0)); /* Validate the user if necessary */ if (s->validate) { if (!pop_xtnd_validate(p->user,s->subcommand)) return((xtnd_table *)pop_msg(p,POP_FAILURE, "Not validated for the %s %s command.", p->pop_command,p->pop_subcommand,0)); } /* 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(p,"unknown command:\"%s %s\".",p->pop_command,p->pop_subcommand,0); return((xtnd_table *)pop_msg(p,POP_FAILURE, "Unknown command: \"%s %s\".",p->pop_command,p->pop_subcommand,0)); } /*************************************************************************** ** 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,0)); }