How do I make a voice call (PSTN)?

Hello, I have a Conexant HSF PCI modem that I used to make phone calls with Microsoft Outlook. (The modem has a handset connected to it).
I am trying to set up KAddressBook to do the same but it expects an external application to dial the number. What application would it be?

Thats a good question, and having never had to do it, I had to do some quick googling.

Anyway, I found a possible application (available in oss repositories) called ant-phone. Download and install, then configure kaddressbook with

ant-phone -e %N

Edit: Another canidate may be the dtmf-dial application

ant-phone works with ISDN and dtmf-dial generates sound, whereas I basically need to send ATD^number to /dev/modem, where number depends on the number to dial and the time of day.

I think the following script should work for me:

#!/bin/sh
phone="${1}"
hour="$(date ‘+%H’)"
if “${hour}” >= ‘8’ && “${hour}” < ‘20’ ]]
then prefix=‘01044’
else prefix=‘01033’
fi
“$1” = ‘+’* ]] && phone=‘0104400’"${1:1}"
“${1}” = ‘+48’* ]] && phone="$prefix${1:3}"
“${1}” = ‘+48’{5,6,7,8}* ]] && phone=‘01044’"${1:3}"
“${1}” = ‘+4880’* ]] && phone=‘01033’"${1:3}"
echo ‘Wywołuję ‘"${phone}"’…’
‘/usr/sbin/chat’>’/dev/modem’<’/dev/modem’ ‘’ ‘ATD^’"${phone}"’@’ ‘CONNECT’

It logs the number I dial; I do not care.

Thanks for sharing you solution :slight_smile:

Hmm, it doesn’t hurt anything, but you are too zealous with quoting on this line:

‘/usr/sbin/chat’>’/dev/modem’<’/dev/modem’ ‘’ ‘ATD^’"${phone}"’@’ ‘CONNECT’

The command name and the device name don’t need to be quoted since they are fixed strings without any whitespace inside. At least you didn’t try to quote the < which would have not worked. :slight_smile:

Also you could have reduced the number of quotes simply by using double quotes around this whole thing.

“ATD^${phone}@ CONNECT”

I imagine quoted text is parsed faster than unquoted :wink:
And failing because of lax quoting is a common pattern in scripts, so I prefer to be on the safe side.
(And, of course, s/>=/-ge/ and s/</-lt/).
Besides, it comes out that setgid scripts are disabled in SuSE, so the script will work only for dialout users.
I have also noticed that the hsfmodem driver puts my call on the loudspeakers, something I never managed to have under Windows. I am curious whether a microphone would work as well.

Surely you are joking. Even if that were true, it would only be minuscule amount for the loss of readability. :stuck_out_tongue:

The problem is that SuSE does not honor setgid in scripts so a compiled program is required, maybe along these lines:

#include <cstdlib>
#include <fstream>
#include <string>
#include <iostream>
#include <cstring>
#include <cerrno>
#include <sstream>
#include <stdexcept>
#include <cassert>

#include <sys/fcntl.h>

#define REQUIRES(P_C) typedef char const requirement_fulfilled_12 +(P_C)];

static char const sc_device_name] = “/dev/modem”,
sc_cmd ] = "AT
", sc_exp ] = "OK
",
sc_daytime_pfx] = “01044”, sc_nightly_pfx] = “01033”;

enum DialoguePart { DLG_QUESTION, DLG_ANSWER, DLG_COUNT };
enum Measures
{
HOUR_PER_DAY = 014, HOUR_PER_NIGHT = 014,
MIN_PER_HOUR = 074, SEC_PER_MIN = 074 };
enum LocalSettings { DAY_BEGIN_HOUR = 010, DAY_END_HOUR = 024 };

static std:: string phone_expand (char const p_phone ])
{
register time_t a_time ((+time (NULL)));
register struct tm const a_tm
(*localtime
(+a_time == -01?
&(a_time = +DAY_BEGIN_HOUR * +MIN_PER_HOUR * +SEC_PER_MIN): &a_time));
register char const
(*const a_pfx)
(+a_tm. tm_hour >= +DAY_BEGIN_HOUR && +a_tm. tm_hour < +DAY_END_HOUR?
sc_daytime_pfx: sc_nightly_pfx);
register std:: ostringstream a_phone;
static char const sc_format ] = "ATDT^\0@;H
";
enum FormatPos { POS_NUMBER = 06 };
REQUIRES (POS_NUMBER * sizeof *sc_format < sizeof sc_format);
REQUIRES (POS_NUMBER >= 0);
register std:: ostringstream:: pos_type const
a_beg (((a_phone << sc_format). tellp()));
static char const sc_local_pfx ] = “+4880”, sc_foreign_pfx ] = “00”;
assert (!sc_format +POS_NUMBER - 01]);
a_phone << p_phone;
if (+p_phone [01] == ‘+’)
a_phone. seekp (a_beg) << sc_daytime_pfx << sc_foreign_pfx << p_phone + 01;
if (!std:: string:: traits_type:: compare (p_phone, sc_local_pfx, 03))
a_phone. seekp (a_beg)
<< (p_phone [03] >= ‘5’ && p_phone [03] <= ‘8’?
std:: string:: traits_type:: compare (p_phone + 03, sc_local_pfx + 03, 02)?
sc_daytime_pfx: sc_nightly_pfx: a_pfx)
<< p_phone + 03; a_phone << sc_format + POS_NUMBER;
if (!a_phone) throw std:: runtime_error (“no phone”);
return a_phone. str(); }

