/**************************************************************************************************
$Header: /pub/cvsroot/yencode/src/ypost/ypost.c,v 1.5 2002/03/21 21:46:47 bboy Exp $
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"
int opt_verbose = 1; /* Should the program be verbose in its operation? */
int opt_debug = 0; /* Debug output? */
char *opt_author = NULL; /* Author name for posting */
int opt_prompt_author = 0; /* Prompt for author? */
char *opt_auth_user = NULL; /* Username for authentication */
char *opt_auth_pass = NULL; /* Password for authentication */
int opt_prompt_pass = 0; /* Prompt for password? */
char *opt_nntp_server = NULL; /* nntp server to use */
char *opt_subject = NULL; /* Subject prefix */
int opt_prompt_subject = 0; /* Prompt for subject? */
char *opt_comment = NULL; /* Subject suffix */
int opt_prompt_comment = 0; /* Prompt for comment? */
int opt_timeout = -1; /* Timeout for socket ops */
char *opt_newsgroup = NULL; /* Newsgroup to post to */
int opt_force = 0; /* Post messages without confirming? */
int opt_nosort = 0; /* Do not sort input files */
int opt_multipart_lines = -1; /* Number of lines for multipart messages */
int opt_info = 0; /* Hidden option for debugging, just outputs file info */
int opt_retry_limit = -1; /* Max number of retries on failed post */
int opt_message_id = 0; /* Generate Message-ID: header when posting? */
int opt_stdout = 0; /* Output to stdout instead of a file? */
char *opt_sender = NULL; /* For header generation, not set directly */
int opt_line_length = -1; /* Line length for posting */
int opt_keep_paths = 0; /* Keep relative path names? */
int opt_overwrite = 0; /* (not actually used by this program..) */
int opt_resume_msg = 0; /* Message number at which to restart */
crc32_t opt_resume_crc = 0; /* CRC value for posts, to ensure they are the same */
YENCFILE **input_files = (YENCFILE **)NULL; /* List of files to process */
int num_input_files = 0; /* Number of items in files */
/* Optional common support files */
int opt_sfv = 0; /* Create .SFV file? */
char *opt_sfv_filename = (char *)NULL; /* SFV filename */
int opt_crc = 0; /* Create .CRC file? */
char *opt_crc_filename = (char *)NULL; /* CRC filename */
char *opt_sort_first = NULL; /* Sort these extensions first, comma separated list */
size_t total_input_bytes = 0; /* Total number of input bytes overall */
int part_current = 0, /* Current part number of this part */
part_total = 0; /* Total number of parts for this part */
int file_current = 0, /* Current file number overall */
file_total = 0; /* Total number of files being posted */
int total_messages = 0, /* Total number of messages to post */
total_posted = 0; /* Total number of messages posted so far */
crc32_t total_crc = 0; /* Total CRC of all messages to post */
static int posting_started = 0; /* Has the posting started yet? */
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
USAGE
Display program usage information.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void
usage(int status)
{
if (status != EXIT_SUCCESS)
{
fprintf(stderr, _("Try `%s --help' for more information."), progname);
fputs("\n", stderr);
}
else
{
printf(_("Usage: %s [OPTION]... FILE..."), progname);
puts("");
puts(_("Post file(s) to Usenet."));
puts("");
// puts("----------------------------------------------------------------------------78");
puts(_(" -a, --author=NAME use NAME as author when posting (or prompt)"));
puts(_(" -c, --comment=COMMENT suffix all subjects with COMMENT (or prompt)"));
puts(_(" -d, --debug output extra debugging information while running"));
puts(_(" -f, --force post messages without confirming"));
puts(_(" -g, --group=NEWSGROUP post messages to the newsgroup called GROUP"));
puts(_(" -l, --line=LEN output lines that are LEN bytes in length"));
puts(_(" -m, --multipart=NUM multipart posts contain NUM lines (default: 5000)"));
puts(_(" -M, --message-id generate Message-ID header field when posting"));
puts(_(" -n, --nosort post articles in the order specified; do not sort"));
puts(_(" -p, --paths maintain paths in input filenames"));
puts(_(" -P, --pass=PASS password for nntp authentication (or prompt)"));
puts(_(" -q, --quiet inhibit all messages written to the standard output"));
puts(_(" -r, --retry=NUM if a post fails, retry NUM times (default: 3)"));
puts(_(" -R, --resume=MSG,CHK resume failed post at MSG, optionally verify CHK"));
puts(_(" -s, --subject=SUBJECT prefix all subjects with SUBJECT (or prompt)"));
puts(_(" -S, --server=ADDR connect to nntp server at ADDR (hostname or IP)"));
puts(_(" -t, --timeout=SECS socket operations timeout after SECS (default: 60)"));
puts(_(" -U, --user=USER username for nntp authentication"));
puts(_(" --crc=NAME post CRC checksum file for all input files"));
puts(_(" --sfv=NAME post SFV checksum file for all input files"));
puts(_(" --stdout output messages to standard output instead of posting"));
puts(_(" --help display this help and exit"));
puts(_(" --version output version information and exit"));
puts("");
puts(_("Command line options always override options found in the configuration file."));
puts("");
puts(_("Report bugs to bugs@yencode.org."));
}
exit(status);
}
/*--- usage() -----------------------------------------------------------------------------------*/
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
CMDLINE
Process command line options.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
cmdline(int argc, char **argv)
{
int optc, optindex;
char *optstr;
struct option const longopts[] =
{
{"author", optional_argument, NULL, 'a'},
{"comment", optional_argument, NULL, 'c'},
{"debug", no_argument, NULL, 'd'},
{"force", no_argument, NULL, 'f'},
{"group", required_argument, NULL, 'g'},
{"line", required_argument, NULL, 'l'},
{"multipart", required_argument, NULL, 'm'},
{"pass", optional_argument, NULL, 'P'},
{"paths", no_argument, NULL, 'p'},
{"quiet", no_argument, NULL, 'q'},
{"retry", required_argument, NULL, 'r'},
{"server", required_argument, NULL, 'S'},
{"subject", optional_argument, NULL, 's'},
{"timeout", required_argument, NULL, 't'},
{"user", required_argument, NULL, 'U'},
{"sfv", optional_argument, NULL, 0},
{"stdout", no_argument, NULL, 0},
{"crc", optional_argument, NULL, 0},
{"resume", required_argument, NULL, 'R'},
{"help", no_argument, NULL, 0},
{"version", no_argument, NULL, 0},
{"info", no_argument, NULL, 0},
{NULL, 0, NULL, 0}
};
optstr = getoptstr(longopts);
while ((optc = getopt_long(argc, argv, optstr, longopts, &optindex)) != -1)
{
switch (optc)
{
case 0:
{
const char *opt = longopts[optindex].name;
if (!strcmp(opt, "version")) // --version
{
printf("%s - " PACKAGE " " VERSION "\n", short_progname);
exit(EXIT_SUCCESS);
}
else if (!strcmp(opt, "help")) // --help
usage(EXIT_SUCCESS);
else if (!strcmp(opt, "sfv")) // --sfv
{
opt_sfv = 1;
opt_sfv_filename = optarg;
}
else if (!strcmp(opt, "crc")) // --crc
{
opt_crc = 1;
opt_crc_filename = optarg;
}
else if (!strcmp(opt, "info")) // --info
opt_info = 1;
else if (!strcmp(opt, "stdout")) // --stdout
opt_stdout = 1;
}
break;
case 'a': // -a, --author=AUTHOR
if (optarg)
opt_author = xstrdup(optarg);
else
opt_prompt_author = 1;
break;
case 'c': // -c, --comment=COMMENT
if (optarg)
opt_comment = xstrdup(optarg);
else
opt_prompt_comment = 1;
break;
case 'd': // -d, --debug
opt_debug = opt_verbose = 1;
break;
case 'f': // -f, --force
opt_force = 1;
break;
case 'g': // -g, --group=GROUP
opt_newsgroup = xstrdup(optarg);
break;
case 'l': // -l, --line
opt_line_length = atoi(optarg);
if (opt_line_length > 254)
Err(_("line lengths greater than 254 are not allowed by the yEnc specification"));
break;
case 'm': // -m, --multipart=LINES
opt_multipart_lines = atoi(optarg);
break;
case 'M': // -M, --message-id
opt_message_id = 1;
break;
case 'p': // -p, --paths
opt_keep_paths = 1;
break;
case 'P': // -P, --pass=PASS
if (optarg)
opt_auth_pass = xstrdup(optarg);
else
opt_prompt_pass = 1;
break;
case 'q': // -q, --quiet
opt_debug = opt_verbose = 0;
break;
case 'r': // -r, --retry=NUM
opt_retry_limit = atoi(optarg);
break;
case 'R': // -R, --resume
{
char *c = strchr(optarg, ',');
if (c)
{
*(c++) = '\0';
opt_resume_crc = (crc32_t)strtoul(c, (char **)NULL, 16);
}
opt_resume_msg = atoi(optarg);
}
break;
case 's': // -s, --subject=SUBJ
if (optarg)
opt_subject = xstrdup(optarg);
else
opt_prompt_subject = 1;
break;
case 'S': // -S, --server=SERV
opt_nntp_server = xstrdup(optarg);
break;
case 't': // -t, --timeout=SECS
opt_timeout = atoi(optarg);
break;
case 'U': // -U, --user=USER
opt_auth_user = xstrdup(optarg);
break;
default:
usage(EXIT_FAILURE);
}
}
/* Set these options for the routines in "error.c" in the library */
err_debug = opt_debug;
err_verbose = opt_verbose;
while (optind < argc)
{
YENCFILE *y;
if ((y = yencfile_create(argv[optind++], NULL, YSUPPORT_NOT_SPECIAL, 0)))
{
input_files = (YENCFILE **)xrealloc(input_files, (num_input_files + 1) * sizeof(YENCFILE *));
input_files[num_input_files++] = y;
total_input_bytes += y->filesize;
/* The xxx_construct() functions want this set */
y->ok = 1;
}
}
/* Some input files must be specified.. */
if (!num_input_files)
{
Warn(_("no input files"));
usage(EXIT_FAILURE);
}
}
/*--- cmdline() ---------------------------------------------------------------------------------*/
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
LOAD_YPOSTRC
Load the file ~/.ypostrc.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
load_ypostrc(void)
{
FILE *fp;
char buf[BUFSIZ];
char *name, *value;
char rcfile[PATH_MAX];
struct passwd *pwd;
int line = 0;
if (!(pwd = getpwuid(getuid())) || !pwd->pw_dir)
return;
snprintf(rcfile, sizeof(rcfile), "%s/.ypostrc", pwd->pw_dir);
memset(pwd, 0, sizeof(struct passwd)); // Get this out of memory
if (!(fp = fopen(rcfile, "r")))
{
if (errno == ENOENT)
return;
ErrERR("%s", rcfile);
}
while (fgets(buf, sizeof(buf), fp))
{
line++;
conftrim(buf);
value = buf;
name = strsep(&value, "=");
if (!name || !value)
continue;
strtrim(name);
strtrim(value);
/* Remove quotation marks around value if any */
while ((value[0] == '\'' && value[strlen(value)-1] == '\'')
|| (value[0] == '"' && value[strlen(value)-1] == '"'))
{
value++;
value[strlen(value)-1] = '\0';
}
#define WARNOVER(OverrideCondition) \
if (OverrideCondition) \
{ \
Warn(_("%s:%d: `%s' option in config file overridden by command-line"), rcfile, line, name); \
continue; \
}
if (!strcmp(name, "author"))
{
WARNOVER(opt_author || opt_prompt_author);
opt_author = xstrdup(value);
}
else if (!strcmp(name, "comment"))
{
WARNOVER(opt_comment || opt_prompt_comment);
opt_comment = xstrdup(value);
}
else if (!strcmp(name, "debug"))
{
WARNOVER(opt_debug);
opt_debug = err_debug = opt_verbose = err_verbose = getbool(value);
}
else if (!strcmp(name, "force"))
{
WARNOVER(opt_force);
opt_force = getbool(value);
}
else if ((!strcmp(name, "group") || !strcmp(name, "newsgroup")))
{
WARNOVER(opt_newsgroup);
opt_newsgroup = xstrdup(value);
}
else if (!strcmp(name, "line"))
{
WARNOVER(opt_line_length != -1);
opt_line_length = atoi(value);
}
else if (!strcmp(name, "multipart"))
{
WARNOVER(opt_multipart_lines != -1);
opt_multipart_lines = atoi(value);
}
else if ((!strcmp(name, "messageid") || !strcmp(name, "message-id")))
{
WARNOVER(opt_message_id);
opt_message_id = getbool(value);
}
else if (!strcmp(name, "nosort"))
{
WARNOVER(opt_nosort);
opt_nosort = getbool(value);
}
else if (!strcmp(name, "paths"))
{
WARNOVER(opt_keep_paths);
opt_keep_paths = getbool(value);
}
else if ((!strcmp(name, "pass") || !strcmp(name, "password")))
{
WARNOVER(opt_auth_pass || opt_prompt_pass);
opt_auth_pass = xstrdup(value);
}
else if (!strcmp(name, "quiet"))
{
WARNOVER(!opt_verbose);
opt_debug = opt_verbose = 0;
}
else if (!strcmp(name, "retry"))
{
WARNOVER(opt_retry_limit != -1);
opt_retry_limit = atoi(value);
}
else if (!strcmp(name, "stdout"))
{
WARNOVER(opt_stdout);
opt_stdout = getbool(value);
}
else if (!strcmp(name, "subject"))
{
WARNOVER(opt_subject || opt_prompt_subject);
opt_subject = xstrdup(value);
}
else if ((!strcmp(name, "serv") || !strcmp(name, "server")))
{
WARNOVER(opt_nntp_server);
opt_nntp_server = xstrdup(value);
}
else if (!strcmp(name, "timeout"))
{
WARNOVER(opt_timeout != -1);
opt_timeout = atoi(value);
}
else if ((!strcmp(name, "user") || !strcmp(name, "username")))
{
WARNOVER(opt_auth_user);
opt_auth_user = xstrdup(value);
}
else if ((!strcmp(name, "sortfirst") || !strcmp(name, "sort_first")))
{
WARNOVER(opt_sort_first);
opt_sort_first = xstrdup(value);
}
else if (!strcmp(name, "sfv"))
{
WARNOVER(opt_sfv);
opt_sfv = getbool(value);
}
else if (!strcmp(name, "crc"))
{
WARNOVER(opt_crc);
opt_crc = getbool(value);
}
else
Warn(_("%s:%d: unknown option `%s' in config file (ignored)"), rcfile, line, name);
}
fclose(fp);
}
/*--- load_ypostrc() ----------------------------------------------------------------------------*/
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
CALCULATE_ENCODED_INFO
Calculates CRC values, encoded data length, and number of lines for all files matching `type'.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
calculate_encoded_info(ysupportfile_t file_type)
{
register int ct, x, linect;
FILE *fp;
unsigned char buf[BUFSIZ]; /* Input data buffer */
register size_t rb; /* Bytes read this read() */
register size_t total_bytes_read; /* Total number of bytes read */
size_t total_bytes; /* Total bytes in all files this scan */
int do_meter = 0; /* Display a progress meter? */
int lines_this_part = 0; /* Number of lines found for current part */
register size_t bytes_this_part = 0; /* Number of bytes this part */
off_t offset; /* Current byte offset in current part */
YENCPART *part; /* Current part info */
total_bytes_read = total_bytes = (size_t)0;
/* Get total number of bytes */
for (ct = 0; ct < num_input_files; ct++)
if (input_files[ct]->file_type == file_type)
total_bytes += input_files[ct]->filesize;
if (total_bytes > 1000000)
do_meter = 1;
/* Scan each file matching `file_type' */
for (ct = 0; ct < num_input_files; ct++)
{
YENCFILE *y = input_files[ct];
if (y->file_type != file_type)
continue;
y->totalparts = 1;
lines_this_part = 0;
bytes_this_part = 0;
offset = 0;
linect = 0;
CRC_START(y->crc);
/* Initialize our `part' list -- all files have at least one part */
y->part = (YENCPART **)xrealloc(y->part, (y->totalparts) * sizeof(YENCPART *));
part = yencpart_create();
y->part[y->totalparts - 1] = part;
part->begin = 1;
if (y->support_data)
{
for (x = 0; x < y->filesize; x++)
{
CRC_UPDATE(y->crc, y->support_data[x]);
/* Update total CRC */
CRC_UPDATE(total_crc, y->support_data[x]);
if (YSHOULD_ESCAPE(YENCODE(y->support_data[x]),linect,opt_line_length))
linect++, y->encsize++;
linect++, y->encsize++, offset++, bytes_this_part++;
if (linect >= opt_line_length)
{
y->enclines++, lines_this_part++, linect = 0;
/* Add a CR to total_crc to indicate line length */
CRC_UPDATE(total_crc, '\r');
if (lines_this_part >= opt_multipart_lines)
{
y->totalparts++;
part->end = offset;
part->size = bytes_this_part;
y->part = (YENCPART **)xrealloc(y->part, (y->totalparts) * sizeof(YENCPART *));
part = yencpart_create();
y->part[y->totalparts - 1] = part;
part->begin = offset + 1;
lines_this_part = 0;
bytes_this_part = 0;
/* Add a LF to total_crc to indicate file length */
CRC_UPDATE(total_crc, '\n');
}
}
}
}
else
{
if (!(fp = fopen(y->input_filename, "r")))
ErrERR("%s", y->input_filename);
while ((rb = fread(buf, sizeof(unsigned char), sizeof(buf), fp)) > 0)
{
/* Update CRC and encoded byte count */
for (x = 0; x < rb; x++)
{
CRC_UPDATE(y->crc, buf[x]);
/* Update total CRC */
CRC_UPDATE(total_crc, buf[x]);
if (YSHOULD_ESCAPE(YENCODE(buf[x]),linect,opt_line_length))
linect++, y->encsize++;
linect++, y->encsize++, offset++, bytes_this_part++;
if (linect >= opt_line_length)
{
y->enclines++, lines_this_part++, linect = 0;
/* Add a CR to total_crc to indicate line length */
CRC_UPDATE(total_crc, '\r');
if (lines_this_part >= opt_multipart_lines)
{
y->totalparts++;
part->end = offset;
part->size = bytes_this_part;
y->part = (YENCPART **)xrealloc(y->part, (y->totalparts) * sizeof(YENCPART *));
part = yencpart_create();
y->part[y->totalparts - 1] = part;
part->begin = offset + 1;
part->size = 0;
lines_this_part = 0;
bytes_this_part = 0;
/* Add a LF to total_crc to indicate file length */
CRC_UPDATE(total_crc, '\n');
}
}
}
if (do_meter)
{
total_bytes_read += rb;
meter(total_bytes_read, total_bytes, _("Scanning files"));
}
}
fclose(fp);
}
if (linect != 0)
y->enclines++;
part->end = offset;
part->size = bytes_this_part;
CRC_FINISH(y->crc);
}
if (do_meter)
meter_clear();
}
/*--- calculate_encoded_info() ------------------------------------------------------------------*/
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
CREATE_SUPPORT_FILES
Precalculates CRC values for all regular input files and generates any requested support files.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
create_support_files(void)
{
int ct;
CRC_START(total_crc);
/* Calculate CRC values, etc. for all regular input files */
calculate_encoded_info(YSUPPORT_NOT_SPECIAL);
/* Generate special file data */
for (ct = 0; ct < num_input_files; ct++)
{
YENCFILE *y = input_files[ct];
switch (y->file_type)
{
case YSUPPORT_IS_SFV:
y->support_data = sfv_construct(input_files, num_input_files);
y->filesize = strlen(y->support_data);
Debug("%s: file generated, %s bytes", y->input_filename, comma(y->filesize));
break;
case YSUPPORT_IS_CRC:
y->support_data = crc_construct(input_files, num_input_files);
y->filesize = strlen(y->support_data);
Debug("%s: file generated, %s bytes", y->input_filename, comma(y->filesize));
break;
case YSUPPORT_NOT_SPECIAL:
default:
break;
}
}
/* Now calculate CRC, etc. for special files */
calculate_encoded_info(YSUPPORT_IS_SFV);
calculate_encoded_info(YSUPPORT_IS_CRC);
}
/*--- create_support_files() --------------------------------------------------------------------*/
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
OUTPUT_FILE_INFO
This is called if you pass `--info' on the command line.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
output_file_info(void)
{
int ct, part;
for (ct = 0; ct < num_input_files; ct++)
{
YENCFILE *y = input_files[ct];
charline('-');
printf(" input filename: %s\n", y->input_filename);
printf(" crc: %08X\n", y->crc);
printf(" file size: %s\n", comma(y->filesize));
printf(" encoded data size: %s\n", comma(y->encsize));
printf(" encoded lines: %s\n", comma(y->enclines));
printf(" total parts: %s\n", comma(y->totalparts));
printf(" file type: ");
switch (y->file_type)
{
case YSUPPORT_IS_SFV: printf("support file - .sfv\n"); break;
case YSUPPORT_IS_CRC: printf("support file - .crc\n"); break;
case YSUPPORT_NOT_SPECIAL: default: printf("regular file\n"); break;
}
printf(" support data: %s\n", y->support_data ? "yes" : "no");
for (part = 0; part < y->totalparts; part++)
printf(" part %03d: begin=%lu end=%lu size=%u\n", part + 1, y->part[part]->begin,
y->part[part]->end, y->part[part]->size);
}
charline('-');
exit(EXIT_SUCCESS);
}
/*--- output_file_info() ------------------------------------------------------------------------*/
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
POST_FILE
Posts a single part file to the news server.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
post_file(YENCFILE *y)
{
unsigned char *subject = NULL, *headers = NULL; /* Subject and headers, current part */
FILE *fp = NULL; /* Input file (if reading from a file) */
unsigned char inbuf[BUFSIZ], outbuf[BUFSIZ]; /* Input and output buffers */
register unsigned char c; /* Current character being encoded */
register int ct, linect, lines, total_lines;
register size_t written; /* Bytes written this part */
size_t rb; /* Number of bytes read */
unsigned char *reply = NULL; /* Reply from server */
register off_t offset; /* Current offset into entire buffer/file */
int orig_debug = opt_debug; /* Original debug value (hide encdata) */
register crc32_t pcrc; /* CRC for this part (if multipart) */
int replynum = 0; /* Numeric value of server reply */
off_t start_offset; /* Starting offset for this part (for retrying) */
int retry = 0; /* Current retry number */
float elapsed; /* Elapsed time for this part */
char desc[80]; /* Description of current action */
opt_debug = err_debug = 0;
/* Open the input file (if there is one) */
if (!y->support_data && !(fp = fopen(y->input_filename, "r")))
ErrERR("%s", y->input_filename);
/*
** Loop through each part of this file
*/
for (offset = 0, part_current = 1; part_current <= y->totalparts; )
{
/*
** If we are resuming, we might need to skip this file.
** We do not want to start posting until total_posted >= opt_resume_msg.
*/
if (opt_resume_msg && (total_posted+1 < opt_resume_msg))
{
offset += y->part[part_current-1]->size;
subject = usenet_make_subject(y, part_current);
Verbose("%s", subject);
free(subject);
Verbose("* Message skipped (resuming, already posted)");
Verbose(" ");
retry = 0;
part_current++;
total_posted++;
continue;
}
/*
** Initialize data; generate subject and headers; send NNTP POST command and headers
*/
start_offset = offset;
subject = usenet_make_subject(y, part_current);
headers = usenet_make_headers(y, part_current);
if (retry)
snprintf(desc, sizeof(desc), "%s %d of %d", _("Retry"), retry, opt_retry_limit);
else
{
snprintf(desc, sizeof(desc), "%s of %s", comma1(total_posted+1), comma2(total_messages));
Verbose("%s", subject);
}
if (!opt_stdout)
nntptransx("POST\n"); /* Send NNTP POST */
sock_write(headers, strlen(headers)); /* Output headers */
free(subject);
free(headers);
/*
** Write the yEnc "=ybegin" line
*/
if (y->totalparts == 1)
sock_printf("=ybegin line=%d size=%u name=%s\n",
opt_line_length, y->filesize, STRIP_PATH(y->input_filename));
else
sock_printf("=ybegin part=%d total=%d line=%d size=%u name=%s\n",
part_current, y->totalparts, opt_line_length, y->filesize, STRIP_PATH(y->input_filename));
/*
** Determine the total number of lines for the current part
*/
if (y->totalparts == 1)
total_lines = y->enclines;
else
total_lines = (part_current < y->totalparts) ? opt_multipart_lines : (y->enclines % opt_multipart_lines);
linect = lines = 0;
/*
** Write the yEnc "=ypart" line (if this is a multipart archive)
*/
if (y->totalparts > 1)
sock_printf("=ypart begin=%lu end=%lu\n", y->part[part_current-1]->begin, y->part[part_current-1]->end);
/*
** Output encoded data for this part
*/
CRC_START(pcrc);
written = 0;
timer_reset(NULL);
if (y->support_data)
{
for (ct = offset; ct < y->filesize && lines < opt_multipart_lines; ct++)
{
offset++;
CRC_UPDATE(pcrc, y->support_data[ct]);
c = YENCODE(y->support_data[ct]);
if (YSHOULD_ESCAPE(c,linect,opt_line_length))
{
outbuf[linect++] = '=';
c = YESCAPE(c);
}
outbuf[linect++] = c;
if (linect >= opt_line_length)
{
outbuf[linect++] = '\0';
if (!opt_stdout || (opt_stdout && !isatty(STDOUT_FILENO)))
written += sock_puts_meter(outbuf, written, y->part[part_current-1]->size, desc);
linect = 0;
lines++;
}
}
}
else /* Reading from a file */
{
fseek(fp, offset, SEEK_SET);
while ((lines < opt_multipart_lines) && ((rb = fread(inbuf, sizeof(unsigned char), sizeof(inbuf), fp)) > 0))
{
for (ct = 0; ct < rb && lines < opt_multipart_lines; ct++)
{
offset++;
CRC_UPDATE(pcrc, inbuf[ct]);
c = YENCODE(inbuf[ct]);
if (YSHOULD_ESCAPE(c,linect,opt_line_length))
{
outbuf[linect++] = '=';
c = YESCAPE(c);
}
outbuf[linect++] = c;
if (linect >= opt_line_length)
{
outbuf[linect++] = '\0';
if (!opt_stdout || (opt_stdout && !isatty(STDOUT_FILENO)))
written += sock_puts_meter(outbuf, written, y->part[part_current-1]->size, desc);
linect = 0;
lines++;
}
}
}
}
/*
** Finished with this part - close the current line if we've output any data
*/
if (linect)
{
outbuf[linect++] = '\0';
if (!opt_stdout || (opt_stdout && !isatty(STDOUT_FILENO)))
sock_puts(outbuf);
}
CRC_FINISH(pcrc);
elapsed = timer_elapsed(NULL);
/*
** If output is going to stdout and stdout is a terminal, we didn't output any of the
** encoded data (to remain screen-friendly). Output a short message explaining this.
*/
if (opt_stdout && isatty(STDOUT_FILENO))
{
puts(_("<< ENCODED DATA OMITTED SINCE OUTPUT IS TO A TERMINAL >>"));
puts(_("<< TO ENABLE ENCODED DATA, REDIRECT STDOUT TO A FILE >>"));
}
/*
** Write the yEnc "=yend" line
*/
if (y->totalparts == 1)
sock_printf("=yend size=%u crc32=%08X\n", y->filesize, y->crc);
else
{
if (part_current == y->totalparts)
sock_printf("=yend size=%u part=%d pcrc32=%08X crc32=%08X\n", y->part[part_current-1]->size, part_current, pcrc, y->crc);
else
sock_printf("=yend size=%u part=%d pcrc32=%08X\n", y->part[part_current-1]->size, part_current, pcrc);
}
/*
** Finish the NNTP POST command and check the server's result
*/
if (!opt_stdout)
meter_clear();
sock_puts("."); /* Do this even if opt_stdout, as a separator there */
if (!opt_stdout)
{
if (!(reply = sock_gets()))
Err(_("server did not reply to our command"));
replynum = nntp_get_reply_num(reply);
if (STATUS_ERR(replynum))
Verbose("* %s", strtrim(reply));
else
Verbose("* %s (%.2fk/s)", strtrim(reply),
(float)((float)((float)y->part[part_current-1]->size / 1024.0) / elapsed));
// Sleep for a couple of seconds, so that we make sure our files are posted in order
sleep(2);
}
else
Verbose("* Done");
/*
** If this part was posted successfully, move on to the next part
*/
if (opt_stdout || !STATUS_ERR(replynum))
{
Verbose(" ");
retry = 0;
part_current++;
total_posted++;
}
else /* An error occurred - incremement retry count, reconnect, and retry */
{
offset = start_offset;
retry++;
if (retry > opt_retry_limit)
Err(_("posting failed; retry limit reached"));
/* Disconnect from server and reconnect */
nntp_disconnect();
sleep(1);
nntp_connect();
}
}
/* Close the input file (if there was one) */
if (!y->support_data)
fclose(fp);
opt_debug = err_debug = orig_debug;
}
/*--- post_file() -------------------------------------------------------------------------------*/
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
FINISH_TOTAL_CRC
Completes the calculation of the `total_crc' global variable, which is a checksum that may
be optionally used when resuming a post.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
finish_total_crc(void)
{
register unsigned char *c, *subject;
YENCFILE *y;
/* Finish total_crc by calculating subject lines for each message, and adding their crc to the end */
for (file_current = 1; file_current <= num_input_files; file_current++)
{
y = input_files[file_current-1];
for (part_current = 1, part_total = y->totalparts; part_current <= part_total; part_current++)
{
subject = usenet_make_subject(y, part_current);
for (c = subject; *c; c++)
CRC_UPDATE(total_crc, *c);
free(subject);
}
}
CRC_FINISH(total_crc);
if (opt_resume_crc && (opt_resume_crc != total_crc))
Err(_("resume checksum (%08X) does not match posting checksum (%08X)"), opt_resume_crc, total_crc);
}
/*--- finish_total_crc() ------------------------------------------------------------------------*/
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
CLEANUP
If the posting has begun, this will let the user know how they can restart the post.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void
cleanup(int code)
{
int ct, special;
if (!posting_started || !total_posted)
exit(EXIT_FAILURE);
for (ct = special = 0; ct < num_input_files && !special; ct++)
if (input_files[ct]->file_type != YSUPPORT_NOT_SPECIAL)
special++;
fputs((isatty(STDERR_FILENO)) ? "\a\n\n" : "\n\n", stderr);
fprintf(stderr, "%s\n\n", _("NOTICE: The program ended while messages were being posted!"));
fprintf(stderr, _("To resume the post that was just interrupted, run %s\n"), short_progname);
fprintf(stderr, _("again with the same arguments as before, plus the following:\n\n"));
/* We can't list the total_crc if there were special files present, since some of them
(like .sfv) contain data that will change on each run (the date/time), thus changing
the crc */
if (special)
fprintf(stderr, "\t--resume=%d\n\n", total_posted+1);
else
fprintf(stderr, "\t--resume=%d,%08X\n\n", total_posted+1, total_crc);
exit(EXIT_FAILURE);
}
/*--- cleanup() ---------------------------------------------------------------------------------*/
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ADD_SUPPORT_FILE
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
add_support_file(const char *filename, const char *ext, ysupportfile_t type)
{
char *fn; /* Filename to use */
char *def = NULL;
char namebuf[PATH_MAX];
int ct;
char *c;
YENCFILE *y; /* New file */
/* If no filename was specified, try to find a .nfo or .m3u file and take the name from that */
if (!filename)
for (ct = 0; ct < num_input_files; ct++)
if ((c = strrchr(input_files[ct]->input_filename, '.')))
if (!strcasecmp(c, ".m3u") || !strcasecmp(c, ".nfo"))
{
if ((c = strrchr(input_files[ct]->input_filename, '/')))
strncpy(namebuf, c+1, sizeof(namebuf)-1);
else
strncpy(namebuf, input_files[ct]->input_filename, sizeof(namebuf)-1);
if ((c = strrchr(namebuf, '.')))
*c = '\0';
def = namebuf;
}
fn = yencfile_default_filename(filename, def ? def : input_files[0]->input_filename, ext);
if ((y = yencfile_create(fn, NULL, type, 0)))
{
input_files = (YENCFILE **)xrealloc(input_files, (num_input_files + 1) * sizeof(YENCFILE *));
input_files[num_input_files++] = y;
}
}
/*--- add_support_file() ------------------------------------------------------------------------*/
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MAIN
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int
main(int argc, char **argv)
{
register int ct;
set_progname(argv[0]);
err_set_exit(cleanup);
signal(SIGWINCH, set_screen_width);
signal(SIGHUP, cleanup);
signal(SIGINT, cleanup);
signal(SIGQUIT, cleanup);
signal(SIGABRT, cleanup);
signal(SIGTERM, cleanup);
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
cmdline(argc, argv);
load_ypostrc();
/* Set default options if values were not specified */
if (opt_timeout == -1)
opt_timeout = DEFAULT_SOCK_TIMEOUT;
if (opt_line_length == -1)
opt_line_length = Y_LL;
if (opt_multipart_lines == -1)
opt_multipart_lines = DEFAULT_MULTIPART_LINES;
if (opt_retry_limit == -1)
opt_retry_limit = DEFAULT_RETRY_LIMIT;
opt_multipart_lines -= 3; /* Subtract 3 lines for the =y lines */
/* Add support files if requested */
if (opt_sfv) add_support_file(opt_sfv_filename, ".sfv", YSUPPORT_IS_SFV);
if (opt_crc) add_support_file(opt_crc_filename, ".crc", YSUPPORT_IS_CRC);
/* Sort the input file list */
if (!opt_nosort)
{
yencfile_seed_sort_first_extensions(opt_sort_first);
qsort(input_files, num_input_files, sizeof(YENCFILE *), yencfile_cmp);
}
create_support_files(); /* Create support files if requested */
if (opt_info)
output_file_info();
/* Set total number of parts and files */
part_total = file_total = total_messages = total_posted = 0;
for (ct = 0; ct < num_input_files; ct++)
{
part_total += input_files[ct]->totalparts;
file_total++;
total_messages += input_files[ct]->totalparts;
}
prompt_for_missing(); /* Prompt user for any missing information */
finish_total_crc(); /* Finish calculating total CRC */
if (!opt_force && !opt_stdout)
prompt_confirm_post(); /* Confirm posting */
/* Post all files */
nntp_connect();
posting_started = 1;
for (ct = 0; ct < num_input_files; ct++, file_current++)
{
part_total = input_files[ct]->totalparts;
post_file(input_files[ct]);
}
posting_started = 0;
return (EXIT_SUCCESS);
}
/*--- main() ------------------------------------------------------------------------------------*/
/* vi:set ts=3: */
syntax highlighted by Code2HTML, v. 0.9.1