// ===================================================================== // TLS support // ===================================================================== /// <summary> /// Send IMap STARTTLS command and initialize client side TLS /// </summary> /// <param name="uTag"> /// <c>0</c> when called by GetConnection to initialize imaps. Otherwise /// a valid tag number causes the STARTTLS imap command to be sent. /// </param> /// <returns> /// <c>true</c> if no problem occurred, <c>false</c> on errors. /// For TLS related error a <see cref="ZIMapException"/> is thrown. /// </returns> /// <remarks> /// This routine throws an exception when TLS is "Required" but not /// availlable. In "Auto" mode invalid server certificates are accepted, /// in mode "Required" an exception is thrown. /// </remarks> public bool StartTls(uint uTag) { if(uTag == 0) MonitorInfo( "StartTls: TLS via imaps"); else { if (tlsmode == TlsModeEnum.Disabled || tlsmode == TlsModeEnum.IMaps) return true; // nothing to do MonitorInfo( "StartTls: send STARTLS"); transport.Send(uTag, "STARTTLS"); string tag, status, message; while (true) { if (!transport.Receive(out tag, out status, out message)) break; if (tag == uTag.ToString()) break; } if (status != "OK") { if (tlsmode == TlsModeEnum.Required) { RaiseError(ZIMapException.Error.CannotConnect, "STARTTLS failed: " + message); return false; // required but not ready } MonitorInfo( "StartTls: STARTTLS failed: " + message); tlsmode = TlsModeEnum.Disabled; return true; } } // now get the TLS stream Stream strm = null; try // all errors throw... { strm = GetTlsStream(); if(strm == null) return true; // tls disabled stream = strm; } catch(Exception ex) { if(tlsmode == TlsModeEnum.Required) { RaiseError(ZIMapException.Error.CannotConnect, ex.Message); return false; // required but not ready } if(ex is ZIMapException) throw ex; MonitorError("TLS failure: " + ex.Message); return false; } if(uTag == 0) tlsmode = TlsModeEnum.IMaps; else transport.Setup(socket, stream, timeout); return true; }
/// <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; }