/* =========================================================================== = (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_UTILITY.C - Version 2.0 = = = = Synopsis: = = This file contains miscellaneous functions that support 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 /* for va_ stuff */ #include # include #include #include #include #include "iupop3_global.h" #include "iupop3_general.h" #include "iupop3_vms.h" #include "version.h" #include "md5.h" /* ====================================================================== */ /* Prototypes for local use */ /* ====================================================================== */ static int vasprintf (char **message, const char * format, va_list ap); /* ======================================================================= */ /* Make Argv */ /* ======================================================================= */ void make_argv(char *string, int *argc, char *argv[], char *delimiters) { #define isdelimiter(a,b) (strchr(b,a) != NULL) char *cpointer; char **argpointer = argv; *argc = 0; cpointer = string; while (*cpointer) { while (*cpointer && isdelimiter(*cpointer,delimiters)) cpointer++; if (*cpointer == '\0') break; if (*cpointer == '\"') { cpointer++; *argpointer++ = cpointer; (*argc)++; while (*cpointer && (*cpointer != '\"')) cpointer++; } else { *argpointer++ = cpointer; (*argc)++; while (*cpointer && !isdelimiter(*cpointer,delimiters)) cpointer++; } if (*cpointer == '\0') break; *(cpointer++) = '\0'; } *(argpointer++) = 0; } /* ====================================================================== */ /* POP Log */ /* ====================================================================== */ void pop_log (int log_level, POP * p, const char *format, ...) { if (log_level <= master_log_level) { va_list ap; char *message; va_start (ap, format); vasprintf (&message, format, ap); va_end (ap); fprintf (log_file, "%s thread %2d: %s\n", logtime(NULL), p->threadnum, message); *message = '\0'; /* mark as free */ issue_logfile_flush_timer (); } } /* ====================================================================== */ /* POP Message */ /* ====================================================================== */ int pop_msg (POP *p, int status, const char *format, ...) { va_list ap; char *message, *status_indicator; status_indicator = (status == POP_SUCCESS) ? POP_OK : POP_ERR; va_start(ap, format); vasprintf (&message, format, ap); va_end(ap); netprintf (p, "%s%s\r\n", status_indicator, message); pop_log (LOG_DEBUG, p, "tx: %s%s", status_indicator, message); *message = '\0'; /* mark as free */ return status; } /* ======================================================================== */ /* System Log */ /* ======================================================================== */ void system_log (int log_level, char *format, ...) { va_list ap; char *message; if (log_level <= master_log_level) { va_start (ap, format); vasprintf (&message, format, ap); va_end (ap); fprintf (log_file, "%s %s\n", logtime(NULL), message); *message = '\0'; /* mark as free */ issue_logfile_flush_timer (); } } /* ======================================================================== */ /* Net Printf */ /* ======================================================================== */ int netprintf (POP *p, char *format, ...) { va_list ap; char *message; int length, bytes = 0; if (!(p->retrieve.flags & RETR_IN_PROGRESS_FLAG)) { va_start(ap, format); length = vasprintf (&message, format, ap); va_end(ap); bytes = netwrite_sync (p, message, length); *message = '\0'; /* mark as free */ } return bytes; } /* ======================================================================== */ /* Netb Printf - print into buffer */ /* remark: the generated string may not exceed MAXLINELEN characters */ /* ======================================================================== */ int netbprintf (POP *p, char *format, ...) { va_list ap; char *message; int length = 0; if (p->retrieve.send_buffer || allocate_send_buffer (p)) { va_start (ap, format); length = vasprintf (&message, format, ap); va_end (ap); length = netbputs (p, message, length, TRUE); *message = '\0'; /* mark as free */ } return length; } /* ======================================================================== */ /* Netb Puts - copy string into buffer */ /* bytestuff must be set to 'true' for each new record */ /* ======================================================================== */ int netbputs (POP *p, char * string, size_t length, int bytestuff) { char *ptr; /* reserve place for multiline termination and "byte stuff" */ if ((length > 0) && (((p->retrieve.send_buffer_length - 8) - p->retrieve.send_bufsize) >= length)) { ptr = p->retrieve.send_buffer + p->retrieve.send_bufsize; if ((*string == POP_TERMINATE) && bytestuff) /* "Byte stuff" if necessary */ { *ptr++ = POP_TERMINATE; p->retrieve.send_bufsize++; } memcpy (ptr, string, length); p->retrieve.send_bufsize += length; } else length = 0; return length; } /* ======================================================================== */ /* vasprintf gets a format string and a variable argument list and returns */ /* a pointer to the resulting string. */ /* ======================================================================== */ static int vasprintf (char ** message_ptr, const char * format, va_list ap) { static char Message1 [MAXLINELEN], Message2 [MAXLINELEN]; int length; if (*Message1) *message_ptr = Message2; else *message_ptr = Message1; length = vsprintf (*message_ptr, format, ap); if (length > sizeof Message1) { system_log (LOG_ERROR, "FATAL! Buffer overflow in vasprintf(), exiting"); exit (SS$_IVBUFLEN); } return length; } /* ======================================================================== */ /* netb_end writes the end multiline sequence into the buffer */ /* ======================================================================== */ int netb_end (POP *p) { char *ptr; int length; if (p->retrieve.send_buffer || allocate_send_buffer (p)) { ptr = p->retrieve.send_buffer + p->retrieve.send_bufsize; sprintf (ptr, "%s",ENDMULTLINTRANS); length = sizeof ENDMULTLINTRANS - 1; p->retrieve.send_bufsize += length; pop_log (LOG_DEBUG, p, "end of multi-line response"); } else length = 0; return length; } /* ====================================================================== */ /* POP Build_info */ /* ====================================================================== */ int pop_build_info (POP *p) { char buffer[BUFSIZ]; Message *mp; int msg_num; int status = SS$_NORMAL; /* ** Initialize mail folder status variables. */ p->msgs_deleted = 0; p->last_msg = 0; p->bytes_deleted = 0; /* ** Allocate memory for message information structures. */ free (p->mptr); p->mptr = calloc ((unsigned)p->msg_count,sizeof(Message)); if (p->mptr == NULL) { p->msg_count = 0; pop_log (LOG_ERROR, p, "Can't build message list for '%s': Out of memory", p->user); return SS$_INSFMEM; } /* ** Scan the NEWMAIL folder in the VMS MAIL file, loading the message ** information list with information about each message. */ mp = p->mptr; for (msg_num=1; msg_num <= p->msg_count; msg_num++, mp++) { mp->number = mp->nummbx = msg_num; status = mail_message_info (p, msg_num, mp); if (vms_error(status)) return status; mp->lines++; p->newmail_size += mp->length; } return SS$_NORMAL; } /* ====================================================================== */ /* Patch From Line */ /* this functions parses the from line and interprets the type of the mes.*/ /* ====================================================================== */ void patch_from_line (POP *p, char *from_line) { int num_tokens; char output[384]; char *tokens[256]; /* make it huge or the stack might get corrupted */ char *pointer; /* Token #1 is address, #2 (if it exists) is personal name */ strcpy (output, from_line); make_argv(from_line, &num_tokens, tokens, " "); if ((num_tokens < 1) || (num_tokens > 2)) { pop_log(LOG_ERROR, p, "error parsing %s", output); return; } *output = '\0'; /* Lower case the address (but not the personal name) */ lower (tokens[0]); /* * If address is of the form: * * DSN%SERVICE "from" date * * convert to SERVICE.DSN@localhost * define it as decnet mail */ if (strstr(tokens[0], "dsn%")) { char * str1 = strchr (tokens[0],'%'); *str1++ = '\0'; p->decnet = TRUE; sprintf(output, "%s.%s@%s", str1, tokens[0], myhostname); pop_log(LOG_THREAD, p, "DSN mail detected"); } /* * If address is of the form: * * FOO%"someone@somewhere" -or- * NODE::"someone@somewhere" * * Just use what is in the quotes. */ else if (pointer = strchr(tokens[0], '"')) { p->decnet = FALSE; strncpy (output, pointer+1, sizeof output); pointer = output + strlen(output) - 1; if (*pointer == '"') *pointer = '\0'; } /* * If address is of the form: * * NAMESPACE:.NODE::USER (DECnet/OSI format) * NODE::USER (DECnet phase IV format) * * Convert it to the form user@node */ else if (pointer = strstr(tokens[0], "::")) { char *str1 ,*str2, logname[256]; p->decnet = TRUE; /* Skip over Phase V namespace */ if ((str1 = strstr(tokens[0], ":.")) != NULL) str1 = str1 + 2; else str1 = tokens[0]; strncpy (output, pointer+2, sizeof output); strcat (output, "@"); *pointer = '\0'; /* use logicals to map nodename */ strncpy (logname,"IUPOP3_NODE_", sizeof logname); strcat (logname, str1); if ((str2 = getenv (upper(logname))) != NULL) strcat (output, str2); else strcat (output, str1); } /* * None of the above; it's local, so append "@hostname" * This could both internet (smtp) or decnet messages * The default type is set with the -default_type commandline option */ else { sprintf(output, "%s@%s", tokens[0], myhostname); } /* * Put the address in angle brackets, and prepend personal name if it exists. */ *from_line = '\0'; if (personal_name) { if (tokens[1]) sprintf(from_line, "\"%s\" ", tokens[1]); } if (*output == '<') sprintf(from_line+strlen(from_line), "%s", output); else sprintf(from_line+strlen(from_line), "<%s>", output); if (!ignore_mail11_headers) { p->decnet = TRUE; } } /* ====================================================================== */ /* allocate send buffer */ /* ====================================================================== */ int allocate_send_buffer (POP *p) { int status = TRUE; if (!p->retrieve.send_buffer) { p->retrieve.send_buffer = calloc (1, default_sendbuffer_size * sizeof (char)); if (!p->retrieve.send_buffer) { server_shutdown = SS$_INSFMEM; /* insufficient dynamic memory */ p->attn_state = ATTN_DISCONNECT; status = FALSE; } else p->retrieve.send_buffer_length = default_sendbuffer_size; } return status; } /* ====================================================================== */ /* enlarge send buffer */ /* ====================================================================== */ int enlarge_send_buffer (POP *p) { unsigned status = TRUE, add_size = Min (MAX_MAIL_HEADER_SIZE - p->retrieve.send_buffer_length, p->retrieve.send_buffer_length/2), max_size = 0x7FFF; if ( !p->retrieve.send_buffer ) status = allocate_send_buffer (p); /* ** Don't allow to grow send buffer more then 32k */ else if ( add_size && ((p->retrieve.send_buffer_length + add_size) <= max_size) ) { char * ptr = realloc (p->retrieve.send_buffer, p->retrieve.send_buffer_length + add_size); if ( ptr ) { p->retrieve.send_buffer = ptr; p->retrieve.send_buffer_length += add_size; pop_log (LOG_THREAD, p, "enlarge send buffer to %d octets", p->retrieve.send_buffer_length); } else { pop_log (LOG_ERROR, p, "fail to enlarge send buffer, size still %d ", p->retrieve.send_buffer_length); status = FALSE; /* kismet */ } } else status = FALSE; return status; } /* ======================================================================== */ /* lower - convert string to lowercase */ /* ======================================================================== */ char * lower (char * str) { char * tmp; /* convert to lower */ if (str) { for (tmp = str; *tmp; tmp++) *tmp = tolower (*tmp); } return str; } /* ======================================================================== */ /* upper - convert string to uppercase */ /* ======================================================================== */ char * upper (char * str) { char * tmp; /* convert to upper */ if (str) { for (tmp = str; *tmp; tmp++) *tmp = toupper (*tmp); } return str; } /* ======================================================================== */ /* replace_tabs - Replace tabs with spaces */ /* ======================================================================== */ char * replace_tabs (char * string) { char * pointer = string; if (string) { while ((pointer = strchr(pointer,'\t'))) *pointer = ' '; } return string; } /* ======================================================================== */ /* checks the digest sent by the client against timestamp and password */ /* Return: 0 (fail) or 1 (ok) */ /* ======================================================================== */ int check_digest (bintime_type *start_timeptr, int threadnum, char *secret, char *digest_client) { char digest_hexstring [34]; MD5_CTX context; unsigned char digest[16]; unsigned char timestamp[120]; char *tmp; int i; int status = 0; /* 0 means wrong password */ sprintf ((char *) timestamp, TIMESTAMP_FORMAT, VERSION, COMPILER, threadnum, myhostname, stamptime (start_timeptr)); if (timestamp && secret && digest_client && (strlen(digest_client) >= 32)) { MD5Init (&context); MD5Update (&context, timestamp, strlen ((char *) timestamp)); MD5Update (&context,(unsigned char *) secret, strlen (secret)); MD5Final (digest, &context); for (i=0; i<16; i++) sprintf (&digest_hexstring[2*i],"%02.2x",digest[i]); if (!strncmp (digest_hexstring,digest_client,32)) status = 1; } return status; } /* ======================================================================== */ /* check existance of external mail files */ /* returns the size of the file or zero. /* ======================================================================== */ #define FAB_RFM_FIXED 1 #define FAB_RFM_VARIABLE 2 #define FAB_RFM_VARIABLE_VFC 3 #define FAB_RFM_STREAM 4 int check_external_file (POP * p, char * name) { char filename[256]; stat_t statbuf; int size = 0; strcpy (filename, p->mail_directory); strcat (filename, name); statbuf.st_size = 0; if (stat(filename,&statbuf) < 0) /* problems with external file */ { size = 0; pop_log(LOG_ERROR, p, "stat() on %s failed",filename); p->retrieve.enable_long_lines = FALSE; } else { size = statbuf.st_size; if (p->retrieve.enable_long_lines) { if ((statbuf.st_fab_rfm != FAB_RFM_VARIABLE) || (statbuf.st_fab_rat != 2)) { p->retrieve.enable_long_lines = FALSE; pop_log(LOG_THREAD,p,"long lines not supported for current message"); } } pop_log(LOG_DEBUG, p, "%s has %d bytes", name, size); } return size; } /* ======================================================================== */ /* vms_strerror converts errno to an readable string and */ /* never returns a NULL pointer (unlike strerror) */ /* ======================================================================== */ char * vms_strerror (int error_number) { char * message; if (error_number == EVMSERR) message = vms_message(vaxc$errno); else message = strerror(error_number); if (!message) message = "unknown error code"; return message; } /* ======================================================================== */ /* returns the descriptor address of a zero terminated string. */ /* DESCRIP.H is needed. */ /* ======================================================================== */ bintime_type get_binary_time (char *time_string) { bintime_type bintime = BINTIME_INIT_ZERO; int ss_stat = SS$_NORMAL; struct dsc$descriptor_s sdesc; /* Descriptor fuellen */ /* ------------------ */ sdesc.dsc$w_length = strlen (time_string); sdesc.dsc$b_dtype = DSC$K_DTYPE_T; sdesc.dsc$b_class = DSC$K_CLASS_S; sdesc.dsc$a_pointer = time_string; ss_stat = sys$bintim (&sdesc,&bintime); if (!(ss_stat&1)) BINTIME_TO_ZERO(bintime); return bintime; } /* ======================================================================== */ /* returns the descriptor address of a zero terminated string. */ /* DESCRIP.H is needed. */ /* ======================================================================== */ STR_DESCR * desz (char * String) { return (String) ? des (String, strlen(String)) : NULL; } /* ======================================================================== */ /* returns the descriptor address of a fixed length string. */ /* remark: every nn invocation the same pointer is returned! */ /* DESCRIP.H is needed. */ /* ======================================================================== */ STR_DESCR * des (char * Buffer, size_t buflen) { static STR_DESCR Descriptors[] = { {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL}, {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL}, {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL}, {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL}, {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL}, {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL}, }; static int cur = 0; STR_DESCR * ptr = NULL; if (Buffer) { ptr = &Descriptors[cur++]; if (cur >= (sizeof Descriptors / sizeof Descriptors[0])) cur = 0; ptr->dsc$w_length = buflen; ptr->dsc$a_pointer = Buffer; } return ptr; }