Comp 272 Programming assignment 2 Dordal Sept 24, 2004 Due: Friday, Oct 1 Take the mail system program, and make the changes listed below. The program, mail.cpp, is available on my web page. 1. In the original version, when you enter the wrong password, you don't get prompted for a new one. Change this so you do. The user should be allowed MAX_TRIES = 3 attempts in all, including the first attempt. If a user fails to enter a correct password after that many tries, the system should hang up on them. 2. Add an option to the administrative mailbox to change the passcode and greeting of any other mailbox. Press 5 to change the passcode of another mailbox 3. Fix the message ordering: messages in Mailbox::_kept should always be stored in the order received. That is, when retrieve_messages() reviews messages in _new and _kept, then: * saved messages in _new should go to the end of _kept * when reviewing _kept, saved messages should remain in original order. 4. In the existing version, all input is handled by the mailbox, once the user enters the extension. The mailsystem class makes no further use of InputReader; in fact, the mailsystem class doesn't do *anything* further until the user has completed the "call". Change this so that the Mailsystem class handles some of the direct user interaction, as specified below. In order to keep things simple, we will continue to assume that the Mailbox takes over **after the login process has been completed**. Up until the login operation succeeds, however, Mailsystem is to handle all I/O (except Message::play() still outputs messages directly). In particular, Mailsystem will now: Play the greeting Get messages from the user Put them into the Mailbox On receipt of "#" instead of a message, prompt for the passcode and verify that it is correct. The revised Mailbox::login2() function will take over after the passcode has been verified. Here are the Mailbox member functions that engage in I/O before login: receive_message prints greeting, prompts for message/#, reads in message login prompts for password, prints opening login statement You will need to replace these, by receive_message2() and login2(). receive_message2() will be moved into class Mailsystem; Mailbox::login2() will be the second half, more or less, of Mailbox::login(). The following will remain unchanged, as they are invoked *after* login: prompt_command prompts for a command, eg "press 1 to ..." retrieve_messages prompts about progress in reading your messages change_greeting prompts for new greeting, and updates it change_passcode ditto for passcode The main point of this assignment is that this redesign changes the class interfaces involved, because the class responsibilities are different. For example, you will now need a way for MailSystem to print the greeting, to add a new message to the Mailbox, and to verify that the passcode supplied is correct when login is attempted. The first can be handled by an accessor: string Mailbox::greeting() To add new messages, we will use bool Mailbox::addnewmessage(Message m); This returns true if the append succeeds, and false if not (which happens when the mailbox is already full). To check the passcode, either of the following would work: int Mailbox::passcode(); // get original passcode bool Mailbox::checkpasscode(int passcode); // check if supplied passcode is good The second approach is more in keeping with how real passcode systems work (which generally do *not* keep unencrypted or even unencryptable copies of the original passcodes anywhere around), but you may choose either. As for parts 1 and 2, note that #1 (prompting for the passcode) will now be handled within Mailsystem. Item #2, letting admin mailboxes change other mailboxes, is more interesting in that it will require a significant interface tweak. Specifically, from within AdminMailbox, how do we even find another mailbox? The mailboxes are stored within a private vector Mailsystem._mailboxes Any other mailbox is *invisible* to AdminMailbox! Making "_mailboxes" public would be dastardly. The slickest way to fix this is to have an accessor in MailSystem that returns a *reference* to any Mailbox, or, for those so inclined, a pointer. We've covered neither technique so far. We can (and will!) have AdminMailbox call Mailsystem::locate_mailbox(int extn); this gets us the index of the other mailbox in the array "_mailboxes". But we now need a member function in *MailSystem* to take the mailbox index (or the original extension, if you prefer), and the new passcode, and make the appropriate call to Mailbox::setpasscode(). Here is one possibility (this code is NOT complete!): Mailsystem::setpasscode(int extn; int newpasscode) { int index = locate_mailbox(extn); _mailboxes[index].setpasscode(newpasscode); } This function must belong to Mailsystem because it refers to the private container _mailboxes of Mailsystem. ========= Part 3: Message order (in Mailbox::retrieve_messages()) Currently, if you have some new messages ("_new") and some kept ("_kept"), then during message retrieval the system puts all those you have selected into a single third queue ("msgs_to_keep") and, if interrupted, appends these to the end of _kept. This works fine if msgs_to_keep contains only new messages, or if there are no remaining _kept messages. However, if _kept was originally [1,2], and an interruption occurs with msgs_to_keep=[1], _kept=[2], then the final _kept is [2,1], the reverse of what it had been. The only way to fix this is to maintain *separate* temporary queues, newkeepers = messages from _new that are earmarked to be saved, and keptkeepers = messages from _kept that are earmarked to be saved. Then, on exit, we execute _kept = keptkeepers + _kept + newkeepers that is, saved *new* messages still go to the end of _kept, but saved *old* messages go to the beginning. (You don't need to insert them at the beginning; just append _kept to keptkeepers, then append newkeepers to that, and finally assign _kept = keptkeepers.)