on-demand release 4.0dev+
[moodle.git] / mnet / peer.php
1 <?php
2 /**
3  * An object to represent lots of information about an RPC-peer machine
4  *
5  * @author  Donal McMullan  donal@catalyst.net.nz
6  * @version 0.0.1
7  * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
8  * @package mnet
9  */
11 require_once($CFG->libdir . '/filelib.php'); // download_file_content() used here
13 class mnet_peer {
15     /** No SSL verification. */
16     const SSL_NONE = 0;
18     /** SSL verification for host. */
19     const SSL_HOST = 1;
21     /** SSL verification for host and peer. */
22     const SSL_HOST_AND_PEER = 2;
24     var $id                 = 0;
25     var $wwwroot            = '';
26     var $ip_address         = '';
27     var $name               = '';
28     var $public_key         = '';
29     var $public_key_expires = 0;
30     var $last_connect_time  = 0;
31     var $last_log_id        = 0;
32     var $force_theme        = 0;
33     var $theme              = '';
34     var $applicationid      = 1; // Default of 1 == Moodle
35     var $keypair            = array();
36     var $error              = array();
37     var $bootstrapped       = false; // set when the object is populated
39     /** @var int $sslverification The level of SSL verification to apply. */
40     public $sslverification = self::SSL_HOST_AND_PEER;
42     /*
43      * Fetch information about a peer identified by wwwroot
44      * If information does not preexist in db, collect it together based on
45      * supplied information
46      *
47      * @param string $wwwroot - address of peer whose details we want
48      * @param string $pubkey - to use if we add a record to db for new peer
49      * @param int $application - table id - what kind of peer are we talking to
50      * @return bool - indication of success or failure
51      */
52     function bootstrap($wwwroot, $pubkey, $application) {
53         global $DB;
55         if (substr($wwwroot, -1, 1) == '/') {
56             $wwwroot = substr($wwwroot, 0, -1);
57         }
59         // If a peer record already exists for this address,
60         // load that info and return
61         if ($this->set_wwwroot($wwwroot)) {
62             return true;
63         }
65         $hostname = mnet_get_hostname_from_uri($wwwroot);
66         // Get the IP address for that host - if this fails, it will return the hostname string
67         $ip_address = gethostbyname($hostname);
69         // Couldn't find the IP address?
70         if ($ip_address === $hostname && !preg_match('/^\d+\.\d+\.\d+.\d+$/',$hostname)) {
71             throw new moodle_exception('noaddressforhost', 'mnet', '', $hostname);
72         }
74         $this->name = $wwwroot;
76         // TODO: In reality, this will be prohibitively slow... need another
77         // default - maybe blank string
78         $homepage = download_file_content($wwwroot);
79         if (!empty($homepage)) {
80             $count = preg_match("@<title>(.*)</title>@siU", $homepage, $matches);
81             if ($count > 0) {
82                 $this->name = $matches[1];
83             }
84         }
86         $this->wwwroot              = $wwwroot;
87         $this->ip_address           = $ip_address;
88         $this->deleted              = 0;
90         $this->application = $DB->get_record('mnet_application', array('name'=>$application));
91         if (empty($this->application)) {
92             $this->application = $DB->get_record('mnet_application', array('name'=>'moodle'));
93         }
95         $this->applicationid = $this->application->id;
97         if(empty($pubkey)) {
98             $this->public_key           = clean_param(mnet_get_public_key($this->wwwroot, $this->application), PARAM_PEM);
99         } else {
100             $this->public_key           = clean_param($pubkey, PARAM_PEM);
101         }
102         $this->public_key_expires   = $this->check_common_name($this->public_key);
103         $this->last_connect_time    = 0;
104         $this->last_log_id          = 0;
105         if ($this->public_key_expires == false) {
106             $this->public_key == '';
107             return false;
108         }
109         $this->bootstrapped = true;
110     }
112     /*
113      * Delete mnet peer
114      * the peer is marked as deleted in the database
115      * we delete current sessions.
116      * @return bool - success
117      */
118     function delete() {
119         global $DB;
121         if ($this->deleted) {
122             return true;
123         }
125         $this->delete_all_sessions();
127         $this->deleted = 1;
128         return $this->commit();
129     }
131     function count_live_sessions() {
132         global $DB;
133         $obj = $this->delete_expired_sessions();
134         return $DB->count_records('mnet_session', array('mnethostid'=>$this->id));
135     }
137     function delete_expired_sessions() {
138         global $DB;
139         $now = time();
140         return $DB->delete_records_select('mnet_session', " mnethostid = ? AND expires < ? ", array($this->id, $now));
141     }
143     function delete_all_sessions() {
144         global $CFG, $DB;
145         // TODO: Expires each PHP session individually
146         $sessions = $DB->get_records('mnet_session', array('mnethostid'=>$this->id));
148         if (count($sessions) > 0 && file_exists($CFG->dirroot.'/auth/mnet/auth.php')) {
149             require_once($CFG->dirroot.'/auth/mnet/auth.php');
150             $auth = new auth_plugin_mnet();
151             $auth->end_local_sessions($sessions);
152         }
154         $deletereturn = $DB->delete_records('mnet_session', array('mnethostid'=>$this->id));
155         return true;
156     }
158     function check_common_name($key) {
159         $credentials = $this->check_credentials($key);
160         return $credentials['validTo_time_t'];
161     }
163     function check_credentials($key) {
164         $credentials = openssl_x509_parse($key);
165         if ($credentials == false) {
166             $this->error[] = array('code' => 3, 'text' => get_string("nonmatchingcert", 'mnet', array('subject' => '','host' => '')));
167             return false;
168         } elseif (array_key_exists('subjectAltName', $credentials['subject']) && $credentials['subject']['subjectAltName'] != $this->wwwroot) {
169             $a['subject'] = $credentials['subject']['subjectAltName'];
170             $a['host'] = $this->wwwroot;
171             $this->error[] = array('code' => 5, 'text' => get_string("nonmatchingcert", 'mnet', $a));
172             return false;
173         } else if ($credentials['subject']['CN'] !== substr($this->wwwroot, 0, 64)) {
174             $a['subject'] = $credentials['subject']['CN'];
175             $a['host'] = $this->wwwroot;
176             $this->error[] = array('code' => 4, 'text' => get_string("nonmatchingcert", 'mnet', $a));
177             return false;
178         } else {
179             if (array_key_exists('subjectAltName', $credentials['subject'])) {
180                 $credentials['wwwroot'] = $credentials['subject']['subjectAltName'];
181             } else {
182                 $credentials['wwwroot'] = $credentials['subject']['CN'];
183             }
184             return $credentials;
185         }
186     }
188     function commit() {
189         global $DB;
190         $obj = new stdClass();
192         $obj->wwwroot               = $this->wwwroot;
193         $obj->ip_address            = $this->ip_address;
194         $obj->name                  = $this->name;
195         $obj->public_key            = $this->public_key;
196         $obj->public_key_expires    = $this->public_key_expires;
197         $obj->deleted               = $this->deleted;
198         $obj->last_connect_time     = $this->last_connect_time;
199         $obj->last_log_id           = $this->last_log_id;
200         $obj->force_theme           = $this->force_theme;
201         $obj->theme                 = $this->theme;
202         $obj->applicationid         = $this->applicationid;
203         $obj->sslverification       = $this->sslverification;
205         if (isset($this->id) && $this->id > 0) {
206             $obj->id = $this->id;
207             return $DB->update_record('mnet_host', $obj);
208         } else {
209             $this->id = $DB->insert_record('mnet_host', $obj);
210             return $this->id > 0;
211         }
212     }
214     function touch() {
215         $this->last_connect_time = time();
216         $this->commit();
217     }
219     function set_name($newname) {
220         if (is_string($newname) && strlen($newname <= 80)) {
221             $this->name = $newname;
222             return true;
223         }
224         return false;
225     }
227     function set_applicationid($applicationid) {
228         if (is_numeric($applicationid) && $applicationid == intval($applicationid)) {
229             $this->applicationid = $applicationid;
230             return true;
231         }
232         return false;
233     }
235     /**
236      * Load information from db about an mnet peer into this object's properties
237      *
238      * @param string $wwwroot - address of peer whose details we want to load
239      * @return bool - indication of success or failure
240      */
241     function set_wwwroot($wwwroot) {
242         global $CFG, $DB;
244         $hostinfo = $DB->get_record('mnet_host', array('wwwroot'=>$wwwroot));
246         if ($hostinfo != false) {
247             $this->populate($hostinfo);
248             return true;
249         }
250         return false;
251     }
253     function set_id($id) {
254         global $CFG, $DB;
256         if (clean_param($id, PARAM_INT) != $id) {
257             $this->errno[]  = 1;
258             $this->errmsg[] = 'Your id ('.$id.') is not legal';
259             return false;
260         }
262         $sql = "
263                 SELECT
264                     h.*
265                 FROM
266                     {mnet_host} h
267                 WHERE
268                     h.id = ?";
270         if ($hostinfo = $DB->get_record_sql($sql, array($id))) {
271             $this->populate($hostinfo);
272             return true;
273         }
274         return false;
275     }
277     /**
278      * Several methods can be used to get an 'mnet_host' record. They all then
279      * send it to this private method to populate this object's attributes.
280      *
281      * @param   object  $hostinfo   A database record from the mnet_host table
282      * @return  void
283      */
284     function populate($hostinfo) {
285         global $DB;
286         $this->id                   = $hostinfo->id;
287         $this->wwwroot              = $hostinfo->wwwroot;
288         $this->ip_address           = $hostinfo->ip_address;
289         $this->name                 = $hostinfo->name;
290         $this->deleted              = $hostinfo->deleted;
291         $this->public_key           = $hostinfo->public_key;
292         $this->public_key_expires   = $hostinfo->public_key_expires;
293         $this->last_connect_time    = $hostinfo->last_connect_time;
294         $this->last_log_id          = $hostinfo->last_log_id;
295         $this->force_theme          = $hostinfo->force_theme;
296         $this->theme                = $hostinfo->theme;
297         $this->applicationid        = $hostinfo->applicationid;
298         $this->sslverification      = $hostinfo->sslverification;
299         $this->application = $DB->get_record('mnet_application', array('id'=>$this->applicationid));
300         $this->bootstrapped = true;
301     }
303     function get_public_key() {
304         if (isset($this->public_key_ref)) return $this->public_key_ref;
305         $this->public_key_ref = openssl_pkey_get_public($this->public_key);
306         return $this->public_key_ref;
307     }