Initial commit - from Precise source
[freerdp-ubuntu-pcb-backport.git] / server / test / tfreerdp.c
1 /**
2  * FreeRDP: A Remote Desktop Protocol Client
3  * FreeRDP Test Server
4  *
5  * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6  * Copyright 2011 Vic Lee
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <pthread.h>
26 #include <signal.h>
27 #include <sys/time.h>
28 #include <freerdp/constants.h>
29 #include <freerdp/utils/sleep.h>
30 #include <freerdp/utils/memory.h>
31 #include <freerdp/utils/thread.h>
32 #include <freerdp/codec/rfx.h>
33 #include <freerdp/listener.h>
34 #include <freerdp/channels/wtsvc.h>
35
36 static char* test_pcap_file = NULL;
37 static boolean test_dump_rfx_realtime = true;
38
39 /* HL1, LH1, HH1, HL2, LH2, HH2, HL3, LH3, HH3, LL3 */
40 static const unsigned int test_quantization_values[] =
41 {
42         6, 6, 6, 6, 7, 7, 8, 8, 8, 9
43 };
44
45 struct test_peer_context
46 {
47         rdpContext _p;
48
49         RFX_CONTEXT* rfx_context;
50         STREAM* s;
51         uint8* icon_data;
52         uint8* bg_data;
53         int icon_width;
54         int icon_height;
55         int icon_x;
56         int icon_y;
57         boolean activated;
58         WTSVirtualChannelManager* vcm;
59         void* debug_channel;
60         freerdp_thread* debug_channel_thread;
61 };
62 typedef struct test_peer_context testPeerContext;
63
64 void test_peer_context_new(freerdp_peer* client, testPeerContext* context)
65 {
66         context->rfx_context = rfx_context_new();
67         context->rfx_context->mode = RLGR3;
68         context->rfx_context->width = client->settings->width;
69         context->rfx_context->height = client->settings->height;
70         rfx_context_set_pixel_format(context->rfx_context, RFX_PIXEL_FORMAT_RGB);
71
72         context->s = stream_new(65536);
73
74         context->icon_x = -1;
75         context->icon_y = -1;
76
77         context->vcm = WTSCreateVirtualChannelManager(client);
78 }
79
80 void test_peer_context_free(freerdp_peer* client, testPeerContext* context)
81 {
82         if (context)
83         {
84                 if (context->debug_channel_thread)
85                 {
86                         freerdp_thread_stop(context->debug_channel_thread);
87                         freerdp_thread_free(context->debug_channel_thread);
88                 }
89                 stream_free(context->s);
90                 xfree(context->icon_data);
91                 xfree(context->bg_data);
92                 rfx_context_free(context->rfx_context);
93                 if (context->debug_channel)
94                 {
95                         WTSVirtualChannelClose(context->debug_channel);
96                 }
97                 WTSDestroyVirtualChannelManager(context->vcm);
98                 xfree(context);
99         }
100 }
101
102 static void test_peer_init(freerdp_peer* client)
103 {
104         client->context_size = sizeof(testPeerContext);
105         client->ContextNew = (psPeerContextNew) test_peer_context_new;
106         client->ContextFree = (psPeerContextFree) test_peer_context_free;
107         freerdp_peer_context_new(client);
108 }
109
110 static STREAM* test_peer_stream_init(testPeerContext* context)
111 {
112         stream_clear(context->s);
113         stream_set_pos(context->s, 0);
114         return context->s;
115 }
116
117 static void test_peer_draw_background(freerdp_peer* client)
118 {
119         testPeerContext* context = (testPeerContext*) client->context;
120         rdpUpdate* update = client->update;
121         SURFACE_BITS_COMMAND* cmd = &update->surface_bits_command;
122         STREAM* s;
123         RFX_RECT rect;
124         uint8* rgb_data;
125         int size;
126
127         if (!client->settings->rfx_codec)
128                 return;
129
130         s = test_peer_stream_init(context);
131
132         rect.x = 0;
133         rect.y = 0;
134         rect.width = client->settings->width;
135         rect.height = client->settings->height;
136
137         size = rect.width * rect.height * 3;
138         rgb_data = xmalloc(size);
139         memset(rgb_data, 0xA0, size);
140
141         rfx_compose_message(context->rfx_context, s,
142                 &rect, 1, rgb_data, rect.width, rect.height, rect.width * 3);
143
144         cmd->destLeft = 0;
145         cmd->destTop = 0;
146         cmd->destRight = rect.width;
147         cmd->destBottom = rect.height;
148         cmd->bpp = 32;
149         cmd->codecID = client->settings->rfx_codec_id;
150         cmd->width = rect.width;
151         cmd->height = rect.height;
152         cmd->bitmapDataLength = stream_get_length(s);
153         cmd->bitmapData = stream_get_head(s);
154         update->SurfaceBits(update->context, cmd);
155
156         xfree(rgb_data);
157 }
158
159 static void test_peer_load_icon(freerdp_peer* client)
160 {
161         testPeerContext* context = (testPeerContext*) client->context;
162         FILE* fp;
163         int i;
164         char line[50];
165         uint8* rgb_data;
166         int c;
167
168         if (!client->settings->rfx_codec)
169                 return;
170
171         if ((fp = fopen("test_icon.ppm", "r")) == NULL)
172                 return;
173
174         /* P3 */
175         fgets(line, sizeof(line), fp);
176         /* Creater comment */
177         fgets(line, sizeof(line), fp);
178         /* width height */
179         fgets(line, sizeof(line), fp);
180         sscanf(line, "%d %d", &context->icon_width, &context->icon_height);
181         /* Max */
182         fgets(line, sizeof(line), fp);
183
184         rgb_data = xmalloc(context->icon_width * context->icon_height * 3);
185
186         for (i = 0; i < context->icon_width * context->icon_height * 3; i++)
187         {
188                 if (fgets(line, sizeof(line), fp))
189                 {
190                         sscanf(line, "%d", &c);
191                         rgb_data[i] = (uint8)c;
192                 }
193         }
194
195         context->icon_data = rgb_data;
196
197         /* background with same size, which will be used to erase the icon from old position */
198         context->bg_data = xmalloc(context->icon_width * context->icon_height * 3);
199         memset(context->bg_data, 0xA0, context->icon_width * context->icon_height * 3);
200 }
201
202 static void test_peer_draw_icon(freerdp_peer* client, int x, int y)
203 {
204         testPeerContext* context = (testPeerContext*) client->context;
205         rdpUpdate* update = client->update;
206         SURFACE_BITS_COMMAND* cmd = &update->surface_bits_command;
207         RFX_RECT rect;
208         STREAM* s;
209
210         if (client->update->dump_rfx)
211                 return;
212         if (!client->settings->rfx_codec || !context)
213                 return;
214         if (context->icon_width < 1 || !context->activated)
215                 return;
216
217         rect.x = 0;
218         rect.y = 0;
219         rect.width = context->icon_width;
220         rect.height = context->icon_height;
221
222         if (context->icon_x >= 0)
223         {
224                 s = test_peer_stream_init(context);
225                 rfx_compose_message(context->rfx_context, s,
226                         &rect, 1, context->bg_data, rect.width, rect.height, rect.width * 3);
227
228                 cmd->destLeft = context->icon_x;
229                 cmd->destTop = context->icon_y;
230                 cmd->destRight = context->icon_x + context->icon_width;
231                 cmd->destBottom = context->icon_y + context->icon_height;
232                 cmd->bpp = 32;
233                 cmd->codecID = client->settings->rfx_codec_id;
234                 cmd->width = context->icon_width;
235                 cmd->height = context->icon_height;
236                 cmd->bitmapDataLength = stream_get_length(s);
237                 cmd->bitmapData = stream_get_head(s);
238                 update->SurfaceBits(update->context, cmd);
239         }
240
241         s = test_peer_stream_init(context);
242         rfx_compose_message(context->rfx_context, s,
243                 &rect, 1, context->icon_data, rect.width, rect.height, rect.width * 3);
244
245         cmd->destLeft = x;
246         cmd->destTop = y;
247         cmd->destRight = x + context->icon_width;
248         cmd->destBottom = y + context->icon_height;
249         cmd->bpp = 32;
250         cmd->codecID = client->settings->rfx_codec_id;
251         cmd->width = context->icon_width;
252         cmd->height = context->icon_height;
253         cmd->bitmapDataLength = stream_get_length(s);
254         cmd->bitmapData = stream_get_head(s);
255         update->SurfaceBits(update->context, cmd);
256
257         context->icon_x = x;
258         context->icon_y = y;
259 }
260
261 static boolean test_sleep_tsdiff(uint32 *old_sec, uint32 *old_usec, uint32 new_sec, uint32 new_usec)
262 {
263         sint32 sec, usec;
264
265         if (*old_sec==0 && *old_usec==0)
266         {
267                 *old_sec = new_sec;
268                 *old_usec = new_usec;
269                 return true;
270         }
271
272         sec = new_sec - *old_sec;
273         usec = new_usec - *old_usec;
274
275         if (sec<0 || (sec==0 && usec<0))
276         {
277                 printf("Invalid time stamp detected.\n");
278                 return false;
279         }
280
281         *old_sec = new_sec;
282         *old_usec = new_usec;
283         
284         while (usec < 0) 
285         {
286                 usec += 1000000;
287                 sec--;
288         }
289         
290         if (sec > 0)
291                 freerdp_sleep(sec);
292         
293         if (usec > 0)
294                 freerdp_usleep(usec);
295         
296         return true;
297 }
298
299 void tf_peer_dump_rfx(freerdp_peer* client)
300 {
301         STREAM* s;
302         uint32 prev_seconds;
303         uint32 prev_useconds;
304         rdpUpdate* update;
305         rdpPcap* pcap_rfx;
306         pcap_record record;
307
308         s = stream_new(512);
309         update = client->update;
310         client->update->pcap_rfx = pcap_open(test_pcap_file, false);
311         pcap_rfx = client->update->pcap_rfx;
312
313         if (pcap_rfx == NULL)
314                 return;
315
316         prev_seconds = prev_useconds = 0;
317
318         while (pcap_has_next_record(pcap_rfx))
319         {
320                 pcap_get_next_record_header(pcap_rfx, &record);
321
322                 s->data = xrealloc(s->data, record.length);
323                 record.data = s->data;
324                 s->size = record.length;
325
326                 pcap_get_next_record_content(pcap_rfx, &record);
327                 s->p = s->data + s->size;
328
329                 if (test_dump_rfx_realtime && test_sleep_tsdiff(&prev_seconds, &prev_useconds, record.header.ts_sec, record.header.ts_usec) == false)
330                         break;
331
332                 update->SurfaceCommand(update->context, s);
333         }
334 }
335
336 static void* tf_debug_channel_thread_func(void* arg)
337 {
338         void* fd;
339         STREAM* s;
340         void* buffer;
341         uint32 bytes_returned = 0;
342         testPeerContext* context = (testPeerContext*) arg;
343         freerdp_thread* thread = context->debug_channel_thread;
344
345         if (WTSVirtualChannelQuery(context->debug_channel, WTSVirtualFileHandle, &buffer, &bytes_returned) == true)
346         {
347                 fd = *((void**)buffer);
348                 WTSFreeMemory(buffer);
349                 thread->signals[thread->num_signals++] = wait_obj_new_with_fd(fd);
350         }
351
352         s = stream_new(4096);
353
354         WTSVirtualChannelWrite(context->debug_channel, (uint8*) "test1", 5, NULL);
355
356         while (1)
357         {
358                 freerdp_thread_wait(thread);
359                 if (freerdp_thread_is_stopped(thread))
360                         break;
361
362                 stream_set_pos(s, 0);
363                 if (WTSVirtualChannelRead(context->debug_channel, 0, stream_get_head(s),
364                         stream_get_size(s), &bytes_returned) == false)
365                 {
366                         if (bytes_returned == 0)
367                                 break;
368                         stream_check_size(s, bytes_returned);
369                         if (WTSVirtualChannelRead(context->debug_channel, 0, stream_get_head(s),
370                                 stream_get_size(s), &bytes_returned) == false)
371                         {
372                                 /* should not happen */
373                                 break;
374                         }
375                 }
376                 stream_set_pos(s, bytes_returned);
377
378                 printf("got %d bytes\n", bytes_returned);
379         }
380
381         stream_free(s);
382         freerdp_thread_quit(thread);
383
384         return 0;
385 }
386
387 boolean tf_peer_post_connect(freerdp_peer* client)
388 {
389         int i;
390         testPeerContext* context = (testPeerContext*) client->context;
391
392         /**
393          * This callback is called when the entire connection sequence is done, i.e. we've received the
394          * Font List PDU from the client and sent out the Font Map PDU.
395          * The server may start sending graphics output and receiving keyboard/mouse input after this
396          * callback returns.
397          */
398         printf("Client %s is activated (osMajorType %d osMinorType %d)", client->hostname,
399                 client->settings->os_major_type, client->settings->os_minor_type);
400         if (client->settings->autologon)
401         {
402                 printf(" and wants to login automatically as %s\\%s",
403                         client->settings->domain ? client->settings->domain : "",
404                         client->settings->username);
405
406                 /* A real server may perform OS login here if NLA is not executed previously. */
407         }
408         printf("\n");
409
410         printf("Client requested desktop: %dx%dx%d\n",
411                 client->settings->width, client->settings->height, client->settings->color_depth);
412
413         /* A real server should tag the peer as activated here and start sending updates in mainloop. */
414         test_peer_load_icon(client);
415
416         /* Iterate all channel names requested by the client and activate those supported by the server */
417         for (i = 0; i < client->settings->num_channels; i++)
418         {
419                 if (client->settings->channels[i].joined)
420                 {
421                         if (strncmp(client->settings->channels[i].name, "rdpdbg", 6) == 0)
422                         {
423                                 context->debug_channel = WTSVirtualChannelOpenEx(context->vcm, "rdpdbg", 0);
424                                 if (context->debug_channel != NULL)
425                                 {
426                                         printf("Open channel rdpdbg.\n");
427                                         context->debug_channel_thread = freerdp_thread_new();
428                                         freerdp_thread_start(context->debug_channel_thread,
429                                                 tf_debug_channel_thread_func, context);
430                                 }
431                         }
432                 }
433         }
434
435         /* Return false here would stop the execution of the peer mainloop. */
436         return true;
437 }
438
439 boolean tf_peer_activate(freerdp_peer* client)
440 {
441         testPeerContext* context = (testPeerContext*) client->context;
442
443         rfx_context_reset(context->rfx_context);
444         context->activated = true;
445
446         if (test_pcap_file != NULL)
447         {
448                 client->update->dump_rfx = true;
449                 tf_peer_dump_rfx(client);
450         }
451         else
452         {
453                 test_peer_draw_background(client);
454         }
455
456         return true;
457 }
458
459 void tf_peer_synchronize_event(rdpInput* input, uint32 flags)
460 {
461         printf("Client sent a synchronize event (flags:0x%X)\n", flags);
462 }
463
464 void tf_peer_keyboard_event(rdpInput* input, uint16 flags, uint16 code)
465 {
466         freerdp_peer* client = input->context->peer;
467         rdpUpdate* update = client->update;
468         testPeerContext* context = (testPeerContext*) input->context;
469
470         printf("Client sent a keyboard event (flags:0x%X code:0x%X)\n", flags, code);
471
472         if ((flags & 0x4000) && code == 0x1F) /* 's' key */
473         {
474                 if (client->settings->width != 800)
475                 {
476                         client->settings->width = 800;
477                         client->settings->height = 600;
478                 }
479                 else
480                 {
481                         client->settings->width = 640;
482                         client->settings->height = 480;
483                 }
484                 update->DesktopResize(update->context);
485                 context->activated = false;
486         }
487         else if ((flags & 0x4000) && code == 0x2E) /* 'c' key */
488         {
489                 if (context->debug_channel)
490                 {
491                         WTSVirtualChannelWrite(context->debug_channel, (uint8*) "test2", 5, NULL);
492                 }
493         }
494 }
495
496 void tf_peer_unicode_keyboard_event(rdpInput* input, uint16 flags, uint16 code)
497 {
498         printf("Client sent a unicode keyboard event (flags:0x%X code:0x%X)\n", flags, code);
499 }
500
501 void tf_peer_mouse_event(rdpInput* input, uint16 flags, uint16 x, uint16 y)
502 {
503         printf("Client sent a mouse event (flags:0x%X pos:%d,%d)\n", flags, x, y);
504
505         test_peer_draw_icon(input->context->peer, x + 10, y);
506 }
507
508 void tf_peer_extended_mouse_event(rdpInput* input, uint16 flags, uint16 x, uint16 y)
509 {
510         printf("Client sent an extended mouse event (flags:0x%X pos:%d,%d)\n", flags, x, y);
511 }
512
513 static void* test_peer_mainloop(void* arg)
514 {
515         int i;
516         int fds;
517         int max_fds;
518         int rcount;
519         void* rfds[32];
520         fd_set rfds_set;
521         testPeerContext* context;
522         freerdp_peer* client = (freerdp_peer*) arg;
523
524         memset(rfds, 0, sizeof(rfds));
525
526         test_peer_init(client);
527
528         /* Initialize the real server settings here */
529         client->settings->cert_file = xstrdup("server.crt");
530         client->settings->privatekey_file = xstrdup("server.key");
531         client->settings->nla_security = false;
532         client->settings->rfx_codec = true;
533
534         client->PostConnect = tf_peer_post_connect;
535         client->Activate = tf_peer_activate;
536
537         client->input->SynchronizeEvent = tf_peer_synchronize_event;
538         client->input->KeyboardEvent = tf_peer_keyboard_event;
539         client->input->UnicodeKeyboardEvent = tf_peer_unicode_keyboard_event;
540         client->input->MouseEvent = tf_peer_mouse_event;
541         client->input->ExtendedMouseEvent = tf_peer_extended_mouse_event;
542
543         client->Initialize(client);
544         context = (testPeerContext*) client->context;
545
546         printf("We've got a client %s\n", client->hostname);
547
548         while (1)
549         {
550                 rcount = 0;
551
552                 if (client->GetFileDescriptor(client, rfds, &rcount) != true)
553                 {
554                         printf("Failed to get FreeRDP file descriptor\n");
555                         break;
556                 }
557                 WTSVirtualChannelManagerGetFileDescriptor(context->vcm, rfds, &rcount);
558
559                 max_fds = 0;
560                 FD_ZERO(&rfds_set);
561
562                 for (i = 0; i < rcount; i++)
563                 {
564                         fds = (int)(long)(rfds[i]);
565
566                         if (fds > max_fds)
567                                 max_fds = fds;
568
569                         FD_SET(fds, &rfds_set);
570                 }
571
572                 if (max_fds == 0)
573                         break;
574
575                 if (select(max_fds + 1, &rfds_set, NULL, NULL, NULL) == -1)
576                 {
577                         /* these are not really errors */
578                         if (!((errno == EAGAIN) ||
579                                 (errno == EWOULDBLOCK) ||
580                                 (errno == EINPROGRESS) ||
581                                 (errno == EINTR))) /* signal occurred */
582                         {
583                                 printf("select failed\n");
584                                 break;
585                         }
586                 }
587
588                 if (client->CheckFileDescriptor(client) != true)
589                         break;
590                 if (WTSVirtualChannelManagerCheckFileDescriptor(context->vcm) != true)
591                         break;
592         }
593
594         printf("Client %s disconnected.\n", client->hostname);
595
596         client->Disconnect(client);
597         freerdp_peer_context_free(client);
598         freerdp_peer_free(client);
599
600         return NULL;
601 }
602
603 static void test_peer_accepted(freerdp_listener* instance, freerdp_peer* client)
604 {
605         pthread_t th;
606
607         pthread_create(&th, 0, test_peer_mainloop, client);
608         pthread_detach(th);
609 }
610
611 static void test_server_mainloop(freerdp_listener* instance)
612 {
613         int i;
614         int fds;
615         int max_fds;
616         int rcount;
617         void* rfds[32];
618         fd_set rfds_set;
619
620         memset(rfds, 0, sizeof(rfds));
621
622         while (1)
623         {
624                 rcount = 0;
625
626                 if (instance->GetFileDescriptor(instance, rfds, &rcount) != true)
627                 {
628                         printf("Failed to get FreeRDP file descriptor\n");
629                         break;
630                 }
631
632                 max_fds = 0;
633                 FD_ZERO(&rfds_set);
634
635                 for (i = 0; i < rcount; i++)
636                 {
637                         fds = (int)(long)(rfds[i]);
638
639                         if (fds > max_fds)
640                                 max_fds = fds;
641
642                         FD_SET(fds, &rfds_set);
643                 }
644
645                 if (max_fds == 0)
646                         break;
647
648                 if (select(max_fds + 1, &rfds_set, NULL, NULL, NULL) == -1)
649                 {
650                         /* these are not really errors */
651                         if (!((errno == EAGAIN) ||
652                                 (errno == EWOULDBLOCK) ||
653                                 (errno == EINPROGRESS) ||
654                                 (errno == EINTR))) /* signal occurred */
655                         {
656                                 printf("select failed\n");
657                                 break;
658                         }
659                 }
660
661                 if (instance->CheckFileDescriptor(instance) != true)
662                 {
663                         printf("Failed to check FreeRDP file descriptor\n");
664                         break;
665                 }
666         }
667
668         instance->Close(instance);
669 }
670
671 int main(int argc, char* argv[])
672 {
673         freerdp_listener* instance;
674
675         /* Ignore SIGPIPE, otherwise an SSL_write failure could crash your server */
676         signal(SIGPIPE, SIG_IGN);
677
678         instance = freerdp_listener_new();
679
680         instance->PeerAccepted = test_peer_accepted;
681
682         if (argc > 1)
683                 test_pcap_file = argv[1];
684         
685         if (argc > 2 && !strcmp(argv[2], "--fast"))
686                 test_dump_rfx_realtime = false;
687
688         /* Open the server socket and start listening. */
689         if (instance->Open(instance, NULL, 3389))
690         {
691                 /* Entering the server main loop. In a real server the listener can be run in its own thread. */
692                 test_server_mainloop(instance);
693         }
694
695         freerdp_listener_free(instance);
696
697         return 0;
698 }
699