Merging r37048, r37051, r37052, r37055 from the-real-msvc branch
[reactos.git] / reactos / base / applications / tsclient / rdesktop / rdesktop.c
1 /* -*- c-basic-offset: 8 -*-
2 rdesktop: A Remote Desktop Protocol client.
3 Entrypoint and utility functions
4 Copyright (C) Matthew Chapman 1999-2005
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include <stdarg.h> /* va_list va_start va_end */
22 #include <unistd.h> /* read close getuid getgid getpid getppid gethostname */
23 #include <fcntl.h> /* open */
24 #include <pwd.h> /* getpwuid */
25 #include <termios.h> /* tcgetattr tcsetattr */
26 #include <sys/stat.h> /* stat */
27 #include <sys/time.h> /* gettimeofday */
28 #include <sys/times.h> /* times */
29 #include <ctype.h> /* toupper */
30 #include <errno.h>
31 #include "rdesktop.h"
32
33 #ifdef HAVE_LOCALE_H
34 #include <locale.h>
35 #endif
36 #ifdef HAVE_ICONV
37 #ifdef HAVE_LANGINFO_H
38 #include <langinfo.h>
39 #endif
40 #endif
41
42 #ifdef EGD_SOCKET
43 #include <sys/types.h>
44 #include <sys/socket.h> /* socket connect */
45 #include <sys/un.h> /* sockaddr_un */
46 #endif
47
48 #include <openssl/md5.h>
49
50 #ifdef RDP2VNC
51 void
52 rdp2vnc_connect(char *server, uint32 flags, char *domain, char *password,
53 char *shell, char *directory);
54 #endif
55 /* Display usage information */
56 static void
57 usage(char *program)
58 {
59 fprintf(stderr, "rdesktop: A Remote Desktop Protocol client.\n");
60 fprintf(stderr, "Version " VERSION ". Copyright (C) 1999-2005 Matt Chapman.\n");
61 fprintf(stderr, "See http://www.rdesktop.org/ for more information.\n\n");
62
63 fprintf(stderr, "Usage: %s [options] server[:port]\n", program);
64 #ifdef RDP2VNC
65 fprintf(stderr, " -V: vnc port\n");
66 fprintf(stderr, " -Q: defer time (ms)\n");
67 #endif
68 fprintf(stderr, " -u: user name\n");
69 fprintf(stderr, " -d: domain\n");
70 fprintf(stderr, " -s: shell\n");
71 fprintf(stderr, " -c: working directory\n");
72 fprintf(stderr, " -p: password (- to prompt)\n");
73 fprintf(stderr, " -n: client hostname\n");
74 fprintf(stderr, " -k: keyboard layout on server (en-us, de, sv, etc.)\n");
75 fprintf(stderr, " -g: desktop geometry (WxH)\n");
76 fprintf(stderr, " -f: full-screen mode\n");
77 fprintf(stderr, " -b: force bitmap updates\n");
78 #ifdef HAVE_ICONV
79 fprintf(stderr, " -L: local codepage\n");
80 #endif
81 fprintf(stderr, " -A: enable SeamlessRDP mode\n");
82 fprintf(stderr, " -B: use BackingStore of X-server (if available)\n");
83 fprintf(stderr, " -e: disable encryption (French TS)\n");
84 fprintf(stderr, " -E: disable encryption from client to server\n");
85 fprintf(stderr, " -m: do not send motion events\n");
86 fprintf(stderr, " -C: use private colour map\n");
87 fprintf(stderr, " -D: hide window manager decorations\n");
88 fprintf(stderr, " -K: keep window manager key bindings\n");
89 fprintf(stderr, " -S: caption button size (single application mode)\n");
90 fprintf(stderr, " -T: window title\n");
91 fprintf(stderr, " -N: enable numlock syncronization\n");
92 fprintf(stderr, " -X: embed into another window with a given id.\n");
93 fprintf(stderr, " -a: connection colour depth\n");
94 fprintf(stderr, " -z: enable rdp compression\n");
95 fprintf(stderr, " -x: RDP5 experience (m[odem 28.8], b[roadband], l[an] or hex nr.)\n");
96 fprintf(stderr, " -P: use persistent bitmap caching\n");
97 fprintf(stderr, " -r: enable specified device redirection (this flag can be repeated)\n");
98 fprintf(stderr,
99 " '-r comport:COM1=/dev/ttyS0': enable serial redirection of /dev/ttyS0 to COM1\n");
100 fprintf(stderr, " or COM1=/dev/ttyS0,COM2=/dev/ttyS1\n");
101 fprintf(stderr,
102 " '-r disk:floppy=/mnt/floppy': enable redirection of /mnt/floppy to 'floppy' share\n");
103 fprintf(stderr, " or 'floppy=/mnt/floppy,cdrom=/mnt/cdrom'\n");
104 fprintf(stderr, " '-r clientname=<client name>': Set the client name displayed\n");
105 fprintf(stderr, " for redirected disks\n");
106 fprintf(stderr,
107 " '-r lptport:LPT1=/dev/lp0': enable parallel redirection of /dev/lp0 to LPT1\n");
108 fprintf(stderr, " or LPT1=/dev/lp0,LPT2=/dev/lp1\n");
109 fprintf(stderr, " '-r printer:mydeskjet': enable printer redirection\n");
110 fprintf(stderr,
111 " or mydeskjet=\"HP LaserJet IIIP\" to enter server driver as well\n");
112 fprintf(stderr, " '-r sound:[local|off|remote]': enable sound redirection\n");
113 fprintf(stderr, " remote would leave sound on server\n");
114 fprintf(stderr,
115 " '-r clipboard:[off|PRIMARYCLIPBOARD|CLIPBOARD]': enable clipboard\n");
116 fprintf(stderr, " redirection.\n");
117 fprintf(stderr,
118 " 'PRIMARYCLIPBOARD' looks at both PRIMARY and CLIPBOARD\n");
119 fprintf(stderr, " when sending data to server.\n");
120 fprintf(stderr, " 'CLIPBOARD' looks at only CLIPBOARD.\n");
121 fprintf(stderr, " -0: attach to console\n");
122 fprintf(stderr, " -4: use RDP version 4\n");
123 fprintf(stderr, " -5: use RDP version 5 (default)\n");
124 }
125
126 static void
127 print_disconnect_reason(uint16 reason)
128 {
129 char *text;
130
131 switch (reason)
132 {
133 case exDiscReasonNoInfo:
134 text = "No information available";
135 break;
136
137 case exDiscReasonAPIInitiatedDisconnect:
138 text = "Server initiated disconnect";
139 break;
140
141 case exDiscReasonAPIInitiatedLogoff:
142 text = "Server initiated logoff";
143 break;
144
145 case exDiscReasonServerIdleTimeout:
146 text = "Server idle timeout reached";
147 break;
148
149 case exDiscReasonServerLogonTimeout:
150 text = "Server logon timeout reached";
151 break;
152
153 case exDiscReasonReplacedByOtherConnection:
154 text = "The session was replaced";
155 break;
156
157 case exDiscReasonOutOfMemory:
158 text = "The server is out of memory";
159 break;
160
161 case exDiscReasonServerDeniedConnection:
162 text = "The server denied the connection";
163 break;
164
165 case exDiscReasonServerDeniedConnectionFips:
166 text = "The server denied the connection for security reason";
167 break;
168
169 case exDiscReasonLicenseInternal:
170 text = "Internal licensing error";
171 break;
172
173 case exDiscReasonLicenseNoLicenseServer:
174 text = "No license server available";
175 break;
176
177 case exDiscReasonLicenseNoLicense:
178 text = "No valid license available";
179 break;
180
181 case exDiscReasonLicenseErrClientMsg:
182 text = "Invalid licensing message";
183 break;
184
185 case exDiscReasonLicenseHwidDoesntMatchLicense:
186 text = "Hardware id doesn't match software license";
187 break;
188
189 case exDiscReasonLicenseErrClientLicense:
190 text = "Client license error";
191 break;
192
193 case exDiscReasonLicenseCantFinishProtocol:
194 text = "Network error during licensing protocol";
195 break;
196
197 case exDiscReasonLicenseClientEndedProtocol:
198 text = "Licensing protocol was not completed";
199 break;
200
201 case exDiscReasonLicenseErrClientEncryption:
202 text = "Incorrect client license enryption";
203 break;
204
205 case exDiscReasonLicenseCantUpgradeLicense:
206 text = "Can't upgrade license";
207 break;
208
209 case exDiscReasonLicenseNoRemoteConnections:
210 text = "The server is not licensed to accept remote connections";
211 break;
212
213 default:
214 if (reason > 0x1000 && reason < 0x7fff)
215 {
216 text = "Internal protocol error";
217 }
218 else
219 {
220 text = "Unknown reason";
221 }
222 }
223 fprintf(stderr, "disconnect: %s.\n", text);
224 }
225
226 static void
227 rdesktop_reset_state(RDPCLIENT * This)
228 {
229 rdp_reset_state(This);
230 }
231
232 static BOOL
233 read_password(char *password, int size)
234 {
235 struct termios tios;
236 BOOL ret = False;
237 int istty = 0;
238 char *p;
239
240 if (tcgetattr(STDIN_FILENO, &tios) == 0)
241 {
242 fprintf(stderr, "Password: ");
243 tios.c_lflag &= ~ECHO;
244 tcsetattr(STDIN_FILENO, TCSANOW, &tios);
245 istty = 1;
246 }
247
248 if (fgets(password, size, stdin) != NULL)
249 {
250 ret = True;
251
252 /* strip final newline */
253 p = strchr(password, '\n');
254 if (p != NULL)
255 *p = 0;
256 }
257
258 if (istty)
259 {
260 tios.c_lflag |= ECHO;
261 tcsetattr(STDIN_FILENO, TCSANOW, &tios);
262 fprintf(stderr, "\n");
263 }
264
265 return ret;
266 }
267
268 static void
269 parse_server_and_port(RDPCLIENT * This, char *server)
270 {
271 char *p;
272 #ifdef IPv6
273 int addr_colons;
274 #endif
275
276 #ifdef IPv6
277 p = server;
278 addr_colons = 0;
279 while (*p)
280 if (*p++ == ':')
281 addr_colons++;
282 if (addr_colons >= 2)
283 {
284 /* numeric IPv6 style address format - [1:2:3::4]:port */
285 p = strchr(server, ']');
286 if (*server == '[' && p != NULL)
287 {
288 if (*(p + 1) == ':' && *(p + 2) != '\0')
289 This->tcp_port_rdp = strtol(p + 2, NULL, 10);
290 /* remove the port number and brackets from the address */
291 *p = '\0';
292 strncpy(server, server + 1, strlen(server));
293 }
294 }
295 else
296 {
297 /* dns name or IPv4 style address format - server.example.com:port or 1.2.3.4:port */
298 p = strchr(server, ':');
299 if (p != NULL)
300 {
301 This->tcp_port_rdp = strtol(p + 1, NULL, 10);
302 *p = 0;
303 }
304 }
305 #else /* no IPv6 support */
306 p = strchr(server, ':');
307 if (p != NULL)
308 {
309 This->tcp_port_rdp = strtol(p + 1, NULL, 10);
310 *p = 0;
311 }
312 #endif /* IPv6 */
313
314 }
315
316 /* Client program */
317 int
318 main(int argc, char *argv[])
319 {
320 char server[64];
321 char fullhostname[64];
322 char domain[16];
323 char password[64];
324 char shell[256];
325 char directory[256];
326 BOOL prompt_password, deactivated;
327 struct passwd *pw;
328 uint32 flags, ext_disc_reason = 0;
329 char *p;
330 int c;
331 char *locale = NULL;
332 int username_option = 0;
333 BOOL geometry_option = False;
334 int run_count = 0; /* Session Directory support */
335 BOOL continue_connect = True; /* Session Directory support */
336 RDPCLIENT * This;
337
338 This = xmalloc(sizeof(RDPCLIENT));
339 memset(This, 0, sizeof(RDPCLIENT));
340
341 This->keylayout = 0x409; /* Defaults to US keyboard layout */
342 This->keyboard_type = 0x4; /* Defaults to US keyboard layout */
343 This->keyboard_subtype = 0x0; /* Defaults to US keyboard layout */
344 This->keyboard_functionkeys = 0xc; /* Defaults to US keyboard layout */
345 This->width = 800; /* width is special: If 0, the
346 geometry will be fetched from
347 _NET_WORKAREA. If negative,
348 absolute value specifies the
349 percent of the whole screen. */
350 This->height = 600;
351 This->server_depth = -1;
352 This->bitmap_compression = True;
353 This->sendmotion = True;
354 This->bitmap_cache = True;
355 This->bitmap_cache_persist_enable = False;
356 This->bitmap_cache_precache = True;
357 This->encryption = True;
358 This->packet_encryption = True;
359 This->desktop_save = True; /* desktop save order */
360 This->polygon_ellipse_orders = True; /* polygon / ellipse orders */
361 This->fullscreen = False;
362 This->grab_keyboard = True;
363 This->hide_decorations = False;
364 This->use_rdp5 = True;
365 This->rdpclip = True;
366 This->console_session = False;
367 This->numlock_sync = False;
368 This->lspci_enabled = False;
369 This->owncolmap = False;
370 This->ownbackstore = True; /* We can't rely on external BackingStore */
371 This->seamless_rdp = False;
372 This->rdp5_performanceflags = RDP5_NO_WALLPAPER | RDP5_NO_FULLWINDOWDRAG | RDP5_NO_MENUANIMATIONS;
373 This->tcp_port_rdp = TCP_PORT_RDP;
374
375 #define NOT_SET -1
376 This->cache.bmpcache_lru[0] = NOT_SET;
377 This->cache.bmpcache_lru[1] = NOT_SET;
378 This->cache.bmpcache_lru[2] = NOT_SET;
379 This->cache.bmpcache_mru[0] = NOT_SET;
380 This->cache.bmpcache_mru[1] = NOT_SET;
381 This->cache.bmpcache_mru[2] = NOT_SET;
382
383 #ifdef HAVE_ICONV
384 This->rdp.iconv_works = True;
385 #endif
386
387 This->xclip.auto_mode = True;
388
389 #ifdef HAVE_LOCALE_H
390 /* Set locale according to environment */
391 locale = setlocale(LC_ALL, "");
392 if (locale)
393 {
394 locale = xstrdup(locale);
395 }
396
397 #endif
398 flags = RDP_LOGON_NORMAL;
399 prompt_password = False;
400 domain[0] = password[0] = shell[0] = directory[0] = 0;
401 This->embed_wnd = 0;
402
403 This->num_devices = 0;
404
405 #ifdef RDP2VNC
406 #define VNCOPT "V:Q:"
407 #else
408 #define VNCOPT
409 #endif
410
411 while ((c = getopt(argc, argv,
412 VNCOPT "Au:L:d:s:c:p:n:k:g:fbBeEmzCDKS:T:NX:a:x:Pr:045h?")) != -1)
413 {
414 switch (c)
415 {
416 #ifdef RDP2VNC
417 case 'V':
418 This->rfb_port = strtol(optarg, NULL, 10);
419 if (This->rfb_port < 100)
420 This->rfb_port += 5900;
421 break;
422
423 case 'Q':
424 This->defer_time = strtol(optarg, NULL, 10);
425 if (This->defer_time < 0)
426 This->defer_time = 0;
427 break;
428 #endif
429
430 case 'A':
431 This->seamless_rdp = True;
432 break;
433
434 case 'u':
435 STRNCPY(This->username, optarg, sizeof(This->username));
436 username_option = 1;
437 break;
438
439 case 'L':
440 #ifdef HAVE_ICONV
441 STRNCPY(This->codepage, optarg, sizeof(This->codepage));
442 #else
443 error("iconv support not available\n");
444 #endif
445 break;
446
447 case 'd':
448 STRNCPY(domain, optarg, sizeof(domain));
449 break;
450
451 case 's':
452 STRNCPY(shell, optarg, sizeof(shell));
453 break;
454
455 case 'c':
456 STRNCPY(directory, optarg, sizeof(directory));
457 break;
458
459 case 'p':
460 if ((optarg[0] == '-') && (optarg[1] == 0))
461 {
462 prompt_password = True;
463 break;
464 }
465
466 STRNCPY(password, optarg, sizeof(password));
467 flags |= RDP_LOGON_AUTO;
468
469 /* try to overwrite argument so it won't appear in ps */
470 p = optarg;
471 while (*p)
472 *(p++) = 'X';
473 break;
474
475 case 'n':
476 STRNCPY(This->hostname, optarg, sizeof(This->hostname));
477 break;
478
479 case 'k':
480 STRNCPY(This->keymapname, optarg, sizeof(This->keymapname));
481 break;
482
483 case 'g':
484 geometry_option = True;
485 This->fullscreen = False;
486 if (!strcmp(optarg, "workarea"))
487 {
488 This->width = This->height = 0;
489 break;
490 }
491
492 This->width = strtol(optarg, &p, 10);
493 if (This->width <= 0)
494 {
495 error("invalid geometry\n");
496 return 1;
497 }
498
499 if (*p == 'x')
500 This->height = strtol(p + 1, &p, 10);
501
502 if (This->height <= 0)
503 {
504 error("invalid geometry\n");
505 return 1;
506 }
507
508 if (*p == '%')
509 {
510 This->width = -This->width;
511 p++;
512 }
513
514 if (*p == '+' || *p == '-')
515 {
516 This->pos |= (*p == '-') ? 2 : 1;
517 This->xpos = strtol(p, &p, 10);
518
519 }
520 if (*p == '+' || *p == '-')
521 {
522 This->pos |= (*p == '-') ? 4 : 1;
523 This->ypos = strtol(p, NULL, 10);
524 }
525
526 break;
527
528 case 'f':
529 This->fullscreen = True;
530 break;
531
532 case 'b':
533 This->bitmap_cache = False;
534 break;
535
536 case 'B':
537 This->ownbackstore = False;
538 break;
539
540 case 'e':
541 This->encryption = False;
542 break;
543 case 'E':
544 This->packet_encryption = False;
545 break;
546 case 'm':
547 This->sendmotion = False;
548 break;
549
550 case 'C':
551 This->owncolmap = True;
552 break;
553
554 case 'D':
555 This->hide_decorations = True;
556 break;
557
558 case 'K':
559 This->grab_keyboard = False;
560 break;
561
562 case 'S':
563 if (!strcmp(optarg, "standard"))
564 {
565 This->win_button_size = 18;
566 break;
567 }
568
569 This->win_button_size = strtol(optarg, &p, 10);
570
571 if (*p)
572 {
573 error("invalid button size\n");
574 return 1;
575 }
576
577 break;
578
579 case 'T':
580 STRNCPY(This->title, optarg, sizeof(This->title));
581 break;
582
583 case 'N':
584 This->numlock_sync = True;
585 break;
586
587 case 'X':
588 This->embed_wnd = strtol(optarg, NULL, 0);
589 break;
590
591 case 'a':
592 This->server_depth = strtol(optarg, NULL, 10);
593 if (This->server_depth != 8 &&
594 This->server_depth != 16 &&
595 This->server_depth != 15 && This->server_depth != 24)
596 {
597 error("Invalid server colour depth.\n");
598 return 1;
599 }
600 break;
601
602 case 'z':
603 DEBUG(("rdp compression enabled\n"));
604 flags |= (RDP_LOGON_COMPRESSION | RDP_LOGON_COMPRESSION2);
605 break;
606
607 case 'x':
608 if (str_startswith(optarg, "m")) /* modem */
609 {
610 This->rdp5_performanceflags =
611 RDP5_NO_WALLPAPER | RDP5_NO_FULLWINDOWDRAG |
612 RDP5_NO_MENUANIMATIONS | RDP5_NO_THEMING;
613 }
614 else if (str_startswith(optarg, "b")) /* broadband */
615 {
616 This->rdp5_performanceflags = RDP5_NO_WALLPAPER;
617 }
618 else if (str_startswith(optarg, "l")) /* lan */
619 {
620 This->rdp5_performanceflags = RDP5_DISABLE_NOTHING;
621 }
622 else
623 {
624 This->rdp5_performanceflags = strtol(optarg, NULL, 16);
625 }
626 break;
627
628 case 'P':
629 This->bitmap_cache_persist_enable = True;
630 break;
631
632 case 'r':
633
634 if (str_startswith(optarg, "sound"))
635 {
636 optarg += 5;
637
638 if (*optarg == ':')
639 {
640 optarg++;
641 while ((p = next_arg(optarg, ',')))
642 {
643 if (str_startswith(optarg, "remote"))
644 flags |= RDP_LOGON_LEAVE_AUDIO;
645
646 if (str_startswith(optarg, "local"))
647 #ifdef WITH_RDPSND
648 This->rdpsnd_enabled = True;
649 #else
650 warning("Not compiled with sound support\n");
651 #endif
652
653 if (str_startswith(optarg, "off"))
654 #ifdef WITH_RDPSND
655 This->rdpsnd_enabled = False;
656 #else
657 warning("Not compiled with sound support\n");
658 #endif
659
660 optarg = p;
661 }
662 }
663 else
664 {
665 #ifdef WITH_RDPSND
666 This->rdpsnd_enabled = True;
667 #else
668 warning("Not compiled with sound support\n");
669 #endif
670 }
671 }
672 else if (str_startswith(optarg, "disk"))
673 {
674 /* -r disk:h:=/mnt/floppy */
675 disk_enum_devices(This, &This->num_devices, optarg + 4);
676 }
677 else if (str_startswith(optarg, "comport"))
678 {
679 serial_enum_devices(This, &This->num_devices, optarg + 7);
680 }
681 else if (str_startswith(optarg, "lspci"))
682 {
683 This->lspci_enabled = True;
684 }
685 else if (str_startswith(optarg, "lptport"))
686 {
687 parallel_enum_devices(This, &This->num_devices, optarg + 7);
688 }
689 else if (str_startswith(optarg, "printer"))
690 {
691 printer_enum_devices(This, &This->num_devices, optarg + 7);
692 }
693 else if (str_startswith(optarg, "clientname"))
694 {
695 This->rdpdr_clientname = xmalloc(strlen(optarg + 11) + 1);
696 strcpy(This->rdpdr_clientname, optarg + 11);
697 }
698 else if (str_startswith(optarg, "clipboard"))
699 {
700 optarg += 9;
701
702 if (*optarg == ':')
703 {
704 optarg++;
705
706 if (str_startswith(optarg, "off"))
707 This->rdpclip = False;
708 else
709 cliprdr_set_mode(This, optarg);
710 }
711 else
712 This->rdpclip = True;
713 }
714 else
715 {
716 warning("Unknown -r argument\n\n\tPossible arguments are: comport, disk, lptport, printer, sound, clipboard\n");
717 }
718 break;
719
720 case '0':
721 This->console_session = True;
722 break;
723
724 case '4':
725 This->use_rdp5 = False;
726 break;
727
728 case '5':
729 This->use_rdp5 = True;
730 break;
731
732 case 'h':
733 case '?':
734 default:
735 usage(argv[0]);
736 return 1;
737 }
738 }
739
740 if (argc - optind != 1)
741 {
742 usage(argv[0]);
743 return 1;
744 }
745
746 STRNCPY(server, argv[optind], sizeof(server));
747 parse_server_and_port(This, server);
748
749 if (This->seamless_rdp)
750 {
751 if (This->win_button_size)
752 {
753 error("You cannot use -S and -A at the same time\n");
754 return 1;
755 }
756 This->rdp5_performanceflags &= ~RDP5_NO_FULLWINDOWDRAG;
757 if (geometry_option)
758 {
759 error("You cannot use -g and -A at the same time\n");
760 return 1;
761 }
762 if (This->fullscreen)
763 {
764 error("You cannot use -f and -A at the same time\n");
765 return 1;
766 }
767 if (This->hide_decorations)
768 {
769 error("You cannot use -D and -A at the same time\n");
770 return 1;
771 }
772 if (This->embed_wnd)
773 {
774 error("You cannot use -X and -A at the same time\n");
775 return 1;
776 }
777 if (!This->use_rdp5)
778 {
779 error("You cannot use -4 and -A at the same time\n");
780 return 1;
781 }
782 This->width = -100;
783 This->grab_keyboard = False;
784 }
785
786 if (!username_option)
787 {
788 pw = getpwuid(getuid());
789 if ((pw == NULL) || (pw->pw_name == NULL))
790 {
791 error("could not determine username, use -u\n");
792 return 1;
793 }
794
795 STRNCPY(This->username, pw->pw_name, sizeof(This->username));
796 }
797
798 #ifdef HAVE_ICONV
799 if (This->codepage[0] == 0)
800 {
801 if (setlocale(LC_CTYPE, ""))
802 {
803 STRNCPY(This->codepage, nl_langinfo(CODESET), sizeof(This->codepage));
804 }
805 else
806 {
807 STRNCPY(This->codepage, DEFAULT_CODEPAGE, sizeof(This->codepage));
808 }
809 }
810 #endif
811
812 if (This->hostname[0] == 0)
813 {
814 if (gethostname(fullhostname, sizeof(fullhostname)) == -1)
815 {
816 error("could not determine local hostname, use -n\n");
817 return 1;
818 }
819
820 p = strchr(fullhostname, '.');
821 if (p != NULL)
822 *p = 0;
823
824 STRNCPY(This->hostname, fullhostname, sizeof(This->hostname));
825 }
826
827 if (This->keymapname[0] == 0)
828 {
829 if (locale && xkeymap_from_locale(This, locale))
830 {
831 fprintf(stderr, "Autoselected keyboard map %s\n", This->keymapname);
832 }
833 else
834 {
835 STRNCPY(This->keymapname, "en-us", sizeof(This->keymapname));
836 }
837 }
838 if (locale)
839 xfree(locale);
840
841
842 if (prompt_password && read_password(password, sizeof(password)))
843 flags |= RDP_LOGON_AUTO;
844
845 if (This->title[0] == 0)
846 {
847 strcpy(This->title, "rdesktop - ");
848 strncat(This->title, server, sizeof(This->title) - sizeof("rdesktop - "));
849 }
850
851 #ifdef RDP2VNC
852 rdp2vnc_connect(server, flags, domain, password, shell, directory);
853 return 0;
854 #else
855
856 if (!ui_init(This))
857 return 1;
858
859 #ifdef WITH_RDPSND
860 if (This->rdpsnd_enabled)
861 rdpsnd_init(This);
862 #endif
863
864 if (This->lspci_enabled)
865 lspci_init(This);
866
867 rdpdr_init(This);
868
869 while (run_count < 2 && continue_connect) /* add support for Session Directory; only reconnect once */
870 {
871 if (run_count == 0)
872 {
873 if (!rdp_connect(This, server, flags, domain, password, shell, directory))
874 return 1;
875 }
876 else if (!rdp_reconnect
877 (This, server, flags, domain, password, shell, directory, This->redirect_cookie))
878 return 1;
879
880 /* By setting encryption to False here, we have an encrypted login
881 packet but unencrypted transfer of other packets */
882 if (!This->packet_encryption)
883 This->encryption = False;
884
885
886 DEBUG(("Connection successful.\n"));
887 memset(password, 0, sizeof(password));
888
889 if (run_count == 0)
890 if (!ui_create_window(This))
891 continue_connect = False;
892
893 if (continue_connect)
894 rdp_main_loop(This, &deactivated, &ext_disc_reason);
895
896 DEBUG(("Disconnecting...\n"));
897 rdp_disconnect(This);
898
899 if ((This->redirect == True) && (run_count == 0)) /* Support for Session Directory */
900 {
901 /* reset state of major globals */
902 rdesktop_reset_state(This);
903
904 STRNCPY(domain, This->redirect_domain, sizeof(domain));
905 STRNCPY(This->username, This->redirect_username, sizeof(This->username));
906 STRNCPY(password, This->redirect_password, sizeof(password));
907 STRNCPY(server, This->redirect_server, sizeof(server));
908 flags |= RDP_LOGON_AUTO;
909
910 This->redirect = False;
911 }
912 else
913 {
914 continue_connect = False;
915 ui_destroy_window(This);
916 break;
917 }
918
919 run_count++;
920 }
921
922 cache_save_state(This);
923 ui_deinit(This);
924
925 if (ext_disc_reason >= 2)
926 print_disconnect_reason(ext_disc_reason);
927
928 if (deactivated)
929 {
930 /* clean disconnect */
931 return 0;
932 }
933 else
934 {
935 if (ext_disc_reason == exDiscReasonAPIInitiatedDisconnect
936 || ext_disc_reason == exDiscReasonAPIInitiatedLogoff)
937 {
938 /* not so clean disconnect, but nothing to worry about */
939 return 0;
940 }
941 else
942 {
943 /* return error */
944 return 2;
945 }
946 }
947
948 #endif
949
950 }
951
952 #ifdef EGD_SOCKET
953 /* Read 32 random bytes from PRNGD or EGD socket (based on OpenSSL RAND_egd) */
954 static BOOL
955 generate_random_egd(uint8 * buf)
956 {
957 struct sockaddr_un addr;
958 BOOL ret = False;
959 int fd;
960
961 fd = socket(AF_UNIX, SOCK_STREAM, 0);
962 if (fd == -1)
963 return False;
964
965 addr.sun_family = AF_UNIX;
966 memcpy(addr.sun_path, EGD_SOCKET, sizeof(EGD_SOCKET));
967 if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1)
968 goto err;
969
970 /* PRNGD and EGD use a simple communications protocol */
971 buf[0] = 1; /* Non-blocking (similar to /dev/urandom) */
972 buf[1] = 32; /* Number of requested random bytes */
973 if (write(fd, buf, 2) != 2)
974 goto err;
975
976 if ((read(fd, buf, 1) != 1) || (buf[0] == 0)) /* Available? */
977 goto err;
978
979 if (read(fd, buf, 32) != 32)
980 goto err;
981
982 ret = True;
983
984 err:
985 close(fd);
986 return ret;
987 }
988 #endif
989
990 /* Generate a 32-byte random for the secure transport code. */
991 void
992 generate_random(uint8 * random)
993 {
994 struct stat st;
995 struct tms tmsbuf;
996 MD5_CTX md5;
997 uint32 *r;
998 int fd, n;
999
1000 /* If we have a kernel random device, try that first */
1001 if (((fd = open("/dev/urandom", O_RDONLY)) != -1)
1002 || ((fd = open("/dev/random", O_RDONLY)) != -1))
1003 {
1004 n = read(fd, random, 32);
1005 close(fd);
1006 if (n == 32)
1007 return;
1008 }
1009
1010 #ifdef EGD_SOCKET
1011 /* As a second preference use an EGD */
1012 if (generate_random_egd(random))
1013 return;
1014 #endif
1015
1016 /* Otherwise use whatever entropy we can gather - ideas welcome. */
1017 r = (uint32 *) random;
1018 r[0] = (getpid()) | (getppid() << 16);
1019 r[1] = (getuid()) | (getgid() << 16);
1020 r[2] = times(&tmsbuf); /* system uptime (clocks) */
1021 gettimeofday((struct timeval *) &r[3], NULL); /* sec and usec */
1022 stat("/tmp", &st);
1023 r[5] = st.st_atime;
1024 r[6] = st.st_mtime;
1025 r[7] = st.st_ctime;
1026
1027 /* Hash both halves with MD5 to obscure possible patterns */
1028 MD5_Init(&md5);
1029 MD5_Update(&md5, random, 16);
1030 MD5_Final(random, &md5);
1031 MD5_Update(&md5, random + 16, 16);
1032 MD5_Final(random + 16, &md5);
1033 }
1034
1035 /* malloc; exit if out of memory */
1036 void *
1037 xmalloc(int size)
1038 {
1039 void *mem = malloc(size);
1040 if (mem == NULL)
1041 {
1042 error("xmalloc %d\n", size);
1043 exit(1);
1044 }
1045 return mem;
1046 }
1047
1048 /* strdup */
1049 char *
1050 xstrdup(const char *s)
1051 {
1052 char *mem = strdup(s);
1053 if (mem == NULL)
1054 {
1055 perror("strdup");
1056 exit(1);
1057 }
1058 return mem;
1059 }
1060
1061 /* realloc; exit if out of memory */
1062 void *
1063 xrealloc(void *oldmem, int size)
1064 {
1065 void *mem;
1066
1067 if (size < 1)
1068 size = 1;
1069 mem = realloc(oldmem, size);
1070 if (mem == NULL)
1071 {
1072 error("xrealloc %d\n", size);
1073 exit(1);
1074 }
1075 return mem;
1076 }
1077
1078 /* free */
1079 void
1080 xfree(void *mem)
1081 {
1082 free(mem);
1083 }
1084
1085 /* report an error */
1086 void
1087 error(char *format, ...)
1088 {
1089 va_list ap;
1090
1091 fprintf(stderr, "ERROR: ");
1092
1093 va_start(ap, format);
1094 vfprintf(stderr, format, ap);
1095 va_end(ap);
1096 }
1097
1098 /* report a warning */
1099 void
1100 warning(char *format, ...)
1101 {
1102 va_list ap;
1103
1104 fprintf(stderr, "WARNING: ");
1105
1106 va_start(ap, format);
1107 vfprintf(stderr, format, ap);
1108 va_end(ap);
1109 }
1110
1111 /* report an unimplemented protocol feature */
1112 void
1113 unimpl(char *format, ...)
1114 {
1115 va_list ap;
1116
1117 fprintf(stderr, "NOT IMPLEMENTED: ");
1118
1119 va_start(ap, format);
1120 vfprintf(stderr, format, ap);
1121 va_end(ap);
1122 }
1123
1124 /* produce a hex dump */
1125 void
1126 hexdump(unsigned char *p, unsigned int len)
1127 {
1128 unsigned char *line = p;
1129 int i, thisline, offset = 0;
1130
1131 while (offset < len)
1132 {
1133 printf("%04x ", offset);
1134 thisline = len - offset;
1135 if (thisline > 16)
1136 thisline = 16;
1137
1138 for (i = 0; i < thisline; i++)
1139 printf("%02x ", line[i]);
1140
1141 for (; i < 16; i++)
1142 printf(" ");
1143
1144 for (i = 0; i < thisline; i++)
1145 printf("%c", (line[i] >= 0x20 && line[i] < 0x7f) ? line[i] : '.');
1146
1147 printf("\n");
1148 offset += thisline;
1149 line += thisline;
1150 }
1151 }
1152
1153 /*
1154 input: src is the string we look in for needle.
1155 Needle may be escaped by a backslash, in
1156 that case we ignore that particular needle.
1157 return value: returns next src pointer, for
1158 succesive executions, like in a while loop
1159 if retval is 0, then there are no more args.
1160 pitfalls:
1161 src is modified. 0x00 chars are inserted to
1162 terminate strings.
1163 return val, points on the next val chr after ins
1164 0x00
1165
1166 example usage:
1167 while( (pos = next_arg( optarg, ',')) ){
1168 printf("%s\n",optarg);
1169 optarg=pos;
1170 }
1171
1172 */
1173 char *
1174 next_arg(char *src, char needle)
1175 {
1176 char *nextval;
1177 char *p;
1178 char *mvp = 0;
1179
1180 /* EOS */
1181 if (*src == (char) 0x00)
1182 return 0;
1183
1184 p = src;
1185 /* skip escaped needles */
1186 while ((nextval = strchr(p, needle)))
1187 {
1188 mvp = nextval - 1;
1189 /* found backslashed needle */
1190 if (*mvp == '\\' && (mvp > src))
1191 {
1192 /* move string one to the left */
1193 while (*(mvp + 1) != (char) 0x00)
1194 {
1195 *mvp = *(mvp + 1);
1196 mvp++;
1197 }
1198 *mvp = (char) 0x00;
1199 p = nextval;
1200 }
1201 else
1202 {
1203 p = nextval + 1;
1204 break;
1205 }
1206
1207 }
1208
1209 /* more args available */
1210 if (nextval)
1211 {
1212 *nextval = (char) 0x00;
1213 return ++nextval;
1214 }
1215
1216 /* no more args after this, jump to EOS */
1217 nextval = src + strlen(src);
1218 return nextval;
1219 }
1220
1221
1222 void
1223 toupper_str(char *p)
1224 {
1225 while (*p)
1226 {
1227 if ((*p >= 'a') && (*p <= 'z'))
1228 *p = toupper((int) *p);
1229 p++;
1230 }
1231 }
1232
1233
1234 BOOL
1235 str_startswith(const char *s, const char *prefix)
1236 {
1237 return (strncmp(s, prefix, strlen(prefix)) == 0);
1238 }
1239
1240
1241 /* Split input into lines, and call linehandler for each
1242 line. Incomplete lines are saved in the rest variable, which should
1243 initially point to NULL. When linehandler returns False, stop and
1244 return False. Otherwise, return True. */
1245 BOOL
1246 str_handle_lines(RDPCLIENT * This, const char *input, char **rest, str_handle_lines_t linehandler, void *data)
1247 {
1248 char *buf, *p;
1249 char *oldrest;
1250 size_t inputlen;
1251 size_t buflen;
1252 size_t restlen = 0;
1253 BOOL ret = True;
1254
1255 /* Copy data to buffer */
1256 inputlen = strlen(input);
1257 if (*rest)
1258 restlen = strlen(*rest);
1259 buflen = restlen + inputlen + 1;
1260 buf = (char *) xmalloc(buflen);
1261 buf[0] = '\0';
1262 if (*rest)
1263 STRNCPY(buf, *rest, buflen);
1264 strncat(buf, input, inputlen);
1265 p = buf;
1266
1267 while (1)
1268 {
1269 char *newline = strchr(p, '\n');
1270 if (newline)
1271 {
1272 *newline = '\0';
1273 if (!linehandler(This, p, data))
1274 {
1275 p = newline + 1;
1276 ret = False;
1277 break;
1278 }
1279 p = newline + 1;
1280 }
1281 else
1282 {
1283 break;
1284
1285 }
1286 }
1287
1288 /* Save in rest */
1289 oldrest = *rest;
1290 restlen = buf + buflen - p;
1291 *rest = (char *) xmalloc(restlen);
1292 STRNCPY((*rest), p, restlen);
1293 xfree(oldrest);
1294
1295 xfree(buf);
1296 return ret;
1297 }
1298
1299 /* Execute the program specified by argv. For each line in
1300 stdout/stderr output, call linehandler. Returns false on failure. */
1301 BOOL
1302 subprocess(RDPCLIENT * This, char *const argv[], str_handle_lines_t linehandler, void *data)
1303 {
1304 pid_t child;
1305 int fd[2];
1306 int n = 1;
1307 char output[256];
1308 char *rest = NULL;
1309
1310 if (pipe(fd) < 0)
1311 {
1312 perror("pipe");
1313 return False;
1314 }
1315
1316 if ((child = fork()) < 0)
1317 {
1318 perror("fork");
1319 return False;
1320 }
1321
1322 /* Child */
1323 if (child == 0)
1324 {
1325 /* Close read end */
1326 close(fd[0]);
1327
1328 /* Redirect stdout and stderr to pipe */
1329 dup2(fd[1], 1);
1330 dup2(fd[1], 2);
1331
1332 /* Execute */
1333 execvp(argv[0], argv);
1334 perror("Error executing child");
1335 _exit(128);
1336 }
1337
1338 /* Parent. Close write end. */
1339 close(fd[1]);
1340 while (n > 0)
1341 {
1342 n = read(fd[0], output, 255);
1343 output[n] = '\0';
1344 str_handle_lines(This, output, &rest, linehandler, data);
1345 }
1346 xfree(rest);
1347
1348 return True;
1349 }
1350
1351
1352 /* not all clibs got ltoa */
1353 #define LTOA_BUFSIZE (sizeof(long) * 8 + 1)
1354
1355 char *
1356 l_to_a(long N, int base)
1357 {
1358 static char ret[LTOA_BUFSIZE];
1359
1360 char *head = ret, buf[LTOA_BUFSIZE], *tail = buf + sizeof(buf);
1361
1362 register int divrem;
1363
1364 if (base < 36 || 2 > base)
1365 base = 10;
1366
1367 if (N < 0)
1368 {
1369 *head++ = '-';
1370 N = -N;
1371 }
1372
1373 tail = buf + sizeof(buf);
1374 *--tail = 0;
1375
1376 do
1377 {
1378 divrem = N % base;
1379 *--tail = (divrem <= 9) ? divrem + '0' : divrem + 'a' - 10;
1380 N /= base;
1381 }
1382 while (N);
1383
1384 strcpy(head, tail);
1385 return ret;
1386 }
1387
1388
1389 int
1390 load_licence(RDPCLIENT * This, unsigned char **data)
1391 {
1392 char *home, *path;
1393 struct stat st;
1394 int fd, length;
1395
1396 home = getenv("HOME");
1397 if (home == NULL)
1398 return -1;
1399
1400 path = (char *) xmalloc(strlen(home) + strlen(This->hostname) + sizeof("/.rdesktop/licence."));
1401 sprintf(path, "%s/.rdesktop/licence.%s", home, This->hostname);
1402
1403 fd = open(path, O_RDONLY);
1404 if (fd == -1)
1405 return -1;
1406
1407 if (fstat(fd, &st))
1408 return -1;
1409
1410 *data = (uint8 *) xmalloc(st.st_size);
1411 length = read(fd, *data, st.st_size);
1412 close(fd);
1413 xfree(path);
1414 return length;
1415 }
1416
1417 void
1418 save_licence(RDPCLIENT * This, unsigned char *data, int length)
1419 {
1420 char *home, *path, *tmppath;
1421 int fd;
1422
1423 home = getenv("HOME");
1424 if (home == NULL)
1425 return;
1426
1427 path = (char *) xmalloc(strlen(home) + strlen(This->hostname) + sizeof("/.rdesktop/licence."));
1428
1429 sprintf(path, "%s/.rdesktop", home);
1430 if ((mkdir(path, 0700) == -1) && errno != EEXIST)
1431 {
1432 perror(path);
1433 return;
1434 }
1435
1436 /* write licence to licence.hostname.new, then atomically rename to licence.hostname */
1437
1438 sprintf(path, "%s/.rdesktop/licence.%s", home, This->hostname);
1439 tmppath = (char *) xmalloc(strlen(path) + sizeof(".new"));
1440 strcpy(tmppath, path);
1441 strcat(tmppath, ".new");
1442
1443 fd = open(tmppath, O_WRONLY | O_CREAT | O_TRUNC, 0600);
1444 if (fd == -1)
1445 {
1446 perror(tmppath);
1447 return;
1448 }
1449
1450 if (write(fd, data, length) != length)
1451 {
1452 perror(tmppath);
1453 unlink(tmppath);
1454 }
1455 else if (rename(tmppath, path) == -1)
1456 {
1457 perror(path);
1458 unlink(tmppath);
1459 }
1460
1461 close(fd);
1462 xfree(tmppath);
1463 xfree(path);
1464 }
1465
1466 /* Create the bitmap cache directory */
1467 BOOL
1468 rd_pstcache_mkdir(void)
1469 {
1470 char *home;
1471 char bmpcache_dir[256];
1472
1473 home = getenv("HOME");
1474
1475 if (home == NULL)
1476 return False;
1477
1478 sprintf(bmpcache_dir, "%s/%s", home, ".rdesktop");
1479
1480 if ((mkdir(bmpcache_dir, S_IRWXU) == -1) && errno != EEXIST)
1481 {
1482 perror(bmpcache_dir);
1483 return False;
1484 }
1485
1486 sprintf(bmpcache_dir, "%s/%s", home, ".rdesktop/cache");
1487
1488 if ((mkdir(bmpcache_dir, S_IRWXU) == -1) && errno != EEXIST)
1489 {
1490 perror(bmpcache_dir);
1491 return False;
1492 }
1493
1494 return True;
1495 }
1496
1497 /* open a file in the .rdesktop directory */
1498 int
1499 rd_open_file(char *filename)
1500 {
1501 char *home;
1502 char fn[256];
1503 int fd;
1504
1505 home = getenv("HOME");
1506 if (home == NULL)
1507 return -1;
1508 sprintf(fn, "%s/.rdesktop/%s", home, filename);
1509 fd = open(fn, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
1510 if (fd == -1)
1511 perror(fn);
1512 return fd;
1513 }
1514
1515 /* close file */
1516 void
1517 rd_close_file(int fd)
1518 {
1519 close(fd);
1520 }
1521
1522 /* read from file*/
1523 int
1524 rd_read_file(int fd, void *ptr, int len)
1525 {
1526 return read(fd, ptr, len);
1527 }
1528
1529 /* write to file */
1530 int
1531 rd_write_file(int fd, void *ptr, int len)
1532 {
1533 return write(fd, ptr, len);
1534 }
1535
1536 /* move file pointer */
1537 int
1538 rd_lseek_file(int fd, int offset)
1539 {
1540 return lseek(fd, offset, SEEK_SET);
1541 }
1542
1543 /* do a write lock on a file */
1544 BOOL
1545 rd_lock_file(int fd, int start, int len)
1546 {
1547 struct flock lock;
1548
1549 lock.l_type = F_WRLCK;
1550 lock.l_whence = SEEK_SET;
1551 lock.l_start = start;
1552 lock.l_len = len;
1553 if (fcntl(fd, F_SETLK, &lock) == -1)
1554 return False;
1555 return True;
1556 }