static int nblk_talk (char const ]);

extern “C” int main (int p_argc, char const *const p_argv ])
{
for (int an_ix ((01)); +an_ix < +p_argc; ++an_ix)
{ register std:: string const a_cmd ((phone_expand (p_argv +an_ix])));
if (std:: cout && !(std:: cout << a_cmd << ’
'))
std:: perror (“write”);
if (nblk_talk (a_cmd. c_str()) != +EXIT_SUCCESS) return +EXIT_FAILURE; }
return std:: cout? +EXIT_SUCCESS: +EXIT_FAILURE; }

static int nblk_talk (char const p_cmd])
{
register FILE (*const a_modem) ((std:: fopen (sc_device_name, “r+”)));
if (a_modem)
{ register int a_code;
register int const a_fno ((+fileno (a_modem)));
if (+a_fno < 0) { perror (“file”); a_code = +EXIT_FAILURE; }
else
{ register int a_fl (+fcntl (+a_fno, +F_GETFL));
if (+a_fl < 0 || +fcntl (+a_fno, +F_SETFL, +a_fl & ~+O_NONBLOCK) == -01)
{ std:: perror (“flags”); a_code = +EXIT_FAILURE; }
else for (register std:: size_t an_ix (0); +an_ix < 01; ++an_ix)
if
(+std:: fputs (p_cmd, a_modem) == +EOF)
{ std:: perror (“write”); a_code = +EXIT_FAILURE; }
else
{ register int a_line ((0));
do
{
if
(+fcntl
(+a_fno, +F_SETFL, +a_line? +a_fl | +O_NONBLOCK: +a_fl & ~+O_NONBLOCK)
== -01)
{ std:: perror (“flags”); a_code = +EXIT_FAILURE; break; } ++a_line;
auto char a_buf +BUFSIZ];
auto char const (*const a_res) ((std:: fgets (a_buf, +BUFSIZ, a_modem)));
if (a_res) {
if (+std:: fputs (a_res, stdout) == +EOF)
{ std:: perror (“write”); a_code = +EXIT_FAILURE; }
else a_code = +EXIT_SUCCESS; }
else { std:: perror (“read”); a_code = +EXIT_FAILURE; }}
while (+a_code == +EXIT_SUCCESS); }}
if (+std:: fclose (a_modem)) { perror (“close”); return +EXIT_FAILURE; }
else return +a_code; } else { perror (“open”); return +EXIT_FAILURE; }}

Works for me. Note that I was had to use an stdio for nblk_talk because of a bug in fstream and because I need fileno.

Several corrections and enhancements:


#include <cstdlib>
#include <fstream>
#include <string>
#include <iostream>
#include <cstring>
#include <cerrno>
#include <sstream>
#include <stdexcept>
#include <cassert>

#include <sys/fcntl.h>

#define REQUIRED(P_C) typedef char const requirement_fulfilled_12 +(P_C)];

static char const sc_device_name] = "/dev/modem",
sc_cmd ] = "AT
", sc_exp ] = "OK
", 
sc_daytime_pfx] = "1044", sc_nightly_pfx] = "1033", 
sc_tel_scheme ] = "tel:";

enum Measures 
{ 
  HOUR_PER_DAY = 014, HOUR_PER_NIGHT = 014, 
  MIN_PER_HOUR = 074, SEC_PER_MIN = 074 };
enum LocalSettings { DAY_BEGIN_HOUR = 010, DAY_END_HOUR = 022 };

static std:: string phone_expand (char const *p_phone) 
/* 
Expands a phone number to a form that can be dialed. 
Strips initial "tel:" if present.
*/
{ 
  register time_t a_time ((+time (NULL))); 
  register struct tm const a_tm 
  (*localtime 
  (+a_time == -01? 
  &(a_time = +DAY_BEGIN_HOUR * +MIN_PER_HOUR * +SEC_PER_MIN): &a_time)); 
  register char const 
  (*const a_pfx) 
  (+a_tm. tm_hour >= +DAY_BEGIN_HOUR && +a_tm. tm_hour < +DAY_END_HOUR? 
  sc_daytime_pfx: sc_nightly_pfx); 
  register std:: ostringstream a_phone; 
  static char const sc_format ] = "ATDT^\0,@;H
"; 
  enum FormatPos { POS_NUMBER = 06 }; 
  register 
  std:: ostringstream:: pos_type const a_beg 
  (((a_phone << sc_format). tellp())); 
  static char const sc_local_pfx ] = "+4880", sc_foreign_pfx ] = "00"; 
  REQUIRED (POS_NUMBER * sizeof *sc_format < sizeof sc_format); 
  REQUIRED (POS_NUMBER > 0);
  std:: clog << a_tm. tm_hour << ' ' << a_pfx << '
';
  if (!std:: char_traits <char >:: compare (p_phone, sc_tel_scheme, 04)) 
    p_phone += 04;
  assert (!sc_format +POS_NUMBER - 01]);
  a_phone << p_phone; 
  if (std:: string:: traits_type:: eq (*p_phone, '+')) 
    a_phone. seekp (a_beg) << sc_daytime_pfx << sc_foreign_pfx << p_phone + 01; 
  if (!std:: string:: traits_type:: compare (p_phone, sc_local_pfx, 03)) 
    a_phone. seekp (a_beg) 
    << (p_phone [03] >= '5' && p_phone [03] <= '8'? 
    std:: string:: traits_type:: compare (p_phone + 03, sc_local_pfx + 03, 02)? 
    sc_daytime_pfx: sc_nightly_pfx: a_pfx) 
    << p_phone + 03; a_phone << sc_format + POS_NUMBER << '\0'; 
  if (!a_phone) throw std:: runtime_error ("no phone"); 
  return a_phone. str(); }
  
 static int nblk_talk (char const ]);
  
 extern "C" int main (int p_argc, char const *const p_argv ]) 
 { 
   for (int an_ix ((01)); +an_ix < +p_argc; ++an_ix) 
   { register std:: string const a_cmd ((phone_expand (p_argv +an_ix])));
     if (std:: cout && !(std:: cout << a_cmd << '
')) 
       std:: perror ("write"); 
     if (nblk_talk (a_cmd. c_str()) != +EXIT_SUCCESS) return +EXIT_FAILURE; }
   return std:: cout? +EXIT_SUCCESS: +EXIT_FAILURE; }

static int nblk_talk (char const p_cmd]) 
{ 
  register FILE (*const a_modem) ((std:: fopen (sc_device_name, "r+"))); 
  if (a_modem) /* ?MODEM_IS_OPEN Y */
  { register int a_code; 
    register int const a_fno ((+fileno (a_modem))); 
    if (+a_fno < 0) /* ?MODEM_NO_FILENO Y */ 
    { perror ("file"); a_code = +EXIT_FAILURE; } 
    else /* ?MODEM_NO_FILENO N */
    { register int a_fl (+fcntl (+a_fno, +F_GETFL)); 
    if (+a_fl < 0 || +fcntl (+a_fno, +F_SETFL, +a_fl & ~+O_NONBLOCK) == -01) 
      /* ?MODEM_NO_BLOCK Y */
    { perror ("flags"); a_code = +EXIT_FAILURE; } 
    else /* ?MODEM_NO_BLOCK N */ 
      for (register std:: size_t an_ix (0); +an_ix < 01; ++an_ix) /* @ONCE */
      if 
	(+std:: fputs (p_cmd, a_modem) == +EOF) /* ?MODEM_NO_COMMAND Y */
	{ perror ("write"); a_code = +EXIT_FAILURE; }
	else /* ?MODEM_NO_COMMAND N */
	{ register int a_line ((0)); 
      do /* @ANSWERS */ 
	/* 
	The code waits for the first answer line 
	and reads all available remaining lines */
      	if 
	  (+fcntl 
	  (+a_fno, +F_SETFL, +a_line? +a_fl | +O_NONBLOCK: +a_fl & ~+O_NONBLOCK) 
	  == -01) /* ?MODEM_BLOCK_NO_TOGGLE Y */
	{ perror ("flags"); a_code = +EXIT_FAILURE; } 
	else /* ?MODEM_BLOCK_NO_TOGGLE N */ { ++a_line;
      auto char a_buf +BUFSIZ]; 
      auto char const (*const a_res) ((std:: fgets (a_buf, +BUFSIZ, a_modem))); 
      if (a_res) /* ?MODEM_ANSWER Y */  
	if (std:: clog << a_res) a_code = +EXIT_SUCCESS; 
	else { perror ("write"); a_code = +EXIT_FAILURE; } 
		/* ?MODEM_BLOCK_NO_TOGGLE */
      else /* ?MODEM_ANSWER N */ { perror ("read"); a_code = +EXIT_FAILURE; } 
      /* ?MODEM_ANSWER */ } /* ?MODEM_BLOCK_NO_TOGGLE */
      while (+a_code == +EXIT_SUCCESS); /* @ANSWERS */ } 
      /* ?MODEM_NO_COMMAND */ /* @ONCE X */ /* ?MODEM_NO_BLOCK */ } 
      /* ?MODEM_NO_FILENO */
	if (+std:: fclose (a_modem)) { perror ("close"); return +EXIT_FAILURE; } 
	else return +a_code; } 
	else /* ?MODEM_IS_OPEN N */ { perror ("open"); return +EXIT_FAILURE; } 
	/* ?MODEM_IS_OPEN */ } /* !nblk_talk X */