1 /*
2  * Copyright (C) 2018 Julien Viet
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 module hunt.database.driver.postgresql.impl.codec.InitCommandCodec;
18 
19 import hunt.database.driver.postgresql.impl.codec.ErrorResponse;
20 import hunt.database.driver.postgresql.impl.codec.PasswordMessage;
21 import hunt.database.driver.postgresql.impl.codec.CommandCodec;
22 import hunt.database.driver.postgresql.impl.codec.PgEncoder;
23 import hunt.database.driver.postgresql.impl.codec.StartupMessage;
24 
25 import hunt.database.base.impl.TxStatus;
26 import hunt.database.base.impl.command.CommandResponse;
27 import hunt.database.base.impl.Connection;
28 import hunt.database.base.impl.command.InitCommand;
29 import hunt.database.driver.postgresql.impl.PostgreSQLSocketConnection;
30 
31 import hunt.logging;
32 import hunt.Exceptions;
33 
34 class InitCommandCodec : CommandCodec!(DbConnection, InitCommand) {
35 
36     private PgEncoder encoder;
37     private string encoding;
38     private PgSocketConnection pgConnection;
39 
40     this(InitCommand cmd) {
41         super(cmd);
42         pgConnection = cast(PgSocketConnection)cmd.connection();
43     }
44 
45     override
46     void encode(PgEncoder encoder) {
47         version(HUNT_DB_DEBUG) tracef("encoding...");
48         this.encoder = encoder;
49         encoder.writeStartupMessage(new StartupMessage(cmd.username(), cmd.database(), cmd.properties()));
50         // encoder.flush();
51     }
52 
53     override
54     void handleAuthenticationMD5Password(byte[] salt) {
55         version(HUNT_DB_DEBUG_MORE) tracef("salt: %(%02X %)", salt);
56         encoder.writePasswordMessage(new PasswordMessage(cmd.username(), cmd.password(), salt));
57         encoder.flush();
58     }
59 
60     override
61     void handleAuthenticationClearTextPassword() {
62         version(HUNT_DB_DEBUG) tracef("running here");
63         encoder.writePasswordMessage(new PasswordMessage(cmd.username(), cmd.password(), null));
64         encoder.flush();
65     }
66 
67     override
68     void handleAuthenticationOk() {
69         version(HUNT_DB_DEBUG) info("TODO: Authentication done.");
70         // TODO: Tasks pending completion -@zxp at Fri, 20 Sep 2019 02:31:50 GMT
71         // Return the server setup information.
72 //      handler.handle(Future.succeededFuture(conn));
73 //      handler = null;
74     }
75 
76     override
77     void handleParameterStatus(string name, string value) {
78         version(HUNT_DB_DEBUG_MORE) tracef("key: %s, value: %s", name, value);
79         // FIXME: Needing refactor or cleanup -@zxp at Fri, 20 Sep 2019 02:25:36 GMT
80         // handle more status
81         // pgjdbc\src\main\java\org\postgresql\core\v3\QueryExecutorImpl.java
82 
83         switch(name) {
84             case "client_encoding":
85                 encoding = value; break;
86 
87             case "standard_conforming_strings":
88                 pgConnection.setStandardConformingStrings(value == "on");
89                 break;
90 
91             default: break;
92         }
93 
94         if (name == "standard_conforming_strings") {
95 
96         }
97     }
98 
99     override
100     void handleBackendKeyData(int processId, int secretKey) {
101         version(HUNT_DB_DEBUG) tracef("processId: %d, secretKey: %d", processId, secretKey);
102         pgConnection.processId = processId;
103         pgConnection.secretKey = secretKey;
104     }
105 
106     override
107     void handleErrorResponse(ErrorResponse errorResponse) {
108         version(HUNT_DB_DEBUG) warningf("errorResponse: %s", errorResponse.toString());
109         CommandResponse!(DbConnection) resp = failedResponse!DbConnection(errorResponse.toException());
110         if(completionHandler !is null) {
111             // resp.cmd = cmd;
112             completionHandler(resp);
113         }
114     }
115 
116     override
117     void handleReadyForQuery(TxStatus txStatus) {
118         version(HUNT_DB_DEBUG) tracef("txStatus: %s, encoding: %s", txStatus, encoding);
119         // The final phase before returning the connection
120         // We should make sure we are supporting only UTF8
121         // https://www.postgresql.org/docs/9.5/static/multibyte.html#MULTIBYTE-CHARSET-SUPPORTED
122         // Charset cs = null;
123         // try {
124         //     cs = Charset.forName(encoding);
125         // } catch (Exception ignore) {
126         // }
127 
128         if(completionHandler !is null) {
129             CommandResponse!(DbConnection) resp;
130             if(encoding != "UTF8") {
131                 resp = failedResponse!(DbConnection)(encoding ~ " is not supported in the client only UTF8");
132             } else {
133                 resp = succeededResponse!(DbConnection)(cmd.connection());
134             }
135             // resp.cmd = cmd;
136             completionHandler(resp);
137         }
138     }
139 }