Bumped copyright dates for 2013
[barry.git] / desktop / src / ModemDlg.cc
blob4a8b1cbc2e1afc0c9c46ee0c7afa9838a0b9dbf2
1 ///
2 /// \file ModemDlg.cc
3 /// Dialog class to handle modem functionality
4 ///
6 /*
7 Copyright (C) 2012-2013, 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>
38 #include "wxi18n.h"
40 using namespace std;
42 // begin wxGlade: ::extracode
43 // end wxGlade
46 ModemDlg::ModemDlg(wxWindow* parent,
47 const std::vector<std::string> &peers,
48 const std::string &default_peer,
49 const Barry::Pin &pin)
50 : wxDialog(parent, Dialog_Modem, _W("Modem Kickstart"))
52 bottom_buttons = CreateButtonSizer(wxOK | wxCANCEL);
54 // begin wxGlade: ModemDlg::ModemDlg
55 sizer_5_staticbox = new wxStaticBox(this, -1, _W("Device"));
56 sizer_1_staticbox = new wxStaticBox(this, -1, _W("Providers"));
57 label_2 = new wxStaticText(this, wxID_ANY, _W("For device pin:"));
58 device_label = new wxStaticText(this, wxID_ANY, wxT("3009efe3"));
59 const wxString list_box_1_choices[] = {
60 wxT("barry-minimal"),
61 wxT("barry-rogers"),
62 wxT("barry-testing")
64 list_box_1 = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 3, list_box_1_choices, wxLB_SINGLE);
65 label_1 = new wxStaticText(this, wxID_ANY, _W("Password:"));
66 text_ctrl_1 = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PASSWORD);
68 set_properties();
69 do_layout();
70 // end wxGlade
72 // add all peers to the listbox
73 list_box_1->Clear();
74 int default_index = -1, index = 0;
75 for( std::vector<std::string>::const_iterator i = peers.begin();
76 i != peers.end(); ++i, ++index )
78 if( default_peer == *i )
79 default_index = index;
80 list_box_1->Append(wxString(i->c_str(), wxConvUTF8));
83 if( default_index >= 0 ) {
84 list_box_1->SetSelection(default_index);
86 else {
87 list_box_1->SetSelection(0);
90 // set the PIN value
91 device_label->SetLabel(wxString(pin.Str().c_str(), wxConvUTF8));
94 void ModemDlg::set_properties()
96 // begin wxGlade: ModemDlg::set_properties
97 SetTitle(_W("Modem Starter"));
98 device_label->SetMinSize(wxSize(100, -1));
99 list_box_1->SetMinSize(wxSize(-1, 150));
100 list_box_1->SetToolTip(_W("Available provider scripts on your system. Must pick one."));
101 list_box_1->SetSelection(0);
102 label_1->SetToolTip(_W("Optional device password"));
103 text_ctrl_1->SetMinSize(wxSize(150, -1));
104 text_ctrl_1->SetToolTip(_W("Optional device password"));
105 // end wxGlade
109 void ModemDlg::do_layout()
111 // begin wxGlade: ModemDlg::do_layout
112 wxBoxSizer* sizer_2 = new wxBoxSizer(wxHORIZONTAL);
113 wxBoxSizer* sizer_3 = new wxBoxSizer(wxVERTICAL);
114 wxStaticBoxSizer* sizer_5 = new wxStaticBoxSizer(sizer_5_staticbox, wxHORIZONTAL);
115 wxStaticBoxSizer* sizer_1 = new wxStaticBoxSizer(sizer_1_staticbox, wxVERTICAL);
116 wxBoxSizer* sizer_4 = new wxBoxSizer(wxHORIZONTAL);
117 sizer_4->Add(label_2, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 2);
118 sizer_4->Add(device_label, 0, wxLEFT|wxALIGN_CENTER_VERTICAL, 10);
119 sizer_3->Add(sizer_4, 0, wxBOTTOM|wxEXPAND, 5);
120 sizer_1->Add(list_box_1, 0, wxEXPAND, 0);
121 sizer_3->Add(sizer_1, 0, wxEXPAND, 0);
122 sizer_5->Add(label_1, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 2);
123 sizer_5->Add(text_ctrl_1, 0, 0, 0);
124 sizer_3->Add(sizer_5, 1, wxEXPAND, 0);
125 sizer_2->Add(sizer_3, 0, wxALL|wxEXPAND, 7);
126 // end wxGlade
128 sizer_3->Add(bottom_buttons, 0, wxTOP|wxEXPAND, 5);
130 SetSizer(sizer_2);
131 sizer_2->Fit(this);
132 Layout();
136 // ProgramDetect
138 /// Searches for the given program name, in various directories, and
139 /// stores the following info:
140 /// Exists() - file exists, and if true, then:
141 /// GetPath() - returns full path of file, and:
142 /// IsExecutable() - returns true if file is a runnable program
143 /// IsSuid() - returns true if file is suid
144 /// GetGroup() - returns the group name of the file
146 /// For pppd, for example, this may return on Debian:
147 /// exists? true
148 /// path? /usr/sbin/pppd
149 /// executable? true if user in group, false if not
150 /// suid? true, suid root on Debian
151 /// group? uses the 'dip' group on Debian
153 /// On Fedora:
154 /// exists? true
155 /// path? /usr/sbin/pppd
156 /// exec? true
157 /// suid? false
158 /// group? root
160 class ProgramDetect
162 std::string m_path;
163 std::string m_group;
164 bool m_executable;
165 bool m_suid;
167 public:
168 void Dump(std::ostream &os)
170 os << "Path: " << m_path << "\n";
171 os << "Group: " << m_group << "\n";
172 os << "Exec: " << (m_executable ? "true" : "false") << "\n";
173 os << "Suid: " << (m_suid ? "true" : "false") << "\n";
176 const std::string& GetPath() const { return m_path; }
177 const std::string& GetGroup() const { return m_group; }
178 bool Exists() const { return m_path.size() > 0; }
179 bool IsExecutable() const { return m_executable; }
180 bool IsSuid() const { return m_suid; }
182 bool CheckPath(const std::string &path)
184 if( access(path.c_str(), F_OK) == 0 ) {
185 m_path = path;
186 return true;
188 return false;
191 void CheckAll(const char *prog, const std::string &path)
193 istringstream iss(path);
194 string p;
196 while( getline(iss, p, ':') ) {
197 string f = p + "/" + prog;
198 if( CheckPath(f) )
199 return;
203 ProgramDetect(const char *prog, const std::string &path)
204 : m_executable(false)
205 , m_suid(false)
207 // find the program first
208 CheckAll(prog, path);
210 // does it exist?
211 if( !m_path.size() )
212 return; // nope
214 // executable?
215 if( access(m_path.c_str(), X_OK) == 0 ) {
216 m_executable = true;
219 // suid?
220 struct stat s;
221 if( stat(m_path.c_str(), &s) == 0 ) {
222 if( s.st_mode & S_ISUID ) {
223 m_suid = true;
227 struct group *g = getgrgid(s.st_gid);
228 if( g ) {
229 m_group = g->gr_name;
235 std::string ModemDlg::GetPeerName() const
237 return string(list_box_1->GetStringSelection().utf8_str());
240 std::string ModemDlg::GetPassword() const
242 return string(text_ctrl_1->GetValue().utf8_str());
245 void ModemDlg::DoModem(wxWindow *parent, const Barry::Pin &pin)
248 // Search for dependency programs: xterm, pppd, gksu, etc.
251 // test whether xterm is in the path
252 ProgramDetect xterm("xterm", getenv("PATH"));
253 if( !xterm.Exists() || !xterm.IsExecutable() ) {
254 wxMessageBox(_W("Cannot locate the xterm program. This is used to display modem connection output. Please install it and try again."), _W("Xterm Not Found"), wxOK | wxICON_ERROR, parent);
255 return;
258 // test whether we can run pppd, probably need to use full
259 // /usr/sbin/pppd path to reach it... check for return code
260 // of 0 or 2
261 ProgramDetect pppd("pppd", "/usr/sbin:/usr/bin");
262 if( !pppd.Exists() ) {
263 wxMessageBox(_W("Cannot find pppd. Please install it for modem use."), _W("pppd Not Found"), wxOK | wxICON_ERROR, parent);
264 return;
269 // Check if we need root access to run pppd
271 string need_sudo;
273 if( pppd.IsExecutable() && pppd.IsSuid() ) {
274 // all good!
276 else if( !pppd.IsExecutable() && pppd.IsSuid() && pppd.GetGroup() != "root" ) {
277 wxString gname(pppd.GetGroup().c_str(), wxConvUTF8);
278 wxString msg = wxString::Format(_W("Your system's PPP has the suid bit set, with a group of '%s'. You should add your account to the '%s' group, to avoid the need to enter the root password every time you use the modem.\n\nContinue anyway?"), gname.c_str(), gname.c_str());
279 int choice = wxMessageBox(msg, _W("System Group"), wxYES_NO,
280 parent);
281 if( choice != wxYES )
282 return;
284 need_sudo = BARRYDESKTOP_SYSTEM_GUI_SU + string(" ");
286 else if( pppd.IsExecutable() && !pppd.IsSuid() ) {
287 need_sudo = BARRYDESKTOP_SYSTEM_GUI_SU + string(" ");
291 // Check if we can run pppd, in the non-root case
293 if( need_sudo.size() == 0 ) {
294 ExecHelper eh(0);
295 wxString cmd((pppd.GetPath() + " permission_test").c_str(), wxConvUTF8);
296 if( !eh.Run(0, "", cmd) ) {
297 wxMessageBox(_W("Internal fork error. Should never happen."), _W("Cannot Run pppd"), wxOK | wxICON_ERROR, parent);
298 return;
301 eh.WaitForChild();
302 if( !(eh.GetChildExitCode() == 0 || eh.GetChildExitCode() == 2) ) {
303 wxString msg = wxString::Format(_W("Unable to run pppd correctly. Unexpected error code: %d, %s"), eh.GetChildExitCode(), cmd.c_str());
304 wxMessageBox(msg, _W("Error Code"), wxOK | wxICON_ERROR, parent);
305 return;
310 // Load all peer files.
312 // do a search in /etc/ppp/peers for all barry-* files and
313 // store in a vector
315 std::vector<std::string> peers;
316 wxDir dir(wxString("/etc/ppp/peers", wxConvUTF8));
317 if( !dir.IsOpened() ) {
318 wxMessageBox(_W("Unable to access files in /etc/ppp/peers. Do you have the correct permissions?"), _W("Cannot Open Peers"), wxOK | wxICON_ERROR, parent);
319 return;
321 wxString filename;
322 bool cont = dir.GetFirst(&filename, _T("barry-*"), wxDIR_FILES);
323 while( cont ) {
324 peers.push_back( string(filename.utf8_str()) );
325 cont = dir.GetNext(&filename);
328 // anything available?
329 if( !peers.size() ) {
330 wxMessageBox(_W("No providers found. Make sure Barry was properly installed, with peer files in /etc/ppp/peers."), _W("No Providers"), wxOK | wxICON_ERROR, parent);
331 return;
334 // sort the vector
335 sort(peers.begin(), peers.end());
339 // Double check that we can read the peer files
342 // do an access or file open test on the first barry-* file
343 // to make sure that we have access to peers/
344 string testfile = "/etc/ppp/peers/" + peers[0];
345 if( access(testfile.c_str(), R_OK) != 0 ) {
346 wxString msg = wxString::Format(_W("Cannot read provider files under /etc/ppp/peers. Please check your file permissions. (Access failed for %s)"), wxString(testfile.c_str(), wxConvUTF8).c_str());
347 wxMessageBox(msg, _W("Permissions Error"), wxOK | wxICON_ERROR, parent);
348 return;
353 // Fetch default peer choice
355 Barry::GlobalConfigFile &config = wxGetApp().GetGlobalConfig();
356 string key = pin.Str() + "-DefaultPeer";
357 string default_peer = config.GetKey(key);
361 // Show the dialog
363 ModemDlg dlg(parent, peers, default_peer, pin);
364 if( dlg.ShowModal() == wxID_OK ) {
365 string peer = dlg.GetPeerName();
366 string peerfile = "/etc/ppp/peers/" + peer;
367 if( !peer.size() || access(peerfile.c_str(), R_OK) != 0 ) {
368 wxString msg = wxString::Format(_W("Unable to open peer file: %s"), wxString(peerfile.c_str(), wxConvUTF8).c_str());
369 wxMessageBox(msg, _W("Invalid Peer"), wxOK | wxICON_ERROR, parent);
370 return;
373 // save peer selection as default for this device
374 config.SetKey(key, peer);
376 // create shell script which calls pppd
377 TempDir tempdir("BarryDesktopModem");
378 string tmp_path = tempdir.GetNewFilename();
380 ofstream otmp(tmp_path.c_str());
381 otmp << "#!/bin/sh" << endl;
382 otmp << "echo " << _C("Starting pppd for device PIN ")
383 << pin.Str() << "... " << endl;
385 ostringstream cmdoss;
386 cmdoss << need_sudo << pppd.GetPath() << " call " << peer;
388 // show command with echo too
389 otmp << "echo" << endl;
390 otmp << "echo '" << cmdoss.str() << "'" << endl;
391 otmp << cmdoss.str() << endl;
393 otmp << "echo " << _C("Press enter to close window...") << endl;
394 otmp << "read" << endl;
395 otmp.close();
397 chmod(tmp_path.c_str(), S_IRUSR | S_IRGRP | S_IROTH |
398 S_IXUSR | S_IXGRP | S_IXOTH);
400 // create command line using xterm as display
401 wxString xterm_cmd((xterm.GetPath() + " " + tmp_path).c_str(),
402 wxConvUTF8);
404 // setup argument fifo
405 Barry::FifoArgs args;
406 args.m_password = dlg.GetPassword();
407 args.m_pin = pin;
408 Barry::FifoServer fifo(args);
410 // run! and go back to main screen
411 ExecHelper run(0);
412 run.Run(parent, "modem", xterm_cmd);
414 fifo.Serve(5);