/**************************************************************************************************
$Header: /pub/cvsroot/yencode/src/ypost/usenet.c,v 1.1 2002/03/15 15:10:49 bboy Exp $
Routines used by `ypost' to validate and construct Usenet (RFC 1036) data.
Copyright (C) 2002 Don Moore <bboy@bboy.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at Your option) any later version.
This program 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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
**************************************************************************************************/
#include "ypost.h"
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
VALIDATE_EMAIL
Is this a valid email address?
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static char *
validate_email(char *email)
{
/* XXX: This is VERY non-robust */
if (strchr(email, '@'))
return (NULL);
else
return (_("invalid email address"));
}
/*--- validate_email() --------------------------------------------------------------------------*/
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
USENET_VALID_FROM
Checks the supplied address to make sure it's a conformant From: value. Will make minor
adjustments to the string if possible. Returns NULL if conformant, or a string describing
the problem if not. This function is not that strict.
RFC 1036 gives the following examples as valid addresses:
From: mark@cbosgd.ATT.COM
From: mark@cbosgd.ATT.COM (Mark Horton)
From: Mark Horton <mark@cbosgd.ATT.COM>
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
char *
usenet_valid_From(char *from, size_t len)
{
char *paren, *angle, *email, *comment, *reason;
char fromcopy[BUFSIZ];
strtrim(from);
strncpy(fromcopy, from, sizeof(fromcopy)-1);
paren = strchr(fromcopy, '(');
angle = strchr(fromcopy, '<');
// First form: No parens, no angle brackets
if (!paren && !angle)
{
email = fromcopy;
opt_sender = xstrdup(email);
strtrim(opt_sender);
return (validate_email(email));
}
// Second form: Comment in parentheses
if (paren)
{
email = fromcopy;
comment = paren;
*(comment++) = '\0';
if (!(paren = strchr(comment, ')')))
return (_("unterminated comment in author name"));
*paren = '\0';
opt_sender = xstrdup(email);
strtrim(opt_sender);
if ((reason = validate_email(email)))
return (reason);
return (NULL);
}
// Third form: Email in angle brackets
if (angle)
{
comment = fromcopy;
email = angle;
*(email++) = '\0';
if (!(angle = strchr(email, '>')))
return (_("unterminated email address in author name"));
*angle = '\0';
opt_sender = xstrdup(email);
strtrim(opt_sender);
if ((reason = validate_email(email)))
return (reason);
return (NULL);
}
return (_("unknown or invalid format for author (see ypost(1))"));
}
/*--- usenet_valid_From() -----------------------------------------------------------------------*/
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
USENET_VALID_SUBJECT
Checks the supplied subject data to make sure it's a conformant Subject: value.
Returns NULL if conformant, or a string describing the problem if not.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
char *
usenet_valid_Subject(char *str, size_t len)
{
strtrim(str);
/* XXX: This isn't much of a check! */
return (NULL);
}
/*--- usenet_valid_Subject() --------------------------------------------------------------------*/
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
USENET_DATE
Returns a static string containing the date in the format specified by RFC 822 / RFC 1036
Ex: Wdy, DD Mon YY HH:MM:SS TIMEZONE
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
char *
usenet_date(void)
{
static char datebuf[80]; /* Return value */
time_t now; /* Current time */
const struct tm *tm; /* Broken down time */
time(&now);
tm = gmtime(&now);
strftime(datebuf, sizeof(datebuf)-1, "%a, %d %b %Y %H:%M:%S %Z", tm);
return (datebuf);
}
/*--- usenet_date() -----------------------------------------------------------------------------*/
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
USENET_MESSAGE_ID
Returns an unique message ID. The basic format is:
<time><pid><idcount>@<hostname>
The <pid> ensures that two copies of ypost running simultaneously will not use the same
Message-ID if they post in the same second, and the <idcount> ensures that if this instance
of ypost posts many messages in the same second they will still be unique, as it increments
with each Message-ID generated.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
char *
usenet_message_id(void)
{
struct utsname uts;
static int idcount = 0; /* Number of IDs generated so far */
static char id[1024]; /* The ID generated */
if (uname(&uts))
ErrERR(_("unable to determine hostname"));
snprintf(id, sizeof(id), "<%lx%x%x@%s>", time(NULL), getpid(), idcount++, uts.nodename);
return (id);
}
/*--- usenet_message_id() -----------------------------------------------------------------------*/
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
PAD_NUMBER
Returns a pointer to a static string containing a number formatted to the width of the
`total' number. 0 indicates 'x' if use_x is nonzero.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
char *
pad_number(int number_to_pad, int total, int use_x)
{
int width = numeric_width(total);
static char numbuf[16];
if (number_to_pad <= 0 && use_x)
snprintf(numbuf, sizeof(numbuf), "%*.*s", width, width, "xxxxxxxxxxxxxxxxx");
else
snprintf(numbuf, sizeof(numbuf), "%0*d", width, number_to_pad);
return (numbuf);
}
/*--- pad_number() ------------------------------------------------------------------------------*/
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
FORMAT_SUBJECT
Replaces variables with values in the subject string specified. The string assumed to be
dynamically allocated, and will be realloc()'d to accomodate the new string.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
format_subject(char *subject)
{
char *c;
char result[BUFSIZ], *r;
int len = 0;
memset(result, 0, sizeof(result));
for (c = subject, r = result; *c && len < sizeof(result)-1; c++)
{
if (*c == '%')
switch (*(c+1))
{
case 'p': /* $p: Current part number */
len += snprintf(r, sizeof(result) - strlen(result), "%s", pad_number(part_current, part_total, 1));
r = result + len, c++;
break;
case 'P': /* $P: Total number of parts */
len += snprintf(r, sizeof(result) - strlen(result), "%d", part_total);
r = result + len, c++;
break;
case 'f': /* $f: Current file number */
len += snprintf(r, sizeof(result) - strlen(result), "%s", pad_number(file_current, file_total, 0));
r = result + len, c++;
break;
case 'F': /* $F: Total number of files */
len += snprintf(r, sizeof(result) - strlen(result), "%d", file_total);
r = result + len, c++;
break;
default:
result[len++] = *c;
break;
}
else
*(r++) = *c, len++;
}
subject = realloc(subject, len);
subject[len] = '\0';
memcpy(subject, result, len);
}
/*--- format_subject() --------------------------------------------------------------------------*/
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
USENET_MAKE_SUBJECT
Constructs the subject line for the current file. `part' is the current part number (relevant
only if the file is multipart). If `part' is -1, "XX" will be inserted for the confirmation
output.
Suggested format, single part binary:
[Comment1] "filename" 12345 yEnc bytes [Comment2]
[Comment1] and [Comment2] are optional. The filename should always be enclosed in quotes;
this allows for easy detection, even when the filename includes spaces or other special
characters. The word "yEnc" should be placed in between the file size and the word "bytes".
Suggested format, multipart binary:
[Comment1] "filename" yEnc (partnum/numparts) [size] [Comment2]
Again, [Comment1] and [Comment2] are optional. The [size] value is also optional here.
The filename must be included, in quotes. The keyword "yEnc" is mandatory, and must
appear between the filename and the size (or Comment2, if size is omitted). Future
revisions of the draft may specify additional information may be inserted between the
"yEnc" keyword and the opening parenthesis of the part number.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
unsigned char *
usenet_make_subject(YENCFILE *y, int part)
{
unsigned char *subj = NULL;
if (opt_subject)
sdprintf(&subj, "[%s] ", opt_subject);
sdprintf(&subj, "\"%s\" ", STRIP_PATH(y->input_filename));
if (y->totalparts < 2) /* Single part subject */
sdprintf(&subj, "%u yEnc bytes", y->filesize);
else /* Multi part subject */
{
/* Get part number as a string */
sdprintf(&subj, "yEnc (%s/%d) %u bytes", pad_number(part, y->totalparts, 1), y->totalparts, y->filesize);
}
if (opt_comment)
sdprintf(&subj, " [%s]", opt_comment);
format_subject(subj);
return (subj);
}
/*--- usenet_make_subject() ---------------------------------------------------------------------*/
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
USENET_MAKE_HEADERS
Creates the full set of headers for a single message.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
unsigned char *
usenet_make_headers(YENCFILE *y, int part)
{
unsigned char *hdr = NULL, **h = &hdr; /* The header data */
unsigned char *subject = usenet_make_subject(y, part);
sdprintf(h, "From: %s" CRLF, opt_author);
if (opt_sender)
sdprintf(h, "Sender: %s" CRLF, opt_sender);
sdprintf(h, "User-Agent: %s/" VERSION " (http://www.yencode.org/)" CRLF, short_progname);
sdprintf(h, "Date: %s" CRLF, usenet_date());
sdprintf(h, "Newsgroups: %s" CRLF, opt_newsgroup);
sdprintf(h, "Subject: %s" CRLF, subject);
if (opt_message_id)
sdprintf(h, "Message-ID: %s" CRLF, usenet_message_id());
/* Calculate number of lines in this post */
if (y->totalparts == 1)
sdprintf(h, "Lines: %u" CRLF, y->enclines + 2);
else
{
if (part < y->totalparts)
sdprintf(h, "Lines: %u" CRLF, opt_multipart_lines + 3);
else
sdprintf(h, "Lines: %u" CRLF, (y->enclines % opt_multipart_lines) + 3);
}
sdprintf(h, CRLF);
free(subject);
return (hdr);
}
/*--- usenet_make_headers() ---------------------------------------------------------------------*/
/* vi:set ts=3: */
syntax highlighted by Code2HTML, v. 0.9.1