File Coverage

blib/lib/Win32/RASE.pm
Criterion Covered Total %
statement 64 664 9.6
branch 2 352 0.5
condition 0 197 0.0
subroutine 21 73 28.7
pod n/a
total 87 1286 6.7


line stmt bran cond sub pod time code
1             # Manipulating RAS/DUN-Entry Properties, outbound dialing
2             # Mike Blazer
3              
4             package Win32::RASE;
5              
6 1         484 use vars qw($VERSION $LOCAL_ID $LOCAL_CODE $LOCAL_AREA $WINVER
7             @ISA @EXPORT %RASCS $Time_HiRes_loaded $LastError $IsWindow
8             $RasDial $RasEnumConnections $RasHangUp $RasRenameEntry $RasDeleteEntry
9             $RasEnumEntries $RasEnumDevices $RasGetConnectStatus $RasGetEntryProperties
10             $RasSetEntryProperties $RasDialDlg $RasGetEntryDialParams $RasSetEntryDialParams
11             $RasGetCountryInfo $RasCreateEntry $RasEditEntry
12             $RasGetErrorString $lineGetTranslateCaps $RasGetProjectionInfo
13             %TAPIEnumeration @RASCS_vars @RASEO_vars $PHONEBOOK
14             $lineInitialize $lineShutdown $lineSetCurrentLocation
15             %RasDevEnumeration
16 1     1   1251 );
  1         3  
17              
18             require 5.000;
19             require Win32::API;
20 1     1   8 use strict "vars";
  1         2  
  1         39  
21 1     1   7 use Carp;
  1         5  
  1         152  
22 1     1   925 use enum 1.014;
  1         1427  
  1         6  
23              
24             require Exporter;
25             @ISA= qw( Exporter );
26              
27             @RASCS_vars = qw(
28             RASCS_OpenPort
29             RASCS_PortOpened
30             RASCS_ConnectDevice
31             RASCS_DeviceConnected
32             RASCS_AllDevicesConnected
33             RASCS_Authenticate
34             RASCS_AuthNotify
35             RASCS_AuthRetry
36             RASCS_AuthCallback
37             RASCS_AuthChangePassword
38             RASCS_AuthProject
39             RASCS_AuthLinkSpeed
40             RASCS_AuthAck
41             RASCS_ReAuthenticate
42             RASCS_Authenticated
43             RASCS_PrepareForCallback
44             RASCS_WaitForModemReset
45             RASCS_WaitForCallback
46             RASCS_Projected
47             RASCS_StartAuthentication
48             RASCS_CallbackComplete
49             RASCS_LogonNetwork
50             RASCS_SubEntryConnected
51             RASCS_SubEntryDisconnected
52             RASCS_Interactive
53             RASCS_PAUSED
54             RASCS_RetryAuthentication
55             RASCS_CallbackSetByCaller
56             RASCS_PasswordExpired
57             RASCS_Connected
58             RASCS_DONE
59             RASCS_Disconnected
60             );
61              
62 1         5 use enum qw(
63             :RASCS_=0
64             OpenPort
65             PortOpened
66             ConnectDevice
67             DeviceConnected
68             AllDevicesConnected
69             Authenticate
70             AuthNotify
71             AuthRetry
72             AuthCallback
73             AuthChangePassword
74             AuthProject
75             AuthLinkSpeed
76             AuthAck
77             ReAuthenticate
78             Authenticated
79             PrepareForCallback
80             WaitForModemReset
81             WaitForCallback
82             Projected
83             StartAuthentication
84             CallbackComplete
85             LogonNetwork
86             SubEntryConnected
87             SubEntryDisconnected
88             Interactive=4096
89             PAUSED=4096
90             RetryAuthentication
91             CallbackSetByCaller
92             PasswordExpired
93             Connected=8192
94             DONE=8192
95             Disconnected
96 1     1   107 );
  1         1  
97              
98             # %RASCS to provide short text explaining numeric value
99             for my $v(@RASCS_vars) {
100             next if $v =~ /(PAUSED|DONE)$/;
101              
102             ($RASCS{eval $v} = $v) =~ s/^RASCS_//;
103             }
104              
105              
106 1         7 use enum @RASEO_vars = qw(
107             BITMASK:
108             RASEO_UseCountryAndAreaCodes
109             RASEO_SpecificIpAddr
110             RASEO_SpecificNameServers
111             RASEO_IpHeaderCompression
112             RASEO_RemoteDefaultGateway
113             RASEO_DisableLcpExtensions
114             RASEO_TerminalBeforeDial
115             RASEO_TerminalAfterDial
116             RASEO_ModemLights
117             RASEO_SwCompression
118             RASEO_RequireEncryptedPw
119             RASEO_RequireMsEncryptedPw
120             RASEO_RequireDataEncryption
121             RASEO_NetworkLogon
122             RASEO_UseLogonCredentials
123             RASEO_PromoteAlternates
124             RASEO_SecureLocalFiles
125 1     1   4004 );
  1         2  
126             shift @RASEO_vars;
127              
128 1         5 use enum qw(
129             MAX_PATH=260
130             :RAS_
131             MaxDeviceType=16
132             MaxPhoneNumber=128
133             MaxIpAddress=15
134             MaxIpxAddress=21
135 1     1   1188 );
  1         1  
136              
137             BEGIN {
138             # build number might have problems with some older NTs
139             # says:
140              
141             # WINVER values in this file:
142             # WINVER < 0x400 = Windows NT 3.5, Windows NT 3.51
143             # WINVER = 0x400 = Windows 95, Windows NT SUR (default)
144             # i.e. 4.0 Shell Update Release
145             # WINVER > 0x400 = Windows NT SUR enhancements (nobody knows what's this)
146 1     1   458 $WINVER = (Win32::GetOSVersion)[3];
147 1         41 $WINVER &= 0xFFFF if Win32::IsWin95;
148             }
149              
150 1 50       7 use enum $WINVER >= 0x400 ?
151             qw( :RAS_
152             MaxEntryName=256
153             MaxDeviceName=128
154             MaxCallbackNumber=128
155             ) :
156             qw( :RAS_
157             MaxEntryName=20
158             MaxDeviceName=32
159             MaxCallbackNumber=48
160 1     1   6 );
  1         2  
161              
162 1         6 use enum qw(
163             :RAS_
164             MaxAreaCode=10
165             MaxPadType=32
166             MaxX25Address=200
167             MaxFacilities=200
168             MaxUserData=200
169 1     1   276 );
  1         2  
170              
171             # RASENTRY 'dwProtocols' bit flags.
172 1     1   457 use enum qw( BITMASK:RASNP_ NetBEUI Ipx Ip);
  1         2  
  1         5  
173              
174             # RASENTRY 'dwFramingProtocols' bit flags.
175 1     1   280 use enum qw( BITMASK:RASFP_ Ppp Slip Ras);
  1         1  
  1         5  
176              
177             # RASENTRY 'szDeviceType' default strings.
178 1     1   5324 use enum qw( :RASDT_ Modem=modem Isdn=isdn X25=x25);
  1         2  
  1         176  
179              
180             # from lmcons.h
181 1         4 use enum qw(
182             UNLEN=256
183             PWLEN=256
184             DNLEN=15
185             PST_MODEM=6
186 1     1   674 );
  1         9  
187              
188              
189             # SpeakerVolume for MODEMSETTINGS
190 1     1   352 use enum qw( :MDMVOL_=0 LOW MEDIUM HIGH );
  1         2  
  1         6  
191              
192             # SpeakerMode for MODEMSETTINGS
193 1     1   243 use enum qw( :MDMSPKR_=0 OFF DIAL ON CALLSETUP);
  1         2  
  1         5  
194              
195             # Modem Options
196 1         5 use enum qw( BITMASK:MDM_ COMPRESSION ERROR_CONTROL FORCED_EC
197             CELLULAR FLOWCONTROL_HARD FLOWCONTROL_SOFT CCITT_OVERRIDE
198             SPEED_ADJUST TONE_DIAL BLIND_DIAL V23_OVERRIDE
199 1     1   317 );
  1         2  
200              
201 1         5 use enum qw(
202             RASP_Amb=0x10000
203             RASP_PppNbf=0x803F
204             RASP_PppIpx=0x802B
205             RASP_PppIp=0x8021
206             RASP_PppLcp=0xC021
207             RASP_Slip=0x20000
208 1     1   768 );
  1         2  
209              
210 1         4 use enum qw(
211             BITMASK:
212             TERMINAL_PRE
213             TERMINAL_POST
214             MANUAL_DIAL
215             LAUNCH_LIGHTS
216 1     1   526 );
  1         2  
217              
218              
219             @EXPORT = (qw(
220             RasEnumConnections RasHangUp HangUp
221             RasGetConnectStatus RasDial RasDialDlg
222             RasGetProjectionInfo
223              
224             TAPICountryName TAPICountryCode IsCountryID
225             TAPISetCurrentLocation
226              
227             RasCreateEntryDlg RasEditEntryDlg RasEnumDevices
228             RasRenameEntry RasDeleteEntry RasEnumEntries IsEntry
229             RasGetEntryDialParams RasSetEntryDialParams RasGetUserPwd
230             RasGetEntryProperties RasSetEntryProperties
231             RasPrintEntryProperties RasChangePhoneNumber RasCopyEntry
232             RasCreateEntry RasEnumDevicesByType
233              
234             RasGetEntryDevProperties RasPrintEntryDevProperties
235             ), @RASCS_vars, @RASEO_vars);
236              
237             $VERSION = "1.01";
238              
239 1     1   399 use constant DWORD_NULL => pack("L",0);
  1         2  
  1         297  
