csmaild-cvs Mailing List for C# Mail Server
Brought to you by:
tamc
You can subscribe to this list here.
| 2003 |
Jan
|
Feb
|
Mar
|
Apr
(7) |
May
|
Jun
|
Jul
(79) |
Aug
(49) |
Sep
|
Oct
|
Nov
|
Dec
|
|---|
|
From: <ta...@us...> - 2003-08-08 22:44:13
|
Update of /cvsroot/csmaild/csmaild/docs In directory sc8-pr-cvs1:/tmp/cvs-serv19295/docs Added Files: Todo.htm Log Message: Oops, here's the todo doc --- NEW FILE: Todo.htm --- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title></title> <meta name="GENERATOR" content="Microsoft Visual Studio .NET 7.1"> <meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5"> </head> <body> Things to do with the csmaild server, split into categories: <UL> <LI>General <UL> <LI>RFC 2822 parsing <UL> <LI>Still needs to be finished; look around, you'll find missing code ;)</LI></UL> <LI>Configuration <UL> <LI>Need to set various options (i.e. ports, ips, aliases, etc. etc.) <LI>IP address/domain restrictions on who can connect <LI>Filters for incoming mail <LI>Lots of other stuff I'm sure</LI></UL> <LI>Code review, refactoring, restructuring, performance alterations, logging, filters, mailing lists, etc. etc.</LI></UL> <LI>Imap <UL> <LI>Bounds checking </LI> <UL> <LI>Most of the time that the client will be using a literl string it'll be for reletively small amounts of data. Need the ability to limit this per command argument, or at a global level. The only command argument that *should* be of any significant size is the APPEND's message data argument. <LI>Need to ensure that the number they send us is smaller than uint.MaxValue before we put it into the uint variable, or some variation thereof. <LI>Both of the above issues may apply to FETCH's BODY[...] and BODY.PEEK[...] requests as they can specify an offset and length (both unsigned ints) <LI>Related to the above issues, but not necessarily bounds checking. If the string that is to be sent/received is going to be of any significant size we should probably figure out a way to not do it all in memory all at once (byte[] buf = new byte[uint.MaxValue] won't work I don't think ;)). Possibly using a chunking method, or some sort of Stream solution. Both have their issues as they both will be sending data either into or pulling data out of the data store, so the solution to this must be supported by the data store as well.</LI>< /UL > <LI>Notifications/locking</LI> <UL> <LI>Client will expect to receive various responses as per certain events that will occur on a message/mailbox. Need to design this framework. <LI>Should implement some sort of thread safety on the various objects to ensure that all clients receive all notifications for an object before they're allowed to modify it themselves.</LI></UL> <LI>Finish RFC-3501 commands <UL> <LI>APPEND <UL> <LI>Not even started</LI></UL> <LI>AUTHENTICATE <UL> <LI> Started, have more authentication mechanisms to implement if the desire exists <UL> <LI>CRAM-MD5 <LI>?others?</LI></UL></LI></UL> <LI>CHECK <UL> <LI>It's implementation is defined as implementation specific, if we can make a use for it, that should be done ;)</LI></UL> <LI>FETCH <UL> <LI>A bunch of the things a client can fetch aren't implemented. Just look for the NotImplementedException, and implement it. <LI>The ENVELOPE request has some addresses defined in the response. Need to figure out what exactly: "Holds route from [RFC-2822] route-addr if" means. Probably just means to read the 2822 spec a tad more closely</LI></UL> <LI>RENAME <UL> <LI>If the user is renaming the INBOX, then we create a new mailbox and move the messages. Still need to implement the moving of the messages. <LI>Not saving the name back to the datastore when a normal rename occurs</LI></UL> <LI>SEARCH <UL> <LI>Not even started</LI></UL> <LI>STORE <UL> <LI>Needs to send a FETCH response for the modified messages if the client didn't specify .SILENT</LI></UL> <LI>STARTTLS <UL> <LI>Ugh, who wants encryption anyway. Refer to rfc3501 and rfc2595 for info.</LI></UL></LI></UL> </LI></UL> <LI>Pop / Smtp <UL> <LI>Not even started yet, but I would imagine the structure would follow suit with what I started in Imap, seems to be working well for it so far (however no performance measurements have been done yet).</LI></UL> <LI>Data <UL> <LI>Actually think about the mail store, instead of just quick writing something that works for now ;) <LI>Abstractly, this is what I'd like. <UL> <LI>Mail server has absolutely no dependance and ideally no knowledge of/on a particular data store, only on the interface that defines them <LI>Seemingly contradictory to the above point, the ability to utilize certain perks of various mail stores (for example, I would imagine it might perform better if we translate the arguments to the SEARCH command into a SQL query and gather the results from that instead of loading all messages into memory and scanning through them via C# code, however, the same might not hold true on a different data store, such as a file-system style one (unix mbox or something))</LI></UL> <LI>Ideas to achieve above <UL> <LI>None as of publishing ;)</LI></UL></LI></UL> <LI>Gui <UL> <LI>This isn't pressing, but it's something that going to be done eventually. Basically it'll just be a way to start/stop/pause, add/edit/delete users, modify config files, etc.</LI></UL> <LI>Testing <UL> <LI>Functionality <LI>Performance</LI></UL></LI></UL> </body> </html> |
|
From: <ta...@us...> - 2003-08-08 22:39:31
|
Update of /cvsroot/csmaild/csmaild/src/Common/Encoders In directory sc8-pr-cvs1:/tmp/cvs-serv18360/src/Common/Encoders Added Files: Base64.cs Log Message: Updated VS.NET 2002 project files to reflect currect VS.NET 2003 project Moved message flags into a FlagList class (this makes flag extensions easier to support) Modified the TestClient for easier debugging of the server Added BASE64 decoder to common library Moved argument parsing from ImapCommand into the newly created types (representing various arguments parsed from the client) Added support for AUTHENTICATE PLAIN Added TODO document in the docs !!Some stuff surely has been broken with all this code moving around, it'll compile and run, but some functionality hasn't been verified yet!! --- NEW FILE: Base64.cs --- using System; namespace Common.Encoders { /// <summary> /// Summary description for Base64. /// </summary> public class Base64 { private Base64() { } private static string Base64Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /// <summary> /// Decodes a the BASE64 encoded string (assumes all extraneous characters (even whitespace) has been removed. /// </summary> /// <param name="input">The BASE64 encoded string</param> /// <returns>The byte array representing what was encoded</returns> public static byte[] Decode(string input) { int strLength = input.Length; if(strLength % 4 != 0) return null; byte[] output; int sz = strLength - (strLength / 4); int padChars = 0; // can be padded by 0, 1, or 2 equal characters if(input[strLength-1] == '=') { ++padChars; if(input[strLength-2] == '=') { ++padChars; output = new byte[sz-2]; // 2 equal signs } else output = new byte[sz-1]; // 1 equal signs } else output = new byte[sz]; // 0 equal signs // go through each block except the last one if there is padding int max = strLength - (padChars != 0 ? 4 : 0); int iidx, oidx; for(iidx = 0, oidx = 0; iidx < max; iidx+=4, oidx+=3) { int i1 = Base64Alphabet.IndexOf(input[iidx]); int i2 = Base64Alphabet.IndexOf(input[iidx+1]); int i3 = Base64Alphabet.IndexOf(input[iidx+2]); int i4 = Base64Alphabet.IndexOf(input[iidx+3]); if(i1 == -1 || i2 == -1 || i3 == -1 || i4 == -1) return null; byte o1 = (byte) ((i1 << 2) + ((i2 & 48) >> 4)); // 48 = 110000 byte o2 = (byte)(((i2 & 15) << 4) + ((i3 & 60) >> 2)); // 15 = 001111, 60 = 111100 byte o3 = (byte)(((i3 & 3) << 6) + i4 ); // 3 = 000011 output[oidx] = o1; output[oidx+1] = o2; output[oidx+2] = o3; } // they are all done except the last one (if it was padded), as it // is special and has padding, this code is basically a duplicate // of the loop above, just subsetted to ignore the padding if(padChars == 1) { int i1 = Base64Alphabet.IndexOf(input[iidx]); int i2 = Base64Alphabet.IndexOf(input[iidx+1]); int i3 = Base64Alphabet.IndexOf(input[iidx+2]); if(i1 == -1 || i2 == -1 || i3 == -1) return null; byte o1 = (byte) ((i1 << 2) + ((i2 & 48) >> 4)); // 48 = 110000 byte o2 = (byte)(((i2 & 15) << 4) + ((i3 & 60) >> 2)); // 15 = 001111, 60 = 111100 output[oidx] = o1; output[oidx+1] = o2; } else if(padChars == 0) { int i1 = Base64Alphabet.IndexOf(input[iidx]); int i2 = Base64Alphabet.IndexOf(input[iidx+1]); if(i1 == -1 || i2 == -1) return null; byte o1 = (byte) ((i1 << 2) + ((i2 & 48) >> 4)); // 48 = 110000 output[oidx] = o1; } return output; } } } /* Each 6-bit group is used as an index into an array of 64 printable characters. The character referenced by the index is placed in the output string. These characters, identified in Table 1, below, are selected so as to be universally representable, and the set excludes characters with particular significance to SMTP (e.g., ".", CR, LF) and to the multipart boundary delimiters defined in RFC 2046 (e.g., "-"). Table 1: The Base64 Alphabet Value Encoding Value Encoding Value Encoding Value Encoding 0 A 17 R 34 i 51 z 1 B 18 S 35 j 52 0 2 C 19 T 36 k 53 1 3 D 20 U 37 l 54 2 4 E 21 V 38 m 55 3 5 F 22 W 39 n 56 4 6 G 23 X 40 o 57 5 7 H 24 Y 41 p 58 6 8 I 25 Z 42 q 59 7 9 J 26 a 43 r 60 8 10 K 27 b 44 s 61 9 11 L 28 c 45 t 62 + 12 M 29 d 46 u 63 / 13 N 30 e 47 v 14 O 31 f 48 w (pad) = 15 P 32 g 49 x 16 Q 33 h 50 y Special processing is performed if fewer than 24 bits are available at the end of the data being encoded. A full encoding quantum is always completed at the end of a body. When fewer than 24 input bits are available in an input group, zero bits are added (on the right) to form an integral number of 6-bit groups. Padding at the end of the data is performed using the "=" character. Since all base64 input is an integral number of octets, only the following cases can arise: (1) the final quantum of encoding input is an integral multiple of 24 bits; here, the final unit of encoded output will be an integral multiple of 4 characters with no "=" padding, (2) the final quantum of encoding input is exactly 8 bits; here, the final unit of encoded output will be two characters followed by two "=" padding characters, or (3) the final quantum of encoding input is exactly 16 bits; here, the final unit of encoded output will be three characters followed by one "=" padding character. Because it is used only for padding at the end of the data, the occurrence of any "=" characters may be taken as evidence that the end of the data has been reached (without truncation in transit). No such assurance is possible, however, when the number of octets transmitted was a multiple of three and no "=" characters are present. Any characters outside of the base64 alphabet are to be ignored in base64-encoded data. Care must be taken to use the proper octets for line breaks if base64 encoding is applied directly to text material that has not been converted to canonical form. In particular, text line breaks must be converted into CRLF sequences prior to base64 encoding. The important thing to note is that this may be done directly by the encoder rather than in a prior canonicalization step in some implementations. NOTE: There is no need to worry about quoting potential boundary delimiters within base64-encoded bodies within multipart entities because no hyphen characters are used in the base64 encoding.*/ |
Update of /cvsroot/csmaild/csmaild/src/Imap/Commands In directory sc8-pr-cvs1:/tmp/cvs-serv18360/src/Imap/Commands Modified Files: AppendCommand.cs AuthenticateCommand.cs CapabilityCommand.cs CheckCommand.cs CloseCommand.cs CopyCommand.cs CreateCommand.cs DeleteCommand.cs ExpungeCommand.cs FetchCommand.cs ImapCommand.cs ListCommand.cs LoginCommand.cs LogoutCommand.cs LsubCommand.cs NoopCommand.cs RenameCommand.cs SearchCommand.cs SelectExamineCommand.cs StarttlsCommand.cs StatusCommand.cs StoreCommand.cs SubscribeCommand.cs UnsubscribeCommand.cs Log Message: Updated VS.NET 2002 project files to reflect currect VS.NET 2003 project Moved message flags into a FlagList class (this makes flag extensions easier to support) Modified the TestClient for easier debugging of the server Added BASE64 decoder to common library Moved argument parsing from ImapCommand into the newly created types (representing various arguments parsed from the client) Added support for AUTHENTICATE PLAIN Added TODO document in the docs !!Some stuff surely has been broken with all this code moving around, it'll compile and run, but some functionality hasn't been verified yet!! Index: AppendCommand.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/AppendCommand.cs,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** AppendCommand.cs 29 Jul 2003 00:46:28 -0000 1.5 --- AppendCommand.cs 8 Aug 2003 22:39:26 -0000 1.6 *************** *** 1,9 **** using System; namespace Imap.Commands { - /// <summary> - /// Summary description for AppendCommand. - /// </summary> public class AppendCommand : ImapCommand { --- 1,8 ---- + using Imap.Types; + using System; namespace Imap.Commands { public class AppendCommand : ImapCommand { *************** *** 12,18 **** } override protected void InternalProcess() { ! //return false; } } --- 11,27 ---- } + protected override void CreateArguments() + { + mArguments = new BaseType[] { + new StringType(StringTypeEnum.AString), + new FlagList(), // TODO: make optional + new StringType(StringTypeEnum.Quoted), // TODO: make optional + new StringType(StringTypeEnum.Literal) + }; + } + override protected void InternalProcess() { ! mConnection.SendTaggedMessage("OK APPEND Completed"); } } Index: AuthenticateCommand.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/AuthenticateCommand.cs,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** AuthenticateCommand.cs 29 Jul 2003 00:46:28 -0000 1.5 --- AuthenticateCommand.cs 8 Aug 2003 22:39:26 -0000 1.6 *************** *** 1,9 **** using System; namespace Imap.Commands { - /// <summary> - /// Summary description for AuthenticateCommand. - /// </summary> public class AuthenticateCommand : ImapCommand { --- 1,8 ---- + using Imap.Types; + using System; namespace Imap.Commands { public class AuthenticateCommand : ImapCommand { *************** *** 12,18 **** } override protected void InternalProcess() { ! //return false; } } --- 11,52 ---- } + protected override void CreateArguments() + { + mArguments = new BaseType[] { + new StringType(StringTypeEnum.Atom) + }; + } + override protected void InternalProcess() { ! string authType = (mArguments[0] as StringType); ! authType = authType.ToLower(); ! ! if(authType == "plain") ! { ! mConnection.SendContinueMessage(string.Empty); ! ! string inp = System.Text.Encoding.ASCII.GetString(Common.Encoders.Base64.Decode(mConnection.ReadLine())); ! if(inp == null || inp[0] != '\0' || inp.IndexOf('\0', 1) == -1) ! { ! mConnection.SendTaggedMessage("BAD Couldn't decode request"); ! return; ! } ! ! string[] parts = inp.Split('\0'); ! ! string username = parts[1]; ! string password = parts[2]; ! ! mConnection.User = Server.MailstoreProvider.GetUser(username, password); ! ! if(mConnection.User == null) ! mConnection.SendTaggedMessage("NO Invalid credentials"); ! else ! { ! mConnection.State = ImapConnectionState.Authenticated; ! mConnection.SendTaggedMessage("OK AUTHENTICATE Completed"); ! } ! } } } Index: CapabilityCommand.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/CapabilityCommand.cs,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** CapabilityCommand.cs 29 Jul 2003 00:46:28 -0000 1.5 --- CapabilityCommand.cs 8 Aug 2003 22:39:26 -0000 1.6 *************** *** 4,10 **** namespace Imap.Commands { - /// <summary> - /// Summary description for CapabilityCommand. - /// </summary> public class CapabilityCommand : ImapCommand { --- 4,7 ---- Index: CheckCommand.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/CheckCommand.cs,v retrieving revision 1.6 retrieving revision 1.7 diff -C2 -d -r1.6 -r1.7 *** CheckCommand.cs 5 Aug 2003 02:40:40 -0000 1.6 --- CheckCommand.cs 8 Aug 2003 22:39:26 -0000 1.7 *************** *** 3,9 **** namespace Imap.Commands { - /// <summary> - /// Summary description for CheckCommand. - /// </summary> public class CheckCommand : ImapCommand { --- 3,6 ---- Index: CloseCommand.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/CloseCommand.cs,v retrieving revision 1.6 retrieving revision 1.7 diff -C2 -d -r1.6 -r1.7 *** CloseCommand.cs 5 Aug 2003 02:40:40 -0000 1.6 --- CloseCommand.cs 8 Aug 2003 22:39:26 -0000 1.7 *************** *** 3,9 **** namespace Imap.Commands { - /// <summary> - /// Summary description for CloseCommand. - /// </summary> public class CloseCommand : ImapCommand { --- 3,6 ---- *************** *** 16,20 **** for(uint idx = 1; idx <= mConnection.Mailbox.Messages.Count;) { ! if(mConnection.Mailbox.Messages.BySeq(idx).Deleted) mConnection.Mailbox.Messages.Delete(idx); else --- 13,17 ---- for(uint idx = 1; idx <= mConnection.Mailbox.Messages.Count;) { ! if(mConnection.Mailbox.Messages.BySeq(idx).Flags.Deleted) mConnection.Mailbox.Messages.Delete(idx); else Index: CopyCommand.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/CopyCommand.cs,v retrieving revision 1.7 retrieving revision 1.8 diff -C2 -d -r1.7 -r1.8 *** CopyCommand.cs 5 Aug 2003 01:49:28 -0000 1.7 --- CopyCommand.cs 8 Aug 2003 22:39:26 -0000 1.8 *************** *** 1,21 **** using Common; using System; namespace Imap.Commands { - /// <summary> - /// Summary description for CopyCommand. - /// </summary> public class CopyCommand : ImapCommand { ! public CopyCommand(ImapServer svr) : base(svr, "COPY", ImapConnectionState.Selected, ArgumentType.SequenceSet, ArgumentType.AString) { } override protected void InternalProcess() { ! SequenceSet msgSet = mParsedArguments[0] as SequenceSet; ! string boxFullName = mParsedArguments[1] as string; Mailbox destination = mConnection.User.Mailboxes.FindMailbox(boxFullName); --- 1,34 ---- using Common; + using Imap.Types; + using System; namespace Imap.Commands { public class CopyCommand : ImapCommand { ! public CopyCommand(ImapServer svr) : base(svr, "COPY", ImapConnectionState.Selected) { } + protected override void CreateArguments() + { + uint maxValue; + if(mUidCalled) + maxValue = mConnection.Mailbox.Messages.BySeq(mConnection.Mailbox.MessageCount).UniqueId; + else + maxValue = mConnection.Mailbox.MessageCount; + + mArguments = new BaseType[] { + new SequenceSet(maxValue), + new StringType(StringTypeEnum.AString) + }; + } + override protected void InternalProcess() { ! Types.SequenceSet msgSet = mArguments[0] as Types.SequenceSet; ! string boxFullName = mArguments[1] as StringType; Mailbox destination = mConnection.User.Mailboxes.FindMailbox(boxFullName); *************** *** 25,48 **** else { ! SequenceSet.SequenceRange rng = msgSet.FirstRange; ! while(rng != null) { ! for(uint idx = rng.Low; idx <= rng.High; ++idx) ! { ! if(!mUidCalled && idx > source.MessageCount) ! break; ! if(mUidCalled && idx > source.NextUniqueId) ! break; ! ! Message msg = null; ! if(mUidCalled) ! msg = source.Messages.ByUID(idx); ! else ! msg = source.Messages.BySeq(idx); ! if(msg != null) ! destination.Messages.AddCopy(msg); ! } ! rng = rng.NextRange; } --- 38,51 ---- else { ! foreach(uint idx in msgSet) { ! Message msg = null; ! if(mUidCalled) ! msg = source.Messages.ByUID(idx); ! else ! msg = source.Messages.BySeq(idx); ! if(msg != null) ! destination.Messages.AddCopy(msg); } Index: CreateCommand.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/CreateCommand.cs,v retrieving revision 1.7 retrieving revision 1.8 diff -C2 -d -r1.7 -r1.8 *** CreateCommand.cs 29 Jul 2003 00:46:28 -0000 1.7 --- CreateCommand.cs 8 Aug 2003 22:39:26 -0000 1.8 *************** *** 1,20 **** using Common; using System; namespace Imap.Commands { - /// <summary> - /// Summary description for CreateCommand. - /// </summary> public class CreateCommand : ImapCommand { ! public CreateCommand(ImapServer svr) : base(svr, "CREATE", ImapCommand.AuthSelectedState, ArgumentType.AString) { } override protected void InternalProcess() { ! string boxFullName = mParsedArguments[0] as string; if(boxFullName.ToUpper() == "INBOX") --- 1,26 ---- using Common; + using Imap.Types; + using System; namespace Imap.Commands { public class CreateCommand : ImapCommand { ! public CreateCommand(ImapServer svr) : base(svr, "CREATE", ImapCommand.AuthSelectedState) ! { ! } ! ! protected override void CreateArguments() { + mArguments = new BaseType[] { + new StringType(StringTypeEnum.AString) + }; } override protected void InternalProcess() { ! string boxFullName = mArguments[0] as StringType; if(boxFullName.ToUpper() == "INBOX") Index: DeleteCommand.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/DeleteCommand.cs,v retrieving revision 1.6 retrieving revision 1.7 diff -C2 -d -r1.6 -r1.7 *** DeleteCommand.cs 29 Jul 2003 00:46:28 -0000 1.6 --- DeleteCommand.cs 8 Aug 2003 22:39:26 -0000 1.7 *************** *** 1,20 **** using Common; using System; namespace Imap.Commands { - /// <summary> - /// Summary description for DeleteCommand. - /// </summary> public class DeleteCommand : ImapCommand { ! public DeleteCommand(ImapServer svr) : base(svr, "DELETE", ImapCommand.AuthSelectedState, ArgumentType.AString) { } override protected void InternalProcess() { ! string boxFullName = mParsedArguments[0] as string; if(boxFullName.ToUpper() == "INBOX") --- 1,26 ---- using Common; + using Imap.Types; + using System; namespace Imap.Commands { public class DeleteCommand : ImapCommand { ! public DeleteCommand(ImapServer svr) : base(svr, "DELETE", ImapCommand.AuthSelectedState) ! { ! } ! ! protected override void CreateArguments() { + mArguments = new BaseType[] { + new StringType(StringTypeEnum.AString) + }; } override protected void InternalProcess() { ! string boxFullName = mArguments[0] as StringType; if(boxFullName.ToUpper() == "INBOX") Index: ExpungeCommand.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/ExpungeCommand.cs,v retrieving revision 1.6 retrieving revision 1.7 diff -C2 -d -r1.6 -r1.7 *** ExpungeCommand.cs 5 Aug 2003 01:49:28 -0000 1.6 --- ExpungeCommand.cs 8 Aug 2003 22:39:26 -0000 1.7 *************** *** 13,17 **** for(uint idx = 1; idx <= mConnection.Mailbox.Messages.Count;) { ! if(mConnection.Mailbox.Messages.BySeq(idx).Deleted) { mConnection.Mailbox.Messages.Delete(idx); --- 13,17 ---- for(uint idx = 1; idx <= mConnection.Mailbox.Messages.Count;) { ! if(mConnection.Mailbox.Messages.BySeq(idx).Flags.Deleted) { mConnection.Mailbox.Messages.Delete(idx); Index: FetchCommand.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/FetchCommand.cs,v retrieving revision 1.8 retrieving revision 1.9 diff -C2 -d -r1.8 -r1.9 *** FetchCommand.cs 5 Aug 2003 01:49:28 -0000 1.8 --- FetchCommand.cs 8 Aug 2003 22:39:26 -0000 1.9 *************** *** 2,5 **** --- 2,7 ---- using rfc = Common.Rfc2822; + using Imap.Types; + using System; using System.Collections; *************** *** 11,149 **** namespace Imap.Commands [...980 lines suppressed...] ! // private bool ParseHeaderList(ParenthesizedList destinationList) ! // { ! // if(mList[mListIdx].IsNestedList) ! // { ! // ParenthesizedList headerList = mList[mListIdx].NestedList; ! // for(int idx = 0; idx < headerList.Count; ++idx) ! // { ! // if(headerList[idx].IsNestedList) ! // return false; ! // else ! // destinationList.AddItem(headerList[idx].ToString()); ! // } ! // return true; ! // } ! // else ! // return false; ! // } #endregion } Index: ImapCommand.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/ImapCommand.cs,v retrieving revision 1.13 retrieving revision 1.14 diff -C2 -d -r1.13 -r1.14 *** ImapCommand.cs 5 Aug 2003 01:50:24 -0000 1.13 --- ImapCommand.cs 8 Aug 2003 22:39:26 -0000 1.14 *************** *** 3,6 **** --- 3,7 ---- using System; using System.Collections; + using System.IO; using System.Text; using System.Text.RegularExpressions; *************** *** 20,30 **** private ImapConnectionState mValidStates; private ImapServer mServer; - private ArgumentType[] mArgumentTypes; private string mUnparsedArguments; - private int mUnparsedArgumentIdx; protected bool mUidCalled; ! protected object[] mParsedArguments; protected ImapConnection mConnection; #endregion --- 21,29 ---- private ImapConnectionState mValidStates; private ImapServer mServer; private string mUnparsedArguments; protected bool mUidCalled; ! protected Types.BaseType[] mArguments; protected ImapConnection mConnection; #endregion *************** *** 56,70 **** /// <param name="validStates">Which connection states are valid for this command</param> /// <param name="argTypes">The argument types for this command (use ArgumentType.CustomHandler if you want to do it yourself)</param> ! protected ImapCommand(ImapServer svr, string command, Imap.ImapConnectionState validStates, params ArgumentType[] argTypes) { mServer = svr; mCommand = command; mValidStates = validStates; - - mArgumentTypes = argTypes; - if(mArgumentTypes == null) - mArgumentTypes = new ArgumentType[0]; - - mParsedArguments = new object[mArgumentTypes.Length]; } --- 55,63 ---- /// <param name="validStates">Which connection states are valid for this command</param> /// <param name="argTypes">The argument types for this command (use ArgumentType.CustomHandler if you want to do it yourself)</param> ! protected ImapCommand(ImapServer svr, string command, Imap.ImapConnectionState validStates) { mServer = svr; mCommand = command; mValidStates = validStates; } *************** *** 74,82 **** mUnparsedArguments = arguments; mConnection = con; ! // initialize couples as the uninitialize of the previous call ! mUnparsedArgumentIdx = 0; ! for(int idx = 0; idx < mParsedArguments.Length; ++idx) ! mParsedArguments[idx] = null; } #endregion --- 67,75 ---- mUnparsedArguments = arguments; mConnection = con; + } ! virtual protected void CreateArguments() ! { ! mArguments = new Types.BaseType[0]; } #endregion *************** *** 97,102 **** if(!ParseArguments()) mConnection.SendTaggedMessage("BAD Invalid arguments"); - else if(mUnparsedArgumentIdx < mUnparsedArguments.Length) - mConnection.SendTaggedMessage("BAD Too many arguments"); else { --- 90,93 ---- *************** *** 112,662 **** #region Argument parsing stuff ! #region Global argument stuff ! protected enum ArgumentType ! { ! CustomHandler = 0, ! QuotedString = 1, ! AtomString = 2, ! AStringChar = 4, ! LiteralString = 8, ! AString = 13, // quoted(1) + astringchar(4) + literal(8) ! ListChars = 16, ! ListString = 25, // quoted(1) + literal(8) + listchars(16) ! SequenceSet = 32, ! ParenthesizedList = 64, ! ParenthesizedListAtom = 66, // paren(64) + atom(2) ! Optional = 128, ! OptionalParenthesizedList = 192, // optional(128) + paren(64) ! OptionalQuotedString = 129, // optional(128) + quoted(1) ! FlagListFlag = 256, ! } ! ! private bool ParseArguments() { ! for(int idx = 0; idx < mArgumentTypes.Length; ++idx) { ! object arg = null; ! if((mArgumentTypes[idx] & ArgumentType.Optional) == ArgumentType.Optional) ! { ! if(mUnparsedArgumentIdx < mUnparsedArguments.Length) ! { ! if(mUnparsedArguments[mUnparsedArgumentIdx++] != ' ') ! return false; ! arg = ParseArgument(mArgumentTypes[idx]); ! if(arg == null) ! --mUnparsedArgumentIdx; // move back to the space ! } ! } ! else ! { ! if(mUnparsedArgumentIdx >= mUnparsedArguments.Length) ! return false; ! else if(mUnparsedArguments[mUnparsedArgumentIdx++] != ' ') ! return false; ! arg = ParseArgument(mArgumentTypes[idx]); ! if(arg == null) ! return false; ! } ! mParsedArguments[idx] = arg; ! } ! return true; ! } ! ! private object ParseArgument(ArgumentType argType) ! { ! if(mUnparsedArgumentIdx >= mUnparsedArguments.Length) // no more string to parse ! return null; ! else if(mUnparsedArguments == string.Empty) ! return null; ! else if(((argType & ArgumentType.LiteralString) == ArgumentType.LiteralString) && mUnparsedArguments[mUnparsedArgumentIdx] == '{') // literal string ! return ParseLiteralString(); ! else if(((argType & ArgumentType.ParenthesizedList) == ArgumentType.ParenthesizedList) && mUnparsedArguments[mUnparsedArgumentIdx] == '(') // paren list ! return ParseParenthesizedList(); ! else if(((argType & ArgumentType.QuotedString) == ArgumentType.QuotedString) && mUnparsedArguments[mUnparsedArgumentIdx] == '"') // quoted string ! return ParseQuotedString(); ! else if(((argType & ArgumentType.ListChars) == ArgumentType.ListChars)) ! return ParseWhileGood(CommandPart.ListChar); ! else if((argType & ArgumentType.AtomString) == ArgumentType.AtomString) // atom ! return ParseWhileGood(CommandPart.AtomChar); ! else if((argType & ArgumentType.AStringChar) == ArgumentType.AStringChar) // astring-char ! return ParseWhileGood(CommandPart.AStringChar); ! else if((argType & ArgumentType.SequenceSet) == ArgumentType.SequenceSet) // sequence set ! return ParseSequenceSet(); ! else if((argType & ArgumentType.FlagListFlag) == ArgumentType.FlagListFlag) // flag-list / flag ! return ParseFlagListFlag(); ! return null; ! } ! private string ParseWhileGood(string validChars) ! { ! StringBuilder strBuilder = new StringBuilder(); ! for(; mUnparsedArgumentIdx < mUnparsedArguments.Length; ++mUnparsedArgumentIdx) ! { ! if(validChars.IndexOf(mUnparsedArguments[mUnparsedArgumentIdx]) == -1) break; - else - strBuilder.Append(mUnparsedArguments[mUnparsedArgumentIdx]); - } - - string str = strBuilder.ToString(); - if(str == string.Empty) - return null; - - return str; - } - - private object ParseFlagListFlag() - { - if(mUnparsedArguments[mUnparsedArgumentIdx] != '(') - return ParseWhileGood(CommandPart.FlagChar); - else - { - ParenthesizedList list = new ParenthesizedList(); - ++mUnparsedArgumentIdx; - - while(true) - { - list.AddItem(ParseWhileGood(CommandPart.FlagChar)); - if(mUnparsedArguments[mUnparsedArgumentIdx] == ' ') - ++mUnparsedArgumentIdx; - else if(mUnparsedArguments[mUnparsedArgumentIdx] == ')') - break; - else - return null; - } ! ++mUnparsedArgumentIdx; ! return list; ! } ! } ! #endregion ! ! #region Literal string ! private string ParseLiteralString() ! { ! string literalSizeString = mUnparsedArguments.Substring(mUnparsedArgumentIdx); ! mUnparsedArgumentIdx += literalSizeString.Length; ! Match match = Regex.Match(literalSizeString, @"^\{([1-9][0-9]{0,9})\}$"); ! if(!match.Success) ! return null; ! ! // TODO: some where we need to make sure that this isn't bigger then uint.Max (4,294,967,295) before we put it into the variable ! uint literalSize = uint.Parse(match.Groups[1].ToString()); ! ! mConnection.SendContinueMessage("More"); ! ! // TODO: if literalSize is big, need to adjust, and do something more intelligent for "chunking" as we read ! string literalString = mConnection.ReadBlockAsString((int)literalSize); ! ! // make sure it's a valid literal string (any ascii character besides \0) ! if(!Regex.IsMatch(literalString, @"^[\x01-\xFF]*$")) ! return null; ! ! // got possibly new arguments, finish the line off and make them uparsed as well ! mUnparsedArguments += mConnection.ReadLine(); ! ! return literalString; ! } ! #endregion ! ! #region Quoted sring ! private string ParseQuotedString() ! { ! bool readEscape = false; // keeps track of whether or not we just read the escape character "\" ! StringBuilder quotedString = new StringBuilder(); // will store the quoted string as we "read" it ! ! for(++mUnparsedArgumentIdx; mUnparsedArgumentIdx < mUnparsedArguments.Length; ++mUnparsedArgumentIdx) ! { ! if(mUnparsedArguments[mUnparsedArgumentIdx] == '"') ! { ! if(readEscape) ! quotedString.Append(mUnparsedArguments[mUnparsedArgumentIdx]); ! else ! { ! ++mUnparsedArgumentIdx; ! return quotedString.ToString(); ! } ! } ! else if(mUnparsedArguments[mUnparsedArgumentIdx] == '\\') ! { ! if(readEscape) ! quotedString.Append(mUnparsedArguments[mUnparsedArgumentIdx]); ! else ! readEscape = true; ! } ! else ! { ! if(CommandPart.TextChar.IndexOf(mUnparsedArguments[mUnparsedArgumentIdx]) != -1) // it's found, good char ! quotedString.Append(mUnparsedArguments[mUnparsedArgumentIdx]); ! else // not found, bad char ! { ! ++mUnparsedArgumentIdx; ! break; ! } ! } ! } ! ! return null; ! } ! #endregion ! ! #region Sequence string ! private SequenceSet ParseSequenceSet() ! { ! int endIdx = mUnparsedArguments.IndexOf(' ', mUnparsedArgumentIdx); ! string sequenceSetString = string.Empty; ! SequenceSet sequenceSet = new SequenceSet(); // will store the sequenceSet string as we "read" it ! if(endIdx == -1) ! sequenceSetString = mUnparsedArguments.Substring(mUnparsedArgumentIdx); ! else ! sequenceSetString = mUnparsedArguments.Substring(mUnparsedArgumentIdx, endIdx - mUnparsedArgumentIdx); ! ! string[] parts = sequenceSetString.Split(','); ! foreach(string part in parts) ! { ! Match m = Regex.Match(part, @"^(?:(?:([1-9][0-9]{0,9})(?::([1-9][0-9]{0,9}|\*))?)|(?:(\*):([1-9][0-9]{0,9})))$"); ! if(!m.Success) ! return null; ! ! uint startNum = 0; ! uint endNum = 0; ! ! if(m.Groups[1].Success) ! { ! startNum = uint.Parse(m.Groups[1].Value); ! if(m.Groups[2].Success) ! { ! if(m.Groups[2].Value == "*") ! endNum = uint.MaxValue; ! else ! endNum = uint.Parse(m.Groups[2].Value); ! } ! else ! endNum = startNum; ! } ! else if(m.Groups[3].Success) ! { ! if(m.Groups[3].Value == "*") ! startNum = uint.MaxValue; ! else ! startNum = uint.Parse(m.Groups[3].Value); ! endNum = uint.Parse(m.Groups[4].Value); ! } ! ! sequenceSet.AddRange(startNum, endNum); ! } ! ! mUnparsedArgumentIdx += sequenceSetString.Length; ! ! return sequenceSet; ! } ! ! #region SequenceSet ! protected class SequenceSet ! { ! public class SequenceRange ! { ! private uint mLow; ! private uint mHigh; ! ! private SequenceRange mPrevious; ! private SequenceRange mNext; ! ! public SequenceRange NextRange ! { ! get ! { ! return mNext; ! } ! } ! ! public SequenceRange PreviousRange ! { ! get ! { ! return mPrevious; ! } ! } ! ! public uint Low ! { ! get ! { ! return mLow; ! } ! set ! { ! if(value < mLow) ! { ! mLow = value; ! ! // we should combine with the previous ! while(mPrevious != null && value <= mPrevious.mHigh+1) ! { ! mLow = mPrevious.mLow; ! mPrevious = mPrevious.mPrevious; ! if(mPrevious != null) ! mPrevious.mNext = this; ! } ! } ! } ! } ! ! public uint High ! { ! get ! { ! return mHigh; ! } ! set ! { ! if(value > mHigh) ! { ! mHigh = value; ! ! // we should combine with the next ! while(mNext != null && value >= mNext.mLow-1) ! { ! mHigh = mNext.mHigh; ! mNext = mNext.mNext; ! if(mNext != null) ! mNext.mPrevious = this; ! } ! } ! } ! } ! ! public SequenceRange(uint low, uint high) ! { ! mLow = low; ! mHigh = high; ! mPrevious = null; ! mNext = null; ! } ! ! public SequenceRange AddRange(uint low, uint high) ! { ! if(low <= mHigh + 1 && high >= mLow - 1) // they can combine with us ! { ! Low = low; ! High = high; ! return this; ! } ! else // they cannot combine with us (move to the next guy) ! { ! if(low < mLow) // taking our spot ! { ! SequenceRange rng = new SequenceRange(low, high); ! rng.mNext = this; // new guy is prior to us ! rng.mPrevious = mPrevious; // after our old previous ! if(mPrevious != null) ! mPrevious.mNext = rng; ! mPrevious = rng; ! return rng; // new root node ! } ! else if(mNext == null) // nobody is after us ! { ! SequenceRange rng = new SequenceRange(low, high); ! rng.mPrevious = this; ! mNext = rng; ! return this; ! } ! else ! { ! mNext.AddRange(low, high); ! return this; ! } ! } ! } ! ! public override string ToString() ! { ! return mLow + ":" + (mHigh == uint.MaxValue ? "*" : mHigh.ToString()) + (mNext != null ? "," + mNext.ToString() : string.Empty); ! } ! } ! ! private SequenceRange mRootNode; ! ! public SequenceRange FirstRange ! { ! get ! { ! return mRootNode; ! } ! } ! ! public void AddRange(uint num) ! { ! AddRange(num, num); ! } ! ! public void AddRange(uint start, uint end) ! { ! uint low = Math.Min(start, end); ! uint high = Math.Max(start, end); ! ! if(mRootNode == null) ! mRootNode = new SequenceRange(low, high); ! else ! mRootNode = mRootNode.AddRange(low, high); ! } ! ! public override string ToString() ! { ! return mRootNode.ToString(); ! } ! } ! #endregion ! ! #endregion ! ! #region Parenthesized list string ! protected virtual ParenthesizedList ParseParenthesizedList() ! { ! string dataItem = string.Empty; // stores the data item as we're reading it ! ParenthesizedList list = new ParenthesizedList(); ! ! for(++mUnparsedArgumentIdx; mUnparsedArgumentIdx < mUnparsedArguments.Length; ++mUnparsedArgumentIdx) ! { ! if(mUnparsedArguments[mUnparsedArgumentIdx] == ')') ! { ! if(dataItem != string.Empty) ! list.AddItem(dataItem); ! ! ++mUnparsedArgumentIdx; ! ! return list; ! } ! else if(mUnparsedArguments[mUnparsedArgumentIdx] == '(') ! { ! if(dataItem != string.Empty) // can't start a list in the middle of an item ! return null; ! ParenthesizedList nestedList = ParseParenthesizedList(); ! if(nestedList == null) ! return null; ! list.AddItem(nestedList); ! --mUnparsedArgumentIdx; ! } ! else ! { ! if(mUnparsedArguments[mUnparsedArgumentIdx] == ' ') ! ++mUnparsedArgumentIdx; ! string arg = ParseArgument(ArgumentType.AString) as string; ! if(arg == null) ! return null; ! list.AddItem(arg); ! if(mUnparsedArguments[mUnparsedArgumentIdx] == ')') ! --mUnparsedArgumentIdx; ! } } - - return null; } ! #region ParenthesizedList ! public class ParenthesizedList { ! #region ParenthesizedListItem ! public class ParenthesizedListItem ! { ! private object mDataItem; ! private ParenthesizedList mNestedList; ! ! public object DataItem ! { ! get ! { ! return mDataItem; ! } ! } ! ! public ParenthesizedList NestedList ! { ! get ! { ! return mNestedList; ! } ! } ! ! public bool IsNestedList ! { ! get ! { ! return (mNestedList != null); ! } ! } ! ! public ParenthesizedListItem(object data) ! { ! if(data is ParenthesizedList) ! { ! mDataItem = null; ! mNestedList = data as ParenthesizedList; ! } ! else ! { ! mDataItem = data; ! mNestedList = null; ! } ! } ! ! public override string ToString() ! { ! if(mDataItem == null) ! return mNestedList.ToString(); ! else ! return mDataItem.ToString(); ! } ! ! } ! #endregion ! ! private ArrayList mItems; ! public ParenthesizedList() ! { ! mItems = new ArrayList(); ! } ! public void AddItem(object data) { ! mItems.Add(new ParenthesizedListItem(data)); ! } ! public override string ToString() ! { ! string rv = "("; ! for(int idx = 0; idx < mItems.Count; ++idx) ! rv += mItems[idx].ToString() + (idx != mItems.Count-1 ? " " : string.Empty); ! return rv + ")"; ! } ! public ParenthesizedListItem this[int idx] ! { ! get ! { ! return mItems[idx] as ParenthesizedListItem; ! } } ! public int Count ! { ! get ! { ! return mItems.Count; ! } ! } ! public void Clear() ! { ! mItems.Clear(); ! } } - #endregion - - #endregion #endregion --- 103,155 ---- #region Argument parsing stuff ! private void HandleLiterals() { ! while(true) { ! Match match = Regex.Match(mUnparsedArguments, @".*\ {([1-9][0-9]{0,9})\}$"); ! // this line ends with a literal string identifier ! if(!match.Success) break; ! // TODO: some where we need to make sure that this isn't bigger then uint.Max (4,294,967,295) before we put it into the variable ! string mtch = match.Groups[1].ToString(); ! uint literalSize = uint.Parse(mtch); ! mConnection.SendContinueMessage("More"); ! // TODO: if literalSize is big, need to adjust, and do something more intelligent for "chunking" as we read ! string literalString = mConnection.ReadBlockAsString((int)literalSize); ! // got possibly new arguments, finish the line off and make them uparsed as well ! mUnparsedArguments += mConnection.ReadLine(); } } ! private bool ParseArguments() { ! HandleLiterals(); // HACK: need to handle arguments better (damn literal strings, I hate thee) ! CreateArguments(); ! TextReader rdr = new StringReader(mUnparsedArguments); ! for(int idx = 0; idx < mArguments.Length; ++idx) { ! int intVal = rdr.Read(); ! if(intVal == -1) ! return false; ! if(Convert.ToChar(intVal) != ' ') ! return false; ! if(!(mArguments[idx] as Types.BaseType).Read(rdr)) ! return false; } ! if(rdr.Read() != -1) ! return false; ! return true; } #endregion Index: ListCommand.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/ListCommand.cs,v retrieving revision 1.7 retrieving revision 1.8 diff -C2 -d -r1.7 -r1.8 *** ListCommand.cs 5 Aug 2003 01:44:39 -0000 1.7 --- ListCommand.cs 8 Aug 2003 22:39:26 -0000 1.8 *************** *** 1,4 **** --- 1,6 ---- using Common; + using Imap.Types; + using System; using System.Text; *************** *** 7,13 **** namespace Imap.Commands { - /// <summary> - /// Summary description for ListCommand. - /// </summary> public class ListCommand : ImapCommand { --- 9,12 ---- *************** *** 15,26 **** private string mMailboxSearchPattern; ! public ListCommand(ImapServer svr) : base(svr, "LIST", ImapCommand.AuthSelectedState, ArgumentType.AString, ArgumentType.ListString) { } override protected void InternalProcess() { ! mReferenceName = mParsedArguments[0] as string; ! mMailboxSearchPattern = ListSearchToRegex(mReferenceName, mParsedArguments[1] as string); // if we have a reference name, navigate to it and perform the list from that level --- 14,33 ---- private string mMailboxSearchPattern; ! public ListCommand(ImapServer svr) : base(svr, "LIST", ImapCommand.AuthSelectedState) { } + protected override void CreateArguments() + { + mArguments = new BaseType[] { + new StringType(StringTypeEnum.AString), + new StringType(StringTypeEnum.List) + }; + } + override protected void InternalProcess() { ! mReferenceName = mArguments[0] as StringType; ! mMailboxSearchPattern = ListSearchToRegex(mReferenceName, mArguments[1] as StringType); // if we have a reference name, navigate to it and perform the list from that level Index: LoginCommand.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/LoginCommand.cs,v retrieving revision 1.6 retrieving revision 1.7 diff -C2 -d -r1.6 -r1.7 *** LoginCommand.cs 5 Aug 2003 01:49:28 -0000 1.6 --- LoginCommand.cs 8 Aug 2003 22:39:26 -0000 1.7 *************** *** 1,21 **** using System; namespace Imap.Commands { - /// <summary> - /// Summary description for LoginCommand. - /// </summary> public class LoginCommand : ImapCommand { ! public LoginCommand(ImapServer svr) : base(svr, "LOGIN", ImapConnectionState.NotAuthenticated, ArgumentType.AString, ArgumentType.AString) { } override protected void InternalProcess() { ! mConnection.User = Server.MailstoreProvider.GetUser(mParsedArguments[0] as String, mParsedArguments[1] as String); if(mConnection.User == null) ! mConnection.SendTaggedMessage("NO"); else { --- 1,31 ---- + using Imap.Types; + using System; namespace Imap.Commands { public class LoginCommand : ImapCommand { ! public LoginCommand(ImapServer svr) : base(svr, "LOGIN", ImapConnectionState.NotAuthenticated) ! { ! } ! ! protected override void CreateArguments() { + mArguments = new BaseType[] { + new StringType(StringTypeEnum.AString), + new StringType(StringTypeEnum.AString) + }; } override protected void InternalProcess() { ! string username = mArguments[0] as StringType; ! string password = mArguments[1] as StringType; ! ! mConnection.User = Server.MailstoreProvider.GetUser(username, password); if(mConnection.User == null) ! mConnection.SendTaggedMessage("NO Invalid credentials"); else { Index: LogoutCommand.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/LogoutCommand.cs,v retrieving revision 1.6 retrieving revision 1.7 diff -C2 -d -r1.6 -r1.7 *** LogoutCommand.cs 5 Aug 2003 01:49:28 -0000 1.6 --- LogoutCommand.cs 8 Aug 2003 22:39:26 -0000 1.7 *************** *** 3,9 **** namespace Imap.Commands { - /// <summary> - /// Summary description for LogoutCommand. - /// </summary> public class LogoutCommand : ImapCommand { --- 3,6 ---- Index: LsubCommand.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/LsubCommand.cs,v retrieving revision 1.6 retrieving revision 1.7 diff -C2 -d -r1.6 -r1.7 *** LsubCommand.cs 5 Aug 2003 01:44:39 -0000 1.6 --- LsubCommand.cs 8 Aug 2003 22:39:26 -0000 1.7 *************** *** 1,4 **** --- 1,6 ---- using Common; + using Imap.Types; + using System; using System.Text.RegularExpressions; *************** *** 6,12 **** namespace Imap.Commands { - /// <summary> - /// Summary description for LsubCommand. - /// </summary> public class LsubCommand : ImapCommand { --- 8,11 ---- *************** *** 15,28 **** private bool[] mSubscribedHandled; ! public LsubCommand(ImapServer svr) : base(svr, "LSUB", ImapCommand.AuthSelectedState, ArgumentType.AString, ArgumentType.ListString) { } override protected void InternalProcess() { mSubscribedHandled = new bool[mConnection.User.SubscribedMailboxes.Count]; ! mReferenceName = mParsedArguments[0] as string; ! mMailboxSearchPattern = ListCommand.ListSearchToRegex(mReferenceName, mParsedArguments[1] as string); // if we have a reference name, navigate to it and perform the list from that level --- 14,35 ---- private bool[] mSubscribedHandled; ! public LsubCommand(ImapServer svr) : base(svr, "LSUB", ImapCommand.AuthSelectedState) { } + protected override void CreateArguments() + { + mArguments = new BaseType[] { + new StringType(StringTypeEnum.AString), + new StringType(StringTypeEnum.List) + }; + } + override protected void InternalProcess() { mSubscribedHandled = new bool[mConnection.User.SubscribedMailboxes.Count]; ! mReferenceName = mArguments[0] as StringType; ! mMailboxSearchPattern = ListCommand.ListSearchToRegex(mReferenceName, mArguments[1] as StringType); // if we have a reference name, navigate to it and perform the list from that level Index: NoopCommand.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/NoopCommand.cs,v retrieving revision 1.6 retrieving revision 1.7 diff -C2 -d -r1.6 -r1.7 *** NoopCommand.cs 5 Aug 2003 01:49:28 -0000 1.6 --- NoopCommand.cs 8 Aug 2003 22:39:26 -0000 1.7 *************** *** 3,9 **** namespace Imap.Commands { - /// <summary> - /// Summary description for NoopCommand. - /// </summary> public class NoopCommand : ImapCommand { --- 3,6 ---- Index: RenameCommand.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/RenameCommand.cs,v retrieving revision 1.6 retrieving revision 1.7 diff -C2 -d -r1.6 -r1.7 *** RenameCommand.cs 30 Jul 2003 22:36:42 -0000 1.6 --- RenameCommand.cs 8 Aug 2003 22:39:26 -0000 1.7 *************** *** 1,21 **** using Common; using System; namespace Imap.Commands { - /// <summary> - /// Summary description for RenameCommand. - /// </summary> public class RenameCommand : ImapCommand { ! public RenameCommand(ImapServer svr) : base(svr, "RENAME", ImapCommand.AuthSelectedState, ArgumentType.AString, ArgumentType.AString) { } override protected void InternalProcess() { ! string srcName = mParsedArguments[0] as string; ! string dstName = mParsedArguments[1] as string; Mailbox srcBox = mConnection.User.Mailboxes.FindMailbox(srcName); --- 1,28 ---- using Common; + using Imap.Types; + using System; namespace Imap.Commands { public class RenameCommand : ImapCommand { ! public RenameCommand(ImapServer svr) : base(svr, "RENAME", ImapCommand.AuthSelectedState) { } + protected override void CreateArguments() + { + mArguments = new BaseType[] { + new StringType(StringTypeEnum.AString), + new StringType(StringTypeEnum.AString) + }; + } + override protected void InternalProcess() { ! string srcName = mArguments[0] as StringType; ! string dstName = mArguments[1] as StringType; Mailbox srcBox = mConnection.User.Mailboxes.FindMailbox(srcName); *************** *** 37,42 **** Mailbox dstBox = dstBoxes.Add(dstName); ! // move the messages ! srcBox.MoveMessages(dstBox); } else --- 44,49 ---- Mailbox dstBox = dstBoxes.Add(dstName); ! // move the messages : TODO: move them ! //srcBox.MoveMessages(dstBox); } else Index: SearchCommand.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/SearchCommand.cs,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** SearchCommand.cs 29 Jul 2003 00:46:28 -0000 1.5 --- SearchCommand.cs 8 Aug 2003 22:39:26 -0000 1.6 *************** *** 1,9 **** using System; namespace Imap.Commands { - /// <summary> - /// Summary description for SearchCommand. - /// </summary> public class SearchCommand : ImapCommand { --- 1,8 ---- + using Imap.Types; + using System; namespace Imap.Commands { public class SearchCommand : ImapCommand { *************** *** 12,18 **** } override protected void InternalProcess() { ! //return false; } } --- 11,25 ---- } + protected override void CreateArguments() + { + mArguments = new BaseType[] { + new StringType(StringTypeEnum.AString), // TODO: make optional + new SearchRequest() // TODO: make this the search thing when finished + }; + } + override protected void InternalProcess() { ! mConnection.SendTaggedMessage("OK"); } } Index: SelectExamineCommand.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/SelectExamineCommand.cs,v retrieving revision 1.4 retrieving revision 1.5 diff -C2 -d -r1.4 -r1.5 *** SelectExamineCommand.cs 30 Jul 2003 22:36:42 -0000 1.4 --- SelectExamineCommand.cs 8 Aug 2003 22:39:26 -0000 1.5 *************** *** 1,23 **** using Common; using System; namespace Imap.Commands { - /// <summary> - /// Summary description for SelectExamineCommand. - /// </summary> public class SelectExamineCommand : ImapCommand { private bool mIsSelect; // true if we're actually SELECT false if we're EXAMINE ! public SelectExamineCommand(bool isSelect, ImapServer svr) : base(svr, (isSelect ? "SELECT" : "EXAMINE") , ImapCommand.AuthSelectedState, ArgumentType.AString) { mIsSelect = isSelect; } override protected void InternalProcess() { ! string boxFullName = mParsedArguments[0] as string; mConnection.Mailbox = mConnection.User.Mailboxes.FindMailbox(boxFullName); --- 1,29 ---- using Common; + using Imap.Types; + using System; namespace Imap.Commands { public class SelectExamineCommand : ImapCommand { private bool mIsSelect; // true if we're actually SELECT false if we're EXAMINE ! public SelectExamineCommand(bool isSelect, ImapServer svr) : base(svr, (isSelect ? "SELECT" : "EXAMINE") , ImapCommand.AuthSelectedState) { mIsSelect = isSelect; } + protected override void CreateArguments() + { + mArguments = new BaseType[] { + new StringType(StringTypeEnum.AString) + }; + } + override protected void InternalProcess() { ! string boxFullName = mArguments[0] as StringType; mConnection.Mailbox = mConnection.User.Mailboxes.FindMailbox(boxFullName); Index: StarttlsCommand.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/StarttlsCommand.cs,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** StarttlsCommand.cs 29 Jul 2003 00:46:28 -0000 1.5 --- StarttlsCommand.cs 8 Aug 2003 22:39:26 -0000 1.6 *************** *** 3,9 **** namespace Imap.Commands { - /// <summary> - /// Summary description for StarttlsCommand. - /// </summary> public class StarttlsCommand : ImapCommand { --- 3,6 ---- *************** *** 14,18 **** override protected void InternalProcess() { ! //return false; } } --- 11,15 ---- override protected void InternalProcess() { ! mConnection.SendTaggedMessage("OK"); } } Index: StatusCommand.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/StatusCommand.cs,v retrieving revision 1.8 retrieving revision 1.9 diff -C2 -d -r1.8 -r1.9 *** StatusCommand.cs 5 Aug 2003 01:49:28 -0000 1.8 --- StatusCommand.cs 8 Aug 2003 22:39:26 -0000 1.9 *************** *** 1,4 **** --- 1,6 ---- using Common; + using Imap.Types; + using System; using System.Text; *************** *** 6,22 **** namespace Imap.Commands { - /// <summary> - /// Summary description for StatusCommand. - /// </summary> public class StatusCommand : ImapCommand { ! public StatusCommand(ImapServer svr) : base(svr, "STATUS", ImapCommand.AuthSelectedState, ArgumentType.AString, ArgumentType.ParenthesizedList) { } override protected void InternalProcess() { ! string boxName = mParsedArguments[0] as String; ! ParenthesizedList list = mParsedArguments[1] as ParenthesizedList; Mailbox box = mConnection.User.Mailboxes.FindMailbox(boxName); --- 8,29 ---- namespace Imap.Commands { public class StatusCommand : ImapCommand { ! public StatusCommand(ImapServer svr) : base(svr, "STATUS", ImapCommand.AuthSelectedState) { } + protected override void CreateArguments() + { + mArguments = new BaseType[] { + new StringType(StringTypeEnum.AString), + new StatusRequest() + }; + } + override protected void InternalProcess() { ! string boxName = mArguments[0] as StringType; ! StatusRequest statusReq = mArguments[1] as StatusRequest; Mailbox box = mConnection.User.Mailboxes.FindMailbox(boxName); *************** *** 25,64 **** else { ! ParenthesizedList output = new ParenthesizedList(); ! ! int idx = 0; ! for(; idx < list.Count; ++idx) ! { ! ParenthesizedList.ParenthesizedListItem item = list[idx]; ! if(item.IsNestedList) ! { ! mConnection.SendTaggedMessage("BAD Invalid request"); ! return; ! } ! output.AddItem(item.DataItem); ! switch((item.DataItem as string).ToUpper()) ! { ! case "MESSAGES": ! output.AddItem(box.MessageCount); ! break; ! case "RECENT": ! output.AddItem(box.MessageRecentCount); ! break; ! case "UIDNEXT": ! output.AddItem(box.NextUniqueId); ! break; ! case "UIDVALIDITY": ! output.AddItem(box.UniqueIdValidity); ! break; ! case "UNSEEN": ! output.AddItem(box.MessageUnseenCount); ! break; ! default: ! mConnection.SendTaggedMessage("BAD Invalid request"); ! return; ! } ! } ! ! mConnection.SendUntaggedMessage(string.Format("STATUS {0} {1}", box.FullName, output.ToString())); mConnection.SendTaggedMessage("OK STATUS Completed"); } --- 32,36 ---- else { ! mConnection.SendUntaggedMessage(string.Format("STATUS {0} {1}", box.FullName, statusReq.GenerateStatusResponse(box))); mConnection.SendTaggedMessage("OK STATUS Completed"); } Index: StoreCommand.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/StoreCommand.cs,v retrieving revision 1.6 retrieving revision 1.7 diff -C2 -d -r1.6 -r1.7 *** StoreCommand.cs 5 Aug 2003 01:49:28 -0000 1.6 --- StoreCommand.cs 8 Aug 2003 22:39:26 -0000 1.7 *************** *** 1,154 **** using Common; using System; namespace Imap.Commands { - /// <summary> - /// Summary description for StoreCommand. - /// </summary> public class StoreCommand : ImapCommand { ! public StoreCommand(ImapServer svr) : base(svr, "STORE", ImapConnectionState.Selected, ArgumentType.SequenceSet, ArgumentType.AtomString, ArgumentType.FlagListFlag) { } ! override protected void InternalProcess() { ! SequenceSet msgSet = mParsedArguments[0] as SequenceSet; ! string dataItemName = mParsedArguments[1] as string; ! if(mParsedArguments[2] is string) ! { ! ParenthesizedList l = new ParenthesizedList(); ! l.AddItem(mParsedArguments[2]); ! mParsedArguments[2] = l; ! } ! ParenthesizedList list = mParsedArguments[2] as ParenthesizedList; ! ! bool silent = false; ! ! if(dataItemName.EndsWith(".SILENT")) ! { ! silent = true; ! dataItemName = dataItemName.Substring(0, dataItemName.Length - 7); ! } ! ! bool answered = false; ! bool flagged = false; ! bool deleted = false; ! bool seen = false; ! bool draft = false; ! ! for(int idx = 0; idx < list.Count; ++idx) ! { ! object o = list[idx].DataItem; ! if(o == null || !(o is string)) ! { ! mConnection.SendTaggedMessage("BAD data item is null or not a string"); ! return; ! } ! string flag = (o as string).ToLower(); ! switch(flag) ! { ! case "\\answered": ! answered = true; ! break; ! case "\\flagged": ! flagged = true; ! break; ! case "\\deleted": ! deleted = true; ! break; ! case "\\seen": ! seen = true; ! break; ! case "\\draft": ! draft = true; ! break; ! default: ! mConnection.SendTaggedMessage("BAD invalid flag"); ! return; ! } ! } ! bool addFlags = false; ! bool setFlags = false; ! bool subFlags = false; ! switch(dataItemName.ToUpper()) { ! case "FLAGS": ! setFlags = true; ! break; ! case "-FLAGS": ! subFlags = true; ! break; ! case "+FLAGS": ! addFlags = true; ! break; ! } ! SequenceSet.SequenceRange rng = msgSet.FirstRange; ! while(rng != null) ! { ! for(uint idx = rng.Low; idx <= rng.High; ++idx) { ! if(!mUidCalled && idx > mConnection.Mailbox.MessageCount) ! break; ! if(mUidCalled && idx > mConnection.Mailbox.NextUniqueId) ! break; ! Message msg = null; ! if(mUidCalled) ! msg = mConnection.Mailbox.Messages.ByUID(idx); ! else ! msg = mConnection.Mailbox.Messages.BySeq(idx); ! if(msg != null) { ! if(addFlags) ! { ! if(answered) ! msg.Answered = true; ! if(flagged) ! msg.Flagged = true; ! if(deleted) ! msg.Deleted = true; ! if(seen) ! msg.Seen = true; ! if(draft) ! msg.Draft = true; ! } ! else if(subFlags) ! { ! if(answered) ! msg.Answered = false; ! if(flagged) ! msg.Flagged = false; ! if(deleted) ! msg.Deleted = false; ! if(seen) ! msg.Seen = false; ! if(draft) ! msg.Draft = false; ! } ! else if(setFlags) ! { ! msg.Answered = answered; ! msg.Flagged = flagged; ! msg.Deleted = deleted; ! msg.Seen = seen; ! msg.Draft = draft; ! } ! msg.Save(); ! ! if(!silent) ! { ! // TODO: send fetch ! } } } - rng = rng.NextRange; } --- 1,61 ---- using Common; + using Imap.Types; + using System; namespace Imap.Commands { public class StoreCommand : ImapCommand { ! public StoreCommand(ImapServer svr) : base(svr, "STORE", ImapConnectionState.Selected) { } ! protected override void CreateArguments() { ! uint maxValue; ! if(mUidCalled) ! maxValue = mConnection.Mailbox.Messages.BySeq(mConnection.Mailbox.MessageCount).UniqueId; ! else ! maxValue = mConnection.Mailbox.MessageCount; ! mArguments = new BaseType[] { ! new SequenceSet(maxValue), ! new StoreRequest(), ! new Types.FlagList() ! }; ! } ! override protected void InternalProcess() ! { ! SequenceSet seqSet = mArguments[0] as SequenceSet; ! StoreRequest storeReq = mArguments[1] as StoreRequest; ! Types.FlagList flagLst = mArguments[2] as Types.FlagList; ! foreach(uint idx in seqSet) { ! Message msg = null; ! if(mUidCalled) ! msg = mConnection.Mailbox.Messages.ByUID(idx); ! else ! msg = mConnection.Mailbox.Messages.BySeq(idx); ! if(msg != null) { ! if(storeReq.Add) ! msg.Flags.AddFlags(flagLst); ! else if(storeReq.Remove) ! msg.Flags.RemoveFlags(flagLst); ! else if(storeReq.Replace) ! msg.Flags = flagLst; ! msg.Save(); ! if(!storeReq.Silent) { ! // TODO: send fetch } } } Index: SubscribeCommand.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/SubscribeCommand.cs,v retrieving revision 1.6 retrieving revision 1.7 diff -C2 -d -r1.6 -r1.7 *** SubscribeCommand.cs 5 Aug 2003 01:44:39 -0000 1.6 --- SubscribeCommand.cs 8 Aug 2003 22:39:26 -0000 1.7 *************** *** 1,4 **** --- 1,6 ---- using Common; + using Imap.Types; + using System; *************** *** 10,20 **** public class SubscribeCommand : ImapCommand { ! public SubscribeCommand(ImapServer svr) : base(svr, "SUBSCRIBE", ImapCommand.AuthSelectedState, ArgumentType.AString) { } override protected void InternalProcess() { ! string boxName = mParsedArguments[0] as String; Mailbox box = mConnection.User.Mailboxes.FindMailbox(boxName); --- 12,29 ---- public class SubscribeCommand : ImapCommand { ! public SubscribeCommand(ImapServer svr) : base(svr, "SUBSCRIBE", ImapCommand.AuthSelectedState) { } + protected override void CreateArguments() + { + mArguments = new BaseType[] { + new StringType(StringTypeEnum.AString) + }; + } + override protected void InternalProcess() { ! string boxName = mArguments[0] as StringType; Mailbox box = mConnection.User.Mailboxes.FindMailbox(boxName); Index: UnsubscribeCommand.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/UnsubscribeCommand.cs,v retrieving revision 1.6 retrieving revision 1.7 diff -C2 -d -r1.6 -r1.7 *** UnsubscribeCommand.cs 5 Aug 2003 01:44:39 -0000 1.6 --- UnsubscribeCommand.cs 8 Aug 2003 22:39:26 -0000 1.7 *************** *** 1,20 **** using Common; using System; namespace Imap.Commands { - /// <summary> - /// Summary description for UnsubscribeCommand. - /// </summary> public class UnsubscribeCommand : ImapCommand { ! public UnsubscribeCommand(ImapServer svr) : base(svr, "UNSUBSCRIBE", ImapCommand.AuthSelectedState, ArgumentType.AString) { } override protected void InternalProcess() { ! string boxName = mParsedArguments[0] as String; if(mConnection.User.SubscribedMailboxes.IndexOf(boxName) == -1) --- 1,26 ---- using Common; + using Imap.Types; + using System; namespace Imap.Commands { public class UnsubscribeCommand : ImapCommand { ! public UnsubscribeCommand(ImapServer svr) : base(svr, "UNSUBSCRIBE", ImapCommand.AuthSelectedState) ! { ! } ! ! protected override void CreateArguments() { + mArguments = new BaseType[] { + new StringType(StringTypeEnum.AString) + }; } override protected void InternalProcess() { ! string boxName = mArguments[0] as StringType; if(mConnection.User.SubscribedMailboxes.IndexOf(boxName) == -1) |
|
From: <ta...@us...> - 2003-08-08 22:39:30
|
Update of /cvsroot/csmaild/csmaild/src/TestClient In directory sc8-pr-cvs1:/tmp/cvs-serv18360/src/TestClient Modified Files: Main.cs Main.resx TestClient.csproj Log Message: Updated VS.NET 2002 project files to reflect currect VS.NET 2003 project Moved message flags into a FlagList class (this makes flag extensions easier to support) Modified the TestClient for easier debugging of the server Added BASE64 decoder to common library Moved argument parsing from ImapCommand into the newly created types (representing various arguments parsed from the client) Added support for AUTHENTICATE PLAIN Added TODO document in the docs !!Some stuff surely has been broken with all this code moving around, it'll compile and run, but some functionality hasn't been verified yet!! Index: Main.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/TestClient/Main.cs,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** Main.cs 29 Jul 2003 00:46:29 -0000 1.5 --- Main.cs 8 Aug 2003 22:39:26 -0000 1.6 *************** *** 15,24 **** { ClientHelper mHelper; ! int mCounter = 0; ! ArrayList mResponseCounts = new ArrayList(); private System.Windows.Forms.Button cmdLocalhost; - private System.Windows.Forms.Button cmdChegg; - private System.Windows.Forms.ListBox lstCommunicationLog; private System.Windows.Forms.TextBox txtAddCommands; private System.Windows.Forms.Button cmdAdd; --- 15,23 ---- { ClientHelper mHelper; ! ArrayList mCommandStartPosition = new ArrayList(); ! private TextReader mCompareFile; ! private string mTextCopy; private System.Windows.Forms.Button cmdLocalhost; private System.Windows.Forms.TextBox txtAddCommands; private System.Windows.Forms.Button cmdAdd; *************** *** 27,32 **** private System.Windows.Forms.Button cmdDelete; private System.Windows.Forms.NumericUpDown numPause; - private System.Windows.Forms.Label label1; private System.Windows.Forms.CheckedListBox lstCommandsToProcess; /// <summary> /// Required designer variable. --- 26,46 ---- private System.Windows.Forms.Button cmdDelete; private System.Windows.Forms.NumericUpDown numPause; private System.Windows.Forms.CheckedListBox lstCommandsToProcess; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.RichTextBox txtCommunicationLog; + private System.Windows.Forms.Button cmdEdit; + private System.Windows.Forms.ContextMenu ctxMenu; + private System.Windows.Forms.MenuItem mnuSaveAs; + private System.Windows.Forms.MenuItem mnuSaveAsText; + private System.Windows.Forms.MenuItem mnuSaveAsRichText; + private System.Windows.Forms.SaveFileDialog saveDlg; + private System.Windows.Forms.CheckBox chkWordWrap; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.CheckBox chkCompareToExpected; + private System.Windows.Forms.Label lblPause; + private System.Windows.Forms.Button cmdOther; /// <summary> /// Required designer variable. *************** *** 71,76 **** { this.cmdLocalhost = new System.Windows.Forms.Button(); - this.cmdChegg = new System.Windows.Forms.Button(); - this.lstCommunicationLog = new System.Windows.Forms.ListBox(); this.txtAddCommands = new System.Windows.Forms.TextBox(); this.cmdAdd = new System.Windows.Forms.Button(); --- 85,88 ---- *************** *** 79,85 **** this.cmdDelete = new System.Windows.Forms.Button(); this.numPause = new System.Windows.Forms.NumericUpDown(); ! this.label1 = new System.Windows.Forms.Label(); this.lstCommandsToProcess = new System.Windows.Forms.CheckedListBox(); ((System.ComponentModel.ISupportInitialize)(this.numPause)).BeginInit(); this.SuspendLayout(); // --- 91,113 ---- this.cmdDelete = new System.Windows.Forms.Button(); this.numPause = new System.Windows.Forms.NumericUpDown(); ! this.lblPause = new System.Windows.Forms.Label(); this.lstCommandsToProcess = new System.Windows.Forms.CheckedListBox(); + this.txtCommunicationLog = new System.Windows.Forms.RichTextBox(); + this.ctxMenu = new System.Windows.Forms.ContextMenu(); + this.mnuSaveAs = new System.Windows.Forms.MenuItem(); + this.mnuSaveAsText = new System.Windows.Forms.MenuItem(); + this.mnuSaveAsRichText = new System.Windows.Forms.MenuItem(); + this.panel1 = new System.Windows.Forms.Panel(); + this.cmdEdit = new System.Windows.Forms.Button(); + this.saveDlg = new System.Windows.Forms.SaveFileDialog(); + this.chkWordWrap = new System.Windows.Forms.CheckBox(); + this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.label5 = new System.Windows.Forms.Label(); + this.chkCompareToExpected = new System.Windows.Forms.CheckBox(); + this.cmdOther = new System.Windows.Forms.Button(); ((System.ComponentModel.ISupportInitialize)(this.numPause)).BeginInit(); + this.panel1.SuspendLayout(); this.SuspendLayout(); // *************** *** 94,125 **** this.cmdLocalhost.Click += new System.EventHandler(this.cmdLocalhost_Click); // - // cmdChegg - // - this.cmdChegg.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.cmdChegg.Location = new System.Drawing.Point(80, 8); - this.cmdChegg.Name = "cmdChegg"; - this.cmdChegg.Size = new System.Drawing.Size(64, 20); - this.cmdChegg.TabIndex = 2; - this.cmdChegg.Text = "Chegg"; - this.cmdChegg.Click += new System.EventHandler(this.cmdChegg_Click); - // - // lstCommunicationLog - // - this.lstCommunicationLog.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.lstCommunicationLog.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); - this.lstCommunicationLog.IntegralHeight = false; - this.lstCommunicationLog.ItemHeight = 16; - this.lstCommunicationLog.Location = new System.Drawing.Point(8, 40); - this.lstCommunicationLog.Name = "lstCommunicationLog"; - this.lstCommunicationLog.SelectionMode = System.Windows.Forms.SelectionMode.MultiSimple; - this.lstCommunicationLog.Size = new System.Drawing.Size(600, 432); - this.lstCommunicationLog.TabIndex = 3; - // // txtAddCommands // this.txtAddCommands.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; ! this.txtAddCommands.Location = new System.Drawing.Point(712, 456); this.txtAddCommands.Name = "txtAddCommands"; ! this.txtAddCommands.Size = new System.Drawing.Size(152, 20); this.txtAddCommands.TabIndex = 5; this.txtAddCommands.Text = ""; --- 122,132 ---- this.cmdLocalhost.Click += new System.EventHandler(this.cmdLocalhost_Click); // // txtAddCommands // + this.txtAddCommands.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.txtAddCommands.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; ! this.txtAddCommands.Location = new System.Drawing.Point(616, 456); this.txtAddCommands.Name = "txtAddCommands"; ! this.txtAddCommands.Size = new System.Drawing.Size(200, 20); this.txtAddCommands.TabIndex = 5; this.txtAddCommands.Text = ""; *************** *** 128,131 **** --- 135,139 ---- // cmdAdd // + this.cmdAdd.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.cmdAdd.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.cmdAdd.Location = new System.Drawing.Point(872, 456); *************** *** 178,213 **** this.numPause.Size = new System.Drawing.Size(40, 20); this.numPause.TabIndex = 10; - this.numPause.Value = new System.Decimal(new int[] { - 5, - 0, - 0, - 0}); // ! // label1 // ! this.label1.AutoSize = true; ! this.label1.Location = new System.Drawing.Point(224, 8); ! this.label1.Name = "label1"; ! this.label1.Size = new System.Drawing.Size(243, 16); ! this.label1.TabIndex = 11; ! this.label1.Text = "Pause between commands (tenths of a second)"; // // lstCommandsToProcess // this.lstCommandsToProcess.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.lstCommandsToProcess.Font = new System.Drawing.Font("Verdana", 8.25F); this.lstCommandsToProcess.Location = new System.Drawing.Point(616, 40); this.lstCommandsToProcess.Name = "lstCommandsToProcess"; ! this.lstCommandsToProcess.Size = new System.Drawing.Size(304, 386); this.lstCommandsToProcess.TabIndex = 12; this.lstCommandsToProcess.SelectedIndexChanged += new System.EventHandler(this.lstCommandsToProcess_SelectedIndexChanged); // // MainF // this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); this.ClientSize = new System.Drawing.Size(928, 485); ! this.Controls.Add(this.lstCommandsToProcess); ! this.Controls.Add(this.label1); this.Controls.Add(this.txtAddCommands); this.Controls.Add(this.numPause); this.Controls.Add(this.cmdDelete); --- 186,370 ---- this.numPause.Size = new System.Drawing.Size(40, 20); this.numPause.TabIndex = 10; // ! // lblPause // ! this.lblPause.AutoSize = true; ! this.lblPause.Location = new System.Drawing.Point(224, 8); ! this.lblPause.Name = "lblPause"; ! this.lblPause.Size = new System.Drawing.Size(217, 16); ! this.lblPause.TabIndex = 11; ! this.lblPause.Text = "Pause before sending (tenths of a second)"; // // lstCommandsToProcess // + this.lstCommandsToProcess.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Right))); this.lstCommandsToProcess.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.lstCommandsToProcess.Font = new System.Drawing.Font("Verdana", 8.25F); this.lstCommandsToProcess.Location = new System.Drawing.Point(616, 40); this.lstCommandsToProcess.Name = "lstCommandsToProcess"; ! this.lstCommandsToProcess.Size = new System.Drawing.Size(304, 402); this.lstCommandsToProcess.TabIndex = 12; this.lstCommandsToProcess.SelectedIndexChanged += new System.EventHandler(this.lstCommandsToProcess_SelectedIndexChanged); // + // txtCommunicationLog + // + this.txtCommunicationLog.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.txtCommunicationLog.ContextMenu = this.ctxMenu; + this.txtCommunicationLog.Dock = System.Windows.Forms.DockStyle.Fill; + this.txtCommunicationLog.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.txtCommunicationLog.HideSelection = false; + this.txtCommunicationLog.Location = new System.Drawing.Point(0, 0); + this.txtCommunicationLog.Name = "txtCommunicationLog"; + this.txtCommunicationLog.ReadOnly = true; + this.txtCommunicationLog.Size = new System.Drawing.Size(598, 414); + this.txtCommunicationLog.TabIndex = 13; + this.txtCommunicationLog.Text = ""; + // + // ctxMenu + // + this.ctxMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.mnuSaveAs}); + // + // mnuSaveAs + // + this.mnuSaveAs.Index = 0; + this.mnuSaveAs.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.mnuSaveAsText, + this.mnuSaveAsRichText}); + this.mnuSaveAs.Text = "Save As..."; + // + // mnuSaveAsText + // + this.mnuSaveAsText.Index = 0; + this.mnuSaveAsText.Text = "Text"; + this.mnuSaveAsText.Click += new System.EventHandler(this.mnuSaveAsText_Click); + // + // mnuSaveAsRichText + // + this.mnuSaveAsRichText.Index = 1; + this.mnuSaveAsRichText.Text = "Rich Text"; + this.mnuSaveAsRichText.Click += new System.EventHandler(this.mnuSaveAsRichText_Click); + // + // panel1 + // + this.panel1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.panel1.Controls.Add(this.txtCommunicationLog); + this.panel1.Location = new System.Drawing.Point(8, 40); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(600, 416); + this.panel1.TabIndex = 14; + // + // cmdEdit + // + this.cmdEdit.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cmdEdit.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.cmdEdit.Location = new System.Drawing.Point(820, 456); + this.cmdEdit.Name = "cmdEdit"; + this.cmdEdit.Size = new System.Drawing.Size(48, 20); + this.cmdEdit.TabIndex = 15; + this.cmdEdit.Text = "Edit"; + // + // chkWordWrap + // + this.chkWordWrap.Checked = true; + this.chkWordWrap.CheckState = System.Windows.Forms.CheckState.Checked; + this.chkWordWrap.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.chkWordWrap.Location = new System.Drawing.Point(472, 8); + this.chkWordWrap.Name = "chkWordWrap"; + this.chkWordWrap.Size = new System.Drawing.Size(80, 16); + this.chkWordWrap.TabIndex = 16; + this.chkWordWrap.Text = "Word Wrap"; + this.chkWordWrap.CheckedChanged += new System.EventHandler(this.chkWordWrap_CheckedChanged); + // + // label2 + // + this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.label2.AutoSize = true; + this.label2.BackColor = System.Drawing.Color.White; + this.label2.ForeColor = System.Drawing.Color.ForestGreen; + this.label2.Location = new System.Drawing.Point(8, 464); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(38, 16); + this.label2.TabIndex = 17; + this.label2.Text = "Server"; + this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // label3 + // + this.label3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.label3.AutoSize = true; + this.label3.BackColor = System.Drawing.Color.White; + this.label3.ForeColor = System.Drawing.Color.Gold; + this.label3.Location = new System.Drawing.Point(279, 464); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(117, 16); + this.label3.TabIndex = 18; + this.label3.Text = "Line is wrong, this isn\'t"; + this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // label4 + // + this.label4.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.label4.AutoSize = true; + this.label4.BackColor = System.Drawing.Color.White; + this.label4.ForeColor = System.Drawing.Color.Blue; + this.label4.Location = new System.Drawing.Point(146, 464); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(33, 16); + this.label4.TabIndex = 19; + this.label4.Text = "Client"; + this.label4.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // label5 + // + this.label5.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.label5.AutoSize = true; + this.label5.BackColor = System.Drawing.Color.White; + this.label5.ForeColor = System.Drawing.Color.Red; + this.label5.Location = new System.Drawing.Point(496, 464); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(106, 16); + this.label5.TabIndex = 20; + this.label5.Text = "Line is wrong, this is"; + this.label5.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // chkCompareToExpected + // + this.chkCompareToExpected.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.chkCompareToExpected.Location = new System.Drawing.Point(568, 8); + this.chkCompareToExpected.Name = "chkCompareToExpected"; + this.chkCompareToExpected.Size = new System.Drawing.Size(136, 16); + this.chkCompareToExpected.TabIndex = 21; + this.chkCompareToExpected.Text = "Compare to Expected"; + // + // cmdOther + // + this.cmdOther.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.cmdOther.Location = new System.Drawing.Point(80, 8); + this.cmdOther.Name = "cmdOther"; + this.cmdOther.Size = new System.Drawing.Size(80, 20); + this.cmdOther.TabIndex = 2; + this.cmdOther.Text = "Test Other..."; + this.cmdOther.Click += new System.EventHandler(this.cmdChegg_Click); + // // MainF // this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); this.ClientSize = new System.Drawing.Size(928, 485); ! this.Controls.Add(this.chkCompareToExpected); ! this.Controls.Add(this.label5); ! this.Controls.Add(this.label4); ! this.Controls.Add(this.label3); ! this.Controls.Add(this.label2); ! this.Controls.Add(this.lblPause); this.Controls.Add(this.txtAddCommands); + this.Controls.Add(this.chkWordWrap); + this.Controls.Add(this.cmdEdit); + this.Controls.Add(this.panel1); + this.Controls.Add(this.lstCommandsToProcess); this.Controls.Add(this.numPause); this.Controls.Add(this.cmdDelete); *************** *** 215,220 **** this.Controls.Add(this.cmdMoveUp); this.Controls.Add(this.cmdAdd); ! this.Controls.Add(this.lstCommunicationLog); ! this.Controls.Add(this.cmdChegg); this.Controls.Add(this.cmdLocalhost); this.Name = "MainF"; --- 372,376 ---- this.Controls.Add(this.cmdMoveUp); this.Controls.Add(this.cmdAdd); ! this.Controls.Add(this.cmdOther); this.Controls.Add(this.cmdLocalhost); this.Name = "MainF"; *************** *** 222,225 **** --- 378,382 ---- this.Load += new System.EventHandler(this.MainF_Load); ((System.ComponentModel.ISupportInitialize)(this.numPause)).EndInit(); + this.panel1.ResumeLayout(false); this.ResumeLayout(false); *************** *** 227,238 **** #endregion ! private void SendLine(string msg) { ! mHelper.Send(msg + "\r\n"); ! lstCommunicationLog.Items.Add(mCounter++.ToString().PadLeft(4, ' ') + " -> " + msg); ! lstCommunicationLog.SelectedIndex = lstCommunicationLog.Items.Count-1; ! mResponseCounts[mResponseCounts.Count-1] = (int)mResponseCounts[mResponseCounts.Count-1]+1; Application.DoEvents(); } --- 384,415 ---- #endregion ! private void AddTextLine(string msg, Color clr) { ! string compareLine = mCompareFile.ReadLine(); ! txtCommunicationLog.SelectionHangingIndent = 35; ! txtCommunicationLog.SelectionColor = clr; ! txtCommunicationLog.SelectedText = msg + "\r\n"; + if(chkCompareToExpected.Checked && compareLine != null && compareLine != msg) + { + int idx = 0; + for(; idx < Math.Min(compareLine.Length, msg.Length); ++idx) + if(compareLine[idx] != msg[idx]) + break; + + txtCommunicationLog.SelectionHangingIndent = 35; + txtCommunicationLog.SelectionColor = Color.Gold; + txtCommunicationLog.SelectedText = compareLine.Substring(0, idx); + txtCommunicationLog.SelectionHangingIndent = 35; + txtCommunicationLog.SelectionColor = Color.Red; + txtCommunicationLog.SelectedText = compareLine.Substring(idx) + "\r\n"; + } + } + + private void SendLine(string msg) + { + mHelper.Send(msg + "\r\n"); + AddTextLine(msg, Color.Blue); Application.DoEvents(); } *************** *** 242,250 **** string line = mHelper.ReceiveLine(); if(line != string.Empty) ! { ! lstCommunicationLog.Items.Add(mCounter++.ToString().PadLeft(4, ' ') + " <- " + line); ! lstCommunicationLog.SelectedIndex = lstCommunicationLog.Items.Count-1; ! mResponseCounts[mResponseCounts.Count-1] = (int)mResponseCounts[mResponseCounts.Count-1]+1; ! } Application.DoEvents(); return line; --- 419,423 ---- string line = mHelper.ReceiveLine(); if(line != string.Empty) ! AddTextLine(line, Color.ForestGreen); Application.DoEvents(); return line; *************** *** 279,286 **** private void Go() { ! lstCommunicationLog.Items.Clear(); ! mResponseCounts.Clear(); ! mResponseCounts.Add(0); cmdAdd.Enabled = false; --- 452,461 ---- private void Go() { ! txtCommunicationLog.ResetText(); ! txtCommunicationLog.Enabled = false; ! mCompareFile = new StreamReader("..\\ExpectedOutput.txt"); ! ! mCommandStartPosition.Clear(); cmdAdd.Enabled = false; *************** *** 290,294 **** lstCommandsToProcess.Enabled = false; cmdLocalhost.Enabled = false; ! cmdChegg.Enabled = false; ReadLine(); // read welcome msg --- 465,469 ---- lstCommandsToProcess.Enabled = false; cmdLocalhost.Enabled = false; ! cmdOther.Enabled = false; ReadLine(); // read welcome msg *************** *** 296,300 **** for(int idx = 0; idx < lstCommandsToProcess.CheckedIndices.Count; ++idx) { ! mResponseCounts.Add(0); lstCommandsToProcess.SelectedIndex = lstCommandsToProcess.CheckedIndices[idx]; SendReceiveCommand(lstCommandsToProcess.SelectedItem as string); --- 471,475 ---- for(int idx = 0; idx < lstCommandsToProcess.CheckedIndices.Count; ++idx) { ! mCommandStartPosition.Add(txtCommunicationLog.TextLength); lstCommandsToProcess.SelectedIndex = lstCommandsToProcess.CheckedIndices[idx]; SendReceiveCommand(lstCommandsToProcess.SelectedItem as string); *************** *** 302,305 **** --- 477,481 ---- } + txtCommunicationLog.Enabled = true; cmdAdd.Enabled = true; cmdDelete.Enabled = true; *************** *** 308,312 **** lstCommandsToProcess.Enabled = true; cmdLocalhost.Enabled = true; ! cmdChegg.Enabled = true; } --- 484,488 ---- lstCommandsToProcess.Enabled = true; cmdLocalhost.Enabled = true; ! cmdOther.Enabled = true; } *************** *** 355,373 **** else { ! lstCommunicationLog.SelectedIndex = -1; ! int idx = lstCommandsToProcess.CheckedIndices.IndexOf(lstCommandsToProcess.SelectedIndex); ! if(mResponseCounts.Count >= idx+2) { ! int pos = 0; ! for(int iidx = idx; iidx >= 0; --iidx) ! pos += (int)mResponseCounts[iidx]; ! int cnt = (int)mResponseCounts[idx+1]-1; ! for(; cnt >= 0; --cnt) ! if(pos+cnt < lstCommunicationLog.Items.Count) ! lstCommunicationLog.SetSelected(pos+cnt, true); } - cmdMoveUp.Enabled = (lstCommandsToProcess.SelectedIndex != 0); - cmdMoveDown.Enabled = (lstCommandsToProcess.SelectedIndex != lstCommandsToProcess.Items.Count-1); - cmdDelete.Enabled = true; } } --- 531,574 ---- else { ! if(lstCommandsToProcess.Enabled) { ! int idx = lstCommandsToProcess.CheckedIndices.IndexOf(lstCommandsToProcess.SelectedIndex); ! ! if(idx >= 0 && idx < mCommandStartPosition.Count) ! { ! ! if(mTextCopy == null) ! mTextCopy = txtCommunicationLog.Rtf; ! // else ! // txtCommunicationLog.Rtf = mTextCopy; ! ! int start = (int)mCommandStartPosition[idx]; ! int end = (idx+1 < mCommandStartPosition.Count ? (int)mCommandStartPosition[idx+1] : txtCommunicationLog.TextLength); ! ! // first set the font color of everything else to gray ! // txtCommunicationLog.SelectionStart = 0; ! // txtCommunicationLog.SelectionLength = start; ! // txtCommunicationLog.SelectionColor = Color.Gray; ! // txtCommunicationLog.SelectionStart = end; ! // txtCommunicationLog.SelectionLength = txtCommunicationLog.TextLength - end; ! // txtCommunicationLog.SelectionColor = Color.Gray; ! ! // now set the font to the select item to not italic and bold ! txtCommunicationLog.SelectionStart = start; ! txtCommunicationLog.SelectionLength = 0; ! txtCommunicationLog.ScrollToCaret(); ! if(idx+1 < mCommandStartPosition.Count) ! txtCommunicationLog.SelectionLength = (int)mCommandStartPosition[idx+1] - (int)mCommandStartPosition[idx]; ! else ! txtCommunicationLog.SelectionLength = txtCommunicationLog.TextLength - (int)mCommandStartPosition[idx]; ! // txtCommunicationLog.SelectionFont = new Font(txtCommunicationLog.Font, FontStyle.Bold); ! ! // txtCommunicationLog.SelectionLength = 0; ! } ! ! cmdMoveUp.Enabled = (lstCommandsToProcess.SelectedIndex != 0); ! cmdMoveDown.Enabled = (lstCommandsToProcess.SelectedIndex != lstCommandsToProcess.Items.Count-1); ! cmdDelete.Enabled = true; } } } *************** *** 435,438 **** --- 636,658 ---- ds.Dispose(); + } + + private void mnuSaveAsText_Click(object sender, System.EventArgs e) + { + saveDlg.Filter = "Text Files (*.txt)|*.txt"; + if(saveDlg.ShowDialog() == DialogResult.OK) + txtCommunicationLog.SaveFile(saveDlg.FileName, RichTextBoxStreamType.PlainText); + } + + private void mnuSaveAsRichText_Click(object sender, System.EventArgs e) + { + saveDlg.Filter = "Rich Text (*.rtf)|*.rtf"; + if(saveDlg.ShowDialog() == DialogResult.OK) + txtCommunicationLog.SaveFile(saveDlg.FileName, RichTextBoxStreamType.RichText); + } + + private void chkWordWrap_CheckedChanged(object sender, System.EventArgs e) + { + txtCommunicationLog.WordWrap = chkWordWrap.Checked; } } Index: Main.resx =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/TestClient/Main.resx,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** Main.resx 29 Jul 2003 00:46:29 -0000 1.5 --- Main.resx 8 Aug 2003 22:39:27 -0000 1.6 *************** *** 107,128 **** <value>Private</value> </data> - <data name="cmdChegg.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> - <value>False</value> - </data> - <data name="cmdChegg.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> - <value>Private</value> - </data> - <data name="cmdChegg.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> - <value>Private</value> - </data> - <data name="lstCommunicationLog.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> - <value>Private</value> - </data> - <data name="lstCommunicationLog.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> - <value>False</value> - </data> - <data name="lstCommunicationLog.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> - <value>Private</value> - </data> <data name="txtAddCommands.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>Private</value> --- 107,110 ---- *************** *** 179,189 **** <value>Private</value> </data> ! <data name="label1.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>False</value> </data> ! <data name="label1.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>Private</value> </data> ! <data name="label1.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>Private</value> </data> --- 161,171 ---- <value>Private</value> </data> ! <data name="lblPause.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>False</value> </data> ! <data name="lblPause.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>Private</value> </data> ! <data name="lblPause.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>Private</value> </data> *************** *** 197,200 **** --- 179,317 ---- <value>Private</value> </data> + <data name="txtCommunicationLog.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="txtCommunicationLog.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="txtCommunicationLog.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="ctxMenu.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="ctxMenu.Location" type="System.Drawing.Point, System.Drawing, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> + <value>17, 17</value> + </data> + <data name="ctxMenu.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="mnuSaveAs.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="mnuSaveAs.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="mnuSaveAsText.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="mnuSaveAsText.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="mnuSaveAsRichText.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="mnuSaveAsRichText.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="panel1.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="panel1.SnapToGrid" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>True</value> + </data> + <data name="panel1.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="panel1.GridSize" type="System.Drawing.Size, System.Drawing, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> + <value>8, 8</value> + </data> + <data name="panel1.DrawGrid" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>True</value> + </data> + <data name="panel1.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="cmdEdit.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="cmdEdit.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="cmdEdit.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="saveDlg.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="saveDlg.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="saveDlg.Location" type="System.Drawing.Point, System.Drawing, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> + <value>110, 17</value> + </data> + <data name="chkWordWrap.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="chkWordWrap.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="chkWordWrap.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="label2.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="label2.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="label2.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="label3.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="label3.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="label3.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="label4.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="label4.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="label4.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="label5.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="label5.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="label5.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="chkCompareToExpected.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="chkCompareToExpected.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="chkCompareToExpected.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="cmdOther.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="cmdOther.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="cmdOther.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> <data name="$this.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>False</value> *************** *** 206,209 **** --- 323,329 ---- <value>False</value> </data> + <data name="$this.Name"> + <value>MainF</value> + </data> <data name="$this.Localizable" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>False</value> *************** *** 215,223 **** <value>True</value> </data> - <data name="$this.Name"> - <value>MainF</value> - </data> <data name="$this.TrayHeight" type="System.Int32, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> ! <value>80</value> </data> <data name="$this.SnapToGrid" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> --- 335,340 ---- <value>True</value> </data> <data name="$this.TrayHeight" type="System.Int32, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> ! <value>52</value> </data> <data name="$this.SnapToGrid" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> Index: TestClient.csproj =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/TestClient/TestClient.csproj,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** TestClient.csproj 24 Jul 2003 04:32:14 -0000 1.1 --- TestClient.csproj 8 Aug 2003 22:39:27 -0000 1.2 *************** *** 87,91 **** <Reference Name = "System.XML" ! AssemblyName = "System.XML" HintPath = "..\..\..\..\..\..\WINDOWS\Microsoft.NET\Framework\v1.1.4322\System.XML.dll" /> --- 87,91 ---- <Reference Name = "System.XML" ! AssemblyName = "System.Xml" HintPath = "..\..\..\..\..\..\WINDOWS\Microsoft.NET\Framework\v1.1.4322\System.XML.dll" /> *************** *** 100,103 **** --- 100,104 ---- <File RelPath = "AssemblyInfo.cs" + SubType = "Code" BuildAction = "Compile" /> *************** *** 116,119 **** --- 117,124 ---- DependentUpon = "Main.cs" BuildAction = "EmbeddedResource" + /> + <File + RelPath = "bin\Commands.xml" + BuildAction = "Content" /> </Include> |
|
From: <ta...@us...> - 2003-08-08 22:39:30
|
Update of /cvsroot/csmaild/csmaild/src/TestClient/bin In directory sc8-pr-cvs1:/tmp/cvs-serv18360/src/TestClient/bin Modified Files: Commands.xml Log Message: Updated VS.NET 2002 project files to reflect currect VS.NET 2003 project Moved message flags into a FlagList class (this makes flag extensions easier to support) Modified the TestClient for easier debugging of the server Added BASE64 decoder to common library Moved argument parsing from ImapCommand into the newly created types (representing various arguments parsed from the client) Added support for AUTHENTICATE PLAIN Added TODO document in the docs !!Some stuff surely has been broken with all this code moving around, it'll compile and run, but some functionality hasn't been verified yet!! Index: Commands.xml =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/TestClient/bin/Commands.xml,v retrieving revision 1.4 retrieving revision 1.5 diff -C2 -d -r1.4 -r1.5 *** Commands.xml 26 Jul 2003 23:55:47 -0000 1.4 --- Commands.xml 8 Aug 2003 22:39:27 -0000 1.5 *************** *** 6,38 **** </Commands> <Commands> ! <Command>2 LOGIN csmaild_test csmaild_test</Command> <Process>False</Process> </Commands> <Commands> ! <Command>2 LOGIN "csmaild_test" "csmaild_test"</Command> ! <Process>True</Process> </Commands> <Commands> ! <Command>2 LOGIN {12}</Command> <Process>False</Process> </Commands> <Commands> ! <Command>csmaild_test {12}</Command> <Process>False</Process> </Commands> <Commands> ! <Command>csmaild_test</Command> <Process>False</Process> </Commands> <Commands> ! <Command>3 SELECT INBOX</Command> <Process>True</Process> </Commands> <Commands> ! <Command>4 FETCH 1 (BODY[HEADER.FIELDS (To From)])</Command> <Process>False</Process> </Commands> <Commands> ! <Command>5 FETCH 1 (BODY[]<0.1500>)</Command> <Process>True</Process> </Commands> --- 6,62 ---- </Commands> <Commands> ! <Command>2 LOGIN "csmaild_test" "csmaild_test"</Command> ! <Process>True</Process> ! </Commands> ! <Commands> ! <Command>2 AUTHENTICATE PLAIN</Command> <Process>False</Process> </Commands> <Commands> ! <Command>AGNzbWFpbGRfdGVzdABjc21haWxkX3Rlc3Q=</Command> ! <Process>False</Process> </Commands> <Commands> ! <Command>A LSUB "" "*"</Command> <Process>False</Process> </Commands> <Commands> ! <Command>A SUBSCRIBE "INBOX/Test"</Command> <Process>False</Process> </Commands> <Commands> ! <Command>A LSUB "" "*"</Command> <Process>False</Process> </Commands> <Commands> ! <Command>A LSUB "" "%"</Command> ! <Process>False</Process> ! </Commands> ! <Commands> ! <Command>B SELECT Drafts</Command> <Process>True</Process> </Commands> <Commands> ! <Command>3 UID fetch 1:* (FLAGS)</Command> ! <Process>True</Process> ! </Commands> ! <Commands> ! <Command>C UID STORE 1:2,4 +FLAGS.SILENT (\Deleted \Seen)</Command> <Process>False</Process> </Commands> <Commands> ! <Command>B SELECT "INBOX"</Command> ! <Process>False</Process> ! </Commands> ! <Commands> ! <Command>C UID FETCH 1:* (BODY.PEEK[HEADER.FIELDS (References X-Ref X-Priority X-MSMail-Priority X-MSOESRec Newsgroups)] ENVELOPE RFC822.SIZE UID FLAGS INTERNALDATE)</Command> ! <Process>False</Process> ! </Commands> ! <Commands> ! <Command>A STATUS "INBOX" (MESSAGES UNSEEN)</Command> ! <Process>False</Process> ! </Commands> ! <Commands> ! <Command>D LOGOUT</Command> <Process>True</Process> </Commands> |
Update of /cvsroot/csmaild/csmaild/src/Imap/Types In directory sc8-pr-cvs1:/tmp/cvs-serv18360/src/Imap/Types Added Files: BaseType.cs FetchRequest.cs FlagList.cs SearchRequest.cs SequenceSet.cs StatusRequest.cs StoreRequest.cs StringType.cs Log Message: Updated VS.NET 2002 project files to reflect currect VS.NET 2003 project Moved message flags into a FlagList class (this makes flag extensions easier to support) Modified the TestClient for easier debugging of the server Added BASE64 decoder to common library Moved argument parsing from ImapCommand into the newly created types (representing various arguments parsed from the client) Added support for AUTHENTICATE PLAIN Added TODO document in the docs !!Some stuff surely has been broken with all this code moving around, it'll compile and run, but some functionality hasn't been verified yet!! --- NEW FILE: BaseType.cs --- using System; using System.IO; using System.Text; namespace Imap.Types { public abstract class BaseType { public abstract bool Read(TextReader rdr); protected string ReadTil(TextReader rdr, char tilChar, bool includeAndPass) { return ReadTil(rdr, new char[]{tilChar}, includeAndPass); } protected string ReadTil(TextReader rdr, char[] tilChars, bool includeAndPass) { StringBuilder str = new StringBuilder(); while(true) { int intVal = rdr.Peek(); if(intVal == -1) break; char ch = Convert.ToChar(intVal); for(int idx = 0; idx < tilChars.Length; ++idx) { if(ch == tilChars[idx]) { if(includeAndPass) { str.Append(ch); rdr.Read(); } return str.ToString(); } } str.Append(ch); rdr.Read(); } return str.ToString(); } } } --- NEW FILE: FetchRequest.cs --- using System; using System.Collections; using System.Collections.Specialized; using System.IO; namespace Imap.Types { public class FetchRequest : BaseType { #region BodySection public class BodySection { private bool mOffsetLength; private uint mOffset; private uint mLength; private bool mHeader; private bool mText; private StringCollection mHeaderFields = new StringCollection(); private StringCollection mHeaderFieldsNot = new StringCollection(); private ArrayList mParts = new ArrayList(); private bool mPartHeader; private bool mPartMime; private bool mPartText; private StringCollection mPartHeaderFields = new StringCollection(); private StringCollection mPartHeaderFieldsNot = new StringCollection(); public bool OffsetLength { get { return mOffsetLength; } set { mOffsetLength = value; } } public uint Offset { get { return mOffset; } set { mOffset = value; } } public uint Length { get { return mLength; } set { mLength = value; } } public bool Header { get { return mHeader; } set { mHeader = value; } } public bool Text { get { return mText; } set { mText = value; } } public StringCollection HeaderFields { get { return mHeaderFields; } } public StringCollection HeaderFieldsNot { get { return mHeaderFieldsNot; } } public ArrayList Parts { get { return mParts; } } public bool PartHeader { get { return mPartHeader; } set { mPartHeader = value; } } public bool PartMime { get { return mPartMime; } set { mPartMime = value; } } public bool PartText { get { return mPartText; } set { mPartText = value; } } public StringCollection PartHeaderFields { get { return mPartHeaderFields; } } public StringCollection PartHeaderFieldsNot { get { return mPartHeaderFieldsNot; } } } #endregion private bool mBody; private bool mPeeking; private bool mNotPeeking; private bool mBodyStructure; private bool mEnvelope; private bool mFlags; private bool mInternalDate; private bool mRfc822; private bool mRfc822Header; private bool mRfc822Size; private bool mRfc822Text; private bool mUid; private ArrayList mBodySections = new ArrayList(); public bool Body { get { return mBody; } set { mBody = value; } } public bool Peeking { get { return mPeeking; } set { if(!value) { mNotPeeking = true; mPeeking = false; } else if(value && !mNotPeeking) mPeeking = true; } } public bool BodyStructure { get { return mBodyStructure; } set { mBodyStructure = value; } } public bool Envelope { get { return mEnvelope; } set { mEnvelope = value; } } public bool Flags { get { return mFlags; } set { mFlags = value; } } public bool InternalDate { get { return mInternalDate; } set { mInternalDate = value; } } public bool Rfc822 { get { return mRfc822; } set { mRfc822 = value; } } public bool Rfc822Header { get { return mRfc822Header; } set { mRfc822Header = value; } } public bool Rfc822Size { get { return mRfc822Size; } set { mRfc822Size = value; } } public bool Rfc822Text { get { return mRfc822Text; } set { mRfc822Text = value; } } public bool Uid { get { return mUid; } set { mUid = value; } } public ArrayList BodySections { get { return mBodySections; } } public void SetAllMacro() { mFlags = true; mInternalDate = true; mRfc822Size = true; mEnvelope = true; } public void SetFullMacro() { mFlags = true; mInternalDate = true; mRfc822Size = true; mEnvelope = true; mBody = true; } public void SetFastMacro() { mFlags = true; mInternalDate = true; mRfc822Size = true; } public override bool Read(TextReader rdr) { if(Convert.ToChar(rdr.Peek()) == '(') { rdr.Read(); while(true) { if(!ReadItem(rdr)) return false; int intVal = rdr.Read(); if(intVal == -1) return false; char ch = Convert.ToChar(intVal); if(ch == ' ') continue; else if(ch == ')') break; else return false; } rdr.Read(); } else ReadItem(rdr); return true; } private bool ReadItem(TextReader rdr) { string text = ReadTil(rdr, new char[]{' ', ')'}, false).ToUpper(); if(text.StartsWith("BODY[")) { Peeking = false; return ReadBodyStructureItem(text.Substring(5), rdr); } else if(text.StartsWith("BODY.PEEK[")) { Peeking = true; return ReadBodyStructureItem(text.Substring(10), rdr); } switch(text) { case "ALL": SetAllMacro(); break; case "FULL": SetFullMacro(); break; case "FAST": SetFastMacro(); break; case "ENVELOPE": mEnvelope = true; break; case "FLAGS": mFlags = true; break; case "INTERNALDATE": mInternalDate = true; break; case "RFC822.HEADER": mRfc822Header = true; break; case "RFC822.SIZE": mRfc822Size = true; break; case "RFC822.TEXT": mRfc822Text = true; break; case "BODY": mBody = true; break; case "BODYSTRUCTURE": mBodyStructure = true; break; case "UID": mUid = true; break; case "RFC822": mRfc822 = true; break; default: return false; } return true; } private bool ReadBodyStructureItem(string left, TextReader rdr) { /* fetch-att = "BODY" section ["<" number "." nz-number ">"] / "BODY.PEEK" section ["<" number "." nz-number ">"] section = "[" [section-spec] "]" section-spec = section-msgtext / (section-part ["." section-text]) section-msgtext = "HEADER" / "HEADER.FIELDS" [".NOT"] SP header-list / "TEXT" section-text = section-msgtext / "MIME" ; text other than actual body part (headers, etc.) section-part = nz-number *("." nz-number) */ return false; } } } --- NEW FILE: FlagList.cs --- using System; using System.Collections.Specialized; using System.IO; namespace Imap.Types { public class FlagList : BaseType { private Common.FlagList mFlags = new Common.FlagList(); public Common.FlagList Flags { get { return mFlags; } } public static implicit operator Common.FlagList(FlagList m) { return m.mFlags; } public override bool Read(TextReader rdr) { if(Convert.ToChar(rdr.Peek()) == '(') { rdr.Read(); while(true) { if(!ReadFlag(rdr)) return false; int intVal = rdr.Read(); if(intVal == -1) return false; char ch = Convert.ToChar(intVal); if(ch == ' ') continue; else if(ch == ')') break; else return false; } rdr.Read(); } else ReadFlag(rdr); return true; } private bool ReadFlag(TextReader rdr) { int intVal = rdr.Peek(); if(intVal == -1) return false; char ch = Convert.ToChar(intVal); if(ch == '\\') // system { rdr.Read(); string str = StringType.ReadWhileGood(rdr, Commands.CommandPart.AtomChar); switch(str.ToLower()) { case "deleted": mFlags.Deleted = true; break; case "flagged": mFlags.Flagged = true; break; case "answered": mFlags.Answered = true; break; case "seen": mFlags.Seen = true; break; case "draft": mFlags.Draft = true; break; default: return false; } } else // extension { string str = StringType.ReadWhileGood(rdr, Commands.CommandPart.AtomChar); if(str == string.Empty) return false; mFlags.Keywords.Add(str); } return true; } } } --- NEW FILE: SearchRequest.cs --- using System; using System.Collections; namespace Imap.Types { public class SearchRequest : BaseType { private ArrayList mExpressions = new ArrayList(); public override bool Read(System.IO.TextReader rdr) { return false; } } public abstract class SearchExpression { } public abstract class SearchStringExpression : SearchExpression { private string mSearchString; public string SearchString { get { return mSearchString; } set { mSearchString = value; } } } public abstract class SearchDateExpression : SearchExpression { private DateTime mSearchDate; public DateTime SearchDate { get { return mSearchDate.Date; } set { mSearchDate = value; } } } public abstract class SearchNumberExpression : SearchExpression { private int mSearchNumber; public int SearchNumber { get { return mSearchNumber; } set { mSearchNumber = value; } } } public abstract class SearchSequenceSetExpression : SearchExpression { private SequenceSet mSearchSet; public SequenceSet SearchSequenceSet { get { return mSearchSet; } set { mSearchSet = value; } } } public class OrExpression : SearchExpression { private SearchExpression mLeftExpression; private SearchExpression mRightExpression; public SearchExpression LeftExpression { get { return mLeftExpression; } set { mLeftExpression = value; } } public SearchExpression RightExpression { get { return mRightExpression; } set { mRightExpression = value; } } } public class NotExpression : SearchExpression { private SearchExpression mExpression; public SearchExpression Expression { get { return mExpression; } set { mExpression = value; } } } public class AllExpression : SearchExpression { } public class AnsweredExpression : SearchExpression { } public class BccExpression : SearchStringExpression { } public class BeforeExpression : SearchDateExpression { } public class BodyExpression : SearchStringExpression { } public class CcExpression : SearchStringExpression { } public class DeletedExpression : SearchExpression { } public class FlaggedExpression : SearchExpression { } public class FromExpression : SearchStringExpression { } public class KeywordExpression : SearchStringExpression { } public class NewExpression : SearchExpression { } public class OldExpression : SearchExpression { } public class OnExpression : SearchDateExpression { } public class RecentExpression : SearchExpression { } public class SeenExpression : SearchExpression { } public class SequenceNumberExpression : SearchSequenceSetExpression { } public class SinceExpression : SearchDateExpression { } public class SubjectExpression : SearchStringExpression { } public class TextExpression : SearchStringExpression { } public class ToExpression : SearchStringExpression { } public class UnansweredExpression : SearchExpression { } public class UndeletedExpression : SearchExpression { } public class UnflaggedExpression : SearchExpression { } public class UnkeywordExpression : SearchStringExpression { } public class UnseenExpression : SearchExpression { } public class DraftExpression : SearchExpression { } public class HeaderExpression : SearchStringExpression { private string mFieldName; public string FieldName { get { return mFieldName; } set { mFieldName = value; } } } public class LargerExpression : SearchNumberExpression { } public class SentBeforeExpression : SearchDateExpression { } public class SentOnExpression : SearchDateExpression { } public class SentSinceExpression : SearchDateExpression { } public class SmallerExpression : SearchNumberExpression { } public class UidExpression : SearchSequenceSetExpression { } public class UndraftExpression : SearchExpression { } } --- NEW FILE: SequenceSet.cs --- using System; using System.Collections; using System.IO; using System.Text; using System.Text.RegularExpressions; namespace Imap.Types { public class SequenceSet : BaseType, IEnumerable { #region SequenceRange public class SequenceRange : IComparable { private uint mLow; private uint mHigh; public uint Low { get { return mLow; } set { mLow = value; } } public uint High { get { return mHigh; } set { mHigh = value; } } public SequenceRange(uint low, uint high) { mLow = low; mHigh = high; } public override string ToString() { if(mLow == mHigh) return mLow.ToString(); else return mLow.ToString() + ":" + mHigh.ToString(); } public int CompareTo(object obj) { SequenceRange oth = obj as SequenceRange; if(oth == null) return 1; if(mLow < oth.mLow) return -1; else if(mLow > oth.mLow) return 1; else return mHigh.CompareTo(oth.mHigh); } } #endregion #region SequenceSetEnumerator private class SequenceSetEnumerator : IEnumerator { private SequenceSet mSequenceSet; private int mRangeIndex; private uint mCurrentValue; private SequenceRange mCurrentRange; public SequenceSetEnumerator(SequenceSet ss) { mSequenceSet = ss; mRangeIndex = -1; } public object Current { get { return mCurrentValue; } } public bool MoveNext() { if(mRangeIndex == -1) return MoveNextRange(); ++mCurrentValue; if(mCurrentValue > mCurrentRange.High) // moved past the end of this range return MoveNextRange(); return true; } private bool MoveNextRange() { ++mRangeIndex; if(mRangeIndex == mSequenceSet.RangeCount) return false; mCurrentRange = mSequenceSet.Ranges[mRangeIndex]; mCurrentValue = mCurrentRange.Low; return true; } public void Reset() { mRangeIndex = -1; } } #endregion private bool mOptimized = true; private SequenceRange[] mOptimizedRanges; private ArrayList mRanges = new ArrayList(); private uint mMaxValue; public SequenceSet(uint maxValue) { mMaxValue = maxValue; } public void AddRange(uint num) { AddRange(num, num); } public void AddRange(uint start, uint end) { uint low = Math.Min(start, end); uint high = Math.Max(start, end); mRanges.Add(new SequenceRange(low, high)); mOptimized = false; mOptimizedRanges = null; } public void Optimize() { if(mOptimized) return; mOptimized = true; // sort them so we can optimize mRanges.Sort(); for(int oidx = 0; oidx < mRanges.Count - 1; ++oidx) { SequenceRange o = mRanges[oidx] as SequenceRange; int iidx = oidx + 1; for(; iidx < mRanges.Count; ++iidx) { SequenceRange i = mRanges[iidx] as SequenceRange; if(o.High + 1 >= i.Low) // these can be combined o.High = Math.Max(o.High, i.High); else // they cannot break; } --iidx; // at this point, oidx points to the last item that can be combined with the item at iidx if(iidx != oidx) // we have combined ranges mRanges.RemoveRange(iidx + 1, oidx - iidx); // remove them permanently and move on } mOptimizedRanges = (SequenceRange[])mRanges.ToArray(typeof(SequenceRange)); } public int RangeCount { get { return mRanges.Count; } } public SequenceRange[] Ranges { get { Optimize(); return mOptimizedRanges; } } public IEnumerator GetEnumerator() { Optimize(); return new SequenceSetEnumerator(this); } public override string ToString() { Optimize(); StringBuilder str = new StringBuilder(); for(int idx = 0; idx < mRanges.Count; ++idx) { str.Append(mRanges[idx].ToString()); if(idx < mRanges.Count - 1) str.Append(','); } return str.ToString(); } public override bool Read(TextReader rdr) { string raw = ReadTil(rdr, ' ', false); string[] ranges = raw.Split(','); foreach(string range in ranges) { Match m = Regex.Match(range, @"^(?:(?:([1-9][0-9]{0,9})(?::([1-9][0-9]{0,9}|\*))?)|(?:(\*):([1-9][0-9]{0,9})))$"); if(!m.Success) return false; uint startNum = 0; uint endNum = 0; if(m.Groups[1].Success) { startNum = uint.Parse(m.Groups[1].Value); if(m.Groups[2].Success) { if(m.Groups[2].Value == "*") endNum = mMaxValue; else endNum = uint.Parse(m.Groups[2].Value); } else endNum = startNum; } else if(m.Groups[3].Success) { if(m.Groups[3].Value == "*") startNum = mMaxValue; else startNum = uint.Parse(m.Groups[3].Value); endNum = uint.Parse(m.Groups[4].Value); } this.AddRange(startNum, endNum); } return true; } } } --- NEW FILE: StatusRequest.cs --- using Common; using System; using System.Text; namespace Imap.Types { public class StatusRequest : BaseType { private bool mMessages; private bool mRecent; private bool mUidNext; private bool mUidValidity; private bool mUnseen; public bool Messages { get { return mMessages; } set { mMessages = value; } } public bool Recent { get { return mRecent; } set { mRecent = value; } } public bool UidNext { get { return mUidNext; } set { mUidNext = value; } } public bool UidValidity { get { return mUidValidity; } set { mUidValidity = value; } } public bool Unseen { get { return mUnseen; } set { mUnseen = value; } } public string GenerateStatusResponse(Mailbox box) { int done = 0; StringBuilder str = new StringBuilder(); str.Append("("); if(mMessages) str.Append((done++ == 0 ? string.Empty : " ") + box.MessageCount); if(mRecent) str.Append((done++ == 0 ? string.Empty : " ") + box.MessageCount); if(mUidNext) str.Append((done++ == 0 ? string.Empty : " ") + box.MessageCount); if(mUidValidity) str.Append((done++ == 0 ? string.Empty : " ") + box.MessageCount); if(mUnseen) str.Append((done++ == 0 ? string.Empty : " ") + box.MessageCount); str.Append(")"); return str.ToString(); } public override bool Read(System.IO.TextReader rdr) { string raw = ReadTil(rdr, ')', true); if(raw[0] != '(' || raw[raw.Length-1] != ')') return false; string[] parts = raw.Substring(1, raw.Length-2).Split(' '); foreach(string part in parts) { switch(part) { case "MESSAGES": mMessages = true; break; case "RECENT": mRecent = true; break; case "UIDNEXT": mUidNext = true; break; case "UIDVALIDITY": mUidValidity = true; break; case "UNSEEN": mUnseen = true; break; default: return false; } } return true; } } } --- NEW FILE: StoreRequest.cs --- using System; namespace Imap.Types { public class StoreRequest : BaseType { private bool mSilent; private bool mAdd; private bool mRemove; private bool mReplace; public bool Silent { get { return mSilent; } set { mSilent = value; } } public bool Add { get { return mAdd; } set { mAdd = value; } } public bool Remove { get { return mRemove; } set { mRemove = value; } } public bool Replace { get { return mReplace; } set { mReplace = value; } } public override bool Read(System.IO.TextReader rdr) { string str = ReadTil(rdr, ' ', false); if(str.EndsWith(".SILENT")) { mSilent = true; str = str.Substring(0, str.Length - 7); } switch(str) { case "FLAGS": mReplace = true; break; case "+FLAGS": mAdd = true; break; case "-FLAGS": mRemove = true; break; default: return false; } return true; } } } --- NEW FILE: StringType.cs --- using System; using System.IO; using System.Text; namespace Imap.Types { public enum StringTypeEnum { Atom, AtomString, Quoted, Literal, AString, List } public class StringType : BaseType { private StringTypeEnum mType; private string mString; public StringType(StringTypeEnum type) { mType = type; } public static implicit operator string(StringType m) { return m.mString; } public override bool Read(TextReader rdr) { char firstChar = Convert.ToChar(rdr.Peek()); if((mType == StringTypeEnum.Quoted || mType == StringTypeEnum.AString || mType == StringTypeEnum.List) && firstChar == '"') return ReadQuoted(rdr); else if((mType == StringTypeEnum.Literal || mType == StringTypeEnum.AString || mType == StringTypeEnum.List) && firstChar == '{') return ReadLiteral(rdr); else if(mType == StringTypeEnum.AString) { mString = ReadWhileGood(rdr, Imap.Commands.CommandPart.AStringChar); return (mString != string.Empty); } else if(mType == StringTypeEnum.List) { mString = ReadWhileGood(rdr, Imap.Commands.CommandPart.ListChar); return (mString != string.Empty); } else if(mType == StringTypeEnum.Atom) { mString = ReadWhileGood(rdr, Imap.Commands.CommandPart.AtomChar); return (mString != string.Empty); } return false; } private bool ReadLiteral(TextReader rdr) { return false; } internal static string ReadWhileGood(TextReader rdr, string validChars) { StringBuilder str = new StringBuilder(); while(true) { int intVal = rdr.Peek(); if(intVal == -1) break; char ch = Convert.ToChar(intVal); if(validChars.IndexOf(ch) == -1) break; str.Append(ch); rdr.Read(); } return str.ToString(); } private bool ReadQuoted(TextReader rdr) { StringBuilder str = new StringBuilder(); rdr.Read(); // skip quote bool readEscape = false; while(true) { int intVal = rdr.Read(); if(intVal == -1) break; char ch = Convert.ToChar(intVal); if(ch == '"') { if(readEscape) str.Append(ch); else break; } else if(ch == '\\') { if(readEscape) str.Append(ch); else readEscape = true; } else str.Append(ch); } mString = str.ToString(); return true; } } } |
|
From: <ta...@us...> - 2003-08-08 22:39:30
|
Update of /cvsroot/csmaild/csmaild/src/Smtp In directory sc8-pr-cvs1:/tmp/cvs-serv18360/src/Smtp Modified Files: Smtp.2002.csproj Smtp.csproj Added Files: SmtpConnection.cs SmtpServer.cs Log Message: Updated VS.NET 2002 project files to reflect currect VS.NET 2003 project Moved message flags into a FlagList class (this makes flag extensions easier to support) Modified the TestClient for easier debugging of the server Added BASE64 decoder to common library Moved argument parsing from ImapCommand into the newly created types (representing various arguments parsed from the client) Added support for AUTHENTICATE PLAIN Added TODO document in the docs !!Some stuff surely has been broken with all this code moving around, it'll compile and run, but some functionality hasn't been verified yet!! --- NEW FILE: SmtpConnection.cs --- using Common.NetworkManager; using System; namespace Smtp { /// <summary> /// Represents an SMTP connection /// </summary> public class SmtpConnection { private Connection mConnection; // the underlying network connection private SmtpServer mServer; // what server we belong to public SmtpConnection(SmtpServer svr, Connection con) { mServer = svr; mConnection = con; mConnection.ReceivedLineEvent += new ReceivedLineDelegate(Connection_ReceivedLine); ReadCommand(); } public void SendMessage(string msg) { mConnection.WriteLine(msg); } public void ReadCommand() { mConnection.BeginReadLine(); } private void Connection_ReceivedLine(Connection cnn, string line) { // ParseAndValidate(line); SendMessage("Got it"); ReadCommand(); } } } --- NEW FILE: SmtpServer.cs --- using Common.MailstoreProviders; using Common.NetworkManager; using System; using System.Collections; namespace Smtp { /// <summary> /// Represents the mail servers smtp server /// </summary> public class SmtpServer { private NetworkManager mNetwork = new NetworkManager(); private Hashtable mSmtpConnections = new Hashtable(); private IMailstoreProvider mMailstoreProvider; public SmtpServer(IMailstoreProvider provider) { mMailstoreProvider = provider; } public void Start() { Listener li = mNetwork.StartListener(25); // TODO: global configuration li.AcceptedConnection += new AcceptDelegate(NewConnectionHandler); } private void NewConnectionHandler(Connection con) { con.ConnectionClosedEvent += new ConnectionClosedDelegate(Connection_Closed); SmtpConnection cnn = new SmtpConnection(this, con); cnn.SendMessage("220 domain.tld ESMTP csmaild dev ready"); mSmtpConnections.Add(con, cnn); } private void Connection_Closed(Connection con) { mSmtpConnections.Remove(con); System.Diagnostics.Trace.WriteLine(con + " Closed"); } } } Index: Smtp.2002.csproj =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Smtp/Smtp.2002.csproj,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** Smtp.2002.csproj 1 Aug 2003 22:02:38 -0000 1.1 --- Smtp.2002.csproj 8 Aug 2003 22:39:26 -0000 1.2 *************** *** 70,76 **** <Reference Name = "System.XML" ! AssemblyName = "System.XML" HintPath = "..\..\..\..\..\..\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.XML.dll" /> </References> </Build> --- 70,81 ---- <Reference Name = "System.XML" ! AssemblyName = "System.Xml" HintPath = "..\..\..\..\..\..\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.XML.dll" /> + <Reference + Name = "Common.2002" + Project = "{19D49838-BF7A-4432-9C84-F50AE399077B}" + Package = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}" + /> </References> </Build> *************** *** 79,82 **** --- 84,97 ---- <File RelPath = "AssemblyInfo.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "SmtpConnection.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "SmtpServer.cs" SubType = "Code" BuildAction = "Compile" Index: Smtp.csproj =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Smtp/Smtp.csproj,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** Smtp.csproj 1 Aug 2003 22:02:38 -0000 1.1 --- Smtp.csproj 8 Aug 2003 22:39:26 -0000 1.2 *************** *** 77,83 **** <Reference Name = "System.XML" ! AssemblyName = "System.XML" HintPath = "..\..\..\..\..\..\WINDOWS\Microsoft.NET\Framework\v1.1.4322\System.XML.dll" /> </References> </Build> --- 77,88 ---- <Reference Name = "System.XML" ! AssemblyName = "System.Xml" HintPath = "..\..\..\..\..\..\WINDOWS\Microsoft.NET\Framework\v1.1.4322\System.XML.dll" /> + <Reference + Name = "Common" + Project = "{19D49838-BF7A-4432-9C84-F50AE399077B}" + Package = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}" + /> </References> </Build> *************** *** 86,89 **** --- 91,104 ---- <File RelPath = "AssemblyInfo.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "SmtpConnection.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "SmtpServer.cs" SubType = "Code" BuildAction = "Compile" |
|
From: <ta...@us...> - 2003-08-08 22:39:30
|
Update of /cvsroot/csmaild/csmaild/src/Imap In directory sc8-pr-cvs1:/tmp/cvs-serv18360/src/Imap Modified Files: Connection.cs Imap.2002.csproj Imap.csproj Log Message: Updated VS.NET 2002 project files to reflect currect VS.NET 2003 project Moved message flags into a FlagList class (this makes flag extensions easier to support) Modified the TestClient for easier debugging of the server Added BASE64 decoder to common library Moved argument parsing from ImapCommand into the newly created types (representing various arguments parsed from the client) Added support for AUTHENTICATE PLAIN Added TODO document in the docs !!Some stuff surely has been broken with all this code moving around, it'll compile and run, but some functionality hasn't been verified yet!! Index: Connection.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Imap/Connection.cs,v retrieving revision 1.11 retrieving revision 1.12 diff -C2 -d -r1.11 -r1.12 *** Connection.cs 5 Aug 2003 01:50:25 -0000 1.11 --- Connection.cs 8 Aug 2003 22:39:26 -0000 1.12 *************** *** 105,109 **** public void SendContinueMessage(string msg) { ! SendMessage("+ " + msg); } private void SendMessage(string msg) --- 105,112 ---- public void SendContinueMessage(string msg) { ! if(msg.Length == 0) ! SendMessage("+"); ! else ! SendMessage("+ " + msg); } private void SendMessage(string msg) *************** *** 195,203 **** private void Connection_ReceivedLine(Connection cnn, string line) { - ParseAndValidate(line); - try { ! if(mCurrentCommand.Process()) { } --- 198,213 ---- private void Connection_ReceivedLine(Connection cnn, string line) { try { ! if(ParseAndValidate(line)) ! { ! if(mCurrentCommand.Process()) ! { ! } ! else ! { ! } ! } ! else { } Index: Imap.2002.csproj =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Imap/Imap.2002.csproj,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** Imap.2002.csproj 29 Jul 2003 00:46:29 -0000 1.2 --- Imap.2002.csproj 8 Aug 2003 22:39:26 -0000 1.3 *************** *** 227,230 **** --- 227,270 ---- BuildAction = "Compile" /> + <File + RelPath = "Types\BaseType.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "Types\FetchRequest.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "Types\FlagList.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "Types\SearchRequest.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "Types\SequenceSet.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "Types\StatusRequest.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "Types\StoreRequest.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "Types\StringType.cs" + SubType = "Code" + BuildAction = "Compile" + /> </Include> </Files> Index: Imap.csproj =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Imap/Imap.csproj,v retrieving revision 1.8 retrieving revision 1.9 diff -C2 -d -r1.8 -r1.9 *** Imap.csproj 3 Aug 2003 01:08:38 -0000 1.8 --- Imap.csproj 8 Aug 2003 22:39:26 -0000 1.9 *************** *** 229,232 **** --- 229,272 ---- BuildAction = "Compile" /> + <File + RelPath = "Types\BaseType.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "Types\FetchRequest.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "Types\FlagList.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "Types\SearchRequest.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "Types\SequenceSet.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "Types\StatusRequest.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "Types\StoreRequest.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "Types\StringType.cs" + SubType = "Code" + BuildAction = "Compile" + /> </Include> </Files> |
|
From: <ta...@us...> - 2003-08-08 22:39:30
|
Update of /cvsroot/csmaild/csmaild/src/Engine In directory sc8-pr-cvs1:/tmp/cvs-serv18360/src/Engine Modified Files: Engine.2002.csproj Engine.cs Engine.csproj Log Message: Updated VS.NET 2002 project files to reflect currect VS.NET 2003 project Moved message flags into a FlagList class (this makes flag extensions easier to support) Modified the TestClient for easier debugging of the server Added BASE64 decoder to common library Moved argument parsing from ImapCommand into the newly created types (representing various arguments parsed from the client) Added support for AUTHENTICATE PLAIN Added TODO document in the docs !!Some stuff surely has been broken with all this code moving around, it'll compile and run, but some functionality hasn't been verified yet!! Index: Engine.2002.csproj =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Engine/Engine.2002.csproj,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** Engine.2002.csproj 25 Jul 2003 14:50:31 -0000 1.1 --- Engine.2002.csproj 8 Aug 2003 22:39:26 -0000 1.2 *************** *** 83,86 **** --- 83,91 ---- Package = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}" /> + <Reference + Name = "Smtp.2002" + Project = "{DD1BF484-EAC2-4C41-8CD5-C45E3CFFA9AA}" + Package = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}" + /> </References> </Build> Index: Engine.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Engine/Engine.cs,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** Engine.cs 27 Jul 2003 16:50:46 -0000 1.5 --- Engine.cs 8 Aug 2003 22:39:26 -0000 1.6 *************** *** 2,5 **** --- 2,6 ---- using Imap; + using Smtp; using System; *************** *** 26,31 **** System.Diagnostics.Trace.Listeners.Add(tWriter); ! ImapServer svr = new ImapServer(new XmlMailstoreProvider(@"..\SampleXmlMailstore")); ! svr.Start(); string input = string.Empty; --- 27,37 ---- System.Diagnostics.Trace.Listeners.Add(tWriter); ! IMailstoreProvider provider = new XmlMailstoreProvider(@"..\SampleXmlMailstore"); ! ! SmtpServer smtpServer = new SmtpServer(provider); ! smtpServer.Start(); ! ! ImapServer imapServer = new ImapServer(provider); ! imapServer.Start(); string input = string.Empty; Index: Engine.csproj =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Engine/Engine.csproj,v retrieving revision 1.4 retrieving revision 1.5 diff -C2 -d -r1.4 -r1.5 *** Engine.csproj 3 Aug 2003 01:08:38 -0000 1.4 --- Engine.csproj 8 Aug 2003 22:39:26 -0000 1.5 *************** *** 90,93 **** --- 90,98 ---- Package = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}" /> + <Reference + Name = "Smtp" + Project = "{DD1BF484-EAC2-4C41-8CD5-C45E3CFFA9AA}" + Package = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}" + /> </References> </Build> |
|
From: <ta...@us...> - 2003-08-08 22:39:29
|
Update of /cvsroot/csmaild/csmaild/src/Common/NetworkManager In directory sc8-pr-cvs1:/tmp/cvs-serv18360/src/Common/NetworkManager Modified Files: Connection.cs Log Message: Updated VS.NET 2002 project files to reflect currect VS.NET 2003 project Moved message flags into a FlagList class (this makes flag extensions easier to support) Modified the TestClient for easier debugging of the server Added BASE64 decoder to common library Moved argument parsing from ImapCommand into the newly created types (representing various arguments parsed from the client) Added support for AUTHENTICATE PLAIN Added TODO document in the docs !!Some stuff surely has been broken with all this code moving around, it'll compile and run, but some functionality hasn't been verified yet!! Index: Connection.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Common/NetworkManager/Connection.cs,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** Connection.cs 5 Aug 2003 01:42:09 -0000 1.2 --- Connection.cs 8 Aug 2003 22:39:26 -0000 1.3 *************** *** 126,129 **** --- 126,132 ---- mReceivedString.Remove(0, idx+2); // remove the line and the \r\n + if(line != string.Empty) + System.Diagnostics.Trace.WriteLine(ToString() + " -> " + (string)line); + return line; } |
|
From: <ta...@us...> - 2003-08-08 22:39:29
|
Update of /cvsroot/csmaild/csmaild/src/Common/MailstoreProviders In directory sc8-pr-cvs1:/tmp/cvs-serv18360/src/Common/MailstoreProviders Modified Files: XmlMailstoreProvider.cs Log Message: Updated VS.NET 2002 project files to reflect currect VS.NET 2003 project Moved message flags into a FlagList class (this makes flag extensions easier to support) Modified the TestClient for easier debugging of the server Added BASE64 decoder to common library Moved argument parsing from ImapCommand into the newly created types (representing various arguments parsed from the client) Added support for AUTHENTICATE PLAIN Added TODO document in the docs !!Some stuff surely has been broken with all this code moving around, it'll compile and run, but some functionality hasn't been verified yet!! Index: XmlMailstoreProvider.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Common/MailstoreProviders/XmlMailstoreProvider.cs,v retrieving revision 1.11 retrieving revision 1.12 diff -C2 -d -r1.11 -r1.12 *** XmlMailstoreProvider.cs 5 Aug 2003 01:42:09 -0000 1.11 --- XmlMailstoreProvider.cs 8 Aug 2003 22:39:26 -0000 1.12 *************** *** 271,279 **** newMsg["MailboxId"] = msg.Mailbox.ProvidersIdentifier; newMsg["UniqueIdentifier"] = msg.UniqueId; ! newMsg["Seen"] = msg.Seen; ! newMsg["Answered"] = msg.Answered; ! newMsg["Flagged"] = msg.Flagged; ! newMsg["Deleted"] = msg.Deleted; ! newMsg["Draft"] = msg.Draft; newMsg["Recent"] = msg.Recent; newMsg["Size"] = msg.Size; --- 271,279 ---- newMsg["MailboxId"] = msg.Mailbox.ProvidersIdentifier; newMsg["UniqueIdentifier"] = msg.UniqueId; ! newMsg["Seen"] = msg.Flags.Seen; ! newMsg["Answered"] = msg.Flags.Answered; ! newMsg["Flagged"] = msg.Flags.Flagged; ! newMsg["Deleted"] = msg.Flags.Deleted; ! newMsg["Draft"] = msg.Flags.Draft; newMsg["Recent"] = msg.Recent; newMsg["Size"] = msg.Size; *************** *** 298,306 **** // row["MailboxId"] = msg.Mailbox.ProvidersIdentifier; // row["UniqueIdentifier"] = msg.UniqueId; ! row["Seen"] = msg.Seen; ! row["Answered"] = msg.Answered; ! row["Flagged"] = msg.Flagged; ! row["Deleted"] = msg.Deleted; ! row["Draft"] = msg.Draft; row["Recent"] = msg.Recent; // row["Size"] = msg.Size; --- 298,306 ---- // row["MailboxId"] = msg.Mailbox.ProvidersIdentifier; // row["UniqueIdentifier"] = msg.UniqueId; ! row["Seen"] = msg.Flags.Seen; ! row["Answered"] = msg.Flags.Answered; ! row["Flagged"] = msg.Flags.Flagged; ! row["Deleted"] = msg.Flags.Deleted; ! row["Draft"] = msg.Flags.Draft; row["Recent"] = msg.Recent; // row["Size"] = msg.Size; |
Update of /cvsroot/csmaild/csmaild/src/Common In directory sc8-pr-cvs1:/tmp/cvs-serv18360/src/Common Modified Files: Common.2002.csproj Common.csproj Mailbox.cs Message.cs MessageCollection.cs Added Files: FlagList.cs Log Message: Updated VS.NET 2002 project files to reflect currect VS.NET 2003 project Moved message flags into a FlagList class (this makes flag extensions easier to support) Modified the TestClient for easier debugging of the server Added BASE64 decoder to common library Moved argument parsing from ImapCommand into the newly created types (representing various arguments parsed from the client) Added support for AUTHENTICATE PLAIN Added TODO document in the docs !!Some stuff surely has been broken with all this code moving around, it'll compile and run, but some functionality hasn't been verified yet!! --- NEW FILE: FlagList.cs --- using System; using System.Collections.Specialized; namespace Common { public class FlagList { private bool mAnswered; private bool mFlagged; private bool mDeleted; private bool mSeen; private bool mDraft; private StringCollection mKeywords; public bool Answered { get { return mAnswered; } set { mAnswered = value; } } public bool Flagged { get { return mFlagged; } set { mFlagged = value; } } public bool Deleted { get { return mDeleted; } set { mDeleted = value; } } public bool Seen { get { return mSeen; } set { mSeen = value; } } public bool Draft { get { return mDraft; } set { mDraft = value; } } public StringCollection Keywords { get { if(mKeywords == null) mKeywords = new StringCollection(); return mKeywords; } } public void AddFlags(FlagList list) { } public void RemoveFlags(FlagList list) { } } } Index: Common.2002.csproj =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Common/Common.2002.csproj,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** Common.2002.csproj 1 Aug 2003 22:02:37 -0000 1.3 --- Common.2002.csproj 8 Aug 2003 22:39:26 -0000 1.4 *************** *** 88,91 **** --- 88,96 ---- /> <File + RelPath = "FlagList.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File RelPath = "Mailbox.cs" SubType = "Code" *************** *** 109,112 **** --- 114,122 ---- <File RelPath = "User.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "Encoders\Base64.cs" SubType = "Code" BuildAction = "Compile" Index: Common.csproj =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Common/Common.csproj,v retrieving revision 1.7 retrieving revision 1.8 diff -C2 -d -r1.7 -r1.8 *** Common.csproj 1 Aug 2003 22:02:37 -0000 1.7 --- Common.csproj 8 Aug 2003 22:39:26 -0000 1.8 *************** *** 95,98 **** --- 95,103 ---- /> <File + RelPath = "FlagList.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File RelPath = "Mailbox.cs" SubType = "Code" *************** *** 116,119 **** --- 121,129 ---- <File RelPath = "User.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "Encoders\Base64.cs" SubType = "Code" BuildAction = "Compile" Index: Mailbox.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Common/Mailbox.cs,v retrieving revision 1.11 retrieving revision 1.12 diff -C2 -d -r1.11 -r1.12 *** Mailbox.cs 5 Aug 2003 01:42:09 -0000 1.11 --- Mailbox.cs 8 Aug 2003 22:39:26 -0000 1.12 *************** *** 131,135 **** /// The number of messages in this mailbox /// </summary> ! public int MessageCount { get --- 131,135 ---- /// The number of messages in this mailbox /// </summary> ! public uint MessageCount { get *************** *** 163,167 **** int rv = 0; foreach(Message msg in Messages) ! if(!msg.Seen) ++rv; return rv; --- 163,167 ---- int rv = 0; foreach(Message msg in Messages) ! if(!msg.Flags.Seen) ++rv; return rv; *************** *** 175,179 **** // TODO: make this more efficient then having to load all the messages for(uint idx = 0; idx < Messages.Count; ++idx) ! if(!Messages.BySeq(idx+1).Seen) return idx; return 0; --- 175,179 ---- // TODO: make this more efficient then having to load all the messages for(uint idx = 0; idx < Messages.Count; ++idx) ! if(!Messages.BySeq(idx+1).Flags.Seen) return idx; return 0; Index: Message.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Common/Message.cs,v retrieving revision 1.8 retrieving revision 1.9 diff -C2 -d -r1.8 -r1.9 *** Message.cs 5 Aug 2003 01:42:09 -0000 1.8 --- Message.cs 8 Aug 2003 22:39:26 -0000 1.9 *************** *** 16,24 **** private uint mUniqueId; ! private bool mSeen; ! private bool mAnswered; ! private bool mFlagged; ! private bool mDeleted; ! private bool mDraft; private bool mRecent; private int mSize; --- 16,20 ---- private uint mUniqueId; ! private FlagList mFlags; private bool mRecent; private int mSize; *************** *** 49,124 **** } ! /// <summary> ! /// Whether the message has been seen ! /// </summary> ! public bool Seen ! { ! get ! { ! return mSeen; ! } ! set ! { ! mSeen = value; ! } ! } ! ! /// <summary> ! /// Whether the message has been answered ! /// </summary> ! public bool Answered ! { ! get ! { ! return mAnswered; ! } ! set ! { ! mAnswered = value; ! } ! } ! ! /// <summary> ! /// Whether the message has been flagged ! /// </summary> ! public bool Flagged ! { ! get ! { ! return mFlagged; ! } ! set ! { ! mFlagged = value; ! } ! } ! ! /// <summary> ! /// Whether the message has been deleted ! /// </summary> ! public bool Deleted ! { ! get ! { ! return mDeleted; ! } ! set ! { ! mDeleted = value; ! } ! } ! ! /// <summary> ! /// Whether the message is a draft ! /// </summary> ! public bool Draft { get { ! return mDraft; } set { ! mDraft = value; } } --- 45,57 ---- } ! public FlagList Flags { get { ! return mFlags; } set { ! mFlags = value; } } *************** *** 189,197 **** mUniqueId = uid; ! mSeen = seen; ! mAnswered = answered; ! mFlagged = flagged; ! mDeleted = deleted; ! mDraft = draft; mRecent = recent; mSize = size; --- 122,131 ---- mUniqueId = uid; ! mFlags = new FlagList(); ! mFlags.Seen = seen; ! mFlags.Answered = answered; ! mFlags.Flagged = flagged; ! mFlags.Deleted = deleted; ! mFlags.Draft = draft; mRecent = recent; mSize = size; Index: MessageCollection.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Common/MessageCollection.cs,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** MessageCollection.cs 5 Aug 2003 01:42:09 -0000 1.5 --- MessageCollection.cs 8 Aug 2003 22:39:26 -0000 1.6 *************** *** 31,39 **** } ! public int Count { get { ! return mSequenceList.Count; } } --- 31,39 ---- } ! public uint Count { get { ! return (uint)mSequenceList.Count; } } *************** *** 60,64 **** { uint uid = mMailbox.NextUniqueIdIncrement; ! Message newMsg = new Message(mMailstoreProvider, mMailbox, null, uid, msg.Seen, msg.Answered, msg.Flagged, msg.Deleted, msg.Draft, true, msg.Size, msg.InternalDate); mMailstoreProvider.InsertMessage(newMsg, msg.RawRfc2822Message); --- 60,64 ---- { uint uid = mMailbox.NextUniqueIdIncrement; ! Message newMsg = new Message(mMailstoreProvider, mMailbox, null, uid, msg.Flags.Seen, msg.Flags.Answered, msg.Flags.Flagged, msg.Flags.Deleted, msg.Flags.Draft, true, msg.Size, msg.InternalDate); mMailstoreProvider.InsertMessage(newMsg, msg.RawRfc2822Message); |
|
From: <ta...@us...> - 2003-08-08 22:39:29
|
Update of /cvsroot/csmaild/csmaild/src/Common/Rfc2822 In directory sc8-pr-cvs1:/tmp/cvs-serv18360/src/Common/Rfc2822 Modified Files: Interpreter.cs Log Message: Updated VS.NET 2002 project files to reflect currect VS.NET 2003 project Moved message flags into a FlagList class (this makes flag extensions easier to support) Modified the TestClient for easier debugging of the server Added BASE64 decoder to common library Moved argument parsing from ImapCommand into the newly created types (representing various arguments parsed from the client) Added support for AUTHENTICATE PLAIN Added TODO document in the docs !!Some stuff surely has been broken with all this code moving around, it'll compile and run, but some functionality hasn't been verified yet!! Index: Interpreter.cs =================================================================== RCS file: /cvsroot/csmaild/csmaild/src/Common/Rfc2822/Interpreter.cs,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** Interpreter.cs 2 Aug 2003 15:57:10 -0000 1.2 --- Interpreter.cs 8 Aug 2003 22:39:26 -0000 1.3 *************** *** 24,27 **** --- 24,28 ---- InterpretBody(msg.Body); + mRawMessage.Close(); return msg; } *************** *** 152,173 **** } #endregion ! /* ! date-time = [ day-of-week "," ] date FWS time [CFWS] ! day-of-week = ([FWS] day-name) / obs-day-of-week ! day-name = "Mon" / "Tue" / "Wed" / "Thu" / ! "Fri" / "Sat" / "Sun" ! date = day month year ! year = 4*DIGIT / obs-year ! month = (FWS month-name FWS) / obs-month ! month-name = "Jan" / "Feb" / "Mar" / "Apr" / ! "May" / "Jun" / "Jul" / "Aug" / ! "Sep" / "Oct" / "Nov" / "Dec" ! day = ([FWS] 1*2DIGIT) / obs-day ! time = time-of-day FWS zone ! time-of-day = hour ":" minute [ ":" second ] ! hour = 2DIGIT / obs-hour ! minute = 2DIGIT / obs-minute ! second = 2DIGIT / obs-second ! zone = (( "+" / "-" ) 4DIGIT) / obs-zone*/ #region Body private void InterpretBody(MessageBody bdy) --- 153,157 ---- } #endregion ! #region Body private void InterpretBody(MessageBody bdy) |
|
From: <ta...@us...> - 2003-08-08 22:33:20
|
Update of /cvsroot/csmaild/csmaild/src/Imap/Types In directory sc8-pr-cvs1:/tmp/cvs-serv17425/Types Log Message: Directory /cvsroot/csmaild/csmaild/src/Imap/Types added to the repository |
|
From: <ta...@us...> - 2003-08-08 22:33:12
|
Update of /cvsroot/csmaild/csmaild/src/Common/Encoders In directory sc8-pr-cvs1:/tmp/cvs-serv17399/Encoders Log Message: Directory /cvsroot/csmaild/csmaild/src/Common/Encoders added to the repository |
|
From: <ta...@us...> - 2003-08-08 22:28:15
|
Update of /cvsroot/csmaild/csmaild/docs/rfcs/mime
In directory sc8-pr-cvs1:/tmp/cvs-serv16597/docs/rfcs/mime
Added Files:
2045 - MIME Part 1 Format of Internet Message Bodies.txt
Log Message:
2595 - Helped with AUTHENTICATE PLAIN command
1731 - Interested read if we want to support more AUTHENTICATE mechanisms
2045 - Defined the BASE64 encoding, which a decoder has been implemented (used in AUTHENTICATE PLAIN)
--- NEW FILE: 2045 - MIME Part 1 Format of Internet Message Bodies.txt ---
Network Working Group N. Freed
Request for Comments: 2045 Innosoft
Obsoletes: 1521, 1522, 1590 N. Borenstein
Category: Standards Track First Virtual
November 1996
Multipurpose Internet Mail Extensions
(MIME) Part One:
Format of Internet Message Bodies
Status of this Memo
[...1700 lines suppressed...]
Freed & Borenstein Standards Track [Page 31]
|
|
From: <ta...@us...> - 2003-08-08 22:28:15
|
Update of /cvsroot/csmaild/csmaild/docs/rfcs
In directory sc8-pr-cvs1:/tmp/cvs-serv16597/docs/rfcs
Added Files:
2595 - TLS with IMAP POP3 ACAP.txt
Log Message:
2595 - Helped with AUTHENTICATE PLAIN command
1731 - Interested read if we want to support more AUTHENTICATE mechanisms
2045 - Defined the BASE64 encoding, which a decoder has been implemented (used in AUTHENTICATE PLAIN)
--- NEW FILE: 2595 - TLS with IMAP POP3 ACAP.txt ---
Network Working Group C. Newman
Request for Comments: 2595 Innosoft
Category: Standards Track June 1999
Using TLS with IMAP, POP3 and ACAP
Status of this Memo
This document specifies an Internet standards track protocol for the
Internet community, and requests discussion and suggestions for
improvements. Please refer to the current edition of the "Internet
Official Protocol Standards" (STD 1) for the standardization state
and status of this protocol. Distribution of this memo is unlimited.
Copyright Notice
Copyright (C) The Internet Society (1999). All Rights Reserved.
1. Motivation
The TLS protocol (formerly known as SSL) provides a way to secure an
application protocol from tampering and eavesdropping. The option of
using such security is desirable for IMAP, POP and ACAP due to common
connection eavesdropping and hijacking attacks [AUTH]. Although
advanced SASL authentication mechanisms can provide a lightweight
version of this service, TLS is complimentary to simple
authentication-only SASL mechanisms or deployed clear-text password
login commands.
Many sites have a high investment in authentication infrastructure
(e.g., a large database of a one-way-function applied to user
passwords), so a privacy layer which is not tightly bound to user
authentication can protect against network eavesdropping attacks
without requiring a new authentication infrastructure and/or forcing
all users to change their password. Recognizing that such sites will
desire simple password authentication in combination with TLS
encryption, this specification defines the PLAIN SASL mechanism for
use with protocols which lack a simple password authentication
command such as ACAP and SMTP. (Note there is a separate RFC for the
STARTTLS command in SMTP [SMTPTLS].)
There is a strong desire in the IETF to eliminate the transmission of
clear-text passwords over unencrypted channels. While SASL can be
used for this purpose, TLS provides an additional tool with different
deployability characteristics. A server supporting both TLS with
Newman Standards Track [Page 1]
RFC 2595 Using TLS with IMAP, POP3 and ACAP June 1999
simple passwords and a challenge/response SASL mechanism is likely to
interoperate with a wide variety of clients without resorting to
unencrypted clear-text passwords.
The STARTTLS command rectifies a number of the problems with using a
separate port for a "secure" protocol variant. Some of these are
mentioned in section 7.
1.1. Conventions Used in this Document
The key words "REQUIRED", "MUST", "MUST NOT", "SHOULD", "SHOULD NOT",
"MAY", and "OPTIONAL" in this document are to be interpreted as
described in "Key words for use in RFCs to Indicate Requirement
Levels" [KEYWORDS].
Terms related to authentication are defined in "On Internet
Authentication" [AUTH].
Formal syntax is defined using ABNF [ABNF].
In examples, "C:" and "S:" indicate lines sent by the client and
server respectively.
2. Basic Interoperability and Security Requirements
The following requirements apply to all implementations of the
STARTTLS extension for IMAP, POP3 and ACAP.
2.1. Cipher Suite Requirements
Implementation of the TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA [TLS] cipher
suite is REQUIRED. This is important as it assures that any two
compliant implementations can be configured to interoperate.
All other cipher suites are OPTIONAL.
2.2. Privacy Operational Mode Security Requirements
Both clients and servers SHOULD have a privacy operational mode which
refuses authentication unless successful activation of an encryption
layer (such as that provided by TLS) occurs prior to or at the time
of authentication and which will terminate the connection if that
encryption layer is deactivated. Implementations are encouraged to
have flexability with respect to the minimal encryption strength or
cipher suites permitted. A minimalist approach to this
recommendation would be an operational mode where the
TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA cipher suite is mandatory prior to
permitting authentication.
Newman Standards Track [Page 2]
RFC 2595 Using TLS with IMAP, POP3 and ACAP June 1999
Clients MAY have an operational mode which uses encryption only when
it is advertised by the server, but authentication continues
regardless. For backwards compatibility, servers SHOULD have an
operational mode where only the authentication mechanisms required by
the relevant base protocol specification are needed to successfully
authenticate.
2.3. Clear-Text Password Requirements
Clients and servers which implement STARTTLS MUST be configurable to
refuse all clear-text login commands or mechanisms (including both
standards-track and nonstandard mechanisms) unless an encryption
layer of adequate strength is active. Servers which allow
unencrypted clear-text logins SHOULD be configurable to refuse
clear-text logins both for the entire server, and on a per-user
basis.
2.4. Server Identity Check
During the TLS negotiation, the client MUST check its understanding
of the server hostname against the server's identity as presented in
the server Certificate message, in order to prevent man-in-the-middle
attacks. Matching is performed according to these rules:
- The client MUST use the server hostname it used to open the
connection as the value to compare against the server name as
expressed in the server certificate. The client MUST NOT use any
form of the server hostname derived from an insecure remote source
(e.g., insecure DNS lookup). CNAME canonicalization is not done.
- If a subjectAltName extension of type dNSName is present in the
certificate, it SHOULD be used as the source of the server's
identity.
- Matching is case-insensitive.
- A "*" wildcard character MAY be used as the left-most name
component in the certificate. For example, *.example.com would
match a.example.com, foo.example.com, etc. but would not match
example.com.
- If the certificate contains multiple names (e.g. more than one
dNSName field), then a match with any one of the fields is
considered acceptable.
If the match fails, the client SHOULD either ask for explicit user
confirmation, or terminate the connection and indicate the server's
identity is suspect.
Newman Standards Track [Page 3]
RFC 2595 Using TLS with IMAP, POP3 and ACAP June 1999
2.5. TLS Security Policy Check
Both the client and server MUST check the result of the STARTTLS
command and subsequent TLS negotiation to see whether acceptable
authentication or privacy was achieved. Ignoring this step
completely invalidates using TLS for security. The decision about
whether acceptable authentication or privacy was achieved is made
locally, is implementation-dependent, and is beyond the scope of this
document.
3. IMAP STARTTLS extension
When the TLS extension is present in IMAP, "STARTTLS" is listed as a
capability in response to the CAPABILITY command. This extension
adds a single command, "STARTTLS" to the IMAP protocol which is used
to begin a TLS negotiation.
3.1. STARTTLS Command
Arguments: none
Responses: no specific responses for this command
Result: OK - begin TLS negotiation
BAD - command unknown or arguments invalid
A TLS negotiation begins immediately after the CRLF at the end of
the tagged OK response from the server. Once a client issues a
STARTTLS command, it MUST NOT issue further commands until a
server response is seen and the TLS negotiation is complete.
The STARTTLS command is only valid in non-authenticated state.
The server remains in non-authenticated state, even if client
credentials are supplied during the TLS negotiation. The SASL
[SASL] EXTERNAL mechanism MAY be used to authenticate once TLS
client credentials are successfully exchanged, but servers
supporting the STARTTLS command are not required to support the
EXTERNAL mechanism.
Once TLS has been started, the client MUST discard cached
information about server capabilities and SHOULD re-issue the
CAPABILITY command. This is necessary to protect against
man-in-the-middle attacks which alter the capabilities list prior
to STARTTLS. The server MAY advertise different capabilities
after STARTTLS.
The formal syntax for IMAP is amended as follows:
Newman Standards Track [Page 4]
RFC 2595 Using TLS with IMAP, POP3 and ACAP June 1999
command_any =/ "STARTTLS"
Example: C: a001 CAPABILITY
S: * CAPABILITY IMAP4rev1 STARTTLS LOGINDISABLED
S: a001 OK CAPABILITY completed
C: a002 STARTTLS
S: a002 OK Begin TLS negotiation now
<TLS negotiation, further commands are under TLS layer>
C: a003 CAPABILITY
S: * CAPABILITY IMAP4rev1 AUTH=EXTERNAL
S: a003 OK CAPABILITY completed
C: a004 LOGIN joe password
S: a004 OK LOGIN completed
3.2. IMAP LOGINDISABLED capability
The current IMAP protocol specification (RFC 2060) requires the
implementation of the LOGIN command which uses clear-text passwords.
Many sites may choose to disable this command unless encryption is
active for security reasons. An IMAP server MAY advertise that the
LOGIN command is disabled by including the LOGINDISABLED capability
in the capability response. Such a server will respond with a tagged
"NO" response to any attempt to use the LOGIN command.
An IMAP server which implements STARTTLS MUST implement support for
the LOGINDISABLED capability on unencrypted connections.
An IMAP client which complies with this specification MUST NOT issue
the LOGIN command if this capability is present.
This capability is useful to prevent clients compliant with this
specification from sending an unencrypted password in an environment
subject to passive attacks. It has no impact on an environment
subject to active attacks as a man-in-the-middle attacker can remove
this capability. Therefore this does not relieve clients of the need
to follow the privacy mode recommendation in section 2.2.
Servers advertising this capability will fail to interoperate with
many existing compliant IMAP clients and will be unable to prevent
those clients from disclosing the user's password.
4. POP3 STARTTLS extension
The POP3 STARTTLS extension adds the STLS command to POP3 servers.
If this is implemented, the POP3 extension mechanism [POP3EXT] MUST
also be implemented to avoid the need for client probing of multiple
commands. The capability name "STLS" indicates this command is
present and permitted in the current state.
Newman Standards Track [Page 5]
RFC 2595 Using TLS with IMAP, POP3 and ACAP June 1999
STLS
Arguments: none
Restrictions:
Only permitted in AUTHORIZATION state.
Discussion:
A TLS negotiation begins immediately after the CRLF at the
end of the +OK response from the server. A -ERR response
MAY result if a security layer is already active. Once a
client issues a STLS command, it MUST NOT issue further
commands until a server response is seen and the TLS
negotiation is complete.
The STLS command is only permitted in AUTHORIZATION state
and the server remains in AUTHORIZATION state, even if
client credentials are supplied during the TLS negotiation.
The AUTH command [POP-AUTH] with the EXTERNAL mechanism
[SASL] MAY be used to authenticate once TLS client
credentials are successfully exchanged, but servers
supporting the STLS command are not required to support the
EXTERNAL mechanism.
Once TLS has been started, the client MUST discard cached
information about server capabilities and SHOULD re-issue
the CAPA command. This is necessary to protect against
man-in-the-middle attacks which alter the capabilities list
prior to STLS. The server MAY advertise different
capabilities after STLS.
Possible Responses:
+OK -ERR
Examples:
C: STLS
S: +OK Begin TLS negotiation
<TLS negotiation, further commands are under TLS layer>
...
C: STLS
S: -ERR Command not permitted when TLS active
Newman Standards Track [Page 6]
RFC 2595 Using TLS with IMAP, POP3 and ACAP June 1999
5. ACAP STARTTLS extension
When the TLS extension is present in ACAP, "STARTTLS" is listed as a
capability in the ACAP greeting. No arguments to this capability are
defined at this time. This extension adds a single command,
"STARTTLS" to the ACAP protocol which is used to begin a TLS
negotiation.
5.1. STARTTLS Command
Arguments: none
Responses: no specific responses for this command
Result: OK - begin TLS negotiation
BAD - command unknown or arguments invalid
A TLS negotiation begins immediately after the CRLF at the end of
the tagged OK response from the server. Once a client issues a
STARTTLS command, it MUST NOT issue further commands until a
server response is seen and the TLS negotiation is complete.
The STARTTLS command is only valid in non-authenticated state.
The server remains in non-authenticated state, even if client
credentials are supplied during the TLS negotiation. The SASL
[SASL] EXTERNAL mechanism MAY be used to authenticate once TLS
client credentials are successfully exchanged, but servers
supporting the STARTTLS command are not required to support the
EXTERNAL mechanism.
After the TLS layer is established, the server MUST re-issue an
untagged ACAP greeting. This is necessary to protect against
man-in-the-middle attacks which alter the capabilities list prior
to STARTTLS. The client MUST discard cached capability
information and replace it with the information from the new ACAP
greeting. The server MAY advertise different capabilities after
STARTTLS.
The formal syntax for ACAP is amended as follows:
command_any =/ "STARTTLS"
Example: S: * ACAP (SASL "CRAM-MD5") (STARTTLS)
C: a002 STARTTLS
S: a002 OK "Begin TLS negotiation now"
<TLS negotiation, further commands are under TLS layer>
S: * ACAP (SASL "CRAM-MD5" "PLAIN" "EXTERNAL")
Newman Standards Track [Page 7]
RFC 2595 Using TLS with IMAP, POP3 and ACAP June 1999
6. PLAIN SASL mechanism
Clear-text passwords are simple, interoperate with almost all
existing operating system authentication databases, and are useful
for a smooth transition to a more secure password-based
authentication mechanism. The drawback is that they are unacceptable
for use over an unencrypted network connection.
This defines the "PLAIN" SASL mechanism for use with ACAP and other
protocols with no clear-text login command. The PLAIN SASL mechanism
MUST NOT be advertised or used unless a strong encryption layer (such
as the provided by TLS) is active or backwards compatibility dictates
otherwise.
The mechanism consists of a single message from the client to the
server. The client sends the authorization identity (identity to
login as), followed by a US-ASCII NUL character, followed by the
authentication identity (identity whose password will be used),
followed by a US-ASCII NUL character, followed by the clear-text
password. The client may leave the authorization identity empty to
indicate that it is the same as the authentication identity.
The server will verify the authentication identity and password with
the system authentication database and verify that the authentication
credentials permit the client to login as the authorization identity.
If both steps succeed, the user is logged in.
The server MAY also use the password to initialize any new
authentication database, such as one suitable for CRAM-MD5
[CRAM-MD5].
Non-US-ASCII characters are permitted as long as they are represented
in UTF-8 [UTF-8]. Use of non-visible characters or characters which
a user may be unable to enter on some keyboards is discouraged.
The formal grammar for the client message using Augmented BNF [ABNF]
follows.
message = [authorize-id] NUL authenticate-id NUL password
authenticate-id = 1*UTF8-SAFE ; MUST accept up to 255 octets
authorize-id = 1*UTF8-SAFE ; MUST accept up to 255 octets
password = 1*UTF8-SAFE ; MUST accept up to 255 octets
NUL = %x00
UTF8-SAFE = %x01-09 / %x0B-0C / %x0E-7F / UTF8-2 /
UTF8-3 / UTF8-4 / UTF8-5 / UTF8-6
UTF8-1 = %x80-BF
UTF8-2 = %xC0-DF UTF8-1
UTF8-3 = %xE0-EF 2UTF8-1
Newman Standards Track [Page 8]
RFC 2595 Using TLS with IMAP, POP3 and ACAP June 1999
UTF8-4 = %xF0-F7 3UTF8-1
UTF8-5 = %xF8-FB 4UTF8-1
UTF8-6 = %xFC-FD 5UTF8-1
Here is an example of how this might be used to initialize a CRAM-MD5
authentication database for ACAP:
Example: S: * ACAP (SASL "CRAM-MD5") (STARTTLS)
C: a001 AUTHENTICATE "CRAM-MD5"
S: + "<189...@po...>"
C: "tim b913a602c7eda7a495b4e6e7334d3890"
S: a001 NO (TRANSITION-NEEDED)
"Please change your password, or use TLS to login"
C: a002 STARTTLS
S: a002 OK "Begin TLS negotiation now"
<TLS negotiation, further commands are under TLS layer>
S: * ACAP (SASL "CRAM-MD5" "PLAIN" "EXTERNAL")
C: a003 AUTHENTICATE "PLAIN" {21+}
C: <NUL>tim<NUL>tanstaaftanstaaf
S: a003 OK CRAM-MD5 password initialized
Note: In this example, <NUL> represents a single ASCII NUL octet.
7. imaps and pop3s ports
Separate "imaps" and "pop3s" ports were registered for use with SSL.
Use of these ports is discouraged in favor of the STARTTLS or STLS
commands.
A number of problems have been observed with separate ports for
"secure" variants of protocols. This is an attempt to enumerate some
of those problems.
- Separate ports lead to a separate URL scheme which intrudes into
the user interface in inappropriate ways. For example, many web
pages use language like "click here if your browser supports SSL."
This is a decision the browser is often more capable of making than
the user.
- Separate ports imply a model of either "secure" or "not secure."
This can be misleading in a number of ways. First, the "secure"
port may not in fact be acceptably secure as an export-crippled
cipher suite might be in use. This can mislead users into a false
sense of security. Second, the normal port might in fact be
secured by using a SASL mechanism which includes a security layer.
Thus the separate port distinction makes the complex topic of
security policy even more confusing. One common result of this
confusion is that firewall administrators are often misled into
Newman Standards Track [Page 9]
RFC 2595 Using TLS with IMAP, POP3 and ACAP June 1999
permitting the "secure" port and blocking the standard port. This
could be a poor choice given the common use of SSL with a 40-bit
key encryption layer and plain-text password authentication is less
secure than strong SASL mechanisms such as GSSAPI with Kerberos 5.
- Use of separate ports for SSL has caused clients to implement only
two security policies: use SSL or don't use SSL. The desirable
security policy "use TLS when available" would be cumbersome with
the separate port model, but is simple with STARTTLS.
- Port numbers are a limited resource. While they are not yet in
short supply, it is unwise to set a precedent that could double (or
worse) the speed of their consumption.
8. IANA Considerations
This constitutes registration of the "STARTTLS" and "LOGINDISABLED"
IMAP capabilities as required by section 7.2.1 of RFC 2060 [IMAP].
The registration for the POP3 "STLS" capability follows:
CAPA tag: STLS
Arguments: none
Added commands: STLS
Standard commands affected: May enable USER/PASS as a side-effect.
CAPA command SHOULD be re-issued after successful completion.
Announced states/Valid states: AUTHORIZATION state only.
Specification reference: this memo
The registration for the ACAP "STARTTLS" capability follows:
Capability name: STARTTLS
Capability keyword: STARTTLS
Capability arguments: none
Published Specification(s): this memo
Person and email address for further information:
see author's address section below
The registration for the PLAIN SASL mechanism follows:
SASL mechanism name: PLAIN
Security Considerations: See section 9 of this memo
Published specification: this memo
Person & email address to contact for further information:
see author's address section below
Intended usage: COMMON
Author/Change controller: see author's address section below
Newman Standards Track [Page 10]
RFC 2595 Using TLS with IMAP, POP3 and ACAP June 1999
9. Security Considerations
TLS only provides protection for data sent over a network connection.
Messages transferred over IMAP or POP3 are still available to server
administrators and usually subject to eavesdropping, tampering and
forgery when transmitted through SMTP or NNTP. TLS is no substitute
for an end-to-end message security mechanism using MIME security
multiparts [MIME-SEC].
A man-in-the-middle attacker can remove STARTTLS from the capability
list or generate a failure response to the STARTTLS command. In
order to detect such an attack, clients SHOULD warn the user when
session privacy is not active and/or be configurable to refuse to
proceed without an acceptable level of security.
A man-in-the-middle attacker can always cause a down-negotiation to
the weakest authentication mechanism or cipher suite available. For
this reason, implementations SHOULD be configurable to refuse weak
mechanisms or cipher suites.
Any protocol interactions prior to the TLS handshake are performed in
the clear and can be modified by a man-in-the-middle attacker. For
this reason, clients MUST discard cached information about server
capabilities advertised prior to the start of the TLS handshake.
Clients are encouraged to clearly indicate when the level of
encryption active is known to be vulnerable to attack using modern
hardware (such as encryption keys with 56 bits of entropy or less).
The LOGINDISABLED IMAP capability (discussed in section 3.2) only
reduces the potential for passive attacks, it provides no protection
against active attacks. The responsibility remains with the client
to avoid sending a password over a vulnerable channel.
The PLAIN mechanism relies on the TLS encryption layer for security.
When used without TLS, it is vulnerable to a common network
eavesdropping attack. Therefore PLAIN MUST NOT be advertised or used
unless a suitable TLS encryption layer is active or backwards
compatibility dictates otherwise.
When the PLAIN mechanism is used, the server gains the ability to
impersonate the user to all services with the same password
regardless of any encryption provided by TLS or other network privacy
mechanisms. While many other authentication mechanisms have similar
weaknesses, stronger SASL mechanisms such as Kerberos address this
issue. Clients are encouraged to have an operational mode where all
mechanisms which are likely to reveal the user's password to the
server are disabled.
Newman Standards Track [Page 11]
RFC 2595 Using TLS with IMAP, POP3 and ACAP June 1999
The security considerations for TLS apply to STARTTLS and the
security considerations for SASL apply to the PLAIN mechanism.
Additional security requirements are discussed in section 2.
10. References
[ABNF] Crocker, D. and P. Overell, "Augmented BNF for Syntax
Specifications: ABNF", RFC 2234, November 1997.
[ACAP] Newman, C. and J. Myers, "ACAP -- Application
Configuration Access Protocol", RFC 2244, November 1997.
[AUTH] Haller, N. and R. Atkinson, "On Internet Authentication",
RFC 1704, October 1994.
[CRAM-MD5] Klensin, J., Catoe, R. and P. Krumviede, "IMAP/POP
AUTHorize Extension for Simple Challenge/Response", RFC
2195, September 1997.
[IMAP] Crispin, M., "Internet Message Access Protocol - Version
4rev1", RFC 2060, December 1996.
[KEYWORDS] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119, March 1997.
[MIME-SEC] Galvin, J., Murphy, S., Crocker, S. and N. Freed,
"Security Multiparts for MIME: Multipart/Signed and
Multipart/Encrypted", RFC 1847, October 1995.
[POP3] Myers, J. and M. Rose, "Post Office Protocol - Version 3",
STD 53, RFC 1939, May 1996.
[POP3EXT] Gellens, R., Newman, C. and L. Lundblade, "POP3 Extension
Mechanism", RFC 2449, November 1998.
[POP-AUTH] Myers, J., "POP3 AUTHentication command", RFC 1734,
December 1994.
[SASL] Myers, J., "Simple Authentication and Security Layer
(SASL)", RFC 2222, October 1997.
[SMTPTLS] Hoffman, P., "SMTP Service Extension for Secure SMTP over
TLS", RFC 2487, January 1999.
[TLS] Dierks, T. and C. Allen, "The TLS Protocol Version 1.0",
RFC 2246, January 1999.
Newman Standards Track [Page 12]
RFC 2595 Using TLS with IMAP, POP3 and ACAP June 1999
[UTF-8] Yergeau, F., "UTF-8, a transformation format of ISO
10646", RFC 2279, January 1998.
11. Author's Address
Chris Newman
Innosoft International, Inc.
1050 Lakes Drive
West Covina, CA 91790 USA
EMail: chr...@in...
A. Appendix -- Compliance Checklist
An implementation is not compliant if it fails to satisfy one or more
of the MUST requirements for the protocols it implements. An
implementation that satisfies all the MUST and all the SHOULD
requirements for its protocols is said to be "unconditionally
compliant"; one that satisfies all the MUST requirements but not all
the SHOULD requirements for its protocols is said to be
"conditionally compliant".
Rules Section
----- -------
Mandatory-to-implement Cipher Suite 2.1
SHOULD have mode where encryption required 2.2
server SHOULD have mode where TLS not required 2.2
MUST be configurable to refuse all clear-text login
commands or mechanisms 2.3
server SHOULD be configurable to refuse clear-text
login commands on entire server and on per-user basis 2.3
client MUST check server identity 2.4
client MUST use hostname used to open connection 2.4
client MUST NOT use hostname from insecure remote lookup 2.4
client SHOULD support subjectAltName of dNSName type 2.4
client SHOULD ask for confirmation or terminate on fail 2.4
MUST check result of STARTTLS for acceptable privacy 2.5
client MUST NOT issue commands after STARTTLS
until server response and negotiation done 3.1,4,5.1
client MUST discard cached information 3.1,4,5.1,9
client SHOULD re-issue CAPABILITY/CAPA command 3.1,4
IMAP server with STARTTLS MUST implement LOGINDISABLED 3.2
IMAP client MUST NOT issue LOGIN if LOGINDISABLED 3.2
POP server MUST implement POP3 extensions 4
ACAP server MUST re-issue ACAP greeting 5.1
Newman Standards Track [Page 13]
RFC 2595 Using TLS with IMAP, POP3 and ACAP June 1999
client SHOULD warn when session privacy not active and/or
refuse to proceed without acceptable security level 9
SHOULD be configurable to refuse weak mechanisms or
cipher suites 9
The PLAIN mechanism is an optional part of this specification.
However if it is implemented the following rules apply:
Rules Section
----- -------
MUST NOT use PLAIN unless strong encryption active
or backwards compatibility dictates otherwise 6,9
MUST use UTF-8 encoding for characters in PLAIN 6
Newman Standards Track [Page 14]
RFC 2595 Using TLS with IMAP, POP3 and ACAP June 1999
Full Copyright Statement
Copyright (C) The Internet Society (1999). All Rights Reserved.
This document and translations of it may be copied and furnished to
others, and derivative works that comment on or otherwise explain it
or assist in its implementation may be prepared, copied, published
and distributed, in whole or in part, without restriction of any
kind, provided that the above copyright notice and this paragraph are
included on all such copies and derivative works. However, this
document itself may not be modified in any way, such as by removing
the copyright notice or references to the Internet Society or other
Internet organizations, except as needed for the purpose of
developing Internet standards in which case the procedures for
copyrights defined in the Internet Standards process must be
followed, or as required to translate it into languages other than
English.
The limited permissions granted above are perpetual and will not be
revoked by the Internet Society or its successors or assigns.
This document and the information contained herein is provided on an
"AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
Acknowledgement
Funding for the RFC Editor function is currently provided by the
Internet Society.
Newman Standards Track [Page 15]
|
|
From: <ta...@us...> - 2003-08-08 22:28:15
|
Update of /cvsroot/csmaild/csmaild/docs/rfcs/imap
In directory sc8-pr-cvs1:/tmp/cvs-serv16597/docs/rfcs/imap
Added Files:
1731 - Authentication Mechanisms.txt
Log Message:
2595 - Helped with AUTHENTICATE PLAIN command
1731 - Interested read if we want to support more AUTHENTICATE mechanisms
2045 - Defined the BASE64 encoding, which a decoder has been implemented (used in AUTHENTICATE PLAIN)
--- NEW FILE: 1731 - Authentication Mechanisms.txt ---
Network Working Group J. Myers
Request for Comments: 1731 Carnegie Mellon
Category: Standards Track December 1994
IMAP4 Authentication Mechanisms
Status of this Memo
This document specifies an Internet standards track protocol for the
Internet community, and requests discussion and suggestions for
improvements. Please refer to the current edition of the "Internet
Official Protocol Standards" (STD 1) for the standardization state
and status of this protocol. Distribution of this memo is unlimited.
1. Introduction
The Internet Message Access Protocol, Version 4 [IMAP4] contains the
AUTHENTICATE command, for identifying and authenticating a user to an
IMAP4 server and for optionally negotiating a protection mechanism
for subsequent protocol interactions. This document describes
several authentication mechanisms for use by the IMAP4 AUTHENTICATE
command.
2. Kerberos version 4 authentication mechanism
The authentication type associated with Kerberos version 4 is
"KERBEROS_V4".
The data encoded in the first ready response contains a random 32-bit
number in network byte order. The client should respond with a
Kerberos ticket and an authenticator for the principal
"imap.hostname@realm", where "hostname" is the first component of the
host name of the server with all letters in lower case and where
"realm" is the Kerberos realm of the server. The encrypted checksum
field included within the Kerberos authenticator should contain the
server provided 32-bit number in network byte order.
Upon decrypting and verifying the ticket and authenticator, the
server should verify that the contained checksum field equals the
original server provided random 32-bit number. Should the
verification be successful, the server must add one to the checksum
and construct 8 octets of data, with the first four octets containing
the incremented checksum in network byte order, the fifth octet
containing a bit-mask specifying the protection mechanisms supported
by the server, and the sixth through eighth octets containing, in
Myers [Page 1]
RFC 1731 IMAP4 Authentication Mechanisms December 1994
network byte order, the maximum cipher-text buffer size the server is
able to receive. The server must encrypt the 8 octets of data in the
session key and issue that encrypted data in a second ready response.
The client should consider the server authenticated if the first four
octets the un-encrypted data is equal to one plus the checksum it
previously sent.
The client must construct data with the first four octets containing
the original server-issued checksum in network byte order, the fifth
octet containing the bit-mask specifying the selected protection
mechanism, the sixth through eighth octets containing in network byte
order the maximum cipher-text buffer size the client is able to
receive, and the following octets containing a user name string. The
client must then append from one to eight octets so that the length
of the data is a multiple of eight octets. The client must then PCBC
encrypt the data with the session key and respond to the second ready
response with the encrypted data. The server decrypts the data and
verifies the contained checksum. The username field identifies the
user for whom subsequent IMAP operations are to be performed; the
server must verify that the principal identified in the Kerberos
ticket is authorized to connect as that user. After these
verifications, the authentication process is complete.
The protection mechanisms and their corresponding bit-masks are as
follows:
1 No protection mechanism
2 Integrity (krb_mk_safe) protection
4 Privacy (krb_mk_priv) protection
EXAMPLE: The following are two Kerberos version 4 login scenarios
(note that the line breaks in the sample authenticators are for
editorial clarity and are not in real authenticators)
S: * OK IMAP4 Server
C: A001 AUTHENTICATE KERBEROS_V4
S: + AmFYig==
C: BAcAQU5EUkVXLkNNVS5FRFUAOCAsho84kLN3/IJmrMG+25a4DT
+nZImJjnTNHJUtxAA+o0KPKfHEcAFs9a3CL5Oebe/ydHJUwYFd
WwuQ1MWiy6IesKvjL5rL9WjXUb9MwT9bpObYLGOKi1Qh
S: + or//EoAADZI=
C: DiAF5A4gA+oOIALuBkAAmw==
S: A001 OK Kerberos V4 authentication successful
Myers [Page 2]
RFC 1731 IMAP4 Authentication Mechanisms December 1994
S: * OK IMAP4 Server
C: A001 AUTHENTICATE KERBEROS_V4
S: + gcfgCA==
C: BAcAQU5EUkVXLkNNVS5FRFUAOCAsho84kLN3/IJmrMG+25a4DT
+nZImJjnTNHJUtxAA+o0KPKfHEcAFs9a3CL5Oebe/ydHJUwYFd
WwuQ1MWiy6IesKvjL5rL9WjXUb9MwT9bpObYLGOKi1Qh
S: A001 NO Kerberos V4 authentication failed
3. GSSAPI authentication mechanism
The authentication type associated with all mechanisms employing the
GSSAPI [RFC1508] is "GSSAPI".
The first ready response issued by the server contains no data. The
client should call GSS_Init_sec_context, passing in 0 for
input_context_handle (initially) and a targ_name equal to output_name
from GSS_Import_Name called with input_name_type of NULL and
input_name_string of "SERVICE:imap@hostname" where "hostname" is the
fully qualified host name of the server with all letters in lower
case. The client must then respond with the resulting output_token.
If GSS_Init_sec_context returns GSS_CONTINUE_NEEDED, then the client
should expect the server to issue a token in a subsequent ready
response. The client must pass the token to another call to
GSS_Init_sec_context.
If GSS_Init_sec_context returns GSS_COMPLETE, then the client should
respond with any resulting output_token. If there is no
output_token, the client should respond with no data. The client
should then expect the server to issue a token in a subsequent ready
response. The client should pass this token to GSS_Unseal and
interpret the first octet of resulting cleartext as a bit-mask
specifying the protection mechanisms supported by the server and the
second through fourth octets as the maximum size output_message to
send to the server. The client should construct data, with the first
octet containing the bit-mask specifying the selected protection
mechanism, the second through fourth octets containing in network
byte order the maximum size output_message the client is able to
receive, and the remaining octets containing a user name string. The
client must pass the data to GSS_Seal with conf_flag set to FALSE,
and respond with the generated output_message. The client can then
consider the server authenticated.
The server must issue a ready response with no data and pass the
resulting client supplied token to GSS_Accept_sec_context as
input_token, setting acceptor_cred_handle to NULL (for "use default
credentials"), and 0 for input_context_handle (initially). If
GSS_Accept_sec_context returns GSS_CONTINUE_NEEDED, the server should
Myers [Page 3]
RFC 1731 IMAP4 Authentication Mechanisms December 1994
return the generated output_token to the client in a ready response
and pass the resulting client supplied token to another call to
GSS_Accept_sec_context.
If GSS_Accept_sec_context returns GSS_COMPLETE, then if an
output_token is returned, the server should return it to the client
in a ready response and expect a reply from the client with no data.
Whether or not an output_token was returned, the server then should
then construct 4 octets of data, with the first octet containing a
bit-mask specifying the protection mechanisms supported by the server
and the second through fourth octets containing in network byte order
the maximum size output_token the server is able to receive. The
server must then pass the plaintext to GSS_Seal with conf_flag set to
FALSE and issue the generated output_message to the client in a ready
response. The server must then pass the resulting client supplied
token to GSS_Unseal and interpret the first octet of resulting
cleartext as the bit-mask for the selected protection mechanism, the
second through fourth octets as the maximum size output_message to
send to the client, and the remaining octets as the user name. Upon
verifying the src_name is authorized to authenticate as the user
name, The server should then consider the client authenticated.
The protection mechanisms and their corresponding bit-masks are as
follows:
1 No protection mechanism
2 Integrity protection.
Sender calls GSS_Seal with conf_flag set to FALSE
4 Privacy protection.
Sender calls GSS_Seal with conf_flag set to TRUE
4. S/Key authentication mechanism
The authentication type associated with S/Key [SKEY] is "SKEY".
The first ready response issued by the server contains no data. The
client responds with the user name string.
The data encoded in the second ready response contains the decimal
sequence number followed by a single space and the seed string for
the indicated user. The client responds with the one-time-password,
as either a 64-bit value in network byte order or encoded in the "six
English words" format.
Upon successful verification of the one-time-password, the server
should consider the client authenticated.
Myers [Page 4]
RFC 1731 IMAP4 Authentication Mechanisms December 1994
S/Key authentication does not provide for any protection mechanisms.
EXAMPLE: The following are two S/Key login scenarios.
S: * OK IMAP4 Server
C: A001 AUTHENTICATE SKEY
S: +
C: bW9yZ2Fu
S: + OTUgUWE1ODMwOA==
C: Rk9VUiBNQU5OIFNPT04gRklSIFZBUlkgTUFTSA==
S: A001 OK S/Key authentication successful
S: * OK IMAP4 Server
C: A001 AUTHENTICATE SKEY
S: +
C: c21pdGg=
S: + OTUgUWE1ODMwOA==
C: BsAY3g4gBNo=
S: A001 NO S/Key authentication failed
5. References
[IMAP4] Crispin, M., "Internet Message Access Protocol - Version 4",
RFC 1730, University of Washington, December 1994.
[RFC1508] Linn, J., "Generic Security Service Application Program
Interface", RFC 1508, Geer Zolot Associates, September 1993.
[SKEY] Haller, Neil M. "The S/Key One-Time Password System",
Bellcore, Morristown, New Jersey, October 1993,
thumper.bellcore.com:pub/nmh/docs/ISOC.symp.ps
Myers [Page 5]
RFC 1731 IMAP4 Authentication Mechanisms December 1994
6. Security Considerations
Security issues are discussed throughout this memo.
7. Author's Address
John G. Myers
Carnegie-Mellon University
5000 Forbes Ave.
Pittsburgh PA, 15213-3890
EMail: jg...@cm...
Myers [Page 6]
|
|
From: <ta...@us...> - 2003-08-08 22:26:32
|
Update of /cvsroot/csmaild/csmaild/docs/rfcs/mime In directory sc8-pr-cvs1:/tmp/cvs-serv16268/mime Log Message: Directory /cvsroot/csmaild/csmaild/docs/rfcs/mime added to the repository |
|
From: <ta...@us...> - 2003-08-05 02:40:42
|
Update of /cvsroot/csmaild/csmaild/src/Imap/Commands
In directory sc8-pr-cvs1:/tmp/cvs-serv9078/src/Imap/Commands
Modified Files:
CheckCommand.cs CloseCommand.cs
Log Message:
Index: CheckCommand.cs
===================================================================
RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/CheckCommand.cs,v
retrieving revision 1.5
retrieving revision 1.6
diff -C2 -d -r1.5 -r1.6
*** CheckCommand.cs 29 Jul 2003 00:46:28 -0000 1.5
--- CheckCommand.cs 5 Aug 2003 02:40:40 -0000 1.6
***************
*** 14,18 ****
override protected void InternalProcess()
{
! //return false;
}
}
--- 14,18 ----
override protected void InternalProcess()
{
! mConnection.SendTaggedMessage("OK CHECK completed");
}
}
Index: CloseCommand.cs
===================================================================
RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/CloseCommand.cs,v
retrieving revision 1.5
retrieving revision 1.6
diff -C2 -d -r1.5 -r1.6
*** CloseCommand.cs 29 Jul 2003 00:46:28 -0000 1.5
--- CloseCommand.cs 5 Aug 2003 02:40:40 -0000 1.6
***************
*** 14,18 ****
override protected void InternalProcess()
{
! //return false;
}
}
--- 14,28 ----
override protected void InternalProcess()
{
! for(uint idx = 1; idx <= mConnection.Mailbox.Messages.Count;)
! {
! if(mConnection.Mailbox.Messages.BySeq(idx).Deleted)
! mConnection.Mailbox.Messages.Delete(idx);
! else
! ++idx;
! }
!
! mConnection.State = ImapConnectionState.Authenticated;
!
! mConnection.SendTaggedMessage("OK CLOSE completed");
}
}
|
|
From: <ta...@us...> - 2003-08-05 01:50:27
|
Update of /cvsroot/csmaild/csmaild/src/Imap
In directory sc8-pr-cvs1:/tmp/cvs-serv1562/src/Imap
Modified Files:
Connection.cs
Log Message:
Oops, forget this fix
Index: Connection.cs
===================================================================
RCS file: /cvsroot/csmaild/csmaild/src/Imap/Connection.cs,v
retrieving revision 1.10
retrieving revision 1.11
diff -C2 -d -r1.10 -r1.11
*** Connection.cs 5 Aug 2003 01:42:10 -0000 1.10
--- Connection.cs 5 Aug 2003 01:50:25 -0000 1.11
***************
*** 199,203 ****
try
{
! if(mCurrentCommand.Process(mCurrentArguments, this, mCurrentCommandUID))
{
}
--- 199,203 ----
try
{
! if(mCurrentCommand.Process())
{
}
|
|
From: <ta...@us...> - 2003-08-05 01:50:27
|
Update of /cvsroot/csmaild/csmaild/src/Imap/Commands
In directory sc8-pr-cvs1:/tmp/cvs-serv1562/src/Imap/Commands
Modified Files:
ImapCommand.cs
Log Message:
Oops, forget this fix
Index: ImapCommand.cs
===================================================================
RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/ImapCommand.cs,v
retrieving revision 1.12
retrieving revision 1.13
diff -C2 -d -r1.12 -r1.13
*** ImapCommand.cs 3 Aug 2003 01:08:38 -0000 1.12
--- ImapCommand.cs 5 Aug 2003 01:50:24 -0000 1.13
***************
*** 1,3 ****
! using Imap.NetworkManager;
using System;
--- 1,3 ----
! using Common.NetworkManager;
using System;
***************
*** 22,30 ****
private ArgumentType[] mArgumentTypes;
protected bool mUidCalled;
- protected string mUnparsedArguments;
- protected int mUnparsedArgumentIdx;
protected object[] mParsedArguments;
-
protected ImapConnection mConnection;
#endregion
--- 22,30 ----
private ArgumentType[] mArgumentTypes;
+ private string mUnparsedArguments;
+ private int mUnparsedArgumentIdx;
+
protected bool mUidCalled;
protected object[] mParsedArguments;
protected ImapConnection mConnection;
#endregion
***************
*** 127,132 ****
ParenthesizedListAtom = 66, // paren(64) + atom(2)
Optional = 128,
! OptionalParenthesizedList = 192,
! OptionalQuotedString = 129
}
--- 127,133 ----
ParenthesizedListAtom = 66, // paren(64) + atom(2)
Optional = 128,
! OptionalParenthesizedList = 192, // optional(128) + paren(64)
! OptionalQuotedString = 129, // optional(128) + quoted(1)
! FlagListFlag = 256,
}
***************
*** 182,185 ****
--- 183,188 ----
else if((argType & ArgumentType.SequenceSet) == ArgumentType.SequenceSet) // sequence set
return ParseSequenceSet();
+ else if((argType & ArgumentType.FlagListFlag) == ArgumentType.FlagListFlag) // flag-list / flag
+ return ParseFlagListFlag();
return null;
}
***************
*** 202,205 ****
--- 205,233 ----
return str;
}
+
+ private object ParseFlagListFlag()
+ {
+ if(mUnparsedArguments[mUnparsedArgumentIdx] != '(')
+ return ParseWhileGood(CommandPart.FlagChar);
+ else
+ {
+ ParenthesizedList list = new ParenthesizedList();
+ ++mUnparsedArgumentIdx;
+
+ while(true)
+ {
+ list.AddItem(ParseWhileGood(CommandPart.FlagChar));
+ if(mUnparsedArguments[mUnparsedArgumentIdx] == ' ')
+ ++mUnparsedArgumentIdx;
+ else if(mUnparsedArguments[mUnparsedArgumentIdx] == ')')
+ break;
+ else
+ return null;
+ }
+
+ ++mUnparsedArgumentIdx;
+ return list;
+ }
+ }
#endregion
***************
*** 284,288 ****
sequenceSetString = mUnparsedArguments.Substring(mUnparsedArgumentIdx, endIdx - mUnparsedArgumentIdx);
! string[] parts = sequenceSetString.Split(' ');
foreach(string part in parts)
{
--- 312,316 ----
sequenceSetString = mUnparsedArguments.Substring(mUnparsedArgumentIdx, endIdx - mUnparsedArgumentIdx);
! string[] parts = sequenceSetString.Split(',');
foreach(string part in parts)
{
***************
*** 517,521 ****
if(mUnparsedArguments[mUnparsedArgumentIdx] == ' ')
++mUnparsedArgumentIdx;
! list.AddItem(ParseArgument(ArgumentType.AString));
if(mUnparsedArguments[mUnparsedArgumentIdx] == ')')
--mUnparsedArgumentIdx;
--- 545,552 ----
if(mUnparsedArguments[mUnparsedArgumentIdx] == ' ')
++mUnparsedArgumentIdx;
! string arg = ParseArgument(ArgumentType.AString) as string;
! if(arg == null)
! return null;
! list.AddItem(arg);
if(mUnparsedArguments[mUnparsedArgumentIdx] == ')')
--mUnparsedArgumentIdx;
|
Update of /cvsroot/csmaild/csmaild/src/Imap/Commands
In directory sc8-pr-cvs1:/tmp/cvs-serv1336/src/Imap/Commands
Modified Files:
CommandPart.cs CopyCommand.cs ExpungeCommand.cs
FetchCommand.cs LoginCommand.cs LogoutCommand.cs
NoopCommand.cs StatusCommand.cs StoreCommand.cs
Log Message:
COPY command now halts appropriate on UID calls
A new argument type of FlagList has been added
EXPUNGE should be completed
FETCH has a better start (works for most common calls from thunderbird and OE), code still sucks
STORE command nearly finished
Small fixes to make some other commands work in OE (OE sucks as an IMAP client, ugh)
Index: CommandPart.cs
===================================================================
RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/CommandPart.cs,v
retrieving revision 1.4
retrieving revision 1.5
diff -C2 -d -r1.4 -r1.5
*** CommandPart.cs 26 Jul 2003 23:09:25 -0000 1.4
--- CommandPart.cs 5 Aug 2003 01:49:28 -0000 1.5
***************
*** 27,30 ****
--- 27,32 ----
// ATOM-CHAR = <any CHAR except atom-specials>
public static string AtomChar = RemoveChars(CHAR8, AtomSpecials);
+ // flag-char
+ public static string FlagChar = CombineChars(AtomChar, "\\");
// ASTRING-CHAR = ATOM-CHAR / resp-specials
public static string AStringChar = CombineChars(AtomChar, RespSpecials);
Index: CopyCommand.cs
===================================================================
RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/CopyCommand.cs,v
retrieving revision 1.6
retrieving revision 1.7
diff -C2 -d -r1.6 -r1.7
*** CopyCommand.cs 3 Aug 2003 01:08:38 -0000 1.6
--- CopyCommand.cs 5 Aug 2003 01:49:28 -0000 1.7
***************
*** 30,34 ****
for(uint idx = rng.Low; idx <= rng.High; ++idx)
{
! if(idx > source.MessageCount)
break;
--- 30,36 ----
for(uint idx = rng.Low; idx <= rng.High; ++idx)
{
! if(!mUidCalled && idx > source.MessageCount)
! break;
! if(mUidCalled && idx > source.NextUniqueId)
break;
***************
*** 45,49 ****
}
! mConnection.SendTaggedMessage("OK Copy completed");
}
}
--- 47,51 ----
}
! mConnection.SendTaggedMessage("OK COPY completed");
}
}
Index: ExpungeCommand.cs
===================================================================
RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/ExpungeCommand.cs,v
retrieving revision 1.5
retrieving revision 1.6
diff -C2 -d -r1.5 -r1.6
*** ExpungeCommand.cs 29 Jul 2003 00:46:28 -0000 1.5
--- ExpungeCommand.cs 5 Aug 2003 01:49:28 -0000 1.6
***************
*** 3,9 ****
namespace Imap.Commands
{
- /// <summary>
- /// Summary description for ExpungeCommand.
- /// </summary>
public class ExpungeCommand : ImapCommand
{
--- 3,6 ----
***************
*** 14,18 ****
override protected void InternalProcess()
{
! //return false;
}
}
--- 11,26 ----
override protected void InternalProcess()
{
! for(uint idx = 1; idx <= mConnection.Mailbox.Messages.Count;)
! {
! if(mConnection.Mailbox.Messages.BySeq(idx).Deleted)
! {
! mConnection.Mailbox.Messages.Delete(idx);
! mConnection.SendUntaggedMessage(idx.ToString() + " EXPUNGE");
! }
! else
! ++idx;
! }
!
! mConnection.SendTaggedMessage("OK EXPUNGE completed");
}
}
Index: FetchCommand.cs
===================================================================
RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/FetchCommand.cs,v
retrieving revision 1.7
retrieving revision 1.8
diff -C2 -d -r1.7 -r1.8
*** FetchCommand.cs 29 Jul 2003 00:46:28 -0000 1.7
--- FetchCommand.cs 5 Aug 2003 01:49:28 -0000 1.8
***************
*** 1,4 ****
--- 1,10 ----
+ using Common;
+ using rfc = Common.Rfc2822;
+
using System;
using System.Collections;
+ using System.Collections.Specialized;
+ using System.IO;
+ using System.Text;
using System.Text.RegularExpressions;
***************
*** 19,24 ****
override protected void InternalProcess()
{
! mConnection.SendUntaggedMessage(mParsedArguments[0].ToString());
! mConnection.SendUntaggedMessage(mParsedArguments[1].ToString());
if(mParsedArguments[1] is string)
--- 25,29 ----
override protected void InternalProcess()
{
! SequenceSet msgSet = mParsedArguments[0] as SequenceSet;
if(mParsedArguments[1] is string)
***************
*** 34,38 ****
if(parts != null)
{
! mConnection.SendTaggedMessage("OK");
}
else
--- 39,73 ----
if(parts != null)
{
! if(mUidCalled)
! parts.Uid = true;
!
! SequenceSet.SequenceRange rng = msgSet.FirstRange;
! while(rng != null)
! {
! for(uint idx = rng.Low; idx <= rng.High; ++idx)
! {
! if(!mUidCalled && idx > mConnection.Mailbox.MessageCount)
! break;
! if(mUidCalled && idx > mConnection.Mailbox.NextUniqueId)
! break;
!
! Message msg = null;
! if(mUidCalled)
! msg = mConnection.Mailbox.Messages.ByUID(idx);
! else
! msg = mConnection.Mailbox.Messages.BySeq(idx);
!
! if(msg != null)
! {
! if(mUidCalled)
! HandleFetch(mConnection.Mailbox.Messages.GetSeqForUID(idx), msg, parts);
! else
! HandleFetch(idx, msg, parts);
! }
! }
! rng = rng.NextRange;
! }
!
! mConnection.SendTaggedMessage("OK FETCH completed");
}
else
***************
*** 40,43 ****
--- 75,339 ----
}
+ private void HandleFetch(uint sequenceNumber, Message msg, FetchParts parts)
+ {
+ ParenthesizedList output = new ParenthesizedList();
+
+ if(parts.Body)
+ {
+ throw new NotImplementedException("Body");
+ }
+
+ if(parts.BodySections.Count != 0)
+ {
+ for(int idx = 0; idx < parts.BodySections.Count; ++idx)
+ HandleFetchBodySection(output, msg, (FetchParts.BodySectionPart)parts.BodySections[idx]);
+ }
+
+ if(parts.BodyStructure)
+ {
+ throw new NotImplementedException("BodyStructure");
+ }
+
+ if(parts.Envelope)
+ {
+ output.AddItem("ENVELOPE");
+ output.AddItem(GenerateEnvelope(msg));
+ }
+
+ if(parts.Flags)
+ {
+ output.AddItem("FLAGS");
+ output.AddItem(GenerateFlags(msg));
+ }
+
+ if(parts.InternalDate)
+ {
+ output.AddItem("INTERNALDATE");
+ string internalDate = msg.InternalDate.ToString("\\\"dd\\-MMM\\-yyyy\\ hh\\:mm\\:ss\\ zzzz\\\"");
+ // stupid .NET won't let me customize the zone format, it puts a colon, which IMAP doesn't want
+ // so I have to remove it
+ internalDate = internalDate.Substring(0, internalDate.Length-4) + internalDate.Substring(internalDate.Length-3);
+ output.AddItem(internalDate);
+ }
+
+ if(parts.Rfc822)
+ {
+ output.AddItem("RFC822");
+ TextReader rdr = msg.RawRfc2822Message;
+ string msgText = rdr.ReadToEnd();
+ output.AddItem("{" + msgText.Length + "}\r\n" + msgText);
+ rdr.Close();
+ }
+
+ if(parts.Rfc822Header)
+ {
+ throw new NotImplementedException("Rfc822Header");
+ }
+
+ if(parts.Rfc822Size)
+ {
+ output.AddItem("RFC822.SIZE");
+ output.AddItem(msg.Size);
+ }
+
+ if(parts.Rfc822Text)
+ {
+ throw new NotImplementedException("Rfc822Text");
+ }
+
+ if(parts.Uid)
+ {
+ output.AddItem("UID");
+ output.AddItem(msg.UniqueId);
+ }
+
+ mConnection.SendUntaggedMessage(sequenceNumber + " FETCH " + output);
+ }
+
+ private void HandleFetchBodySection(ParenthesizedList output, Message msg, FetchParts.BodySectionPart part)
+ {
+ string name = "BODY[";
+ string msgText = string.Empty;
+
+ if(part.BodySectionParts.Count != 0)
+ {
+ throw new NotImplementedException("BodySectionParts");
+ // public ArrayList BodySectionParts = new ArrayList();
+ // public bool BodySectionPartHeader;
+ // public bool BodySectionPartMime;
+ // public bool BodySectionPartText;
+ // public ArrayList BodySectionPartHeaderFields = new ArrayList();
+ // public ArrayList BodySectionPartHeaderFieldsNot = new ArrayList();
+ }
+ else if(part.BodySectionHeader)
+ {
+ throw new NotImplementedException("BodySectionHeader");
+ // public bool BodySectionHeader; // BODY[HEADER]
+ }
+ else if(part.BodySectionText)
+ {
+ throw new NotImplementedException("BodySectionText");
+ // public bool BodySectionText; // BODY[TEXT]
+ }
+ else if(part.BodySectionHeaderFields.Count != 0)
+ {
+ name += "HEADER.FIELDS " + part.BodySectionHeaderFields.ToString();
+ for(int idx = 0; idx < part.BodySectionHeaderFields.Count; ++idx)
+ {
+ rfc.HeaderField[] flds = msg.Rfc2822Message.Headers[part.BodySectionHeaderFields[idx].DataItem as String];
+ if(flds != null)
+ {
+ for(int iidx = 0; iidx < flds.Length; ++iidx)
+ msgText += flds[iidx].ToString() + "\r\n";
+ }
+ }
+ msgText += "\r\n";
+ // public ArrayList BodySectionHeaderFields = new ArrayList();
+ }
+ else if(part.BodySectionHeaderFieldsNot.Count != 0)
+ {
+ throw new NotImplementedException("BodySectionHeaderFieldsNot");
+ // public ArrayList BodySectionHeaderFieldsNot = new ArrayList();
+ }
+ else
+ {
+ TextReader rdr = msg.RawRfc2822Message;
+ msgText = rdr.ReadToEnd();
+ rdr.Close();
+ }
+
+ name += "]";
+
+ if(part.BodySectionOffsetLength)
+ {
+ name += "<" + part.BodySectionOffset.ToString() + ">";
+ msgText = msgText.Substring((int)part.BodySectionOffset, (int)part.BodySectionLength);
+ }
+
+ output.AddItem(name);
+ output.AddItem("{" + msgText.Length + "}\r\n" + msgText);
+ }
+
+ private ParenthesizedList GenerateEnvelope(Message msg)
+ {
+ ParenthesizedList list = new ParenthesizedList();
+
+ rfc.HeaderFieldList hdrs = msg.Rfc2822Message.Headers;
+
+ list.AddItem(MakeNILOrQuote(hdrs["Date"][0].FieldValueString));
+ list.AddItem(MakeNILOrQuote(hdrs["Subject"][0].FieldValueString));
+ list.AddItem(MakeAddressList(hdrs["From"][0]));
+ if(hdrs["Sender"] != null)
+ list.AddItem(MakeAddressList(hdrs["Sender"][0]));
+ else
+ list.AddItem(MakeNIL(null));
+ if(hdrs["Reply-To"] != null)
+ list.AddItem(MakeAddressList(hdrs["Reply-To"][0]));
+ else
+ list.AddItem(MakeNIL(null));
+ list.AddItem(MakeAddressList(hdrs["To"][0]));
+ if(hdrs["Cc"] != null)
+ list.AddItem(MakeAddressList(hdrs["Cc"][0]));
+ else
+ list.AddItem(MakeNIL(null));
+ if(hdrs["Bcc"] != null)
+ list.AddItem(MakeAddressList(hdrs["Bcc"][0]));
+ else
+ list.AddItem(MakeNIL(null));
+ if(hdrs["In-Reply-To"] != null)
+ list.AddItem(MakeNIL(hdrs["In-Reply-To"][0].FieldValueString));
+ else
+ list.AddItem(MakeNIL(null));
+ list.AddItem(MakeNILOrQuote(hdrs["Message-ID"][0].FieldValueString));
+ return list;
+ }
+
+ private ParenthesizedList GenerateFlags(Message msg)
+ {
+ ParenthesizedList list = new ParenthesizedList();
+ if(msg.Answered)
+ list.AddItem("\\Answered");
+ if(msg.Deleted)
+ list.AddItem("\\Deleted");
+ if(msg.Draft)
+ list.AddItem("\\Draft");
+ if(msg.Flagged)
+ list.AddItem("\\Flagged");
+ if(msg.Recent)
+ list.AddItem("\\Recent");
+ if(msg.Seen)
+ list.AddItem("\\Seen");
+ return list;
+ }
+
+
+ #region MakeAddressList
+ private string MakeAddressList(rfc.HeaderField hdr)
+ {
+ string addrList = "(";
+ rfc.AddressList list = hdr.FieldValueAddressList;
+
+ for(int idx = 0; idx < list.Count; ++idx)
+ addrList += MakeAddressList(list[idx]);
+
+ return addrList + ")";
+ }
+
+ private string MakeAddressList(rfc.Address addr)
+ {
+ if(addr is rfc.AddressMailbox)
+ return MakeAddressList(addr as rfc.AddressMailbox);
+ else
+ return MakeAddressList(addr as rfc.AddressGroup);
+ }
+
+ private string MakeAddressList(rfc.AddressMailbox box)
+ {
+ // TODO: understand the 2nd argument better and figure out what to put there
+ return MakeAddressList(box.DisplayName, null, box.LocalPart, box.Host);
+ }
+
+ private string MakeAddressList(rfc.AddressGroup grp)
+ {
+ // this is a shot in the dark as neither OE or ThunderBird will allow me to create a "group"
+ string rv = MakeAddressList(null, null, grp.DisplayName, null);
+
+ for(int idx = 0; idx < grp.List.Count; ++idx)
+ rv += MakeAddressList(grp.List[idx]);
+
+ rv += MakeAddressList(null, null, null, null);
+
+ return rv;
+ }
+
+ private string MakeAddressList(string name, string adl, string mailbox, string host)
+ {
+ return string.Format("({0} {1} {2} {3})", MakeNILOrQuote(name),
+ MakeNILOrQuote(adl),
+ MakeNILOrQuote(mailbox),
+ MakeNILOrQuote(host));
+ }
+
+ #endregion
+
+ #region NIL and quoting helpers
+ private string MakeNIL(string input)
+ {
+ if(input == null)
+ return "NIL";
+ else
+ return input;
+ }
+
+ private string MakeNILOrQuote(string input)
+ {
+ if(input == null)
+ return MakeNIL(input);
+ else
+ return "\"" + input + "\"";
+ }
+ #endregion
+
+ #region Parse help
private FetchParts ParseList()
{
***************
*** 46,49 ****
--- 342,359 ----
{
string item = mList[mListIdx].ToString();
+
+ if(item.StartsWith("BODY["))
+ {
+ rv.BodyPeekNo = true;
+ rv.BodyPeek = false;
+ }
+
+ if(item.IndexOf(".PEEK[") != -1)
+ {
+ item = item.Replace(".PEEK[", "[");
+ if(!rv.BodyPeekNo)
+ rv.BodyPeek = true;
+ }
+
switch(item)
{
***************
*** 99,117 ****
rv.Uid = true;
break;
default:
int sectionIdx;
if(item.StartsWith("BODY["))
- {
- rv.Body = true;
sectionIdx = 5;
- }
- else if(item.StartsWith("BODY.PEEK["))
- {
- rv.BodyPeek = true;
- sectionIdx = 10;
- }
else
return null;
! return ParseBodySection(rv, item.Substring(sectionIdx));
}
}
--- 409,446 ----
rv.Uid = true;
break;
+ case "RFC822":
+ rv.Rfc822 = true;
+ break;
+ case "BODY[]":
+ {
+ FetchParts.BodySectionPart part = new FetchParts.BodySectionPart();
+ rv.BodySections.Add(part);
+ }
+ break;
+ case "BODY[HEADER]":
+ {
+ FetchParts.BodySectionPart part = new FetchParts.BodySectionPart();
+ part.BodySectionHeader = true;
+ rv.BodySections.Add(part);
+ }
+ break;
+ case "BODY[TEXT]":
+ {
+ FetchParts.BodySectionPart part = new FetchParts.BodySectionPart();
+ part.BodySectionText = true;
+ rv.BodySections.Add(part);
+ }
+ break;
default:
+ {
int sectionIdx;
if(item.StartsWith("BODY["))
sectionIdx = 5;
else
return null;
! FetchParts.BodySectionPart part = ParseBodySection(item.Substring(sectionIdx));
! rv.BodySections.Add(part);
! break;
! }
}
}
***************
*** 119,124 ****
}
! private FetchParts ParseBodySection(FetchParts rv, string section)
{
int idx = 0;
if(CommandPart.DigitNz.IndexOf(section[idx]) != -1) // a section part
--- 448,454 ----
}
! private FetchParts.BodySectionPart ParseBodySection(string section)
{
+ FetchParts.BodySectionPart bodySectionPart = new FetchParts.BodySectionPart();
int idx = 0;
if(CommandPart.DigitNz.IndexOf(section[idx]) != -1) // a section part
***************
*** 130,134 ****
if(section[idx] == '.') // moving on to the next section
{
! rv.BodySectionParts.Add(part);
part = 0;
}
--- 460,464 ----
if(section[idx] == '.') // moving on to the next section
{
! bodySectionPart.BodySectionParts.Add(part);
part = 0;
}
***************
*** 163,180 ****
{
case "HEADER]":
! rv.BodySectionPartHeader = true;
idx += 6;
break;
case "TEXT]":
! rv.BodySectionPartText = true;
idx += 4;
break;
case "MIME]":
! rv.BodySectionPartMime = true;
idx += 4;
break;
case "HEADER.FIELDS":
++mListIdx; // move into header list
! if(!ParseHeaderList(rv.BodySectionPartHeaderFields))
return null;
section = mList[++mListIdx].ToString();
--- 493,510 ----
{
case "HEADER]":
! bodySectionPart.BodySectionPartHeader = true;
idx += 6;
break;
case "TEXT]":
! bodySectionPart.BodySectionPartText = true;
idx += 4;
break;
case "MIME]":
! bodySectionPart.BodySectionPartMime = true;
idx += 4;
break;
case "HEADER.FIELDS":
++mListIdx; // move into header list
! if(!ParseHeaderList(bodySectionPart.BodySectionPartHeaderFields))
return null;
section = mList[++mListIdx].ToString();
***************
*** 183,187 ****
case "HEADER.FIELDS.NOT":
++mListIdx; // move into header list
! if(!ParseHeaderList(rv.BodySectionPartHeaderFieldsNot))
return null;
section = mList[++mListIdx].ToString();
--- 513,517 ----
case "HEADER.FIELDS.NOT":
++mListIdx; // move into header list
! if(!ParseHeaderList(bodySectionPart.BodySectionPartHeaderFieldsNot))
return null;
section = mList[++mListIdx].ToString();
***************
*** 204,218 ****
switch(left)
{
- case "HEADER]":
- rv.BodySectionPartHeader = true;
- idx += 6;
- break;
- case "TEXT]":
- rv.BodySectionPartText = true;
- idx += 4;
- break;
case "HEADER.FIELDS":
++mListIdx; // move into header list
! if(!ParseHeaderList(rv.BodySectionPartHeaderFields))
return null;
section = mList[++mListIdx].ToString();
--- 534,540 ----
switch(left)
{
case "HEADER.FIELDS":
++mListIdx; // move into header list
! if(!ParseHeaderList(bodySectionPart.BodySectionHeaderFields))
return null;
section = mList[++mListIdx].ToString();
***************
*** 225,229 ****
case "HEADER.FIELDS.NOT":
++mListIdx; // move into header list
! if(!ParseHeaderList(rv.BodySectionPartHeaderFieldsNot))
return null;
section = mList[++mListIdx].ToString();
--- 547,551 ----
case "HEADER.FIELDS.NOT":
++mListIdx; // move into header list
! if(!ParseHeaderList(bodySectionPart.BodySectionHeaderFieldsNot))
return null;
section = mList[++mListIdx].ToString();
***************
*** 253,259 ****
// TODO: some where we need to make sure that this isn't bigger then uint.Max (4,294,967,295) before we put it into the variable
! rv.BodySectionOffsetLength = true;
! rv.BodySectionOffset = uint.Parse(match.Groups[1].ToString());
! rv.BodySectionLength = uint.Parse(match.Groups[2].ToString());
}
else // if their is more, and it's not this, oops
--- 575,581 ----
// TODO: some where we need to make sure that this isn't bigger then uint.Max (4,294,967,295) before we put it into the variable
! bodySectionPart.BodySectionOffsetLength = true;
! bodySectionPart.BodySectionOffset = uint.Parse(match.Groups[1].ToString());
! bodySectionPart.BodySectionLength = uint.Parse(match.Groups[2].ToString());
}
else // if their is more, and it's not this, oops
***************
*** 261,268 ****
}
! return rv;
}
! private bool ParseHeaderList(ArrayList destinationList)
{
if(mList[mListIdx].IsNestedList)
--- 583,590 ----
}
! return bodySectionPart;
}
! private bool ParseHeaderList(ParenthesizedList destinationList)
{
if(mList[mListIdx].IsNestedList)
***************
*** 274,278 ****
return false;
else
! destinationList.Add(headerList[idx].ToString());
}
return true;
--- 596,600 ----
return false;
else
! destinationList.AddItem(headerList[idx].ToString());
}
return true;
***************
*** 284,350 ****
public class FetchParts
{
! public bool Body;
! public bool BodyPeek;
!
! public bool BodySectionOffsetLength;
! public uint BodySectionOffset;
! public uint BodySectionLength;
!
! private ArrayList mBodySectionParts; // TODO: make this a strongly typed for ints
! public ArrayList BodySectionParts
! {
! get
! {
! if(mBodySectionParts == null)
! mBodySectionParts = new ArrayList();
! return mBodySectionParts;
! }
! }
! public bool BodySectionPartHeader;
! public bool BodySectionPartMime;
! public bool BodySectionPartText;
! private ArrayList mBodySectionPartHeaderFields; // TODO: make this a StringSet such that we don't have duplicates
! public ArrayList BodySectionPartHeaderFields
! {
! get
! {
! if(mBodySectionPartHeaderFields == null)
! mBodySectionPartHeaderFields = new ArrayList();
! return mBodySectionPartHeaderFields;
! }
! }
! private ArrayList mBodySectionPartHeaderFieldsNot; // TODO: make this a StringSet such that we don't have duplicates
! public ArrayList BodySectionPartHeaderFieldsNot
! {
! get
! {
! if(mBodySectionPartHeaderFieldsNot == null)
! mBodySectionPartHeaderFieldsNot = new ArrayList();
! return mBodySectionPartHeaderFieldsNot;
! }
! }
! public bool BodySectionHeader;
! public bool BodySectionText;
! private ArrayList mBodySectionHeaderFields; // TODO: make this a StringSet such that we don't have duplicates
! public ArrayList BodySectionHeaderFields
! {
! get
! {
! if(mBodySectionHeaderFields == null)
! mBodySectionHeaderFields = new ArrayList();
! return mBodySectionHeaderFields;
! }
! }
! private ArrayList mBodySectionHeaderFieldsNot; // TODO: make this a StringSet such that we don't have duplicates
! public ArrayList BodySectionHeaderFieldsNot
{
! get
! {
! if(mBodySectionHeaderFieldsNot == null)
! mBodySectionHeaderFieldsNot = new ArrayList();
! return mBodySectionHeaderFieldsNot;
! }
}
public bool BodyStructure;
public bool Envelope;
--- 606,635 ----
public class FetchParts
{
! #region class BodySection
! public class BodySectionPart
{
! public bool BodySectionOffsetLength;
! public uint BodySectionOffset;
! public uint BodySectionLength;
! public ArrayList BodySectionParts = new ArrayList();
! public bool BodySectionPartHeader;
! public bool BodySectionPartMime;
! public bool BodySectionPartText;
! public ParenthesizedList BodySectionPartHeaderFields = new ParenthesizedList();
! public ParenthesizedList BodySectionPartHeaderFieldsNot = new ParenthesizedList();
! public bool BodySectionHeader; // BODY[HEADER]
! public bool BodySectionText; // BODY[TEXT]
! public ParenthesizedList BodySectionHeaderFields = new ParenthesizedList();
! public ParenthesizedList BodySectionHeaderFieldsNot = new ParenthesizedList();
}
+ #endregion
+
+ public bool Body; // BODY
+
+ public bool BodyPeek; // if BODY.PEEK[... was used exclusively
+ public bool BodyPeekNo; // to help determine exclusiveness of BODY.PEEK[...
+ public ArrayList BodySections = new ArrayList();
+
public bool BodyStructure;
public bool Envelope;
***************
*** 388,391 ****
--- 673,677 ----
}
}
+ #endregion
}
}
Index: LoginCommand.cs
===================================================================
RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/LoginCommand.cs,v
retrieving revision 1.5
retrieving revision 1.6
diff -C2 -d -r1.5 -r1.6
*** LoginCommand.cs 29 Jul 2003 00:46:28 -0000 1.5
--- LoginCommand.cs 5 Aug 2003 01:49:28 -0000 1.6
***************
*** 1,4 ****
- using Imap.NetworkManager;
-
using System;
--- 1,2 ----
Index: LogoutCommand.cs
===================================================================
RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/LogoutCommand.cs,v
retrieving revision 1.5
retrieving revision 1.6
diff -C2 -d -r1.5 -r1.6
*** LogoutCommand.cs 29 Jul 2003 00:46:28 -0000 1.5
--- LogoutCommand.cs 5 Aug 2003 01:49:28 -0000 1.6
***************
*** 14,19 ****
override protected void InternalProcess()
{
! mConnection.SendUntaggedMessage("BYE");
! mConnection.SendTaggedMessage("OK");
}
}
--- 14,26 ----
override protected void InternalProcess()
{
! try
! {
! mConnection.SendUntaggedMessage("BYE");
! mConnection.SendTaggedMessage("OK");
! }
! catch(System.IO.IOException)
! {
! // ignore it, client closed prematurely
! }
}
}
Index: NoopCommand.cs
===================================================================
RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/NoopCommand.cs,v
retrieving revision 1.5
retrieving revision 1.6
diff -C2 -d -r1.5 -r1.6
*** NoopCommand.cs 29 Jul 2003 00:46:28 -0000 1.5
--- NoopCommand.cs 5 Aug 2003 01:49:28 -0000 1.6
***************
*** 14,18 ****
override protected void InternalProcess()
{
! //return false;
}
}
--- 14,18 ----
override protected void InternalProcess()
{
! mConnection.SendTaggedMessage("OK NOOP completed");
}
}
Index: StatusCommand.cs
===================================================================
RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/StatusCommand.cs,v
retrieving revision 1.7
retrieving revision 1.8
diff -C2 -d -r1.7 -r1.8
*** StatusCommand.cs 3 Aug 2003 01:08:38 -0000 1.7
--- StatusCommand.cs 5 Aug 2003 01:49:28 -0000 1.8
***************
*** 61,65 ****
mConnection.SendUntaggedMessage(string.Format("STATUS {0} {1}", box.FullName, output.ToString()));
! mConnection.SendTaggedMessage("OK");
}
}
--- 61,65 ----
mConnection.SendUntaggedMessage(string.Format("STATUS {0} {1}", box.FullName, output.ToString()));
! mConnection.SendTaggedMessage("OK STATUS Completed");
}
}
Index: StoreCommand.cs
===================================================================
RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/StoreCommand.cs,v
retrieving revision 1.5
retrieving revision 1.6
diff -C2 -d -r1.5 -r1.6
*** StoreCommand.cs 29 Jul 2003 00:46:28 -0000 1.5
--- StoreCommand.cs 5 Aug 2003 01:49:28 -0000 1.6
***************
*** 1,2 ****
--- 1,4 ----
+ using Common;
+
using System;
***************
*** 8,12 ****
public class StoreCommand : ImapCommand
{
! public StoreCommand(ImapServer svr) : base(svr, "STORE", ImapConnectionState.Selected)
{
}
--- 10,14 ----
public class StoreCommand : ImapCommand
{
! public StoreCommand(ImapServer svr) : base(svr, "STORE", ImapConnectionState.Selected, ArgumentType.SequenceSet, ArgumentType.AtomString, ArgumentType.FlagListFlag)
{
}
***************
*** 14,18 ****
override protected void InternalProcess()
{
! //return false;
}
}
--- 16,157 ----
override protected void InternalProcess()
{
! SequenceSet msgSet = mParsedArguments[0] as SequenceSet;
! string dataItemName = mParsedArguments[1] as string;
! if(mParsedArguments[2] is string)
! {
! ParenthesizedList l = new ParenthesizedList();
! l.AddItem(mParsedArguments[2]);
! mParsedArguments[2] = l;
! }
! ParenthesizedList list = mParsedArguments[2] as ParenthesizedList;
!
! bool silent = false;
!
! if(dataItemName.EndsWith(".SILENT"))
! {
! silent = true;
! dataItemName = dataItemName.Substring(0, dataItemName.Length - 7);
! }
!
! bool answered = false;
! bool flagged = false;
! bool deleted = false;
! bool seen = false;
! bool draft = false;
!
! for(int idx = 0; idx < list.Count; ++idx)
! {
! object o = list[idx].DataItem;
! if(o == null || !(o is string))
! {
! mConnection.SendTaggedMessage("BAD data item is null or not a string");
! return;
! }
!
! string flag = (o as string).ToLower();
! switch(flag)
! {
! case "\\answered":
! answered = true;
! break;
! case "\\flagged":
! flagged = true;
! break;
! case "\\deleted":
! deleted = true;
! break;
! case "\\seen":
! seen = true;
! break;
! case "\\draft":
! draft = true;
! break;
! default:
! mConnection.SendTaggedMessage("BAD invalid flag");
! return;
! }
! }
!
! bool addFlags = false;
! bool setFlags = false;
! bool subFlags = false;
!
! switch(dataItemName.ToUpper())
! {
! case "FLAGS":
! setFlags = true;
! break;
! case "-FLAGS":
! subFlags = true;
! break;
! case "+FLAGS":
! addFlags = true;
! break;
! }
!
! SequenceSet.SequenceRange rng = msgSet.FirstRange;
! while(rng != null)
! {
! for(uint idx = rng.Low; idx <= rng.High; ++idx)
! {
! if(!mUidCalled && idx > mConnection.Mailbox.MessageCount)
! break;
! if(mUidCalled && idx > mConnection.Mailbox.NextUniqueId)
! break;
!
! Message msg = null;
! if(mUidCalled)
! msg = mConnection.Mailbox.Messages.ByUID(idx);
! else
! msg = mConnection.Mailbox.Messages.BySeq(idx);
!
! if(msg != null)
! {
! if(addFlags)
! {
! if(answered)
! msg.Answered = true;
! if(flagged)
! msg.Flagged = true;
! if(deleted)
! msg.Deleted = true;
! if(seen)
! msg.Seen = true;
! if(draft)
! msg.Draft = true;
! }
! else if(subFlags)
! {
! if(answered)
! msg.Answered = false;
! if(flagged)
! msg.Flagged = false;
! if(deleted)
! msg.Deleted = false;
! if(seen)
! msg.Seen = false;
! if(draft)
! msg.Draft = false;
! }
! else if(setFlags)
! {
! msg.Answered = answered;
! msg.Flagged = flagged;
! msg.Deleted = deleted;
! msg.Seen = seen;
! msg.Draft = draft;
! }
! msg.Save();
!
! if(!silent)
! {
! // TODO: send fetch
! }
! }
! }
! rng = rng.NextRange;
! }
!
! mConnection.SendTaggedMessage("OK STORE completed");
}
}
|
|
From: <ta...@us...> - 2003-08-05 01:44:42
|
Update of /cvsroot/csmaild/csmaild/src/Imap/Commands
In directory sc8-pr-cvs1:/tmp/cvs-serv679/src/Imap/Commands
Modified Files:
ListCommand.cs LsubCommand.cs SubscribeCommand.cs
UnsubscribeCommand.cs
Log Message:
LIST, LSUB, SUBSCRIBE, UNSUBSCRIBE are done (hopefully)
Index: ListCommand.cs
===================================================================
RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/ListCommand.cs,v
retrieving revision 1.6
retrieving revision 1.7
diff -C2 -d -r1.6 -r1.7
*** ListCommand.cs 30 Jul 2003 22:37:12 -0000 1.6
--- ListCommand.cs 5 Aug 2003 01:44:39 -0000 1.7
***************
*** 49,55 ****
}
! private string ListSearchToRegex(string reference, string listSearch)
{
! string rv = "^" + mReferenceName;
char[] wildCards = CommandPart.ListWildcards.ToCharArray();
int lastIdx = 0;
--- 49,55 ----
}
! public static string ListSearchToRegex(string reference, string listSearch)
{
! string rv = "^" + reference;
char[] wildCards = CommandPart.ListWildcards.ToCharArray();
int lastIdx = 0;
***************
*** 83,87 ****
}
! private string BuildFlagList(Mailbox box)
{
StringBuilder str = new StringBuilder();
--- 83,87 ----
}
! public static string BuildFlagList(Mailbox box)
{
StringBuilder str = new StringBuilder();
***************
*** 92,96 ****
{
first = false;
! str.Append("NoSelect");
}
--- 92,96 ----
{
first = false;
! str.Append("\\NoSelect");
}
***************
*** 100,104 ****
str.Append(" ");
first = false;
! str.Append("NoInferiors");
}
--- 100,104 ----
str.Append(" ");
first = false;
! str.Append("\\NoInferiors");
}
***************
*** 108,112 ****
str.Append(" ");
first = false;
! str.Append("NoInferiors");
}
--- 108,112 ----
str.Append(" ");
first = false;
! str.Append("\\Marked");
}
***************
*** 116,120 ****
str.Append(" ");
first = false;
! str.Append("NoInferiors");
}
--- 116,120 ----
str.Append(" ");
first = false;
! str.Append("\\Unmarked");
}
Index: LsubCommand.cs
===================================================================
RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/LsubCommand.cs,v
retrieving revision 1.5
retrieving revision 1.6
diff -C2 -d -r1.5 -r1.6
*** LsubCommand.cs 29 Jul 2003 00:46:28 -0000 1.5
--- LsubCommand.cs 5 Aug 2003 01:44:39 -0000 1.6
***************
*** 2,5 ****
--- 2,6 ----
using System;
+ using System.Text.RegularExpressions;
namespace Imap.Commands
***************
*** 10,13 ****
--- 11,18 ----
public class LsubCommand : ImapCommand
{
+ private string mReferenceName;
+ private string mMailboxSearchPattern;
+ private bool[] mSubscribedHandled;
+
public LsubCommand(ImapServer svr) : base(svr, "LSUB", ImapCommand.AuthSelectedState, ArgumentType.AString, ArgumentType.ListString)
{
***************
*** 16,24 ****
override protected void InternalProcess()
{
! // TODO: actually handle the arguments
! foreach(Mailbox box in mConnection.User.Mailboxes)
! mConnection.SendUntaggedMessage("LSUB () \"/\" \"" + box.Name + "\"");
mConnection.SendTaggedMessage("OK LSUB completed");
}
}
--- 21,84 ----
override protected void InternalProcess()
{
! mSubscribedHandled = new bool[mConnection.User.SubscribedMailboxes.Count];
!
! mReferenceName = mParsedArguments[0] as string;
! mMailboxSearchPattern = ListCommand.ListSearchToRegex(mReferenceName, mParsedArguments[1] as string);
!
! // if we have a reference name, navigate to it and perform the list from that level
! if(mReferenceName != string.Empty)
! {
! Mailbox box = mConnection.User.Mailboxes.FindMailbox(mReferenceName);
! if(box != null)
! ListBoxes(box.Children);
! }
! else
! ListBoxes(mConnection.User.Mailboxes);
!
! for(int idx = 0; idx < mSubscribedHandled.Length; ++idx)
! if(!mSubscribedHandled[idx])
! mConnection.SendUntaggedMessage("LSUB () \"/\" \"" + mConnection.User.SubscribedMailboxes[idx] + "\"");
mConnection.SendTaggedMessage("OK LSUB completed");
+
+ }
+
+ private bool ListBoxes(MailboxCollection boxes)
+ {
+ bool rv = false;
+ foreach(Mailbox box in boxes)
+ {
+ int idx = mConnection.User.SubscribedMailboxes.IndexOf(box.FullName);
+ if(idx != -1)
+ mSubscribedHandled[idx] = true;
+
+ if(Regex.IsMatch(box.FullName, mMailboxSearchPattern))
+ {
+ // it's a match and it's subscribed
+ if(idx != -1)
+ mConnection.SendUntaggedMessage("LSUB " + ListCommand.BuildFlagList(box) + " \"/\" \"" + box.FullName + "\"");
+ else
+ {
+ // it's a match, but not subscribed, if we have subscribed (but not matched) children, then we print
+ if(box.HasChildren)
+ {
+ if(ListBoxes(box.Children))
+ {
+ bool oldVal = box.NoSelect;
+ box.NoSelect = true;
+ mConnection.SendUntaggedMessage("LSUB " + ListCommand.BuildFlagList(box) + " \"/\" \"" + box.FullName + "\"");
+ box.NoSelect = oldVal;
+ }
+ continue;
+ }
+ }
+ }
+ else if(idx != -1)
+ rv = true;
+
+ if(box.HasChildren)
+ ListBoxes(box.Children);
+ }
+ return rv;
}
}
Index: SubscribeCommand.cs
===================================================================
RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/SubscribeCommand.cs,v
retrieving revision 1.5
retrieving revision 1.6
diff -C2 -d -r1.5 -r1.6
*** SubscribeCommand.cs 29 Jul 2003 00:46:28 -0000 1.5
--- SubscribeCommand.cs 5 Aug 2003 01:44:39 -0000 1.6
***************
*** 1,2 ****
--- 1,4 ----
+ using Common;
+
using System;
***************
*** 8,12 ****
public class SubscribeCommand : ImapCommand
{
! public SubscribeCommand(ImapServer svr) : base(svr, "SUBSCRIBE", ImapCommand.AuthSelectedState)
{
}
--- 10,14 ----
public class SubscribeCommand : ImapCommand
{
! public SubscribeCommand(ImapServer svr) : base(svr, "SUBSCRIBE", ImapCommand.AuthSelectedState, ArgumentType.AString)
{
}
***************
*** 14,18 ****
override protected void InternalProcess()
{
! //return false;
}
}
--- 16,38 ----
override protected void InternalProcess()
{
! string boxName = mParsedArguments[0] as String;
!
! Mailbox box = mConnection.User.Mailboxes.FindMailbox(boxName);
! if(box == null)
! {
! mConnection.SendTaggedMessage("NO Mailbox doesn't exist");
! return;
! }
!
! if(mConnection.User.SubscribedMailboxes.IndexOf(box.FullName) != -1)
! {
! mConnection.SendTaggedMessage("NO Already subscribed");
! return;
! }
!
! mConnection.User.SubscribedMailboxes.Add(box.FullName);
! mConnection.User.Save();
!
! mConnection.SendTaggedMessage("OK SUBSCRIBE completed");
}
}
Index: UnsubscribeCommand.cs
===================================================================
RCS file: /cvsroot/csmaild/csmaild/src/Imap/Commands/UnsubscribeCommand.cs,v
retrieving revision 1.5
retrieving revision 1.6
diff -C2 -d -r1.5 -r1.6
*** UnsubscribeCommand.cs 29 Jul 2003 00:46:28 -0000 1.5
--- UnsubscribeCommand.cs 5 Aug 2003 01:44:39 -0000 1.6
***************
*** 1,2 ****
--- 1,4 ----
+ using Common;
+
using System;
***************
*** 8,12 ****
public class UnsubscribeCommand : ImapCommand
{
! public UnsubscribeCommand(ImapServer svr) : base(svr, "UNSUBSCRIBE", ImapCommand.AuthSelectedState)
{
}
--- 10,14 ----
public class UnsubscribeCommand : ImapCommand
{
! public UnsubscribeCommand(ImapServer svr) : base(svr, "UNSUBSCRIBE", ImapCommand.AuthSelectedState, ArgumentType.AString)
{
}
***************
*** 14,18 ****
override protected void InternalProcess()
{
! //return false;
}
}
--- 16,31 ----
override protected void InternalProcess()
{
! string boxName = mParsedArguments[0] as String;
!
! if(mConnection.User.SubscribedMailboxes.IndexOf(boxName) == -1)
! {
! mConnection.SendTaggedMessage("NO not subscibed to that mailbox");
! return;
! }
!
! mConnection.User.SubscribedMailboxes.Remove(boxName);
! mConnection.User.Save();
!
! mConnection.SendTaggedMessage("OK UNSUBSCRIBE completed");
}
}
|
|
From: <ta...@us...> - 2003-08-05 01:42:13
|
Update of /cvsroot/csmaild/csmaild/src/Imap
In directory sc8-pr-cvs1:/tmp/cvs-serv32691/src/Imap
Modified Files:
Connection.cs Server.cs
Log Message:
Mail store provider has more functionality
- Can save messages (flag changes)
- Can expunge a message
Fixed namespace issues
User now has subscribed mailboxes
Commands no longer "register" themselves, resorted back to switch statement
Index: Connection.cs
===================================================================
RCS file: /cvsroot/csmaild/csmaild/src/Imap/Connection.cs,v
retrieving revision 1.9
retrieving revision 1.10
diff -C2 -d -r1.9 -r1.10
*** Connection.cs 3 Aug 2003 01:08:38 -0000 1.9
--- Connection.cs 5 Aug 2003 01:42:10 -0000 1.10
***************
*** 2,6 ****
using Imap.Commands;
! using Imap.NetworkManager;
using System;
--- 2,6 ----
using Imap.Commands;
! using Common.NetworkManager;
using System;
***************
*** 13,18 ****
/// The various states an ImapConnection can be in
/// </summary>
! [Flags()]
! public enum ImapConnectionState : byte
{
NotAuthenticated = 1, /// user is not authenticated, generally right after a connect
--- 13,17 ----
/// The various states an ImapConnection can be in
/// </summary>
! public enum ImapConnectionState
{
NotAuthenticated = 1, /// user is not authenticated, generally right after a connect
***************
*** 166,182 ****
if(idxCommandEnd == -1) // nothing after the command
{
! cmd = line.Substring(idxTagEnd).ToUpper(); // one space past the tag til the end
mCurrentArguments = string.Empty; // no arguments
}
else
{
! cmd = line.Substring(idxTagEnd, idxCommandEnd-idxTagEnd).ToUpper(); // one space past tag til the next space
mCurrentArguments = line.Substring(idxCommandEnd); // args will include the preceding space
}
! if(mCurrentCommandUID)
! mCurrentCommand = (ImapCommand)Server.UidCommands[cmd];
! else
! mCurrentCommand = (ImapCommand)Server.Commands[cmd];
if(mCurrentCommand == null)
--- 165,178 ----
if(idxCommandEnd == -1) // nothing after the command
{
! cmd = line.Substring(idxTagEnd); // one space past the tag til the end
mCurrentArguments = string.Empty; // no arguments
}
else
{
! cmd = line.Substring(idxTagEnd, idxCommandEnd-idxTagEnd); // one space past tag til the next space
mCurrentArguments = line.Substring(idxCommandEnd); // args will include the preceding space
}
! mCurrentCommand = Server.CreateCommand(cmd, mCurrentCommandUID);
if(mCurrentCommand == null)
***************
*** 199,211 ****
private void Connection_ReceivedLine(Connection cnn, string line)
{
- // this shouldn't ever be a problem, but what the heck, clock cycles are cheap ;)
- if(cnn != mConnection)
- throw new Exception("Fucked up, this is just fucked up!");
-
ParseAndValidate(line);
try
{
! if(mCurrentCommand.Process())
{
}
--- 195,203 ----
private void Connection_ReceivedLine(Connection cnn, string line)
{
ParseAndValidate(line);
try
{
! if(mCurrentCommand.Process(mCurrentArguments, this, mCurrentCommandUID))
{
}
***************
*** 216,219 ****
--- 208,212 ----
Console.WriteLine(ex.ToString());
}
+
ReadCommand();
}
Index: Server.cs
===================================================================
RCS file: /cvsroot/csmaild/csmaild/src/Imap/Server.cs,v
retrieving revision 1.8
retrieving revision 1.9
diff -C2 -d -r1.8 -r1.9
*** Server.cs 3 Aug 2003 01:08:38 -0000 1.8
--- Server.cs 5 Aug 2003 01:42:10 -0000 1.9
***************
*** 1,18 ****
! using System;
using Imap.Commands;
! using Imap.NetworkManager;
using System.Collections;
using System.IO;
- //using System.Net.Sockets;
- //using System.Text;
-
- using Common.MailstoreProviders;
-
namespace Imap
{
public class ImapServer
{
! private NetworkManager.NetworkManager mNetwork;
private Hashtable mImapConnections = new Hashtable();
private Hashtable mImapCommands = new Hashtable();
--- 1,16 ----
! using Common.MailstoreProviders;
! using Common.NetworkManager;
!
using Imap.Commands;
!
! using System;
using System.Collections;
using System.IO;
namespace Imap
{
public class ImapServer
{
! private NetworkManager mNetwork = new NetworkManager();
private Hashtable mImapConnections = new Hashtable();
private Hashtable mImapCommands = new Hashtable();
***************
*** 22,59 ****
public ImapServer(IMailstoreProvider provider)
{
- mNetwork = new NetworkManager.NetworkManager();
mMailstoreProvider = provider;
-
- #region Register handlers
- mImapCommands.Add("APPEND", new AppendCommand(this));
- mImapCommands.Add("AUTHENTICATE", new AuthenticateCommand(this));
- mImapCommands.Add("CAPABILITY", new CapabilityCommand(this));
- mImapCommands.Add("CHECK", new CheckCommand(this));
- mImapCommands.Add("CLOSE", new CloseCommand(this));
- mImapCommands.Add("COPY", new CopyCommand(this));
- mImapCommands.Add("CREATE", new CreateCommand(this));
- mImapCommands.Add("DELETE", new DeleteCommand(this));
- mImapCommands.Add("EXAMINE", new SelectExamineCommand(false, this));
- mImapCommands.Add("EXPUNGE", new ExpungeCommand(this));
- mImapCommands.Add("FETCH", new FetchCommand(this));
- mImapCommands.Add("LIST", new ListCommand(this));
- mImapCommands.Add("LOGIN", new LoginCommand(this));
- mImapCommands.Add("LOGOUT", new LogoutCommand(this));
- mImapCommands.Add("LSUB", new LsubCommand(this));
- mImapCommands.Add("NOOP", new NoopCommand(this));
- mImapCommands.Add("RENAME", new RenameCommand(this));
- mImapCommands.Add("SEARCH", new SearchCommand(this));
- mImapCommands.Add("SELECT", new SelectExamineCommand(true, this));
- mImapCommands.Add("STARTTLS", new StarttlsCommand(this));
- mImapCommands.Add("STATUS", new StatusCommand(this));
- mImapCommands.Add("STORE", new StoreCommand(this));
- mImapCommands.Add("SUBSCRIBE", new SubscribeCommand(this));
- mImapCommands.Add("UNSUBSCRIBE", new UnsubscribeCommand(this));
-
- mUidImapCommands.Add("COPY", mImapCommands["COPY"]);
- mUidImapCommands.Add("FETCH", mImapCommands["FETCH"]);
- mUidImapCommands.Add("STORE", mImapCommands["STORE"]);
- mUidImapCommands.Add("SEARCH", mImapCommands["SEARCH"]);
- #endregion
}
--- 20,24 ----
***************
*** 74,93 ****
}
- public Hashtable Commands
- {
- get
- {
- return mImapCommands;
- }
- }
-
- public Hashtable UidCommands
- {
- get
- {
- return mUidImapCommands;
- }
- }
-
public void Start()
{
--- 39,42 ----
***************
*** 104,108 ****
mImapConnections.Add(con, cnn);
-
}
--- 53,56 ----
***************
*** 113,116 ****
--- 61,138 ----
}
+ #region Command handler switches
+ public ImapCommand CreateCommand(string name, bool uid)
+ {
+ if(uid)
+ {
+ switch(name.ToUpper())
+ {
+ case "COPY":
+ return new CopyCommand(this);
+ case "FETCH":
+ return new FetchCommand(this);
+ case "STORE":
+ return new StoreCommand(this);
+ case "SEARCH":
+ return new SearchCommand(this);
+ }
+ }
+ else
+ {
+ switch(name.ToUpper())
+ {
+ case "APPEND":
+ return new AppendCommand(this);
+ case "AUTHENTICATE":
+ return new AuthenticateCommand(this);
+ case "CAPABILITY":
+ return new CapabilityCommand(this);
+ case "CHECK":
+ return new CheckCommand(this);
+ case "CLOSE":
+ return new CloseCommand(this);
+ case "COPY":
+ return new CopyCommand(this);
+ case "CREATE":
+ return new CreateCommand(this);
+ case "DELETE":
+ return new DeleteCommand(this);
+ case "EXAMINE":
+ return new SelectExamineCommand(false, this);
+ case "EXPUNGE":
+ return new ExpungeCommand(this);
+ case "FETCH":
+ return new FetchCommand(this);
+ case "LIST":
+ return new ListCommand(this);
+ case "LOGIN":
+ return new LoginCommand(this);
+ case "LOGOUT":
+ return new LogoutCommand(this);
+ case "LSUB":
+ return new LsubCommand(this);
+ case "NOOP":
+ return new NoopCommand(this);
+ case "RENAME":
+ return new RenameCommand(this);
+ case "SEARCH":
+ return new SearchCommand(this);
+ case "SELECT":
+ return new SelectExamineCommand(true, this);
+ case "STARTTLS":
+ return new StarttlsCommand(this);
+ case "STATUS":
+ return new StatusCommand(this);
+ case "STORE":
+ return new StoreCommand(this);
+ case "SUBSCRIBE":
+ return new SubscribeCommand(this);
+ case "UNSUBSCRIBE":
+ return new UnsubscribeCommand(this);
+ }
+ }
+ return null;
+ }
+ #endregion
}
}
|