/* =========================================================================== = (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_VMS.C - Version 2.0 = = = = Synopsis: = = A collection of VMS system services and callable mail functions = = for the VMS IUPOP3 server. = = = = 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. = = = =========================================================================== */ /* ======================================================================== */ /* Includes */ /* ======================================================================== */ #include #include #include #include #include #include #include /* system services */ #include /* lib$ services */ #include #include #include #include #include # include # include "mailmsgdef.h" #ifdef MULTINET /* 16-Aug-1996 */ #include "multinet_root:[multinet.include]errno.h" #else #include #endif /* MULTINET */ #include "iupop3_general.h" #include "iupop3_vms.h" #include "iupop3_global.h" #include "version.h" #include "secsrvmsgdef.h" /* ======================================================================== */ /* Defines */ /* ======================================================================== */ #ifdef MULTINET #define ERRNO socket_errno #else #define ERRNO errno #endif #define START_HEADER_STRING "============== RFC 822 Headers ===============" /* ======================================================================== */ /* Prototypes look into iupop3_general.h */ /* ======================================================================== */ /* ======================================================================== */ /* Prototypes of mail$xxx rouines /* ======================================================================== */ #define MAIL_FUNCTION_PROTOTYPE \ unsigned int *context, ITEMLIST *in_item_list , ITEMLIST *out_item_list int mail$mailfile_begin (MAIL_FUNCTION_PROTOTYPE); int mail$mailfile_open (MAIL_FUNCTION_PROTOTYPE); int mail$mailfile_purge_waste (MAIL_FUNCTION_PROTOTYPE); int mail$mailfile_info_file (MAIL_FUNCTION_PROTOTYPE); int mail$mailfile_close (MAIL_FUNCTION_PROTOTYPE); int mail$mailfile_end (MAIL_FUNCTION_PROTOTYPE); int mail$message_begin (MAIL_FUNCTION_PROTOTYPE); int mail$message_delete (MAIL_FUNCTION_PROTOTYPE); int mail$message_select (MAIL_FUNCTION_PROTOTYPE); int mail$message_info (MAIL_FUNCTION_PROTOTYPE); int mail$message_get (MAIL_FUNCTION_PROTOTYPE); int mail$message_copy (MAIL_FUNCTION_PROTOTYPE); int mail$message_end (MAIL_FUNCTION_PROTOTYPE); int mail$user_begin (MAIL_FUNCTION_PROTOTYPE); int mail$user_get_info (MAIL_FUNCTION_PROTOTYPE); int mail$user_set_info (MAIL_FUNCTION_PROTOTYPE); int mail$user_end (MAIL_FUNCTION_PROTOTYPE); /* ======================================================================== */ /* Other local prototypes /* ======================================================================== */ static int get_date_time_context (char *init_string); static int Expired (bintime_type expiration_time); static int restore_position (POP *p); static int get_message_line (POP *p, ITEMLIST *inlist, ITEMLIST *outlist); static char *id_from_bintime (bintime_type *bintimeptr); static char *header_from_bintime (bintime_type *bintimeptr); static char *retr_time (bintime_type *bintimeptr); static int mail_sync_maildrop (POP *p, int msg_id); static int mail__get_message_record (POP *p); static int mail__parse_record (POP *p); static int compar_bindate_low (const void *e1, const void *e2); /* ======================================================================== */ /* Global variables of this compilation unit /* ======================================================================== */ static int jpi_wsquota; /* working set size quota in pages or pagelets */ /* ======================================================================== */ /* creates time string used in message headers from a binary time */ /* ======================================================================== */ static char *header_from_bintime (bintime_type *bintimeptr) { static char date_time[40]; static unsigned int user_context = 0; static struct dsc$descriptor_s s_des = {sizeof date_time - 6, DSC$K_DTYPE_T, DSC$K_CLASS_S, date_time}; int length = 0; int status = SS$_NORMAL; if (!user_context) { user_context = get_date_time_context ("|!WAC, !D0 !MAAC !Y4|!H04:!M0:!S0 |"); } *date_time = '\0'; status = lib$format_date_time (&s_des,bintimeptr, &user_context,&length,0); if (vms_error(status)) system_log(LOG_ERROR, "time conversion error: %s", vms_message(status)); else strcpy (&date_time[length], get_timezone()); return date_time; } /* ======================================================================== */ /* creates a message id string from a binary time */ /* ======================================================================== */ static char *id_from_bintime (bintime_type *bintimeptr) { static char id[32]; static unsigned int user_context = 0; static struct dsc$descriptor_s s_des = {sizeof id - 1, DSC$K_DTYPE_T, DSC$K_CLASS_S, id}; int length = 0; int status = SS$_NORMAL; if (!user_context) { user_context = get_date_time_context ("|!Y4!MN0!D0!H04!M0!S0!C7|"); } *id = '\0'; status = lib$format_date_time (&s_des,bintimeptr,&user_context,&length,0); if (vms_error(status)) system_log(LOG_ERROR, "time conversion error: %s", vms_message(status)); else id[length] = '\0'; return id; } /* ======================================================================== */ /* time format used as timestamp */ /* ======================================================================== */ char *stamptime (bintime_type *bintimeptr) { static char date_time[32]; static unsigned int user_context = 0; static struct dsc$descriptor_s s_des = {sizeof date_time - 1, DSC$K_DTYPE_T, DSC$K_CLASS_S, date_time}; int length = 0; int status = SS$_NORMAL; if (!user_context) { user_context = get_date_time_context ("|!D0-!MAAU-!Y4|!H04:!M0:!S0.!C4|"); } status = lib$format_date_time (&s_des,bintimeptr, &user_context,&length,0); if (vms_error(status)) { *date_time = '\0'; system_log(LOG_ERROR, "time conversion error: %s", vms_message(status)); } else date_time[length] = '\0'; return date_time; } /* ======================================================================== */ /* creates logfile time string from a binary time */ /* ======================================================================== */ char *logtime (bintime_type *bintimeptr) { static char date_time[30]; static unsigned int user_context = 0; static struct dsc$descriptor_s s_des = {sizeof date_time - 1, DSC$K_DTYPE_T, DSC$K_CLASS_S, date_time}; int length = 0; int status = SS$_NORMAL; if (!user_context) { user_context = get_date_time_context ("|!Y4-!MN0-!D0|!H04:!M0:!S0.!C2|"); } status = lib$format_date_time (&s_des,bintimeptr, &user_context,&length,0); if (vms_error(status)) { *date_time = '\0'; system_log(LOG_ERROR, "time conversion error: %s", vms_message(status)); } else date_time[length] = '\0'; return date_time; } /* ======================================================================== */ /* creates file retrieve time string from a binary time */ /* ======================================================================== */ static char *retr_time (bintime_type *bintimeptr) { char *time_string = logtime (bintimeptr); time_string[10] = '.'; return time_string; } /* ======================================================================== */ /* returns a context number for use with lib$format_date_time */ /* ======================================================================== */ static int get_date_time_context (char *init_string) { unsigned int user_context = 0; int component = LIB$K_OUTPUT_FORMAT; int status; status = lib$init_date_time_context (&user_context,&component,desz(init_string)); if (vms_error(status)) { system_log(LOG_ERROR, "lib$init_date_time_context error %s:", vms_message(status)); system_log(LOG_ERROR, "cannot get user context for %s", init_string); user_context = 0; } return user_context; } /* ======================================================================== */ /* converts a binary time into ascii time */ /* a pointer to the ascii string is returned */ /* modes: 0 - date and time */ /* 1 - time */ /* ======================================================================== */ char *time_from_bintime (bintime_type *bintimeptr, int mode) { static char time_string[32]; static struct dsc$descriptor_s s_des = {sizeof time_string - 1, DSC$K_DTYPE_T, DSC$K_CLASS_S, time_string}; int time_len = 0; *time_string = '\0'; sys$asctim (&time_len,&s_des,bintimeptr,mode); time_string[time_len] = '\0'; return time_string; } /* ======================================================================== */ /* Get CPU Utilization */ /* ======================================================================== */ float get_cpu (void) { int status; int cpu = 0; int length = 0; int pid = 0; static float start_cpu = -1; struct IOSB iosb; if (start_cpu < 0) { start_cpu = 0.0; start_cpu = get_cpu(); } itemopen(outlist_ptr,outlist); itemadd(outlist_ptr,sizeof(cpu),JPI$_CPUTIM,&cpu,&length); itemclose(outlist_ptr); status = sys$getjpiw(0,&pid,0,outlist,&iosb,0,0); if (vms_error(status)) { system_log(LOG_ERROR, "sys$getjpiw: %s", vms_message(status)); cpu = 0; } return (((float)cpu) / (100.0)) - start_cpu; } /* ======================================================================== */ /* Get CPU Utilization */ /* ======================================================================== */ proc_info_type * get_process_information (void) { static int length, cpu; static proc_info_type info; int status; int i, pid = 0; struct IOSB iosb; static ITEMLIST out_items[] = { {sizeof cpu,JPI$_CPUTIM,&cpu,&length}, {sizeof (int),JPI$_ASTCNT,&info.cur[JPI_ASTCNT],&length}, {sizeof (int),JPI$_BIOCNT,&info.cur[JPI_BIOCNT],&length}, {sizeof (int),JPI$_BYTCNT,&info.cur[JPI_BYTCNT],&length}, {sizeof (int),JPI$_DIOCNT,&info.cur[JPI_DIOCNT],&length}, {sizeof (int),JPI$_ENQCNT,&info.cur[JPI_ENQCNT],&length}, {sizeof (int),JPI$_TQCNT,&info.cur[JPI_TQCNT],&length}, {sizeof (int),JPI$_FILCNT,&info.cur[JPI_FILCNT],&length}, {sizeof (int),JPI$_GPGCNT,&info.cur[JPI_GPGCNT],&length}, {sizeof (int),JPI$_PAGFILCNT,&info.cur[JPI_PAGFILCNT],&length}, {sizeof (int),JPI$_PPGCNT,&info.cur[JPI_PPGCNT],&length}, {sizeof (int),JPI$_WSQUOTA,&jpi_wsquota,&length}, {0,0,NULL,NULL} }; cpu = length = 0; status = sys$getjpiw(0,&pid,0,out_items,&iosb,0,0); if (vms_error(status)) { system_log(LOG_ERROR, "sys$getjpiw: %s", vms_message(status)); } else { info.count++; info.cpu = (float) cpu / 100.0; for (i=0; i < JPI_MAX; i++) { info.min[i] = Min (info.min[i], info.cur[i]); info.max[i] = Max (info.max[i], info.cur[i]); } } return &info; } /* ======================================================================== */ /* Get Timezone Information */ /* ======================================================================== */ char *get_timezone (void) { static char timezone_string[] = "+0000"; static int timezone_differential = 0; char * env_string; env_string = getenv ("SYS$TIMEZONE_DIFFERENTIAL"); if (env_string && (timezone_differential != atoi (env_string))) { timezone_differential = atoi (env_string); if (abs(timezone_differential / 3600) < 14) sprintf (timezone_string, "%c%02.2d00", (timezone_differential > 0) ? '+' : '-', abs(timezone_differential / 3600)); } return timezone_string; } /* ======================================================================== */ /* Close External File */ /* ======================================================================== */ int close_external_file (POP *p) { int status = SS$_NORMAL; if (p->retrieve.ext_file) { fclose (p->retrieve.ext_file); p->retrieve.ext_file = NULL; if (p->retrieve.ext_file_buf) { free (p->retrieve.ext_file_buf); p->retrieve.ext_file_buf = NULL; } pop_log (LOG_DEBUG, p, "external file closed"); } return status; } /* ======================================================================== */ /* Mail Close File Context */ /* ======================================================================== */ int mail_close_file_context (POP *p) { int status = SS$_NORMAL; if (p->file_context) { status = mail$mailfile_close(&p->file_context,nullist,nullist); if (vms_error(status)) pop_log(LOG_ERROR, p, "mail$mailfile_close: %s", vms_message(status)); status = mail$mailfile_end(&p->file_context,nullist,nullist); if (vms_error(status)) pop_log(LOG_ERROR, p, "mail$mailfile_end: %s", vms_message(status)); else pop_log (LOG_DEBUG, p, "file context closed"); } return status; } /* ======================================================================== */ /* Mail Close Message Context */ /* ======================================================================== */ int mail_close_message_context (POP *p) { int status = SS$_NORMAL; if (p->message_context) { status = mail$message_end(&p->message_context,nullist,nullist); if (vms_error(status)) pop_log(LOG_ERROR, p, "mail$message_end: %s", vms_message(status)); else pop_log (LOG_DEBUG, p, "message context closed"); p->message_context = NO_CONTEXT; p->newmail_selected = FALSE; } return status; } /* ======================================================================== */ /* Mail Close User Context */ /* ======================================================================== */ int mail_close_user_context (POP *p) { int status = SS$_NORMAL; if (p->user_context) { status = mail$user_end(&p->user_context,nullist,nullist); if (vms_error(status)) pop_log(LOG_ERROR, p, "mail$user_end: %s", vms_message(status)); p->user_context = NO_CONTEXT; } return status; } /* ======================================================================== */ /* Mail Folder Select */ /* ======================================================================== */ int mail_folder_select (POP *p,char *folder_name, int *num_messages) { int length = 0; int messages = 0; int status; if (!p->message_context) mail_open_message_context (p); itemopen(inlist_ptr,inlist); itemadd(inlist_ptr,strlen(folder_name),MAIL$_MESSAGE_FOLDER,folder_name,0); itemadd(inlist_ptr,0,MAIL$_NOSIGNAL,0,0); itemclose(inlist_ptr); itemopen(outlist_ptr,outlist); itemadd(outlist_ptr,sizeof(messages),MAIL$_MESSAGE_SELECTED, &messages,&length); itemclose(outlist_ptr); status = mail$message_select(&p->message_context,inlist,outlist); if (vms_error(status)) { messages = 0; if (status == MAIL$_NOTEXIST) /* no mail file, use as empty maildrop */ status = SS$_NORMAL; else pop_log(LOG_ERROR, p, "mail$message_select: %s", vms_message(status)); } else { pop_log(LOG_DEBUG, p, "folder %s with %d mails selected", folder_name, messages); if (p->msg_count && p->mptr && !(p->retrieve.flags & RETR_PURGING_FLAG)) status = mail_sync_maildrop (p, messages); /* check synchronization */ p->newmail_selected = TRUE; } if (num_messages) *num_messages = messages; return status; } /* ======================================================================== */ /* Mail Synchronize Maildrop */ /* ======================================================================== */ static int mail_sync_maildrop (POP *p, int new_msg_count) { bintime_type bintime = BINTIME_INIT_ZERO; Message *mp = p->mptr; int msg_count = p->msg_count; int msg_num, newmsg_num; int length = 0; int status = SS$_NORMAL; pop_log (LOG_DEBUG, p, "resynchronize maildrop"); /* * prepare item lists */ itemopen(inlist_ptr, inlist); itemadd(inlist_ptr, sizeof newmsg_num, MAIL$_MESSAGE_ID,&newmsg_num, 0); itemadd(inlist_ptr, 0, MAIL$_NOSIGNAL, 0, 0); itemclose(inlist_ptr); itemopen(outlist_ptr, outlist); itemadd(outlist_ptr,sizeof bintime,MAIL$_MESSAGE_BINARY_DATE,&bintime,&length); itemclose(outlist_ptr); /* * loop through the message array */ for (msg_num=1,newmsg_num=0; msg_num <= msg_count; msg_num++, mp++) { while (!vms_error(status) && (BINTIME_COMPARE(bintime,mp->bindate) < 0)) { /* skip over inserted messages */ if (++newmsg_num <= new_msg_count) { status = mail$message_info (&p->message_context,inlist,outlist); if (vms_error(status)) pop_log (LOG_ERROR,p,"sync_maildrop msg %d: %s", newmsg_num,vms_message(status)); } else status = MAIL$_NOMOREMSG; } if (vms_error(status)) mp->msg_flags |= MSG_UNAVAILABLE_FLAG; else { if (!BINTIME_COMPARE(bintime,mp->bindate)) { mp->nummbx = newmsg_num; if (mp->msg_flags & MSG_UNAVAILABLE_FLAG) mp->msg_flags ^= MSG_UNAVAILABLE_FLAG; } else mp->msg_flags |= MSG_UNAVAILABLE_FLAG; } } return status; } /* ======================================================================== */ /* Mail Message Info */ /* ======================================================================== */ #define MEDIUM_RECORD_SIZE 78 /* max. line length allowed in MIME messages */ int mail_message_info (POP *p, int message_id, Message *mp) { int length = 0, size; int lines = 0; int i, status; char external_id[256]; int tmp, is_external=FALSE; short msg_flags; memset(external_id, '\0', sizeof(external_id)); itemopen (inlist_ptr, inlist); itemadd (inlist_ptr,sizeof(int *),MAIL$_MESSAGE_ID,&message_id,0); itemadd (inlist_ptr,0,MAIL$_NOSIGNAL,0,0); itemclose (inlist_ptr); itemopen (outlist_ptr, outlist); itemadd (outlist_ptr, sizeof(msg_flags), MAIL$_MESSAGE_RETURN_FLAGS, &msg_flags, &length); itemadd (outlist_ptr,sizeof(external_id)-1,MAIL$_MESSAGE_EXTID,external_id, &length); itemadd(outlist_ptr,sizeof(mp->lines),MAIL$_MESSAGE_SIZE,&mp->lines,&length); itemadd (outlist_ptr,sizeof (bintime_type),MAIL$_MESSAGE_BINARY_DATE, &mp->bindate, &length); itemadd (outlist_ptr,0,MAIL$_NOSIGNAL,0,0); itemclose (outlist_ptr); status = mail$message_info (&p->message_context,inlist,outlist); if (vms_error(status)) { pop_log (LOG_ERROR, p, "mail$message_info: %s", vms_message(status)); return (POP_FAILURE); } /* Check if message was sent with MAIL/FOREIGN */ if (msg_flags & MAIL$M_EXTNSTD) { pop_log(LOG_DEBUG, p, "foreign message detected"); mp->msg_flags |= MSG_FOREIGN_FLAG; mp->lines = 6; /* length of the informational message */ } is_external = *external_id; if (is_external) mp->msg_flags |= MSG_EXTERNAL_FLAG; if (fast_scan) { /* estimate file size from number of records */ mp->length += mp->lines * MEDIUM_RECORD_SIZE; } else { if (is_external) { /* Mail file is external to MAIL.MAI */ mp->length += check_external_file (p, external_id); } else { mp->length += mp->lines * MEDIUM_RECORD_SIZE; /* estimate value */ } if (!vms_error(status)) { int hdr_from = 0, hdr_cc = 0, hdr_subject = 0, hdr_to = 0; /* get the size of all header lines */ status = SS$_NORMAL; itemopen(outlist_ptr,outlist); itemadd(outlist_ptr,ITEM_LENGTH,MAIL$_MESSAGE_FROM, p->retrieve.buffer,&hdr_from); itemadd(outlist_ptr,ITEM_LENGTH,MAIL$_MESSAGE_CC, p->retrieve.buffer,&hdr_cc); itemadd(outlist_ptr,ITEM_LENGTH,MAIL$_MESSAGE_SUBJECT, p->retrieve.buffer,&hdr_subject); itemadd(outlist_ptr,ITEM_LENGTH,MAIL$_MESSAGE_TO, p->retrieve.buffer,&hdr_to); itemadd(outlist_ptr,0,MAIL$_NOSIGNAL,0,0); itemclose(outlist_ptr); status = SS$_NORMAL; status = mail$message_info (&p->message_context,inlist,outlist); if (vms_error(status)) { pop_log(LOG_ERROR, p, "mail$message_get in headers loop: %s", vms_message(status)); return(POP_FAILURE); } mp->length += hdr_from + hdr_cc + hdr_subject + hdr_to; } } if (mp->length < 350) mp->length += 350; /* min. header length */ return status; } /* ======================================================================== */ /* Mail Open File Context */ /* ======================================================================== */ int mail_open_file_context (POP *p) { int status; strcpy (p->retrieve.buffer,p->mail_directory); strncat (p->retrieve.buffer,"MAIL.MAI", sizeof p->retrieve.buffer - sizeof "MAIL.MAI"); pop_log(LOG_DEBUG, p, "opening %s",p->retrieve.buffer); itemopen(inlist_ptr,inlist); itemadd(inlist_ptr,0,MAIL$_NOSIGNAL,0,0); itemclose(inlist_ptr); status = mail$mailfile_begin(&p->file_context,inlist,nullist); if (vms_error(status)) { pop_log(LOG_ERROR, p, "mail$mailfile_begin: %s", vms_message(status)); if (p->file_context) { int tempsts; tempsts = mail$mailfile_end(&p->file_context,nullist,nullist); if (vms_error(tempsts)) pop_log(LOG_ERROR, p, "mail$mailfile_end: %s", vms_message(tempsts)); } } else { itemopen(inlist_ptr,inlist); itemadd(inlist_ptr,strlen(p->retrieve.buffer),MAIL$_MAILFILE_NAME, &p->retrieve.buffer,0); itemadd(inlist_ptr,0,MAIL$_NOSIGNAL,0,0); itemclose(inlist_ptr); status = mail$mailfile_open(&p->file_context,inlist,nullist); if (vms_error(status)) { int tempsts; if (status == MAIL$_OPENIN) { pop_log(LOG_DEBUG, p, "user's mail file does not exist"); status = SS$_NORMAL; p->has_no_mail_file = TRUE; } else pop_log(LOG_ERROR, p, "mail$mailfile_open: %s", vms_message(status)); tempsts = mail$mailfile_end(&p->file_context,nullist,nullist); if (vms_error(tempsts)) pop_log(LOG_ERROR, p, "mail$mailfile_end: %s", vms_message(tempsts)); } } return status; } /* ======================================================================== */ /* Mail Open Message Context */ /* ======================================================================== */ int mail_open_message_context (POP *p) { int status; itemopen(inlist_ptr,inlist); itemadd(inlist_ptr,sizeof(p->file_context),MAIL$_MESSAGE_FILE_CTX, &p->file_context,0); itemadd(inlist_ptr,0,MAIL$_NOSIGNAL,0,0); itemclose(inlist_ptr); status = mail$message_begin(&p->message_context,inlist,nullist); if (vms_error(status)) { pop_log(LOG_ERROR, p, "open_message_context: %s", vms_message(status)); } return status; } /* ======================================================================== */ /* Mail Open User Context */ /* ======================================================================== */ int mail_open_user_context (POP *p) { int status; p->user_context = 0; status = mail$user_begin(&p->user_context,nullist,nullist); if (vms_error(status)) { pop_log(LOG_ERROR, p, "mail$user_begin: %s", vms_message(status)); if (p->user_context) mail_close_user_context(p); } return status; } /* ======================================================================== */ /* Mail Retrieve Message */ /* ======================================================================== */ int mail_retrieve_message (POP *p) { int status = SS$_NORMAL; int retval = POP_FAILURE; if (default_to_smtp) /* set with logical or commandline switch */ p->decnet = FALSE; else p->decnet = TRUE; if (!p->newmail_selected) status = mail_folder_select(p,p->folder_name,NULL); if (vms_error(status)) { pop_log(LOG_ERROR, p, "retrieve_message context: %s", vms_message(status)); retval = pop_msg(p, POP_FAILURE, "error accessing folder %s.",p->folder_name); } else { if ((p->retrieve.mp->number < 1) || (p->retrieve.mp->number > p->msg_count)) retval = pop_msg(p, POP_FAILURE, "Message %d does not exist.", p->retrieve.mp->number); else if (p->retrieve.mp->msg_flags & MSG_UNAVAILABLE_FLAG) retval = pop_msg(p, POP_FAILURE, "Message %d unaccessible.", p->retrieve.mp->number); else { sys$gettim (&p->retrieve.start_time); if (mail_retrieve_message_headers(p)) { pop_msg(p,POP_SUCCESS,"%u octets",p->retrieve.mp->length); p->retrieve.function = mail_retrieve_message_text; if (mail_retrieve_message_text(p)) retval = POP_SUCCESS; } else retval = pop_msg(p, POP_FAILURE, "Message %d could not be accessed.", p->retrieve.mp->number); } } return retval; } #define DECNET_HEADER_FORMAT "Received: by %s (OpenVMS MAIL)\r\n\twith DECNET; %s\r\n\ Message-ID: <%s@%s>\r\n\ Date: %s\r\n\ From: %s\r\n\ Subject: %s\r\n\ To: %s\r\n\ Cc: %s\r\n\ X-VMS-From: %s\r\n\ X-POP3-Server: %s IUPOP3 V%s\r\n\ X-POP3-ID: %s.%d\r\n" #define ACCESSFAIL_MESSAGE_FORMAT "\r\n\ An error has been detected in this VMS mail file !!!\r\n\ It seems that the body of the message is missing on the VMS host\r\n\ Use the return address to contact the original sender...\r\n\r\n\ message generated by IUPOP3 V%s on %s\r\n" /* ======================================================================== */ /* Mail Retrieve Message Headers */ /* ======================================================================== */ int mail_retrieve_message_headers (POP *p) { int status; int num_headers, lines; int ext_file_size = 0; int from_len = 0, cc_len = 0, to_len = 0, subject_len = 0; int access_fail = FALSE; short int record_type = 0; char *pointer; char *header_date; char from[ITEM_LENGTH+4], xfrom[ITEM_LENGTH+4], cc[ITEM_LENGTH+4], to[ITEM_LENGTH+4], subject[ITEM_LENGTH+4]; p->retrieve.send_octets = 0; p->retrieve.enable_long_lines = enable_long_lines; if (!(p->retrieve.send_buffer || allocate_send_buffer (p))) return POP_FAILURE; if (p->retrieve.mp->number != p->retrieve.mp->nummbx) pop_log (LOG_DEBUG, p, "message %d is number %d in maildrop ", p->retrieve.mp->number,p->retrieve.mp->nummbx); /* Get message date and headers */ itemopen(inlist_ptr, inlist); itemadd(inlist_ptr, sizeof(p->retrieve.mp->nummbx), MAIL$_MESSAGE_ID, &p->retrieve.mp->nummbx, 0); itemadd(inlist_ptr, 0, MAIL$_NOSIGNAL, 0, 0); itemclose(inlist_ptr); itemopen(outlist_ptr, outlist); itemadd (outlist_ptr,ITEM_LENGTH,MAIL$_MESSAGE_EXTID, p->retrieve.buffer,&p->retrieve.bufsize); itemadd(outlist_ptr,ITEM_LENGTH,MAIL$_MESSAGE_FROM,from,&from_len); itemadd(outlist_ptr,ITEM_LENGTH,MAIL$_MESSAGE_CC,cc,&cc_len); itemadd(outlist_ptr,ITEM_LENGTH,MAIL$_MESSAGE_SUBJECT,subject,&subject_len); itemadd(outlist_ptr,ITEM_LENGTH,MAIL$_MESSAGE_TO,to,&to_len); itemclose(outlist_ptr); status = mail$message_info (&p->message_context,inlist,outlist); if (vms_error(status)) { pop_log(LOG_ERROR, p, "mail$message_info (head): %s", vms_message(status)); return FALSE; } if (p->retrieve.bufsize) /* true if mailfile is external */ { p->retrieve.flags |= RETR_IS_EXTERNAL_FLAG; p->retrieve.buffer[p->retrieve.bufsize] = '\0'; if (!(ext_file_size = check_external_file (p, p->retrieve.buffer))) { p->retrieve.message_lines = 0; access_fail = TRUE; /* mark such a message not as retrieved */ if (p->retrieve.flags & RETR_IS_RETRIEVE_FLAG) p->retrieve.flags ^= RETR_IS_RETRIEVE_FLAG; } else if (read_direct_threshold && (ext_file_size > read_direct_threshold)) /* use ANSI-C to process file */ { p->retrieve.flags |= RETR_USE_ANSIC_FLAG; } } header_date = header_from_bintime (&p->retrieve.mp->bindate); /* Prepare for remaining headers */ from[from_len] = cc[cc_len] = subject[subject_len] = to[to_len] = '\0'; /* Process remaining headers */ replace_tabs (from); replace_tabs (lower(cc)); replace_tabs (subject); replace_tabs (lower(to)); strcpy (xfrom, from); status = SS$_NORMAL; lines = p->retrieve.mp->lines - 1; patch_from_line (p, from); /* sets p->decnet to true or false */ p->retrieve.bufsize = 0; /* if there is any problem with the mail file, only decnet headers are send */ if (access_fail) p->decnet = TRUE; else p->retrieve.flags |= RETR_RESTORE_FLAG; if (ignore_mail11_headers && !p->decnet) { int i, is_header = TRUE; num_headers = 0; if (use_bottom_headers) { char deliminator[256]; char *env_string = getenv("IUPOP3_HEADER_DELIMINATOR"); unsigned short int outlen = 0; if (!env_string || !my_strncasecmp(env_string,"UCX",3)) env_string = "!16*= RFC 822 Headers !16*="; /* UCX */ else if (!my_strncasecmp(env_string,"MX",2)) env_string = "!80*-"; /* MX */ status = sys$fao (desz(env_string), &outlen, des(deliminator,sizeof deliminator - 1)); if (vms_error(status)) { pop_log(LOG_ERROR,p,"sys$fao: %s",vms_message(status)); p->decnet = TRUE; } else { deliminator[outlen] = '\0'; pop_log(LOG_DEBUG,p,"delim = \"%s\"",deliminator); } is_header = FALSE; for (i = 0; (i < lines) && !vms_error(status); i++) { status = get_message_line (p, inlist, outlist); if (vms_error(status)) { pop_log(LOG_ERROR,p,"headers: (line %d): %s", p->retrieve.lines_sent,vms_message(status)); /* something is wrong with this message */ p->retrieve.message_lines = Min (p->retrieve.message_lines, p->retrieve.lines_sent); if (!num_headers || (status != MAIL$_NOMOREREC)) p->decnet = TRUE; break; } p->retrieve.lines_sent++; if (*p->retrieve.line == *deliminator) { if (p->retrieve.line != p->retrieve.buffer) /* if long lines allowed */ { /* * p->retrieve.line is not a zero terminated string as needed for strstr(). */ int len = Min(80,p->retrieve.bufsize); strncpy (p->retrieve.buffer,p->retrieve.line,len); p->retrieve.buffer[len] = '\0'; } if (strstr(p->retrieve.buffer,deliminator)) { num_headers = 0; p->retrieve.send_bufsize = 0; is_header = TRUE; } } else if (!p->retrieve.bufsize) /* no empty lines within headers allowed */ { num_headers = 0; p->retrieve.send_bufsize = 0; is_header = FALSE; } if (is_header && num_headers++) { int byte_stuff = TRUE; /* * Normally p->retrieve.line contains a whole record. Only if the mail * is read with ANSI-C functions (p->retrieve.ext_file is no null pointer) * multiple calls to get_message_line() may be necessary. * The record will be copied to the send buffer (netbputs()) and the * send buffer will be enlarged (enlarge_send_buffer()) if size * is insufficient. */ do { while (!netbputs(p,p->retrieve.line,p->retrieve.bufsize,byte_stuff) && enlarge_send_buffer(p)); byte_stuff = FALSE; /* only for begin of record */ p->retrieve.bufsize = 0; } while (p->retrieve.ext_file && !strchr(p->retrieve.line,'\n') && !vms_error(get_message_line(p,NULL,NULL)) && p->retrieve.bufsize); netbputs(p,"\r\n",2,FALSE); } while (p->retrieve.ext_file && p->retrieve.bufsize && !strchr(p->retrieve.line,'\n') && !vms_error(status)) { /* just jump through the rest of the record */ p->retrieve.bufsize = 0; /* process next part of long record */ status = get_message_line (p, NULL, NULL); } } close_external_file (p); p->retrieve.flags |= RETR_RESTORE_FLAG; p->retrieve.bufsize = 0; /* if no headers found assume they are topdown */ if (num_headers || p->decnet) is_header = FALSE; else is_header = TRUE; } /* the header lines are from the top of the message up to the first blank line */ while (is_header) { num_headers++; status = get_message_line (p, inlist, outlist); p->retrieve.lines_sent++; if (vms_error(status)) { is_header = FALSE; pop_log(LOG_ERROR,p,"mail$message_get (head): %s",vms_message(status)); p->decnet = TRUE; num_headers = 0; p->retrieve.flags |= RETR_RESTORE_FLAG; p->retrieve.bufsize = 0; break; } else if (!p->retrieve.bufsize) { if (num_headers > 1) /* first line is everytime empty */ { is_header = FALSE; } } else { int byte_stuff = TRUE; do /* see comments at previous netbputs() statement */ { while (!(is_header = netbputs(p,p->retrieve.line,p->retrieve.bufsize,byte_stuff)) && enlarge_send_buffer(p)); byte_stuff = FALSE; /* only for begin of record */ p->retrieve.bufsize = 0; } while (p->retrieve.ext_file && !strchr(p->retrieve.line,'\n') && !vms_error(get_message_line(p,NULL,NULL)) && p->retrieve.bufsize); netbputs(p,"\r\n",2,FALSE); } } p->retrieve.lines_sent = 0; if (num_headers > lines) num_headers = lines; p->retrieve.message_lines = Min (p->retrieve.message_lines, (lines - num_headers)); } if (p->decnet) { /* decnet message */ /* create RFC 822 compatible headers for decnet mails */ static unsigned int decnet_message_count = 0; num_headers = 11; sprintf (p->retrieve.send_buffer, DECNET_HEADER_FORMAT, myhostname, header_date, id_from_bintime(&p->retrieve.mp->bindate), myhostname, header_date, from, subject, to, cc, xfrom, myhostname, VERSION, retr_time(NULL), decnet_message_count++); p->retrieve.send_bufsize = strlen (p->retrieve.send_buffer); if (access_fail) { sprintf (p->retrieve.send_buffer + p->retrieve.send_bufsize, ACCESSFAIL_MESSAGE_FORMAT, VERSION, myhostname); p->retrieve.send_bufsize = strlen (p->retrieve.send_buffer); } } pop_log(LOG_DEBUG, p,"%s message, %d lines total (%d headers, %d body left)", p->decnet ? "DECNET":"SMTP", lines,num_headers,p->retrieve.message_lines); p->retrieve.bufsize = 0; netbprintf (p,"\r\n"); if (!p->retrieve.message_lines || (p->retrieve.lines_sent > p->retrieve.message_lines)) { /* should be a "top n 0" command */ p->retrieve.lines_sent = p->retrieve.message_lines = 0; netb_end (p); } /* * Estimate the memory usage of current thread by callable mail in pagelets. * If processed a 'top n 0' command on DECNET message or if * external message is read with ansi-c funktions, * no memory is used by callable mail. */ if ((p->retrieve.message_lines || !p->decnet) && !(p->retrieve.flags & RETR_USE_ANSIC_FLAG)) { if (p->retrieve.flags & RETR_IS_EXTERNAL_FLAG) p->memory_usage += ext_file_size/512 + 1; else p->memory_usage += 3; /* est. size of internal mail */ } return TRUE; } /* ======================================================================== */ /* Mail Retrieve Message Text */ /* ======================================================================== */ int mail_retrieve_message_text (POP *p) { int bytes; int status; int reading = TRUE; /* If message is FOREIGN, send an informative message instead */ if ((p->retrieve.mp->msg_flags & MSG_FOREIGN_FLAG) && p->retrieve.send_bufsize) { int i, lines = p->retrieve.message_lines; char *body[] = { "\r\nNote from IUPOP3 server:", "You have received a foreign (binary) mail message from the sender shown ", "on the 'From:' line above. That message is now in your VMS MAIL folder.\r\n", "To access it, log in to your VMS host and use the VMS MAIL 'EXTRACT'", "command. If you have any questions, please contact your VMS system", "administrator or support person.",NULL}; pop_log(LOG_INFO, p, "foreign mail: send informational message only"); for (i=0;(p->retrieve.lines_sent < p->retrieve.message_lines) && body[i]; p->retrieve.lines_sent++, i++) netbprintf (p,"%s\r\n",body[i]); if (p->retrieve.message_lines) netb_end (p); p->retrieve.lines_sent = p->retrieve.message_lines; } /* * some sanity checks */ if (!(p->retrieve.send_buffer || allocate_send_buffer (p))) return FALSE; itemopen(inlist_ptr, inlist); itemadd(inlist_ptr, 0, MAIL$_MESSAGE_CONTINUE, 0, 0); itemadd(inlist_ptr, 0, MAIL$_NOSIGNAL, 0, 0); itemclose(inlist_ptr); itemopen(outlist_ptr, outlist); itemadd(outlist_ptr, ITEM_LENGTH, MAIL$_MESSAGE_RECORD, p->retrieve.buffer, &p->retrieve.bufsize); itemadd(outlist_ptr, 0, MAIL$_NOSIGNAL, 0, 0); itemclose(outlist_ptr); if (!p->retrieve.send_bufsize && (p->retrieve.lines_sent == p->retrieve.message_lines)) { /* we are ready! */ p->retrieve.flags |= RETR_COMPLETED_FLAG; p->retrieve.flags ^= RETR_IN_PROGRESS_FLAG; if (p->retrieve.flags & RETR_IS_RETRIEVE_FLAG) /* else a "top" command */ { int size; bintime_type end_time, diff_time; sys$gettim (&end_time); lib$sub_times (&end_time,&p->retrieve.start_time,&diff_time); pop_log(LOG_THREAD, p, "Msg. %d, %d bytes, sent in %s", retrieved_messages,p->retrieve.send_octets, (time_from_bintime(&diff_time,0)+5)); retrieved_messages++; size = p->retrieve.send_octets; retrieved_octets += size; if (size > maximum_octets) maximum_octets = size; if (size < minimum_octets) minimum_octets = size; } /* closing the message context from time to time reduces virtual memory usage significant */ if (p->memory_usage > (jpi_wsquota/2)) { pop_log (LOG_THREAD,p, "releasing %d kb memory (estimated)",p->memory_usage*512); p->memory_usage = 0; mail_close_message_context (p); } close_external_file (p); } else { int send_octets, max_octets, bytestuff; reading = TRUE; /* fill send buffer */ while ((p->retrieve.lines_sent < p->retrieve.message_lines) && reading) { max_octets = ((p->retrieve.send_buffer_length - 12) - p->retrieve.send_bufsize); if (!p->retrieve.bufsize) /* else old line not yet processed */ { status = get_message_line (p, inlist, outlist); if (vms_error(status)) { pop_log (LOG_ERROR, p, "get_message_line (body) (line %d of %d): %s", p->retrieve.lines_sent, p->retrieve.message_lines, vms_message(status)); p->retrieve.lines_sent = p->retrieve.message_lines - 1; p->retrieve.bufsize = 0; if ((status != MAIL$_NOMOREREC) && (p->retrieve.flags & RETR_IS_RETRIEVE_FLAG)) p->retrieve.flags ^= RETR_IS_RETRIEVE_FLAG; /* do not mark as retrieved */ } bytestuff = TRUE; } else { status = SS$_NORMAL; bytestuff = FALSE; } send_octets = netbputs (p,p->retrieve.line, Min(p->retrieve.bufsize,max_octets),bytestuff); if (p->retrieve.bufsize > send_octets) { reading = FALSE; p->retrieve.bufsize -= send_octets; p->retrieve.line += send_octets; } else if (p->retrieve.ext_file && (p->retrieve.line[p->retrieve.bufsize] != '\n') && !vms_error(status)) { p->retrieve.bufsize = 0; /* process next part of long record */ if (!p->retrieve.enable_long_lines) netbputs(p,"\r\n",2,FALSE);/* insert line break */ } else { netbputs (p,"\r\n",2,FALSE); p->retrieve.lines_sent++; p->retrieve.bufsize = 0; } if (p->retrieve.lines_sent == p->retrieve.message_lines) { netbputs (p,"\r\n",2,FALSE); netb_end (p); } } /* Perform the asynchron network write */ p->retrieve.send_octets += p->retrieve.send_bufsize; netwrite_async (p, p->retrieve.send_buffer, p->retrieve.send_bufsize); } return TRUE; } /* ======================================================================== */ /* Mail User Info */ /* ======================================================================== */ int mail_user_info (POP *p) { int status; int length = 0; if (!p->user_context) mail_open_user_context(p); itemopen(inlist_ptr,inlist); itemadd(inlist_ptr,strlen(p->user),MAIL$_USER_USERNAME, &p->user,0); itemadd(inlist_ptr,0,MAIL$_NOSIGNAL,0,0); itemclose(inlist_ptr); itemopen(outlist_ptr,outlist); itemadd(outlist_ptr,sizeof(p->mail_directory)-1,MAIL$_USER_FULL_DIRECTORY, p->mail_directory,&length); itemadd(outlist_ptr,0,MAIL$_NOSIGNAL,0,0); itemclose(outlist_ptr); status = mail$user_get_info (&p->user_context,inlist,outlist); if (vms_error(status)) { p->mail_directory[0] = '\0'; if (status == MAIL$_NOSUCHUSR) { p->has_no_mail_file = TRUE; status = SS$_NORMAL; } else pop_log (LOG_ERROR, p, "mail$user_get_info: %s", vms_message(status)); } else { p->mail_directory[length] = '\0'; } mail_close_user_context(p); return status; } /* ========================================================================= */ /* Valid VMS User */ /* ========================================================================= */ int valid_vms_user (POP *p) { bintime_type expiration_time = BINTIME_INIT_ZERO; /* absolute time value */ bintime_type pwd_date = BINTIME_INIT_ZERO; /* date of the last pwd change */ bintime_type pwd_lifetime = BINTIME_INIT_ZERO; /* pwd lifetime, delta time */ bintime_type pwd2_date = BINTIME_INIT_ZERO; /* date of the last pwd change */ int pwd_has_expired = FALSE; int status; int retval = FALSE; int length = 0; int check_uai_flags = FALSE; uai_flags_u uai_flags; itemopen(outlist_ptr, outlist); itemadd(outlist_ptr, sizeof(uai_flags), UAI$_FLAGS, &uai_flags, &length); itemadd(outlist_ptr, sizeof expiration_time, UAI$_EXPIRATION, &expiration_time, 0); itemadd(outlist_ptr, sizeof pwd_date, UAI$_PWD_DATE, &pwd_date, 0); itemadd(outlist_ptr, sizeof pwd_lifetime, UAI$_PWD_LIFETIME, &pwd_lifetime, 0); itemadd(outlist_ptr, sizeof pwd2_date, UAI$_PWD2_DATE, &pwd2_date, 0); itemclose(outlist_ptr); status = sys$getuai(0, 0, desz(upper(p->user)), outlist, 0, 0, 0); lower(p->user); if (vms_error(status)) pop_log(LOG_ERROR, p, "sys$getuai: %s", vms_message(status)); else { check_uai_flags = !(FLAGS_DISUSER_DISMAIL); if (!ignore_expired_passwords && check_uai_flags) { bintime_type pwd_expiration = BINTIME_INIT_ZERO; check_uai_flags = !(FLAGS_EXPIRED_PASSWORD); if (!BINTIME_IS_ZERO(pwd_lifetime)) { status = lib$add_times(&pwd_lifetime,&pwd_date,&pwd_expiration); if (vms_error(status)) pop_log(LOG_ERROR, p, "lib$add_times: %s", vms_message(status)); else pwd_has_expired = Expired(pwd_expiration); } } if (check_uai_flags) { if (Expired(expiration_time)) { retval = FALSE; pop_log(LOG_ERROR, p, "account of \"%s\" has expired!", p->user); } else if (pwd_has_expired) { retval = FALSE; pop_log(LOG_ERROR, p, "password of \"%s\" has expired!", p->user); } else { retval = TRUE; } } else pop_log(LOG_ERROR, p, "account \"%s\"is administratively disabled", p->user); } return retval; } /* ========================================================================== */ /* returns true (1) if current time is newer than expiration_time */ /* ========================================================================== */ static int Expired (bintime_type expiration_time) { int has_expired = FALSE; if (!BINTIME_IS_ZERO(expiration_time)) /* expiration date set ? */ { bintime_type current_time; sys$gettim (¤t_time); has_expired = (BINTIME_COMPARE(current_time,expiration_time) > 0); } return has_expired; } /* ========================================================================== */ /* VMS Message String */ /* ========================================================================== */ char *vms_message(unsigned int code) { static char message[256]; static struct dsc$descriptor_s s_des = {sizeof message - 1, DSC$K_DTYPE_T, DSC$K_CLASS_S, message}; unsigned short length = 0; memset (message,'\0',sizeof message); sys$getmsg (code,&length, &s_des,0xf,0); message[length] = '\0'; return message; } /* ======================================================================== */ /* Mail Delete Message */ /* ======================================================================== */ int mail_delete_message (POP *p,int message_id) { int length = 0, status; Message *mp = &p->mptr[message_id-1]; if (mp->number != mp->nummbx) pop_log (LOG_DEBUG, p, "message %d is number %d in maildrop ",mp->number,mp->nummbx); if (!p->newmail_selected) { status = mail_folder_select (p,p->folder_name,NULL); if (vms_error(status) || (mp->msg_flags & MSG_UNAVAILABLE_FLAG)) return FALSE; } if (master_log_level >= LOG_DEBUG) { char filname[260]; itemopen (inlist_ptr, inlist); itemadd (inlist_ptr,sizeof(int *),MAIL$_MESSAGE_ID,&mp->nummbx,0); itemadd (inlist_ptr,0,MAIL$_NOSIGNAL,0,0); itemclose (inlist_ptr); itemopen (outlist_ptr, outlist); itemadd (outlist_ptr,ITEM_LENGTH,MAIL$_MESSAGE_EXTID,filname,&length); itemclose (outlist_ptr); status = mail$message_info (&p->message_context,inlist,outlist); filname[length] = '\0'; if (length) pop_log(LOG_DEBUG, p, "deleting file %s ",filname); else pop_log(LOG_DEBUG, p, "deleting "); } itemopen(inlist_ptr,inlist); itemadd(inlist_ptr,sizeof(mp->nummbx),MAIL$_MESSAGE_ID,&mp->nummbx,0); itemadd(inlist_ptr,0,MAIL$_NOSIGNAL,0,0); itemclose(inlist_ptr); status = mail$message_delete(&p->message_context,inlist,nullist); if (vms_error(status)) { pop_log(LOG_ERROR, p, "message_delete (info): %s", vms_message(status)); status = FALSE; } else status = TRUE; return status; } /* ======================================================================== */ /* Mail Message Move */ /* ======================================================================== */ int mail_message_move (POP *p, char *target_folder, int message_id) { int length = 0,status = TRUE; Message *mp = p->mptr ? &p->mptr[message_id-1] : NULL; if (!p->newmail_selected) status = mail_folder_select(p, p->folder_name,NULL); if (vms_error(status)) status = FALSE; if (mp) { if (mp->msg_flags & MSG_UNAVAILABLE_FLAG) status = FALSE; if (mp->number != mp->nummbx) pop_log (LOG_DEBUG, p, "message %d is number %d in maildrop ",mp->number,mp->nummbx); } if (status && strcmp (p->folder_name, target_folder)) { /* no action if target is the current folder */ strcpy (p->retrieve.buffer,p->mail_directory); strncat (p->retrieve.buffer,"MAIL.MAI", sizeof p->retrieve.buffer - sizeof "MAIL.MAI"); itemopen(inlist_ptr,inlist); itemadd(inlist_ptr,sizeof(message_id),MAIL$_MESSAGE_ID,&message_id,0); itemadd(inlist_ptr,strlen(p->retrieve.buffer),MAIL$_MESSAGE_DEFAULT_NAME, &p->retrieve.buffer,0); itemadd(inlist_ptr,strlen(target_folder), MAIL$_MESSAGE_FOLDER,target_folder,0); itemadd(inlist_ptr,0,MAIL$_MESSAGE_DELETE,0,0); itemadd(inlist_ptr,0,MAIL$_NOSIGNAL,0,0); itemclose(inlist_ptr); status = mail$message_copy(&p->message_context,inlist,nullist); if (vms_error(status)) { pop_log(LOG_ERROR, p, "mail$message_copy: %s", vms_message(status)); status = FALSE; } else status = TRUE; pop_log(LOG_DEBUG, p, "mail_message_move: message #%d moved to %s", message_id, target_folder ); } else if (status) pop_log(LOG_DEBUG, p, "mail_message_move: message #%d already in %s", message_id, target_folder); return status; } /* ======================================================================== */ /* Mail set newmail count */ /* ======================================================================== */ int mail_message_new_count (POP *p, int moved_mail_count) { int status = SS$_NORMAL; int new_mail_count = 0; int length = 0; /* * set new message count only if NEWMAIL folder is selected */ if (strcmp(p->folder_name,NEWMAIL_FOLDER) || !moved_mail_count) return status; if (!p->user_context) mail_open_user_context(p); /* get current new_mail count */ itemopen(inlist_ptr,inlist); itemadd(inlist_ptr,strlen(p->user),MAIL$_USER_USERNAME, &p->user,0); itemadd(inlist_ptr,0,MAIL$_NOSIGNAL,0,0); itemclose(inlist_ptr); itemopen(outlist_ptr,outlist); itemadd(outlist_ptr,sizeof (short int),MAIL$_USER_NEW_MESSAGES, &new_mail_count, &length); itemclose(outlist_ptr); status = mail$user_get_info (&p->user_context,inlist,outlist); if (vms_error(status)) pop_log(LOG_ERROR, p, "mail$user_get_info: %s", vms_message(status)); new_mail_count -= moved_mail_count; if (new_mail_count < 0) new_mail_count = 0; itemopen(inlist_ptr,inlist); itemadd(inlist_ptr,strlen(p->user),MAIL$_USER_USERNAME, &p->user,0); itemadd(inlist_ptr, sizeof (short int),MAIL$_USER_SET_NEW_MESSAGES, &new_mail_count,0); itemadd(inlist_ptr,0,MAIL$_NOSIGNAL,0,0); itemclose(inlist_ptr); status = mail$user_set_info(&p->user_context,inlist,nullist); if (vms_error(status)) pop_log(LOG_ERROR, p, "mail$user_set_info: %s", vms_message(status)); else pop_log(LOG_DEBUG, p, "newmail count changed to %d", new_mail_count); mail_close_user_context (p); return status; } /* ======================================================================== */ /* Autopurge Status */ /* ======================================================================== */ #define AUTOPURGE_ENABLED -1 #define AUTOPURGE_DISABLED -2 int is_autopurge (POP *p) { int autopurge = FALSE; int status; /* * Get the status of the user's auto_purge flag. */ if (!p->user_context) mail_open_user_context(p); if (!p->file_context) mail_open_user_context(p); itemopen(inlist_ptr,inlist); itemadd(inlist_ptr,strlen(p->user),MAIL$_USER_USERNAME,&p->user,0); itemadd(inlist_ptr,0,MAIL$_NOSIGNAL,0,0); itemclose(inlist_ptr); itemopen(outlist_ptr,outlist); itemadd(outlist_ptr,sizeof autopurge,MAIL$_USER_AUTO_PURGE,&autopurge,0); itemclose(outlist_ptr); status = mail$user_get_info (&p->user_context,inlist,outlist); if (vms_error(status)) { pop_log(LOG_ERROR, p, "mail$user_get_info: %s", vms_message(status)); autopurge = 0; } mail_close_user_context (p); return (autopurge == AUTOPURGE_ENABLED); } /* ======================================================================== */ /* MailFile Purge Waste */ /* ======================================================================== */ int mail_purge_waste (POP *p) { int status = SS$_NORMAL; int autopurge = is_autopurge(p); int length = 0; int deleted_bytes = 0, messages_deleted = 0; #if defined ALPHA && !defined __GNUC__ vaxc$establish(lib$sig_to_ret); /* return to caller on error */ #endif if (p->message_context) mail_close_message_context(p); if (autopurge) { char wastebasket_name[MAX_FOLDER_LENGTH+1]; int num_messages = 0; bintime_type current_time = BINTIME_INIT_ZERO; bintime_type timeout_time = BINTIME_INIT_ZERO; /* * Get the name of the WASTEBASKET folder. */ itemopen(inlist_ptr,inlist); itemadd(inlist_ptr,0,MAIL$_NOSIGNAL,0,0); itemclose(inlist_ptr); itemopen(outlist_ptr,outlist); itemadd(outlist_ptr,MAX_FOLDER_LENGTH,MAIL$_MAILFILE_WASTEBASKET, wastebasket_name,&length); itemclose(outlist_ptr); status = mail$mailfile_info_file (&p->file_context,inlist,outlist); if (vms_error(status)) { pop_log(LOG_ERROR, p, "mail$mailfile_purge_waste: %s",vms_message(status)); return POP_FAILURE; } wastebasket_name[length] = '\0'; /* * Check if there are too many messages in WASTEBASKET. * If true move some files to a temp. folder. * If empty, move some files back from the temp. folder. */ p->folder_name = wastebasket_name; status = mail_folder_select(p,wastebasket_name,&num_messages); timeout_time = get_binary_time (COMMAND_TIMEOUT); /* two seconds */ if (num_messages > MAX_PURGE_MESSAGES) { Message *mp; int msg_num; int msg_moved = 0; p->msg_count = num_messages; pop_build_info(p); mp = p->mptr; sys$gettim (¤t_time); lib$add_times (¤t_time,&timeout_time,&timeout_time); for (msg_num=1; msg_num <= num_messages; msg_num++, mp++) { if (mp->msg_flags & MSG_EXTERNAL_FLAG) { status = mail_message_move (p, TEMP_WASTE_FOLDER, msg_num); msg_moved++; } sys$gettim (¤t_time); if (BINTIME_COMPARE(current_time,timeout_time) > 0) { pop_log (LOG_THREAD, p, "timeout after moving %d out of %d messages to %s", msg_moved, num_messages,TEMP_WASTE_FOLDER); p->attn_state = ATTN_PURGE; return POP_SUCCESS; } } if (msg_moved) pop_log (LOG_DEBUG, p, "moving %d out of %d messages to %s", msg_moved,num_messages,TEMP_WASTE_FOLDER); } else if (!num_messages) { p->folder_name = TEMP_WASTE_FOLDER; status = mail_folder_select(p,TEMP_WASTE_FOLDER,&num_messages); if (num_messages) { int gl_fast_scan = fast_scan; int msg_num, i; Message *mp; unsigned int *msg_date; p->msg_count = num_messages; fast_scan = TRUE; pop_build_info(p); fast_scan = gl_fast_scan; /* sort the mail$nnnnnnnn.mai files from top to down */ qsort (p->mptr, num_messages, sizeof (Message), compar_bindate_low); sys$gettim (¤t_time); lib$add_times (¤t_time,&timeout_time,&timeout_time); mp = p->mptr; for (msg_num=1; (msg_num <= num_messages) && !vms_error(status); msg_num++, mp++) { status = mail_delete_message (p,msg_num); messages_deleted++; if (!(messages_deleted&1)) { itemopen(inlist_ptr,inlist); itemadd(inlist_ptr,0,MAIL$_NOSIGNAL,0,0); itemclose(inlist_ptr); itemopen(outlist_ptr,outlist); itemadd(outlist_ptr,sizeof (int),MAIL$_MAILFILE_DELETED_BYTES, &deleted_bytes,&length); itemclose(outlist_ptr); status = mail$mailfile_purge_waste(&p->file_context,inlist,outlist); if (vms_error(status)) pop_log(LOG_ERROR, p, "mail$mailfile_purge_waste: %s",vms_message(status)); sys$gettim (¤t_time); if (BINTIME_COMPARE(current_time,timeout_time) > 0) break; } } pop_log(LOG_THREAD, p, "%d messages deleted, %d deleted message bytes", messages_deleted,deleted_bytes); } } if (num_messages) p->attn_state = ATTN_PURGE; /* * purge the wastebasket folder without purge_reclaim operation. */ /* inlist same as above */ itemopen(inlist_ptr,inlist); itemadd(inlist_ptr,0,MAIL$_NOSIGNAL,0,0); itemclose(inlist_ptr); itemopen(outlist_ptr,outlist); itemadd(outlist_ptr,sizeof (int),MAIL$_MAILFILE_DELETED_BYTES, &deleted_bytes,&length); itemadd(outlist_ptr,sizeof (int),MAIL$_MAILFILE_MESSAGES_DELETED, &messages_deleted,&length); itemclose(outlist_ptr); status = mail$mailfile_purge_waste(&p->file_context,inlist,outlist); if (vms_error(status)) { pop_log(LOG_ERROR, p, "mail$mailfile_purge_waste: %s",vms_message(status)); return POP_FAILURE; } if (messages_deleted) pop_log(LOG_THREAD, p, "%d messages deleted, %d deleted message bytes", messages_deleted,deleted_bytes); /* * Now check if the deleted message bytes are greater then the * purge_reclaim_threshold and issue a purge_reclaim operation * if necessary. * Purge/reclaim only after all messages purged. */ if (purge_reclaim_threshold && (deleted_bytes > purge_reclaim_threshold) && (p->attn_state != ATTN_PURGE)) { int total_reclaim = 0, data_scan = 0; int tries = 0; pop_log(LOG_THREAD, p, "performing PURGE/RECLAIM operation on mailbox"); /* On some systems (here: VMS VAX 5.3-1 + 5.5-2) purge_reclaim does not work with MAIL$_NOSIGNAL enabled */ do { itemopen(inlist_ptr,inlist); itemadd(inlist_ptr,0,MAIL$_MAILFILE_RECLAIM,0,0); if (!tries) {itemadd(inlist_ptr,0,MAIL$_NOSIGNAL,0,0);}; itemclose(inlist_ptr); itemopen(outlist_ptr,outlist); itemadd(outlist_ptr,sizeof (int),MAIL$_MAILFILE_DATA_SCAN, &data_scan,&length); itemadd(outlist_ptr,sizeof (int),MAIL$_MAILFILE_TOTAL_RECLAIM, &total_reclaim,&length); itemclose(outlist_ptr); status = mail$mailfile_purge_waste(&p->file_context,inlist,outlist); } while ((status == MAIL$_RECLPLSWAIT) && !tries++); if (vms_error(status) || !data_scan) { if ((status == 8261786) || !data_scan) { /* %MAIL-E-OPENIN, error opening !AS as input */ pop_log (LOG_ERROR, p, "purge_reclaim: mail file locked by another user"); } else pop_log(LOG_ERROR, p, "purge_reclaim: %s",vms_message(status)); /* The context is now corrupted. */ status = mail$mailfile_end(&p->file_context,nullist,nullist); p->attn_state = ATTN_SLEEP; } pop_log(LOG_THREAD, p, "%d messages scanned, %d reclaimed ", data_scan,total_reclaim); } } else pop_log (LOG_DEBUG, p, "mail_purge_waste: autopurge is disabled"); return POP_SUCCESS; } /* ======================================================================== */ /* compar_bindate_low 64bit time values */ /* Evaluates the order of the mail$nnnnnnnnnnnnnnnn.mai files */ /* ======================================================================== */ static int compar_bindate_low (const void *e1, const void *e2) { unsigned short int *s1 = (unsigned short int *) &(((Message *)e1)->bindate); unsigned short int *s2 = (unsigned short int *) &(((Message *)e2)->bindate); int result; if (s1[2] == s2[2]) { if (s1[1] == s2[1]) result = 0; else if (s1[1] > s2[1]) result = -1; else result = 1; } else if (s1[2] > s2[2]) result = -1; else result = 1; #if 0 system_log(LOG_THREAD, "comparing (MAIL$%04.4X%04.4X*.MAI) with (MAIL$%04.4X%04.4X*.MAI) (=%d)", s1[2],s1[1],s2[2],s2[1],result); #endif return result; } /* ======================================================================== ** update_maildrop: ** 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 update_maildrop (POP *p) { Message *mp; char *foreign_folder = getenv ("IUPOP3_FOREIGN_FOLDER"); int msg_num; /* Current message counter */ int msg_count; int moved_num = 0; /* Number of moved messages */ int status, deleted_mails = FALSE; int autopurge = (is_autopurge(p) && purge_mailboxes); bintime_type current_time = BINTIME_INIT_ZERO; bintime_type timeout_time = BINTIME_INIT_ZERO; msg_count = p->retrieve.message_lines; pop_log(LOG_DEBUG, p, "updating %s\'s maildrop", p->user); /* * now clean the maildrop */ timeout_time = get_binary_time (COMMAND_TIMEOUT); /* two seconds */ sys$gettim (¤t_time); lib$add_times (¤t_time,&timeout_time,&timeout_time); if (!foreign_folder) foreign_folder = MAIL_FOLDER; mp = p->mptr + p->retrieve.send_count; for (msg_num = p->retrieve.send_count + 1; msg_num <= msg_count; msg_num++, mp++) { /* ** Get a pointer to the message information list */ if (mp->msg_flags & MSG_UNAVAILABLE_FLAG) ; /* just ignore */ else if (mp->msg_flags & MSG_DEL_FLAG) { if (mp->msg_flags & MSG_FOREIGN_FLAG) { pop_log(LOG_THREAD, p, "moving foreign message id #%d to \"%s\"", msg_num, foreign_folder); status = mail_message_move (p, foreign_folder, msg_num); } else { pop_log(LOG_DEBUG, p, "deleting message #%d", msg_num); if (autopurge && (mp->msg_flags & MSG_EXTERNAL_FLAG)) status = mail_message_move (p, TEMP_WASTE_FOLDER, msg_num); else status = mail_delete_message (p, msg_num); if (status) deleted_mails = TRUE; } if (status) { p->msg_count--; moved_num++; } } else if (mp->msg_flags & MSG_RETR_FLAG) { pop_log(LOG_DEBUG, p, "moving message #%d to MAIL folder", msg_num); status = mail_message_move (p, MAIL_FOLDER, msg_num); if (status) { p->msg_count--; moved_num++; } } p->retrieve.send_count++; sys$gettim (¤t_time); if (BINTIME_COMPARE(current_time,timeout_time) > 0) { /* continue after checking the other threads */ if (moved_num) mail_message_new_count (p, moved_num); pop_log(LOG_THREAD, p, "timeout, %d messages out of %d processed", p->retrieve.send_count,msg_count); p->attn_state = ATTN_UPDATE; return POP_SUCCESS; } } /* --for loop */ p->retrieve.flags |= RETR_PURGING_FLAG; if (purge_mailboxes) p->attn_state = ATTN_PURGE; else p->attn_state = ATTN_CLOSE; if (moved_num) status = mail_message_new_count (p, moved_num); if (p->mptr) { free (p->mptr); p->mptr = NULL; } return POP_SUCCESS; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - GET_SHARED_SECRET Gets the shared secret used for the APOP command from a file. Inputs: p Pointer to the private data of current thread len Maximum length of the shared secret. shared_secret Pointer to the buffer for the shared secret. Outputs: shared_secret Pointer to the buffer filled with the shared secret. Function Return Value: status Error condition Comments: The filename is APOP_FILENAME and resides in the user's mail directory. The first record is used as shread secret. Revision History: 1.0 Michael Stenns April 1997 Original Version - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ int get_shared_secret (POP *p, char * shared_secret, int len) { FILE * secret_file; char *tmp; int status = 0; *shared_secret = '\0'; if ((mail_open_user_context(p) == SS$_NORMAL) && (mail_user_info(p) == SS$_NORMAL) && (mail_close_user_context(p) == SS$_NORMAL) ) { strcpy (p->retrieve.buffer,p->mail_directory); strncat (p->retrieve.buffer, APOP_FILENAME, sizeof p->retrieve.buffer - sizeof APOP_FILENAME); secret_file = fopen (p->retrieve.buffer, "r", "shr=get"); if (secret_file) { fgets (shared_secret, len, secret_file); fclose (secret_file); len = strlen (shared_secret); for (tmp = &shared_secret[len-1]; len && isspace(*tmp); len--) *tmp-- = '\0'; if (len >= MIN_PASSWORD_LENGTH) status = 1; else { pop_log(LOG_ERROR, p, "shared secret for user %s has insufficient length", p->user); *shared_secret = '\0'; } } else { pop_log(LOG_DEBUG, p, "failed to open shared secret file for user %s", p->user); } } else { pop_log(LOG_ERROR, p, "apop: failed to get maildir for user %s", p->user); } return status; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - GET_MESSAGE_LINE Gets the next record of the current message. The record is either read via the mail$message_get callable mail routine, from the internal data of callable mail (using some undocumented features) or direct from the file with ansi-c functions. Inputs: p Pointer to the private data of current thread inlist Pointer to itmlst_3 type item list. outlist Pointer to itmlst_3 type item list. Outputs: p->retrieved.line Pointer to next message record p->retrieved.bufsize Length of next message record p->retrieve.ext_file FILE-pointer to external message file or NULL Function Return Value: status Error condition Comments: If callable mail fails reading a external message, the file is opened and read directly. The line pointer 'p->retrieved.line' points either to the thread's data buffer 'p->retrieved.buffer' or to the start of the record in the mail11's internal data structure ('enable_long_lines' is true). When the data are read from file ('p->retrieve.ext_file' is not NULL), the '\n' character can be used to check if the record is complete. Remark: the string returned in 'p->retrieved.line' is not necessary zero terminated! Revision History: 1.0 Michael Stenns July 1997 Original Version 1.1 Michael Stenns Nov 1997 fixes for long record support - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ static int get_message_line (POP *p, ITEMLIST *inlist, ITEMLIST *outlist) { int status; p->retrieve.bufsize = 0; p->retrieve.line = p->retrieve.buffer; if (p->retrieve.ext_file) { if (fgets(p->retrieve.buffer, sizeof(p->retrieve.buffer)-4, p->retrieve.ext_file)) { status = SS$_NORMAL; p->retrieve.bufsize = strlen (p->retrieve.buffer); /* the '\n' is later used to detect if record * is complete, so don't overwrite it here! */ if (p->retrieve.bufsize && p->retrieve.buffer[p->retrieve.bufsize-1] == '\n') p->retrieve.bufsize--; } else { p->retrieve.bufsize = 0; if (feof(p->retrieve.ext_file)) status = MAIL$_NOMOREREC; else if (ERRNO == EVMSERR) status = vaxc$errno; else status = errno; } } else { if (p->retrieve.flags & RETR_RESTORE_FLAG) status = restore_position (p); /* go to top of message body */ else if (p->retrieve.enable_long_lines) status = mail__get_message_record (p); else status = mail$message_get (&p->message_context,inlist,outlist); if (vms_error(status)) { if (((status == MAIL$_NOMOREREC) && (p->retrieve.lines_sent < 10)) || (status == MAIL$_RECTOBIG)) { /* this is probably through a callable mail bug */ ITEMLIST inlist[3], outlist[3]; ITEMLIST *inlist_ptr, *outlist_ptr; int itmp; p->retrieve.bufsize = 0; itemopen (inlist_ptr, inlist); itemadd(inlist_ptr, sizeof(p->retrieve.mp->nummbx), MAIL$_MESSAGE_ID, &p->retrieve.mp->nummbx, 0); itemadd (inlist_ptr,0,MAIL$_NOSIGNAL,0,0); itemclose (inlist_ptr); itemopen (outlist_ptr, outlist); itemadd (outlist_ptr,ITEM_LENGTH, MAIL$_MESSAGE_EXTID,p->retrieve.buffer,&p->retrieve.bufsize); itemadd (outlist_ptr,0,MAIL$_NOSIGNAL,0,0); itemclose (outlist_ptr); status = mail$message_info (&p->message_context,inlist,outlist); if (vms_error(status) || !p->retrieve.bufsize) { if (vms_error(status)) pop_log(LOG_ERROR, p, "mail$message_info: %s", vms_message(status)); else { pop_log (LOG_DEBUG, p, "not a external message"); status = MAIL$_NOMOREREC; } } else { p->retrieve.buffer[p->retrieve.bufsize++] = '\0'; itmp = strlen (p->mail_directory); memmove (p->retrieve.buffer + itmp, p->retrieve.buffer, p->retrieve.bufsize); memcpy (p->retrieve.buffer, p->mail_directory, itmp); p->retrieve.ext_file = fopen (p->retrieve.buffer,"r","shr=get","mbc=64","mbf=64"); if (!p->retrieve.ext_file) { pop_log (LOG_DEBUG, p, "failure opening external file %s", p->retrieve.buffer); status = MAIL$_NOMOREREC; } else { /* * Mail files are sequential stream files with carriage return * carriage control. The record format is variable length, but no * maximum defined. Standard C-functions like fgets() fails on * large records if the associated buffer is not enlarged. */ status = setvbuf (p->retrieve.ext_file,NULL,_IOFBF,MAXRECORDLEN); if (status) /* 0 indicates success */ { /* seems to fail with VAXCRTL */ p->retrieve.ext_file_buf = malloc (MAXRECORDLEN); /* max. record size */ if (p->retrieve.ext_file_buf) status = setvbuf (p->retrieve.ext_file, p->retrieve.ext_file_buf,_IOFBF,MAXRECORDLEN); if (status) pop_log (LOG_ERROR,p,"associating buffer with external file failed"); } pop_log (LOG_THREAD, p, "process file %s",p->retrieve.buffer); p->retrieve.bufsize = 0; /* for callable mail compability */ p->retrieve.buffer[0] = '\n'; p->retrieve.buffer[1] = '\0'; itmp = p->retrieve.lines_sent; /* spool to the right position */ do { do { status = get_message_line (p, NULL, NULL); } while (!vms_error(status) && (p->retrieve.line[p->retrieve.bufsize] != '\n')); } while (itmp--); status = SS$_NORMAL; } } } else pop_log(LOG_DEBUG, p, "get_message_line: %s (%s)", vms_message(status), vms_strerror(ERRNO)); } else if (!p->retrieve.enable_long_lines) /* not vms_error(status) */ p->retrieve.buffer[p->retrieve.bufsize] = '\0'; } return status; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - RESTORE_POSITION Restores the position to the first line of the message. The mail11 header lines are skipped. Inputs: p Pointer to the private data of current thread Outputs: p->retrieve.tbh Address of first message block p->retrieve.dataptr NULL p->retrieve.line Pointer to the first message record p->retrieve.bufsize Length of the first message record Function Return Value: status Error condition Comments: Revision History: 1.0 Michael Stenns July 1997 Original Version - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ static int restore_position (POP *p) { int status; int rtype = 0; /* record type (head or body) */ if (p->retrieve.flags & RETR_USE_ANSIC_FLAG) { /* do not process with callable mail */ status = MAIL$_NOMOREREC; return status; } /* open message */ itemopen(inlist_ptr, inlist); itemadd(inlist_ptr, sizeof(p->retrieve.mp->nummbx), MAIL$_MESSAGE_ID, &p->retrieve.mp->nummbx, 0); itemadd(inlist_ptr, 0, MAIL$_NOSIGNAL, 0, 0); itemclose(inlist_ptr); status = mail$message_get(&p->message_context, inlist, nullist); if (vms_error(status)) pop_log(LOG_DEBUG, p, "mail$message_get (rest. 1): %s", vms_message(status)); /* scroll through mail11 headers */ itemopen(inlist_ptr,inlist); itemadd(inlist_ptr,0,MAIL$_MESSAGE_CONTINUE,0,0); itemadd(inlist_ptr,0,MAIL$_NOSIGNAL,0,0); itemclose(inlist_ptr); itemopen(outlist_ptr,outlist); itemadd(outlist_ptr,ITEM_LENGTH,MAIL$_MESSAGE_RECORD, p->retrieve.buffer,&p->retrieve.bufsize); itemadd(outlist_ptr,2,MAIL$_MESSAGE_RECORD_TYPE,&rtype,0); itemadd(outlist_ptr,sizeof (TEXTBLOCKHEADER *),MAIL$_MESSAGE_BUFFER,&p->retrieve.tbh,0); itemadd(outlist_ptr,0,MAIL$_NOSIGNAL,0,0); itemclose(outlist_ptr); do { status = mail$message_get (&p->message_context,inlist,outlist); } while (!vms_error(status) && (rtype == MAIL$_MESSAGE_HEADER)); if (vms_error(status)) pop_log(LOG_DEBUG, p, "mail$message_get (rest. 2): %s", vms_message(status)); p->retrieve.flags ^= RETR_RESTORE_FLAG; p->retrieve.dataptr = NULL; if (p->retrieve.enable_long_lines) status = mail__get_message_record (p); else { /* restore outlist for get_message_line() */ itemopen(outlist_ptr,outlist); itemadd(outlist_ptr,ITEM_LENGTH,MAIL$_MESSAGE_RECORD, p->retrieve.buffer,&p->retrieve.bufsize); itemadd(outlist_ptr,0,MAIL$_NOSIGNAL,0,0); itemclose(outlist_ptr); } return status; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - MAIL__PARSE_RECORD Translate a pointer to a record, extract the length and the data of that record and adjust the pointers to the next record after it. Inputs: p Pointer to the private data of current thread Outputs: p->retrieve.line Pointer to the message reccord p->retrieve.bufsize Length of the message record p->retrieve.dataptr Pointer to the pointer to the next record Function Return Value: Error condition. Comments: The record format is slightly different between internal and external messages: In the external case, the records are padded to EVEN length; in the internal case, they are not padded. This requires some special adjustment to the record pointer to ensure we stay in sync. Thanks to Michael Hitch for this detail. Revision History: 1.0 Andy Harper August 1997 Original Version 1.1 Michael Stenns August 1997 some modifications for IUPOP3 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ static int mail__parse_record (POP *p) { unsigned short int rlen = ((RECORDHEADER *)(p->retrieve.dataptr))->length; int status = SS$_NORMAL; p->retrieve.dataptr += sizeof(RECORDHEADER); p->retrieve.line = p->retrieve.dataptr; p->retrieve.bufsize = rlen; p->retrieve.dataptr += rlen; if ((p->retrieve.flags & RETR_IS_EXTERNAL_FLAG) && (rlen & 1)) p->retrieve.dataptr++; return status; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - MAIL__GET_MESSAGE_RECORD From the supplied context, get the next record from the message buffer and update the context. Inputs: p Pointer to the private data of current thread Outputs: p->retrieve.tbh Address of current message block p->retrieve.dataptr Pointer to the pointer to the next record p->retrieve.line Pointer to the message record p->retrieve.bufsize Length of the message record Function Return Value: Error condition. Comment: This routine parses the undocumented message structure used by callable mail to get the next message record. See above for details. Revision History: 1.0 Andy Harper August 1997 Original Version 1.1 Michael Stenns August 1997 some modifications for IUPOP3 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ static int mail__get_message_record (POP *p) { int status = SS$_NORMAL; if (p->retrieve.tbh) { /* Initialize if first entry in block */ if (!p->retrieve.dataptr) p->retrieve.dataptr = &p->retrieve.tbh->first; /* Parse out next record */ status = mail__parse_record(p); /* If now at end of block, move to next one */ if (p->retrieve.dataptr >= (&p->retrieve.tbh->first+p->retrieve.tbh->size)) { p->retrieve.tbh = p->retrieve.tbh->next; p->retrieve.dataptr = NULL; } } else status = MAIL$_NOMOREREC; return status; } /* * The following code needs OpenVMS 6.2 alpha or OpenVMS 6.1 vax */ #ifndef __VMS_VER /* old compilers did not define __vms_ver */ #ifdef ALPHA #ifdef NO_SCAN_INTRUSION #define __VMS_VER 10500000 #else #define __VMS_VER 60200000 #endif #endif /* ALPHA */ #ifdef VAX #ifdef NO_SCAN_INTRUSION #define __VMS_VER 50200000 #else #define __VMS_VER 60100000 #endif #endif /* VAX */ #endif /* no __VMS_VER */ #if (defined ALPHA && __VMS_VER >= 60200000) || (defined VAX && __VMS_VER >= 60100000) /* SCAN_INTRUSION possible ?? */ #include /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CHECK_SCAN_INTRUSION This function implements the security_server access on newer OpenVMS versions using the $scan_intrusion system service. Inputs: p Pointer to the private data of current thread password Pointer to the password string option Number, selects what to check for Outputs: Function Return Value: retval TRUE on success, else FALSE. Comments: The $scan_intrusion system service is only available on OpenVMS vax 6.1 and OpenVMS alpha 6.2 or newer systems. Revision History: 1.0 Alberto Meregalli, DIF July 1997 Original Version 1.1 Michael Stenns August 1997 Some modifications for better IUPOP3 2.0 compliance - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ int check_scan_intrusion (POP * p, char * password, int option) { int status ,retval = TRUE; int logfail_status; int flags; char source_user[80]; switch (option) { case SCAN_INVALID_USER : logfail_status = SS$_NOSUCHUSER; flags = CIA$M_IGNORE_RETURN; break; case SCAN_INVALID_PASS : logfail_status = SS$_INVLOGIN; flags = CIA$M_REAL_USERNAME; break; default: logfail_status = SS$_NORMAL; flags = 0; break; } if (scan_intrusion) { sprintf (source_user, "%s:%s",p->ipaddr,p->user); status = sys$scan_intrusion (logfail_status, desz(p->user), JPI$K_NETWORK, desz(""), desz("IUPOP3"),desz(source_user), 0, desz(password), 0, 0, flags); } else status = SS$_NORMAL; switch (status) { case SS$_NORMAL : case SECSRV$_NOSCANNEDINTRUDER : break; case SECSRV$_INTRUDER : pop_log (LOG_INFO, p, "INTRUDER from %s detected", source_user); retval = FALSE; break; case SECSRV$_SUSPECT : pop_log (LOG_DEBUG, p, "%s: %s",source_user,vms_message(status)); break; default: pop_log (LOG_ERROR, p, "scan_intrusion: %s",vms_message(status)); break; } return retval; } #else /* old VMS versions */ int check_scan_intrusion (POP * p, char * password, int option) { if (scan_intrusion) pop_log (LOG_ERROR, p, "scan_intrusion is not available on OpenVMS %d",__VMS_VER); return TRUE; } #endif /* SCAN_INTRUSION check */