/* export OPTS="-Wfatal-errors \ -Wswitch-default \ -Wswitch-enum \ -Wunused-parameter \ -Wfloat-equal \ -Wundef \ -Wstrict-null-sentinel \ -std=c++0x \ -pedantic \ -Wall \ -Wextra " export COMPILER="g++ " */ /*************************************************************************** main.cpp - description ------------------- begin : Tue Mar 25 08:26:04 EST 2003 copyright : (C) 2003 by George Kelly Flanagin email : me@georgeflanagin.com : : Created as a teaching example for CS246 at VCU. revisions : Tue Nov 30 16:00 EST 2004 : gkf -> Changed the behavior for unknown : pattern characters to be literals. : Sun Aug 10 09:50 EST 2003 : gkf -> added the -sql option to facilitate : a better and more portable way of creating : sql loadable data. : Also added the commandMatch() function to : support more flexible command line arg matching. : : Wed Jul 23 08:33 EST 2003 : gkf -> Added "-quotes" option to control whether : words from a dictionary that contain quotes : appear in the output. Since it is a bad idea : in most cases, the state defaults to "forbidden." : Also added a, A, and x to control upper/lower case : for random character generation. : And added "-uniq" to ensure uniqueness of the : tokens generated. : And added "-seed" to allow user to specify a : random seed. : : Sat April 16 2011 : gkf -> Resolved ambiguous, difficult to type passwords by : making all cap-O lower case, and all lower-l upper case : : Friday August 12 2011 : gkf -> Fine tuning for publication, including adding some : sensible defaults so that the program will run without : arguments. *************************************************************************** * * * 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. * * * ***************************************************************************/ #include // sorts and finds #include // opening the files #include // writing to the screen #include // to store the list of generated IDs #include // get the system clock #include // to store the list of words using namespace std; //--- // A list of functions, besides main(), contained in this file. //--- int addWordsToList(string FileName, vector & WordList); bool commandMatch(string, string); char getDigit(void); char getLetter(char); char getPunct(void); string getWord(vector & List); int Usage(void); bool maxLenOK( string Pattern, int MaxLen ); char noOhsAndEls(char c); //--- // A few global flags //--- bool quotesAllowed = false; bool checkUnique = false; bool SQL = false; //--- // main() //--- int main(int argc, char *argv[]) { if (argc<2) return Usage(); string pattern(argv[1]); unsigned int i, j; unsigned int maxLen(0); bool cyclePattern(false); string stringArg; unsigned int numNeeded(1); vector randomWords; int numWords(0); char patternChar(' '); string token; bool tokenOK(false); int seed(0); map dictionary; dictionary[" "] = 1; //--- // Let's look at the command line options. //--- for (i=2; i(argc); i++) { stringArg = argv[i]; //--- // The number of tokens to generate ... //--- if (commandMatch(stringArg, "-number")) { numNeeded = atol(argv[++i]); if (!numNeeded) numNeeded = 1; } //--- // Allow the quote/tick character in the output ... //--- else if (commandMatch(stringArg, "-quotes")) ::quotesAllowed = true; //--- // Check the uniqueness of the generated token ... //--- else if (commandMatch(stringArg, "-unique")) ::checkUnique = true; //--- // Allow user to specify the seed for repeatable, psuedorandom // generation ... //--- else if (commandMatch(stringArg, "-seed")) seed = atol(argv[++i]); //--- // Allow the specification of an include file ... //--- else if (commandMatch(stringArg, "-include")) numWords += addWordsToList(argv[++i], randomWords); //--- // Allow the user to specify a maximum token length ... //--- else if (commandMatch(stringArg, "-maxlen")) maxLen = atol(argv[++i]); //--- // Allow the user to specify SQL type parens and quotes. //--- else if (commandMatch(stringArg, "-sql")) SQL = true; else if (commandMatch(stringArg, "-cycle")) cyclePattern = true; //--- // Gee ... I'm not really sure what you meant. //--- else { cerr << "Unknown command line argument [" << stringArg << "]" << endl; return (EXIT_SUCCESS); } } //--- // Make sure the maxlen is greater than the pattern length //--- if (maxLen && !maxLenOK( pattern, maxLen )) { cerr << "craic says: " << "-maxlen " << maxLen << " is too short for pattern " << pattern << endl; return (EXIT_SUCCESS); } //--- // OK, we have processed everything. //--- // Initialize the random number generator with the system clock, // or the seed specified by the user if they have done so. //--- srand(seed ? seed : time(0)); //--- // Check to see if there is anything /in/ the list of words. There // is no point in allowing the user to use the "w" in the pattern // match if there are no words to choose from. //--- const bool HaveWords = randomWords.size() > 1; //--- // We have an empty increment in the for loop because we don't really // know if we will say "Eureka!" until we have checked the operation. //--- for (j=0; j ( wordCount * 4 ) + ( static_cast(pattern.size()) - wordCount ); } //--- // addWordsToList(FileName, WordList) -- //--- // Attempts to open FileName for reading. Adds space delimited tokens // found therein to WordList. Closes the opened file before exit. //--- int addWordsToList(string FileName, vector & WordList) { string Word; ifstream Input; Input.open(FileName.c_str()); int Count = 0; if (!Input) { cerr << "Error opening input file [" << FileName << "]" << endl; return (0); } do { Input >> Word; //--- // Don't mistakenly add words containing an apostrophe //--- if (!::quotesAllowed) if (Word.find("\'") != string::npos) continue; WordList.push_back(Word); Count++; } while (Input); Input.clear(); Input.close(); return (Count); } //--- // commandMatch(UserResponse, Target) // Takes the entire string contained in UserResponse, and determines // if it identical to the first n-characters of Target. So, // -u, -un, -uniq, etc... all match -unique //--- bool commandMatch(string UserResponse, string Target) { return !Target.find(UserResponse, 0); } //--- // getDigit() // Returns a random digit. //--- char getDigit(void) { static const string s = "0123456789"; return s[random() % s.length()]; } //--- // getLetter(c) // Returns a random letter. Uses c to determine what exactly is meant // by a random letter. "a" selects a lowercase letter, "A" selects an // uppercase letter, "x" selects without regard to case. //--- // The alphabet has been modified to discard the lower case ell and // the upper case oh. //--- char getLetter(char c) { static const string UpperCaseLetters = "ABCDEFGHIJKLMNPQRSTUVWXYZ"; static const string LowerCaseLetters = "abcdefghijkmnopqrstuvwxyz"; static const string Letters = "abcdefghijkmnopqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ"; switch (c) { case 'a': return LowerCaseLetters[random() % LowerCaseLetters.length()]; break; case 'A': return UpperCaseLetters[random() % UpperCaseLetters.length()]; break; case 'x': return Letters[random() % Letters.length()]; break; default: break; } return c; } //--- // getPunct() // Returns a random punctuation character. //--- char getPunct(void) { static const string s = "~!@#$%^&*-=+:;"; return s[random() % s.length()]; } //--- // getWord(Words) // Returns a random word from the list called Words. //--- string getWord(vector & Words) { string s = Words[random() % Words.size()]; transform(s.begin(), s.end(), s.begin(), noOhsAndEls); return s; } char noOhsAndEls(char c) { if ((c - 'O') * (c - 'l')) return c; return c - 'O' ? 'L' : 'o'; } //--- // Usage(ProgramName) // Copies an explanation of how to use this program to the user's // console. //--- int Usage(void) { static const string helpMessage = ( "CRAIC version of " __DATE__ "\n\n" "CRAIC is a program to generate one or more random passwords according\n" "to a pattern that the user supplies. The results are written to stdout, \n" "and they are written one to a line. \n\n" "Usage: \n" " craic {pattern} [-opt [param]] \n" " where {pattern} is some combination of: \n" " 9 => a random digit \n" " x => a random LeTtEr \n" " A => a random UPPERCASE letter \n" " a => a random lowercase letter \n" " - => a random non-alpha symbol \n" " w => a random \"word\" from any of the files specified \n" " anything else is a literal ...\n\n" " and the options are ...\n" " -c[ycle] => circular rotates the pattern before each generated password to the left\n" " -i[nclude] {file} => is used to specify a file whose contents are assumed\n" " to be space delimited words.\n" " -m[axlen] {number} => is used to specify a max overall length of any password.\n\n" " -n[umber] {number} => is used to specify the number of desired passwords.\n" " -q[uotes] => allows single quotes that might be retrieved from \n" " a dictionary.\n" " -se[ed] {number} => seeds the random number generator with a specific value\n" " -sq[l] => adds parens and quotes around each token for ease\n" " in incorporating them into an SQL INSERT statement.\n" " -u[nique] => checks that each new term is unique\n\n" " EXAMPLE:\n" " craic w9w -n 10 -m 20 -c -i /usr/share/dict/linux.words\n" " acentrouscanabae0\n" " graspscrewcut1\n" " bLinds9subcommunity\n" " 2TiLLfordwound-worn\n" " twenty-ton4copiers\n" " 3coannexedceLLobiose\n" " 8Atacamenotier\n" " MonopyLarianavarch2\n" " prededuction7drafted\n" " embracesFe9\n" ); cerr << helpMessage; return (EXIT_SUCCESS); }