Fix changelog email address
[freerdp-ubuntu-pcb-backport.git] / channels / drdynvc / drdynvc_main.c
1 /**
2  * FreeRDP: A Remote Desktop Protocol client.
3  * Dynamic Virtual Channel
4  *
5  * Copyright 2010-2011 Vic Lee
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <freerdp/constants.h>
24 #include <freerdp/utils/memory.h>
25 #include <freerdp/utils/stream.h>
26 #include <freerdp/utils/svc_plugin.h>
27 #include <freerdp/utils/wait_obj.h>
28
29 #include "dvcman.h"
30 #include "drdynvc_types.h"
31 #include "drdynvc_main.h"
32
33 #define CREATE_REQUEST_PDU     0x01
34 #define DATA_FIRST_PDU         0x02
35 #define DATA_PDU               0x03
36 #define CLOSE_REQUEST_PDU      0x04
37 #define CAPABILITY_REQUEST_PDU 0x05
38
39 struct drdynvc_plugin
40 {
41         rdpSvcPlugin plugin;
42
43         int version;
44         int PriorityCharge0;
45         int PriorityCharge1;
46         int PriorityCharge2;
47         int PriorityCharge3;
48
49         IWTSVirtualChannelManager* channel_mgr;
50 };
51
52 static int drdynvc_write_variable_uint(STREAM* stream, uint32 val)
53 {
54         int cb;
55
56         if (val <= 0xFF)
57         {
58                 cb = 0;
59                 stream_write_uint8(stream, val);
60         }
61         else if (val <= 0xFFFF)
62         {
63                 cb = 1;
64                 stream_write_uint16(stream, val);
65         }
66         else
67         {
68                 cb = 3;
69                 stream_write_uint32(stream, val);
70         }
71         return cb;
72 }
73
74 int drdynvc_write_data(drdynvcPlugin* drdynvc, uint32 ChannelId, uint8* data, uint32 data_size)
75 {
76         STREAM* data_out;
77         uint32 pos = 0;
78         uint32 cbChId;
79         uint32 cbLen;
80         uint32 chunk_len;
81         int error;
82
83         DEBUG_DVC("ChannelId=%d size=%d", ChannelId, data_size);
84
85         data_out = stream_new(CHANNEL_CHUNK_LENGTH);
86         stream_set_pos(data_out, 1);
87         cbChId = drdynvc_write_variable_uint(data_out, ChannelId);
88
89         if (data_size <= CHANNEL_CHUNK_LENGTH - pos)
90         {
91                 pos = stream_get_pos(data_out);
92                 stream_set_pos(data_out, 0);
93                 stream_write_uint8(data_out, 0x30 | cbChId);
94                 stream_set_pos(data_out, pos);
95                 stream_write(data_out, data, data_size);
96                 error = svc_plugin_send((rdpSvcPlugin*)drdynvc, data_out);
97         }
98         else
99         {
100                 /* Fragment the data */
101                 cbLen = drdynvc_write_variable_uint(data_out, data_size);
102                 pos = stream_get_pos(data_out);
103                 stream_set_pos(data_out, 0);
104                 stream_write_uint8(data_out, 0x20 | cbChId | (cbLen << 2));
105                 stream_set_pos(data_out, pos);
106                 chunk_len = CHANNEL_CHUNK_LENGTH - pos;
107                 stream_write(data_out, data, chunk_len);
108                 data += chunk_len;
109                 data_size -= chunk_len;
110                 error = svc_plugin_send((rdpSvcPlugin*)drdynvc, data_out);
111
112                 while (error == CHANNEL_RC_OK && data_size > 0)
113                 {
114                         data_out = stream_new(CHANNEL_CHUNK_LENGTH);
115                         stream_set_pos(data_out, 1);
116                         cbChId = drdynvc_write_variable_uint(data_out, ChannelId);
117
118                         pos = stream_get_pos(data_out);
119                         stream_set_pos(data_out, 0);
120                         stream_write_uint8(data_out, 0x30 | cbChId);
121                         stream_set_pos(data_out, pos);
122
123                         chunk_len = data_size;
124                         if (chunk_len > CHANNEL_CHUNK_LENGTH - pos)
125                                 chunk_len = CHANNEL_CHUNK_LENGTH - pos;
126                         stream_write(data_out, data, chunk_len);
127                         data += chunk_len;
128                         data_size -= chunk_len;
129                         error = svc_plugin_send((rdpSvcPlugin*)drdynvc, data_out);
130                 }
131         }
132         if (error != CHANNEL_RC_OK)
133         {
134                 DEBUG_WARN("VirtualChannelWrite failed %d", error);
135                 return 1;
136         }
137         return 0;
138 }
139
140 int drdynvc_push_event(drdynvcPlugin* drdynvc, RDP_EVENT* event)
141 {
142         int error;
143
144         error = svc_plugin_send_event((rdpSvcPlugin*)drdynvc, event);
145         if (error != CHANNEL_RC_OK)
146         {
147                 DEBUG_WARN("pVirtualChannelEventPush failed %d", error);
148                 return 1;
149         }
150         return 0;
151 }
152
153 static int drdynvc_process_capability_request(drdynvcPlugin* drdynvc, int Sp, int cbChId, STREAM* s)
154 {
155         STREAM* data_out;
156         int error;
157
158         DEBUG_DVC("Sp=%d cbChId=%d", Sp, cbChId);
159         stream_seek(s, 1); /* pad */
160         stream_read_uint16(s, drdynvc->version);
161         if (drdynvc->version == 2)
162         {
163                 stream_read_uint16(s, drdynvc->PriorityCharge0);
164                 stream_read_uint16(s, drdynvc->PriorityCharge1);
165                 stream_read_uint16(s, drdynvc->PriorityCharge2);
166                 stream_read_uint16(s, drdynvc->PriorityCharge3);
167         }
168         data_out = stream_new(4);
169         stream_write_uint16(data_out, 0x0050); /* Cmd+Sp+cbChId+Pad. Note: MSTSC sends 0x005c */
170         stream_write_uint16(data_out, drdynvc->version);
171         error = svc_plugin_send((rdpSvcPlugin*)drdynvc, data_out);
172         if (error != CHANNEL_RC_OK)
173         {
174                 DEBUG_WARN("VirtualChannelWrite failed %d", error);
175                 return 1;
176         }
177         return 0;
178 }
179
180 static uint32 drdynvc_read_variable_uint(STREAM* stream, int cbLen)
181 {
182         uint32 val;
183
184         switch (cbLen)
185         {
186                 case 0:
187                         stream_read_uint8(stream, val);
188                         break;
189                 case 1:
190                         stream_read_uint16(stream, val);
191                         break;
192                 default:
193                         stream_read_uint32(stream, val);
194                         break;
195         }
196         return val;
197 }
198
199 static int drdynvc_process_create_request(drdynvcPlugin* drdynvc, int Sp, int cbChId, STREAM* s)
200 {
201         STREAM* data_out;
202         int pos;
203         int error;
204         uint32 ChannelId;
205
206         ChannelId = drdynvc_read_variable_uint(s, cbChId);
207         pos = stream_get_pos(s);
208         DEBUG_DVC("ChannelId=%d ChannelName=%s", ChannelId, stream_get_tail(s));
209
210         error = dvcman_create_channel(drdynvc->channel_mgr, ChannelId, (char*)stream_get_tail(s));
211
212         data_out = stream_new(pos + 4);
213         stream_write_uint8(data_out, 0x10 | cbChId);
214         stream_set_pos(s, 1);
215         stream_copy(data_out, s, pos - 1);
216         
217         if (error == 0)
218         {
219                 DEBUG_DVC("channel created");
220                 stream_write_uint32(data_out, 0);
221         }
222         else
223         {
224                 DEBUG_DVC("no listener");
225                 stream_write_uint32(data_out, (uint32)(-1));
226         }
227
228         error = svc_plugin_send((rdpSvcPlugin*)drdynvc, data_out);
229         if (error != CHANNEL_RC_OK)
230         {
231                 DEBUG_WARN("VirtualChannelWrite failed %d", error);
232                 return 1;
233         }
234         return 0;
235 }
236
237 static int drdynvc_process_data_first(drdynvcPlugin* drdynvc, int Sp, int cbChId, STREAM* s)
238 {
239         uint32 ChannelId;
240         uint32 Length;
241         int error;
242
243         ChannelId = drdynvc_read_variable_uint(s, cbChId);
244         Length = drdynvc_read_variable_uint(s, Sp);
245         DEBUG_DVC("ChannelId=%d Length=%d", ChannelId, Length);
246
247         error = dvcman_receive_channel_data_first(drdynvc->channel_mgr, ChannelId, Length);
248         if (error)
249                 return error;
250
251         return dvcman_receive_channel_data(drdynvc->channel_mgr, ChannelId,
252                 stream_get_tail(s), stream_get_left(s));
253 }
254
255 static int drdynvc_process_data(drdynvcPlugin* drdynvc, int Sp, int cbChId, STREAM* s)
256 {
257         uint32 ChannelId;
258
259         ChannelId = drdynvc_read_variable_uint(s, cbChId);
260         DEBUG_DVC("ChannelId=%d", ChannelId);
261
262         return dvcman_receive_channel_data(drdynvc->channel_mgr, ChannelId,
263                 stream_get_tail(s), stream_get_left(s));
264 }
265
266 static int drdynvc_process_close_request(drdynvcPlugin* drdynvc, int Sp, int cbChId, STREAM* s)
267 {
268         uint32 ChannelId;
269
270         ChannelId = drdynvc_read_variable_uint(s, cbChId);
271         DEBUG_DVC("ChannelId=%d", ChannelId);
272         dvcman_close_channel(drdynvc->channel_mgr, ChannelId);
273
274         return 0;
275 }
276
277 static void drdynvc_process_receive(rdpSvcPlugin* plugin, STREAM* s)
278 {
279         drdynvcPlugin* drdynvc = (drdynvcPlugin*)plugin;
280         int value;
281         int Cmd;
282         int Sp;
283         int cbChId;
284
285         stream_read_uint8(s, value);
286         Cmd = (value & 0xf0) >> 4;
287         Sp = (value & 0x0c) >> 2;
288         cbChId = (value & 0x03) >> 0;
289
290         DEBUG_DVC("Cmd=0x%x", Cmd);
291
292         switch (Cmd)
293         {
294                 case CAPABILITY_REQUEST_PDU:
295                         drdynvc_process_capability_request(drdynvc, Sp, cbChId, s);
296                         break;
297                 case CREATE_REQUEST_PDU:
298                         drdynvc_process_create_request(drdynvc, Sp, cbChId, s);
299                         break;
300                 case DATA_FIRST_PDU:
301                         drdynvc_process_data_first(drdynvc, Sp, cbChId, s);
302                         break;
303                 case DATA_PDU:
304                         drdynvc_process_data(drdynvc, Sp, cbChId, s);
305                         break;
306                 case CLOSE_REQUEST_PDU:
307                         drdynvc_process_close_request(drdynvc, Sp, cbChId, s);
308                         break;
309                 default:
310                         DEBUG_WARN("unknown drdynvc cmd 0x%x", Cmd);
311                         break;
312         }
313
314         stream_free(s);
315 }
316
317 static void drdynvc_process_connect(rdpSvcPlugin* plugin)
318 {
319         drdynvcPlugin* drdynvc = (drdynvcPlugin*)plugin;
320
321         DEBUG_DVC("connecting");
322
323         drdynvc->channel_mgr = dvcman_new(drdynvc);
324         dvcman_load_plugin(drdynvc->channel_mgr, svc_plugin_get_data(plugin));
325         dvcman_init(drdynvc->channel_mgr);
326 }
327
328 static void drdynvc_process_event(rdpSvcPlugin* plugin, RDP_EVENT* event)
329 {
330         freerdp_event_free(event);
331 }
332
333 static void drdynvc_process_terminate(rdpSvcPlugin* plugin)
334 {
335         drdynvcPlugin* drdynvc = (drdynvcPlugin*)plugin;
336
337         DEBUG_DVC("terminating");
338
339         if (drdynvc->channel_mgr != NULL)
340                 dvcman_free(drdynvc->channel_mgr);
341         xfree(drdynvc);
342 }
343
344 DEFINE_SVC_PLUGIN(drdynvc, "drdynvc",
345         CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP |
346         CHANNEL_OPTION_COMPRESS_RDP)