00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 require_once dirname(__FILE__).'/class.ilBMFBase.php';
00024 require_once dirname(__FILE__).'/class.ilBMFFault.php';
00025 require_once dirname(__FILE__).'/class.ilBMFParser.php';
00026 require_once dirname(__FILE__).'/class.ilBMFValue.php';
00027 require_once dirname(__FILE__).'/class.ilBMFWSDL.php';
00028
00029 $soap_server_fault = null;
00030 function ilBMFServerErrorHandler($errno, $errmsg, $filename, $linenum, $vars) {
00031
00032
00033
00034 if (!$errno || $errno == E_NOTICE) {
00035 return;
00036 }
00037
00038 global $soap_server_fault;
00039 $detail = "Errno: $errno\nFilename: $filename\nLineno: $linenum\n";
00040
00041 $soap_server_fault = new ilBMFFault($errmsg, 'Server', 'PHP', $detail);
00042 }
00043
00056 class ilBMFServer extends ilBMFBase
00057 {
00062 var $dispatch_map = array();
00063 var $dispatch_objects = array();
00064 var $soapobject = NULL;
00065 var $call_methodname = NULL;
00066 var $callHandler = NULL;
00067 var $callValidation = true;
00068
00073 var $headers = '';
00074
00079 var $request = '';
00080
00085 var $xml_encoding = SOAP_DEFAULT_ENCODING;
00086 var $response_encoding = 'UTF-8';
00087
00088 var $result = 'successful';
00089
00090 var $endpoint = '';
00091
00092 var $service = '';
00093 var $method_namespace = NULL;
00094 var $__options = array('use'=>'encoded','style'=>'rpc','parameters'=>0);
00095
00096 function ilBMFServer($options=NULL) {
00097 ini_set('track_errors',1);
00098 parent::ilBMFBase('Server');
00099 if (is_array($options)) {
00100 if (isset($options['use']))
00101 $this->__options['use'] = $options['use'];
00102 if (isset($options['style']))
00103 $this->__options['style'] = $options['style'];
00104 if (isset($options['parameters']))
00105 $this->__options['parameters'] = $options['parameters'];
00106 }
00107 $this->_section5 = TRUE;
00108 if ($this->__options['use']=='literal') $this->_section5 = FALSE;
00109 }
00110
00111 function _getContentEncoding($content_type)
00112 {
00113
00114
00115 $this->xml_encoding = 'UTF-8';
00116 if (strpos($content_type,'=')) {
00117 $enc = strtoupper(str_replace('"',"",substr(strstr($content_type,'='),1)));
00118 if (!in_array($enc, $this->_encodings)) {
00119 return FALSE;
00120 }
00121 $this->xml_encoding = $enc;
00122 }
00123 return TRUE;
00124 }
00125
00126
00127
00128 function service($data, $endpoint = '', $test = FALSE)
00129 {
00130 $response = NULL;
00131 $attachments = array();
00132 $headers = array();
00133 $useEncoding = 'DIME';
00134
00135 $this->endpoint = $endpoint;
00136 if (!$test && !$this->endpoint) {
00137
00138 $this->endpoint = 'http://'.$_SERVER['SERVER_NAME'];
00139 if ($_SERVER['SERVER_PORT']) $this->endpoint .= ':'.$_SERVER['SERVER_PORT'];
00140 $this->endpoint .= $_SERVER['SCRIPT_NAME'];
00141 }
00142
00143
00144
00145 if (isset($_SERVER['CONTENT_TYPE'])) {
00146 if (strcasecmp($_SERVER['CONTENT_TYPE'],'application/dime')==0) {
00147 $this->_decodeDIMEMessage($data,$headers,$attachments);
00148 $useEncoding = 'DIME';
00149 } else if (stristr($_SERVER['CONTENT_TYPE'],'multipart/related')) {
00150
00151 $data = 'Content-Type: '.stripslashes($_SERVER['CONTENT_TYPE'])."\r\n\r\n".$data;
00152 $this->_decodeMimeMessage($data,$headers,$attachments);
00153 $useEncoding = 'Mime';
00154 }
00155 if (!isset($headers['content-type'])) {
00156 $headers['content-type'] = stripslashes($_SERVER['CONTENT_TYPE']);
00157 }
00158 if (!$this->fault &&
00159 !$this->_getContentEncoding($headers['content-type'])) {
00160 $this->xml_encoding = SOAP_DEFAULT_ENCODING;
00161
00162 $this->_raiseSoapFault('Unsupported encoding, use one of ISO-8859-1, US-ASCII, UTF-8','','','Server');
00163 }
00164 }
00165
00166
00167 if (!$this->fault && !$test && ($_SERVER['REQUEST_METHOD'] != 'POST' ||
00168 strncmp($headers['content-type'],'text/xml',8) != 0)) {
00169
00170 $this->_raiseSoapFault("Invalid SOAP request, must be POST with content-type: text/xml, got: ".(isset($headers['content-type'])?$headers['content-type']:'Nothing!'),'','','Server');
00171 }
00172
00173 if (!$this->fault) {
00174
00175 $soap_msg = $this->parseRequest($data, $attachments);
00176
00177
00178
00179
00180 if (count($this->__attachments)) {
00181 if ($useEncoding == 'Mime') {
00182 $soap_msg = $this->_makeMimeMessage($soap_msg);
00183 } else {
00184
00185 $soap_msg = $this->_makeDIMEMessage($soap_msg);
00186 $header['Content-Type'] = 'application/dime';
00187 }
00188 if (PEAR::isError($soap_msg)) {
00189 return $this->raiseSoapFault($soap_msg);
00190 }
00191 }
00192
00193 if (is_array($soap_msg)) {
00194 $response = $soap_msg['body'];
00195 if (count($soap_msg['headers'])) {
00196 $header = $soap_msg['headers'];
00197 }
00198 } else {
00199 $response = $soap_msg;
00200 }
00201 }
00202
00203
00204
00205 if(stristr(php_sapi_name(),'cgi') === 0)
00206 $hdrs_type = 'Status:';
00207 else
00208 $hdrs_type = 'HTTP/1.1';
00209
00210 if ($this->fault) {
00211 $hdrs = "$hdrs_type 500 Soap Fault\r\n";
00212 $response = $this->fault->message();
00213 } else {
00214 $hdrs = "$hdrs_type 200 OK\r\n";
00215 }
00216 header($hdrs);
00217
00218 $header['Server'] = SOAP_LIBRARY_NAME;
00219 if (!isset($header['Content-Type']))
00220 $header['Content-Type'] = "text/xml; charset=$this->response_encoding";
00221 $header['Content-Length'] = strlen($response);
00222
00223 reset($header);
00224 foreach ($header as $k => $v) {
00225 header("$k: $v");
00226 $hdrs .= "$k: $v\r\n";
00227 }
00228
00229 $this->response = $hdrs . "\r\n" . $response;
00230 print $response;
00231 }
00232
00233 function &callMethod($methodname, &$args) {
00234 global $soap_server_fault;
00235 $soap_server_fault = null;
00236
00237 if ($this->callHandler) {
00238 return @call_user_func_array($this->callHandler,array($methodname,$args));
00239 }
00240
00241 set_error_handler('ilBMFServerErrorHandler');
00242
00243 if ($args) {
00244
00245 if (isset($this->soapobject) && is_object($this->soapobject)) {
00246 $ret = @call_user_func_array(array(&$this->soapobject, $methodname),$args);
00247 } else {
00248 $ret = @call_user_func_array($methodname,$args);
00249 }
00250 } else {
00251
00252 if (is_object($this->soapobject)) {
00253 $ret = @call_user_func(array(&$this->soapobject, $methodname));
00254 } else {
00255 $ret = @call_user_func($methodname);
00256 }
00257 }
00258
00259 restore_error_handler();
00260
00261 return is_null($soap_server_fault) ? $ret : $soap_server_fault;
00262 }
00263
00264
00265 function buildResult(&$method_response, &$return_type, $return_name='return', $namespace = '')
00266 {
00267 if (gettype($method_response) == 'object' && is_a($method_response,'ilbmfvalue')) {
00268 $return_val = array($method_response);
00269 } else {
00270 if (is_array($return_type) && is_array($method_response)) {
00271 $i = 0;
00272
00273 foreach ($return_type as $key => $type) {
00274 if (is_numeric($key)) $key = 'item';
00275 if (is_a($method_response[$i],'ilbmfvalue')) {
00276 $return_val[] = $method_response[$i++];
00277 } else {
00278 $qn =& new ilBMFQName($key, $namespace);
00279 $return_val[] =& new ilBMFValue($qn->fqn(),$type,$method_response[$i++]);
00280 }
00281 }
00282 } else {
00283 if (is_array($return_type)) {
00284 $keys = array_keys($return_type);
00285 if (!is_numeric($keys[0])) $return_name = $keys[0];
00286 $values = array_values($return_type);
00287 $return_type = $values[0];
00288 }
00289 $qn =& new ilBMFQName($return_name, $namespace);
00290 $return_val = array();
00291 $return_val[] =& new ilBMFValue($qn->fqn(),$return_type,$method_response);
00292 }
00293 }
00294 return $return_val;
00295 }
00296
00297 function parseRequest($data = '', $attachments = null)
00298 {
00299
00300 $parser =& new ilBMFParser($data,$this->xml_encoding,$attachments);
00301
00302 if ($parser->fault) {
00303 $this->fault = $parser->fault;
00304 return null;
00305 }
00306
00307
00308
00309
00310 $request_headers = $parser->getHeaders();
00311 $header_results = array();
00312
00313 if ($request_headers) {
00314 if (!is_a($request_headers,'ilbmfvalue')) {
00315 $this->_raiseSoapFault("parser did not return ilBMFValue object: $request_headers",'','','Server');
00316 return null;
00317 }
00318 if ($request_headers->value) {
00319
00320 foreach ($request_headers->value as $header_val) {
00321 $f_exists = $this->validateMethod($header_val->name, $header_val->namespace);
00322
00323 # XXX this does not take into account message routing yet
00324 $myactor = (
00325 !$header_val->actor ||
00326 $header_val->actor == 'http://schemas.xmlsoap.org/soap/actor/next' ||
00327 $header_val->actor == $this->endpoint);
00328
00329 if (!$f_exists && $header_val->mustunderstand && $myactor) {
00330 $this->_raiseSoapFault("I don't understand header $header_val->name.",'','','MustUnderstand');
00331 return null;
00332 }
00333
00334
00335 $isok = $f_exists && $myactor;
00336
00337 if ($isok) {
00338 # call our header now!
00339 $header_method = $header_val->name;
00340 $header_data = array($this->_decode($header_val));
00341
00342 $hr =& $this->callMethod($header_method, $header_data);
00343 # if they return a fault, then it's all over!
00344 if (PEAR::isError($hr)) {
00345 $this->_raiseSoapFault($hr);
00346 return null;
00347 }
00348 $header_results[] = array_shift($this->buildResult($hr, $this->return_type, $header_method, $header_val->namespace));
00349 }
00350 }
00351 }
00352 }
00353
00354
00355
00356
00357
00358 $this->call_methodname = $this->methodname = $parser->root_struct_name[0];
00359
00360
00361 $this->method_namespace = $parser->message[$parser->root_struct[0]]['namespace'];
00362
00363 if ($this->_wsdl) {
00364 $this->_setSchemaVersion($this->_wsdl->xsd);
00365 $dataHandler = $this->_wsdl->getDataHandler($this->methodname,$this->method_namespace);
00366 if ($dataHandler)
00367 $this->call_methodname = $this->methodname = $dataHandler;
00368
00369 $this->_portName = $this->_wsdl->getPortName($this->methodname);
00370 if (PEAR::isError($this->_portName)) {
00371 return $this->_raiseSoapFault($this->_portName);
00372 }
00373 $opData = $this->_wsdl->getOperationData($this->_portName, $this->methodname);
00374 if (PEAR::isError($opData)) {
00375 return $this->_raiseSoapFault($opData);
00376 }
00377 $this->__options['style'] = $opData['style'];
00378 $this->__options['use'] = $opData['output']['use'];
00379 $this->__options['parameters'] = $opData['parameters'];
00380 }
00381
00382
00383 if (!$this->methodname || !$this->validateMethod($this->methodname,$this->method_namespace)) {
00384 $this->_raiseSoapFault("method '{{$this->method_namespace}}$this->methodname' not defined in service",'','','Server');
00385 return NULL;
00386 }
00387
00388 if (!$request_val = $parser->getResponse()) {
00389 return NULL;
00390 }
00391 if (!is_a($request_val,'ilbmfvalue')) {
00392 $this->_raiseSoapFault("parser did not return ilBMFValue object: $request_val",'','','Server');
00393 return NULL;
00394 }
00395
00396
00397 if (!$this->verifyMethod($request_val)) {
00398
00399 return NULL;
00400 }
00401
00402
00403
00404 $request_data = $this->__decodeRequest($request_val);
00405 if (PEAR::isError($request_data)) {
00406 return $this->_raiseSoapFault($request_data);
00407 }
00408 $method_response =& $this->callMethod($this->call_methodname, $request_data);
00409
00410 if (PEAR::isError($method_response)) {
00411 $this->_raiseSoapFault($method_response);
00412 return null;
00413 }
00414
00415 if ($this->__options['parameters'] || !$method_response || $this->__options['style']=='rpc') {
00416
00417 if (is_null($method_response))
00418 $return_val = NULL;
00419 else
00420 $return_val = $this->buildResult($method_response, $this->return_type);
00421
00422 $qn =& new ilBMFQName($this->methodname.'Response',$this->method_namespace);
00423 $methodValue =& new ilBMFValue($qn->fqn(), 'Struct', $return_val);
00424 } else {
00425 $methodValue =& $method_response;
00426 }
00427 return $this->_makeEnvelope($methodValue, $header_results, $this->response_encoding);
00428 }
00429
00430 function &__decodeRequest($request,$shift=false)
00431 {
00432 if (!$request) return NULL;
00433
00434 if (PEAR::isError($request)) {
00435 return $this->_raiseSoapFault($request);
00436 } else if (!is_a($request,'ilbmfvalue')) {
00437 return $this->_raiseSoapFault("Invalid data in server::__decodeRequest");
00438 }
00439
00440
00441 $requestArray = $this->_decode($request);
00442
00443 if (PEAR::isError($requestArray)) {
00444 return $this->_raiseSoapFault($requestArray);
00445 }
00446 if (is_object($requestArray)&& get_class($requestArray) == 'stdClass') {
00447 $requestArray = get_object_vars($requestArray);
00448 } else
00449 if ($this->__options['style']=='document') {
00450 $requestArray = array($requestArray);
00451 }
00452 if (is_array($requestArray)) {
00453 if (isset($requestArray['faultcode']) || isset($requestArray['SOAP-ENV:faultcode'])) {
00454 $faultcode = $faultstring = $faultdetail = $faultactor = '';
00455 foreach ($requestArray as $k => $v) {
00456 if (stristr($k,'faultcode')) $faultcode = $v;
00457 if (stristr($k,'faultstring')) $faultstring = $v;
00458 if (stristr($k,'detail')) $faultdetail = $v;
00459 if (stristr($k,'faultactor')) $faultactor = $v;
00460 }
00461 return $this->_raiseSoapFault($faultstring, $faultdetail, $faultactor, $faultcode);
00462 }
00463
00464 if ($shift && count($requestArray) == 1) {
00465 return array_shift($requestArray);
00466 }
00467 return $requestArray;
00468 }
00469 return $requestArray;
00470 }
00471
00472 function verifyMethod($request)
00473 {
00474 if (!$this->callValidation) return TRUE;
00475
00476 $params = $request->value;
00477
00478
00479 $map = NULL;
00480 if (array_key_exists($this->methodname, $this->dispatch_map)) {
00481 $map = $this->dispatch_map[$this->methodname];
00482 } else if (isset($this->soapobject)) {
00483 if (method_exists($this->soapobject, '__dispatch')) {
00484 $map = $this->soapobject->__dispatch($this->methodname);
00485 } else if (method_exists($this->soapobject, $this->methodname)) {
00486
00487 return TRUE;
00488 }
00489 }
00490 if (!$map) {
00491 $this->_raiseSoapFault("soap request specified an unhandled method '$this->methodname'",'','','Client');
00492 return FALSE;
00493 }
00494
00495
00496
00497 if (array_key_exists('alias',$map) && !empty($map['alias'])) {
00498 $this->call_methodname = $map['alias'];
00499 }
00500
00501
00502 if ($sig = $map['in']) {
00503 $this->input_value = count($sig);
00504 $this->return_type = $this->getReturnType($map['out']);
00505 if (is_array($params)) {
00506
00507 if (count($params) == count($sig)) {
00508
00509 foreach ($params as $param) {
00510 $p[] = strtolower($param->type);
00511 }
00512 $sig_t = array_values($sig);
00513
00514 for($i=0; $i < count($p); $i++) {
00515
00516
00517
00518
00519 if (strcasecmp($sig_t[$i],$p[$i])!=0 &&
00520 (isset($this->_typemap[SOAP_XML_SCHEMA_VERSION][$sig_t[$i]]) &&
00521 strcasecmp($this->_typemap[SOAP_XML_SCHEMA_VERSION][$sig_t[$i]],$this->_typemap[SOAP_XML_SCHEMA_VERSION][$p[$i]])!=0)) {
00522
00523 $param = $params[$i];
00524 $this->_raiseSoapFault("soap request contained mismatching parameters of name $param->name had type [{$p[$i]}], which did not match signature's type: [{$sig_t[$i]}], matched? ".(strcasecmp($sig_t[$i],$p[$i])),'','','Client');
00525 return false;
00526 }
00527 }
00528 return true;
00529
00530 } else {
00531 $this->_raiseSoapFault("soap request contained incorrect number of parameters. method '$this->methodname' required ".count($sig).' and request provided '.count($params),'','','Client');
00532 return false;
00533 }
00534
00535 } else {
00536 $this->_raiseSoapFault("soap request contained incorrect number of parameters. method '$this->methodname' requires ".count($sig).' parameters, and request provided none','','','Client');
00537 return false;
00538 }
00539
00540 }
00541
00542 return true;
00543 }
00544
00545
00546 function getReturnType($returndata)
00547 {
00548 if (is_array($returndata)) {
00549 if (count($returndata) > 1) {
00550 return $returndata;
00551 }
00552 $type = array_shift($returndata);
00553 return $type;
00554 }
00555 return false;
00556 }
00557
00558 function validateMethod($methodname, $namespace = NULL)
00559 {
00560 unset($this->soapobject);
00561 if (!$this->callValidation) return TRUE;
00562 # no soap access to private functions
00563 if ($methodname[0] == '_') return FALSE;
00564
00565
00566 if (array_key_exists($methodname, $this->dispatch_map) &&
00567 (!$namespace || !array_key_exists('namespace', $this->dispatch_map[$methodname]) ||
00568 $namespace == $this->dispatch_map[$methodname]['namespace'])) {
00569 if (array_key_exists('namespace', $this->dispatch_map[$methodname]))
00570 $this->method_namespace = $this->dispatch_map[$methodname]['namespace'];
00571 return TRUE;
00572 }
00573
00574
00575 if (isset($this->dispatch_objects[$namespace])) {
00576 $c = count($this->dispatch_objects[$namespace]);
00577 for ($i=0; $i < $c; $i++) {
00578 $obj =& $this->dispatch_objects[$namespace][$i];
00579
00580
00581 if (method_exists($obj, '__dispatch')) {
00582 if ($obj->__dispatch($methodname)) {
00583 $this->method_namespace = $namespace;
00584 $this->soapobject =& $obj;
00585 return TRUE;
00586 }
00587 } else
00588 if (method_exists($obj, $methodname)) {
00589 $this->method_namespace = $namespace;
00590 $this->soapobject =& $obj;
00591 return TRUE;
00592 }
00593 }
00594 }
00595 return FALSE;
00596 }
00597
00598 function addObjectMap(&$obj, $namespace = null, $service_name = 'Default', $service_desc = '')
00599 {
00600 if (!$namespace) {
00601 if (isset($obj->namespace)) {
00602
00603 $namespace = $obj->namespace;
00604 } else {
00605 $this->_raiseSoapFault('No namespace provided for class!','','','Server');
00606 return false;
00607 }
00608 }
00609 if (!isset($this->dispatch_objects[$namespace])) {
00610 $this->dispatch_objects[$namespace] = array();
00611 }
00612 $this->dispatch_objects[$namespace][] =& $obj;
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635
00636
00637 if (isset($obj->__dispatch_map) && isset($obj->__typedef)) {
00638 $this->addObjectWSDL($obj, $namespace, $service_name, $service_desc);
00639 }
00640 return true;
00641 }
00642
00643
00644 function addToMap($methodname, $in, $out, $namespace = NULL, $alias=NULL)
00645 {
00646 if (!function_exists($methodname)) {
00647 $this->_raiseSoapFault("error mapping function\n",'','','Server');
00648 return false;
00649 }
00650 $this->dispatch_map[$methodname]['in'] = $in;
00651 $this->dispatch_map[$methodname]['out'] = $out;
00652 $this->dispatch_map[$methodname]['alias'] = $alias;
00653 if ($namespace) $this->dispatch_map[$methodname]['namespace'] = $namespace;
00654 return true;
00655 }
00656
00657 function setCallHandler($callHandler, $validation=true) {
00658 $this->callHandler = $callHandler;
00659 $this->callValidation = $validation;
00660 }
00661
00665 function bind($wsdl_url) {
00666 $this->bindWSDL($wsdl_url);
00667 }
00668
00673 function bindWSDL($wsdl_url) {
00674
00675 $this->_wsdl =& new ilBMFWSDL($wsdl_url);
00676 if ($this->_wsdl->fault) {
00677 $this->_raiseSoapFault($this->_wsdl->fault);
00678 }
00679 }
00680
00684 function addObjectWSDL(&$wsdl_obj, $targetNamespace, $service_name, $service_desc = '') {
00685 if (!isset($this->_wsdl)) {
00686 $this->_wsdl =& new ilBMFWSDL;
00687 }
00688
00689 $this->_wsdl->parseObject($wsdl_obj, $targetNamespace, $service_name, $service_desc);
00690
00691 if ($this->_wsdl->fault) {
00692 $this->_raiseSoapFault($this->_wsdl->fault);
00693 }
00694 }
00695 }
00696 ?>