protected override object ReadNext() { if (_nullMap.IsNull(CurrentField)) { return(DBNull.Value); } NpgsqlRowDescription.FieldData field_descr = FieldData; Int32 field_value_size = PGUtil.ReadInt32(Stream) - 4; byte[] buffer = new byte[field_value_size]; PGUtil.CheckedStreamRead(Stream, buffer, 0, field_value_size); char[] charBuffer = new char[UTF8Encoding.GetCharCount(buffer, 0, buffer.Length)]; UTF8Encoding.GetChars(buffer, 0, buffer.Length, charBuffer, 0); try { return (NpgsqlTypesHelper.ConvertBackendStringToSystemType(field_descr.TypeInfo, new string(charBuffer), field_descr.TypeSize, field_descr.TypeModifier)); } catch (InvalidCastException ice) { return(ice); } catch (Exception ex) { return(new InvalidCastException(ex.Message, ex)); } }
protected override void SkipOne() { if (!_nullMap.IsNull(CurrentField)) { PGUtil.EatStreamBytes(Stream, PGUtil.ReadInt32(Stream) - 4); } }
public FieldDataV2(Stream stream, NpgsqlBackendTypeMapping typeMapping) { Name = PGUtil.ReadString(stream); TypeInfo = typeMapping[TypeOID = PGUtil.ReadInt32(stream)]; TypeSize = PGUtil.ReadInt16(stream); TypeModifier = PGUtil.ReadInt32(stream); }
public StringRowReaderV3(NpgsqlRowDescription rowDesc, Stream inputStream) : base(rowDesc, inputStream) { _messageSize = PGUtil.ReadInt32(inputStream); if (PGUtil.ReadInt16(inputStream) != rowDesc.NumFields) { throw new DataException(); } }
public NpgsqlBackEndKeyData(Stream stream) { // Read the BackendKeyData message contents. Two Int32 integers = 8 Bytes. // For protocol version 3.0 they are three integers. The first one is just the size of message // so, just read it. PGUtil.EatStreamBytes(stream, 4); ProcessID = PGUtil.ReadInt32(stream); SecretKey = PGUtil.ReadInt32(stream); }
public FieldDataV3(Stream stream, NpgsqlBackendTypeMapping typeMapping) { Name = PGUtil.ReadString(stream); TableOID = PGUtil.ReadInt32(stream); ColumnAttributeNumber = PGUtil.ReadInt16(stream); TypeInfo = typeMapping[TypeOID = PGUtil.ReadInt32(stream)]; TypeSize = PGUtil.ReadInt16(stream); TypeModifier = PGUtil.ReadInt32(stream); FormatCode = (FormatCode)PGUtil.ReadInt16(stream); }
private int GetThisFieldCount() { if (_nextFieldSize.HasValue) { return(_nextFieldSize.Value); } int s; _nextFieldSize = s = PGUtil.ReadInt32(Stream); return(s); }
private void ReadFromStream_Ver_3(Stream input_stream, Encoding encoding, NpgsqlBackendTypeMapping type_mapping) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream_Ver_3"); Byte[] input_buffer = new Byte[4]; // Max read will be 4 + 2 + 4 + 2 + 4 + 2 // Read the length of message. // [TODO] Any use for now? PGUtil.ReadInt32(input_stream, input_buffer); Int16 num_fields = PGUtil.ReadInt16(input_stream, input_buffer); // Temporary FieldData object to get data from stream and put in array. NpgsqlRowDescriptionFieldData fd; fields_data = new NpgsqlRowDescriptionFieldData[num_fields]; fields_index = new string[num_fields]; field_name_index_table = new Hashtable(num_fields); for (Int16 i = 0; i < num_fields; i++) { fd = new NpgsqlRowDescriptionFieldData(); fd.name = PGUtil.ReadString(input_stream, encoding); fd.table_oid = PGUtil.ReadInt32(input_stream, input_buffer); fd.column_attribute_number = PGUtil.ReadInt16(input_stream, input_buffer); fd.type_oid = PGUtil.ReadInt32(input_stream, input_buffer); fd.type_info = type_mapping[fd.type_oid]; fd.type_size = PGUtil.ReadInt16(input_stream, input_buffer); fd.type_modifier = PGUtil.ReadInt32(input_stream, input_buffer); fd.format_code = (FormatCode)PGUtil.ReadInt16(input_stream, input_buffer); fields_data[i] = fd; fields_index[i] = fd.name; if (!field_name_index_table.ContainsKey(fd.name)) { field_name_index_table.Add(fd.name, i); } } }
protected override object ReadNext() { if (_nullMap.IsNull(CurrentField)) { return(DBNull.Value); } NpgsqlRowDescription.FieldData field_descr = FieldData; Int32 field_value_size = PGUtil.ReadInt32(Stream) - 4; if (field_value_size >= 85000) { return(ReadLargeObject(field_descr, field_value_size)); } byte[] buffer = new byte[field_value_size]; PGUtil.CheckedStreamRead(Stream, buffer, 0, field_value_size); var str = UTF8Encoding.GetString(buffer); try { return (NpgsqlTypesHelper.ConvertBackendStringToSystemType( field_descr.TypeInfo, str, field_descr.TypeSize, field_descr.TypeModifier)); } catch (InvalidCastException ice) { return(ice); } catch (Exception ex) { return(new InvalidCastException(ex.Message, ex)); } }
private void ReadFromStream_Ver_3(Stream inputStream, Encoding encoding) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream_Ver_3"); PGUtil.ReadInt32(inputStream, _inputBuffer); Int16 numCols = PGUtil.ReadInt16(inputStream, _inputBuffer); Decoder decoder = encoding.GetDecoder(); for (Int16 field_count = 0; field_count < numCols; field_count++) { Int32 field_value_size = PGUtil.ReadInt32(inputStream, _inputBuffer); // Check if this field is null if (field_value_size == -1) // Null value { data.Add(DBNull.Value); continue; } NpgsqlRowDescriptionFieldData field_descr = row_desc[field_count]; if (row_desc[field_count].format_code == FormatCode.Text) { string result = ReadStringFromStream(inputStream, field_value_size, decoder); // Add them to the AsciiRow data. data.Add(NpgsqlTypesHelper.ConvertBackendStringToSystemType(field_descr.type_info, result, field_descr.type_size, field_descr.type_modifier)); } else { Byte[] binary_data = ReadBytesFromStream(inputStream, field_value_size); data.Add(NpgsqlTypesHelper.ConvertBackendBytesToSystemType(field_descr.type_info, binary_data, encoding, field_value_size, field_descr.type_modifier)); } } }
protected IEnumerable <IServerResponseObject> ProcessBackendResponses_Ver_3(NpgsqlConnector context) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses"); using (new ContextResetter(context)) { Stream stream = context.Stream; NpgsqlMediator mediator = context.Mediator; List <NpgsqlError> errors = new List <NpgsqlError>(); for (;;) { // Check the first Byte of response. BackEndMessageCode message = (BackEndMessageCode)stream.ReadByte(); switch (message) { case BackEndMessageCode.ErrorResponse: NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion, stream); error.ErrorSql = mediator.SqlSent; errors.Add(error); NpgsqlEventLog.LogMsg(resman, "Log_ErrorResponse", LogLevel.Debug, error.Message); // Return imediately if it is in the startup state or connected state as // there is no more messages to consume. // Possible error in the NpgsqlStartupState: // Invalid password. // Possible error in the NpgsqlConnectedState: // No pg_hba.conf configured. if (!context.RequireReadyForQuery) { throw new NpgsqlException(errors); } break; case BackEndMessageCode.AuthenticationRequest: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AuthenticationRequest"); // Get the length in case we're getting AuthenticationGSSContinue int authDataLength = PGUtil.ReadInt32(stream) - 8; AuthenticationRequestType authType = (AuthenticationRequestType)PGUtil.ReadInt32(stream); switch (authType) { case AuthenticationRequestType.AuthenticationOk: NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug); break; case AuthenticationRequestType.AuthenticationClearTextPassword: NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationClearTextRequest", LogLevel.Debug); // Send the PasswordPacket. ChangeState(context, NpgsqlStartupState.Instance); context.Authenticate(context.Password); break; case AuthenticationRequestType.AuthenticationMD5Password: NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationMD5Request", LogLevel.Debug); // Now do the "MD5-Thing" // for this the Password has to be: // 1. md5-hashed with the username as salt // 2. md5-hashed again with the salt we get from the backend MD5 md5 = MD5.Create(); // 1. byte[] passwd = context.Password; byte[] saltUserName = BackendEncoding.UTF8Encoding.GetBytes(context.UserName); byte[] crypt_buf = new byte[passwd.Length + saltUserName.Length]; passwd.CopyTo(crypt_buf, 0); saltUserName.CopyTo(crypt_buf, passwd.Length); StringBuilder sb = new StringBuilder(); byte[] hashResult = md5.ComputeHash(crypt_buf); foreach (byte b in hashResult) { sb.Append(b.ToString("x2")); } String prehash = sb.ToString(); byte[] prehashbytes = BackendEncoding.UTF8Encoding.GetBytes(prehash); crypt_buf = new byte[prehashbytes.Length + 4]; stream.Read(crypt_buf, prehashbytes.Length, 4); // Send the PasswordPacket. ChangeState(context, NpgsqlStartupState.Instance); // 2. prehashbytes.CopyTo(crypt_buf, 0); sb = new StringBuilder("md5"); // This is needed as the backend expects md5 result starts with "md5" hashResult = md5.ComputeHash(crypt_buf); foreach (byte b in hashResult) { sb.Append(b.ToString("x2")); } context.Authenticate(BackendEncoding.UTF8Encoding.GetBytes(sb.ToString())); break; #if WINDOWS && UNMANAGED case AuthenticationRequestType.AuthenticationSSPI: { if (context.IntegratedSecurity) { // For SSPI we have to get the IP-Address (hostname doesn't work) string ipAddressString = ((IPEndPoint)context.Socket.RemoteEndPoint).Address.ToString(); context.SSPI = new SSPIHandler(ipAddressString, "POSTGRES"); ChangeState(context, NpgsqlStartupState.Instance); context.Authenticate(context.SSPI.Continue(null)); break; } else { // TODO: correct exception throw new Exception(); } } case AuthenticationRequestType.AuthenticationGSSContinue: { byte[] authData = new byte[authDataLength]; PGUtil.CheckedStreamRead(stream, authData, 0, authDataLength); byte[] passwd_read = context.SSPI.Continue(authData); if (passwd_read.Length != 0) { context.Authenticate(passwd_read); } break; } #endif default: // Only AuthenticationClearTextPassword and AuthenticationMD5Password supported for now. errors.Add( new NpgsqlError(context.BackendProtocolVersion, String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType))); throw new NpgsqlException(errors); } break; case BackEndMessageCode.RowDescription: yield return(new NpgsqlRowDescriptionV3(stream, context.OidToNameMapping, context.CompatVersion)); break; case BackEndMessageCode.ParameterDescription: // Do nothing,for instance, just read... int lenght = PGUtil.ReadInt32(stream); int nb_param = PGUtil.ReadInt16(stream); for (int i = 0; i < nb_param; i++) { int typeoid = PGUtil.ReadInt32(stream); } break; case BackEndMessageCode.DataRow: yield return(new StringRowReaderV3(stream)); break; case BackEndMessageCode.ReadyForQuery: // NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ReadyForQuery"); // Possible status bytes returned: // I = Idle (no transaction active). // T = In transaction, ready for more. // E = Error in transaction, queries will fail until transaction aborted. // Just eat the status byte, we have no use for it at this time. PGUtil.ReadInt32(stream); stream.ReadByte(); ChangeState(context, NpgsqlReadyState.Instance); if (errors.Count != 0) { throw new NpgsqlException(errors); } yield break; case BackEndMessageCode.BackendKeyData: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BackendKeyData"); // BackendKeyData message. NpgsqlBackEndKeyData backend_keydata = new NpgsqlBackEndKeyData(context.BackendProtocolVersion, stream); context.BackEndKeyData = backend_keydata; // Wait for ReadForQuery message break; case BackEndMessageCode.NoticeResponse: // Notices and errors are identical except that we // just throw notices away completely ignored. context.FireNotice(new NpgsqlError(context.BackendProtocolVersion, stream)); break; case BackEndMessageCode.CompletedResponse: PGUtil.ReadInt32(stream); yield return(new CompletedResponse(stream)); break; case BackEndMessageCode.ParseComplete: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParseComplete"); // Just read up the message length. PGUtil.ReadInt32(stream); break; case BackEndMessageCode.BindComplete: // NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BindComplete"); // Just read up the message length. PGUtil.ReadInt32(stream); break; case BackEndMessageCode.EmptyQueryResponse: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "EmptyQueryResponse"); PGUtil.ReadInt32(stream); break; case BackEndMessageCode.NotificationResponse: // Eat the length PGUtil.ReadInt32(stream); context.FireNotification(new NpgsqlNotificationEventArgs(stream, true)); if (context.IsNotificationThreadRunning) { yield break; } break; case BackEndMessageCode.ParameterStatus: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParameterStatus"); NpgsqlParameterStatus parameterStatus = new NpgsqlParameterStatus(stream); NpgsqlEventLog.LogMsg(resman, "Log_ParameterStatus", LogLevel.Debug, parameterStatus.Parameter, parameterStatus.ParameterValue); context.AddParameterStatus(parameterStatus); if (parameterStatus.Parameter == "server_version") { // Deal with this here so that if there are // changes in a future backend version, we can handle it here in the // protocol handler and leave everybody else put of it. string versionString = parameterStatus.ParameterValue.Trim(); for (int idx = 0; idx != versionString.Length; ++idx) { char c = parameterStatus.ParameterValue[idx]; if (!char.IsDigit(c) && c != '.') { versionString = versionString.Substring(0, idx); break; } } context.ServerVersion = new Version(versionString); } break; case BackEndMessageCode.NoData: // This nodata message may be generated by prepare commands issued with queries which doesn't return rows // for example insert, update or delete. // Just eat the message. NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParameterStatus"); PGUtil.ReadInt32(stream); break; case BackEndMessageCode.CopyInResponse: // Enter COPY sub protocol and start pushing data to server NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CopyInResponse"); ChangeState(context, NpgsqlCopyInState.Instance); PGUtil.ReadInt32(stream); // length redundant context.CurrentState.StartCopy(context, ReadCopyHeader(stream)); yield break; // Either StartCopy called us again to finish the operation or control should be passed for user to feed copy data case BackEndMessageCode.CopyOutResponse: // Enter COPY sub protocol and start pulling data from server NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CopyOutResponse"); ChangeState(context, NpgsqlCopyOutState.Instance); PGUtil.ReadInt32(stream); // length redundant context.CurrentState.StartCopy(context, ReadCopyHeader(stream)); yield break; // Either StartCopy called us again to finish the operation or control should be passed for user to feed copy data case BackEndMessageCode.CopyData: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CopyData"); Int32 len = PGUtil.ReadInt32(stream) - 4; byte[] buf = new byte[len]; PGUtil.ReadBytes(stream, buf, 0, len); context.Mediator.ReceivedCopyData = buf; yield break; // read data from server one chunk at a time while staying in copy operation mode case BackEndMessageCode.CopyDone: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CopyDone"); PGUtil.ReadInt32(stream); // CopyDone can not have content so this is always 4 // This will be followed by normal CommandComplete + ReadyForQuery so no op needed break; case BackEndMessageCode.IO_ERROR: // Connection broken. Mono returns -1 instead of throwing an exception as ms.net does. throw new IOException(); default: // This could mean a number of things // We've gotten out of sync with the backend? // We need to implement this type? // Backend has gone insane? // FIXME // what exception should we really throw here? throw new NotSupportedException(String.Format("Backend sent unrecognized response type: {0}", (Char)message)); } } } }
protected IEnumerable <IServerResponseObject> ProcessBackendResponses_Ver_2(NpgsqlConnector context) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses"); using (new ContextResetter(context)) { Stream stream = context.Stream; NpgsqlMediator mediator = context.Mediator; List <NpgsqlError> errors = new List <NpgsqlError>(); for (;;) { // Check the first Byte of response. switch ((BackEndMessageCode)stream.ReadByte()) { case BackEndMessageCode.ErrorResponse: { NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion, stream); error.ErrorSql = mediator.GetSqlSent(); errors.Add(error); NpgsqlEventLog.LogMsg(resman, "Log_ErrorResponse", LogLevel.Debug, error.Message); } // Return imediately if it is in the startup state or connected state as // there is no more messages to consume. // Possible error in the NpgsqlStartupState: // Invalid password. // Possible error in the NpgsqlConnectedState: // No pg_hba.conf configured. if (!context.RequireReadyForQuery) { throw new NpgsqlException(errors); } break; case BackEndMessageCode.AuthenticationRequest: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AuthenticationRequest"); AuthenticationRequestType authType = (AuthenticationRequestType)PGUtil.ReadInt32(stream); switch (authType) { case AuthenticationRequestType.AuthenticationOk: NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug); break; case AuthenticationRequestType.AuthenticationClearTextPassword: NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationClearTextRequest", LogLevel.Debug); // Send the PasswordPacket. ChangeState(context, NpgsqlStartupState.Instance); context.Authenticate(context.Password); context.Stream.Flush(); break; case AuthenticationRequestType.AuthenticationMD5Password: NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationMD5Request", LogLevel.Debug); // Now do the "MD5-Thing" // for this the Password has to be: // 1. md5-hashed with the username as salt // 2. md5-hashed again with the salt we get from the backend MD5 md5 = MD5.Create(); // 1. byte[] passwd = context.Password; byte[] saltUserName = BackendEncoding.UTF8Encoding.GetBytes(context.UserName); byte[] crypt_buf = new byte[passwd.Length + saltUserName.Length]; passwd.CopyTo(crypt_buf, 0); saltUserName.CopyTo(crypt_buf, passwd.Length); StringBuilder sb = new StringBuilder(); byte[] hashResult = md5.ComputeHash(crypt_buf); foreach (byte b in hashResult) { sb.Append(b.ToString("x2")); } String prehash = sb.ToString(); byte[] prehashbytes = BackendEncoding.UTF8Encoding.GetBytes(prehash); byte[] saltServer = new byte[4]; stream.Read(saltServer, 0, 4); // Send the PasswordPacket. ChangeState(context, NpgsqlStartupState.Instance); // 2. crypt_buf = new byte[prehashbytes.Length + saltServer.Length]; prehashbytes.CopyTo(crypt_buf, 0); saltServer.CopyTo(crypt_buf, prehashbytes.Length); sb = new StringBuilder("md5"); // This is needed as the backend expects md5 result starts with "md5" hashResult = md5.ComputeHash(crypt_buf); foreach (byte b in hashResult) { sb.Append(b.ToString("x2")); } context.Authenticate(BackendEncoding.UTF8Encoding.GetBytes(sb.ToString())); context.Stream.Flush(); break; default: // Only AuthenticationClearTextPassword and AuthenticationMD5Password supported for now. errors.Add( new NpgsqlError(context.BackendProtocolVersion, String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType))); throw new NpgsqlException(errors); } break; case BackEndMessageCode.RowDescription: yield return(new NpgsqlRowDescriptionV2(stream, context.OidToNameMapping, context.CompatVersion)); break; case BackEndMessageCode.DataRow: yield return(new StringRowReaderV2(stream)); break; case BackEndMessageCode.BinaryRow: throw new NotSupportedException(); case BackEndMessageCode.ReadyForQuery: ChangeState(context, NpgsqlReadyState.Instance); if (errors.Count != 0) { throw new NpgsqlException(errors); } yield break; case BackEndMessageCode.BackendKeyData: context.BackEndKeyData = new NpgsqlBackEndKeyData(context.BackendProtocolVersion, stream); break; case BackEndMessageCode.NoticeResponse: context.FireNotice(new NpgsqlError(context.BackendProtocolVersion, stream)); break; case BackEndMessageCode.CompletedResponse: yield return(new CompletedResponse(stream)); break; case BackEndMessageCode.CursorResponse: // This is the cursor response message. // It is followed by a C NULL terminated string with the name of // the cursor in a FETCH case or 'blank' otherwise. // In this case it should be always 'blank'. // [FIXME] Get another name for this function. NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CursorResponse"); String cursorName = PGUtil.ReadString(stream); // Continue waiting for ReadyForQuery message. break; case BackEndMessageCode.EmptyQueryResponse: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "EmptyQueryResponse"); PGUtil.ReadString(stream); break; case BackEndMessageCode.NotificationResponse: context.FireNotification(new NpgsqlNotificationEventArgs(stream, false)); if (context.IsNotificationThreadRunning) { yield break; } break; case BackEndMessageCode.IO_ERROR: // Connection broken. Mono returns -1 instead of throw an exception as ms.net does. throw new IOException(); default: // This could mean a number of things // We've gotten out of sync with the backend? // We need to implement this type? // Backend has gone insane? throw new DataException("Backend sent unrecognized response type"); } } } }
protected virtual void ProcessBackendResponses_Ver_2(NpgsqlConnector context) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses"); Stream stream = context.Stream; NpgsqlMediator mediator = context.Mediator; // Often used buffer Byte[] inputBuffer = new Byte[4]; Boolean readyForQuery = false; byte[] asciiRowBytes = new byte[300]; char[] asciiRowChars = new char[300]; while (!readyForQuery) { // Check the first Byte of response. switch (stream.ReadByte()) { case NpgsqlMessageTypes_Ver_2.ErrorResponse: { NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion); error.ReadFromStream(stream, context.Encoding); error.ErrorSql = mediator.SqlSent; mediator.Errors.Add(error); NpgsqlEventLog.LogMsg(resman, "Log_ErrorResponse", LogLevel.Debug, error.Message); } // Return imediately if it is in the startup state or connected state as // there is no more messages to consume. // Possible error in the NpgsqlStartupState: // Invalid password. // Possible error in the NpgsqlConnectedState: // No pg_hba.conf configured. if (!mediator.RequireReadyForQuery) { return; } break; case NpgsqlMessageTypes_Ver_2.AuthenticationRequest: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AuthenticationRequest"); { Int32 authType = PGUtil.ReadInt32(stream, inputBuffer); if (authType == NpgsqlMessageTypes_Ver_2.AuthenticationOk) { NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug); break; } if (authType == NpgsqlMessageTypes_Ver_2.AuthenticationClearTextPassword) { NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationClearTextRequest", LogLevel.Debug); // Send the PasswordPacket. ChangeState(context, NpgsqlStartupState.Instance); context.Authenticate(context.Password); break; } if (authType == NpgsqlMessageTypes_Ver_2.AuthenticationMD5Password) { NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationMD5Request", LogLevel.Debug); // Now do the "MD5-Thing" // for this the Password has to be: // 1. md5-hashed with the username as salt // 2. md5-hashed again with the salt we get from the backend MD5 md5 = MD5.Create(); // 1. byte[] passwd = context.Encoding.GetBytes(context.Password); byte[] saltUserName = context.Encoding.GetBytes(context.UserName); byte[] crypt_buf = new byte[passwd.Length + saltUserName.Length]; passwd.CopyTo(crypt_buf, 0); saltUserName.CopyTo(crypt_buf, passwd.Length); StringBuilder sb = new StringBuilder(); byte[] hashResult = md5.ComputeHash(crypt_buf); foreach (byte b in hashResult) { sb.Append(b.ToString("x2")); } String prehash = sb.ToString(); byte[] prehashbytes = context.Encoding.GetBytes(prehash); byte[] saltServer = new byte[4]; stream.Read(saltServer, 0, 4); // Send the PasswordPacket. ChangeState(context, NpgsqlStartupState.Instance); // 2. crypt_buf = new byte[prehashbytes.Length + saltServer.Length]; prehashbytes.CopyTo(crypt_buf, 0); saltServer.CopyTo(crypt_buf, prehashbytes.Length); sb = new StringBuilder("md5"); // This is needed as the backend expects md5 result starts with "md5" hashResult = md5.ComputeHash(crypt_buf); foreach (byte b in hashResult) { sb.Append(b.ToString("x2")); } context.Authenticate(sb.ToString()); break; } // Only AuthenticationClearTextPassword and AuthenticationMD5Password supported for now. mediator.Errors.Add(new NpgsqlError(context.BackendProtocolVersion, String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType))); } return; case NpgsqlMessageTypes_Ver_2.RowDescription: // This is the RowDescription message. NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "RowDescription"); { NpgsqlRowDescription rd = new NpgsqlRowDescription(context.BackendProtocolVersion); rd.ReadFromStream(stream, context.Encoding, context.OidToNameMapping); // Initialize the array list which will contain the data from this rowdescription. mediator.AddRowDescription(rd); } // Now wait for the AsciiRow messages. break; case NpgsqlMessageTypes_Ver_2.AsciiRow: // This is the AsciiRow message. NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AsciiRow"); { NpgsqlAsciiRow asciiRow = new NpgsqlAsciiRow(context.Mediator.LastRowDescription, context.BackendProtocolVersion, asciiRowBytes, asciiRowChars); asciiRow.ReadFromStream(stream, context.Encoding); // Add this row to the rows array. mediator.AddAsciiRow(asciiRow); } // Now wait for CompletedResponse message. break; case NpgsqlMessageTypes_Ver_2.BinaryRow: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BinaryRow"); { NpgsqlBinaryRow binaryRow = new NpgsqlBinaryRow(context.Mediator.LastRowDescription); binaryRow.ReadFromStream(stream, context.Encoding); mediator.AddBinaryRow(binaryRow); } break; case NpgsqlMessageTypes_Ver_2.ReadyForQuery: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ReadyForQuery"); readyForQuery = true; ChangeState(context, NpgsqlReadyState.Instance); break; case NpgsqlMessageTypes_Ver_2.BackendKeyData: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BackendKeyData"); // BackendKeyData message. NpgsqlBackEndKeyData backend_keydata = new NpgsqlBackEndKeyData(context.BackendProtocolVersion); backend_keydata.ReadFromStream(stream); mediator.SetBackendKeydata(backend_keydata); // Wait for ReadForQuery message break; ; case NpgsqlMessageTypes_Ver_2.NoticeResponse: { NpgsqlError notice = new NpgsqlError(context.BackendProtocolVersion); notice.ReadFromStream(stream, context.Encoding); mediator.Notices.Add(notice); NpgsqlEventLog.LogMsg(resman, "Log_NoticeResponse", LogLevel.Debug, notice.Message); } // Wait for ReadForQuery message break; case NpgsqlMessageTypes_Ver_2.CompletedResponse: // This is the CompletedResponse message. // Get the string returned. String result = PGUtil.ReadString(stream, context.Encoding); NpgsqlEventLog.LogMsg(resman, "Log_CompletedResponse", LogLevel.Debug, result); // Add result from the processing. mediator.AddCompletedResponse(result); // Now wait for ReadyForQuery message. break; case NpgsqlMessageTypes_Ver_2.CursorResponse: // This is the cursor response message. // It is followed by a C NULL terminated string with the name of // the cursor in a FETCH case or 'blank' otherwise. // In this case it should be always 'blank'. // [FIXME] Get another name for this function. NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CursorResponse"); String cursorName = PGUtil.ReadString(stream, context.Encoding); // Continue waiting for ReadyForQuery message. break; case NpgsqlMessageTypes_Ver_2.EmptyQueryResponse: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "EmptyQueryResponse"); PGUtil.ReadString(stream, context.Encoding); break; case NpgsqlMessageTypes_Ver_2.NotificationResponse: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "NotificationResponse"); Int32 PID = PGUtil.ReadInt32(stream, inputBuffer); String notificationResponse = PGUtil.ReadString(stream, context.Encoding); mediator.AddNotification(new NpgsqlNotificationEventArgs(PID, notificationResponse)); if (context.IsNotificationThreadRunning) { readyForQuery = true; } // Wait for ReadForQuery message break; case -1: // Connection broken. Mono returns -1 instead of throw an exception as ms.net does. throw new IOException(); default: // This could mean a number of things // We've gotten out of sync with the backend? // We need to implement this type? // Backend has gone insane? // FIXME // what exception should we really throw here? throw new NotSupportedException("Backend sent unrecognized response type"); } } }
private void ReadFromStream_Ver_3(Stream inputStream, Encoding encoding) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream_Ver_3"); Int32 messageLength = PGUtil.ReadInt32(inputStream, new Byte[4]); // [TODO] Would this be the right way to do? // Check the messageLength value. If it is 1178686529, this would be the // "FATA" string, which would mean a protocol 2.0 error string. if (messageLength == 1178686529) { String Raw; String[] Parts; Raw = "FATA" + PGUtil.ReadString(inputStream, encoding); Parts = Raw.Split(new char[] { ':' }, 2); if (Parts.Length == 2) { _severity = Parts[0].Trim(); _message = Parts[1].Trim(); } else { _message = Parts[0].Trim(); } protocol_version = ProtocolVersion.Version2; return; } Char field; String fieldValue; field = (Char)inputStream.ReadByte(); // Now start to read fields. while (field != 0) { fieldValue = PGUtil.ReadString(inputStream, encoding); switch (field) { case 'S': _severity = fieldValue; break; case 'C': _code = fieldValue; break; case 'M': _message = fieldValue; break; case 'D': _detail = fieldValue; break; case 'H': _hint = fieldValue; break; case 'P': _position = fieldValue; break; case 'W': _where = fieldValue; break; case 'F': _file = fieldValue; break; case 'L': _line = fieldValue; break; case 'R': _routine = fieldValue; break; } field = (Char)inputStream.ReadByte(); } }
internal NpgsqlError(ProtocolVersion protocolVersion, Stream stream) { switch (protocol_version = protocolVersion) { case ProtocolVersion.Version2: string[] parts = PGUtil.ReadString(stream).Split(new char[] { ':' }, 2); if (parts.Length == 2) { _severity = parts[0].Trim(); _message = parts[1].Trim(); } else { _severity = string.Empty; _message = parts[0].Trim(); } break; case ProtocolVersion.Version3: // Check the messageLength value. If it is 1178686529, this would be the // "FATA" string, which would mean a protocol 2.0 error string. if (PGUtil.ReadInt32(stream) == 1178686529) { string[] v2Parts = ("FATA" + PGUtil.ReadString(stream)).Split(new char[] { ':' }, 2); if (v2Parts.Length == 2) { _severity = v2Parts[0].Trim(); _message = v2Parts[1].Trim(); } else { _severity = string.Empty; _message = v2Parts[0].Trim(); } protocol_version = ProtocolVersion.Version2; } else { for (char field = (char)stream.ReadByte(); field != 0; field = (char)stream.ReadByte()) { switch (field) { case 'S': _severity = PGUtil.ReadString(stream); ; break; case 'C': _code = PGUtil.ReadString(stream); ; break; case 'M': _message = PGUtil.ReadString(stream); ; break; case 'D': _detail = PGUtil.ReadString(stream); ; break; case 'H': _hint = PGUtil.ReadString(stream); ; break; case 'P': _position = PGUtil.ReadString(stream); ; break; case 'p': _internalPosition = PGUtil.ReadString(stream); ; break; case 'q': _internalQuery = PGUtil.ReadString(stream); ; break; case 'W': _where = PGUtil.ReadString(stream); ; break; case 'F': _file = PGUtil.ReadString(stream); ; break; case 'L': _line = PGUtil.ReadString(stream); ; break; case 'R': _routine = PGUtil.ReadString(stream); ; break; } } } break; } }
protected virtual void ProcessBackendResponses_Ver_3(NpgsqlConnector context) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses"); Stream stream = context.Stream; NpgsqlMediator mediator = context.Mediator; // Often used buffers Byte[] inputBuffer = new Byte[4]; String Str; Boolean readyForQuery = false; byte[] asciiRowBytes = new byte[300]; char[] asciiRowChars = new char[300]; while (!readyForQuery) { // Check the first Byte of response. Int32 message = stream.ReadByte(); switch (message) { case NpgsqlMessageTypes_Ver_3.ErrorResponse: { NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion); error.ReadFromStream(stream, context.Encoding); error.ErrorSql = mediator.SqlSent; mediator.Errors.Add(error); NpgsqlEventLog.LogMsg(resman, "Log_ErrorResponse", LogLevel.Debug, error.Message); } // Return imediately if it is in the startup state or connected state as // there is no more messages to consume. // Possible error in the NpgsqlStartupState: // Invalid password. // Possible error in the NpgsqlConnectedState: // No pg_hba.conf configured. if (!mediator.RequireReadyForQuery) { return; } break; case NpgsqlMessageTypes_Ver_3.AuthenticationRequest: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AuthenticationRequest"); // Eat length PGUtil.ReadInt32(stream, inputBuffer); { Int32 authType = PGUtil.ReadInt32(stream, inputBuffer); if (authType == NpgsqlMessageTypes_Ver_3.AuthenticationOk) { NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug); break; } if (authType == NpgsqlMessageTypes_Ver_3.AuthenticationClearTextPassword) { NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationClearTextRequest", LogLevel.Debug); // Send the PasswordPacket. ChangeState(context, NpgsqlStartupState.Instance); context.Authenticate(context.Password); break; } if (authType == NpgsqlMessageTypes_Ver_3.AuthenticationMD5Password) { NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationMD5Request", LogLevel.Debug); // Now do the "MD5-Thing" // for this the Password has to be: // 1. md5-hashed with the username as salt // 2. md5-hashed again with the salt we get from the backend MD5 md5 = MD5.Create(); // 1. byte[] passwd = context.Encoding.GetBytes(context.Password); byte[] saltUserName = context.Encoding.GetBytes(context.UserName); byte[] crypt_buf = new byte[passwd.Length + saltUserName.Length]; passwd.CopyTo(crypt_buf, 0); saltUserName.CopyTo(crypt_buf, passwd.Length); StringBuilder sb = new StringBuilder(); byte[] hashResult = md5.ComputeHash(crypt_buf); foreach (byte b in hashResult) { sb.Append(b.ToString("x2")); } String prehash = sb.ToString(); byte[] prehashbytes = context.Encoding.GetBytes(prehash); stream.Read(inputBuffer, 0, 4); // Send the PasswordPacket. ChangeState(context, NpgsqlStartupState.Instance); // 2. crypt_buf = new byte[prehashbytes.Length + 4]; prehashbytes.CopyTo(crypt_buf, 0); inputBuffer.CopyTo(crypt_buf, prehashbytes.Length); sb = new StringBuilder("md5"); // This is needed as the backend expects md5 result starts with "md5" hashResult = md5.ComputeHash(crypt_buf); foreach (byte b in hashResult) { sb.Append(b.ToString("x2")); } context.Authenticate(sb.ToString()); break; } // Only AuthenticationClearTextPassword and AuthenticationMD5Password supported for now. mediator.Errors.Add(new NpgsqlError(context.BackendProtocolVersion, String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType))); } return; case NpgsqlMessageTypes_Ver_3.RowDescription: // This is the RowDescription message. NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "RowDescription"); { NpgsqlRowDescription rd = new NpgsqlRowDescription(context.BackendProtocolVersion); rd.ReadFromStream(stream, context.Encoding, context.OidToNameMapping); mediator.AddRowDescription(rd); } // Now wait for the AsciiRow messages. break; case NpgsqlMessageTypes_Ver_3.ParameterDescription: // Do nothing,for instance, just read... int lenght = PGUtil.ReadInt32(stream, inputBuffer); int nb_param = PGUtil.ReadInt16(stream, inputBuffer); for (int i = 0; i < nb_param; i++) { int typeoid = PGUtil.ReadInt32(stream, inputBuffer); } break; case NpgsqlMessageTypes_Ver_3.DataRow: // This is the AsciiRow message. NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "DataRow"); { NpgsqlAsciiRow asciiRow = new NpgsqlAsciiRow(context.Mediator.LastRowDescription, context.BackendProtocolVersion, asciiRowBytes, asciiRowChars); asciiRow.ReadFromStream(stream, context.Encoding); // Add this row to the rows array. mediator.AddAsciiRow(asciiRow); } // Now wait for CompletedResponse message. break; case NpgsqlMessageTypes_Ver_3.ReadyForQuery: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ReadyForQuery"); // Possible status bytes returned: // I = Idle (no transaction active). // T = In transaction, ready for more. // E = Error in transaction, queries will fail until transaction aborted. // Just eat the status byte, we have no use for it at this time. PGUtil.ReadInt32(stream, inputBuffer); PGUtil.ReadString(stream, context.Encoding, 1); readyForQuery = true; ChangeState(context, NpgsqlReadyState.Instance); break; case NpgsqlMessageTypes_Ver_3.BackendKeyData: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BackendKeyData"); // BackendKeyData message. NpgsqlBackEndKeyData backend_keydata = new NpgsqlBackEndKeyData(context.BackendProtocolVersion); backend_keydata.ReadFromStream(stream); mediator.SetBackendKeydata(backend_keydata); // Wait for ReadForQuery message break; case NpgsqlMessageTypes_Ver_3.NoticeResponse: // Notices and errors are identical except that we // just throw notices away completely ignored. { NpgsqlError notice = new NpgsqlError(context.BackendProtocolVersion); notice.ReadFromStream(stream, context.Encoding); mediator.Notices.Add(notice); NpgsqlEventLog.LogMsg(resman, "Log_NoticeResponse", LogLevel.Debug, notice.Message); } // Wait for ReadForQuery message break; case NpgsqlMessageTypes_Ver_3.CompletedResponse: // This is the CompletedResponse message. // Get the string returned. PGUtil.ReadInt32(stream, inputBuffer); Str = PGUtil.ReadString(stream, context.Encoding); NpgsqlEventLog.LogMsg(resman, "Log_CompletedResponse", LogLevel.Debug, Str); // Add result from the processing. mediator.AddCompletedResponse(Str); break; case NpgsqlMessageTypes_Ver_3.ParseComplete: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParseComplete"); // Just read up the message length. PGUtil.ReadInt32(stream, inputBuffer); readyForQuery = true; break; case NpgsqlMessageTypes_Ver_3.BindComplete: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BindComplete"); // Just read up the message length. PGUtil.ReadInt32(stream, inputBuffer); readyForQuery = true; break; case NpgsqlMessageTypes_Ver_3.EmptyQueryResponse: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "EmptyQueryResponse"); PGUtil.ReadInt32(stream, inputBuffer); break; case NpgsqlMessageTypes_Ver_3.NotificationResponse: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "NotificationResponse"); // Eat the length PGUtil.ReadInt32(stream, inputBuffer); { // Process ID sending notification Int32 PID = PGUtil.ReadInt32(stream, inputBuffer); // Notification string String notificationResponse = PGUtil.ReadString(stream, context.Encoding); // Additional info, currently not implemented by PG (empty string always), eat it PGUtil.ReadString(stream, context.Encoding); mediator.AddNotification(new NpgsqlNotificationEventArgs(PID, notificationResponse)); } if (context.IsNotificationThreadRunning) { readyForQuery = true; } // Wait for ReadForQuery message break; case NpgsqlMessageTypes_Ver_3.ParameterStatus: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParameterStatus"); NpgsqlParameterStatus parameterStatus = new NpgsqlParameterStatus(); parameterStatus.ReadFromStream(stream, context.Encoding); NpgsqlEventLog.LogMsg(resman, "Log_ParameterStatus", LogLevel.Debug, parameterStatus.Parameter, parameterStatus.ParameterValue); mediator.AddParameterStatus(parameterStatus.Parameter, parameterStatus); if (parameterStatus.Parameter == "server_version") { // Add this one under our own name so that if the parameter name // changes in a future backend version, we can handle it here in the // protocol handler and leave everybody else put of it. mediator.AddParameterStatus("__npgsql_server_version", parameterStatus); // context.ServerVersionString = parameterStatus.ParameterValue; } break; case NpgsqlMessageTypes_Ver_3.NoData: // This nodata message may be generated by prepare commands issued with queries which doesn't return rows // for example insert, update or delete. // Just eat the message. NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParameterStatus"); PGUtil.ReadInt32(stream, inputBuffer); break; case -1: // Connection broken. Mono returns -1 instead of throw an exception as ms.net does. throw new IOException(); default: // This could mean a number of things // We've gotten out of sync with the backend? // We need to implement this type? // Backend has gone insane? // FIXME // what exception should we really throw here? throw new NotSupportedException(String.Format("Backend sent unrecognized response type: {0}", (Char)message)); } } }
protected override int GetNextFieldCount() { return(_nullMap.IsNull(CurrentField) ? -1 : PGUtil.ReadInt32(Stream) - 4); }
internal NpgsqlError(ProtocolVersion protocolVersion, Stream stream) { switch (protocol_version = protocolVersion) { case ProtocolVersion.Version2: string[] parts = PGUtil.ReadString(stream).Split(new char[] { ':' }, 2); if (parts.Length == 2) { _severity = parts[0].Trim(); _message = parts[1].Trim(); } else { _severity = string.Empty; _message = parts[0].Trim(); } break; case ProtocolVersion.Version3: // Check the messageLength value. If it is 1178686529, this would be the // "FATA" string, which would mean a protocol 2.0 error string. if (PGUtil.ReadInt32(stream) == 1178686529) { string[] v2Parts = ("FATA" + PGUtil.ReadString(stream)).Split(new char[] { ':' }, 2); if (v2Parts.Length == 2) { _severity = v2Parts[0].Trim(); _message = v2Parts[1].Trim(); } else { _severity = string.Empty; _message = v2Parts[0].Trim(); } protocol_version = ProtocolVersion.Version2; } else { bool done = false; int fieldCode; while (!done && (fieldCode = stream.ReadByte()) != -1) { switch ((byte)fieldCode) { case 0: // Null terminator; error message fully consumed. done = true; ; break; case (byte)ErrorFieldTypeCodes.Severity: _severity = PGUtil.ReadString(stream); ; break; case (byte)ErrorFieldTypeCodes.Code: _code = PGUtil.ReadString(stream); ; break; case (byte)ErrorFieldTypeCodes.Message: _message = PGUtil.ReadString(stream); ; break; case (byte)ErrorFieldTypeCodes.Detail: _detail = PGUtil.ReadString(stream); ; break; case (byte)ErrorFieldTypeCodes.Hint: _hint = PGUtil.ReadString(stream); ; break; case (byte)ErrorFieldTypeCodes.Position: _position = PGUtil.ReadString(stream); ; break; case (byte)ErrorFieldTypeCodes.InternalPosition: _internalPosition = PGUtil.ReadString(stream); ; break; case (byte)ErrorFieldTypeCodes.InternalQuery: _internalQuery = PGUtil.ReadString(stream); ; break; case (byte)ErrorFieldTypeCodes.Where: _where = PGUtil.ReadString(stream); ; break; case (byte)ErrorFieldTypeCodes.File: _file = PGUtil.ReadString(stream); ; break; case (byte)ErrorFieldTypeCodes.Line: _line = PGUtil.ReadString(stream); ; break; case (byte)ErrorFieldTypeCodes.Routine: _routine = PGUtil.ReadString(stream); ; break; case (byte)ErrorFieldTypeCodes.SchemaName: _schemaName = PGUtil.ReadString(stream); ; break; case (byte)ErrorFieldTypeCodes.TableName: _tableName = PGUtil.ReadString(stream); ; break; case (byte)ErrorFieldTypeCodes.ColumnName: _columnName = PGUtil.ReadString(stream); ; break; case (byte)ErrorFieldTypeCodes.DataTypeName: _datatypeName = PGUtil.ReadString(stream); ; break; case (byte)ErrorFieldTypeCodes.ConstraintName: _constraintName = PGUtil.ReadString(stream); ; break; default: // Unknown error field; consume and discard. PGUtil.ReadString(stream); ; break; } } } break; } }
public StringRowReaderV3(Stream inputStream) : base(inputStream) { _messageSize = PGUtil.ReadInt32(inputStream); }
private int GetThisFieldCount() { return((_nextFieldSize = _nextFieldSize ?? PGUtil.ReadInt32(Stream)).Value); }
internal NpgsqlNotificationEventArgs(Stream stream, bool readAdditional) { PID = PGUtil.ReadInt32(stream); Condition = PGUtil.ReadString(stream); AdditionalInformation = readAdditional ? PGUtil.ReadString(stream) : string.Empty; }