| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 | using System;using System.Diagnostics;using System.IO;using System.Xml;using System.Text;using System.Threading;namespace WinSCP{    internal class SessionLogReader : CustomLogReader    {        public SessionLogReader(Session session) :            base(session)        {            _position = 0;        }        public void SetTimeouted()        {            _timeouted = true;        }        public override void Dispose()        {            using (Session.Logger.CreateCallstack())            {                LogContents();                Cleanup();            }            base.Dispose();        }        private void Cleanup()        {            if (_stream != null)            {                Session.Logger.WriteLine("Closing log");                _stream.Dispose();                _stream = null;            }            if (_reader != null)            {                ((IDisposable)_reader).Dispose();                _reader = null;            }        }        public override bool Read(LogReadFlags flags)        {            using (Session.Logger.CreateCallstack())            {                bool result;                if (_timeouted)                {                    Session.Logger.WriteLine("Not reading, session has timed out");                    result = false;                }                else                {                    bool retry;                    do                    {                        result = DoRead();                        retry = false;                        if (result &&                            IsNonEmptyElement("failure"))                        {                            SessionRemoteException e = SessionRemoteException.ReadFailure(this);                            Session.RaiseFailed(e);                            if ((flags & LogReadFlags.ThrowFailures) == 0)                            {                                retry = true;                            }                            else                            {                                throw Session.Logger.WriteException(e);                            }                        }                    }                    while (retry);                }                return result;            }        }        private bool DoRead()        {            int interval = 50;            bool result;            do            {                if (_reader == null)                {                    OpenLog();                }                Debug.Assert(_reader != null);                try                {                    result = _reader.Read();                    if (result)                    {                        ++_position;                        Session.Logger.WriteLine("Read node {0}: {1} {2}{3}{4}",                            _position, _reader.NodeType, _reader.Name,                            (_reader.HasValue && !string.IsNullOrEmpty(_reader.Name) && !string.IsNullOrEmpty(_reader.Value) ? "=" : string.Empty),                            _reader.Value);                        Session.GotOutput();                    }                    else                    {                        Session.Logger.WriteLine("Cannot read");                        if (!_closed)                        {                            // this should not happen as when the log is not closed,                            // we should get XmlException on reaching the end                            _closed = true;                            Cleanup();                        }                        Session.CheckForTimeout();                    }                }                catch (XmlException e)                {                    Cleanup();                    // check if the the root cause was session abort                    Session.CheckForTimeout();                    LogContents();                    string message = "Error parsing session log file";                    // This is possibly a race condition, as we may not have processed the event with the error yet                    // The ExeSessionProcess loops every 100ms                    Thread.Sleep(200);                    string s = Session.GetErrorOutputMessage();                    if (!string.IsNullOrEmpty(s))                    {                        message += " - " + s;                    }                    throw Session.Logger.WriteException(new SessionLocalException(Session, message, e));                }                if (!result && !_closed)                {                    Session.Logger.WriteLine("Waiting for log update and dispatching events for {0}", interval);                    Session.DispatchEvents(interval);                    if (interval < 500)                    {                        interval *= 2;                    }                }            }            while (!result && !_closed);            if (result)            {                LogContents();            }            return result;        }        private void LogContents()        {            if (Session.Logger.Logging)            {                try                {                    // alterative to File.ReadAllText with write-sharing                    // (note that the StreamReader disposes the Stream)                    using (StreamReader reader = new StreamReader(new FileStream(Session.XmlLogPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), Encoding.UTF8))                    {                        string contents = reader.ReadToEnd();                        if ((_logged == null) || (_logged != contents))                        {                            Session.Logger.WriteLine("Log contents:\n{0}", contents);                            _logged = contents;                        }                        else                        {                            Session.Logger.WriteLine("Log contents has not changed");                        }                    }                }                catch (Exception e)                {                    Session.Logger.WriteLine("Error logging log contents [{0}]", e.Message);                }            }        }        private void OpenLog()        {            if (_closed)            {                throw Session.Logger.WriteException(new InvalidOperationException("Log was closed already"));            }            try            {                Session.Logger.WriteLine("Opening log without write sharing");                // First try to open file without write sharing.                // This fails, if WinSCP is still writing to the log file.                // This is done only as a way to detect that log file is not complete yet.                _stream = new PatientFileStream(Session, Session.XmlLogPath, FileMode.Open, FileAccess.Read, FileShare.Read);                _closed = true;                LogContents();            }            catch (IOException)            {                Session.Logger.WriteLine("Opening log with write sharing");                // If log file is still being written to, open it with write sharing                _stream = new PatientFileStream(Session, Session.XmlLogPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);                _closed = false;            }            Session.Logger.WriteLine("Log opened");            // Allow control characters in log            var settings = new XmlReaderSettings() { CheckCharacters = false };            _reader = XmlReader.Create(_stream, settings);            int skip = _position;            Session.Logger.WriteLine("Skipping {0} nodes", skip);            while (skip > 0)            {                if (!_reader.Read())                {                    throw Session.Logger.WriteException(new SessionLocalException(Session, "Read less nodes than in previous log parsing"));                }                --skip;            }        }        internal override XmlReader Reader        {            get            {                if (_reader == null)                {                    throw Session.Logger.WriteException(new SessionLocalException(Session, "Reading has not commenced yet"));                }                return _reader;            }        }        private int _position;        private XmlReader _reader;        private PatientFileStream _stream;        private bool _closed;        private string _logged;        private bool _timeouted;    }}
 |