PageRenderTime 98ms CodeModel.GetById 77ms app.highlight 15ms RepoModel.GetById 2ms app.codeStats 0ms

/admin/models/system.php

https://github.com/Excito/bubba-frontend
PHP | 488 lines | 444 code | 40 blank | 4 comment | 49 complexity | 83968719ae1016a9c72e19459dcfaf26 MD5 | raw file
  1<?php
  2require_once 'HTTP/Request2.php';
  3
  4class System extends CI_Model {
  5  private $version;
  6  public function __construct() {
  7    parent::__construct();
  8  }
  9
 10  public function set_timezone($timezone) {
 11
 12    $target = "/usr/share/zoneinfo/$timezone";
 13    if(!file_exists($target)) {
 14      throw new Exception("Timezone $timezone doesn't exists");
 15    }
 16    unlink('/etc/localtime');
 17    symlink($target, '/etc/localtime');
 18    file_put_contents('/etc/timezone', $timezone);
 19  }
 20
 21  public function get_timezone() {
 22    return trim(file_get_contents('/etc/timezone'));
 23  }
 24
 25  # Lists all timezones with region, UTC has region false
 26  public function list_timezones() {
 27    $timezones = array();
 28    foreach(DateTimeZone::listIdentifiers() as $ts) {
 29      if(strpos($ts,'/')) {
 30        list($region, $country) = explode('/', $ts);
 31        $timezones[$country] = $region;
 32      } else {
 33        $timezones[$ts] = false;
 34      }
 35    }
 36    ksort($timezones);
 37    return $timezones;
 38  }
 39
 40  public function get_raw_uptime() {
 41    $upt=file("/proc/uptime");
 42    sscanf($upt[0],"%d",$secs_tot);
 43    return $secs_tot;
 44  }
 45
 46  public function get_uptime() {
 47    $start = new DateTime();
 48    $start->sub(DateInterval::createFromDateString("{$this->get_raw_uptime()} seconds"));
 49    return $start->diff(new DateTime());
 50  }
 51
 52  public function get_system_version() {
 53    if(!$this->version) {
 54      $this->version = file_get_contents(BUBBA_VERSION);
 55    }
 56    return $this->version;
 57  }
 58
 59  public function get_hardware_id() {
 60    return getHWType();
 61  }
 62
 63  public function list_printers() {
 64    $json =  _system('cups-list-printers');
 65    return json_decode(implode($json),true);
 66  }
 67
 68  const accounts_file = '/etc/bubba/remote_accounts.yml';
 69  const remote_jobs_file = '/etc/bubba/remote_backup_jobs.yml';
 70  const fstab_file = '/etc/fstab';
 71  const webdav_secrets_file  = '/etc/davfs2/secrets';
 72  const ssh_keydir = '/etc/bubba/ssh-keys';
 73
 74  # faithfully stolen from http://stackoverflow.com/questions/2040240/php-function-to-generate-v4-uuid
 75  private function gen_uuid() {
 76    return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
 77      // 32 bits for "time_low"
 78      mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
 79
 80      // 16 bits for "time_mid"
 81      mt_rand( 0, 0xffff ),
 82
 83      // 16 bits for "time_hi_and_version",
 84      // four most significant bits holds version number 4
 85      mt_rand( 0, 0x0fff ) | 0x4000,
 86
 87      // 16 bits, 8 bits for "clk_seq_hi_res",
 88      // 8 bits for "clk_seq_low",
 89      // two most significant bits holds zero and one for variant DCE1.1
 90      mt_rand( 0, 0x3fff ) | 0x8000,
 91
 92      // 48 bits for "node"
 93      mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )
 94    );
 95  }
 96  private function create_ssh_key($uuid) {
 97    $priv_path = implode(DIRECTORY_SEPARATOR, array(self::ssh_keydir, $uuid));
 98    if(file_exists($priv_path)) {
 99      # XXX forcefully removing old key
100      @unlink($priv_path);
101      @unlink($priv_path.".pub");
102    }
103    _system("ssh-keygen", '-f', $priv_path, '-N', '', '-q');
104    $pubkey = file_get_contents($priv_path.'.pub');
105    return $pubkey;
106  }
107
108  public function get_pubkey($uuid) {
109    $priv_path = implode(DIRECTORY_SEPARATOR, array(self::ssh_keydir, $uuid));
110    $pub_path = $priv_path.'.pub';
111    $pubkey = file_get_contents($pub_path);
112    return $pubkey;
113  }
114
115  private function upload_sshkey($host, $username, $password, $uuid) {
116    $priv_path = implode(DIRECTORY_SEPARATOR, array(self::ssh_keydir, $uuid));
117    $pub_path = $priv_path.'.pub';
118    $descriptorspec = array(
119      13 => array('pipe', 'r')
120    );
121
122    $process = proc_open("sshpass-copy-id -i $pub_path $username@$host", $descriptorspec, $pipes);
123    fwrite($pipes[13], $password);
124    fclose($pipes[13]);
125    $ret = proc_close($process);
126    error_log("sshpass-copy-id returned $ret!");
127  }
128
129  private $hidrive_token = null;
130  private $hidrive_api = 'https://api.hidrive.strato.com/1.0/';
131
132  public function get_hidrive_token($username, $password, $force = false) {
133    if($this->hidrive_token && !$force) {
134      return $this->hidrive_token;
135    }
136
137    $request = new HTTP_Request2($this->hidrive_api."auth.getToken", HTTP_Request2::METHOD_GET, array('ssl_verify_peer' => false));
138    $request->setAuth($username, $password);
139
140    $response = $request->send();
141    if($response->getStatus() == 200) {
142      $data = json_decode($response->getBody(), true);
143      if(isset($data['status']['code']) && $data['status']['code'] == 0) {
144        $this->hidrive_token =  $data['data']['token'];
145        return $this->hidrive_token;
146      } else {
147        return false;
148      }
149    }
150  }
151
152  public function verify_hidrive_protocols($token) {
153    $request = new HTTP_Request2($this->hidrive_api."space.getFeatures", HTTP_Request2::METHOD_GET, array('ssl_verify_peer' => false));
154    $request->setHeader('X-Auth-Token', $token);
155
156    $response = $request->send();
157    if($response->getStatus() == 200) {
158      $data = json_decode($response->getBody(), true);
159      if(isset($data['status']['code']) && $data['status']['code'] == 0) {
160        $protocols = $data['data']['protocols'];
161        return $protocols['rsync'] && $protocols['webdav'];
162      }
163    }
164  }
165
166  public function verify_hidrive_space($token, $required) {
167    $request = new HTTP_Request2($this->hidrive_api."quota.list", HTTP_Request2::METHOD_GET, array('ssl_verify_peer' => false));
168    $request->setHeader('X-Auth-Token', $token);
169
170    $response = $request->send();
171    if($response->getStatus() == 200) {
172      $data = json_decode($response->getBody(), true);
173      if(isset($data['status']['code']) && $data['status']['code'] == 0) {
174        $root = $data['data']['root'];
175        return $required < $root['available'];
176      }
177    }
178  }
179
180
181  public function add_remote_account($type, $username, $password, $host) {
182
183    # Check HiDrive for permissions
184    # https://api.freemium.stg.rzone.de/ contains incomplete api spec
185    if($type == 'HiDrive') {
186      $token = $this->get_hidrive_token($username, $password);
187      if(!$token) {
188        throw new Exception(_("Unable to verify credentials"));
189      }
190      if(!$this->verify_hidrive_protocols($token)) {
191        throw new Exception(_("Selected HiDrive account doesn't have the stuff required for this system"));
192      }
193    }
194
195    $accounts = array();
196    if(file_exists(self::accounts_file)) {
197      $accounts = spyc_load_file(self::accounts_file);
198    }
199
200
201    $arr = array(
202      'type' => $type,
203      'username' => $username,
204      'password' => $password
205    );
206    if($host) {
207      $arr['host'] = $host;
208      $key = "$type|$host|$username";
209    } else {
210      $key = "$type|$username";
211    }
212
213    if(isset($accounts[$key])) {
214      throw new Exception(_('Account allready defined'));
215    }
216    $uuid = $this->gen_uuid();
217    $pubkey = $this->create_ssh_key($uuid);
218    $arr['uuid'] = $uuid;
219
220    $accounts[$key] = $arr;
221    file_put_contents(self::accounts_file,Spyc::YAMLDump($accounts));
222    if($type == 'ssh') {
223      $this->upload_sshkey($host, $username, $password, $uuid);
224    }
225    return array('key' => $key, 'uuid' => $uuid, 'pubkey' => $pubkey);
226  }
227
228  public function remove_remote_account($type, $username, $host) {
229    if($host) {
230      $target = "$type|$host|$username";
231    } else {
232      $target = "$type|$username";
233    }
234
235    $jobs = array();
236    if(file_exists(self::remote_jobs_file)) {
237      $jobs = spyc_load_file(self::remote_jobs_file);
238    }
239    unset($jobs[$target]);
240    file_put_contents(self::remote_jobs_file,Spyc::YAMLDump($jobs));
241
242    $accounts = array();
243    if(file_exists(self::accounts_file)) {
244      $accounts = spyc_load_file(self::accounts_file);
245    }
246    unset($accounts[$target]);
247    file_put_contents(self::accounts_file,Spyc::YAMLDump($accounts));
248    return true;
249  }
250
251
252  public function ssh_edit_remote_account($key, $username, $host) {
253
254    $jobs = array();
255    if(file_exists(self::remote_jobs_file)) {
256      $jobs = spyc_load_file(self::remote_jobs_file);
257    }
258    $job = $jobs[$key];
259    unset($jobs[$key]);
260
261
262    $accounts = array();
263    if(file_exists(self::accounts_file)) {
264      $accounts = spyc_load_file(self::accounts_file);
265    }
266    $account = $accounts[$key];
267    unset($accounts[$key]);
268
269    $account['username'] = $username;
270    $account['host'] = $host;
271
272    $newkey = "ssh|$host|$username";
273    $accounts[$newkey] = $account;
274
275    $jobs[$newkey] = $job;
276
277    file_put_contents(self::accounts_file,Spyc::YAMLDump($accounts));
278
279    file_put_contents(self::remote_jobs_file,Spyc::YAMLDump($jobs));
280
281    return $newkey;
282
283  }
284
285  public function get_remote_accounts() {
286    $targets = array();
287    if(file_exists(self::accounts_file)) {
288      $accounts = spyc_load_file(self::accounts_file);
289      foreach($accounts as $id => $account) {
290        $target = array(
291          'id' => $id,
292          'uuid' => $account['uuid'],
293          'type' => $account['type'],
294          'username' => $account['username'],
295        );
296        if(isset($account['host'])) {
297          $target['host'] = $account['host'];
298        }
299        $targets[] = $target;
300      }
301    }
302    return $targets;
303  }
304
305  public function get_remote_account($key) {
306    $target = array();
307    if(file_exists(self::accounts_file)) {
308      $accounts = spyc_load_file(self::accounts_file);
309      foreach($accounts as $id => $account) {
310        if($id != $key) {
311          continue;
312        }
313        $target = array(
314          'id' => $id,
315          'type' => $account['type'],
316          'username' => $account['username'],
317          'uuid' => $account['uuid'],
318        );
319        if(isset($account['host'])) {
320          $target['host'] = $account['host'];
321        }
322      }
323    }
324    return $target;
325  }
326
327  public function get_webdav_path($type, $username) {
328    return "/home/admin/$type/$username";
329  }
330
331  public function create_webdav_path($type, $username) {
332    $path = "/home/admin/$type";
333    if(! file_exists($path) ) {
334      mkdir($path, 0700);
335      chown($path, 'admin');
336      chgrp($path, 'admin');
337    }
338    $path = "/home/admin/$type/$username";
339    if(! file_exists($path) ) {
340      mkdir($path, 0700);
341      chown($path, 'admin');
342      chgrp($path, 'admin');
343    }
344  }
345
346  public function get_webdav_url($type) {
347    switch($type) {
348    case 'HiDrive':
349      return 'http://webdav.hidrive.strato.com';
350    }
351  }
352
353  private function is_mounted($path) {
354    exec("mountpoint -q " . escapeshellarg($path), $out, $ret);
355    return $ret == 0;
356  }
357  public function add_webdav($type, $username, $password) {
358    $url = $this->get_webdav_url($type);
359    $path = $this->get_webdav_path($type, $username);
360
361    if($this->is_mounted($path)) {
362      _system("umount", '-f', $path);
363    }
364    $oldsecrets = file_get_contents(self::webdav_secrets_file);
365
366    # Remove old path if allready there
367    $secrets = preg_replace("#^".preg_quote($path).".*#m", "", $oldsecrets);
368
369    $secrets .= sprintf("\n%s\t\"%s\"\t\"%s\"\n", addslashes($path), addslashes($username), addslashes($password));
370
371    if($oldsecrets != $secrets) {
372      file_put_contents(self::webdav_secrets_file, $secrets);
373    }
374
375    $oldfstab = file_get_contents(self::fstab_file);
376    $fstab = preg_replace("#^".preg_quote($url)."\s+".preg_quote($path).".*#m", "", $oldfstab);
377
378    $fstab .= "$url $path davfs defaults,gid=users,dir_mode=775,file_mode=664,_netdev 0 0\n";
379
380    if(! file_exists($path) ) {
381      $this->create_webdav_path($type, $username);
382    }
383
384    if($fstab != $oldfstab) {
385      file_put_contents(self::fstab_file, $fstab);
386      _system("mount", "-a");
387    }
388
389  }
390
391  public function remove_webdav($type, $username) {
392    $url = $this->get_webdav_url($type);
393    $path = $this->get_webdav_path($type, $username);
394    if($this->is_mounted($path)) {
395      _system("umount", '-f', $path);
396    }
397    $oldsecrets = file_get_contents(self::webdav_secrets_file);
398
399    # Remove old path if allready there
400    $secrets = preg_replace("#^".preg_quote($path).".*#m", "", $oldsecrets);
401    file_put_contents(self::webdav_secrets_file, $secrets);
402
403    $oldfstab = file_get_contents(self::fstab_file);
404    $fstab = preg_replace("#^".preg_quote($url)."\s+".preg_quote($path).".*#m", "", $oldfstab);
405    if($fstab != $oldfstab) {
406      file_put_contents(self::fstab_file, $fstab);
407      _system("mount", "-a");
408    }
409
410    if( file_exists($path) ) {
411      @rmdir($path);
412    }
413  }
414
415  public function edit_webdav($type, $username, $password) {
416    $this->remove_webdav($type, $username);
417    $this->add_webdav($type, $username, $password);
418  }
419
420
421  public function get_sshfs_path($host, $username) {
422    return "/home/admin/ssh/${username}@${host}";
423  }
424
425  public function create_sshfs_path($host, $username) {
426    $path = "/home/admin/ssh";
427    if(! file_exists($path) ) {
428      @mkdir($path, 0700);
429      chown($path, 'admin');
430      chgrp($path, 'admin');
431    }
432    $path = $this->get_sshfs_path($host,$username);
433    if(! file_exists($path) ) {
434      @mkdir($path, 0700);
435      chown($path, 'admin');
436      chgrp($path, 'admin');
437    }
438  }
439
440  public function move_sshfs($old_host, $old_username, $new_host, $new_username, $uuid) {
441    if( $old_host == $new_host && $old_username == $new_username ) {
442      return;
443    }
444    $this->remove_sshfs($old_hostm, $old_username);
445    $this->add_sshfs($new_username, $new_host, $uuid);
446  }
447
448  public function add_sshfs($host, $username, $uuid) {
449    $sshkey = implode(DIRECTORY_SEPARATOR, array(self::ssh_keydir, $uuid));
450
451    $oldfstab = file_get_contents(self::fstab_file);
452    $fstab = preg_replace("#^sshfs\#".preg_quote($username)."@".preg_quote($host).".*#m", "", $oldfstab);
453    $path = $this->get_sshfs_path($host, $username);
454    if($this->is_mounted($path)) {
455      _system("umount", '-f', $path);
456    }
457
458    $fstab .= "sshfs#$username@$host: $path fuse identityfile=$sshkey,defaults,allow_other,_netdev,gid=users 0 0\n";
459
460    if(! file_exists($path) ) {
461      $this->create_sshfs_path($host, $username);
462    }
463
464    if($fstab != $oldfstab) {
465      file_put_contents(self::fstab_file, $fstab);
466      sleep(5);
467      _system("mount", "-a");
468    }
469
470  }
471
472  public function remove_sshfs($host, $username) {
473    $path = $this->get_sshfs_path($host, $username);
474    if($this->is_mounted($path)) {
475      _system("umount", '-f', $path);
476    }
477
478    $oldfstab = file_get_contents(self::fstab_file);
479    $fstab = preg_replace("#^sshfs\#".preg_quote($username)."@".preg_quote($host).".*#m", "", $oldfstab);
480    if($fstab != $oldfstab) {
481      file_put_contents(self::fstab_file, $fstab);
482      _system("mount", "-a");
483    }
484    if( file_exists($path) ) {
485      @rmdir($path);
486    }
487  }
488}