/****************************************************************************** * MailReport.C * * Creates reports on mail files based on headers specified on the command line * * Mail folders are specified, one per line, in a file (eg, .mailcheckerrc), * that must be given as one of the command line arguments. The report is * printed to standard out. * * The entries in the file specifying which mail folders to check can have one * of two forms. The first form is an absolute path to a mail folder, * /path/to/my/mail/folder. The second form is a relative path to the mail * folder. It is relative to a MAIL_PATH entry that has been previously * specified using MAIL_PATH=/my/path. There can be multiple MAIL_PATHs. Lines * beginning with #, space, or tab and empty lines are ignored. * * Here is an example file to clarify the preceding: * # .mailcheckerrc for mailreport program * # No white space at beginning of folder lines * # Blank lines and lines beginning with '#', ' ', or '' will be ignored * # Lines with a leading '/' will be considered absolute paths * # Lines without a leading '/' will have MAIL_PATH prepended * # MAIL_PATH can be changed at will * MAIL_PATH=/home6/wbaldwin * .mail * MAIL_PATH=/home6/wbaldwin/mail * lauren * itc * cs * web * ebay * * Usage: mailreport rcfile --new||--all --sep||--nosep HEADER1 [HEADER2] ... * rcfile: path to any file meeting the specification listed above * --new||--all: pick one option only to print only headers of unread msgs * or of all msgs * --sep||--nosep: pick one option only to print seperators between each * set of headers * HEADER: At least one header must be specified * * Any header may be specified. If the header does not exist, or does not exist * for a particular message, nothing will be printed. This program takes no * responsibility for having knowledge of any or all particular or possible * headers. * * 'Body' may be specified as one of the headers on the command line. In this * case, the expected occurs; i.e., message bodies are printed as part of the * report. * * Example 1: print the From: and Subject: headers from unread mail * 'mailreport ~/.mailcheckerrc --new --sep From: Subject:' * * Example 2: print the bodies of unread messages along with the From: and * Subject: headers * 'mailreport ~/.mailcheckerrc --new --sep From: Subject: Body' * * Exit codes: * 0 Reporting new msgs only: New mail in some folder * Reporting all msgs: No errors encountered * 1 Reporting new msgs only: There is NO new mail * ???Reporting all msgs: Header not found in any message * 2 Error * *****************************************************************************/ #include using std::string; #include using std::cout; using std::endl; using std::flush; using std::ios; #include using std::ifstream; #include using std::map; #include using std::vector; const string MAIL_PATH = "MAIL_PATH="; const string WHITESPACE = " \t\n"; void getLines(ifstream& in, vector& lines); void printHeaders(map& headers, vector& H, string& folder,bool first,bool sep) { string separator = "----------------------------------------"; separator += separator; vector::const_iterator it; bool last_sep = false; string body = ""; for (it = H.begin(); it != H.end(); it++) { if (*it == "Body") { body = headers[*it]; } else { last_sep = true; if (first && sep) { first = false; cout << "\n[FOLDER: " << folder << "]\n"; cout << separator << "\n"; } if (headers[*it] != "") { cout << headers[*it] << "\n"; } else { cout << *it << "\n"; } } } if (body != "") { cout << "\n" << body; } if (last_sep && sep) { cout << separator << endl; } } void usage() { string usage = "Usage: mailreport rcfile --new||--all "; usage += "--sep||--nosep HEADER1 [HEADER2] ..."; cout << usage << endl; exit(2); } int main(int argc, char* argv[]) { char *rcfile; // file specifying mail folders to check ifstream in; bool all, new_only, sep, first = false; map headers; vector H; switch (argc) { case 1: case 2: case 3: case 4: usage(); default: rcfile = argv[1]; in.open(rcfile); // Open rcfile containing list of mail folders to report on if (in.fail()) { cout << "FATAL ERROR: Folder file (" << rcfile << ") does not exist!" << endl; usage(); } if ((string) argv[2] == "--all") { all = 1; } else if ((string) argv[2] == "--new") { new_only= 1; } else { usage(); } if ((string) argv[3] == "--sep") { sep = 1; } else if ((string) argv[3] == "--nosep") { sep = 0; } else { usage(); } } // Get headers specified by user on command line for (int i = 4; i < argc; i++) { if ( ((string) argv[i]).substr(0, 11) == "Message-Id:" || ((string) argv[i]).substr(0, 11) == "Message-ID:") { headers["Message-ID:"] = ""; H.push_back("Message-ID:"); } else { headers[argv[i]] = ""; H.push_back(argv[i]); } } int return_code = 1; string mail_path = ""; string line; string folder; string header; string status; vector lines; vector folders; // Parse out just the folders from the rc file // I.e., skip comments and resolve relative paths // getLines(in, lines); for (int i = 0; i < lines.size(); ++i) { line = lines.at(i); if (line == "" || line[0] == ' ' || line[0] == '\t' || line[0] == '#') { // Do nothing - the line is a comment } else if (line.substr(0,MAIL_PATH.length()) == MAIL_PATH) { // Set path to mail folders mail_path = line.substr(MAIL_PATH.length()); } else if (line[0] != '/') { // Relative paths line = mail_path + '/' + line; folders.push_back(line); } else { // Absolute paths folders.push_back(line); } } in.close(); // This is where message headers are extracted from folders // For each of the folders... for (int i = 0; i < folders.size(); ++i) { folder = folders.at(i); // ...open the current folder and get it's lines ifstream in(folder.c_str()); if (!in && sep) { cout << "WARNING: The mail folder " << folders.at(i) << " does not exist!\n" << flush; continue; } first = true; lines.clear(); getLines(in, lines); in.close(); // ...look at each of the folder lines we just got for (int i = 0; i < lines.size(); ++i) { status = ""; line = lines.at(i); // Skip lines until the start of a new message is reached if (line.substr(0, 5) != "From ") continue; // Found the next message, extract specified headers // Clear the previous header entries first map::const_iterator map_it; for (map_it = headers.begin(); map_it != headers.end(); ++map_it) { headers[map_it->first] = ""; } do { line = lines.at(i); header = line.substr(0, line.find_first_of(WHITESPACE)); if (new_only && header == "Status:") { status = line; } if ( (header == "Message-ID:" || header == "Message-Id:") && headers.count("Message-ID:") ) { headers["Message-ID:"] = line; } else if (headers.count(header)) { headers[header] = line; } ++i; } while (i < lines.size() && line != ""); // Get the message body if specified if (headers.count("Body")) { headers["Body"] = ""; while (i < lines.size()) { line = lines.at(i); // If the current line is blank and the next line starts a new message // then just stop right here and move on to the next message if (line == "" && i+1 < lines.size() && /* make sure we're not trying to look past EOF */ lines.at(i+1).substr(0,5) == "From ") { break; } headers["Body"] += line + "\n"; ++i; } } // Print the headers for the current message if (new_only && !(status == "" || status == "Status: O")) { // do nothing } else if (first) { return_code = 0; printHeaders(headers,H,folder,first,sep); first = false; } else { printHeaders(headers,H,folder,first,sep); } } } if (new_only && return_code == 1) { cout << "No new mail" << endl; } return return_code; } // main void getLines(ifstream& in, vector& lines) { string line; while (getline(in, line)) { lines.push_back(line); } }