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 } } |