240              
241             sub CRUNCH (@) {
242 0     0     local $_;
243 0           for (@_) { s/\0.*$//s }
  0            
244             }
245              
246             sub TRIM_LR ($) {
247 0     0     $_[0] =~ s/^ *(.*?) *$/$1/s;
248             }
249              
250             sub DWORD_ALIGN ($) {
251 0 0   0     $_[0] = $_[0] + 4 - $_[0] % 4 if $_[0] % 4;
252             }
253              
254             # for precise loops
255             BEGIN{
256 1     1   61 eval "require Time::HiRes";
257 1 50       2030 unless ($@) {
258 1         5 import Time::HiRes qw(sleep time);
259 1         200 $Time_HiRes_loaded = 1;
260             }
261 1         13010 undef $@;
262             }
263              
264              
265              
266             TAPIlineGetTranslateCaps();
267              
268              
269             sub new (@) {
270 0     0     my ($ret, $dll);
271 0           ($dll = $_[0])=~ s/(\.dll)?$/.dll/i;
272 0 0         $ret = new Win32::API(@_) or croak "Win32::RASE: $_[1] not found in $dll\n";
273             }
274              
275             sub RASERROR ($) {
276 0     0     my $ret = shift;
277 0           my $sub = (caller(1))[3];
278              
279 0           croak "$sub: ".FormatMessage($ret)."\n";
280             }
281              
282             sub RASCROAK ($) {
283 0     0     my $sub = (caller(1))[3];
284              
285 0           croak "$sub: ".shift()."\n";
286             }
287              
288             =head1 NAME
289              
290             Win32::RASE - managing dialup entries and network connections on Win32
291              
292             =head1 SYNOPSIS
293              
294             use Win32::RASE;
295              
296              
297             =head1 ABSTRACT
298              
299             This module implements the client part of Win32 RAS API.
300              
301             It is named RASE(RAS-entry) because it was originally designed
302             to create/delete/change/manage RAS/DUN entries. Now it implements
303             synchronous dialing, hang up and the wide range of RAS/DUN
304             entry manipulations.
305              
306             The current version of Win32::RASE is available at:
307              
308             http://www.dux.ru/guest/fno/perl/
309              
310             =head1 DESCRIPTION
311              
312             This module is a collection of subroutines. As their names are very long
313             and specific and almost each corresponds to a Win32 API call I decided
314             to export a lot of them by default. Everything is exported except those
315             subs that are claimed as non-exported.
316              
317             OK, you can C it instead of C.
318              
319             B
320             All functions (if the other behavior is not stated explicitly)
321             return TRUE on success, FALSE on error
322             to conform the handy calling rule
323              
324             RESULT = function(PARAMS) or die MESSAGE;
325              
326             where RESULT could be scalar or list either. Note that "||" is not
327             the same thing as "or".
328              
329             The following logic is used: almost all functions croak on obvious programmer's
330             errors like invalid entry-name or such.
331             But they return FALSE and set LastError on internal API errors.
332             It is made to give the programmer a chance to complete all actions and may be
333             to trap some errors without exiting the program.
334              
335             For example if some phonebook file is corrupted you have a chance
336             to try another one etc.
337              
338             =over 4
339              
340              
341             The following two functions are available after any other function was executed.
342             They are both non-exported to provide feel and look of Win32-Perl built-in
343             functions with the same names.
344              
345             =item GetLastError ( )
346              
347             Returns 0 or the last encountered RAS, TAPI or Windows error number.
348              
349             $lastErr = Win32::RASE::GetLastError();
350              
351             Usually you should call this function after some other function
352             returned C. In case of Windows error it returns the same value as
353             C. Unlike the built-in one it always returns 0
354             if the last called function finished successfully.
355              
356             You can use it for example like this:
357              
358             some_function();
359             Win32::RASE::GetLastError and die Win32::RASE::FormatMessage;
360              
361             or implicitly
362              
363             some_function() or die Win32::RASE::FormatMessage;
364              
365             =cut
366              
367             #================
368             sub GetLastError () {
369             #================
370 0 0   0     $LastError||0;
371             }
372              
373             =item FormatMessage ( )
374              
375             Converts the supplied RAS, TAPI or Win32 error number (e.g.
376             returned by C) to a descriptive string.
377              
378             $message = Win32::RASE::FormatMessage($err_num);
379              
380             Without the parameter assumes that the result of
381             C was sent.
382              
383             =cut
384              
385             #================
386             sub FormatMessage (;$) {
387             #================
388 0   0 0     my ($errnum, $buf) = (shift || GetLastError(), "\0"x1024);
389              
390 0 0         $errnum =~ /^\-?\d+$/ or
391             RASCROAK "non-numeric value `$errnum'";
392              
393 0 0 0       if ($errnum >= 600 && $errnum <= 750) {
    0          
    0          
    0          
    0          
    0          
394 0   0       $RasGetErrorString ||= new("rasapi32", "RasGetErrorString", [I,P,N], N);
395              
396 0           my $ret = $RasGetErrorString->Call($errnum, $buf, length $buf);
397 0 0         $ret and RASERROR($ret);
398              
399 0           CRUNCH($buf);
400 0           return "($errnum) $buf";
401              
402             } elsif ($errnum == 751) {
403 0           return "(751) ERROR_INVALID_CALLBACK_NUMBER";
404             } elsif ($errnum == 752) {
405 0           return "(752) ERROR_SCRIPT_SYNTAX";
406              
407             # TAPI LINEERR_* constants
408             } elsif ($errnum & 0x80000000) {
409 0           return "TAPI-error 0x".(sprintf "%8.8X",$errnum);
410              
411             # TAPI PHONEERR_* constants
412             } elsif ($errnum & 0x90000000) {
413 0           return "TAPI-error 0x".(sprintf "%8.8X",$errnum);
414              
415             # TAPI TAPIERR_* constants
416             } elsif ($errnum > 0xFFFF0000) {
417 0           return "TAPI-error 0x".(sprintf "%8.8X",$errnum);
418             }
419              
420 0           "($errnum) ".Win32::FormatMessage($errnum);
421             }
422              
423              
424             =item IsWindow ( )
425              
426             This function is non-exported for not to corrupt some other GUI related
427             synonym.
428              
429             Win32::RASE::IsWindow( $hwnd );
430              
431             Returns TRUE if $hwnd identifies an existing window, otherwise FALSE.
432              
433             This function is handy to use before the functions that display a dialog box -
434             to verify the parent window.
435              
436             =cut
437              
438             #================
439             sub IsWindow ($) {
440             #================
441 0     0     my $hwnd = shift;
442              
443             # to free dll right after the call (Dlg-functions are rare)
444 0           my $IsWindow = new("user32", "IsWindow", [N], N);
445 0           $IsWindow->Call($hwnd);
446             }
447              
448             =pod
449              
450             =back
451              
452             B< =====================================>
453              
454             B< PHONEBOOK RELATED FUNCTIONS>
455              
456             B< =====================================>
457              
458             Note that by default all functions in this section work
459             with the default phonebook (on Windows NT).
460              
461             The registry key C<"HKEY_CURRENT_USER\Software\Microsoft\RAS Phonebook">
462             has a dword subkey "PhonebookMode" which could have 3 values:
463              
464             0 - the "system" phonebook is in use.
465             This is probably %SYSTEMROOT%\system32\ras\rasphone.pbk
466             1 - the "user" phonebook is in use.
467             This one is located in %SYSTEMROOT%\system32\ras\
468             here is the value of "PersonalPhonebookFile" subkey
469             that is located under the same key.
470             2 - the "alternate" phonebook is in use.
471             The full path to the alternate phonebook could be found in the
472             "AlternatePhonebookPath" subkey under the same key.
473              
474             This version of C provides no way to change these registry
475             settings. If C<"HKEY_CURRENT_USER\Software\Microsoft\RAS Phonebook\PhonebookMode">
476             is equal to 0 C will use the "system" phonebook, in case 1 -
477             the "user" phonebook, in case 2 - the "alternate" phonebook.
478              
479             The user can use the main Dial-Up Networking dialog box to create personal
480             phonebook files or change defaults (registry settings). The Win32 API does
481             not currently provide support for creating a phonebook file.
482              
483             B
484              
485             At any time you can set a global variable B<$Win32::RASE::PHONEBOOK> to the full path
486             of your phonebook file, and this phonebook will be in use until
487             B<$Win32::RASE::PHONEBOOK> is changed. Setting this variable to 0 or C
488             returns us to registry defined phonebook(s).
489              
490             B Dial-up networking stores phonebook entries in the registry
491             rather than in a phonebook file. Windows 9x does not support personal
492             phonebook files. So B<$Win32::RASE::PHONEBOOK> has no meaning and must
493             always be C.
494              
495             All functions treat entry-names as case-sensitive because RAS functions
496             are kinda semi-case-sensitive. Some of them fail when entry was given
497             with case-changes. But at the same time C API call
498             (in C) fails to create both QWERTY and QwErTy, it renames
499             instead. Ou-h-h MS, MS...
500              
501             The moral is: don't use names that differ only in upper/lower case.
502              
503             There also is a danger in using multiple processes that are calling
504             RAS APIs that update the phonebook. Microsoft reported this problem
505             has been corrected in Service Pack 3.
506              
507             http://support.microsoft.com/support/ntserver/serviceware/nts40/E9MSKWBJI.ASP
508              
509             B: there are no ways to use Multilink
510             programmatically on Win95/98. So, the current version of the module does not
511             support it for WinNT also. For more info read:
512              
513             http://support.microsoft.com/support/kb/articles/q198/7/77.asp
514              
515             Entry names for Windows CE cannot exceed 20 characters.
516             http://msdn.microsoft.com/library/wincesdk/wcecomm/ras_24.htm
517              
518             A similiar problem is reported for the InternetMail Service (IMS) on
519             MS BackOffice Small Business Server version 4.5 and Windows NT Server version 4.0
520             http://support.microsoft.com/support/kb/articles/Q217/9/37.asp
521              
522             So, the entries with long names may be unusable by the other applications.
523              
524             =over 4
525              
526             =item RasCreateEntryDlg ( )
527              
528             This function displays a dialog box in which the user types information
529             about the phonebook entry.
530              
531             RasCreateEntryDlg( [$hwnd] );
532              
533             $hwnd - handle to the parent window of the dialog box. Optional.
534             If you are using Win32::GUI this would be $Window->{handle}
535              
536             As this is a synchronous operation and waits for user input it provides no
537             way to find out whether the new entry was created or not. You should use
538             C to understand what has happened.
539              
540             Here and everywhere in the functions that display a dialog box - if C<$hwnd>
541             is omitted or does not identify an existing window a dialog box is centered
542             on the screen.
543              
544             =cut
545              
546             #================
547             sub RasCreateEntryDlg (;$) {
548             #================
549 0     0     my $hwnd = shift;
550 0           $LastError = 0;
551              
552 0 0 0       $hwnd = 0 if $hwnd && !IsWindow($hwnd);
553              
554 0   0       $RasCreateEntry ||= new("rasapi32", "RasCreatePhonebookEntry", [N,P], N);
555              
556 0   0       my $ret = $RasCreateEntry->Call($hwnd||0, $PHONEBOOK||0);
      0        
557              
558 0 0         $ret and ($LastError = $ret, return);
559 0           1;
560             }
561              
562             =item RasEditEntryDlg ( )
563              
564             This function displays a dialog box in which the user types information
565             about the phonebook entry. For a programmatical way to edit an existing
566             entry take a look at C.
567              
568             RasEditEntryDlg( $entry [, $hwnd] );
569              
570             $entry - existing phonebook entry to edit.
571              
572             $hwnd - handle to the parent window of the dialog box. Optional.
573             If you are using Win32::GUI this would be $Window->{handle}
574              
575             This is a synchronous operation and waits for user input.
576              
577             Croaks if C<$entry> does not exist.
578             You should call C before to verify C<$entry>.
579              
580             =cut
581              
582             #================
583             sub RasEditEntryDlg ($;$) {
584             #================
585 0     0     my ($entry, $hwnd) = @_;
586 0           $LastError = 0;
587              
588 0 0 0       $hwnd = 0 if $hwnd && !IsWindow($hwnd);
589              
590 0 0         IsEntry($entry) or RASCROAK "`$entry' entry not found";
591              
592 0   0       $RasEditEntry ||= new("rasapi32", "RasEditPhonebookEntry", [N,P,P], N);
593              
594 0   0       my $ret = $RasEditEntry->Call($hwnd||0, $PHONEBOOK||0, $entry);
      0        
595              
596 0 0         $ret and ($LastError = $ret, return);
597 0           1;
598             }
599              
600             =item RasRenameEntry ( )
601              
602             RasRenameEntry( $oldname, $newname );
603              
604             Croaks if C<$oldname> does not exist or C<$newname> already exists.
605             You should call C or C before to verify both.
606              
607             =cut
608              
609             #================
610             sub RasRenameEntry ($$) {
611             #================
612 0     0     my ($old, $new) = @_;
613 0           $LastError = 0;
614              
615 0 0         IsEntry($old) or RASCROAK "`$old' entry not found";
616 0 0         IsEntry($new) and RASCROAK "`$new' entry already exists";
617              
618 0   0       $RasRenameEntry ||= new("rasapi32", "RasRenameEntry", [P,P,P], N);
619              
620 0   0       my $ret = $RasRenameEntry->Call($PHONEBOOK||0, $old, $new);
621              
622 0 0         $ret and ($LastError = $ret, return);
623 0           1;
624             }
625              
626             =item RasDeleteEntry ( )
627              
628             RasDeleteEntry( $entry );
629              
630             Croaks if C<$entry> does not exist.
631             You should call C or C before to verify C<$entry>.
632              
633             =cut
634              
635             #================
636             sub RasDeleteEntry ($) {
637             #================
638 0     0     my $entry = shift;
639 0           $LastError = 0;
640              
641 0 0         IsEntry($entry) or RASCROAK "`$entry' entry not found";
642              
643 0   0       $RasDeleteEntry ||= new("rasapi32", "RasDeleteEntry", [P,P], N);
644              
645 0   0       my $ret = $RasDeleteEntry->Call($PHONEBOOK||0, $entry);
646              
647 0 0         $ret and ($LastError = $ret, return);
648 0           1;
649             }
650              
651             =item RasEnumEntries ( )
652              
653             @entries = RasEnumEntries();
654              
655             This function lists all entry names in the phonebook.
656              
657             As this function is heavily used internally it croaks on errors - for
658             example if non-existing phonebook name is given. So, FALSE result means
659             that the selected phonebook is empty.
660              
661             Command line syntax:
662              
663             perl -MWin32::RASE -e "$,=q{, };print RasEnumEntries"
664              
665             =cut
666              
667             #================
668             sub RasEnumEntries () {
669             #================
670 0     0     $LastError = 0;
671 0   0       $RasEnumEntries ||= new("rasapi32", "RasEnumEntries", [P,P,P,P,P], N);
672              
673 0           my $dwSize = RAS_MaxEntryName+1+4; DWORD_ALIGN($dwSize);
  0            
674              
675 0           my $RASENTRYNAME = pack "La".(20*$dwSize-4), ($dwSize, "");
676              
677 0           my ($lpcb, $lpcEntries) = (pack("L",length $RASENTRYNAME), DWORD_NULL);
678              
679 0   0       my $ret = $RasEnumEntries->Call(0, $PHONEBOOK||0,
680             $RASENTRYNAME, $lpcb, $lpcEntries);
681              
682 0 0         if ($ret) {
683 0           my $cb = unpack "L",$lpcb;
684 0           $RASENTRYNAME = pack "La".($cb-4), ($dwSize, "");
685              
686 0 0 0       $ret = $RasEnumEntries->Call(0, $PHONEBOOK||0,
687             $RASENTRYNAME, $lpcb, $lpcEntries) and RASERROR($ret);
688             }
689              
690 0           my @entries;
691              
692 0           for my $i(1..unpack "L",$lpcEntries) {
693 0           my $buffer = substr $RASENTRYNAME, ($dwSize*($i-1)), $dwSize;
694              
695 0           my ($dwSize1, $szEntryName) = unpack "La".($dwSize-4), $buffer;
696              
697 0           CRUNCH($szEntryName);
698 0           push @entries, $szEntryName;
699             }
700 0           @entries;
701             }
702              
703             =item IsEntry ( )
704              
705             IsEntry ( $entry );
706              
707             $entry - name of the RAS/DUN entry
708              
709             Returns TRUE if C<$entry> was found in the phonebook,
710             otherwise FALSE.
711              
712             B It treats entry-names as case-sensitive (see above).
713              
714             =cut
715              
716             #================
717             sub IsEntry ($) {
718             #================
719 0     0     my $entry = shift;
720 0           $LastError = 0;
721 0           grep {$_ eq $entry} RasEnumEntries();
  0            
722             }
723              
724             =item RasGetEntryDialParams ( )
725              
726             This function retrieves the connection information saved by the last successful
727             call to the C or C function for a specified
728             phonebook entry.
729              
730             ($UserName, $Password, $Domain, $CallbackNumber) =
731             RasGetEntryDialParams($entry);
732              
733             $entry - name of RAS/DUN entry
734             $UserName - user's user name ;-)
735             $Password - yes, it's that secure
736             $Domain - domain on which authentication is to occur
737             $CallbackNumber - callback phone number
738              
739             Croaks if C<$entry> does not exist.
740              
741             =cut
742              
743             #================
744             sub RasGetEntryDialParams ($) {
745             #================
746             # domain in addr form because DNLEN = 15
747             # alternate $szPhoneNumber seems like is not stored in phonebook
748             # because RasSetEntryDialParams() does not set it
749 0     0     my ($szEntryName, $szPhoneNumber, $szUserName,
750             $szPassword, $szDomain, $szCallbackNumber) = shift;
751 0           local $_;
752 0           $LastError = 0;
753              
754 0 0         IsEntry($szEntryName) or RASCROAK "`$szEntryName' entry not found";
755              
756 0   0       $RasGetEntryDialParams ||= new("rasapi32", "RasGetEntryDialParams", [P,P,P], N);
757              
758 0 0         my $dwSize = 4 + RAS_MaxEntryName + 1 + RAS_MaxPhoneNumber + 1 +
759             RAS_MaxCallbackNumber + 1 + UNLEN + 1 + PWLEN + 1 + DNLEN + 1 +
760             (Win32::IsWinNT && $WINVER >= 0x401 ? 4+4 : 0);
761              
762 0           DWORD_ALIGN($dwSize);
763              
764 0           my $RASDIALPARAMS =
765             pack "La".(RAS_MaxEntryName + 1), ($dwSize, $szEntryName);
766              
767 0           $RASDIALPARAMS .= "\0"x($dwSize - length $RASDIALPARAMS);
768              
769 0           my $lpfPassword = DWORD_NULL;
770 0           my $ret;
771 0   0       $ret = $RasGetEntryDialParams->Call($PHONEBOOK||0,
772             $RASDIALPARAMS, $lpfPassword);
773              
774 0 0         $ret and ($LastError = $ret, return);
775              
776 0           my $fPassword = unpack "L", $lpfPassword;
777              
778 0           ($szCallbackNumber, $szUserName, $szPassword, $szDomain) =
779             unpack "a".(RAS_MaxCallbackNumber + 1)."a".(UNLEN + 1).
780             "a".(PWLEN + 1)."a".(DNLEN + 1),
781             substr($RASDIALPARAMS, 4 + RAS_MaxEntryName + 1 + RAS_MaxPhoneNumber + 1);
782              
783 0           CRUNCH($szUserName, $szPassword, $szDomain, $szCallbackNumber);
784 0 0         undef $szPassword unless $fPassword;
785              
786 0           ($szUserName, $szPassword, $szDomain, $szCallbackNumber);
787             }
788              
789             =item RasGetUserPwd ( )
790              
791             The short variant of previous.
792              
793             ($UserName, $Password) = RasGetUserPwd($entry);
794              
795             Croaks if C<$entry> does not exist.
796              
797             Command line syntax:
798              
799             perl -MWin32::RASE -e "print ((RasGetUserPwd('NEV1'))[0])"
800             perl -MWin32::RASE -e "@_=RasGetUserPwd('NEV1');print qq{@_}"
801              
802             =cut
803              
804             #================
805             sub RasGetUserPwd ($) {
806             #================
807 0     0     $LastError = 0;
808 0 0         my @a = RasGetEntryDialParams(shift) or return;
809 0           @a[0,1];
810             }
811              
812             =item RasSetEntryDialParams ( )
813              
814             This function changes the connection information for a specified
815             phonebook entry.
816              
817             RasSetEntryDialParams($entry, $UserName, $Password, $Domain,
818             $CallbackNumber, $fRemovePassword);
819              
820             All parameters except C<$entry> are optional. C or omitted
821             parameters are considered to be "" - this means that no changes will
822             be made to this parameter.
823              
824             $entry - name of RAS/DUN entry
825             $UserName - user name
826             $Password - password for the user specified by $UserName.
827             If $UserName is an empty string, the password is not changed.
828             If $Password is an empty string and $fRemovePassword is FALSE,
829             the password is set to the empty string. If $fRemovePassword is
830             TRUE, the password stored in this phonebook entry for the user
831             specified by $UserName is removed regardless of the contents
832             of the $Password string.
833             $Domain - domain on which authentication is to occur.
834             15 chars limitation.
835             $CallbackNumber - callback phone number
836             $fRemovePassword - (above) 0 if undefined/omitted
837              
838              
839             This is another excerpt from the API docs:
840              
841             B You can use $Password to send a new password to the remote server
842             when you restart a RasDial() connection from a RASCS_PasswordExpired paused state.
843             When changing a password on an entry that calls Microsoft Networks, you should
844             limit the new password to 14 characters in length to avoid down-level
845             compatibility problems.
846              
847             Croaks if C<$entry> does not exist.
848              
849             =cut
850              
851             #================
852             sub RasSetEntryDialParams ($;$$$$$) {
853             #================
854             # domain in addr form because DNLEN = 15
855             # alternate $szPhoneNumber is not set
856             # each empty/undef value here means "don't change old value".
857              
858 0     0     my ($szEntryName, $szUserName, $szPassword,
859             $szDomain, $szCallbackNumber, $fRemovePassword) = @_;
860 0           my $szPhoneNumber;
861 0           local $_;
862 0           $LastError = 0;
863              
864 0 0         IsEntry($szEntryName) or RASCROAK "`$szEntryName' entry not found";
865              
866 0   0       $RasSetEntryDialParams ||= new("rasapi32", "RasSetEntryDialParams", [P,P,N], N);
867              
868 0 0         my $dwSize = 4 + RAS_MaxEntryName + 1 + RAS_MaxPhoneNumber + 1 +
869             RAS_MaxCallbackNumber + 1 + UNLEN + 1 + PWLEN + 1 + DNLEN + 1 +
870             (Win32::IsWinNT && $WINVER >= 0x401 ? 4+4 : 0);
871              
872 0           DWORD_ALIGN($dwSize);
873              
874 0   0       my $RASDIALPARAMS =
      0        
      0        
      0        
      0        
      0        
875             pack "La".(RAS_MaxEntryName + 1)."a".(RAS_MaxPhoneNumber + 1).
876             "a".(RAS_MaxCallbackNumber + 1)."a".(UNLEN + 1).
877             "a".(PWLEN + 1)."a".(DNLEN + 1)
878             ,
879             ($dwSize, $szEntryName||"", $szPhoneNumber||"", $szCallbackNumber||"",
880             $szUserName||"", $szPassword||"", $szDomain||"");
881              
882 0           $RASDIALPARAMS .= "\0"x($dwSize - length $RASDIALPARAMS);
883              
884 0   0       my $ret = $RasSetEntryDialParams->Call($PHONEBOOK||0,
      0        
885             $RASDIALPARAMS, $fRemovePassword||0);
886              
887 0 0         $ret and ($LastError = $ret, return);
888 0           1;
889             }
890              
891             =item RasGetEntryProperties ( )
892              
893             This function retrieves the properties of a phonebook entry.
894              
895             $props = RasGetEntryProperties($entry);
896              
897             $entry - name of RAS/DUN entry
898             $props - pointer to hash
899              
900              
901             The description of the %$props hash is common for this function and
902             C.
903              
904              
905             KEY VALUE
906              
907             name - copy of $entry
908             Flags - numeric flag value, combination of RASEO_* flags.
909             You don't need to use it directly, it's here for
910             information purpose only. In RasSetEntryProperties()
911             it is ignored if present, you should manipulate
912             mnemonic flags as described below, with the
913             'newFlags' key.
914             FlagsReadable - $props->{FlagsReadable} refers to array of
915             "mnemonic flags" that are affecting the behavior
916             of the other properties.
917             Not used by RasSetEntryProperties().
918              
919             Manipulating these flags is described in C section.
920              
921             ipaddr - constant ip-address, ignored unless "SpecificIpAddr"
922             is present in the array of "mnemonic flags"
923             ipaddrDns - primary DNS server
924             ipaddrDnsAlt - secondary(backup) DNS server
925             ipaddrWins - IP address of the primary WINS server
926             ipaddrWinsAlt - secondary WINS server
927              
928             C, C, C, C are
929             ignored unless "SpecificNameServers" is present in the array of "mnemonic flags"
930              
931             All IP-addresses are in xxx.xxx.xxx.xxx decimal form without leading zeros
932             in each part(octet). For example: 195.100.0.28
933              
934             The common rule here is that empty or blank values will produce 0.0.0.0
935             (as well as "0.0.0.0" itself).
936              
937             CountryID -
938             CountryName -
939             CountryCode -
940             AreaCode -
941              
942             (Country ID-Name-Code and AreaCode are described in the
943             C section except that here they are describing
944             the computer you want to dial to.)
945              
946             In C
947             C would be ignored. C not matching C
948             would give error. You could easily give only one of these two values. C
949             would be counted properly if C is given (described in
950             C section). But if you'll give C
951             C would be set equal to C that is sometimes incorrect
952             but does not affect the dialup connection.
953              
954             You can also check the correctness of the C with the
955             C function.
956              
957             LocalPhoneNumber - phone number without country/area parts
958              
959             Script - script file's path.
960             On Win95 this is DialUp Scripting Tool script.
961              
962             Windows NT: To indicate a SWITCH.INF script name, set the first character
963             of the name to "[".
964              
965             C function may have a problem
966             saving the full script path (NT, fixed in the Service Pack 4).
967             http://support.microsoft.com/support/kb/articles/Q160/1/90.asp
968              
969             DeviceType - one of the following string constants
970             (case-insensitive):
971             "modem" A modem accessed through a COM port
972             "isdn" An ISDN card with corresponding NDISWAN driver installed
973             "x25" An X.25 card with corresponding NDISWAN driver installed
974             "x25" type is not implemented in RasSetEntryProperties()
975             in this version of the module
976             "vpn" A Microsoft VPN Adapter
977              
978             You can read a note about VPN and PPTP in the C section.
979              
980             DeviceName - name of a TAPI device to use with this phonebook entry
981              
982             NetProtocols - network protocols to negotiate.
983             $props->{NetProtocols} refers to the array that can
984             contain one or more of the strings
985             (case insensitive in RasSetEntryProperties()):
986             "NetBEUI" NetBIOS End User Interface standard
987             "Ipx" IPX/SPX Compartible
988             "Ip" TCP/IP
989              
990             FramingProtocol - framing protocol used by the server.
991             One of the following strings:
992             "PPP", "Slip", "RAS"
993             (case insensitive in RasSetEntryProperties())
994              
995             B
996              
997             Subentries(multilink dialing) are currently not supported as well as X.25-related
998             parameters. Current version of Win32::RASE also does not allow you to change
999             'DeviceType' and 'DeviceName' elements. This will be added in some future.
1000             Right now any changes in these fields will not affect the
1001             C execution.
1002              
1003             B don't misuse this function, in list context it returns
1004             unreadable things for internal needs.
1005              
1006             Croaks if C<$entry> does not exist.
1007              
1008             For an easy way to change just the phone-number take a look at the
1009             C section.
1010              
1011             =cut
1012              
1013             #================
1014             sub RasGetEntryProperties ($) {
1015             #================
1016 0     0     my $entry = shift;
1017 0           $LastError = 0;
1018              
1019 0 0         IsEntry($entry) or RASCROAK "`$entry' entry not found";
1020              
1021 0   0       $RasGetEntryProperties ||= new("rasapi32", "RasGetEntryProperties", [P,P,P,P,P,P], N);
1022              
1023 0           my ($RASENTRY, $dwSize) = InitializeRASENTRY();
1024              
1025             # first call to find $lpdwDeviceInfoSize
1026 0           my ($lpdwEntryInfoSize, $lpbDeviceInfo, $lpdwDeviceInfoSize) =
1027             # (pack("L",$dwSize), "\0"x1024, pack("L",1024));
1028             (pack("L",$dwSize), 0, DWORD_NULL);
1029              
1030 0   0       my $ret = $RasGetEntryProperties->Call($PHONEBOOK||0, $entry, $RASENTRY,
1031             $lpdwEntryInfoSize, $lpbDeviceInfo, $lpdwDeviceInfoSize);
1032             #print "get_ret1:$ret\n";
1033             # $ret and ($LastError = $ret, return);
1034              
1035 0           my $dwDeviceInfoSize = unpack "L",$lpdwDeviceInfoSize;
1036             #print "\$dwDeviceInfoSize: $dwDeviceInfoSize\n";
1037              
1038 0           $lpbDeviceInfo = "\0"x$dwDeviceInfoSize;
1039              
1040 0   0       $ret = $RasGetEntryProperties->Call($PHONEBOOK||0, $entry, $RASENTRY,
1041             $lpdwEntryInfoSize, $lpbDeviceInfo, $lpdwDeviceInfoSize);
1042              
1043             #print "get_ret2:$ret\n";
1044 0 0         $ret and ($LastError = $ret, return);
1045              
1046             #print "DeviceInfo length:".length($lpbDeviceInfo)."\n";
1047              
1048             #if ($lpdwDeviceInfoSize) {
1049             #print hexizer($lpbDeviceInfo),"\n";
1050             #}
1051             #sub hexizer {
1052             # local $_ = uc unpack "H*", shift;
1053             # s/(..)/$1 /g;
1054             # s/.{48}/$&\n/g; $_;
1055             #}
1056              
1057              
1058 0 0         wantarray ? ($RASENTRY, $lpbDeviceInfo) :
1059             RasBuildEntryProperties($entry, $RASENTRY, $lpbDeviceInfo);
1060             }
1061              
1062             #===========================
1063             sub InitializeRASENTRY () {
1064             #===========================
1065             # creates empty RASENTRY
1066              
1067 0 0   0     my $dwSize = 4*13 + 4*((Win32::IsWinNT && $WINVER >= 0x401) ? 10 : 3) +
1068             (RAS_MaxAreaCode+1) + (RAS_MaxPhoneNumber+1) + 3*MAX_PATH +
1069             (RAS_MaxDeviceType+1) + (RAS_MaxDeviceName+1) +
1070             (RAS_MaxPadType+1) + (RAS_MaxX25Address+1) +
1071             (RAS_MaxFacilities+1) + (RAS_MaxUserData+1);
1072              
1073 0           DWORD_ALIGN($dwSize);
1074 0           my $dwAlternateOffset = $dwSize;
1075              
1076 0           my $RASENTRY = pack "La".($dwSize-4), ($dwSize, "");
1077 0           substr($RASENTRY,
1078             (4*4 + RAS_MaxAreaCode+1+RAS_MaxPhoneNumber+1), 4) =
1079             pack "L", $dwAlternateOffset;
1080              
1081 0           ($RASENTRY, $dwSize);
1082             }
1083             #====================
1084             sub RasBuildEntryProperties ($$$) {
1085             #====================
1086 0     0     my ($entry, $tagRASENTRY, $lpbDeviceInfo) = @_;
1087 0           my (@attr, @attrNP, $attrFP);
1088              
1089             my (
1090 0           $dwSize,
1091             $dwfOptions, # +4
1092              
1093             $dwCountryID, # +8
1094             $dwCountryCode, # +12
1095             $szAreaCode, # +16
1096             $szLocalPhoneNumber,
1097             $dwAlternateOffset,
1098              
1099             $ipaddr,
1100             $ipaddrDns,
1101             $ipaddrDnsAlt,
1102             $ipaddrWins,
1103             $ipaddrWinsAlt,
1104              
1105             $dwFrameSize,
1106             $dwfNetProtocols,
1107             $dwFramingProtocol,
1108             $szScript,
1109             $szAutodialDll,
1110             $szAutodialFunc,
1111             $szDeviceType,
1112             $szDeviceName,
1113             # $szX25PadType,
1114             # $szX25Address,
1115             # $szX25Facilities,
1116             # $szX25UserData,
1117             # $dwChannels,
1118             # $dwReserved1,
1119             # $dwReserved2,
1120             # $dwSubEntries,
1121             # $dwDialMode,
1122             # $dwDialExtraPercent,
1123             # $dwDialExtraSampleSeconds,
1124             # $dwHangUpExtraPercent,
1125             # $dwHangUpExtraSampleSeconds,
1126             # $dwIdleDisconnectSeconds,
1127             ) = unpack "LLLLa".(RAS_MaxAreaCode+1)."a".(RAS_MaxPhoneNumber+1).
1128             "La4a4a4a4a4LLLa".(MAX_PATH)."a".(MAX_PATH)."a".(MAX_PATH).
1129             "a".(RAS_MaxDeviceType+1)."a".(RAS_MaxDeviceName+1)
1130             # ."a".(RAS_MaxPadType+1) ."a".(RAS_MaxX25Address+1).
1131             # "a".(RAS_MaxFacilities+1)."a".(RAS_MaxUserData+1)
1132             # .(($WINVER >= 0x401) ? "LLLLLLLLLL" : "LLL")
1133             , $tagRASENTRY;
1134              
1135              
1136              
1137 0 0         $dwfNetProtocols & RASNP_NetBEUI and push @attrNP, "NetBEUI";
1138 0 0         $dwfNetProtocols & RASNP_Ipx and push @attrNP, "Ipx";
1139 0 0         $dwfNetProtocols & RASNP_Ip and push @attrNP, "Ip";
1140              
1141 0 0         $dwFramingProtocol eq RASFP_Ppp and $attrFP = "PPP";
1142 0 0         $dwFramingProtocol eq RASFP_Slip and $attrFP = "Slip";
1143 0 0         $dwFramingProtocol eq RASFP_Ras and $attrFP = "RAS";
1144              
1145 0           CRUNCH($szAreaCode, $szLocalPhoneNumber, $szScript,
1146             # $szAutodialDll, $szAutodialFunc,
1147             $szDeviceType,$szDeviceName);
1148              
1149 0 0         %TAPIEnumeration = TAPIEnumCountries() if !defined %TAPIEnumeration;
1150              
1151 0 0         my $props = {
1152             name => $entry,
1153             ipaddr => (join '.',map ord, split//,$ipaddr),
1154             ipaddrDns => (join '.',map ord, split//,$ipaddrDns),
1155             ipaddrDnsAlt => (join '.',map ord, split//,$ipaddrDnsAlt),
1156             ipaddrWins => (join '.',map ord, split//,$ipaddrWins),
1157             ipaddrWinsAlt => (join '.',map ord, split//,$ipaddrWinsAlt),
1158             CountryID => $dwCountryID,
1159             CountryName => (exists($TAPIEnumeration{$dwCountryID}) ?
1160             $TAPIEnumeration{$dwCountryID}->[0] : ""),
1161             CountryCode => $dwCountryCode,
1162             AreaCode => $szAreaCode,
1163             LocalPhoneNumber => $szLocalPhoneNumber,
1164             Script => $szScript,
1165             # AutodialDll => $szAutodialDll,
1166             # AutodialFunc => $szAutodialFunc,
1167             DeviceType => $szDeviceType,
1168             DeviceName => $szDeviceName,
1169             Flags => $dwfOptions,
1170             FlagsReadable => [],
1171             NetProtocols => \@attrNP,
1172             FramingProtocol => $attrFP,
1173             };
1174              
1175 0           for my $i(@RASEO_vars) {
1176 0 0         push(@{ $props->{FlagsReadable} }, $i) if $dwfOptions & eval($i);
  0            
1177             }
1178              
1179 0           $props;
1180             }
1181              
1182             =item RasPrintEntryProperties ( )
1183              
1184             This function provides nice printing of a phonebook entry properties.
1185             For debugging, for fun etc.
1186              
1187             RasPrintEntryProperties( $entry );
1188              
1189             $entry - name of RAS/DUN entry
1190              
1191             Croaks if C<$entry> does not exist.
1192              
1193             =cut
1194              
1195             #====================
1196             sub RasPrintEntryProperties ($) {
1197             #====================
1198 0     0     my $entry = shift;
1199 0           $LastError = 0;
1200              
1201 0 0         my $props = RasGetEntryProperties($entry) or return;
1202              
1203 0           print "RAS/DUN entry: $entry\n\n";
1204              
1205 0           for my $p(sort keys %$props) {
1206 0 0         next if $p eq "name";
1207 0 0         if (! ref $props->{$p}) {
1208 0           printf "%18s: %s\n", $p, $props->{$p};
1209             } else {
1210 0 0         printf "%18s: %s\n", $p, @{$props->{$p}} ? $props->{$p}->[0] : "";
  0            
1211 0           map {printf "%18s %s\n", "",$_} @{$props->{$p}}[1..$#{$props->{$p}}];
  0            
  0            
  0            
1212             }
1213             }
1214 0           1;
1215             }
1216              
1217             =item RasGetEntryDevProperties ( )
1218              
1219             This function retrieves the properties of a device used by the phonebook entry
1220             if this entry uses MS Unimodem compartible TSP (Telephone Service Provider) or
1221             in other words - Unimodem compartible driver, on Win95 - always.
1222              
1223              
1224             $props = RasGetEntryDevProperties($entry);
1225              
1226             $entry - name of RAS/DUN entry
1227             $props - pointer to hash
1228              
1229             (Sorry, the description might not be clear enough, just print your
1230             properties with the C and it'd be much easier.)
1231              
1232             The description of the C<%$props> hash is common for this function and
1233             C (not implemented yet).
1234              
1235             It's much likely that only a small part of the described data is
1236             really usefull. Look at the Win32 SDK/MS Platform SDK
1237             (TAPI Prorammer's Reference - "comm/datamodem", "COMMCONFIG", "DCB",
1238             "MODEMSETTINGS" sections) for more info.
1239              
1240              
1241             KEY VALUE
1242              
1243             name - copy of $entry
1244             DeviceName - name of a TAPI device to use with this phonebook entry
1245             DeviceType - described in the RasGetEntryProperties() section
1246              
1247             Options - numeric flag value, combination of the Option flags
1248             that appear on the Unimodem Option page.
1249             This member can be a combination of these values:
1250              
1251             TERMINAL_PRE (1) - Displays the pre-terminal screen.
1252             TERMINAL_POST (2) - Displays the post-terminal screen.
1253             MANUAL_DIAL (4) - Dials the phone manually, if capable of doing so
1254             LAUNCH_LIGHTS (8) - Displays the modem tray icon.
1255              
1256             Only the LAUNCH_LIGHTS value is set by default
1257              
1258              
1259             OptionsReadable - an array ref, a readable representation of those
1260             Options, that are switched on. The array consists of zero or more
1261             strings
1262             "TERMINAL_PRE", "TERMINAL_POST", "MANUAL_DIAL", "LAUNCH_LIGHTS"
1263              
1264             WaitBong - Number of seconds (in two seconds granularity) to
1265             replace the wait for credit tone (default - 10 s)
1266              
1267             CallSetupFailTimer - the maximum number of seconds the modem should
1268             wait, after dialing is completed, for an indication that a
1269             modem-to-modem connection has been established. If a connection
1270             is not established in this interval, the call is assumed to have
1271             failed. This member is equivalent to register S7 in Hayes
1272             compatible modems.
1273              
1274             InactivityTimeout - the maximum number of seconds of inactivity
1275             allowed after a connection is established. If no data is either
1276             transmitted or received for this period of time, the call is
1277             automatically terminated.
1278             This time-out is used to avoid excessive long distance charges
1279             or online service charges if an application unexpectedly locks up
1280             or the user leaves.
1281              
1282             SpeakerVolume - one of the following values: "LOW", "MEDIUM", "HIGH"
1283             Note that actual volumes are hardware-specific.
1284              
1285             SpeakerMode - one of the following values:
1286             "OFF" - The speaker is always off
1287             "CALLSETUP" - The speaker is on until a connection is established
1288             "ON" - The speaker is always on
1289             "DIAL" - The speaker is on until a connection is established,
1290             except that it is off while the modem is actually dialing
1291              
1292             PreferredModemOptions - a numeric flag value. Specifies the modem
1293             options requested by the application. The local and remote modems
1294             negotiate modem options during call setup; this member specifies
1295             the initial negotiating position of the local modem. A combination
1296             of bit flags.
1297              
1298             PreferredModemOptionsReadable - refers to array of strings that
1299             represent bit flags of the previous. Contains zero or more of the
1300             following strings:
1301             "COMPRESSION", "ERROR_CONTROL", "FORCED_EC",
1302             "CELLULAR", "FLOWCONTROL_HARD", "FLOWCONTROL_SOFT",
1303             "CCITT_OVERRIDE", "SPEED_ADJUST",
1304             "TONE_DIAL", "BLIND_DIAL", "V23_OVERRIDE"
1305              
1306             Comments:
1307             CCITT_OVERRIDE - When set, CCITT modulations are enabled for V.21
1308             and V.22 or V.23.When clear, bell modulations
1309             are enabled for 103 and 212A.
1310             V23_OVERRIDE - When set, CCITT modulations are enabled for V.23.
1311             When clear, CCITT modulations are enabled for
1312             V.21 and V.22.
1313              
1314             For V.23 to be set, both CCITT_OVERRIDE and V23_OVERRIDE must be set.
1315              
1316             NegotiatedModemOptions - a numeric flag value. Specifies the modem
1317             options that are actually in effect. This member is filled in
1318             after a connection is established and the local and remote
1319             modems negotiate modem options. This value is read only.
1320             (On my Win95 - always 0).
1321              
1322             NegotiatedModemOptionsReadable - the same ref to array of the readable
1323             strings as PreferredModemOptionsReadable,
1324             but for NegotiatedModemOptions.
1325              
1326             NegotiatedDCERate - Specifies the DCE rate that is in effect.
1327             This member is filled in after a connection is established and
1328             the local and remote modems negotiate modem modulations.
1329             Also read-only.
1330            
1331             DCE - Open Software Foundation (OSF) Distributed Computing Environment.
1332              
1333             The DCB structure defines the control setting for a serial communications device.
1334             The following keys are members of the DCB structure.
1335              
1336             DCB_BaudRate - Specifies the baud rate at which the communications
1337             device operates. This member can be one of the following values:
1338             110, 300, 600, 1200, 2400, 4800, 9600, 14400, 38400,
1339             56000, 57600, 115200, 128000, 256000
1340              
1341             DCB_Flags - numeric flag value, concatenation of many DCB flags.
1342             You don't need to use it directly, it's here for
1343             information purpose only.
1344              
1345             DCB_FlagsReadable - an array ref. The array consists of the 13 string
1346             values. Each string is in the form "flagname:value".
1347             The values are in most cases 0/1. The flags names are:
1348              
1349             fBinary - Specifies whether binary mode is enabled.
1350             The Win32 API does not support nonbinary mode transfers, so this
1351             member should be 1. Trying to use 0 will not work.
1352             Under Windows 3.1, if this member is 0, nonbinary mode is
1353             enabled, and the character specified by the DBC_EofChar member
1354             is recognized on input and remembered as the end of data. (0/1)
1355              
1356             fParity - Specifies whether parity checking is enabled (0/1)
1357              
1358             fOutxCtsFlow - Specifies whether the CTS (clear-to-send) signal
1359             is monitored for output flow control. If this member is 1 and CTS
1360             is turned off, output is suspended until CTS is sent again. (0/1)
1361              
1362             fOutxDsrFlow - Specifies whether the DSR (data-set-ready) signal
1363             is monitored for output flow control. If this member is 1 and DSR
1364             is turned off, output is suspended until DSR is sent again. (0/1)
1365              
1366             fDtrControl - Specifies the DTR (data-terminal-ready)
1367             flow control.
1368             This member can be one of the following values:
1369             0 - Disables the DTR line when the device is opened and leaves it
1370             disabled
1371             1 - Enables the DTR line when the device is opened and leaves it on
1372             2 - Enables DTR handshaking
1373              
1374             fDsrSensitivity - Specifies whether the communications driver is
1375             sensitive to the state of the DSR signal. If this member is 1,
1376             the driver ignores any bytes received, unless the DSR modem input
1377             line is high. (0/1)
1378              
1379             fTXContinueOnXoff - Specifies whether transmission stops when the
1380             input buffer is full and the driver has transmitted the
1381             DCB_XoffChar character.
1382             If this member is 1, transmission continues after the input
1383             buffer has come within DCB_XoffLim bytes of being full and the
1384             driver has transmitted the DCB_XoffChar character to stop
1385             receiving bytes.
1386             If this member is 0, transmission does not continue until the
1387             input buffer is within DCB_XonLim bytes of being empty and the
1388             driver has transmitted the DCB_XonChar character to resume
1389             reception. (0/1)
1390              
1391             fOutX - Specifies whether XON/XOFF flow control is used
1392             during transmission. If this member is 1, transmission stops when
1393             the DCB_XoffChar character is received and starts again when the
1394             DCB_XonChar character is received. (0/1)
1395              
1396             fInX - Specifies whether XON/XOFF flow control is used
1397             during reception. If this member is 1, the DCB_XoffChar character
1398             is sent when the input buffer comes within DCB_XoffLim bytes of
1399             being full, and the DCB_XonChar character is sent when the input
1400             buffer comes within DCB_XonLim bytes of being empty. (0/1)
1401              
1402             fErrorChar - Specifies whether bytes received with parity
1403             errors are replaced with the character specified by the
1404             DCB_ErrorChar member.
1405             If this member is 1 and the fParity member is 1, replacement
1406             occurs. (0/1)
1407              
1408             fNull - pecifies whether null bytes are discarded.
1409             If this member is 1, null bytes are discarded when received.(0/1)
1410              
1411             fRtsControl - Specifies the RTS (request-to-send) flow control.
1412             This member can be one of the following values:
1413             0 - Disables the RTS line when the device is opened and leaves
1414             it disabled.
1415             1 - Enables the RTS line when the device is opened and leaves
1416             it on.
1417             2 - Enables RTS handshaking. The driver raises the RTS line when
1418             the "type-ahead" (input) buffer is less than one-half full
1419             and lowers the RTS line when the buffer is more than
1420             three-quarters full.
1421             3 - Specifies that the RTS line will be high if bytes are
1422             available for transmission. After all buffered bytes have
1423             been sent, the RTS line will be low.
1424              
1425             fAbortOnError - Specifies whether read and write operations are
1426             terminated if an error occurs. If this member is 1, the driver
1427             terminates all read and write operations with an error status if
1428             an error occurs. (0/1)
1429              
1430             DCB_XonLim - Specifies the minimum number of bytes allowed in the
1431             input buffer before the XON character is sent.
1432              
1433             DCB_XoffLim - Specifies the maximum number of bytes allowed in the
1434             input buffer before the XOFF character is sent. The maximum
1435             number of bytes allowed is calculated by subtracting this value
1436             from the size, in bytes, of the input buffer.
1437              
1438             DCB_ByteSize - Specifies the number of bits in the bytes transmitted
1439             and received.
1440              
1441             DCB_Parity - Specifies the parity scheme to be used. This member
1442             can be one of the following values:
1443             "No parity", "Odd", "Even", "Mark", "Space"
1444              
1445             DCB_StopBits - Specifies the number of stop bits to be used.
1446             This member can be one of the following values:
1447             0 - 1 stop bit
1448             1 - 1.5 stop bits
1449             2 - 2 stop bits
1450              
1451             DCB_XonChar - Specifies the value of the XON character for both
1452             transmission and reception.
1453              
1454             DCB_XoffChar - Specifies the value of the XOFF character for both
1455             transmission and reception.
1456              
1457             DCB_ErrorChar - Specifies the value of the character used to replace
1458             bytes received with a parity error.
1459              
1460             DCB_EofChar - Specifies the value of the character used to signal
1461             the end of data.
1462              
1463             DCB_EvtChar - Specifies the value of the character used to signal
1464             an event.
1465              
1466              
1467             Manipulating these flags is described in C section.
1468             (not implemented yet).
1469              
1470             The function croaks if C<$entry> does not exist.
1471              
1472              
1473             =cut
1474              
1475             #==================================
1476             sub RasGetEntryDevProperties ($) {
1477             #==================================
1478 0     0     my $entry = shift;
1479 0           local $_;
1480 0           $LastError = 0;
1481              
1482 0 0         my ($RASENTRY, $lpbDeviceInfo) = RasGetEntryProperties($entry) or return;
1483 0           my $props = RasBuildEntryProperties($entry, $RASENTRY, $lpbDeviceInfo);
1484              
1485 0           my $devOptions = {
1486             name => $entry,
1487             DeviceName => $props->{DeviceName},
1488             DeviceType => $props->{DeviceType},
1489             };
1490 0 0         return unless $lpbDeviceInfo;
1491             # MS Unimodem driver
1492 0           my ($DEVCFGHDR, $COMMCONFIG) =
1493             (substr($lpbDeviceInfo, 0,12), substr($lpbDeviceInfo, 12));
1494              
1495 0           my ($dwSize1,
1496             $dwVersion,
1497             $fwOptions,
1498             $wWaitBong) = unpack "LLSS", $DEVCFGHDR;
1499              
1500 0 0         return unless $dwVersion == 0x10003; # Unimodem
1501             #open O,">out";binmode O;print O $COMMCONFIG;close O;
1502             #exit;
1503              
1504 0           my ($dwSize2,
1505             $wVersion,
1506             $wReserved,
1507             $DCB,
1508             $dwProviderSubType,
1509             $dwProviderOffset,
1510             $dwProviderSize,
1511             ) = unpack "LSS a28 LLL", $COMMCONFIG;
1512              
1513 0 0         return unless $dwProviderSubType == PST_MODEM;
1514              
1515 0           $devOptions->{WaitBong} = $wWaitBong;
1516 0           $devOptions->{Options} = $fwOptions;
1517 0           $devOptions->{OptionsReadable} = [];
1518              
1519 0           for (qw( TERMINAL_PRE TERMINAL_POST MANUAL_DIAL LAUNCH_LIGHTS )) {
1520              
1521 0           (eval "$_") & $fwOptions and
1522 0 0         push @{$devOptions->{OptionsReadable}}, $_;
1523             }
1524              
1525 0           my $MODEMSETTINGS = substr $COMMCONFIG, $dwProviderOffset, $dwProviderSize;
1526              
1527 0           my ( $dwActualSize, # size of returned data, in bytes
1528             $dwRequiredSize, # total size of structure
1529             $dwDevSpecificOffset, # offset of provider-defined data
1530             $dwDevSpecificSize, # size of provider-defined data
1531              
1532             # Static local options (read/write)
1533             $dwCallSetupFailTimer, # call setup timeout, in seconds
1534             $dwInactivityTimeout, # inactivity timeout, in tenths of seconds
1535             $dwSpeakerVolume, # speaker volume level
1536             $dwSpeakerMode, # speaker mode
1537             $dwPreferredModemOptions, # bitmap specifying preferred options
1538              
1539             # negotiated options (read only) for current or last call
1540             $dwNegotiatedModemOptions, # bitmap specifying actual options
1541             $dwNegotiatedDCERate, # DCE rate, in bits per second
1542              
1543             # Variable portion for proprietary expansion
1544             # BYTE abVariablePortion[1]
1545             ) = unpack "LLLLLLLLLLL", $MODEMSETTINGS;
1546              
1547 0           $devOptions->{CallSetupFailTimer} = $dwCallSetupFailTimer;
1548 0           $devOptions->{InactivityTimeout} = $dwInactivityTimeout;
1549 0           $devOptions->{SpeakerVolume} = (qw(LOW MEDIUM HIGH))[$dwSpeakerVolume];
1550 0           $devOptions->{SpeakerMode} = (qw(OFF DIAL ON CALLSETUP))[$dwSpeakerMode];
1551 0           $devOptions->{PreferredModemOptions} = $dwPreferredModemOptions;
1552 0           $devOptions->{PreferredModemOptionsReadable} = [];
1553 0           $devOptions->{NegotiatedModemOptions} = $dwNegotiatedModemOptions;
1554 0           $devOptions->{NegotiatedModemOptionsReadable} = [];
1555 0           $devOptions->{NegotiatedDCERate} = $dwNegotiatedDCERate;
1556              
1557 0           for (qw(COMPRESSION ERROR_CONTROL FORCED_EC
1558             CELLULAR FLOWCONTROL_HARD FLOWCONTROL_SOFT CCITT_OVERRIDE
1559             SPEED_ADJUST TONE_DIAL BLIND_DIAL V23_OVERRIDE)) {
1560              
1561 0           (eval "MDM_$_") & $dwPreferredModemOptions and
1562 0 0         push @{$devOptions->{PreferredModemOptionsReadable}}, $_;
1563              
1564 0           (eval "MDM_$_") & $dwNegotiatedModemOptions and
1565 0 0         push @{$devOptions->{NegotiatedModemOptionsReadable}}, $_;
1566             }
1567              
1568 0           my ( $DCBlength,
1569             $BaudRate, # current baud rate
1570             $Flags,
1571             $wReserved2, # not currently used
1572              
1573             $XonLim, # transmit XON threshold
1574             $XoffLim, # transmit XOFF threshold
1575             $ByteSize, # number of bits/byte, 4-8
1576             $Parity, # 0-4=no,odd,even,mark,space
1577             $StopBits, # 0,1,2 = 1, 1.5, 2
1578             $XonChar, # Tx and Rx XON character
1579             $XoffChar, # Tx and Rx XOFF character
1580             $ErrorChar, # error replacement character
1581              
1582             $EofChar, # end of input character
1583             $EvtChar, # received event character
1584             $wReserved1,
1585             ) = unpack "LLLSSSCCCaaaaaS", $DCB;
1586              
1587 0           my @temp = (
1588             "fBinary:1", # binary mode, no EOF check
1589             "fParity:1", # enable parity checking
1590             "fOutxCtsFlow:1", # CTS output flow control
1591             "fOutxDsrFlow:1", # DSR output flow control
1592             "fDtrControl:2", # DTR flow control type
1593             "fDsrSensitivity:1", # DSR sensitivity
1594              
1595             "fTXContinueOnXoff:1", # XOFF continues Tx
1596             "fOutX:1", # XON/XOFF out flow control
1597             "fInX:1", # XON/XOFF in flow control
1598             "fErrorChar:1", # enable error replacement
1599             "fNull:1", # enable null stripping
1600             "fRtsControl:2", # RTS flow control
1601             "fAbortOnError:1", # abort reads/writes on error
1602             # "fDummy2:17", # reserved
1603             );
1604              
1605 0           my $BFlags = reverse unpack "B32",reverse pack "L",$Flags;
1606             #print "$BFlags\n";
1607 0           my $pos = 0;
1608              
1609 0           for (0..$#temp) {
1610 0           my($k,$v) = $temp[$_] =~ /^(.+):(\d+)$/;
1611 0           my $b = substr($BFlags, $pos, $v); $pos+=$v;
  0            
1612             # $devOptions->{"DCB_$k"} = ord pack "B8", substr("00000000".$b, -8);
1613 0           $temp[$_] = "$k:".ord pack "B8", substr("00000000".$b, -8);
1614              
1615             }
1616              
1617 0           $devOptions->{"DCB_FlagsReadable"} = \@temp;
1618              
1619 0           my $caller = (caller(1))[3];
1620              
1621 0           for (qw(BaudRate Flags XonLim XoffLim ByteSize Parity StopBits
1622             XonChar XoffChar ErrorChar EofChar EvtChar)) {
1623              
1624 0 0 0       $devOptions->{"DCB_$_"} =
1625             /Char$/ && $caller =~ /RasPrintEntryDevProperties/
1626             ? sprintf("0x%2.2X", ord eval "\$$_") : eval "\$$_";
1627             }
1628              
1629 0           $devOptions->{DCB_Parity} =
1630             ("No parity", "Odd", "Even", "Mark", "Space")[$devOptions->{DCB_Parity}];
1631              
1632 0           $devOptions;
1633             }
1634              
1635             =item RasPrintEntryDevProperties ( )
1636              
1637             This function provides nice printing of a phonebook entry device properties
1638             if this entry uses MS Unimodem compartible TSP (Telephone Service Provider) or
1639             in other words - Unimodem compartible driver, on Win95 - always.
1640              
1641             Look at the C section and Win32 SDK
1642             for more info.
1643              
1644             Char values (XonChar, XoffChar, ErrorChar, EofChar, EvtChar) are printed
1645             in hexadecimal form like 0x13.
1646              
1647             For debugging, for fun etc.
1648              
1649             RasPrintEntryDevProperties( $entry );
1650              
1651             $entry - name of RAS/DUN entry
1652              
1653             Croaks if C<$entry> does not exist. Silently returns if the device is not
1654             Unimodem-compartible.
1655              
1656             =cut
1657              
1658             #====================
1659             sub RasPrintEntryDevProperties ($) {
1660             #====================
1661 0     0     my $entry = shift;
1662 0           $LastError = 0;
1663              
1664 0 0         my $props = RasGetEntryDevProperties($entry) or return;
1665              
1666 0           print "RAS/DUN entry: $entry\n\n";
1667              
1668 0           for my $p(sort keys %$props) {
1669 0 0         next if $p eq "name";
1670 0 0         if (! ref $props->{$p}) {
1671 0           printf "%30s: %s\n", $p, $props->{$p};
1672             } else {
1673 0 0         printf "%30s: %s\n", $p, @{$props->{$p}} ? $props->{$p}->[0] : "";
  0            
1674 0           map {printf "%30s %s\n", "",$_} @{$props->{$p}}[1..$#{$props->{$p}}];
  0            
  0            
  0            
1675             }
1676             }
1677 0           1;
1678             }
1679              
1680             =item RasCopyEntry ( )
1681              
1682             This function makes a copy of the existing RAS entry.
1683             Some properties of this newly created entry could then be changed with the use
1684             of C. In previous versions of the
1685             module it was the only way to create a new entry silently, programmatically. But
1686             as of 0.07 we have full featured C.
1687              
1688             You can also create new entry via dialog, see C.
1689              
1690             RasCopyEntry( $oldname, $newname );
1691              
1692             Croaks if C<$oldname> does not exist or C<$newname> already exists.
1693             You should call C or C before to verify both.
1694              
1695             C<$newname> must contain at least one non-white-space alphanumeric character
1696             and cannot begin with a period (".").
1697              
1698             Username, password etc. (see C
1699             and C) are not copied
1700             to the newly created entry.
1701              
1702             =cut
1703              
1704             #======================
1705             sub RasCopyEntry ($$) {
1706             #======================
1707             # NB! country code is not TAPI countryID
1708 0     0     my ($old, $new) = @_;
1709 0           $LastError = 0;
1710              
1711 0 0         IsEntry($old) or RASCROAK "`$old' entry not found";
1712 0 0         IsEntry($new) and RASCROAK "`$new' entry already exists";
1713              
1714 0   0       $RasSetEntryProperties ||= new("rasapi32", "RasSetEntryProperties", [P,P,P,N,P,N], N);
1715              
1716 0 0         my ($tagRASENTRY, $lpbDI) = RasGetEntryProperties($old) or return;
1717              
1718 0   0       my $ret = $RasSetEntryProperties->Call($PHONEBOOK||0, $new, $tagRASENTRY,
1719             length($tagRASENTRY), $lpbDI, length $lpbDI);
1720              
1721 0 0         $ret and ($LastError = $ret, return);
1722 0           1;
1723             }
1724              
1725             =item RasSetEntryProperties ( )
1726              
1727             This function changes the connection information for an existing entry.
1728              
1729             RasSetEntryProperties( $props );
1730              
1731             $props - reference to hash with replacing properties
1732              
1733             Mainly keys/values of the %$props hash are described in the
1734             C
1735             section. But here we can use just part of the full hash - if keys are
1736             undefined no changes will be made to the corresponding properties. Only
1737             $props->{name} has to contain a name of the existing phonebook entry, all other
1738             keys are optional.
1739              
1740             Those properties that do exist in %$props will replace current properties.
1741             If $props->{some-key} is defined and empty ("") the corresponding property
1742             will be empty.
1743              
1744             C, C, C and
1745             C keys are not used by this function. Anyway, all
1746             unneeded keys will be ignored without any errors.
1747              
1748             As of the version 0.07 you B change the RAS device using with
1749             the entry by specifying the new device name in $props->{DeviceName}.
1750             The function finds the device type internally, so $props->{DeviceType}
1751             is ignored if specified.
1752              
1753             If "DeviceName" key is present in the C<%$props>
1754             the function resets device properties for C<$props->{name}> entry to the
1755             default values (for the list of device properties see
1756             C). C function gives the
1757             RAS-capable devices enumeration.
1758              
1759             B: With multiple modems installed under
1760             Windows NT 4.0, the RasSetEntryProperties
1761             API function calls will reset the selected modem to the first available modem.
1762             This problem has been corrected in the latest U.S. Service Pack (4).
1763              
1764              
1765             Print the whole enumeraton like this:
1766              
1767             %devices = RasEnumDevices() or die "Error";
1768             print map "\"$_\" of type \"$devices{$_}\"\n", keys %devices;
1769              
1770             In addition to the keys decribed in the C
1771             section the string value
1772             $props->{newFlags} can be used for adding/removing the existing flags
1773             within the RAS-entry.
1774              
1775             This string has the format: " ..." (any C<\s> separators are possible)
1776              
1777             Each token can be one of the following values (same as mnemonic flags
1778             described in the C section):
1779              
1780             UseCountryAndAreaCodes
1781             SpecificIpAddr
1782             SpecificNameServers
1783             IpHeaderCompression
1784             RemoteDefaultGateway
1785             DisableLcpExtensions
1786             TerminalBeforeDial
1787             TerminalAfterDial
1788             ModemLights
1789             SwCompression
1790             RequireEncryptedPw
1791             RequireMsEncryptedPw
1792             RequireDataEncryption
1793             NetworkLogon
1794             UseLogonCredentials
1795             PromoteAlternates
1796             SecureLocalFiles
1797              
1798             These strings are just the meaningful parts of C constants' names
1799             (from "ras.h" file). They are rather descriptive, you can easily find
1800             their meaning by changing and printing an existing RAS entry. Not
1801             all of them will work in this version of the module.
1802              
1803             Each of these flags could be used with or without the "RASEO_" prefix.
1804             With or without
1805             `+' or `-' prefix (no blanks between [+-] and "mnemonic flag") - this
1806             is the token mentioned above.
1807              
1808             Additional token that can't be prefixed with `+' or `-' is "KeepOldFlags",
1809             it still can be prefixed with "RASEO_".
1810              
1811             If this new flag-string ($props->{newFlags}) is C the default action
1812             is to reset all old flags. "KeepOldFlags" prevents from this cleanup.
1813              
1814             The token with `-' will reset the corresponding flag if it was set, otherwise -
1815             no effect. The token with `+' will set the corresponding flag if it was not
1816             set, otherwise - no effect. The order of tokens is not important, tokens are
1817             separated by any number of blanks. Token without `+' or `-' means `+'.
1818              
1819             Examples:
1820              
1821             C<"NetworkLogon +SwCompression"> - reset old flags and add these two.
1822              
1823             C<"-NetworkLogon -SwCompression KeepOldFlags"> - keep old flags and clean these two.
1824              
1825             The function croaks if C<$entry> does not exist and on some impossible
1826             values of the parameters.
1827              
1828             B (Point to Point Tunneling Protocol):
1829             You can use an ip-address in place of LocalPhoneNumber if your DUN/RAS entry
1830             is configured to work with VPN (Virtual Private Networking) via PPTP.
1831             PPTP appears as a new modem type that can be selected in DUN entry only manually.
1832             It DeviceName is "Microsoft VPN Adapter" and DeviceType is "vpn".
1833             In this case you can change the ip-address of the
1834             VPN-host as if it were local phone number. For example
1835              
1836             RasSetEntryProperties({
1837             name=>"NEV5",
1838             LocalPhoneNumber=>"21.100.14.12",
1839             });
1840              
1841             You can get info about VPN and PPTP at
1842              
1843             http://support.microsoft.com/support/kb/articles/q154/0/91.asp
1844              
1845             DUN 1.3 that supports VPN is downloadable from
1846              
1847             http://support.microsoft.com/download/support/mslfiles/MSDUN13.EXE
1848              
1849             and is described here
1850              
1851             http://support.microsoft.com/support/kb/articles/q194/4/77.asp
1852              
1853              
1854             Thanks to Carl Sewell C<<>csewell@hiwaay.netC<>> for his explanations
1855             and testing of VPN features.
1856              
1857             B
1858             After applying Service Pack 2, the RasSetEntryProperties flags for
1859             RASEO_TerminalAfterDial and RASEO_TerminalBeforeDial specified in
1860             the Win32 function call are not set. This problem occurs because
1861             Service Pack 2 causes the parameters to be ignored.
1862             This problem has been corrected in Service Pack 3.
1863              
1864             http://support.microsoft.com/support/ntserver/serviceware/nts40/E9MSL2CSA.ASP
1865              
1866             B When using the RasSetEntryProperties API call to change the connection
1867             information for an entry in the phone book or create a new phone-book entry,
1868             the szScript (C<$props->{Script}> in C) parameter of the RASENTRY
1869             structure is not always preserved.
1870              
1871             http://support.microsoft.com/support/kb/articles/q160/1/90.asp
1872              
1873             This problem applies to WinNT 4.0 and was corrected in the latest
1874             Microsoft Windows NT 4.0 U.S. Service Pack (4).
1875              
1876             The function croaks if the specfied device is not found.
1877              
1878             =cut
1879              
1880             #======================
1881             sub RasSetEntryProperties ($) {
1882             #======================
1883 0     0     my $props = shift;
1884 0           $LastError = 0;
1885              
1886 0 0         ref($props) eq "HASH" or RASCROAK "argument is not a hash-reference";
1887              
1888 0 0         $props->{name} or RASCROAK "\$props->{name} hash key does not exist";
1889              
1890 0 0         IsEntry($props->{name}) or
1891             RASCROAK "\$props->{name}==`$props->{name}' is not an existing entry";
1892              
1893 0 0         my ($RASENTRY, $lpbDeviceInfo) =
1894             RasGetEntryProperties($props->{name}) or return;
1895              
1896             # if ($props->{DeviceName}) {
1897             # my $COMMCONFIG = GetDefaultCommConfig($props->{DeviceName}) or return;
1898             #
1899             # my $dwDeviceInfoSize = 12 + length $COMMCONFIG;
1900             # my $DEVCFGHDR = pack "LLSS", $dwDeviceInfoSize, 0x00010003, 8, 10;
1901             # $lpbDeviceInfo = $DEVCFGHDR.$COMMCONFIG;
1902             # }
1903              
1904 0           $RASENTRY = ParseRASENTRY($props, $RASENTRY);
1905              
1906 0   0       $RasSetEntryProperties ||= new("rasapi32", "RasSetEntryProperties", [P,P,P,N,P,N], N);
1907              
1908 0           my $ret;
1909              
1910 0 0         unless ($props->{DeviceName}) {
1911 0   0       $ret = $RasSetEntryProperties->Call($PHONEBOOK||0,
1912             $props->{name}, $RASENTRY, length($RASENTRY),
1913             $lpbDeviceInfo, length $lpbDeviceInfo);
1914              
1915             #print "ret1:$ret\n";
1916             } else {
1917 0   0       $ret = $RasSetEntryProperties->Call($PHONEBOOK||0,
1918             $props->{name}, $RASENTRY, length($RASENTRY),0,0);
1919             #print "ret2:$ret\n";
1920              
1921 0 0         my ($RASENTRY1, $lpbDeviceInfo1) =
1922             RasGetEntryProperties($props->{name}) or return;
1923             #print "New lpbDeviceInfo size:".length($lpbDeviceInfo1)."\n";
1924              
1925 0   0       $ret = $RasSetEntryProperties->Call($PHONEBOOK||0,
1926             $props->{name}, $RASENTRY, length($RASENTRY),
1927             $lpbDeviceInfo1, length $lpbDeviceInfo1);
1928             #print "ret3:$ret\n";
1929             }
1930              
1931 0 0         $ret and ($LastError = $ret, return);
1932 0           1;
1933             }
1934              
1935             =item RasCreateEntry ( )
1936              
1937             This function creates RAS/DUN entry programmatically (note that
1938             C displays dialo boxes).
1939              
1940             RasCreateEntry( $props );
1941              
1942             C defines the phonebook in which the new entry will
1943             be created (WinNT).
1944              
1945             For the explanation of the C<%$props> hash see the previous C
1946             function. The main difference is that these keys
1947              
1948             name, LocalPhoneNumber, NetProtocols, FramingProtocol, DeviceName
1949              
1950             are mandatory in this hash.
1951              
1952             You have to specify at least one of CountryID and CountryCode keys and AreaCode
1953             key if C<$props->{newFlags}> contains "+UseCountryAndAreaCodes".
1954              
1955             All ip-addresses if omitted are assumed to be "0.0.0.0". Empty or non-existing
1956             C<$props->{newFlags}> gives zero numeric flag which means that none of the
1957             C options are in use. Flag "KeepOldFlags" has no meaning but makes
1958             no error.
1959              
1960             Note that the device settings would be copied from your system defaults and
1961             some minor features still could not be customized (see C).
1962              
1963             =cut
1964              
1965             #======================
1966             sub RasCreateEntry ($) {
1967             #======================
1968 0     0     my $props = shift;
1969 0           local $_;
1970 0           $LastError = 0;
1971              
1972 0 0         ref($props) eq "HASH" or RASCROAK "argument is not a hash-reference";
1973              
1974 0 0         $props->{name} or RASCROAK "\$props->{name} hash key does not exist";
1975              
1976 0 0         IsEntry($props->{name}) and
1977             RASCROAK "\$props->{name}==`$props->{name}' entry already exists";
1978              
1979 0           my @mandatory = qw(name LocalPhoneNumber NetProtocols FramingProtocol DeviceName);
1980              
1981 0           for (@mandatory) {
1982 0 0         exists $props->{$_} or
1983             RASCROAK "\$props->{$_} mandatory key does not exist";
1984 0 0         $props->{$_} or
1985             RASCROAK "\$props->{$_} is empty";
1986             }
1987              
1988 0           my $RASENTRY = ParseRASENTRY($props);
1989             # my $COMMCONFIG = GetDefaultCommConfig($props->{DeviceName}) or return;
1990             #
1991             # my $dwDeviceInfoSize = 12 + length $COMMCONFIG;
1992             # my $DEVCFGHDR = pack "LLSS", $dwDeviceInfoSize, 0x00010003, 8, 10;
1993             # my $lpbDeviceInfo = $DEVCFGHDR.$COMMCONFIG;
1994              
1995 0   0       $RasSetEntryProperties ||= new("rasapi32", "RasSetEntryProperties", [P,P,P,N,P,N], N);
1996              
1997 0   0       my $ret = $RasSetEntryProperties->Call($PHONEBOOK||0,
1998             $props->{name}, $RASENTRY, length($RASENTRY),0,0);
1999              
2000             #print "ret1:$ret\n";
2001              
2002 0           my($RASENTRY1, $lpbDeviceInfo) = RasGetEntryProperties($props->{name});
2003              
2004             #print "lpbDeviceInfo size:".length($lpbDeviceInfo)."\n";
2005              
2006 0   0       $ret = $RasSetEntryProperties->Call($PHONEBOOK||0,
2007             $props->{name}, $RASENTRY, length($RASENTRY),
2008             $lpbDeviceInfo, length $lpbDeviceInfo);
2009              
2010             #print "ret2:$ret\n";
2011              
2012              
2013 0 0         $ret and ($LastError = $ret, return);
2014 0           1;
2015             }
2016              
2017              
2018             #======================
2019             sub ParseRASENTRY ($;$) {
2020             #======================
2021 0     0     my ($props, $RASENTRY) = @_;
2022 0           my ($NP, $FP, $newFlags);
2023 0           my $pat = HOSTNUMBER();
2024 0           local $_;
2025              
2026              
2027 0           my ($entry, $Flags, $CountryID, $CountryCode, $AreaCode, $LocalPhoneNumber,
2028             $NetProtocols, $FramingProtocol, $Script, $DeviceName) =
2029             map $props->{$_}, qw(
2030             name newFlags CountryID CountryCode AreaCode LocalPhoneNumber
2031             NetProtocols FramingProtocol Script DeviceName
2032             );
2033              
2034              
2035 0 0         ($RASENTRY) = InitializeRASENTRY() unless $RASENTRY;
2036              
2037             my (
2038 0           $dwSize,
2039             $dwfOptions,
2040              
2041             $dwCountryID,
2042             $dwCountryCode,
2043             $szAreaCode,
2044             $szLocalPhoneNumber,
2045             $dwAlternateOffset,
2046              
2047             $ipaddr,
2048             $ipaddrDns,
2049             $ipaddrDnsAlt,
2050             $ipaddrWins,
2051             $ipaddrWinsAlt,
2052              
2053             $dwFrameSize,
2054             $dwfNetProtocols,
2055             $dwFramingProtocol,
2056             $szScript,
2057              
2058             $szAutodialDll,
2059             $szAutodialFunc,
2060              
2061             $szDeviceType,
2062             $szDeviceName,
2063             ) = unpack "LLLLa".(RAS_MaxAreaCode+1)."a".(RAS_MaxPhoneNumber+1).
2064             "La4a4a4a4a4LLL".(("a".MAX_PATH) x 3).
2065             "a".(RAS_MaxDeviceType + 1)."a".(RAS_MaxDeviceName + 1), $RASENTRY;
2066              
2067              
2068 0 0         if (defined $DeviceName) {
2069 0           TRIM_LR($DeviceName);
2070 0           CRUNCH($szDeviceName);
2071              
2072 0 0         if ($DeviceName ne $szDeviceName) {
2073 0 0         %RasDevEnumeration = RasEnumDevices() unless defined %RasDevEnumeration;
2074 0 0         exists $RasDevEnumeration{$DeviceName} or
2075             RASCROAK "device `$DeviceName' not found or non RAS-capable";
2076              
2077 0           $szDeviceName = $DeviceName;
2078 0           $szDeviceType = $RasDevEnumeration{$DeviceName};
2079             }
2080             }
2081              
2082 0 0         if (defined $Script) {
2083 0           TRIM_LR($Script);
2084 0 0 0       RASCROAK "script `$Script' not found/empty"
      0        
2085             unless $Script eq "" || (-f $Script && -s_);
2086 0           $szScript = $Script;
2087             }
2088              
2089 0 0         if (defined $AreaCode) {
2090 0           TRIM_LR($AreaCode);
2091 0 0         RASCROAK "wrong area code `$AreaCode'"
2092             unless $AreaCode =~ /^\d*$/;
2093 0           $szAreaCode = $AreaCode;
2094             }
2095              
2096 0 0         if (defined $LocalPhoneNumber) {
2097 0           TRIM_LR($LocalPhoneNumber);
2098              
2099 0 0         RASCROAK "wrong local phone number `$LocalPhoneNumber'"
2100             unless $LocalPhoneNumber =~ /^[\d\-.]*$/;
2101             # dot '.' added for ip-address (DUN 1.3 - VPN via PPTP) or French style
2102              
2103 0           $szLocalPhoneNumber = $LocalPhoneNumber;
2104             }
2105              
2106 0 0         if (defined $CountryID) {
    0          
2107 0 0         %TAPIEnumeration = TAPIEnumCountries() if !defined %TAPIEnumeration;
2108              
2109 0           TRIM_LR($CountryID);
2110              
2111 0 0         RASCROAK "wrong CountryID `$CountryID'"
2112             unless $CountryID =~ /^\d*$/;
2113              
2114 0 0         RASCROAK "CountryID not found `$CountryID'"
2115             unless exists $TAPIEnumeration{$CountryID};
2116              
2117 0           $dwCountryID = $CountryID;
2118              
2119 0 0         if (defined $CountryCode) {
2120 0 0         RASCROAK "CountryID `$CountryID'".
2121             " does not match CountryCode `$CountryCode'"
2122             unless $CountryCode == $TAPIEnumeration{$CountryID}->[1];
2123             }
2124              
2125 0           $dwCountryCode = $TAPIEnumeration{$CountryID}->[1];
2126              
2127             } elsif (defined $CountryCode) {
2128 0 0         %TAPIEnumeration = TAPIEnumCountries() if !defined %TAPIEnumeration;
2129              
2130 0           TRIM_LR($CountryCode);
2131              
2132 0 0         RASCROAK "wrong CountryCode `$CountryCode'" unless $CountryCode =~ /^\d*$/;
2133              
2134 0 0         grep {$TAPIEnumeration{$_}->[1] == $CountryCode} keys %TAPIEnumeration or
  0            
2135             RASCROAK "CountryCode not found `$CountryCode'";
2136              
2137 0           $dwCountryCode = $dwCountryID = $CountryCode;
2138             }
2139              
2140 0           for (qw(ipaddrDns ipaddrDnsAlt ipaddrWins ipaddrWinsAlt ipaddr)) {
2141 0 0         if (defined $props->{$_}) {
2142 0           my $var = $props->{$_};
2143              
2144 0           TRIM_LR($var);
2145              
2146 0 0         if (!$var) {
2147 0           eval "\$$_ = DWORD_NULL";
2148             } else {
2149 0 0         RASCROAK "wrong $_ `$var'" unless $var =~ /^$pat$/;
2150 0           eval "\$$_ = pack 'C4', split/\\./, \$var";
2151             }
2152             }
2153             }
2154              
2155 0 0         if (defined $FramingProtocol) {
2156 0           ($FP = $FramingProtocol) =~ s/^ *(.*?) *$/uc $1/es;
  0            
2157 0 0         RASCROAK "wrong framing protocol `$FramingProtocol'"
2158             unless $FP =~ /^(PPP|SLIP|RAS)$/;
2159 0 0         $dwFramingProtocol = RASFP_Ppp if $FP eq 'PPP';
2160 0 0         $dwFramingProtocol = RASFP_Slip if $FP eq 'SLIP';
2161 0 0         $dwFramingProtocol = RASFP_Ras if $FP eq 'RAS';
2162             }
2163              
2164 0 0         if (defined $NetProtocols) {
2165 0 0         RASCROAK "\$props->{$NetProtocols} is not an array ref"
2166             unless ref $NetProtocols eq "ARRAY";
2167              
2168 0           ($NP = join "|", @$NetProtocols) =~ s/^ *(.*?) *$/uc $1/es;
  0            
2169 0 0         RASCROAK "wrong net protocols `$NetProtocols'"
2170             unless $NP =~ /^(NETBEUI|IPX|IP)(\|(NETBEUI|IPX|IP))*$/;
2171 0           $dwfNetProtocols = 0;
2172 0 0         $dwfNetProtocols |= RASNP_NetBEUI if $NP =~ /NETBEUI/;
2173 0 0         $dwfNetProtocols |= RASNP_Ipx if $NP =~ /IPX/;
2174 0 0         $dwfNetProtocols |= RASNP_Ip if $NP =~ /IP(\||$)/;
2175             }
2176              
2177             # flags logic
2178 0 0         if (defined $Flags) {
2179 0 0         $newFlags = ($Flags =~ s/\+?(RASEO_)?KeepOldFlags//) ? $dwfOptions : 0;
2180 0 0         $newFlags = 0 if $Flags =~ s/\-(RASEO_)?KeepOldFlags//;
2181              
2182              
2183 0           for(split/\s*\+|\s+/,$Flags) {
2184 0 0         next unless $_;
2185              
2186 0 0 0       if (defined(&$_)) {
  0 0 0        
    0          
    0          
2187 0           $newFlags |= &$_;
2188             } elsif (defined &{"RASEO_$_"}) {
2189 0           $newFlags |= &{"RASEO_$_"};
  0            
2190 0           } elsif (/^-(.+)$/ && defined &$1) {
2191 0           $newFlags = $newFlags ^ ($newFlags & &$1);
2192             } elsif (/^-(.+)$/ && defined &{"RASEO_$1"}) {
2193 0           $newFlags = $newFlags ^ ($newFlags & &{"RASEO_$1"});
  0            
2194             } else {
2195 0           RASCROAK "wrong flag specified `$_'";
2196             }
2197             }
2198             } else {
2199 0           $newFlags = $dwfOptions;
2200             }
2201              
2202             #print "$newFlags, $dwCountryID, $dwCountryCode, $szAreaCode, $szLocalPhoneNumber,
2203             #$ipaddr, $ipaddrDns, $ipaddrDnsAlt, $ipaddrWins, $ipaddrWinsAlt,
2204             #$dwFrameSize, $dwfNetProtocols, $dwFramingProtocol, $szScript\n";#exit;
2205              
2206             # pack new header
2207 0           my $newHead = pack "LLLLa".(RAS_MaxAreaCode+1).
2208             "a".(RAS_MaxPhoneNumber+1)."La4a4a4a4a4LLL".(("a".MAX_PATH) x 3).
2209             "a".(RAS_MaxDeviceType + 1)."a".(RAS_MaxDeviceName + 1), (
2210             $dwSize,
2211             $newFlags, # +4
2212              
2213             $dwCountryID, # +8
2214             $dwCountryCode, # +12
2215             $szAreaCode, # +16
2216             $szLocalPhoneNumber,
2217             $dwAlternateOffset,
2218              
2219             $ipaddr,
2220             $ipaddrDns,
2221             $ipaddrDnsAlt,
2222             $ipaddrWins,
2223             $ipaddrWinsAlt,
2224              
2225             $dwFrameSize,
2226             $dwfNetProtocols,
2227             $dwFramingProtocol,
2228             $szScript,
2229              
2230             $szAutodialDll,
2231             $szAutodialFunc,
2232              
2233             $szDeviceType,
2234             $szDeviceName);
2235              
2236 0           substr($RASENTRY, 0, length $newHead) = $newHead;
2237 0           $RASENTRY;
2238             }
2239              
2240             =item RasChangePhoneNumber ( )
2241              
2242             This is a simplified version of the C.
2243              
2244             RasChangePhoneNumber($entry, $new_phone_number);
2245              
2246             $entry - name of RAS/DUN entry
2247             $new_phone_number - fully qualified phone number of the remote
2248             computer in almost any human-readable form.
2249              
2250             For example:
2251              
2252             '7-095-5555555' or '7(095)5555555' or '7 -( 095)-555-5555'
2253             or '+7 (095) - 5-5-5-5-5-5-5' or '7 095 5555555'
2254              
2255             It is smart enough to adjust entry flags to avoid long distance dialing if
2256             country and area codes are the same as in Dialing Properties/Default Location.
2257             All other flags are kept unchanged.
2258              
2259             B country code here is not TAPI C.
2260              
2261             =cut
2262              
2263             #=======================
2264             sub RasChangePhoneNumber ($$) {
2265             #=======================
2266             # full country-code-area-code-local in the form
2267 0     0     my ($entry, $phone) = @_;
2268 0           $LastError = 0;
2269              
2270 0 0 0       TAPIlineGetTranslateCaps()
      0        
2271             unless defined($LOCAL_ID) && defined($LOCAL_CODE) && defined($LOCAL_AREA);
2272              
2273 0           my $props = {};
2274 0           $props->{name} = $entry;
2275              
2276 0 0         ($props->{CountryCode}, $props->{AreaCode}, $props->{LocalPhoneNumber}) =
2277             $phone =~
2278             /(\d+)(?:[+\- ]*\( *|[+\- ]+)(\d+)(?: *\)[+\- ]*|[+\- ]+)(\d[\d\-]+\d)/ or
2279             RASCROAK "wrong number `$phone'";
2280              
2281 0 0 0       if ($props->{AreaCode} eq $LOCAL_AREA && $props->{CountryCode} eq $LOCAL_CODE) {
2282 0           $props->{newFlags} = 'KeepOldFlags -UseCountryAndAreaCodes';
2283             } else {
2284 0           $props->{newFlags} = 'KeepOldFlags +UseCountryAndAreaCodes';
2285             }
2286              
2287 0 0         my $ret = RasSetEntryProperties($props) or return;
2288 0           1;
2289             }
2290              
2291              
2292             =pod
2293              
2294             =back
2295              
2296             B< =====================================>
2297              
2298             B< CONNECTION RELATED FUNCTIONS>
2299              
2300             B< =====================================>
2301              
2302              
2303             =over 4
2304              
2305             =item RasEnumConnections ( )
2306              
2307             %connections = RasEnumConnections ( ); or as list
2308              
2309             ($entry1, $hrasconn1, ...) = RasEnumConnections ( );
2310              
2311             Returns handles for each active RAS/DUN connection. C<$entry> is entry-name.
2312             C<$hrasconn> is a numeric handle that might be used in C to
2313             hang up the active connection or in C or in
2314             C.
2315              
2316             Croaks on errors. Returns FALSE if no one active connection was found.
2317              
2318             Note that C also returns $hrasconn on success.
2319              
2320             =cut
2321              
2322             #================
2323             sub RasEnumConnections () {
2324             #================
2325 0     0     my ($dwSize, $hrasconn, $szEntryName, $szDeviceType, $szDeviceName);
2326 0           $LastError = 0;
2327              
2328 0   0       $RasEnumConnections ||= new("rasapi32", "RasEnumConnections", [P,P,P], N);
2329              
2330 0 0         $dwSize = 4+4+(RAS_MaxEntryName+1)+
2331             ($WINVER >= 0x400 ? RAS_MaxDeviceType+1 + RAS_MaxDeviceName+1 : 0);
2332              
2333 0           DWORD_ALIGN($dwSize);
2334              
2335 0           my $RASCONN = pack "LLa".($dwSize-8), ($dwSize, 0, "");
2336              
2337 0           my ($lpcb, $lpcConnections) =
2338             (pack ("L", length $RASCONN), DWORD_NULL);
2339              
2340 0           my $ret = $RasEnumConnections->Call($RASCONN, $lpcb, $lpcConnections);
2341              
2342 0           my $cb = unpack "L",$lpcb;
2343              
2344 0 0         if ($ret) {
2345 0           $RASCONN = pack "LLa".($cb-8), ($dwSize, 0, "");
2346 0           $ret = $RasEnumConnections->Call($RASCONN, $lpcb, $lpcConnections);
2347             }
2348              
2349 0 0         $ret and RASERROR($ret);
2350              
2351 0           my $conns = unpack "L",$lpcConnections;
2352              
2353 0           my %connects;
2354              
2355 0           for my $i(1..$conns) {
2356 0           my $buffer = substr $RASCONN, $dwSize*($i-1), $dwSize;
2357 0           ($dwSize, $hrasconn, $szEntryName) =
2358             unpack "LL". "a".($dwSize-8), $buffer;
2359 0           CRUNCH($szEntryName);
2360 0           $connects{$szEntryName} = $hrasconn;
2361             }
2362 0           %connects;
2363             }
2364              
2365             =item RasGetProjectionInfo ( )
2366              
2367             In the current version projection info is implemented for IP protocol only.
2368             This is a subject to change.
2369              
2370             ($ip, $server_ip) = RasGetProjectionInfo ( $hrasconn );
2371              
2372             $hrasconn - handle of the active connection returned by either
2373             RasDial() or RasEnumConnections().
2374             $ip - the client's IP address on the RAS connection
2375             $server_ip - the IP address of the remote PPP peer (that is, the
2376             server's IP address)
2377              
2378             Both IP addrs are in "nnn.nnn.nnn.nnn" form.
2379              
2380              
2381             B
2382              
2383             Remote access projection is the process whereby a remote access server
2384             and a remote client negotiate network protocol-specific information.
2385             A remote access server uses this network protocol-specific information
2386             to represent a remote client on the network.
2387              
2388             B Remote access projection information is not available until
2389             the operating system has executed the C C state on the
2390             remote access connection. If C is called prior to the
2391             C state, it returns C.
2392              
2393             B Windows 95 Dial-Up Networking does not support the
2394             C state. The projection phase may be done during the
2395             C state. If the authentication is successful, the connection
2396             operation proceeds to the C state, and projection information
2397             is available for successfully configured protocols. If C
2398             is called prior to the C state, it returns
2399             C.
2400              
2401             PPP does not require that servers provide this address, but Windows NT
2402             servers will consistently return the address anyway. Other PPP vendors
2403             may not provide the address. If the address is not available, this member
2404             returns an empty string ("").
2405              
2406             I guess the last note is probably outdated because my Advanced Dialer
2407             has a field for "Server's IP address" - so, it expects that it's always available.
2408              
2409             If you are using C in a single process application you can't
2410             monitor C states (for more info look at C).
2411             So, the rule is: use this function after C successfully
2412             returned C<$hrasconn>.
2413              
2414             The typical usage if you have only one connection is:
2415              
2416             unless ( $hrasconn = (RasEnumConnections())[1] ) {
2417             print "Dialing sequence not started\n";
2418              
2419             } elsif ( ($ip, $server_ip) = RasGetProjectionInfo( $hrasconn ) ) {
2420             print "LOCAL:$ip SERVER:$server_ip\n";
2421              
2422             } elsif ( Win32::RASE::GetLastError == 731 ) {
2423             print "Protocol not configured yet\n";
2424              
2425             } else {
2426             die Win32::RASE::FormatMessage();
2427             }
2428              
2429             Note also that LastError=6 means that C<$hrasconn> is an invalid handle.
2430              
2431             Command line syntax:
2432              
2433             perl -MWin32::RASE -e "$,=', ';print RasGetProjectionInfo((RasEnumConnections)[1])"
2434              
2435             =cut
2436              
2437             #================
2438             sub RasGetProjectionInfo ($) {
2439             #================
2440 0     0     my $hrasconn = shift;
2441 0           my ($RASPPPIP, $dwSize, $lpcb, $dwError, $ip, $server_ip, $ret);
2442 0           my $rasprojection = RASP_PppIp;
2443 0           $LastError = 0;
2444              
2445 0   0       $RasGetProjectionInfo ||= new("rasapi32", "RasGetProjectionInfo",[N,N,P,P],N);
2446              
2447 0 0         if ($rasprojection == RASP_PppIp) {
2448 0           $dwSize = 4+4+RAS_MaxIpAddress+1+RAS_MaxIpAddress+1;
2449              
2450 0           DWORD_ALIGN($dwSize);
2451              
2452 0           $RASPPPIP = pack "La".($dwSize-4), $dwSize, "";
2453 0           $lpcb = pack "L", $dwSize;
2454              
2455 0 0         $ret = $RasGetProjectionInfo->Call(
2456             $hrasconn, $rasprojection, $RASPPPIP, $lpcb)
2457             and ($LastError = $ret, return);
2458              
2459 0           ($dwSize, $dwError, $ip, $server_ip) =
2460             unpack "LL"."a".(RAS_MaxIpAddress+1)."a".(RAS_MaxIpAddress+1), $RASPPPIP;
2461 0           CRUNCH($ip, $server_ip);
2462              
2463 0 0         $dwError and ($LastError = $dwError, return);
2464              
2465 0           return ($ip, $server_ip);
2466             }
2467              
2468             }
2469              
2470             =item RasHangUp ( )
2471              
2472             RasHangUp($hrasconn [, $timeout]);
2473              
2474             $hrasconn - handle of the active connection returned by either
2475             RasDial() or RasEnumConnections().
2476              
2477             $timeout - in sec, optional (3 sec by default). Maximum time to wait
2478             for graceful disconnection. You can use float values if
2479             Time::HiRes is installed. Otherwise cycle uses sleep(1)
2480             and thus wastes some additional time.
2481              
2482             This function gracefully terminates the connection. You don't need to add any
2483             C after it.
2484              
2485             The connection is terminated even if the C call has not yet been completed.
2486              
2487             After this call, the $hrasconn handle can no longer be used.
2488              
2489             Returns FALSE if invalid handle was given but this is harmless
2490             most of the time. Probably the connection failed itself and C<$hrasconn>
2491             is not valid any more. So, you don't have to trap this error.
2492              
2493             Returns FALSE on timeout also (connection might be still active). LastError
2494             is 0 in this case. So the exact logic is:
2495              
2496             if ( RasHangUp($hrasconn, $timeout) ) {
2497             print "Connection is terminated successfully.\n";
2498              
2499             } elsif ( !Win32::RASE::GetLastError ) {
2500             print "Timeout. Connection is still active.\n";
2501              
2502             } else {
2503             # we don't have to die here
2504             warn Win32::RASE::FormatMessage(), "\n";
2505             }
2506              
2507             For more take a look at the API docs.
2508              
2509             =cut
2510              
2511             #================
2512             sub RasHangUp ($;$) {
2513             #================
2514             # returns 0 on success or error-code
2515 0     0     my ($hrasconn, $timeout) = @_;
2516 0           $LastError = 0;
2517 0 0 0       ($LastError = 6, return) unless $hrasconn && $hrasconn !~ /\D/;
2518              
2519 0   0       $RasHangUp ||= new("rasapi32", "RasHangUp", [N], N);
2520              
2521 0   0       $timeout ||= 3;
2522              
2523 0 0         my ($delay) = $Time_HiRes_loaded ? 0.1 : 1;
2524              
2525 0           my $ret = $RasHangUp->Call($hrasconn);
2526              
2527 0 0         $ret and ($LastError = $ret, return);
2528              
2529 0           my $starttime = time;
2530              
2531 0           while ($starttime + $timeout >= time) {
2532 0 0         RasGetConnectStatus($hrasconn) or ($LastError = 0, return 1);
2533              
2534 0           sleep $delay;
2535             }
2536              
2537 0           return;
2538             }
2539              
2540             =item HangUp ( )
2541              
2542             This is the easier version of previous.
2543              
2544             Without parameters it will terminate all active connections, otherwise
2545             terminates connections by B given as parameters. Note that
2546             this function uses entry-names, not handles.
2547              
2548             $code = HangUp ( [$entry1, ...] );
2549              
2550             Returns FALSE if at least one connection was not terminated gracefully,
2551             otherwise TRUE even if no one active connecton was found.
2552              
2553             Command line syntax:
2554              
2555             perl -MWin32::RASE -e HangUp
2556              
2557              
2558             =cut
2559              
2560             #================
2561             sub HangUp (;@) {
2562             #================
2563 0     0     $LastError = 0;
2564 0 0         my %conns = RasEnumConnections() or return 1;
2565 0           my @entries = @_;
2566 0           my $ret = 1;
2567 0           local $_;
2568              
2569 0 0         @entries = keys %conns unless @entries;
2570              
2571 0           for (@entries) {
2572 0 0         next unless exists $conns{$_};
2573              
2574 0 0         RasHangUp($conns{$_}) or $ret = 0;
2575             }
2576 0           $ret;
2577             }
2578              
2579             =item RasGetConnectStatus ( )
2580              
2581             This function is used to monitor active connection in progress. In most
2582             cases it's good to cycle calls to this function after a very small interval,
2583             say 0.1 sec or less - at least at the dialing time. It's possible in
2584             multithreading process (thread safety is not verified in this version)
2585             or one process can monitor another, which is closer to perl practice.
2586              
2587             $status = RasGetConnectStatus($hrasconn);
2588              
2589             or
2590              
2591             ($status, $status_text) = RasGetConnectStatus($hrasconn);
2592              
2593             $hrasconn - handle to active RAS/DUN connection
2594              
2595             In scalar context returns numeric status (RASCS_* enumerator values) or
2596             FALSE if C<$hrasconn> is not a valid handle (LastError is set to 6).
2597              
2598             In list context returns numeric status and the string that characterizes
2599             this status in short (the descriptive part of the corresponding RASCS_ constant's
2600             name, like "OpenPort") or FALSE if handle is invalid.
2601              
2602             FALSE is also returned if handle is "not valid any more", i.e. connection
2603             is terminated.
2604              
2605             These string constants ("PortOpened" etc.) are stored in a non-exported hash
2606             B<%Win32::RASE::RASCS> where the keys are numeric values of the corresponding RASCS_*
2607             constants. So
2608              
2609             $Win32::RASE::RASCS{1} eq "PortOpened"
2610              
2611             You can check status yourself against exported RASCS_* constants:
2612              
2613             RASCS_OpenPort
2614             RASCS_PortOpened
2615             RASCS_ConnectDevice
2616             RASCS_DeviceConnected
2617             RASCS_AllDevicesConnected
2618             RASCS_Authenticate
2619             RASCS_AuthNotify
2620             RASCS_AuthRetry
2621             RASCS_AuthCallback
2622             RASCS_AuthChangePassword
2623             RASCS_AuthProject
2624             RASCS_AuthLinkSpeed
2625             RASCS_AuthAck
2626             RASCS_ReAuthenticate
2627             RASCS_Authenticated
2628             RASCS_PrepareForCallback
2629             RASCS_WaitForModemReset
2630             RASCS_WaitForCallback
2631             RASCS_Projected
2632             RASCS_StartAuthentication // Windows 95 only
2633             RASCS_CallbackComplete // Windows 95 only
2634             RASCS_LogonNetwork // Windows 95 only
2635             RASCS_SubEntryConnected
2636             RASCS_SubEntryDisconnected
2637             RASCS_Interactive = RASCS_PAUSED
2638             RASCS_RetryAuthentication
2639             RASCS_CallbackSetByCaller
2640             RASCS_PasswordExpired
2641             RASCS_Connected = RASCS_DONE
2642             RASCS_Disconnected
2643              
2644             B
2645              
2646             The connection process states are divided into three classes: running states,
2647             paused states, and terminal states. An application can easily determine the
2648             class of a specific state by performing Boolean bit operations with the RASCS_PAUSED
2649             and RASCS_DONE bitmasks. Here are some examples:
2650              
2651             $fDoneState = $status & RASCS_DONE;
2652             $fPausedState = $status & RASCS_PAUSED;
2653             $fRunState = !($fDoneState || $fPausedState);
2654              
2655             =cut
2656              
2657             #================
2658             sub RasGetConnectStatus ($) {
2659             #================
2660             # dwError is sometimes 600
2661             # values are in %RASCS
2662 0     0     my $hrasconn = shift;
2663 0           $LastError = 0;
2664 0 0 0       ($LastError = 6, return) unless $hrasconn && $hrasconn !~ /\D/;
2665              
2666 0   0       $RasGetConnectStatus ||= new("rasapi32", "RasGetConnectStatus", [N,P], N);
2667              
2668 0           my $dwSize = 4+4+4 + RAS_MaxDeviceType+1 + RAS_MaxDeviceName+1;
2669              
2670 0           DWORD_ALIGN($dwSize);
2671              
2672 0           my $RASCONNSTATUS = pack "La".($dwSize-4), ($dwSize, "");
2673              
2674 0           my ($ret, $dwError);
2675 0           $ret = $RasGetConnectStatus->Call($hrasconn, $RASCONNSTATUS);
2676              
2677 0 0         $ret == 6 and ($LastError = 6, return); # invalid handle
2678              
2679 0 0         $ret and RASERROR($ret);
2680              
2681             # don't know why do we need another error code if the function
2682             # itself returns one
2683             #$dwError = unpack L, substr($RASCONNSTATUS, 8,4) and RASERROR($dwError);
2684              
2685 0           my $status = unpack "L", substr($RASCONNSTATUS, 4,4);
2686 0 0         wantarray ? ($status, $RASCS{$status}) : $status;
2687             }
2688              
2689             =item RasDialDlg ( )
2690              
2691             This function tries to establish a RAS connection using
2692             a specified phonebook entry and the credentials of the logged-on user.
2693             It displays a stream of dialog boxes that indicate the state of the connection
2694             operation and returns when the connection is established,
2695             or when the user cancels the operation. B
2696              
2697             RasDialDlg( $EntryName [, $hwnd, $PhoneNumber] );
2698              
2699             $EntryName - RAS/DUN entry, the only mandatory parameter
2700             $hwnd - Identifies the window that owns the modal RasDialDlg
2701             dialog boxes.
2702             This member can be any valid window handle, or it can
2703             be 0, undef (or omitted) if the dialog box has no owner
2704              
2705             The dialog box is centered on the owner window unless C<$hwnd> is C
2706             or invalid handle, in which case the dialog box is centered on the screen.
2707              
2708             $PhoneNumber - an overriding phone number (if not needed - use "" or
2709             undef).
2710              
2711             It does not inherit anything from phonebook if specified - no prefix,
2712             no callin card, no waiting.
2713             You should even add DP before the number for pulse dialing.
2714              
2715             Returns TRUE on success, FALSE if user selects "Cancel" button or an error occurs.
2716             You can check the last case with C.
2717              
2718             if ( RasDialDlg("NEV4") ) {
2719             print "Connection established\n";
2720             } elsif ( !Win32::RASE::GetLastError ) {
2721             print "User selected \n";
2722             } else {
2723             warn Win32::RASE::FormatMessage(), "\n";
2724             }
2725              
2726             =cut
2727              
2728             #================
2729             sub RasDialDlg ($;$$) {
2730             #================
2731 0     0     $LastError = 0;
2732 0           RASCROAK "this function works on NT only" unless Win32::IsWinNT;
2733              
2734 0   0       $RasDialDlg ||= new("rasdlg", "RasDialDlg", [P,P,P,P], N);
2735              
2736 0           my ($entry, $hwnd, $lpszPhoneNumber) = @_;
2737 0           my $dwSize = 36;
2738              
2739 0 0 0       $hwnd = 0 if $hwnd && !IsWindow($hwnd);
2740              
2741 0   0       my $RASDIALDLG = pack "LLa".($dwSize-8), ($dwSize, $hwnd||0, "");
2742              
2743 0 0 0       my $ret = $RasDialDlg->Call($PHONEBOOK||0,
      0        
2744             $entry, $lpszPhoneNumber||0, $RASDIALDLG) and return 1;
2745              
2746 0           $LastError = unpack "L", substr($RASDIALDLG, 6*4,4);
2747 0           return;
2748             }
2749              
2750             =item RasDial ( )
2751              
2752             This function establishes a RAS/DUN connection. The connection data includes
2753             callback and user authentication information.
2754              
2755             $hrasconn = RasDial($EntryName, $PhoneNumber, $UserName, $Password,
2756             $Domain, $CallbackNumber);
2757              
2758             $EntryName - RAS/DUN entry, the only mandatory parameter
2759             $PhoneNumber - an overriding phone number (if not needed - use "" or
2760             undef).
2761            
2762             It does not inherit anything from the phonebook if specified -
2763             no prefix, no calling card, no waiting.
2764             You should add DP before the number for pulse dialing.
2765              
2766             $UserName - user's user name (look below)
2767             $Password - user's password
2768             $Domain - domain on which authentication is to occur. An empty
2769             string ("" or undef) specifies the domain in which the remote
2770             access server is a member (NT only). An asterisk specifies the
2771             domain stored in the phonebook for the entry.
2772             It's in addr form (size is limited to 15 chars).
2773             $CallbackNumber - a callback phone number. An empty string ("") or
2774             undef indicates that callback should not be used. This string is
2775             ignored unless the user has "Set By Caller" callback permission
2776             on the RAS server (NT only). An asterisk indicates that the number
2777             stored in the phonebook should be used for callback.
2778              
2779             B
2780             [These 2 paragraphs are copied from the API docs. I wanted to add this
2781             for some completeness but I was told that probably this is not truth and if
2782             Username or Password are empty user will get a dialog box with Username/Password
2783             prompts.]
2784              
2785             RAS does not actually log the user onto the network. The user does this in the usual
2786             manner, for example, by logging on with cached credentials prior to making the
2787             connection or by using CTRL+ALT+DEL, after the RAS connection is established.
2788              
2789             If both the UserName and Password members are empty strings (""), RAS uses the
2790             user name and password of the current logon context for authentication. For a user
2791             mode application, RAS uses the credentials of the currently logged-on interactive user.
2792             For a Win32 service process, RAS uses the credentials associated with the service.
2793              
2794             B
2795              
2796             RAS uses the UserName and Password strings to log the user onto the network.
2797             Windows 95 cannot get the password of the currently logged-on user, so if both
2798             the UserName and the Password members are empty strings ("" or undef), RAS leaves
2799             the user name and password empty during authentication. I.e. it provides no
2800             additional search (look at C for that).
2801              
2802              
2803             B It seems that overriding phone number is being dialed "as is" - without using
2804             any long-distance/international phone settings. So you have to provide this number
2805             with all prefixes and waitings (W etc.) if needed. Additional
2806             dashes, blanks and brackets are OK.
2807              
2808             $hrasconn - on success - handle to active RAS/DUN connection,
2809             otherwise undef
2810              
2811              
2812             You can use C<$hrasconn> in C or C.
2813             Note that this function calls C internally on error, so after that,
2814             the handle of the failed connection is not available and the port is ready
2815             for the next try.
2816              
2817             B
2818              
2819             ($err, $errtext) = RasDial("CLICK",undef,"ppblazer","qwerty");
2820             if ($err) {
2821             print "$err, $errtext\n"; exit;
2822             } else {
2823             ... your work here ...
2824             }
2825              
2826             B this is the B operation. Nobody knows if it could really
2827             hang fast enough if the line is busy (for ex.) The best way would be to run C
2828             in the separate process or thread. In most cases you don't really need C<$hrasconn>
2829             in the main process - you can terminate the connection at any time with C.
2830             Or you can easily get C<$hrasconn> with the use of C.
2831              
2832             If you run C in a child-process and terminate dialing in progress (for ex.
2833             on timeout) you have to free the port yourself (C or C).
2834              
2835             For more info take a look at Win32 API docs (RASDIALPARAMS etc).
2836              
2837             Command line syntax:
2838              
2839             perl -MWin32::RASE -e RasDial(NEV1,undef,ppblazer,'6hTR7dwA')
2840             perl -MWin32::RASE -e "RasDial(NEV1,undef,ppblazer,'6hTR7dwA') or print Win32::RASE::FormatMessage"
2841             perl -MWin32::RASE -e "print RasDial(NEV1,undef,ppblazer,'6hTR7dwA')||Win32::RASE::FormatMessage"
2842              
2843             =cut
2844              
2845             #================
2846             sub RasDial ($;$$$$$) {
2847             #================
2848 0     0     my ($szEntryName, $szPhoneNumber, $szUserName,
2849             $szPassword, $szDomain, $szCallbackNumber) = @_;
2850 0           $LastError = 0;
2851              
2852 0 0 0       RASCROAK "entry-name and alt phone-number can't be both empty"
2853             unless $szEntryName || $szPhoneNumber;
2854              
2855 0   0       $RasDial ||= new("rasapi32", "RasDial", [P,P,P,N,P,P], N);
2856              
2857 0 0         my $dwSize = 4 + RAS_MaxEntryName + 1 + RAS_MaxPhoneNumber + 1 +
2858             RAS_MaxCallbackNumber + 1 + UNLEN + 1 + PWLEN + 1 + DNLEN + 1 +
2859             (Win32::IsWinNT && $WINVER >= 0x401 ? 4+4 : 0);
2860              
2861 0           DWORD_ALIGN($dwSize);
2862              
2863 0   0       my $RASDIALPARAMS =
      0        
      0        
      0        
      0        
      0        
2864             pack "La".(RAS_MaxEntryName + 1)."a".(RAS_MaxPhoneNumber + 1).
2865             "a".(RAS_MaxCallbackNumber + 1)."a".(UNLEN + 1).
2866             "a".(PWLEN + 1)."a".(DNLEN + 1)
2867             ,
2868             ($dwSize, $szEntryName||"", $szPhoneNumber||"", $szCallbackNumber||"",
2869             $szUserName||"", $szPassword||"", $szDomain||"");
2870              
2871 0           $RASDIALPARAMS .= "\0"x($dwSize - length $RASDIALPARAMS);
2872              
2873 0           my $lphRasConn = DWORD_NULL;
2874 0   0       my $ret = $RasDial->Call(0, $PHONEBOOK||0,
2875             $RASDIALPARAMS, 0, 0, $lphRasConn);
2876              
2877 0           my $hrasconn = unpack "L", $lphRasConn;
2878              
2879 0 0         if ($ret) {
2880 0 0         RasHangUp($hrasconn) if $hrasconn;
2881 0           $LastError = $ret, return;
2882             } else {
2883 0           return $hrasconn;
2884             }
2885             }
2886              
2887              
2888             =pod
2889              
2890             =back
2891              
2892             B< =====================================>
2893              
2894             B< TAPI RELATED FUNCTIONS>
2895              
2896             B< =====================================>
2897              
2898              
2899             =over 4
2900              
2901             =item RasEnumDevices ( )
2902              
2903             %devices = RasEnumDevices();
2904              
2905             This function returns the name and type of all available RAS-capable devices.
2906             In the C<%devices> hash device names are keys and types are values. Common
2907             device types are "modem", "x25", "vpn", "isdn", "rastapi" etc.
2908              
2909             Croaks on errors. Returns FALSE if no one RAS capable device was found.
2910              
2911             For example the first RAS-capable device name is
2912              
2913             $DeviceName = (RasEnumDevices())[0];
2914              
2915             This function fills out a non-exported hash C<%Win32::RASE::RasDevEnumeration>
2916             of the same structure as C<%devices>, so in most cases there is no need to call
2917             this function more then once.
2918              
2919             Command line syntax:
2920              
2921             perl -MWin32::RASE -e "print ((RasEnumDevices)[0])"
2922              
2923             =cut
2924              
2925             #================
2926             sub RasEnumDevices () {
2927             #================
2928 0     0     $LastError = 0;
2929 0   0       $RasEnumDevices ||= new("rasapi32", "RasEnumDevices",[P,P,P],N);
2930              
2931 0           my $dwSize = RAS_MaxDeviceType+1+RAS_MaxDeviceName+1+4;
2932              
2933 0           DWORD_ALIGN($dwSize);
2934              
2935 0           my $RASDEVINFO = pack "La".(10*$dwSize-4), ($dwSize, ""); # 10 devices initially
2936              
2937 0           my ($lpcb, $lpcDevices) = (pack("L",length $RASDEVINFO), DWORD_NULL);
2938              
2939 0           my $ret = $RasEnumDevices->Call($RASDEVINFO, $lpcb, $lpcDevices);
2940              
2941 0 0         if ($ret) {
2942 0           my $b = unpack "L",$lpcb;
2943 0           $RASDEVINFO = pack "La".($b-4), ($dwSize, "");
2944 0           $ret = $RasEnumDevices->Call($RASDEVINFO, $lpcb, $lpcDevices);
2945             }
2946              
2947 0 0         $ret and RASERROR($ret);
2948              
2949 0           my %devices;
2950              
2951 0           for my $i(1..unpack "L",$lpcDevices) {
2952 0           my $buffer = substr $RASDEVINFO, ($dwSize*($i-1)), $dwSize;
2953 0           my ($dwSize1, $szDeviceType, $szDeviceName) =
2954             unpack "La".(RAS_MaxDeviceType+1)."a".(RAS_MaxDeviceName+1), $buffer;
2955              
2956 0           CRUNCH($szDeviceType, $szDeviceName);
2957 0           $devices{$szDeviceName} = $szDeviceType;
2958             }
2959 0           %RasDevEnumeration = %devices;
2960             }
2961              
2962             =item RasEnumDevicesByType ( )
2963              
2964             The easier version of previous.
2965              
2966             @DevNames = RasEnumDevicesByType( $devtype );
2967              
2968             Returns names of RAS-capable devices of type C<$devtype>. For example
2969             the first modem's name
2970              
2971             $ModemName = (RasEnumDevicesByType("modem"))[0];
2972              
2973             C<$devtype> is case insensitive.
2974              
2975             =cut
2976              
2977             #=============================
2978             sub RasEnumDevicesByType ($) {
2979             #=============================
2980 0     0     my $type = shift;
2981 0 0         %RasDevEnumeration = RasEnumDevices() unless defined %RasDevEnumeration;
2982              
2983 0           grep {lc($RasDevEnumeration{$_}) eq lc($type)} keys %RasDevEnumeration;
  0            
2984             }
2985              
2986             =item TAPIlineGetTranslateCaps ( )
2987              
2988             This function is not exported and is not intended for public use.
2989             It is called each time you load Win32::RASE and fills out 3 global variables
2990             and global hash (below).
2991              
2992             It takes local information from your dialup settings.
2993              
2994             ($countryID, $countryCode, $areaCode) =
2995             Win32::RASE::TAPIlineGetTranslateCaps ();
2996              
2997             The return values are describing the B that is selected
2998             in you dialing properties.
2999              
3000             $countryID - the unique number that TAPI assigns to each country.
3001             It is not what you are typing on your phone, though it
3002             sometimes has the same value. Different countries always
3003             have different countryID. This allows multiple entries
3004             to exist in the country list with the same country code
3005             (for example, all countries in North America and the
3006             Caribbean share country code 1, but require separate
3007             entries in the list).
3008              
3009             $countryCode - this really is the code that would be dialed in an
3010             international call to your computer's location.
3011              
3012             $areaCode - city or area code (local).
3013              
3014             These 3 values are copied to non-exported global variables
3015             B<$Win32::RASE::LOCAL_ID>, B<$Win32::RASE::LOCAL_CODE> and
3016             B<$Win32::RASE::LOCAL_AREA>.
3017              
3018             They are mainly for internal use, just note that they are here.
3019              
3020             The complete TAPI countries list is being copied to non-exported global hash
3021             B<%Win32::RASE::TAPIEnumeration>. Keys are countryID's, each value points
3022             to 3-element array: [0] is country-name, [1] is countryCode described above,
3023             [2] is NextCountryID in TAPI-enumeration (TAPI docs, but in most cases you
3024             don't need to use this hash explicitly).
3025              
3026             Use C to print this hash (for fun ;)
3027              
3028             =cut
3029              
3030             #================
3031             sub TAPIlineGetTranslateCaps () {
3032             #================
3033 0     0     $LastError = 0;
3034 0           my ($CurrentLocation, %locations) = TAPIEnumLocations();
3035 0           ($LOCAL_ID, $LOCAL_CODE, $LOCAL_AREA) = @{$locations{$CurrentLocation}}[0,1,2];
  0            
3036              
3037 0 0         IsCountryID($LOCAL_ID) or
3038             RASCROAK "TAPI could not find your local settings\nPlease, contact the author of this module.";
3039              
3040 0 0 0       TAPICountryCode($LOCAL_ID) == $LOCAL_CODE and $LOCAL_AREA !~ /\D/ or
3041             RASCROAK "TAPI-error. Please adjust your dialing properties.";
3042              
3043 0           ($LOCAL_ID, $LOCAL_CODE, $LOCAL_AREA);
3044             }
3045              
3046             =item TAPIEnumLocations ( )
3047              
3048             Just a handy function (non-exported) to enumerate locations in your Dialing Properties.
3049             It's being executed internally when Win32::RASE needs it, so in most cases you don't
3050             need to use it explicitly.
3051              
3052             ($CurrentLocation, %locations) = Win32::RASE::TAPIEnumLocations;
3053              
3054             $CurrentLocation - current dialing location's name
3055             %locations - keys are location-names, values are anonymous
3056             arrays that are filled out like:
3057             [$CountryID, $CountryCode, $CityCode, $Options, $LocalAccessCode,
3058             $LongDistanceAccessCode, $TollPrefixList, $PermanentLocationID]
3059              
3060             $Options - 0/1 tone/pulse dialing, this value could be
3061             used to define good timeout for RasDial()
3062             $LocalAccessCode - the access code to be dialed before calls to
3063             addresses in the local calling area
3064             $LongDistanceAccessCode - the access code to be dialed before calls to
3065             addresses outside the local calling area
3066             $TollPrefixList - the toll prefix list for the location. The
3067             string will contain only prefixes consisting
3068             of the digits "0" through "9", separated
3069             from each other by a single comma
3070             $PermanentLocationID - internal unique identifier of the location
3071              
3072             Other values in array are described in C.
3073              
3074             B
3075              
3076             ($CurrentLocation, %locations) = Win32::RASE::TAPIEnumLocations;
3077             print "$CurrentLocation\n";
3078             print map "$_ => [".(join", ",@{$locations{$_}})."]\n",
3079             keys %locations;
3080              
3081              
3082             =cut
3083              
3084             #================
3085             sub TAPIEnumLocations () {
3086             #================
3087 0     0     $LastError = 0;
3088 0           my ($dwTotalSize, $dwNeededSize, $dwUsedSize, $dwNumLocations,
3089             $dwLocationListSize, $dwLocationListOffset, $dwCurrentLocationID,
3090             $dwNumCards, $dwCardListSize, $dwCardListOffset, $dwCurrentPreferredCardID);
3091 0           my ($dwPermanentLocationID, $dwLocationNameSize, $dwLocationNameOffset,
3092             $dwCountryCode, $dwCityCodeSize, $dwCityCodeOffset, $dwPreferredCardID,
3093             $dwLocalAccessCodeSize, $dwLocalAccessCodeOffset, $dwLongDistanceAccessCodeSize,
3094             $dwLongDistanceAccessCodeOffset, $dwTollPrefixListSize, $dwTollPrefixListOffset,
3095             $dwCountryID, $dwOptions, $dwCancelCallWaitingSize, $dwCancelCallWaitingOffset);
3096 0           my (%locations, $CityCode, $LocationName, $CurrentLocation, $LocalAccessCode,
3097             $LongDistanceAccessCode, $TollPrefixList);
3098 0           $dwTotalSize = 4*11;
3099              
3100 0   0       $lineGetTranslateCaps ||= new("tapi32", "lineGetTranslateCaps", [N,N,P], N);
3101              
3102 0           my $LINETRANSLATECAPS = pack "La".($dwTotalSize-4), ($dwTotalSize, "");
3103              
3104 0           my $ret = $lineGetTranslateCaps->Call(0, 0x10004, $LINETRANSLATECAPS);
3105              
3106 0 0         $ret and RASERROR($ret);
3107              
3108 0           ($dwNeededSize, $dwUsedSize) = unpack "LL", substr($LINETRANSLATECAPS, 4);
3109              
3110 0           $LINETRANSLATECAPS = pack "La".($dwNeededSize-4), ($dwNeededSize, "");
3111              
3112 0           $ret = $lineGetTranslateCaps->Call(0, 0x10004, $LINETRANSLATECAPS);
3113              
3114 0 0         $ret and RASERROR($ret);
3115              
3116 0           ($dwNeededSize, $dwUsedSize, $dwNumLocations,
3117             $dwLocationListSize, $dwLocationListOffset, $dwCurrentLocationID,
3118             $dwNumCards, $dwCardListSize, $dwCardListOffset, $dwCurrentPreferredCardID) =
3119             unpack "LLLLLLLLLL", substr($LINETRANSLATECAPS, 4);
3120              
3121 0           for my $i(0..$dwNumLocations-1) {
3122 0           ($dwPermanentLocationID, $dwLocationNameSize, $dwLocationNameOffset,
3123             $dwCountryCode, $dwCityCodeSize, $dwCityCodeOffset, $dwPreferredCardID,
3124             $dwLocalAccessCodeSize, $dwLocalAccessCodeOffset, $dwLongDistanceAccessCodeSize,
3125             $dwLongDistanceAccessCodeOffset, $dwTollPrefixListSize, $dwTollPrefixListOffset,
3126             $dwCountryID, $dwOptions, $dwCancelCallWaitingSize, $dwCancelCallWaitingOffset) =
3127             unpack "LLLLLLLLLLLLLLLLL",
3128             # 4*17 - sizeof(LINELOCATIONENTRY)
3129             substr($LINETRANSLATECAPS, $dwLocationListOffset+$i*4*17);
3130              
3131 0           $LocationName = substr($LINETRANSLATECAPS, $dwLocationNameOffset, $dwLocationNameSize);
3132 0           $CityCode = substr($LINETRANSLATECAPS, $dwCityCodeOffset, $dwCityCodeSize);
3133 0           $LocalAccessCode = substr($LINETRANSLATECAPS, $dwLocalAccessCodeOffset, $dwLocalAccessCodeSize);
3134 0           $LongDistanceAccessCode = substr($LINETRANSLATECAPS, $dwLongDistanceAccessCodeOffset, $dwLongDistanceAccessCodeSize);
3135 0           $TollPrefixList = substr($LINETRANSLATECAPS, $dwTollPrefixListOffset, $dwTollPrefixListSize);
3136              
3137 0           CRUNCH($LocationName, $CityCode, $LocalAccessCode,
3138             $LongDistanceAccessCode, $TollPrefixList);
3139              
3140 0           $locations{$LocationName} = [$dwCountryID, $dwCountryCode, $CityCode, $dwOptions,
3141             $LocalAccessCode, $LongDistanceAccessCode, $TollPrefixList, $dwPermanentLocationID];
3142              
3143 0 0         $CurrentLocation = $LocationName if $dwCurrentLocationID == $dwPermanentLocationID;
3144             }
3145              
3146 0           ($CurrentLocation, %locations);
3147             }
3148              
3149             =item TAPISetCurrentLocation ( )
3150              
3151             TAPISetCurrentLocation( $location );
3152              
3153             $location - optional, the name of the location that is configured
3154             in the Dialing Properies.
3155             If omitted the "Default Location" is used.
3156              
3157             Returns TRUE on success, FALSE if C<$location> was not found in the
3158             Dialing Properties, croaks on TAPI errors.
3159              
3160             =cut
3161              
3162             #================
3163             sub TAPISetCurrentLocation (;$) {
3164             #================
3165 0     0     $LastError = 0;
3166 0   0       my $location = shift || "Default Location";
3167 0           $location =~ s/^ *(.*?) *$/$1/;
3168 0           my ($CurrentLocation, %locations) = TAPIEnumLocations();
3169 0           my $ret;
3170              
3171 0 0         exists($locations{$location}) or return;
3172              
3173 0   0       $lineSetCurrentLocation ||= new("tapi32", "lineSetCurrentLocation", [N,N], N);
3174              
3175 0           my $dwLocation = $locations{$location}->[7];
3176              
3177 0           my $hLineApp = TAPIlineInitialize();
3178              
3179 0 0         $ret = $lineSetCurrentLocation->Call($hLineApp, $dwLocation) and
3180             (TAPIlineShutdown($hLineApp), RASERROR($ret));
3181              
3182              
3183 0 0         $ret = TAPIlineShutdown($hLineApp) and RASERROR($ret);
3184 0           1;
3185             }
3186              
3187             #================
3188             sub RasGetCountryInfo ($) {
3189             #================
3190 0   0 0     $RasGetCountryInfo ||= new("rasapi32", "RasGetCountryInfo", [P,P], N);
3191              
3192 0           my $dwCountryId = shift;
3193 0           my $dwSize = 20;
3194 0           my $SizeBuf = 256;
3195 0           my $RASCTRYINFO = pack "LLa".($SizeBuf-8), ($dwSize, $dwCountryId, "");
3196              
3197 0           my $dwSizeBuf = pack "L", $SizeBuf;
3198 0           my $ret = $RasGetCountryInfo->Call($RASCTRYINFO, $dwSizeBuf);
3199              
3200 0 0         if ($ret == 603) {
3201 0           $SizeBuf = unpack "L", $dwSizeBuf;
3202 0           $RASCTRYINFO = pack "LLa".($SizeBuf-8), ($dwSize, $dwCountryId, "");
3203 0 0         $ret = $RasGetCountryInfo->Call($RASCTRYINFO, $dwSizeBuf) and RASERROR($ret);
3204             }
3205              
3206 0 0         $ret and RASERROR($ret);
3207              
3208 0           my ($dwNextCountryID, $dwCountryCode, $dwCountryNameOffset) =
3209             unpack "x8 LLL", $RASCTRYINFO;
3210 0           my $Country = substr $RASCTRYINFO, $dwCountryNameOffset;
3211              
3212 0           CRUNCH($Country);
3213              
3214 0           ($Country, $dwCountryCode, $dwNextCountryID);
3215             }
3216              
3217             #================
3218             sub TAPIEnumCountries () {
3219             #================
3220 0     0     my $dwCountryId = 1;
3221 0           my ($Country, $dwCountryCode, $dwNextCountryID, %cou);
3222              
3223 0           do {
3224 0           ($Country, $dwCountryCode, $dwNextCountryID) = RasGetCountryInfo($dwCountryId);
3225 0           $cou{$dwCountryId} = [$Country, $dwCountryCode, $dwNextCountryID];
3226 0           $dwCountryId = $dwNextCountryID;
3227             } until $dwNextCountryID == 0;
3228 0           %cou;
3229             }
3230              
3231             =item TAPIEnumerationPrint ( )
3232              
3233             This function prints nicely formatted TAPI countries table that is stored in
3234             the B<%Win32::RASE::TAPIEnumeration> (see above). Not exported by default;
3235              
3236             Win32::RASE::TAPIEnumerationPrint();
3237              
3238             Columns: CountryID, CountryName, CountryCode, NextCountryID
3239              
3240             For more: C and TAPI docs.
3241              
3242             Always returns TRUE.
3243              
3244             =cut
3245              
3246             #================
3247             sub TAPIEnumerationPrint () {
3248             #================
3249 0     0     my $maxlen = 0;
3250 0           local $_;
3251 0           $LastError = 0;
3252              
3253 0 0         %TAPIEnumeration = TAPIEnumCountries() if !defined %TAPIEnumeration;
3254              
3255 0           for (keys %TAPIEnumeration) {
3256 0 0         $maxlen = length($TAPIEnumeration{$_}->[0])
3257             if $maxlen < length $TAPIEnumeration{$_}->[0];
3258             }
3259              
3260 0           printf "%9s%".($maxlen-6)."s%16s %6s\n\n", "CountryID", "CountryName",
3261             "CountryCode", "NextID";
3262              
3263 0           map { printf "%6d %${maxlen}s %6d %6d\n", $_, $TAPIEnumeration{$_}->[0],
  0            
3264             $TAPIEnumeration{$_}->[1], $TAPIEnumeration{$_}->[2]} sort keys %TAPIEnumeration;
3265 0           1;
3266             }
3267              
3268             =item TAPICountryName ( )
3269              
3270             Returns CountryName by CountryID or FALSE if given CountryID does not
3271             exist in TAPI-table.
3272              
3273             $CountryName = TAPICountryName($CountryID);
3274              
3275             Command line syntax:
3276              
3277             perl -MWin32::RASE -e "print TAPICountryName(1)"
3278              
3279             =cut
3280              
3281             #================
3282             sub TAPICountryName ($) {
3283             #================
3284 0     0     my $CountryID = shift;
3285 0           $LastError = 0;
3286              
3287 0 0         %TAPIEnumeration = TAPIEnumCountries() if !defined %TAPIEnumeration;
3288 0 0         exists($TAPIEnumeration{$CountryID}) ? $TAPIEnumeration{$CountryID}->[0] : undef;
3289             }
3290              
3291             =item TAPICountryCode ( )
3292              
3293             Returns CountryCode by CountryID or FALSE if given CountryID does not
3294             exist in TAPI-table.
3295              
3296             $CountryCode = TAPICountryCode($CountryID);
3297              
3298             =cut
3299              
3300             #================
3301             sub TAPICountryCode ($) {
3302             #================
3303 0     0     my $CountryID = shift;
3304 0           $LastError = 0;
3305              
3306 0 0         %TAPIEnumeration = TAPIEnumCountries() if !defined %TAPIEnumeration;
3307 0 0         exists($TAPIEnumeration{$CountryID}) ? $TAPIEnumeration{$CountryID}->[1] : undef;
3308             }
3309              
3310             =item IsCountryID ( )
3311              
3312             Returns TRUE if given $CountryID exist in TAPI-table, otherwise FALSE.
3313              
3314             IsCountryID($CountryID);
3315              
3316             Just to have such a pretty name ;)
3317              
3318             =cut
3319              
3320             #================
3321             sub IsCountryID ($) {
3322             #================
3323 0     0     my $CountryID = shift;
3324 0           $LastError = 0;
3325              
3326 0 0         %TAPIEnumeration = TAPIEnumCountries() if !defined %TAPIEnumeration;
3327 0 0         exists($TAPIEnumeration{$CountryID}) ? 1 : 0;
3328             }
3329              
3330             #======================
3331             sub GetDefaultCommConfig ($) {
3332             #======================
3333 0 0   0     my $dev = shift
3334             or RASCROAK "empty DeviceName";
3335              
3336 0           my $GetDefaultCommConfig = new("kernel32", "GetDefaultCommConfig", [P,P,P], N);
3337              
3338 0           my $lpCC = "";
3339 0           my $lpdwSize = DWORD_NULL;
3340              
3341 0           my $ret = $GetDefaultCommConfig->Call($dev, $lpCC, $lpdwSize);
3342 0           my $dwSize = unpack "L", $lpdwSize;
3343              
3344 0           $lpCC = "\0"x$dwSize;
3345 0 0         $ret = $GetDefaultCommConfig->Call($dev, $lpCC, $lpdwSize)
3346             or ($LastError = Win32::GetLastError(), return);
3347              
3348 0           substr $lpCC, 0, $dwSize;
3349             }
3350              
3351             =item TAPIlineInitialize ( )
3352              
3353             This is a non-exported function mainly for internal use. It could be handy only
3354             if you'd start writing your own TAPI-related functions.
3355              
3356             ($hLineApp, $dwNumDevs) = Win32::RASE::TAPIlineInitialize();
3357              
3358             or in scalar context
3359              
3360             $hLineApp = Win32::RASE::TAPIlineInitialize();
3361              
3362             $hLineApp - the application's usage non-zero handle for TAPI
3363             $dwNumDevs - number of line devices available to the TAPI application
3364              
3365             Croaks on TAPI errors.
3366              
3367             The applicaton should always call C to release memory
3368             resources allocated by TAPI.DLL.
3369              
3370             =cut
3371              
3372             #================
3373             sub TAPIlineInitialize () {
3374             #================
3375 0     0     $LastError = 0;
3376 0   0       $lineInitialize ||= new("tapi32","lineInitialize",[P,N,P,P,P],N);
3377              
3378             # dll-instance
3379             #my $tapi32dll = $Win32::API::Libraries{"tapi32"};
3380 0           my $tapi32dll = $lineInitialize->{dll};
3381              
3382 0           my ($lphLineApp, $lpfnCallback, $lpdwNumDevs) =
3383             (DWORD_NULL, DWORD_NULL, DWORD_NULL);
3384              
3385 0           my $ret;
3386 0 0         $ret = $lineInitialize->Call($lphLineApp,
3387             $tapi32dll, $lpfnCallback, "Win32::RASE v.$VERSION\0", $lpdwNumDevs)
3388             and RASERROR($ret);
3389              
3390 0           my $hLineApp = unpack "L", $lphLineApp;
3391 0           my $dwNumDevs = unpack "L", $lpdwNumDevs;
3392              
3393 0 0         wantarray ? ($hLineApp, $dwNumDevs) : $hLineApp;
3394             }
3395              
3396             =item TAPIlineShutdown ( )
3397              
3398             This is a non-exported function mainly for internal use. It could be handy only
3399             if you'd start writing your own TAPI-related functions.
3400              
3401             Win32::RASE::TAPIlineShutdown($hLineApp);
3402              
3403             $hLineApp - the application's usage handle for TAPI
3404              
3405             Returns zero if the request is successful or a negative error number
3406             if an error has occurred.
3407              
3408             =cut
3409              
3410             #================
3411             sub TAPIlineShutdown ($) {
3412             #================
3413 0     0     $LastError = 0;
3414 0   0       $lineShutdown ||= new("tapi32","lineShutdown",[N],N);
3415 0           $lineShutdown->Call(shift);
3416             }
3417              
3418              
3419             # from RegExps.pm
3420 0     0     sub OCTET {'(?:1\d\d|2[0-4]\d|25[0-5]|[1-9]\d?|0)'}
3421 0     0     sub HOSTNUMBER {'(?:(?:'.OCTET.'\.){3}'.OCTET.'\.?)'}
3422              
3423              
3424             1;
3425              
3426             __END__