📄 qfax.c
字号:
/* * qfax.c - Renaissoft Qfax 1.3, an e-mail to fax gateway for use * with Efax 0.7a. * Copyright 1994-1996 Robert LeBlanc and Renaissoft */#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>#include "qfax.h"int parse_from(char *line, char *alias, char *fullname);void read_mail(Fax *f){/* Read the piped input on stdin from the mailer and parse it, extracting useful information from the headers and copying anything else to a simple ASCII file which will later be converted into the fax document. In the fax.rc file, a "fax comment start" and "fax comment end" header can be specified (e.g. X-Fax-Comment-Start:, X-Fax-Comment-End:). If these headers are found at the beginning of the body of the message, any text between them is taken to be a comment for the fax cover page. The output filename is of the form: FAXQUEUE/fax.user.timestamp e.g. /usr/spool/fax/sendq/fax.tom.26Aug230715 "user" in this context ("tom") is the local username of the sender, and "timestamp" is the time and date the message was received by Qfax. This is done to ensure that on a multiuser system all fax files have a unique identification, since there does exist the possibility that more than one user might want to send out a fax at the same time.*/ FILE *ifp; FILE *of; int i, j; char tmp[LINELEN]; char line[LINELEN]; char header[LINELEN]; char outfile[LONGLEN]; enum { headers, commentin, commentout, copy } stage = headers; of = stdout; ifp = stdin; strcpy(line, getsline(ifp)); while (!feof(ifp)) { strcpy(header, ""); switch (stage) { /* Look for "official" mail headers, stopping after finding the first blank line. Munge pertinent information from useful fields, ignore all others. */ case headers: sscanf(line, "%s", header); if (strcasecmp(header, "From:") == 0) { strcpy(tmp, getfield(line)); if (parse_from(tmp, f->fperson.username, f->fperson.fullname)) { /* * Some consistent error handling features would be nice, * but for now we just announce the error to stderr and * use dummy values to get through the read_mail() routine; * we check for these dummy values again in main(). */ fprintf(stderr, "Qfax: Can't parse 'From:' header:\n"); fprintf(stderr, " From: %s\n", tmp); strcpy(f->fperson.username, "unknown"); strcpy(f->fperson.fullname, "Unknown User"); } } else if (strcasecmp(header, "Subject:") == 0) { strcpy(f->subject, getfield(line)); } else if (strcasecmp(header, "To:") == 0) { strcpy(tmp, getfield(line)); i = 0; j = 0; while (tmp[i++] != '@') f->tperson.alias[j++] = tmp[i-1]; f->tperson.alias[j] = '\0'; j = 0; while (tmp[i++] != '.') f->tcompany.alias[j++] = tmp[i-1]; f->tcompany.alias[j] = '\0'; } else if (strcasecmp(header, "Date:") == 0) { strcpy(f->date, getfield(line)); } else if (strcmp(line, "") == 0) { stage = commentin; } break; /* Look for a user-supplied "fax cover page comment start" header, which must be in the first line of the body of the message to be recognized. If it's found, enter "comment mode", otherwise assume everything to follow is message text. */ case commentin: strcpy(f->tstamp, time_stamp()); sprintf(outfile, "%s/fax.%s.%s", FAXQUEUE, f->fperson.username, f->tstamp); of = fopen(outfile, "w"); sscanf(line, "%s", header); if (strcasecmp(header, f->labels.headerstart) == 0) { stage = commentout; } else { fputs(line, of); fputs("\n", of); stage = copy; } break; /* Consider anything read to be part of a comment for the fax cover page, until we find a user-supplied "comment end" header, at which point we can comfortably assume the remainder of the text is just message text. */ case commentout: sscanf(line, "%s", header); if (strcasecmp(header, f->labels.headerend) == 0) stage = copy; else strcat(f->comments, line); break; /* Copy everything from stdin directly to the output file verbatim. */ case copy: fputs(line, of); fputs("\n", of); break; } strcpy(line, getsline(ifp)); } fclose(of); fclose(ifp);}void make_fax(Fax *f){/* Use Efax to convert the ASCII mailfile to a G3 fax. This incidentally allows us to determine how many pages it will contain, so that we can include this information on the cover page.*/ char tmp[LINELEN]; char line[LINELEN]; int i = 0; FILE *ifp; sprintf(tmp, "%s make %s/fax.%s.%s > /dev/null", FAXSCRIPT, FAXQUEUE, f->fperson.username, f->tstamp); system(tmp); f->pages = 0; sprintf(line, "%s/fax.%s.%s.%03d", FAXQUEUE, f->fperson.username, f->tstamp, ++i); while ((ifp = fopen(line, "r")) != NULL) { fclose(ifp); (f->pages)++; sprintf(line, "%s/fax.%s.%s.%03d", FAXQUEUE, f->fperson.username, f->tstamp, ++i); }}/* * parse_from(): Parses the "From:" header of an e-mail message and * tries to extract the sender's username (alias) and * the sender's full name (fullname). Returns 0 if * successful, non-zero otherwise. * * Specifically, this routine has been tested on addresses of the * following forms: * * (1) rjl * (2) rjl@renaissoft.com * (3) rjl@renaissoft.com (Robert J. LeBlanc) * (4) rjl (Robert J. LeBlanc) * (5) Robert J. LeBlanc (rjl@renaissoft.com) * (6) Robert J. LeBlanc <rjl@renaissoft.com> * (7) "Robert J. LeBlanc" <rjl@renaissoft.com> * (8) "Robert J. LeBlanc" (rjl@renaissoft.com) * (9) "Robert J. LeBlanc" <rjl> * * It's a fairly flexible finite-state algorithm, so it may work with * a number of other formats that include (), <>, and "" constructs, * or simple text. I can only vouch for the test cases listed above, * however, though these should probably account for 95% of the header * formats likely to be encountered. * * Thanks to Andrew Daviel <andrew@andrew.triumf.ca> for providing an * extensive list of header formats he's encountered. */int parse_from(char *line, char *alias, char *fullname){ char parentheses[LONGLEN]; char brackets[LONGLEN]; char quotes[LONGLEN]; char buf[LINELEN]; char c; int j = 0, errors = 0; enum parse_state { start, intext, infullname, ignoretext, gotspace, inparen, ignoreparen, inbracket, ignorebracket, inquote, ignorequote, error } state = start; *parentheses = *quotes = *brackets = *alias = *fullname = *buf = 0; while (c = *line++) { switch(state) { /* * This is our initial parsing state. We skip any leading * whitespace, but otherwise we'll accept any alphanumeric * characters, ", (, and <. */ case start: if (c == '"') { state = inquote; buf[j=0] = 0; } else if (c == '(') { state = inparen; buf[j=0] = 0; } else if (c == '<') { state = inbracket; buf[j=0] = 0; } else if (isalnum(c)) { state = intext; buf[j=0] = c; buf[++j] = 0; } else if (isspace(c)) state = start; else state = error; break; /* * Here we're expecting text, and we're very liberal about what * we'll accept. If we detect a whitespace character, however, * we change state to gotspace because we need to know whether * this signals the break between words or a separator between * a word and a bracket/quote/parenthesis. */ case intext: if (c == '(') { state = inparen; strcpy(alias, buf); buf[j=0] = 0; } else if (c == '<') { state = inbracket; strcpy(alias, buf); buf[j=0] = 0; } else if (c == '"') { state = inquote; strcpy(alias, buf); buf[j=0] = 0; } else if (c == '@') /* found alias, skip the rest */ { strcpy(alias, buf); state = ignoretext; } else if (isspace(c)) { state = gotspace; buf[j++] = c; buf[j] = 0; } else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -