desktop: added modem support
[barry.git] / desktop / src / ModemDlg.cc
blobb5d9a3e10453f525ca23091a7158769316655018
1 ///
2 /// \file ModemDlg.cc
3 /// Dialog class to handle modem functionality
4 ///
6 /*
7 Copyright (C) 2012, Net Direct Inc. (http://www.netdirect.ca/)
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License in the COPYING file at the
19 root directory of this project for more details.
22 #include "ModemDlg.h"
23 #include "windowids.h"
24 #include "exechelper.h"
25 #include "barrydesktop.h"
26 #include "tempdir.h"
27 #include <iostream>
28 #include <sstream>
29 #include <algorithm>
30 #include <unistd.h>
31 #include <sys/types.h>
32 #include <grp.h>
33 #include <limits.h>
34 #include <stdlib.h>
35 #include <wx/dir.h>
36 #include <wx/filename.h>
37 #include <barry/barry.h>
39 using namespace std;
41 // begin wxGlade: ::extracode
42 // end wxGlade
45 ModemDlg::ModemDlg(wxWindow* parent,
46 const std::vector<std::string> &peers,
47 const std::string &default_peer)
48 : wxDialog(parent, Dialog_Modem, _T("Modem Kickstart"))
50 bottom_buttons = CreateButtonSizer(wxOK | wxCANCEL);
52 // begin wxGlade: ModemDlg::ModemDlg
53 sizer_5_staticbox = new wxStaticBox(this, -1, wxT("Device"));
54 sizer_1_staticbox = new wxStaticBox(this, -1, wxT("Providers"));
55 const wxString list_box_1_choices[] = {
56 wxT("barry-minimal"),
57 wxT("barry-rogers"),
58 wxT("barry-testing")
60 list_box_1 = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 3, list_box_1_choices, wxLB_SINGLE);
61 label_1 = new wxStaticText(this, wxID_ANY, wxT("Password:"));
62 text_ctrl_1 = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PASSWORD);
64 set_properties();
65 do_layout();
66 // end wxGlade
68 // add all peers to the listbox
69 list_box_1->Clear();
70 int default_index = -1, index = 0;
71 for( std::vector<std::string>::const_iterator i = peers.begin();
72 i != peers.end(); ++i, ++index )
74 if( default_peer == *i )
75 default_index = index;
76 list_box_1->Append(wxString(i->c_str(), wxConvUTF8));
79 if( default_index >= 0 ) {
80 list_box_1->SetSelection(default_index);
82 else {
83 list_box_1->SetSelection(0);
87 void ModemDlg::set_properties()
89 // begin wxGlade: ModemDlg::set_properties
90 SetTitle(wxT("Modem Starter"));
91 list_box_1->SetMinSize(wxSize(-1, 150));
92 list_box_1->SetSelection(0);
93 text_ctrl_1->SetMinSize(wxSize(150, -1));
94 // end wxGlade
98 void ModemDlg::do_layout()
100 // begin wxGlade: ModemDlg::do_layout
101 wxBoxSizer* sizer_2 = new wxBoxSizer(wxHORIZONTAL);
102 wxBoxSizer* sizer_3 = new wxBoxSizer(wxVERTICAL);
103 wxStaticBoxSizer* sizer_5 = new wxStaticBoxSizer(sizer_5_staticbox, wxHORIZONTAL);
104 wxStaticBoxSizer* sizer_1 = new wxStaticBoxSizer(sizer_1_staticbox, wxVERTICAL);
105 sizer_1->Add(list_box_1, 0, wxEXPAND, 0);
106 sizer_3->Add(sizer_1, 0, wxEXPAND, 0);
107 sizer_5->Add(label_1, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 2);
108 sizer_5->Add(text_ctrl_1, 0, 0, 0);
109 sizer_3->Add(sizer_5, 1, wxEXPAND, 0);
110 sizer_2->Add(sizer_3, 0, wxALL|wxEXPAND, 7);
111 // end wxGlade
113 sizer_3->Add(bottom_buttons, 0, wxTOP|wxEXPAND, 5);
115 SetSizer(sizer_2);
116 sizer_2->Fit(this);
117 Layout();
121 // ProgramDetect
123 /// Searches for the given program name, in various directories, and
124 /// stores the following info:
125 /// Exists() - file exists, and if true, then:
126 /// GetPath() - returns full path of file, and:
127 /// IsExecutable() - returns true if file is a runnable program
128 /// IsSuid() - returns true if file is suid
129 /// GetGroup() - returns the group name of the file
131 /// For pppd, for example, this may return on Debian:
132 /// exists? true
133 /// path? /usr/sbin/pppd
134 /// executable? true if user in group, false if not
135 /// suid? true, suid root on Debian
136 /// group? uses the 'dip' group on Debian
138 /// On Fedora:
139 /// exists? true
140 /// path? /usr/sbin/pppd
141 /// exec? true
142 /// suid? false
143 /// group? root
145 class ProgramDetect
147 std::string m_path;
148 std::string m_group;
149 bool m_executable;
150 bool m_suid;
152 public:
153 void Dump(std::ostream &os)
155 os << "Path: " << m_path << "\n";
156 os << "Group: " << m_group << "\n";
157 os << "Exec: " << (m_executable ? "true" : "false") << "\n";
158 os << "Suid: " << (m_suid ? "true" : "false") << "\n";
161 const std::string& GetPath() const { return m_path; }
162 const std::string& GetGroup() const { return m_group; }
163 bool Exists() const { return m_path.size() > 0; }
164 bool IsExecutable() const { return m_executable; }
165 bool IsSuid() const { return m_suid; }
167 bool CheckPath(const std::string &path)
169 if( access(path.c_str(), F_OK) == 0 ) {
170 m_path = path;
171 return true;
173 return false;
176 void CheckAll(const char *prog, const std::string &path)
178 istringstream iss(path);
179 string p;
181 while( getline(iss, p, ':') ) {
182 string f = p + "/" + prog;
183 if( CheckPath(f) )
184 return;
188 ProgramDetect(const char *prog, const std::string &path)
189 : m_executable(false)
190 , m_suid(false)
192 // find the program first
193 CheckAll(prog, path);
195 // does it exist?
196 if( !m_path.size() )
197 return; // nope
199 // executable?
200 if( access(m_path.c_str(), X_OK) == 0 ) {
201 m_executable = true;
204 // suid?
205 struct stat s;
206 if( stat(m_path.c_str(), &s) == 0 ) {
207 if( s.st_mode & S_ISUID ) {
208 m_suid = true;
212 struct group *g = getgrgid(s.st_gid);
213 if( g ) {
214 m_group = g->gr_name;
220 std::string ModemDlg::GetPeerName() const
222 return string(list_box_1->GetStringSelection().utf8_str());
225 std::string ModemDlg::GetPassword() const
227 return string(text_ctrl_1->GetValue().utf8_str());
230 void ModemDlg::DoModem(wxWindow *parent, const Barry::Pin &pin)
232 // test whether xterm is in the path
233 ProgramDetect xterm("xterm", getenv("PATH"));
234 if( !xterm.Exists() || !xterm.IsExecutable() ) {
235 wxMessageBox(_T("Cannot locate the xterm program. This is used to display modem connection output. Please install it and try again."), _T("Xterm Not Found"), wxOK | wxICON_ERROR, parent);
236 return;
239 // in preparation for pppob search, find sbin, relative to our
240 // current application path
241 wxFileName app_path(wxTheApp->argv[0]);
242 string app_sbin(app_path.GetPath().utf8_str());
243 app_sbin += "/../sbin";
244 if( app_sbin[0] != '/' ) {
245 char cwdbuf[PATH_MAX];
246 getcwd(cwdbuf, PATH_MAX);
248 // insert current directory
249 app_sbin.insert(0, string(cwdbuf) + "/");
251 char *app_real = realpath(app_sbin.c_str(), NULL);
252 app_sbin = app_real;
253 free(app_real);
254 cout << "app_sbin = " << app_sbin << endl;
256 // test whether pppob is available... use app directory as
257 // part of search as well, in case Barry is installed in an
258 // unusual place
259 ProgramDetect pppob("pppob", app_sbin + ":/usr/sbin:" + getenv("PATH"));
260 if( !pppob.Exists() || !pppob.IsExecutable() ) {
261 wxMessageBox(_T("Cannot find pppob. Please make sure Barry's command line utilities are installed."), _T("pppob Not Found"), wxOK | wxICON_ERROR, parent);
262 return;
264 cout << pppob.GetPath() << endl;
266 // test whether we can run pppd, probably need to use full
267 // /usr/sbin/pppd path to reach it... check for return code
268 // of 0 or 2
269 ProgramDetect pppd("pppd", "/usr/sbin:/usr/bin");
270 if( !pppd.Exists() ) {
271 wxMessageBox(_T("Cannot find pppd. Please install it for modem use."), _T("pppd Not Found"), wxOK | wxICON_ERROR, parent);
272 return;
275 string need_sudo;
277 if( !(pppd.IsSuid() && pppd.GetGroup() != "root") ) {
278 // No group / suid setup, so need sudo
279 // Use gtk's gui sudo, since we're using GTK's wxwidgets
280 // and probably safe here
281 // FIXME - need a portable method for all of this someday :-(
282 need_sudo = "gksu ";
285 ExecHelper eh(0);
286 wxString cmd((need_sudo + pppd.GetPath() + " help").c_str(), wxConvUTF8);
287 if( !eh.Run(0, "", cmd) ) {
288 if( need_sudo.size() ) {
289 wxMessageBox(_T("Unable to run pppd. Please make sure you have it enabled in your sudo config, or put pppd in a group of its own."), _T("Cannot Run pppd"), wxOK | wxICON_ERROR, parent);
291 else {
292 wxString msg = wxString::Format(_T("Unable to run pppd. Please make sure your user account is included in the '%s' group."), wxString(pppd.GetGroup().c_str(), wxConvUTF8).c_str());
294 wxMessageBox(msg, _T("Cannot Run pppd"), wxOK | wxICON_ERROR, parent);
297 return;
300 eh.WaitForChild();
301 if( !(eh.GetChildExitCode() == 0 || eh.GetChildExitCode() == 2) ) {
302 wxString msg = wxString::Format(_T("Unable to run pppd correctly. Unexpected error code: %d, %s"), eh.GetChildExitCode(), cmd.c_str());
303 wxMessageBox(msg, _T("Error Code"), wxOK | wxICON_ERROR, parent);
304 return;
307 // do a search in /etc/ppp/peers for all barry-* files and
308 // store in a vector
309 std::vector<std::string> peers;
310 wxDir dir(wxString("/etc/ppp/peers", wxConvUTF8));
311 if( !dir.IsOpened() ) {
312 wxMessageBox(_T("Unable to access files in /etc/ppp/peers. Do you have the correct permissions?"), _T("Cannot Open Peers"), wxOK | wxICON_ERROR, parent);
313 return;
315 wxString filename;
316 bool cont = dir.GetFirst(&filename, _T("barry-*"), wxDIR_FILES);
317 while( cont ) {
318 peers.push_back( string(filename.utf8_str()) );
319 cont = dir.GetNext(&filename);
322 // anything available?
323 if( !peers.size() ) {
324 wxMessageBox(_T("No providers found. Make sure Barry was properly installed, with peer files in /etc/ppp/peers."), _T("No Providers"), wxOK | wxICON_ERROR, parent);
325 return;
328 // sort the vector
329 sort(peers.begin(), peers.end());
331 // do an access or file open test on the first barry-* file
332 // to make sure that we have access to peers/
333 string testfile = "/etc/ppp/peers/" + peers[0];
334 if( access(testfile.c_str(), R_OK) != 0 ) {
335 wxString msg = wxString::Format(_T("Cannot read provider files under /etc/ppp/peers. Please check your file permissions. (Access failed for %s)"), wxString(testfile.c_str(), wxConvUTF8).c_str());
336 wxMessageBox(msg, _T("Permissions Error"), wxOK | wxICON_ERROR, parent);
337 return;
340 // fetch default peer choice
341 Barry::GlobalConfigFile &config = wxGetApp().GetGlobalConfig();
342 string key = pin.Str() + "-DefaultPeer";
343 string default_peer = config.GetKey(key);
345 // show the dialog
346 ModemDlg dlg(parent, peers, default_peer);
347 if( dlg.ShowModal() == wxID_OK ) {
348 string password = dlg.GetPassword();
349 string peer = dlg.GetPeerName();
350 string peerfile = "/etc/ppp/peers/" + peer;
351 if( !peer.size() || access(peerfile.c_str(), R_OK) != 0 ) {
352 wxString msg = wxString::Format(_T("Unable to open peer file: %s"), wxString(peerfile.c_str(), wxConvUTF8).c_str());
353 wxMessageBox(msg, _T("Invalid Peer"), wxOK | wxICON_ERROR, parent);
354 return;
357 // save peer selection as default for this device
358 config.SetKey(key, peer);
360 // create shell script which calls pppd with proper
361 // pty pppob override, and password if needed
362 TempDir tempdir("BarryDesktopModem");
363 string tmp_path = tempdir.GetNewFilename();
365 ofstream otmp(tmp_path.c_str());
366 otmp << "#!/bin/sh" << endl;
367 otmp << "echo Starting pppd for device PIN "
368 << pin.Str() << "... " << endl;
369 // otmp << need_sudo << pppd.GetPath()
370 // FIXME - need gksu here, for the pty override :-( Need method to
371 // pass options to pppob without root
372 otmp << "gksu " << pppd.GetPath()
373 << " call " << peer
374 << " pty \"" << pppob.GetPath()
375 << " -p " << pin.Str();
376 if( password.size() )
377 otmp << " -P " << password;
378 otmp << '"' << endl;
379 otmp << "echo Press enter to close window..." << endl;
380 otmp << "read" << endl;
381 otmp.close();
383 chmod(tmp_path.c_str(), S_IRUSR | S_IRGRP | S_IROTH |
384 S_IXUSR | S_IXGRP | S_IXOTH);
386 // create command line using xterm as display
387 wxString xterm_cmd((xterm.GetPath() + " " + tmp_path).c_str(),
388 wxConvUTF8);
390 // run! and go back to main screen
391 ExecHelper run(0);
392 run.Run(parent, "modem", xterm_cmd);
394 // let run, then let TempDir cleanup
395 sleep(2);