the tophat api url changed
[plewww.git] / plekit / php / tophat_api.php
1 <?php
2 //
3 // TopHat API PHP interface
4 //
5
6 // TODO add tophat_api in the php default path in /etc/plc.d/httpd
7 // TODO occurences to PLC
8
9 define('TOPHAT_API_HOST', 'api.top-hat.info');
10 define('TOPHAT_API_PATH', '/API/');
11 define('TOPHAT_API_PORT', 443);
12
13 class TopHatAPI
14 {
15   var $auth;
16   var $server;
17   var $port;
18   var $path;
19   var $errors;
20   var $trace;
21   var $calls;
22   var $multicall;
23
24   function TopHatAPI($auth = NULL,
25                   $server = TOPHAT_API_HOST,
26                   $port = TOPHAT_API_PORT,
27                   $path = TOPHAT_API_PATH,
28                   $cainfo = NULL)
29   {
30     $this->auth = $auth;
31     $this->server = $server;
32     $this->port = $port;
33     $this->path = $path;
34     $this->cainfo = $cainfo;
35     $this->errors = array();
36     $this->trace = array();
37     $this->calls = array();
38     $this->multicall = false;
39   }
40
41   function error_log($error_msg, $backtrace_level = 1)
42   {
43     $backtrace = debug_backtrace();
44     $file = $backtrace[$backtrace_level]['file'];
45     $line = $backtrace[$backtrace_level]['line'];
46
47     $this->errors[] = 'TopHatAPI error:  ' . $error_msg . ' in ' . $file . ' on line ' . $line;
48     error_log(end($this->errors));
49   }
50
51   function error()
52   {
53     if (empty($this->trace)) {
54       return NULL;
55     } else {
56       $last_trace = end($this->trace);
57       return implode("\\n", $last_trace['errors']);
58     }
59   }
60
61   function trace()
62   {
63     return $this->trace;
64   }
65
66   function microtime_float()
67   {
68     list($usec, $sec) = explode(" ", microtime());
69     return ((float) $usec + (float) $sec);
70   }
71
72   function call($method, $args = NULL)
73   {
74     if ($this->multicall) {
75       $this->calls[] = array ('methodName' => $method,
76                                 'params' => $args);
77       return NULL;
78     } else {
79       return $this->internal_call ($method, $args, 3);
80     }
81   }
82
83   function internal_call($method, $args = NULL, $backtrace_level = 2)
84   {
85       $curl = curl_init();
86
87       // Verify peer certificate if talking over SSL
88       if ($this->port == 443) {
89           curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); // XXX 2
90           if (!empty($this->cainfo)) {
91               curl_setopt($curl, CURLOPT_CAINFO, $this->cainfo);
92           } elseif (defined('PLC_API_CA_SSL_CRT')) {
93               curl_setopt($curl, CURLOPT_CAINFO, PLC_API_CA_SSL_CRT);
94           }
95           $url = 'https://';
96       } else {
97           $url = 'http://';
98       }
99
100       // Set the URL for the request
101       $url .= $this->server . ':' . $this->port . '/' . $this->path;
102       curl_setopt($curl, CURLOPT_URL, $url);
103
104       // Marshal the XML-RPC request as a POST variable. <nil/> is an
105       // extension to the XML-RPC spec that is supported in our custom
106       // version of xmlrpc.so via the 'allow_null' output_encoding key.
107       $request = xmlrpc_encode_request($method, $args, array('allow_null' => TRUE));
108       curl_setopt($curl, CURLOPT_POSTFIELDS, $request);
109
110       // Construct the HTTP header
111       $header[] = 'Content-type: text/xml';
112       $header[] = 'Content-length: ' . strlen($request);
113       curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
114
115       // Set some miscellaneous options
116       curl_setopt($curl, CURLOPT_TIMEOUT, 180);
117
118       // Get the output of the request
119       curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
120       $t0 = $this->microtime_float();
121       $output = curl_exec($curl);
122       $t1 = $this->microtime_float();
123
124       if (curl_errno($curl)) {
125           $this->error_log('curl: ' . curl_error($curl), true);
126           $ret = NULL;
127       } else {
128           $ret = xmlrpc_decode($output);
129           if (is_array($ret) && xmlrpc_is_fault($ret)) {
130               $this->error_log('Fault Code ' . $ret['faultCode'] . ': ' .
131                       $ret['faultString'], $backtrace_level, true);
132               $ret = NULL;
133           }
134       }
135
136       curl_close($curl);
137
138       $this->trace[] = array('method' => $method,
139               'args' => $args,
140               'runtime' => $t1 - $t0,
141               'return' => $ret,
142               'errors' => $this->errors);
143       $this->errors = array();
144
145       return $ret;
146   }
147
148   function begin()
149   {
150     if (!empty($this->calls)) {
151       $this->error_log ('Warning: multicall already in progress');
152     }
153
154     $this->multicall = true;
155   }
156
157   function commit()
158   {
159     if (!empty ($this->calls)) {
160       $ret = array();
161       $results = $this->internal_call ('system.multicall', array ($this->calls));
162       foreach ($results as $result) {
163         if (is_array($result)) {
164           if (xmlrpc_is_fault($result)) {
165             $this->error_log('Fault Code ' . $result['faultCode'] . ': ' .
166                              $result['faultString'], 1, true);
167             $ret[] = NULL;
168             // Thierry - march 30 2007 
169             // using $adm->error() is broken with begin/commit style 
170             // this is because error() uses last item in trace and checks for ['errors']
171             // when using begin/commit we do run internal_call BUT internal_call checks for 
172             // multicall's result globally, not individual results, so ['errors'] comes empty
173             // I considered hacking internal_call 
174             // to *NOT* maintain this->trace at all when invoked with multicall
175             // but it is too complex to get all values right
176             // so let's go for the hacky way, and just record individual errors at the right place
177             $this->trace[count($this->trace)-1]['errors'][] = end($this->errors);
178           } else {
179             $ret[] = $result[0];
180           }
181         } else {
182           $ret[] = $result;
183         }
184       }
185     } else {
186       $ret = NULL;
187     }
188
189     $this->calls = array();
190     $this->multicall = false;
191
192     return $ret;
193   }
194
195   //
196   // TopHatAPI Methods
197   //
198
199   // Gets measurement information.
200   //
201   // Returns the measurement (cf doc).
202
203   function Test($param = NULL)
204   {
205     $args[] = $this->auth;
206     if (func_num_args() > 0) $args[] = $param;
207     return $this->call('Test', $args);
208   }
209
210   function Get($method, $timestamp, $input_filter = NULL, $output_fields = NULL, $callback = NULL)
211   {
212     $args[] = $this->auth;
213     $args[] = $method;
214     $args[] = $timestamp;
215     if (func_num_args() > 2) $args[] = $input_filter;
216     if (func_num_args() > 3) $args[] = $output_fields;
217     if (func_num_args() > 4) $args[] = $callback;
218     return $this->call('Get', $args);
219   }
220
221   // TDMI Methods
222
223   function GetPlatforms($input_filter = NULL, $output_fields = NULL)
224   {
225     $args[] = $this->auth;
226     if (func_num_args() > 0) $args[] = $input_filter;
227     if (func_num_args() > 1) $args[] = $output_fields;
228     return $this->call('GetPlatforms', $args);
229   }
230
231   function GetTraceroutes($input_filter = NULL, $output_fields = NULL)
232   {
233     $args[] = $this->auth;
234     if (func_num_args() > 0) $args[] = $input_filter;
235     if (func_num_args() > 1) $args[] = $output_fields;
236     return $this->call('GetTraceroutes', $args);
237   }
238
239   // Imported PLC Methods
240
241   // Returns a new session key if a user or node authenticated
242   // successfully, faults otherwise.
243   
244   function GetSession ()
245   {
246     $args[] = $this->auth;
247     return $this->call('GetSession', $args);
248   }
249   
250   // Returns an array of structs containing details about users sessions. If
251   // session_filter is specified and is an array of user identifiers or
252   // session_keys, or a struct of session attributes, only sessions matching the
253   // filter will be returned. If return_fields is specified, only the
254   // specified details will be returned.
255   
256   function GetSessions ($session_filter = NULL)
257   {
258     $args[] = $this->auth;
259     if (func_num_args() > 0) $args[] = $session_filter;
260     return $this->call('GetSessions', $args);
261   }
262   
263   // Returns an array of structs containing details about users. If
264   // person_filter is specified and is an array of user identifiers or
265   // usernames, or a struct of user attributes, only users matching the
266   // filter will be returned. If return_fields is specified, only the
267   // specified details will be returned.
268   // 
269   // Users and techs may only retrieve details about themselves. PIs
270   // may retrieve details about themselves and others at their
271   // sites. Admins and nodes may retrieve details about all accounts.
272   
273   function GetPersons ($person_filter = NULL, $return_fields = NULL)
274   {
275     $args[] = $this->auth;
276     if (func_num_args() > 0) $args[] = $person_filter;
277     if (func_num_args() > 1) $args[] = $return_fields;
278     return $this->call('GetPersons', $args);
279   }
280   
281   // Returns 1 if the user or node authenticated successfully, faults
282   // otherwise.
283   
284   function AuthCheck ()
285   {
286     $args[] = $this->auth;
287     return $this->call('AuthCheck', $args);
288   }
289
290 }
291
292 ?>