// ============================================================================= // xtor // ============================================================================= public CacheData(ZIMapApplication application) : base(application.Parent) { this.application = application; connection = application.Connection; server = application.Server; factory = connection.CommandLayer; progress = connection.ProgressReporting; // At least deliver Info level messages ... MonitorLevel = application.MonitorLevel; if(MonitorLevel > ZIMapConnection.Monitor.Info) MonitorLevel = ZIMapConnection.Monitor.Info; Data = data = new DataRefImp(this); }
// Pretty formatting of monitor messages ... public override bool Monitor(ZIMapConnection connection, ZIMapConnection.Monitor level, string source, string message) { if(message == null || source == null) return true; // output messages ... string text = null; if(level <= ZIMapConnection.Monitor.Error) { if(message.Length > 2 && message[0] == ':') text = message.Substring(1); } if(text == null) text = source + ": " + message; switch(level) { case ZIMapConnection.Monitor.Debug: LineTool.Extra(text); return true; case ZIMapConnection.Monitor.Info: LineTool.Info(text); return true; case ZIMapConnection.Monitor.Error: ZIMapAdmin.ErrorCalled = true; LineTool.Error(text); return true; default: return true; } }
// ===================================================================== // The 'command loop' routine // ===================================================================== /// <summary>The 'command loop' routine</summary> /// <param name="server">A name or URL to contact the server</param> /// <param name="prot">A protocol name like 'imap' or 'imaps'</param> /// <param name="mode">SSL handling, see /// <see cref="ZIMapConnection.TlsModeEnum"/></param> /// <param name="timeout">Timeout in [s]</param> /// <param name="account">The IMAP account (user) to be used</param> /// <param name="password">Password for the given account</param> /// <param name="debug">Turns debug output on</param> /// <returns><c>true</c> on success</returns> public static bool Run(string server, string prot, ZIMapConnection.TlsModeEnum mode, uint timeout, string account, string password, bool debug) { ZIMapConnection connection; ZIMapProtocol protocol; ZIMapConnection.Callback = new IMapCallback(); try { connection = ZIMapConnection.GetConnection(server, ZIMapConnection.GetIMapPort(prot), mode, timeout); if(connection == null) { LineTool.Error("Connect failed"); return false; } connection.MonitorLevel = debug ? ZIMapConnection.Monitor.Debug : ZIMapConnection.Monitor.Error; protocol = connection.ProtocolLayer; protocol.MonitorLevel = connection.MonitorLevel; connection.TransportLayer.MonitorLevel = connection.MonitorLevel; LineTool.Info(protocol.ServerGreeting); } catch(Exception e) { LineTool.Error("Connect failed with exception: {0}", e.Message); return false; } if(!string.IsNullOrEmpty(account)) { StringBuilder sb = new StringBuilder("LOGIN "); if(!ZIMapConverter.QuotedString(sb, account, false) || sb.Append(' ') == null || !ZIMapConverter.QuotedString(sb, password, false)) { LineTool.Error("Error: Can use only 7-bit data for '-account'"); return false; } LineTool.Info(sb.ToString()); protocol.Send(sb.ToString()); ZIMapProtocol.ReceiveData data; protocol.Receive(out data); if(!data.Succeeded) { LineTool.Error("Error: {0}", data.Message); return false; } } // The loop that does the real work string[] command; while((command = LineTool.Prompt("IMAP", 2)) != null) { try { string message = command[0].ToUpper(); if(command.Length > 1) message += " " + command[1]; protocol.Send(message); uint tag; string status; ZIMapProtocol.ReceiveState info; do { info = protocol.Receive(out tag, out status, out message); LineTool.Message("{0} {1} {2}", tag, status, message); } while(info == ZIMapProtocol.ReceiveState.Info); if(connection.IsTransportClosed) return false; } catch(Exception e) { LineTool.Error("Command failed with exception: {0}", e.Message); } } return true; }
/// <summary> /// Output a top level error message via ZIMapConnection. /// </summary> /// <param name="message"> /// Message to be displayed as an application error. /// </param> /// <param name="level"> /// Severity or kind of the message. /// </param> /// <remarks> /// The ZIMapAdmin application uses an additional colon to filter /// top level error messages. Example: ":Top level error". /// </remarks> protected override void MonitorInvoke(ZIMapConnection.Monitor level, string message) { if(MonitorLevel <= level) ZIMapConnection.MonitorInvoke(connection, "Cache", level, ":" + message); }
// ===================================================================== // Progress reporting // ===================================================================== /// <summary> /// Update the progress display even before having a <see cref="ZIMapConnection"/> /// instance. /// </summary> /// <param name="connection"> /// Should be the current connection but can also be <c>null</c> if no connection /// has been created until the time of the call. /// </param> /// <param name="percent"> /// The progress value <see cref="ZIMapConnection.Progress.Update(uint)"/>. /// </param> /// <remarks> /// This wrapper is efficient and can be used by library routines as a standard /// call to update the progress display. Another way for applications is to use /// the <see cref="ProgressReporting"/> protperty to obtain a reference to an /// instance of the <see cref="ZIMapConnection.Progress"/> class that offers more /// features. /// </remarks> public static void ProgressUpdate(ZIMapConnection connection, uint percent) { if(connection == null) { if(percent > 100) percent = 100; MonitorInvoke(null, "ZIMapConnection", Monitor.Progress, percent.ToString()); } else if(connection.progress != null) connection.progress.Update(percent); else connection.ProgressReporting.Update(percent); }
public virtual bool Result(ZIMapConnection connection, object info) { return false; }
public virtual bool Closed(ZIMapConnection connection) { return false; }
public virtual bool Progress(ZIMapConnection connection, uint percent) { return false; }
// ===================================================================== // Debug stuff // ===================================================================== public void SetMonitorLevel(ZIMapConnection.Monitor level, bool allLayers) { if(level > ZIMapConnection.Monitor.Error) return; monitorLevel = level; monitorAll = allLayers; if(factory == null) return; factory.MonitorLevel = level; factory.Connection.MonitorLevel = level; if(!allLayers && level != ZIMapConnection.Monitor.Error) level = ZIMapConnection.Monitor.Info; factory.Connection.TransportLayer.MonitorLevel = level; factory.Connection.ProtocolLayer.MonitorLevel = level; }
/// <summary> /// Pass exception object to ZIMapConnection.Callback, throw as error is not handled. /// </summary> /// <param name="conn"> /// The parent connection or <c>null</c> if unknown. /// </param> /// <param name="code"> /// The error code <see cref="ZIMapException.ErrorCode"/> /// </param> /// <param name="arg1"> /// <c>null</c> for no info, an Exception object as inner exception or any other /// object (ToString will be called). /// </param> public static void Throw(ZIMapConnection conn, Error code, object arg1) { ZIMapException err; if(arg1 == null) err = new ZIMapException(code); else if(arg1 is Exception) err = new ZIMapException(code, (Exception)arg1); else err = new ZIMapException(code, MessageFromCode(code) + ": " + arg1.ToString()); if(ZIMapConnection.Callback.Error(conn, err)) return; throw err; }
/// <summary> /// Hook for sending error and debug messages or to inform of /// status changes. /// </summary> /// <param name="level"> /// <see cref="ZIMapConnection.Monitor"/> indicates the importance of the message. /// </param> /// <param name="message"> /// The payload. /// </param> /// <remarks>Implementing this in a derived class gives the implementor /// full control about the message output. Usually the implementation /// will just call /// <see cref="ZIMapConnection.MonitorInvoke(ZIMapConnection.Monitor, string)"/>. /// <para /> /// Messages will only be processed if <paramref name="level"/> is less /// or equal to <see cref="MonitorLevel"/>). /// </remarks> protected abstract void MonitorInvoke(ZIMapConnection.Monitor level, string message);
/// <summary>The one and only constructor.</summary> /// <param name="parent"> /// The owning connection. /// </param> /// <remarks> /// The constructor is protected, use <see cref="ZIMapConnection.CommandLayer"/> /// to get an instance of a factory. /// </remarks> protected ZIMapFactory(ZIMapConnection parent) : base(parent) { }
// ============================================================================= // Xtor // ============================================================================= /// <summary> /// This is the one and only constructor. /// </summary> /// <param name="parent"> /// Required reference to the connection. Must not be <c>null</c>. /// </param> public ZIMapExport(ZIMapConnection parent) : base(parent) { }
// kill program on exception ... public override bool Error(ZIMapConnection connection, ZIMapException error) { if(Thread.CurrentThread.ThreadState == ThreadState.AbortRequested) return true; ZIMapAdmin.Error("Error [exception]: {0}", error.Message); if(!Executing) { if(LineTool.LogWriter != null) LineTool.LogWriter.Close(); ZIMapAdmin.Error("Terminating the program"); System.Environment.Exit(2); } if(Debug == 0) LineTool.Info( "Ignoring the error. Use the 'debug 2' command to enable debug output."); return false; }
public override bool Progress(ZIMapConnection connection, uint percent) { if(ZIMapAdmin.Output < OutLevel.All) return true; if(ZIMapAdmin.Debug > 0) return true; if(percent < 100) LineTool.Progress("Working [", percent, string.Format("] {0,2}%\r", percent)); return true; }
public static void MonitorInvoke(ZIMapConnection origin, string name, Monitor level, string message) { if(message == null) message = "<null>"; if(level == Monitor.Progress) { if(message == MonitorProgress) return; MonitorProgress = message; } else if(level != Monitor.Messages) MonitorProgress = null; if(name == null) name = "<null>"; // must use locking to make this thread safe (see ZIMapTransport.Reader) ... try { if(origin != null) System.Threading.Monitor.Enter(origin); if(level == Monitor.Progress) { uint percent; if(uint.TryParse(message, out percent) && ZIMapConnection.Callback.Progress(origin, percent)) return; Console.Write("{0} {1}: {2}\r", name, level, message); } else if(level == Monitor.Messages) { uint existing; if(!uint.TryParse(message, out existing)) return; ZIMapConnection.Callback.Message(origin, existing); } else { if(ZIMapConnection.Callback.Monitor(origin, level, name, message)) return; Console.WriteLine("{0} {1}: {2}", name, level, message); } } finally { if(origin != null) System.Threading.Monitor.Exit(origin); } }
{ public virtual bool Monitor(ZIMapConnection connection, ZIMapConnection.Monitor level, string source, string message) { return false; }
// must implement, abstract in base ... protected override void MonitorInvoke(ZIMapConnection.Monitor level, string message) { if(MonitorLevel <= level) ZIMapConnection.MonitorInvoke(connection, "ZIMapFactory", level, message); }
public virtual bool Message(ZIMapConnection connection, uint existing) { return false; }
/// <summary> /// Connect to the IMap server and optionally login. /// </summary> /// <param name="user"> /// An IMap account name or <c>null</c> for no login. /// </param> /// <param name="password"> /// The password for the given account. /// </param> /// <param name="tlsMode"> /// Controls TLS (tranport layer security). /// </param> /// <returns> /// On success <c>true</c> is returned. /// </returns> /// <remarks>This function calls <see cref="ZIMapConnection.GetConnection(string)"/> /// and uses <see cref="ZIMapCommand.Login"/> for login. /// <para /> /// If this method is called while having an open connection <c>Disconnect</c> /// gets called first. /// </remarks> public bool Connect(string user, string password, ZIMapConnection.TlsModeEnum tlsMode) { // step 1: Open a new connection and get factory ... ZIMapConnection.ProgressUpdate(null, 0); if(connection != null) Disconnect(); connection = ZIMapConnection.GetConnection(ServerName, ServerPort, tlsMode, timeout); if(connection != null) { connection.MonitorLevel = MonitorLevel; progress = connection.ProgressReporting; progress.Update(20); factory = connection.CommandLayer; } if(factory == null) { connection = null; MonitorError("Connect: failed to open connection"); return false; } // defaults for logging ... SetMonitorLevel(monitorLevel, monitorAll); // step 2: login string greeting = connection.ProtocolLayer.ServerGreeting; MonitorInfo("Connect: server greeting: " + greeting); progress.Update(40); ZIMapCommand.Login cmd = new ZIMapCommand.Login(factory); cmd.Queue(user, password); if(!cmd.CheckSuccess()) { MonitorError("Connect: login failed"); return false; } username = user; progress.Update(60); // set 3: get server configuration bool bIMap = true; CheckCapability("IMAP4rev1", false, ref bIMap); CheckCapability("NAMESPACE", bIMap, ref enableNamespaces); CheckCapability("QUOTA", false, ref enableQuota); CheckCapability("ACL", false, ref enableRights); CheckCapability("UIDPLUS", bIMap, ref enableUid); progress.Update(80); // step4: get Namespace info if(enableNamespaces) { if(Server.NamespaceDataPersonal.Valid) MonitorInfo("Connect: NAMESPACE enabled"); else { MonitorInfo("Connect: NAMESPACE disabled (not supported)"); enableNamespaces = false; } factory.HierarchyDelimiter = server.DefaultDelimiter; } progress.Done(); return true; }
public virtual bool Request(ZIMapConnection connection, uint tag, string command) { return false; }
/// <summary> /// Logout and close the IMap connection. /// </summary> /// <remarks> /// If a user is logged in this command sends an IMap LOGOUT command. If also /// closes the connection if it is open. The <see cref="ZIMapFactory"/> /// object gets released. After a call to this method a connection can be /// reestablished by calling <see cref="Connect(string, string)"/>. /// </remarks> public void Disconnect() { if(factory != null && IsLoggedIn) { ZIMapCommand.Logout cmd = new ZIMapCommand.Logout(factory); cmd.Execute(true); cmd.Dispose(); } if(connection != null) { connection.Close(); MonitorInfo("Disconnect: done"); } connection = null; factory = null; username = null; mailboxName = null; }
public virtual bool Error(ZIMapConnection connection, ZIMapException error) { return false; }
/// <summary> /// Creates a connection instance and opens an IMap connection. /// </summary> /// <param name="server"> /// The server URL or IP Address. /// </param> /// <param name="port"> /// The port number to be used, see <see cref="GetIMapPort(string)"/>. /// </param> /// <param name="tlsMode"> /// Controls TLS (tranport layer security), see <see cref="TlsModeEnum"/>. /// </param> /// <param name="timeout"> /// Connection timeout, use <c>0</c> for no timeout. /// </param> /// <returns> /// A new instance of <c>ZIMapConnection</c> that is connected to the /// specified IMap server. /// </returns> /// <remarks> /// The method raises an error of type <see cref="ZIMapException.Error.CannotConnect"/> /// if no connection can be established. /// </remarks> public static ZIMapConnection GetConnection(string server, uint port, TlsModeEnum tlsMode, uint timeout) { ZIMapConnection conn = new ZIMapConnection(); conn.tlsmode = tlsMode; conn.timeout = timeout; try { conn.socket = new System.Net.Sockets.Socket(System.Net.Sockets.AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.IP); // The socket timeouts will be reset by the transport layer! if(timeout > 0) { conn.socket.ReceiveTimeout = (int)timeout * 1000; conn.socket.SendTimeout = (int)timeout * 1000; } conn.socket.NoDelay = true; // nagle causes more windows trouble conn.server = server; conn.socket.Connect(server, (int)port); conn.stream = new System.Net.Sockets.NetworkStream(conn.socket); } catch(Exception inner) { if(inner is System.Net.Sockets.SocketException) conn.MonitorError("GetConnection: " + inner.Message); else conn.RaiseError(ZIMapException.Error.CannotConnect, inner.Message); return null; } // init transport level and start async receive ... if(port == 993 && !conn.StartTls(0)) return null; // after Throw() conn.transport = new Transport(conn); // start async receive and init protocol layer ... conn.transport.Poll(0); conn.protocol = new Protocol(conn, conn.transport); return conn; }
public Progress(ZIMapConnection parent) { connection = parent; min = 0; max = 100; }
// base has no def xtor ... public Transport(ZIMapConnection parent) : base(parent) { name = "ZIMapTransport"; Setup(parent.socket, parent.stream, parent.timeout); }
// base has no def xtor ... public Factory(ZIMapConnection parent) : base(parent) { name = "ZIMapFactory"; }
// must implement, abstract in base ... protected override void MonitorInvoke(ZIMapConnection.Monitor level, string message) { if(MonitorLevel <= level) ZIMapConnection.MonitorInvoke(Parent, name, level, message); }
public override bool Monitor(ZIMapConnection connection, ZIMapConnection.Monitor level, string origin, string message) { if(origin == null || message == null) return true; switch(level) { case ZIMapConnection.Monitor.Debug: LineTool.Extra("{0}: {1}", origin, message); return true; case ZIMapConnection.Monitor.Info: LineTool.Info("{0}: {1}", origin, message); return true; case ZIMapConnection.Monitor.Error: LineTool.Error("{0}: {1}", origin, message); return true; } return true; }
public ZIMapProtocol(ZIMapBase parent, ZIMapTransport transport) : base(parent) { this.transport = transport; this.connection = parent as ZIMapConnection; }