diff options
| author | Jan-Hendrik Willms <tleilax+github@gmail.com> | 2021-07-22 16:07:19 +0200 |
|---|---|---|
| committer | Jan-Hendrik Willms <tleilax+github@gmail.com> | 2021-07-22 16:19:12 +0200 |
| commit | a3da1483a9e689846179159355badfec8073dbec (patch) | |
| tree | 770dcca6bdf5f6f2a11b0e7fcbbeda6919a3fc52 /vendor | |
current code from svn, revision 62608
Diffstat (limited to 'vendor')
326 files changed, 55948 insertions, 0 deletions
diff --git a/vendor/email_message/blackhole_message.php b/vendor/email_message/blackhole_message.php new file mode 100644 index 0000000..fdfe4f3 --- /dev/null +++ b/vendor/email_message/blackhole_message.php @@ -0,0 +1,21 @@ +<?php +/* + * blackhole_message.php + * + * no escape :) + * inject into StudipMail to send all mail to /dev/null + */ + + +class blackhole_message_class extends email_message_class +{ + + /* (non-PHPdoc) + * @see vendor/email_message/email_message_class::SendMail() + */ + function SendMail($to,$subject,$body,$headers,$return_path) + { + } + +} +?>
\ No newline at end of file diff --git a/vendor/email_message/debug_message.php b/vendor/email_message/debug_message.php new file mode 100644 index 0000000..f8d4f02 --- /dev/null +++ b/vendor/email_message/debug_message.php @@ -0,0 +1,29 @@ +<?php +/* + * debug_message.php + * + * + * + */ + + +class debug_message_class extends email_message_class +{ + + private $logfile =""; + + function __construct() { + $this->logfile = $GLOBALS['TMP_PATH'] . '/studip-mail-debug.log'; + } + + function SendMail($to,$subject,$body,$headers,$return_path) { + if ($log = fopen($this->logfile, "a")){ + if(strlen($headers)) $headers.="\n"; + fwrite($log, "\n-- " . strftime("%x %X"). ' ' . $GLOBALS['auth']->auth['uname']); + fwrite($log, "\nTo: ".$to."\nSubject: ".$subject."\n".$headers."\n"); + fwrite($log,$body."\n"); + fclose($log); + } + } +} +?>
\ No newline at end of file diff --git a/vendor/email_message/documentation/email_message_class.html b/vendor/email_message/documentation/email_message_class.html new file mode 100644 index 0000000..b9606b9 --- /dev/null +++ b/vendor/email_message/documentation/email_message_class.html @@ -0,0 +1,1036 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> +<head> +<title>Class: MIME E-mail message composing and sending</title> +</head> +<body> +<center><h1>Class: MIME E-mail message composing and sending</h1></center> +<hr /> +<ul> +<p><b>Version:</b> <tt>@(#) $Id: email_message.php,v 1.85 2008/04/17 04:15:56 mlemos Exp $</tt></p> +<h2><a name="table_of_contents">Contents</a></h2> +<ul> +<li><a href="#2.1.1">Summary</a></li> +<ul> +<li><a href="#3.2.0">Name</a></li> +<li><a href="#3.2.0.0">Author</a></li> +<li><a href="#3.2.0.1">Copyright</a></li> +<li><a href="#3.2.0.2">Version</a></li> +<li><a href="#3.2.0.3">Purpose</a></li> +<li><a href="#3.2.0.4">Translation</a></li> +<li><a href="#3.2.0.5">Support</a></li> +<li><a href="#3.2.0.6">Usage</a></li> +</ul> +<li><a href="#4.1.1">Variables</a></li> +<ul> +<li><a href="#5.2.11">email_regular_expression</a></li> +<li><a href="#5.2.12">mailer</a></li> +<li><a href="#5.2.13">mailer_delivery</a></li> +<li><a href="#5.2.14">default_charset</a></li> +<li><a href="#5.2.15">line_quote_prefix</a></li> +<li><a href="#5.2.16">break_long_lines</a></li> +<li><a href="#5.2.17">file_buffer_length</a></li> +<li><a href="#5.2.18">debug</a></li> +<li><a href="#5.2.19">cache_body</a></li> +<li><a href="#5.2.20">error</a></li> +</ul> +<li><a href="#6.1.1">Functions</a></li> +<ul> +<li><a href="#7.2.38">ValidateEmailAddress</a></li> +<li><a href="#9.2.39">WrapText</a></li> +<li><a href="#11.2.40">CenterText</a></li> +<li><a href="#13.2.41">Ruler</a></li> +<li><a href="#15.2.42">QuoteText</a></li> +<li><a href="#17.2.43">SetHeader</a></li> +<li><a href="#19.2.44">SetEncodedHeader</a></li> +<li><a href="#21.2.45">SetEncodedEmailHeader</a></li> +<li><a href="#23.2.46">SetMultipleEncodedEmailHeader</a></li> +<li><a href="#25.2.47">ResetMessage</a></li> +<li><a href="#25.2.48">AddPart</a></li> +<li><a href="#27.2.49">ReplacePart</a></li> +<li><a href="#29.2.50">CreatePlainTextPart</a></li> +<li><a href="#31.2.51">AddPlainTextPart</a></li> +<li><a href="#33.2.52">CreateQuotedPrintableTextPart</a></li> +<li><a href="#35.2.53">AddQuotedPrintableTextPart</a></li> +<li><a href="#37.2.54">CreateHTMLPart</a></li> +<li><a href="#39.2.55">AddHTMLPart</a></li> +<li><a href="#41.2.56">CreateQuotedPrintableHTMLPart</a></li> +<li><a href="#43.2.57">AddQuotedPrintableHTMLPart</a></li> +<li><a href="#45.2.58">CreateFilePart</a></li> +<li><a href="#47.2.59">AddFilePart</a></li> +<li><a href="#49.2.60">CreateMessagePart</a></li> +<li><a href="#51.2.61">AddMessagePart</a></li> +<li><a href="#53.2.62">CreateAlternativeMultipart</a></li> +<li><a href="#55.2.63">AddAlternativeMultipart</a></li> +<li><a href="#57.2.64">CreateRelatedMultipart</a></li> +<li><a href="#59.2.65">AddRelatedMultipart</a></li> +<li><a href="#61.2.66">CreateMixedMultipart</a></li> +<li><a href="#63.2.67">AddMixedMultipart</a></li> +<li><a href="#65.2.68">GetPartContentID</a></li> +<li><a href="#67.2.69">GetDataURL</a></li> +<li><a href="#69.2.70">Send</a></li> +<li><a href="#69.2.71">GetMessage</a></li> +<li><a href="#71.2.72">GetMessageSize</a></li> +<li><a href="#73.2.73">Mail</a></li> +<li><a href="#75.2.74">SetBulkMail</a></li> +</ul> +</ul> +<p><a href="#table_of_contents">Top of the table of contents</a></p> +</ul> +<hr /> +<ul> +<h2><li><a name="2.1.1">Summary</a></li></h2> +<ul> +<h3><a name="3.2.0">Name</a></h3> +<p>MIME E-mail message composing and sending</p> +<h3><a name="3.2.0.0">Author</a></h3> +<p>Manuel Lemos (<a href="mailto:mlemos-at-acm.org">mlemos-at-acm.org</a>)</p> +<h3><a name="3.2.0.1">Copyright</a></h3> +<p>Copyright © (C) Manuel Lemos 1999-2004</p> +<h3><a name="3.2.0.2">Version</a></h3> +<p>@(#) $Id: email_message.php,v 1.85 2008/04/17 04:15:56 mlemos Exp $</p> +<h3><a name="3.2.0.3">Purpose</a></h3> +<p>Compose and send e-mail messages according to the MIME standards.</p> +<h3><a name="3.2.0.4">Translation</a></h3> +<p>If you are interested in translating the documentation of this class to your own idiom, please <a href="mailto:mlemos-at-acm.org">contact the author</a>.</p> +<h3><a name="3.2.0.5">Support</a></h3> +<p>Technical support for using this class may be obtained in the <tt>mimemessage-dev</tt> mailing list. Just go to the mailing list page to browse the list archives, learn how to to join and post support request messages:</p> +<p> <a href="http://groups-beta.google.com/group/mimemessage-dev">http://groups-beta.google.com/group/mimemessage-dev</a></p> +<h3><a name="3.2.0.6">Usage</a></h3> +<p>To used this class just create a new object as follows, set any variables to configure its behavior and call the functions you need to compose and send your messages.</p> +<p> <tt>require('email_message.php');<br /> + <br /> + $message_object = new email_message_class;<br /> +</tt></p> +<p> <b>- Set the sender and recipients</b></p> +<p> You can set the message sender and one or more recipient addresses using the <tt><a href="#function_SetHeader">SetHeader</a></tt> or the <tt><a href="#function_SetEncodedEmailHeader">SetEncodedEmailHeader</a></tt> functions specifying the addresses for the <tt>From</tt>, <tt>To</tt>, <tt>Cc</tt> and <tt>Bcc</tt> headers.</p> +<p> <b>- Formatting text messages</b></p> +<p> You can use the <tt><a href="#function_WrapText">WrapText</a></tt> to assure that a text message does not have more than 75 columns by breaking the longer lines between words.</p> +<p> </p> +<p> If you are composing a reply to another text message, you can use the <tt><a href="#function_QuoteText">QuoteText</a></tt> function to conveniently mark the text quoted from the original message.</p> +<p> <b>- Add a plain text message body</b></p> +<p> If the text of the message that you want to send only contains ASCII characters (7 bits), use the <tt><a href="#function_AddPlainTextPart">AddPlainTextPart</a></tt> function to add the text to the message.</p> +<p> <b>- Add a text message with non-ASCII characters</b></p> +<p> If your message text may contains non-ASCII characters (8 bits or more), use the <tt><a href="#function_AddQuotedPrintableTextPart">AddQuotedPrintableTextPart</a></tt> function to add the text to the message.</p> +<p> </p> +<p> If the text uses a character set other than <i>ISO-8859-1</i> (ISO Latin 1), set the <tt><a href="#variable_default_charset">default_charset</a></tt> variable to change the default character set.</p> +<p> <b>- Setting the error message bounce address</b></p> +<p> This class provides a means to specify the address where error messages should be bounced in case it is not possible to deliver a message. That can be done by setting the header <tt>Return-Path</tt> with the <tt><a href="#function_SetHeader">SetHeader</a></tt> function.</p> +<p> <b>- Request message receipt notification</b></p> +<p> If you would like to be receive an notification when a message that is sent is received, just use the <tt><a href="#function_SetHeader">SetHeader</a></tt> function with the <tt>Disposition-Notification-To</tt> header to specify the address to where you want to receive the notification message.</p> +<p> Keep in mind that this header just expresses that you want to get a receipt notification, but it may be denied or ignored by the recipient, which does not mean the message was not received.</p> +<p> <b>- Avoding temporary delivery failure warning messages</b></p> +<p> Sometimes it is not possible to deliver a message immediately due to a networking failure or some other problem. In that case, the mail transfer system usually leaves the message in a queue and keeps retrying to deliver the message until it succeeds or it has reached the limit number of days before it gives up. When it gives up the the message is bounced to the return-path address.</p> +<p> However some systems send a warning message to the original sender when it is not delivered after the first few hours. This may be an useful notification when the message is sent by a human but it maybe inconvinient when you are sending messages to many users like for instance newsletters or messages to subscribers of mailing lists.</p> +<p> If you want to hint the mail transfer system to not send temporary delivery failure warning messages, just use the <tt><a href="#function_SetHeader">SetHeader</a></tt> function to set the <tt>Precedence</tt> header to <tt>bulk</tt>.</p> +<p> Setting this header this way is a convention used by mailing list manager programs precisely for this purpose. It may also hint some mail receiving systems to not send auto-response messages, for instance when the recipient user is away on vaction. However, not all systems are aware of this convention and still send auto-response messages when you set this header.</p> +<p> <b>- Send the message</b></p> +<p> Once you have set the message sender, the recipients and added the message text, use the <tt><a href="#function_Send">Send</a></tt> function to send the message. This class uses the PHP function <tt>mail()</tt> to send messages.</p> +<p> </p> +<p> If for some reason you need to use a different message delivery method, you may use one of the existing sub-classes that are specialized in delivering messages by connecting to an SMTP server or using directly the programs sendmail and qmail.</p> +<p> <b>- Add an HTML message body</b></p> +<p> If you want to send an HTML message you can use the <tt><a href="#function_AddHTMLPart">AddHTMLPart</a></tt> function if it contains only ASCII characters. If it contains non-ASCII characters, you should the <tt><a href="#function_AddQuotedPrintableHTMLPart">AddQuotedPrintableHTMLPart</a></tt> function instead.</p> +<p> <b>- Add alternative text body for HTML messages</b></p> +<p> Not every e-mail program can display HTML messages. Therefore, when you send an HTML message, you should also include an alternative text part to be displayed by programs that do not support HTML messages.</p> +<p> </p> +<p> This is achieved by composing <tt>multipart/alternative</tt> messages. This type of message is composed by creating the HTML message part with the <tt><a href="#function_CreateHTMLPart">CreateHTMLPart</a></tt> or the <tt><a href="#function_CreateQuotedPrintableHTMLPart">CreateQuotedPrintableHTMLPart</a></tt> functions, then create the alternative text part with the <tt><a href="#function_CreatePlainTextPart">CreatePlainTextPart</a></tt> or the <tt><a href="#function_CreateQuotedPrintableTextPart">CreateQuotedPrintableTextPart</a></tt> functions, and finally use the <tt><a href="#function_AddAlternativeMultipart">AddAlternativeMultipart</a></tt> function to add an assembly of both message parts.</p> +<p> Note that the text part should be the first to be specified in the array of parts passed to the <tt><a href="#function_AddAlternativeMultipart">AddAlternativeMultipart</a></tt> function, or else it will not appear correctly.</p> +<p> Despite this procedure adds a little complexity to the process of sending HTML messages, it is the same procedure that is followed by e-mail programs that are used by most people to send HTML messages.</p> +<p> Therefore, you are strongly recommended to follow the same procedure because some of the modern spam filter programs discard HTML messages without an alternative plain text part, as it constitutes a pattern that identifies messages composed by some of the spam sending programs.</p> +<p> <b><a name="embed-image">- Embed images in HTML messages</a></b></p> +<p> One way to show an image in an HTML message is to use <tt><img></tt> tag with <tt>src</tt> attribute set to the remote site URL of the image that is meant to be displayed. However, since the message recipient user may not be online when they will check their e-mail, an image referenced this way may not appear.</p> +<p> Alternatively, an image file can be embedded in an HTML message using <tt>multipart/related</tt> message parts. This type of message part is composed by creating the image file part with the <tt><a href="#function_CreateFilePart">CreateFilePart</a></tt> function.</p> +<p> Then use the <tt><a href="#function_GetPartContentID">GetPartContentID</a></tt> function the image part identifier text. Prepend the string 'cid:' to this identifier to form a special URL that should be used in the HTML part to reference the image part like this:</p> +<p> <tt>$image_tag = '<img src="cid:' . $message_object->GetPartContentID($image_part) . '">' ;</tt></p> +<p> When you have composed the whole HTML document, create the HTML message part with the <tt><a href="#function_CreateHTMLPart">CreateHTMLPart</a></tt> or the <tt><a href="#function_CreateQuotedPrintableHTMLPart">CreateQuotedPrintableHTMLPart</a></tt> functions, and finally use the <tt><a href="#function_CreateRelatedMultipart">CreateRelatedMultipart</a></tt> function to create a message part that can be added to the message with the function <tt><a href="#function_AddAlternativeMultipart">AddAlternativeMultipart</a></tt> like HTML messages with alternative text parts described before.</p> +<p> Note that the HTML part must be the first listed in the parts array argument that is passed to the function <tt><a href="#function_CreateRelatedMultipart">CreateRelatedMultipart</a></tt>, or else the message may not appear correctly.</p> +<p> Note also that when you are composing an HTML message with embedded images and an alternative text part, first you need to create the <tt>multipart/alternative</tt> part with the HTML and the text parts using the <tt><a href="#function_CreateAlternativeMultipart">CreateAlternativeMultipart</a></tt> function, and then you add the <tt>multipart/related</tt> part to the message with the <tt><a href="#function_AddRelatedMultipart">AddRelatedMultipart</a></tt> function, passing an array of parts that lists first the <tt>multipart/alternative</tt> part and then the image part created before.</p> +<p> <b>- Attach files to messages</b></p> +<p> To send a message with attached files, it is necessary to compose a <tt>multipart/mixed</tt> message. This is a type of message made by a text or HTML part followed by one or more file parts.</p> +<p> If you add multiple parts to a message, this class implicitly turns it into a <tt>multipart/mixed</tt> message. Therefore you only need to use the function <tt><a href="#function_AddFilePart">AddFilePart</a></tt> for each file that you want to attach and the class will automatically generate the message treating any parts added after the first as attachments.</p> +<p> <b>- Forward received messages</b></p> +<p> To forward an e-mail message received from somewhere, just use the function <tt><a href="#function_AddMessagePart">AddMessagePart</a></tt> passing the message complete with the original headers and body data. The message is forwarded as an attachment that most mail programs can display.</p> +<p> <b>- Sending messages to many recipients (mass or bulk mailing)</b></p> +<p> Sending messages to many recipients is an activity also known as mass or bulk mailing. There are several alternatives for mass mailing. One way consists on specifying all recipient addresses with the <tt>Bcc</tt> header, separating the addresses with commas (,), or using the <tt><a href="#function_SetMultipleEncodedEmailHeader">SetMultipleEncodedEmailHeader</a></tt> function. This way you only need to send one message that is distributed to all recipients by your mail transfer system.</p> +<p> Unfortunately, many mail account providers like Hotmail, tend to consider messages sent this way as spam because the real recipients addresses are not visible in <tt>To</tt> of <tt>Cc</tt> headers. So, this method is no longer a good solution these days.</p> +<p> The alternative is to send a separate message to each recipient by iteratively setting the <tt>To</tt> header with each recipient address and calling the <tt><a href="#function_Send">Send</a></tt> function. This way tends to take too much time and CPU as the number of recipients grow.</p> +<p> When sending messages to many recipients, call the <tt><a href="#function_SetBulkMail">SetBulkMail</a></tt> function to hint the class to optimize the way it works to make the delivery of the messages more efficient and eventually faster.</p> +<p> The actual optimizations that are performed depend on the delivery method that is used by this class or any of its subclasses specialized on the different delivery methods that are supported. Check the documentation of the subclass that you use to learn about the optimizations that are performed, if any.</p> +<p> If you intend to send messages with the same body to all recipients, the class can optimize the generation of the messages and reduce significantly the composition time if you set the <tt><a href="#variable_cache_body">cache_body</a></tt> variable to <tt>1</tt>.</p> +<p> If you really need to personalize the content of a message part with different text, HTML or file to each recipient, you should use the <tt><a href="#function_ReplacePart">ReplacePart</a></tt> function to avoid as much as possible the overhead of composing a new message to each of the recipients of the mailing.</p> +<p> If you are sending personalized messages to multiple recipients but the messages include attached or embedded files that are the same for all recipients, you should also set the 'Cached' option of the <tt><a href="#argument_CreateFilePart_file">file</a></tt> parameter of the <tt><a href="#function_CreateFilePart">CreateFilePart</a></tt> function.</p> +<p> Other than that, take a look at the documentation of the this class sub-classes that may be used in your PHP environment, as these may provide more efficient delivery solutions for mass mailing.</p> +<p> <b>- Error handling</b></p> +<p> Most of the functions of this class that may fail, return an error message string that describes the error that has occurred. If there was no error, the functions return an empty string.</p> +<p> Verifying the return value of all the functions to determine whether there was an error is a tedious task to implement for most developers. To avoid this problem, this class supports <i>cumulative error checking</i>.</p> +<p> Cumulative error checking means that when an error occurs, the class stores the error message in the <tt><a href="#variable_error">error</a></tt> variable. Then, when another function that may fail is called, it does nothing and immediately returns the same error message.</p> +<p> This way, the developers only need to check the return value of the last function that is called, which is usually the <tt><a href="#function_Send">Send</a></tt> function. </p> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> +<hr /> +<ul> +<h2><li><a name="variables"></a><a name="4.1.1">Variables</a></li></h2> +<ul> +<li><tt><a href="#variable_email_regular_expression">email_regular_expression</a></tt></li><br /> +<li><tt><a href="#variable_mailer">mailer</a></tt></li><br /> +<li><tt><a href="#variable_mailer_delivery">mailer_delivery</a></tt></li><br /> +<li><tt><a href="#variable_default_charset">default_charset</a></tt></li><br /> +<li><tt><a href="#variable_line_quote_prefix">line_quote_prefix</a></tt></li><br /> +<li><tt><a href="#variable_break_long_lines">break_long_lines</a></tt></li><br /> +<li><tt><a href="#variable_file_buffer_length">file_buffer_length</a></tt></li><br /> +<li><tt><a href="#variable_debug">debug</a></tt></li><br /> +<li><tt><a href="#variable_cache_body">cache_body</a></tt></li><br /> +<li><tt><a href="#variable_error">error</a></tt></li><br /> +<p><a href="#table_of_contents">Table of contents</a></p> +<h3><a name="variable_email_regular_expression"></a><li><a name="5.2.11">email_regular_expression</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>'^([-!#$%&\'*+./0-9=?A-Z^_`a-z{|}~])+@([-!#$%&\'*+/0-9=?A-Z^_`a-z{|}~]+\\.)+[a-zA-Z]{2,6}$'</tt></p> +<h3>Purpose</h3> +<p>Specify the regular expression that is used by the <tt><a href="#function_ValidateEmailAddress">ValidateEmailAddress</a></tt> function to verify whether a given e-mail address may be valid.</p> +<h3>Usage</h3> +<p>Do not change this variable unless you have reason to believe that it is rejecting existing e-mail addresses that are known to be valid.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_mailer"></a><li><a name="5.2.12">mailer</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>'http://www.phpclasses.org/mimemessage $Revision: 1.85 $'</tt></p> +<h3>Purpose</h3> +<p>Specify the base text that is used identify the name and the version of the class that is used to send the message by setting an implicit the <tt>X-Mailer</tt> message header. This is meant mostly to assist on the debugging of delivery problems.</p> +<h3>Usage</h3> +<p>Change this to set another mailer identification string or leave it to an empty string to prevent that the <tt>X-Mailer</tt> header be added to the message.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_mailer_delivery"></a><li><a name="5.2.13">mailer_delivery</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>'mail'</tt></p> +<h3>Purpose</h3> +<p>Specify the text that is used to identify the mail delivery class or sub-class. This text is appended to the <tt>X-Mailer</tt> header text defined by the <tt><a href="#variable_mailer">mailer</a></tt> variable.</p> +<h3>Usage</h3> +<p>This variable should only be redefined by the different mail delivery sub-classes.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_default_charset"></a><li><a name="5.2.14">default_charset</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>'ISO-8859-1'</tt></p> +<h3>Purpose</h3> +<p>Specify the default character set to be assumed for the message headers and body text.</p> +<h3>Usage</h3> +<p>Change this variable to the correct character set name if it is different than the default.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_line_quote_prefix"></a><li><a name="5.2.15">line_quote_prefix</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>'> '</tt></p> +<h3>Purpose</h3> +<p>Specify the default line quote prefix text used by the <tt><a href="#function_QuoteText">QuoteText</a></tt> function.</p> +<h3>Usage</h3> +<p>Change it only if you prefer to quote lines marking them with a different line prefix.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_break_long_lines"></a><li><a name="5.2.16">break_long_lines</a></li></h3> +<h3>Type</h3> +<p><tt><i>bool</i></tt></p> +<h3>Default value</h3> +<p><tt>1</tt></p> +<h3>Purpose</h3> +<p>Determine whether lines exceeding the length limit will be broken by the line break character when using the <tt><a href="#function_WrapText">WrapText</a></tt> function.</p> +<h3>Usage</h3> +<p>Change it only if you to avoid breaking long lines without any space characters, like for instance of messages with long URLs.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_file_buffer_length"></a><li><a name="5.2.17">file_buffer_length</a></li></h3> +<h3>Type</h3> +<p><tt><i>int</i></tt></p> +<h3>Default value</h3> +<p><tt>8000</tt></p> +<h3>Purpose</h3> +<p>Specify the length of the buffer that is used to read files in chunks of limited size.</p> +<h3>Usage</h3> +<p>The default value may be increased if you have plenty of memory and want to benefit from additional speed when processing the files that are used to compose messages.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_debug"></a><li><a name="5.2.18">debug</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>''</tt></p> +<h3>Purpose</h3> +<p>Specify the name of a function that is called whenever an error occurs.</p> +<h3>Usage</h3> +<p>If you need to track the errors that may happen during the use of the class, set this variable to the name of a callback function. It should take only one argument that is the error message. When this variable is set to an empty string, no debug callback function is called.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_cache_body"></a><li><a name="5.2.19">cache_body</a></li></h3> +<h3>Type</h3> +<p><tt><i>bool</i></tt></p> +<h3>Default value</h3> +<p><tt>0</tt></p> +<h3>Purpose</h3> +<p>Specify whether the message bodies that are generated by the class before sending, should be cached in memory to be reused on the next message delivery.</p> +<h3>Usage</h3> +<p>Set this variable to <tt>1</tt> if you intend to send the a message with the same body to many recipients, so the class avoids the overhead of regenerating messages with the same content.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_error"></a><li><a name="5.2.20">error</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>''</tt></p> +<h3>Purpose</h3> +<p>Store the last error return by any function that may fail due to some error.</p> +<h3>Usage</h3> +<p>Do not change this variable value unless you intend to clear the error status by setting it to an empty string.</p> +<p><a href="#variables">Variables</a></p> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> +<hr /> +<ul> +<h2><li><a name="functions"></a><a name="6.1.1">Functions</a></li></h2> +<ul> +<li><tt><a href="#function_ValidateEmailAddress">ValidateEmailAddress</a></tt></li><br /> +<li><tt><a href="#function_WrapText">WrapText</a></tt></li><br /> +<li><tt><a href="#function_CenterText">CenterText</a></tt></li><br /> +<li><tt><a href="#function_Ruler">Ruler</a></tt></li><br /> +<li><tt><a href="#function_QuoteText">QuoteText</a></tt></li><br /> +<li><tt><a href="#function_SetHeader">SetHeader</a></tt></li><br /> +<li><tt><a href="#function_SetEncodedHeader">SetEncodedHeader</a></tt></li><br /> +<li><tt><a href="#function_SetEncodedEmailHeader">SetEncodedEmailHeader</a></tt></li><br /> +<li><tt><a href="#function_SetMultipleEncodedEmailHeader">SetMultipleEncodedEmailHeader</a></tt></li><br /> +<li><tt><a href="#function_ResetMessage">ResetMessage</a></tt></li><br /> +<li><tt><a href="#function_AddPart">AddPart</a></tt></li><br /> +<li><tt><a href="#function_ReplacePart">ReplacePart</a></tt></li><br /> +<li><tt><a href="#function_CreatePlainTextPart">CreatePlainTextPart</a></tt></li><br /> +<li><tt><a href="#function_AddPlainTextPart">AddPlainTextPart</a></tt></li><br /> +<li><tt><a href="#function_CreateQuotedPrintableTextPart">CreateQuotedPrintableTextPart</a></tt></li><br /> +<li><tt><a href="#function_AddQuotedPrintableTextPart">AddQuotedPrintableTextPart</a></tt></li><br /> +<li><tt><a href="#function_CreateHTMLPart">CreateHTMLPart</a></tt></li><br /> +<li><tt><a href="#function_AddHTMLPart">AddHTMLPart</a></tt></li><br /> +<li><tt><a href="#function_CreateQuotedPrintableHTMLPart">CreateQuotedPrintableHTMLPart</a></tt></li><br /> +<li><tt><a href="#function_AddQuotedPrintableHTMLPart">AddQuotedPrintableHTMLPart</a></tt></li><br /> +<li><tt><a href="#function_CreateFilePart">CreateFilePart</a></tt></li><br /> +<li><tt><a href="#function_AddFilePart">AddFilePart</a></tt></li><br /> +<li><tt><a href="#function_CreateMessagePart">CreateMessagePart</a></tt></li><br /> +<li><tt><a href="#function_AddMessagePart">AddMessagePart</a></tt></li><br /> +<li><tt><a href="#function_CreateAlternativeMultipart">CreateAlternativeMultipart</a></tt></li><br /> +<li><tt><a href="#function_AddAlternativeMultipart">AddAlternativeMultipart</a></tt></li><br /> +<li><tt><a href="#function_CreateRelatedMultipart">CreateRelatedMultipart</a></tt></li><br /> +<li><tt><a href="#function_AddRelatedMultipart">AddRelatedMultipart</a></tt></li><br /> +<li><tt><a href="#function_CreateMixedMultipart">CreateMixedMultipart</a></tt></li><br /> +<li><tt><a href="#function_AddMixedMultipart">AddMixedMultipart</a></tt></li><br /> +<li><tt><a href="#function_GetPartContentID">GetPartContentID</a></tt></li><br /> +<li><tt><a href="#function_GetDataURL">GetDataURL</a></tt></li><br /> +<li><tt><a href="#function_Send">Send</a></tt></li><br /> +<li><tt><a href="#function_GetMessage">GetMessage</a></tt></li><br /> +<li><tt><a href="#function_GetMessageSize">GetMessageSize</a></tt></li><br /> +<li><tt><a href="#function_Mail">Mail</a></tt></li><br /> +<li><tt><a href="#function_SetBulkMail">SetBulkMail</a></tt></li><br /> +<p><a href="#table_of_contents">Table of contents</a></p> +<h3><a name="function_ValidateEmailAddress"></a><li><a name="7.2.38">ValidateEmailAddress</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>bool</i> ValidateEmailAddress(</tt><ul> +<tt><i>string</i> </tt><tt><a href="#argument_ValidateEmailAddress_address">address</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Determine whether a given e-mail address may be valid.</p> +<h3>Usage</h3> +<p>Just pass the e-mail <tt><a href="#argument_ValidateEmailAddress_address">address</a></tt> to be checked as function argument. This function uses the regular expression defined by the <tt><a href="#variable_email_regular_expression">email_regular_expression</a></tt> variable to check the address.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_ValidateEmailAddress_address">address</a></b></tt> - Specify the e-mail address to be validated.</p> +</ul> +<h3>Return value</h3> +<p>The function returns <tt>1</tt> if the specified address may be valid.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_WrapText"></a><li><a name="9.2.39">WrapText</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> WrapText(</tt><ul> +<tt><i>string</i> </tt><tt><a href="#argument_WrapText_text">text</a></tt><tt>,</tt><br /> +<tt><i>int</i> </tt><tt><a href="#argument_WrapText_line_length">line_length</a></tt> [default 0]<tt>,</tt><br /> +<tt><i>string</i> </tt><tt><a href="#argument_WrapText_line_break">line_break</a></tt> [default '']<tt>,</tt><br /> +<tt><i>string</i> </tt><tt><a href="#argument_WrapText_line_prefix">line_prefix</a></tt> [default '']</ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Split a text in lines that do not exceed the length limit avoiding to break it in the middle of any words.</p> +<h3>Usage</h3> +<p>Just pass the <tt><a href="#argument_WrapText_text">text</a></tt> to be wrapped.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_WrapText_text">text</a></b></tt> - Text to be wrapped.</p> +<p><tt><b><a name="argument_WrapText_line_length">line_length</a></b></tt> - Line length limit. Pass a value different than <tt>0</tt> to use a line length limit other than the default of 75 characters.</p> +<p><tt><b><a name="argument_WrapText_line_break">line_break</a></b></tt> - Character sequence that is used to break the lines longer than the length limit. Pass a non-empty to use a line breaking sequence other than the default <tt>"\n"</tt>.</p> +<p><tt><b><a name="argument_WrapText_line_prefix">line_prefix</a></b></tt> - Character sequence that is used to insert in the beginning of all lines.</p> +</ul> +<h3>Return value</h3> +<p>The wrapped text eventually broken in multiple lines that do not exceed the line length limit.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_CenterText"></a><li><a name="11.2.40">CenterText</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> CenterText(</tt><ul> +<tt><i>string</i> </tt><tt><a href="#argument_CenterText_text">text</a></tt><tt>,</tt><br /> +<tt><i>int</i> </tt><tt><a href="#argument_CenterText_line_length">line_length</a></tt> [default 0]</ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Center a text in the middle of line.</p> +<h3>Usage</h3> +<p>Just pass the <tt><a href="#argument_CenterText_text">text</a></tt> to be centered.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_CenterText_text">text</a></b></tt> - Text to be centered.</p> +<p><tt><b><a name="argument_CenterText_line_length">line_length</a></b></tt> - Line length limit. Pass a value different than <tt>0</tt> to use a line length limit other than the default of 75 characters.</p> +</ul> +<h3>Return value</h3> +<p>The centered text.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_Ruler"></a><li><a name="13.2.41">Ruler</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> Ruler(</tt><ul> +<tt><i>int</i> </tt><tt><a href="#argument_Ruler_line_length">line_length</a></tt> [default 0]</ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Generate a line with characters that can be displayed as a separator ruler in a text message.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_Ruler_line_length">line_length</a></b></tt> - Line length limit. Pass a value different than <tt>0</tt> to use a line length limit other than the default of 75 characters.</p> +</ul> +<h3>Return value</h3> +<p>The ruler line string.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_QuoteText"></a><li><a name="15.2.42">QuoteText</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> QuoteText(</tt><ul> +<tt><i>string</i> </tt><tt><a href="#argument_QuoteText_text">text</a></tt><tt>,</tt><br /> +<tt><i>string</i> </tt><tt><a href="#argument_QuoteText_quote_prefix">quote_prefix</a></tt> [default '']</ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Mark a text block to appear like in reply messages composed with common e-mail programs that include text from the original message being replied.</p> +<h3>Usage</h3> +<p>Just pass the <tt><a href="#argument_QuoteText_text">text</a></tt> to be marked as a quote.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_QuoteText_text">text</a></b></tt> - Text to be quoted.</p> +<p><tt><b><a name="argument_QuoteText_quote_prefix">quote_prefix</a></b></tt> - Character sequence that is inserted in the beginning of all lines as a quote mark. Set to an empty string to tell the function to use the default specified by the <tt><a href="#variable_line_quote_prefix">line_quote_prefix</a></tt> variable.</p> +</ul> +<h3>Return value</h3> +<p>The quoted text with all lines prefixed with a quote prefix mark.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_SetHeader"></a><li><a name="17.2.43">SetHeader</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> SetHeader(</tt><ul> +<tt><i>string</i> </tt><tt><a href="#argument_SetHeader_header">header</a></tt><tt>,</tt><br /> +<tt><i>string</i> </tt><tt><a href="#argument_SetHeader_value">value</a></tt><tt>,</tt><br /> +<tt><i>string</i> </tt><tt><a href="#argument_SetHeader_encoding_charset">encoding_charset</a></tt> [default '']</ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Set the value of a message header.</p> +<h3>Usage</h3> +<p>Use this function to set the values of the headers of the message that may be needed. There are some message headers that are automatically set by the class when the message is sent. Others must be defined before sending. Here follows the list of the names of the headers that must be set before sending:</p> +<p> </p> +<p> <b>Message subject</b> - <tt>Subject</tt></p> +<p> <b>Sender address</b> - <tt>From</tt></p> +<p> <b>Recipient addresses</b> - <tt>To</tt>, <tt>Cc</tt> and <tt>Bcc</tt></p> +<p> Each of the recipient address headers may contain one or more addresses. Multiple addresses must be separated by a comma and a space.</p> +<p> <b>Return path address</b> - <tt>Return-Path</tt></p> +<p> Optional header to specify the address where the message should be bounced in case it is not possible to deliver it.</p> +<p> In reality this is a virtual header. This means that adding this header to a message will not do anything by itself. However, this class looks for this header to adjust the message delivery procedure in such way that the Message Transfer Agent (MTA) system is hinted to direct any bounced messages to the address specified by this header.</p> +<p> Note that under some systems there is no way to set the return path address programmatically. This is the case when using the PHP <tt>mail()</tt> function under Windows where the return path address should be set in the <tt>php.ini</tt> configuration file.</p> +<p> Keep in mind that even when it is possible to set the return path address, the systems of some e-mail account providers may ignore this address and send bounced messages to the sender address. This is a bug of those systems. There is nothing that can be done other than complain.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_SetHeader_header">header</a></b></tt> - Name of the header.</p> +<p><tt><b><a name="argument_SetHeader_value">value</a></b></tt> - Text value for the header.</p> +<p><tt><b><a name="argument_SetHeader_encoding_charset">encoding_charset</a></b></tt> - Character set used in the header value. If it is set to an empty string, it is assumed the character set defined by the <tt><a href="#variable_default_charset">default_charset</a></tt> variable.</p> +</ul> +<h3>Return value</h3> +<p>An error message in case there was an error or an empty string otherwise. This return value may be safely ignored if the function parameters are set correctly.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_SetEncodedHeader"></a><li><a name="19.2.44">SetEncodedHeader</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> SetEncodedHeader(</tt><ul> +<tt><i>string</i> </tt><tt><a href="#argument_SetEncodedHeader_header">header</a></tt><tt>,</tt><br /> +<tt><i>string</i> </tt><tt><a href="#argument_SetEncodedHeader_value">value</a></tt><tt>,</tt><br /> +<tt><i>string</i> </tt><tt><a href="#argument_SetEncodedHeader_encoding_charset">encoding_charset</a></tt> [default '']</ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>The same as the <tt><a href="#function_SetHeader">SetHeader</a></tt> function assuming the default character set specified by the <tt><a href="#variable_default_charset">default_charset</a></tt> variable.</p> +<h3>Usage</h3> +<p>See the <tt><a href="#function_SetHeader">SetHeader</a></tt> function.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_SetEncodedHeader_header">header</a></b></tt> - Name of the header.</p> +<p><tt><b><a name="argument_SetEncodedHeader_value">value</a></b></tt> - Text value for the header.</p> +<p><tt><b><a name="argument_SetEncodedHeader_encoding_charset">encoding_charset</a></b></tt> - Character set used in the header value. If it is set to an empty string, it is assumed the character set defined by the <tt><a href="#variable_default_charset">default_charset</a></tt> variable.</p> +</ul> +<h3>Return value</h3> +<p>An error message in case there was an error or an empty string otherwise. This return value may be safely ignored if the function parameters are set correctly.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_SetEncodedEmailHeader"></a><li><a name="21.2.45">SetEncodedEmailHeader</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> SetEncodedEmailHeader(</tt><ul> +<tt><i>string</i> </tt><tt><a href="#argument_SetEncodedEmailHeader_header">header</a></tt><tt>,</tt><br /> +<tt><i>string</i> </tt><tt><a href="#argument_SetEncodedEmailHeader_address">address</a></tt><tt>,</tt><br /> +<tt><i>string</i> </tt><tt><a href="#argument_SetEncodedEmailHeader_name">name</a></tt><tt>,</tt><br /> +<tt><i>string</i> </tt><tt><a href="#argument_SetEncodedEmailHeader_encoding_charset">encoding_charset</a></tt> [default '']</ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Set the value of an header that is meant to represent the e-mail address of a person or entity with a known name. This is meant mostly to set the <tt>From</tt>, <tt>To</tt>, <tt>Cc</tt> and <tt>Bcc</tt> headers.</p> +<h3>Usage</h3> +<p>Use this function like the <tt><a href="#function_SetHeader">SetHeader</a></tt> specifying the e-mail <tt><a href="#argument_SetEncodedEmailHeader_address">address</a></tt> as header value and also specifying the <tt><a href="#argument_SetEncodedEmailHeader_name">name</a></tt> of the known person or entity.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_SetEncodedEmailHeader_header">header</a></b></tt> - Name of the header.</p> +<p><tt><b><a name="argument_SetEncodedEmailHeader_address">address</a></b></tt> - E-mail address value.</p> +<p><tt><b><a name="argument_SetEncodedEmailHeader_name">name</a></b></tt> - Person or entity name associated with the specified e-mail address.</p> +<p><tt><b><a name="argument_SetEncodedEmailHeader_encoding_charset">encoding_charset</a></b></tt> - Character set used in the header value. If it is set to an empty string, it is assumed the character set defined by the <tt><a href="#variable_default_charset">default_charset</a></tt> variable.</p> +</ul> +<h3>Return value</h3> +<p>An error message in case there was an error or an empty string otherwise. This return value may be safely ignored if the function parameters are set correctly.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_SetMultipleEncodedEmailHeader"></a><li><a name="23.2.46">SetMultipleEncodedEmailHeader</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> SetMultipleEncodedEmailHeader(</tt><ul> +<tt><i>string</i> </tt><tt><a href="#argument_SetMultipleEncodedEmailHeader_header">header</a></tt><tt>,</tt><br /> +<tt>(input and output) <i>array</i> </tt><tt><a href="#argument_SetMultipleEncodedEmailHeader_addresses">addresses</a></tt><tt>,</tt><br /> +<tt><i>string</i> </tt><tt><a href="#argument_SetMultipleEncodedEmailHeader_encoding_charset">encoding_charset</a></tt> [default '']</ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Set the value of an header that is meant to represent a list of e-mail addresses of names of people or entities. This is meant mostly to set the <tt>To</tt>, <tt>Cc</tt> and <tt>Bcc</tt> headers.</p> +<h3>Usage</h3> +<p>Use this function specifying the <tt><a href="#argument_SetMultipleEncodedEmailHeader_header">header</a></tt> and all the <tt><a href="#argument_SetMultipleEncodedEmailHeader_addresses">addresses</a></tt> in an associative array that should have the email addresses as entry indexes and the name of the respective people or entities as entry values.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_SetMultipleEncodedEmailHeader_header">header</a></b></tt> - Name of the header.</p> +<p><tt><b><a name="argument_SetMultipleEncodedEmailHeader_addresses">addresses</a></b></tt> - List of all email addresses and associated person or entity names.</p> +<p><tt><b><a name="argument_SetMultipleEncodedEmailHeader_encoding_charset">encoding_charset</a></b></tt> - Character set used in the header value. If it is set to an empty string, it is assumed the character set defined by the <tt><a href="#variable_default_charset">default_charset</a></tt> variable.</p> +</ul> +<h3>Return value</h3> +<p>An error message in case there was an error or an empty string otherwise. This return value may be safely ignored if the function parameters are set correctly.</p> +<h3>Example</h3> +<p></p> +<pre>$message_object->SetMultipleEncodedEmailHeader('Bcc', array( + 'peter@gabriel.org' => 'Peter Gabriel', + 'paul@simon.net' => 'Paul Simon', + 'mary@chain.com' => 'Mary Chain' +));</pre> +<p></p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_ResetMessage"></a><li><a name="25.2.47">ResetMessage</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i></i> ResetMessage(</tt><tt>)</tt></p> +<h3>Purpose</h3> +<p>Restore the content of the message to the initial state when the class object is created, i.e. without any headers or body parts.</p> +<h3>Usage</h3> +<p>Use this function if you want to start composing a completely new message.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_AddPart"></a><li><a name="25.2.48">AddPart</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> AddPart(</tt><ul> +<tt><i>int</i> </tt><tt><a href="#argument_AddPart_part">part</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Add a previously created part to the message.</p> +<h3>Usage</h3> +<p>Use any of the functions to create standalone message parts and then use this function to add them to the message.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_AddPart_part">part</a></b></tt> - Number of the part as returned by the function that originally created it.</p> +</ul> +<h3>Return value</h3> +<p>An error message in case there was an error or an empty string otherwise. This return value may be safely ignored if the function parameters are set correctly.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_ReplacePart"></a><li><a name="27.2.49">ReplacePart</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> ReplacePart(</tt><ul> +<tt><i>int</i> </tt><tt><a href="#argument_ReplacePart_old_part">old_part</a></tt><tt>,</tt><br /> +<tt><i>int</i> </tt><tt><a href="#argument_ReplacePart_new_part">new_part</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Replace a message part already added to the message with a newly created part. The replaced part gets the definition of the replacing part. The replacing part is discarded and its part number becomes free for creation of a new part.</p> +<h3>Usage</h3> +<p>Use one of the functions to create message parts and then pass the returned part numbers to this function.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_ReplacePart_old_part">old_part</a></b></tt> - Number of the previously added part.</p> +<p><tt><b><a name="argument_ReplacePart_new_part">new_part</a></b></tt> - Number of the replacing part.</p> +</ul> +<h3>Return value</h3> +<p>An error message in case there was an error or an empty string otherwise. This return value may be safely ignored if the function parameters are set correctly.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_CreatePlainTextPart"></a><li><a name="29.2.50">CreatePlainTextPart</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> CreatePlainTextPart(</tt><ul> +<tt><i>string</i> </tt><tt><a href="#argument_CreatePlainTextPart_text">text</a></tt><tt>,</tt><br /> +<tt><i>string</i> </tt><tt><a href="#argument_CreatePlainTextPart_charset">charset</a></tt><tt>,</tt><br /> +<tt>(output) <i>int &</i> </tt><tt><a href="#argument_CreatePlainTextPart_part">part</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Create a plain text message part.</p> +<h3>Usage</h3> +<p>Pass an ASCII (7 bits) <tt><a href="#argument_CreatePlainTextPart_text">text</a></tt> string and get the created part number in the <tt><a href="#argument_CreatePlainTextPart_part">part</a></tt> that is returned by reference.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_CreatePlainTextPart_text">text</a></b></tt> - Text of the message part to create.</p> +<p><tt><b><a name="argument_CreatePlainTextPart_charset">charset</a></b></tt> - Character set used in the part text. If it is set to an empty string, it is assumed the character set defined by the <tt><a href="#variable_default_charset">default_charset</a></tt> variable.</p> +<p><tt><b><a name="argument_CreatePlainTextPart_part">part</a></b></tt> - Number of the created part that is returned by reference.</p> +</ul> +<h3>Return value</h3> +<p>An error message in case there was an error or an empty string otherwise. This return value may be safely ignored if the function parameters are set correctly.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_AddPlainTextPart"></a><li><a name="31.2.51">AddPlainTextPart</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> AddPlainTextPart(</tt><ul> +<tt><i>string</i> </tt><tt><a href="#argument_AddPlainTextPart_text">text</a></tt><tt>,</tt><br /> +<tt><i>string</i> </tt><tt><a href="#argument_AddPlainTextPart_charset">charset</a></tt> [default '']</ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Add a plain text part to the message.</p> +<h3>Usage</h3> +<p>Pass an ASCII (7 bits) <tt><a href="#argument_AddPlainTextPart_text">text</a></tt> string.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_AddPlainTextPart_text">text</a></b></tt> - Text of the message part to add.</p> +<p><tt><b><a name="argument_AddPlainTextPart_charset">charset</a></b></tt> - Character set used in the part text. If it is set to an empty string, it is assumed the character set defined by the <tt><a href="#variable_default_charset">default_charset</a></tt> variable.</p> +</ul> +<h3>Return value</h3> +<p>An error message in case there was an error or an empty string otherwise. This return value may be safely ignored if the function parameters are set correctly.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_CreateQuotedPrintableTextPart"></a><li><a name="33.2.52">CreateQuotedPrintableTextPart</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> CreateQuotedPrintableTextPart(</tt><ul> +<tt><i>string</i> </tt><tt><a href="#argument_CreateQuotedPrintableTextPart_text">text</a></tt><tt>,</tt><br /> +<tt><i>string</i> </tt><tt><a href="#argument_CreateQuotedPrintableTextPart_charset">charset</a></tt><tt>,</tt><br /> +<tt>(output) <i>int &</i> </tt><tt><a href="#argument_CreateQuotedPrintableTextPart_part">part</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Create a text message part that may contain non-ASCII characters (8 bits or more).</p> +<h3>Usage</h3> +<p>Pass a <tt><a href="#argument_CreateQuotedPrintableTextPart_text">text</a></tt> string and get the created part number in the <tt><a href="#argument_CreateQuotedPrintableTextPart_part">part</a></tt> that is returned by reference.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_CreateQuotedPrintableTextPart_text">text</a></b></tt> - Text of the message part to create.</p> +<p><tt><b><a name="argument_CreateQuotedPrintableTextPart_charset">charset</a></b></tt> - Character set used in the part text. If it is set to an empty string, it is assumed the character set defined by the <tt><a href="#variable_default_charset">default_charset</a></tt> variable.</p> +<p><tt><b><a name="argument_CreateQuotedPrintableTextPart_part">part</a></b></tt> - Number of the created part that is returned by reference.</p> +</ul> +<h3>Return value</h3> +<p>An error message in case there was an error or an empty string otherwise. This return value may be safely ignored if the function parameters are set correctly.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_AddQuotedPrintableTextPart"></a><li><a name="35.2.53">AddQuotedPrintableTextPart</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> AddQuotedPrintableTextPart(</tt><ul> +<tt><i>string</i> </tt><tt><a href="#argument_AddQuotedPrintableTextPart_text">text</a></tt><tt>,</tt><br /> +<tt><i>string</i> </tt><tt><a href="#argument_AddQuotedPrintableTextPart_charset">charset</a></tt> [default '']</ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Add a text part to the message that may contain non-ASCII characters (8 bits or more).</p> +<h3>Usage</h3> +<p>Pass a <tt><a href="#argument_AddQuotedPrintableTextPart_text">text</a></tt> string.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_AddQuotedPrintableTextPart_text">text</a></b></tt> - Text of the message part to create.</p> +<p><tt><b><a name="argument_AddQuotedPrintableTextPart_charset">charset</a></b></tt> - Character set used in the part text. If it is set to an empty string, it is assumed the character set defined by the <tt><a href="#variable_default_charset">default_charset</a></tt> variable.</p> +</ul> +<h3>Return value</h3> +<p>An error message in case there was an error or an empty string otherwise. This return value may be safely ignored if the function parameters are set correctly.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_CreateHTMLPart"></a><li><a name="37.2.54">CreateHTMLPart</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> CreateHTMLPart(</tt><ul> +<tt><i>string</i> </tt><tt><a href="#argument_CreateHTMLPart_html">html</a></tt><tt>,</tt><br /> +<tt><i>string</i> </tt><tt><a href="#argument_CreateHTMLPart_charset">charset</a></tt><tt>,</tt><br /> +<tt>(output) <i>int &</i> </tt><tt><a href="#argument_CreateHTMLPart_part">part</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Create an HTML message part only with ASCII characters (7 bit).</p> +<h3>Usage</h3> +<p>Pass an ASCII (7 bits) <tt><a href="#argument_CreateHTMLPart_html">html</a></tt> text string and get the created part number in the <tt><a href="#argument_CreateHTMLPart_part">part</a></tt> that is returned by reference.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_CreateHTMLPart_html">html</a></b></tt> - HTML of the message part to create.</p> +<p><tt><b><a name="argument_CreateHTMLPart_charset">charset</a></b></tt> - Character set used in the part text. If it is set to an empty string, it is assumed the character set defined by the <tt><a href="#variable_default_charset">default_charset</a></tt> variable.</p> +<p><tt><b><a name="argument_CreateHTMLPart_part">part</a></b></tt> - Number of the created part that is returned by reference.</p> +</ul> +<h3>Return value</h3> +<p>An error message in case there was an error or an empty string otherwise. This return value may be safely ignored if the function parameters are set correctly.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_AddHTMLPart"></a><li><a name="39.2.55">AddHTMLPart</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> AddHTMLPart(</tt><ul> +<tt><i>string</i> </tt><tt><a href="#argument_AddHTMLPart_html">html</a></tt><tt>,</tt><br /> +<tt><i>string</i> </tt><tt><a href="#argument_AddHTMLPart_charset">charset</a></tt> [default '']</ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Add an HTML part to the message only with ASCII characters.</p> +<h3>Usage</h3> +<p>Pass an <tt><a href="#argument_AddHTMLPart_html">html</a></tt> text string.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_AddHTMLPart_html">html</a></b></tt> - HTML of the message part to create.</p> +<p><tt><b><a name="argument_AddHTMLPart_charset">charset</a></b></tt> - Character set used in the part text. If it is set to an empty string, it is assumed the character set defined by the <tt><a href="#variable_default_charset">default_charset</a></tt> variable.</p> +</ul> +<h3>Return value</h3> +<p>An error message in case there was an error or an empty string otherwise. This return value may be safely ignored if the function parameters are set correctly.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_CreateQuotedPrintableHTMLPart"></a><li><a name="41.2.56">CreateQuotedPrintableHTMLPart</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> CreateQuotedPrintableHTMLPart(</tt><ul> +<tt><i>string</i> </tt><tt><a href="#argument_CreateQuotedPrintableHTMLPart_html">html</a></tt><tt>,</tt><br /> +<tt><i>string</i> </tt><tt><a href="#argument_CreateQuotedPrintableHTMLPart_charset">charset</a></tt><tt>,</tt><br /> +<tt>(output) <i>int &</i> </tt><tt><a href="#argument_CreateQuotedPrintableHTMLPart_part">part</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Create an HTML message part that may contain non-ASCII characters (8 bits or more).</p> +<h3>Usage</h3> +<p>Pass a <tt><a href="#argument_CreateQuotedPrintableHTMLPart_html">html</a></tt> text string and get the created part number in the <tt><a href="#argument_CreateQuotedPrintableHTMLPart_part">part</a></tt> that is returned by reference.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_CreateQuotedPrintableHTMLPart_html">html</a></b></tt> - HTML of the message part to create.</p> +<p><tt><b><a name="argument_CreateQuotedPrintableHTMLPart_charset">charset</a></b></tt> - Character set used in the part text. If it is set to an empty string, it is assumed the character set defined by the <tt><a href="#variable_default_charset">default_charset</a></tt> variable.</p> +<p><tt><b><a name="argument_CreateQuotedPrintableHTMLPart_part">part</a></b></tt> - Number of the created part that is returned by reference.</p> +</ul> +<h3>Return value</h3> +<p>An error message in case there was an error or an empty string otherwise. This return value may be safely ignored if the function parameters are set correctly.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_AddQuotedPrintableHTMLPart"></a><li><a name="43.2.57">AddQuotedPrintableHTMLPart</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> AddQuotedPrintableHTMLPart(</tt><ul> +<tt><i>string</i> </tt><tt><a href="#argument_AddQuotedPrintableHTMLPart_html">html</a></tt><tt>,</tt><br /> +<tt><i>string</i> </tt><tt><a href="#argument_AddQuotedPrintableHTMLPart_charset">charset</a></tt> [default '']</ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Add an HTML part to the message that may contain non-ASCII characters (8 bits or more).</p> +<h3>Usage</h3> +<p>Pass a <tt><a href="#argument_AddQuotedPrintableHTMLPart_html">html</a></tt> text string.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_AddQuotedPrintableHTMLPart_html">html</a></b></tt> - HTML of the message part to create.</p> +<p><tt><b><a name="argument_AddQuotedPrintableHTMLPart_charset">charset</a></b></tt> - Character set used in the part text. If it is set to an empty string, it is assumed the character set defined by the <tt><a href="#variable_default_charset">default_charset</a></tt> variable.</p> +</ul> +<h3>Return value</h3> +<p>An error message in case there was an error or an empty string otherwise. This return value may be safely ignored if the function parameters are set correctly.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_CreateFilePart"></a><li><a name="45.2.58">CreateFilePart</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> CreateFilePart(</tt><ul> +<tt>(input and output) <i>array</i> </tt><tt><a href="#argument_CreateFilePart_file">file</a></tt><tt>,</tt><br /> +<tt>(output) <i>int &</i> </tt><tt><a href="#argument_CreateFilePart_part">part</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Create a message part to be handled as a file.</p> +<h3>Usage</h3> +<p>Pass a <tt><a href="#argument_CreateFilePart_file">file</a></tt> definition associative array and get the created part number in the <tt><a href="#argument_CreateFilePart_part">part</a></tt> that is returned by reference.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_CreateFilePart_file">file</a></b></tt> - Associative array to specify parameters that describe the file part. Here follows the list of supported parameters that should be used as indexes of the array:</p> +<p> <tt>FileName</tt></p> +<p> Name of the file from which the part data will be read when the message is generated. It may be a remote URL as long as your PHP installation is configured to allow accessing remote files with the <tt>fopen()</tt> function.</p> +<p> <tt>Data</tt></p> +<p> String that specifies the data of the file. This should be used as alternative data source to <tt>FileName</tt> for passing data available in memory, like for instance files stored in a database that was queried dynamically and the file contents was fetched into a string variable.</p> +<p> <tt>Name</tt></p> +<p> Name of the file that will appear in the message. If this parameter is missing the base name of the <tt>FileName</tt> parameter is used, if present.</p> +<p> <tt>Content-Type</tt></p> +<p> Content type of the part: <tt>text/plain</tt> for text, <tt>text/html</tt> for HTML, <tt>image/gif</tt> for GIF images, etc..</p> +<p> There is one special type named <tt>automatic/name</tt> that may be used to tell the class to try to guess the content type from the file name. Many file types are recognized from the file name extension. If the file name extension is not recognized, the default for binary data <tt>application/octet-stream</tt> is assumed.</p> +<p> <tt>Disposition</tt></p> +<p> Information to whether this file part is meant to be used as a file <tt>attachment</tt> or as a part meant to be displayed <tt>inline</tt>, eventually integrated with another related part.</p> +<p> <tt>Cache</tt></p> +<p> Boolean flag that indicates that this message part should be cached when generating the message body. Use only when sending many messages to multiple recipients, but this part does not change between each of the messages that are sent.</p> +<p> Note that it is also not worth using this option when setting the <tt><a href="#variable_cache_body">cache_body</a></tt>, as that variable makes the class cache the whole message body and the internal message parts will not be rebuilt.</p> +<p><tt><b><a name="argument_CreateFilePart_part">part</a></b></tt> - Number of the created part that is returned by reference.</p> +</ul> +<h3>Return value</h3> +<p>An error message in case there was an error or an empty string otherwise. This return value may be safely ignored if the function parameters are set correctly.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_AddFilePart"></a><li><a name="47.2.59">AddFilePart</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> AddFilePart(</tt><ul> +<tt>(input and output) <i>array</i> </tt><tt><a href="#argument_AddFilePart_file">file</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Add a message part to be handled as a file.</p> +<h3>Usage</h3> +<p>Pass a <tt><a href="#argument_AddFilePart_file">file</a></tt> definition associative array.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_AddFilePart_file">file</a></b></tt> - Associative array to specify parameters that describe the file part. See the <tt><a href="#argument_CreateFilePart_file">file</a></tt> argument description of the <tt><a href="#function_CreateFilePart">CreateFilePart</a></tt> function for an explanation about the supported file parameters.</p> +</ul> +<h3>Return value</h3> +<p>An error message in case there was an error or an empty string otherwise. This return value may be safely ignored if the function parameters are set correctly.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_CreateMessagePart"></a><li><a name="49.2.60">CreateMessagePart</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> CreateMessagePart(</tt><ul> +<tt>(input and output) <i>array</i> </tt><tt><a href="#argument_CreateMessagePart_message">message</a></tt><tt>,</tt><br /> +<tt>(output) <i>int &</i> </tt><tt><a href="#argument_CreateMessagePart_part">part</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Create a message part to encapsulate another message. This is usually meant to create an attachment that contains a message that was received and is being forwarded intact with the original the headers and body data.</p> +<h3>Usage</h3> +<p>This function should be used like the <tt><a href="#function_CreateFilePart">CreateFilePart</a></tt> function, passing the same parameters to the <tt><a href="#argument_CreateMessagePart_message">message</a></tt> argument.</p> +<p> The message to be encapsulated can be specified either as an existing file with the <tt>FileName</tt> parameter, or as string of data in memory with the <tt>Data</tt> parameter.</p> +<p> The <tt>Content-Type</tt> and <tt>Disposition</tt> file parameters do not need to be specified because they are overridden by this function.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_CreateMessagePart_message">message</a></b></tt> - Associative array that specifies definition parameters of the message file part.</p> +<p><tt><b><a name="argument_CreateMessagePart_part">part</a></b></tt> - Number of the created part that is returned by reference.</p> +</ul> +<h3>Return value</h3> +<p>An error message in case there was an error or an empty string otherwise. This return value may be safely ignored if the function parameters are set correctly.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_AddMessagePart"></a><li><a name="51.2.61">AddMessagePart</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> AddMessagePart(</tt><ul> +<tt>(input and output) <i>array</i> </tt><tt><a href="#argument_AddMessagePart_message">message</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Add a message part that encapsulates another message. This is usually meant to add an attachment that contains a message that was received and is being forwarded intact with the original the headers and body data.</p> +<h3>Usage</h3> +<p>This function should be used like the <tt><a href="#function_AddFilePart">AddFilePart</a></tt> function, passing the same parameters to the <tt><a href="#argument_AddMessagePart_message">message</a></tt> argument. See the <tt><a href="#function_CreateFilePart">CreateFilePart</a></tt> function for more details.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_AddMessagePart_message">message</a></b></tt> - Associative array that specifies definition parameters of the message file part.</p> +</ul> +<h3>Return value</h3> +<p>An error message in case there was an error or an empty string otherwise. This return value may be safely ignored if the function parameters are set correctly.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_CreateAlternativeMultipart"></a><li><a name="53.2.62">CreateAlternativeMultipart</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> CreateAlternativeMultipart(</tt><ul> +<tt>(input and output) <i>array</i> </tt><tt><a href="#argument_CreateAlternativeMultipart_parts">parts</a></tt><tt>,</tt><br /> +<tt>(output) <i>int &</i> </tt><tt><a href="#argument_CreateAlternativeMultipart_part">part</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Create a message part composed of multiple parts that can be displayed by the recipient e-mail program in alternative formats.</p> +<p> This is usually meant to create HTML messages with an alternative text part to be displayed by programs that cannot display HTML messages.</p> +<h3>Usage</h3> +<p>Create all the alternative message parts that are going to be sent and pass their numbers to the <tt><a href="#argument_CreateAlternativeMultipart_parts">parts</a></tt> array argument.</p> +<p> The least sophisticated part, usually the text part, should appear first in the parts array because the e-mail programs that support displaying more sophisticated message parts will pick the last part in the message that is supported.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_CreateAlternativeMultipart_parts">parts</a></b></tt> - Array with the numbers with all the alternative parts.</p> +<p><tt><b><a name="argument_CreateAlternativeMultipart_part">part</a></b></tt> - Number of the created part that is returned by reference.</p> +</ul> +<h3>Return value</h3> +<p>An error message in case there was an error or an empty string otherwise. This return value may be safely ignored if the function parameters are set correctly.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_AddAlternativeMultipart"></a><li><a name="55.2.63">AddAlternativeMultipart</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> AddAlternativeMultipart(</tt><ul> +<tt>(input and output) <i>array</i> </tt><tt><a href="#argument_AddAlternativeMultipart_parts">parts</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Add a message part composed of multiple parts that can be displayed by the recipient e-mail program in alternative formats.</p> +<p> This is usually meant to create HTML messages with an alternative text part to be displayed by programs that cannot display HTML messages.</p> +<h3>Usage</h3> +<p>Create all the alternative message parts that are going to be sent and pass their numbers to the <tt><a href="#argument_AddAlternativeMultipart_parts">parts</a></tt> array argument.</p> +<p> The least sophisticated part, usually the text part, should appear first in the parts array because the e-mail programs that support displaying more sophisticated message parts will pick the last part in the message that is supported.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_AddAlternativeMultipart_parts">parts</a></b></tt> - Array with the numbers with all the alternative parts.</p> +</ul> +<h3>Return value</h3> +<p>An error message in case there was an error or an empty string otherwise. This return value may be safely ignored if the function parameters are set correctly.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_CreateRelatedMultipart"></a><li><a name="57.2.64">CreateRelatedMultipart</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> CreateRelatedMultipart(</tt><ul> +<tt>(input and output) <i>array</i> </tt><tt><a href="#argument_CreateRelatedMultipart_parts">parts</a></tt><tt>,</tt><br /> +<tt>(output) <i>int &</i> </tt><tt><a href="#argument_CreateRelatedMultipart_part">part</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Create a message part that groups several related parts.</p> +<p> This is usually meant to group an HTML message part with images or other types of files that should be embedded in the same message and be displayed as a single part by the recipient e-mail program.</p> +<h3>Usage</h3> +<p>Create all the related message parts that are going to be sent and pass their numbers to the <tt><a href="#argument_CreateRelatedMultipart_parts">parts</a></tt> array argument.</p> +<p> When using this function to group an HTML message with embedded images or other related files, make sure that the HTML part number is the first listed in the <tt><a href="#argument_CreateRelatedMultipart_parts">parts</a></tt> array argument, or else the message may not appear correctly.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_CreateRelatedMultipart_parts">parts</a></b></tt> - Array with the numbers with all the related parts.</p> +<p><tt><b><a name="argument_CreateRelatedMultipart_part">part</a></b></tt> - Number of the created part that is returned by reference.</p> +</ul> +<h3>Return value</h3> +<p>An error message in case there was an error or an empty string otherwise. This return value may be safely ignored if the function parameters are set correctly.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_AddRelatedMultipart"></a><li><a name="59.2.65">AddRelatedMultipart</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> AddRelatedMultipart(</tt><ul> +<tt>(input and output) <i>array</i> </tt><tt><a href="#argument_AddRelatedMultipart_parts">parts</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Add a message part that groups several related parts.</p> +<p> This is usually meant to group an HTML message part with images or other types of files that should be embedded in the same message and be displayed as a single part by the recipient e-mail program.</p> +<h3>Usage</h3> +<p>Create all the related message parts that are going to be sent and pass their numbers to the <tt><a href="#argument_AddRelatedMultipart_parts">parts</a></tt> array argument.</p> +<p> When using this function to group an HTML message with embedded images or other related files, make sure that the HTML part number is the first listed in the <tt><a href="#argument_AddRelatedMultipart_parts">parts</a></tt> array argument, or else the message may not appear correctly.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_AddRelatedMultipart_parts">parts</a></b></tt> - Array with the numbers with all the related parts.</p> +</ul> +<h3>Return value</h3> +<p>An error message in case there was an error or an empty string otherwise. This return value may be safely ignored if the function parameters are set correctly.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_CreateMixedMultipart"></a><li><a name="61.2.66">CreateMixedMultipart</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> CreateMixedMultipart(</tt><ul> +<tt>(input and output) <i>array</i> </tt><tt><a href="#argument_CreateMixedMultipart_parts">parts</a></tt><tt>,</tt><br /> +<tt>(output) <i>int &</i> </tt><tt><a href="#argument_CreateMixedMultipart_part">part</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Create a message part that groups several independent parts.</p> +<p> Usually this is meant compose messages with one or more file attachments. However, it is not necessary to use this function as the class implicitly creates a <tt>multipart/mixed</tt> message when more than one part is added to the message.</p> +<h3>Usage</h3> +<p>Create all the independent message parts that are going to be sent and pass their numbers to the <tt><a href="#argument_CreateMixedMultipart_parts">parts</a></tt> array argument.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_CreateMixedMultipart_parts">parts</a></b></tt> - Array with the numbers with all the related parts.</p> +<p><tt><b><a name="argument_CreateMixedMultipart_part">part</a></b></tt> - Number of the created part that is returned by reference.</p> +</ul> +<h3>Return value</h3> +<p>An error message in case there was an error or an empty string otherwise. This return value may be safely ignored if the function parameters are set correctly.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_AddMixedMultipart"></a><li><a name="63.2.67">AddMixedMultipart</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> AddMixedMultipart(</tt><ul> +<tt>(input and output) <i>array</i> </tt><tt><a href="#argument_AddMixedMultipart_parts">parts</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Add a message part that groups several independent parts.</p> +<p> Usually this is meant compose messages with one or more file attachments. However, it is not necessary to use this function as the class implicitly creates a <tt>multipart/mixed</tt> message when more than one part is added to the message.</p> +<h3>Usage</h3> +<p>Create all the independent message parts that are going to be sent and pass their numbers to the <tt><a href="#argument_AddMixedMultipart_parts">parts</a></tt> array argument.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_AddMixedMultipart_parts">parts</a></b></tt> - Array with the numbers with all the related parts.</p> +</ul> +<h3>Return value</h3> +<p>An error message in case there was an error or an empty string otherwise. This return value may be safely ignored if the function parameters are set correctly.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_GetPartContentID"></a><li><a name="65.2.68">GetPartContentID</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> GetPartContentID(</tt><ul> +<tt><i>int</i> </tt><tt><a href="#argument_GetPartContentID_part">part</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Retrieve the content identifier associated to a given message part.</p> +<h3>Usage</h3> +<p>Create a message part and pass its number to the <tt><a href="#argument_GetPartContentID_part">part</a></tt> argument.</p> +<p> This function is usually meant to create an URL that can be used in an HTML message part to reference related parts like images, CSS (Cascaded Style Sheets), or any other type of files related to the HTML part that are embedded in the same message as part of a <tt>multipart/related</tt> composite part.</p> +<p> To use the part content identifier returned by this function you need to prepend the string <tt>'cid:'</tt> to form a special URL that can be used in the HTML document this part file.</p> +<p> You may read more about using this function in the class usage section about <a href="#embed-image">embedding images in HTML messages</a>.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_GetPartContentID_part">part</a></b></tt> - Number of the part as returned by the function that originally created it.</p> +</ul> +<h3>Return value</h3> +<p>The content identifier text string.</p> +<p> If it is specified an invalid message part, this function returns an empty string.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_GetDataURL"></a><li><a name="67.2.69">GetDataURL</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> GetDataURL(</tt><ul> +<tt>(input and output) <i>array</i> </tt><tt><a href="#argument_GetDataURL_file">file</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Generate a <tt>data:</tt> URL according to the <a href="http://www.ietf.org/rfc/rfc2397.txt">RFC 2397</a> suitable for using in HTML messages to represent an image or other type of file on which the data is directly embedded in the HTML code instead of being fetched from a separate file or remote URL.</p> +<p> Note that not all e-mail programs are capable of displaying images or other types of files embedded in HTML messages this way.</p> +<h3>Usage</h3> +<p>Pass a <tt><a href="#argument_GetDataURL_file">file</a></tt> part definition array like for the <tt><a href="#function_CreateFilePart">CreateFilePart</a></tt> function.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_GetDataURL_file">file</a></b></tt> - File definition.</p> +</ul> +<h3>Return value</h3> +<p>The <tt>data:</tt> representing the described file or an empty string in case there was an error.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_Send"></a><li><a name="69.2.70">Send</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> Send(</tt><tt>)</tt></p> +<h3>Purpose</h3> +<p>Send a composed message.</p> +<h3>Usage</h3> +<p>Use this function after you have set the necessary message headers and added the message body parts.</p> +<h3>Return value</h3> +<p>An error message in case there was an error or an empty string otherwise. This return value may be safely ignored if the function parameters are set correctly.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_GetMessage"></a><li><a name="69.2.71">GetMessage</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> GetMessage(</tt><ul> +<tt>(output) <i>string &</i> </tt><tt><a href="#argument_GetMessage_message">message</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Get the whole message headers and body.</p> +<h3>Usage</h3> +<p>Use this function to retrieve the message headers and body without sending it.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_GetMessage_message">message</a></b></tt> - Reference to a string variable to store the text of the message headers and body.</p> +</ul> +<h3>Return value</h3> +<p>An error message in case there was an error or an empty string otherwise. This return value may be safely ignored if the function parameters are set correctly.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_GetMessageSize"></a><li><a name="71.2.72">GetMessageSize</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> GetMessageSize(</tt><ul> +<tt>(output) <i>string &</i> </tt><tt><a href="#argument_GetMessageSize_message">message</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Get the size of the whole message headers and body.</p> +<h3>Usage</h3> +<p>Use this function to retrieve the size in bytes of the message headers and body without sending it.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_GetMessageSize_message">message</a></b></tt> - Reference to an integer variable to store the size of the message headers and body.</p> +</ul> +<h3>Return value</h3> +<p>An error message in case there was an error or an empty string otherwise. This return value may be safely ignored if the function parameters are set correctly.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_Mail"></a><li><a name="73.2.73">Mail</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>bool</i> Mail(</tt><ul> +<tt><i>string</i> </tt><tt><a href="#argument_Mail_to">to</a></tt><tt>,</tt><br /> +<tt><i>string</i> </tt><tt><a href="#argument_Mail_subject">subject</a></tt><tt>,</tt><br /> +<tt><i>string</i> </tt><tt><a href="#argument_Mail_message">message</a></tt><tt>,</tt><br /> +<tt><i>string</i> </tt><tt><a href="#argument_Mail_additional_headers">additional_headers</a></tt> [default '']<tt>,</tt><br /> +<tt><i>string</i> </tt><tt><a href="#argument_Mail_additional_parameters">additional_parameters</a></tt> [default '']</ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Emulate the PHP <tt>mail()</tt> function by composing and sending a message given the same arguments.</p> +<p> This is mostly meant to provide a solution for sending messages with alternative delivery methods provided by this class sub-classes. It uses the same arguments as the PHP <tt>mail()</tt> function. Developers willing to use this alternative do not need to change much their scripts that already use the <tt>mail()</tt> function.</p> +<h3>Usage</h3> +<p>Use this function passing the same arguments as to PHP <tt><a href="http://www.php.net/manual/en/function.mail.php">mail()</a></tt> function.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_Mail_to">to</a></b></tt> - Recipient e-mail address.</p> +<p><tt><b><a name="argument_Mail_subject">subject</a></b></tt> - Message subject.</p> +<p><tt><b><a name="argument_Mail_message">message</a></b></tt> - Message body.</p> +<p><tt><b><a name="argument_Mail_additional_headers">additional_headers</a></b></tt> - Text string headers and the respective values. There should be one header and value per line with line breaks separating each line.</p> +<p><tt><b><a name="argument_Mail_additional_parameters">additional_parameters</a></b></tt> - Text string with additional parameters. In the original PHP <tt>mail()</tt> function these were actual switches to be passed in the sendmail program invocation command line. This function only supports the <tt>-f</tt> switch followed by an e-mail address meant to specify the message bounce return path address.</p> +</ul> +<h3>Return value</h3> +<p>If this function succeeds, it returns <tt>1</tt>.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_SetBulkMail"></a><li><a name="75.2.74">SetBulkMail</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>bool</i> SetBulkMail(</tt><ul> +<tt><i>bool</i> </tt><tt><a href="#argument_SetBulkMail_on">on</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Hint the class to adjust itself in order to send individual messages to many recipients more efficiently.</p> +<h3>Usage</h3> +<p>Call this function before starting sending messages to many recipients passing 1 to the <tt><a href="#argument_SetBulkMail_on">on</a></tt> argument. Then call this function again after the bulk mailing delivery has ended passing passing 1 to the <tt><a href="#argument_SetBulkMail_on">on</a></tt> argument.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_SetBulkMail_on">on</a></b></tt> - Boolean flag that indicates whether a bulk delivery is going to start if set to 1 or that the bulk delivery has ended if set to 0.</p> +</ul> +<h3>Return value</h3> +<p>If this function succeeds, it returns <tt>1</tt>.</p> +<p><a href="#functions">Functions</a></p> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> + +<hr /> +<address>Manuel Lemos (<a href="mailto:mlemos-at-acm.org">mlemos-at-acm.org</a>)</address> +</body> +</html> diff --git a/vendor/email_message/documentation/pickup_message_class.html b/vendor/email_message/documentation/pickup_message_class.html new file mode 100644 index 0000000..dade063 --- /dev/null +++ b/vendor/email_message/documentation/pickup_message_class.html @@ -0,0 +1,170 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> +<head> +<title>Class: MIME E-mail message composing and sending using a Windows mail + server pickup directory</title> +</head> +<body> +<center><h1>Class: MIME E-mail message composing and sending using a Windows mail + server pickup directory</h1></center> +<hr /> +<ul> +<p><b>Version:</b> <tt>@(#) $Id: pickup_message.php,v 1.3 2004/09/22 21:21:31 mlemos Exp $</tt></p> +<h2><a name="table_of_contents">Contents</a></h2> +<ul> +<li><a href="#2.1.1">Summary</a></li> +<ul> +<li><a href="#3.2.0">Name</a></li> +<li><a href="#3.2.0.0">Author</a></li> +<li><a href="#3.2.0.1">Copyright</a></li> +<li><a href="#3.2.0.2">Version</a></li> +<li><a href="#3.2.0.3">Parent classes</a></li> +<li><a href="#4.2.0">Purpose</a></li> +<li><a href="#4.2.0.0">Usage</a></li> +</ul> +<li><a href="#5.1.1">Variables</a></li> +<ul> +<li><a href="#6.2.3">mailroot_directory</a></li> +<li><a href="#6.2.4">mailer_delivery</a></li> +</ul> +<li><a href="#7.1.1">Inherited variables</a></li> +<li><a href="#9.1.1">Functions</a></li> +<li><a href="#11.1.1">Inherited functions</a></li> +</ul> +<p><a href="#table_of_contents">Top of the table of contents</a></p> +</ul> +<hr /> +<ul> +<h2><li><a name="2.1.1">Summary</a></li></h2> +<ul> +<h3><a name="3.2.0">Name</a></h3> +<p>MIME E-mail message composing and sending using a Windows mail server pickup directory</p> +<h3><a name="3.2.0.0">Author</a></h3> +<p>Manuel Lemos (<a href="mailto:mlemos-at-acm.org">mlemos-at-acm.org</a>)</p> +<h3><a name="3.2.0.1">Copyright</a></h3> +<p>Copyright ¿ (C) Manuel Lemos 1999-2004</p> +<h3><a name="3.2.0.2">Version</a></h3> +<p>@(#) $Id: pickup_message.php,v 1.3 2004/09/22 21:21:31 mlemos Exp $</p> +<h3><a name="3.2.0.3">Parent classes</a></h3> +<ul> +<p><li>MIME E-mail message composing and sending</li></p> +<p><b>Version:</b> <tt>@(#) $Id: email_message.php,v 1.53 2004/09/14 03:01:04 mlemos Exp $</tt></p> +</ul> +<h3><a name="4.2.0">Purpose</a></h3> +<p>Implement an alternative message delivery method by dropping messages in a Windows mail server pickup directory, thus overriding the method of using the PHP <tt>mail()</tt> function implemented by the base class.</p> +<p> It is meant to be used by on Windows 2000 or later with IIS or Exchange mail servers because since this release the pickup directory started being supported.</p> +<p> It is much faster than relaying messages to an SMTP server because it works simply by storing messages in a special directory. This delivery method does not have the overhead of the SMTP protocol. The class does not need to wait for the mail server to pickup the messages and deliver them to the destination recipients. Therefore, it is recommended for bulk mailing.</p> +<h3><a name="4.2.0.0">Usage</a></h3> +<p>This class should be used exactly the same way as the base class for composing and sending messages. Just create a new object of this class as follows and set only the necessary variables to configure details of the message pickup.</p> +<p> <tt>require('email_message.php');<br /> + require('pickup_message.php');<br /> + <br /> + $message_object = new pickup_message_class;<br /> +</tt></p> +<p> <b>- Requirements</b></p> +<p> You need to use at least Windows 2000 with IIS mail server or Exchange 2000 or later.</p> +<p> The PHP script using this class must also run in the same Windows machine on which the mail server is running. The current user must have sufficient privileges to write to the mail server pickup directory.</p> +<p> <b>- Pickup directory</b></p> +<p> Before sending a message you need set the <tt><a href="#variable_mailroot_directory">mailroot_directory</a></tt> variable to specify the path of the mail server directory, so the class knows where the messages must be dropped for subsequent pickup and delivery by the mail server.</p> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> +<hr /> +<ul> +<h2><li><a name="variables"></a><a name="5.1.1">Variables</a></li></h2> +<ul> +<li><tt><a href="#variable_mailroot_directory">mailroot_directory</a></tt></li><br /> +<li><tt><a href="#variable_mailer_delivery">mailer_delivery</a></tt></li><br /> +<p><a href="#table_of_contents">Table of contents</a></p> +<h3><a name="variable_mailroot_directory"></a><li><a name="6.2.3">mailroot_directory</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>''</tt></p> +<h3>Purpose</h3> +<p>Specify the path of the directory where the <tt>Pickup</tt> sub-directory is located. This sub-directory is used by the mail server to pickup the messages to deliver.</p> +<h3>Usage</h3> +<p>If this variable is set to an empty string, the class attempts to locate the directory automatically checking the registry.</p> +<p> If the class is not able to determine the mailroot directory path and you are certain that IIS or Exchange programs are installed in your Windows 2000 or later machine, set this variable to the correct path of your mail server root directory.</p> +<p> Usually it is located inside the <tt>Inetpub</tt> directory of IIS or Exchange installation path, but it may also be located in a slightly different path.</p> +<h3>Example</h3> +<p><tt>'C:\\Inetpub\\mailroot\\'</tt></p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_mailer_delivery"></a><li><a name="6.2.4">mailer_delivery</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>'pickup $Revision: 1.3 $'</tt></p> +<h3>Purpose</h3> +<p>Specify the text that is used to identify the mail delivery class or sub-class. This text is appended to the <tt>X-Mailer</tt> header text defined by the mailer variable.</p> +<h3>Usage</h3> +<p>Do not change this variable.</p> +<p><a href="#variables">Variables</a></p> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> +<ul> +<h2><li><a name="functions"></a><a name="7.1.1">Inherited variables</a></li></h2> +<ul> +<li>email_regular_expression</li><br /> +<li>mailer</li><br /> +<li>default_charset</li><br /> +<li>line_quote_prefix</li><br /> +<li>file_buffer_length</li><br /> +<li>debug</li><br /> +<li>cache_body</li><br /> +<li>error</li><br /> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> +<hr /> +<ul> +<h2><li><a name="functions"></a><a name="9.1.1">Functions</a></li></h2> +<ul> +<p><a href="#table_of_contents">Table of contents</a></p> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> +<ul> +<h2><li><a name="functions"></a><a name="11.1.1">Inherited functions</a></li></h2> +<ul> +<li>ValidateEmailAddress</li><br /> +<li>WrapText</li><br /> +<li>QuoteText</li><br /> +<li>SetHeader</li><br /> +<li>SetEncodedHeader</li><br /> +<li>SetEncodedEmailHeader</li><br /> +<li>SetMultipleEncodedEmailHeader</li><br /> +<li>ResetMessage</li><br /> +<li>AddPart</li><br /> +<li>ReplacePart</li><br /> +<li>CreatePlainTextPart</li><br /> +<li>AddPlainTextPart</li><br /> +<li>CreateQuotedPrintableTextPart</li><br /> +<li>AddQuotedPrintableTextPart</li><br /> +<li>CreateHTMLPart</li><br /> +<li>AddHTMLPart</li><br /> +<li>CreateQuotedPrintableHTMLPart</li><br /> +<li>AddQuotedPrintableHTMLPart</li><br /> +<li>CreateFilePart</li><br /> +<li>AddFilePart</li><br /> +<li>CreateMessagePart</li><br /> +<li>AddMessagePart</li><br /> +<li>CreateAlternativeMultipart</li><br /> +<li>AddAlternativeMultipart</li><br /> +<li>CreateRelatedMultipart</li><br /> +<li>AddRelatedMultipart</li><br /> +<li>CreateMixedMultipart</li><br /> +<li>AddMixedMultipart</li><br /> +<li>GetPartContentID</li><br /> +<li>GetDataURL</li><br /> +<li>Send</li><br /> +<li>Mail</li><br /> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> + +<hr /> +<address>Manuel Lemos (<a href="mailto:mlemos-at-acm.org">mlemos-at-acm.org</a>)</address> +</body> +</html> diff --git a/vendor/email_message/documentation/qmail_message_class.html b/vendor/email_message/documentation/qmail_message_class.html new file mode 100644 index 0000000..5f1362b --- /dev/null +++ b/vendor/email_message/documentation/qmail_message_class.html @@ -0,0 +1,156 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> +<head> +<title>Class: MIME E-mail message composing and sending using Qmail</title> +</head> +<body> +<center><h1>Class: MIME E-mail message composing and sending using Qmail</h1></center> +<hr /> +<ul> +<p><b>Version:</b> <tt>@(#) $Id: qmail_message.php,v 1.9 2004/07/08 07:18:00 mlemos Exp $</tt></p> +<h2><a name="table_of_contents">Contents</a></h2> +<ul> +<li><a href="#2.1.1">Summary</a></li> +<ul> +<li><a href="#3.2.0">Name</a></li> +<li><a href="#3.2.0.0">Author</a></li> +<li><a href="#3.2.0.1">Copyright</a></li> +<li><a href="#3.2.0.2">Version</a></li> +<li><a href="#3.2.0.3">Parent classes</a></li> +<li><a href="#4.2.0">Purpose</a></li> +<li><a href="#4.2.0.0">Usage</a></li> +</ul> +<li><a href="#5.1.1">Variables</a></li> +<ul> +<li><a href="#6.2.3">qmail_path</a></li> +<li><a href="#6.2.4">mailer_delivery</a></li> +</ul> +<li><a href="#7.1.1">Inherited variables</a></li> +<li><a href="#9.1.1">Functions</a></li> +<li><a href="#11.1.1">Inherited functions</a></li> +</ul> +<p><a href="#table_of_contents">Top of the table of contents</a></p> +</ul> +<hr /> +<ul> +<h2><li><a name="2.1.1">Summary</a></li></h2> +<ul> +<h3><a name="3.2.0">Name</a></h3> +<p>MIME E-mail message composing and sending using Qmail</p> +<h3><a name="3.2.0.0">Author</a></h3> +<p>Manuel Lemos (<a href="mailto:mlemos-at-acm.org">mlemos-at-acm.org</a>)</p> +<h3><a name="3.2.0.1">Copyright</a></h3> +<p>Copyright © (C) Manuel Lemos 2001-2004</p> +<h3><a name="3.2.0.2">Version</a></h3> +<p>@(#) $Id: qmail_message.php,v 1.9 2004/07/08 07:18:00 mlemos Exp $</p> +<h3><a name="3.2.0.3">Parent classes</a></h3> +<ul> +<p><li>MIME E-mail message composing and sending</li></p> +<p><b>Version:</b> <tt>@(#) $Id: email_message.php,v 1.45 2004/07/08 03:37:48 mlemos Exp $</tt></p> +</ul> +<h3><a name="4.2.0">Purpose</a></h3> +<p>Implement an alternative message delivery method using <a href="http://www.qmail.org/">Qmail</a> MTA (Mail Transfer Agent).</p> +<h3><a name="4.2.0.0">Usage</a></h3> +<p>This class should be used exactly the same way as the base class for composing and sending messages. Just create a new object of this class as follows and set only the necessary variables to configure details of delivery using Qmail.</p> +<p> <tt>require('email_message.php');<br /> + require('qmail_message.php');<br /> + <br /> + $message_object = new qmail_message_class;<br /> +</tt></p> +<p> </p> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> +<hr /> +<ul> +<h2><li><a name="variables"></a><a name="5.1.1">Variables</a></li></h2> +<ul> +<li><tt><a href="#variable_qmail_path">qmail_path</a></tt></li><br /> +<li><tt><a href="#variable_mailer_delivery">mailer_delivery</a></tt></li><br /> +<p><a href="#table_of_contents">Table of contents</a></p> +<h3><a name="variable_qmail_path"></a><li><a name="6.2.3">qmail_path</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>'/var/qmail/bin'</tt></p> +<h3>Purpose</h3> +<p>Specifying the path of the Qmail programs.</p> +<h3>Usage</h3> +<p>Usually the default is correct.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_mailer_delivery"></a><li><a name="6.2.4">mailer_delivery</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>'qmail $Revision: 1.9 $'</tt></p> +<h3>Purpose</h3> +<p>Specify the text that is used to identify the mail delivery class or sub-class. This text is appended to the <tt>X-Mailer</tt> header text defined by the mailer variable.</p> +<h3>Usage</h3> +<p>Do not change this variable.</p> +<p><a href="#variables">Variables</a></p> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> +<ul> +<h2><li><a name="functions"></a><a name="7.1.1">Inherited variables</a></li></h2> +<ul> +<li>email_regular_expression</li><br /> +<li>mailer</li><br /> +<li>default_charset</li><br /> +<li>line_quote_prefix</li><br /> +<li>file_buffer_length</li><br /> +<li>debug</li><br /> +<li>cache_body</li><br /> +<li>error</li><br /> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> +<hr /> +<ul> +<h2><li><a name="functions"></a><a name="9.1.1">Functions</a></li></h2> +<ul> +<p><a href="#table_of_contents">Table of contents</a></p> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> +<ul> +<h2><li><a name="functions"></a><a name="11.1.1">Inherited functions</a></li></h2> +<ul> +<li>ValidateEmailAddress</li><br /> +<li>WrapText</li><br /> +<li>QuoteText</li><br /> +<li>SetHeader</li><br /> +<li>SetEncodedHeader</li><br /> +<li>SetEncodedEmailHeader</li><br /> +<li>ResetMessage</li><br /> +<li>AddPart</li><br /> +<li>ReplacePart</li><br /> +<li>CreatePlainTextPart</li><br /> +<li>AddPlainTextPart</li><br /> +<li>CreateQuotedPrintableTextPart</li><br /> +<li>AddQuotedPrintableTextPart</li><br /> +<li>CreateHTMLPart</li><br /> +<li>AddHTMLPart</li><br /> +<li>CreateQuotedPrintableHTMLPart</li><br /> +<li>AddQuotedPrintableHTMLPart</li><br /> +<li>CreateFilePart</li><br /> +<li>AddFilePart</li><br /> +<li>CreateMessagePart</li><br /> +<li>AddMessagePart</li><br /> +<li>CreateAlternativeMultipart</li><br /> +<li>AddAlternativeMultipart</li><br /> +<li>CreateRelatedMultipart</li><br /> +<li>AddRelatedMultipart</li><br /> +<li>CreateMixedMultipart</li><br /> +<li>AddMixedMultipart</li><br /> +<li>GetDataURL</li><br /> +<li>Send</li><br /> +<li>Mail</li><br /> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> + +<hr /> +<address>Manuel Lemos (<a href="mailto:mlemos-at-acm.org">mlemos-at-acm.org</a>)</address> +</body> +</html> diff --git a/vendor/email_message/documentation/sendmail_message_class.html b/vendor/email_message/documentation/sendmail_message_class.html new file mode 100644 index 0000000..e2fcbf6 --- /dev/null +++ b/vendor/email_message/documentation/sendmail_message_class.html @@ -0,0 +1,214 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> +<head> +<title>Class: MIME E-mail message composing and sending using Sendmail</title> +</head> +<body> +<center><h1>Class: MIME E-mail message composing and sending using Sendmail</h1></center> +<hr /> +<ul> +<p><b>Version:</b> <tt>@(#) $Id: sendmail_message.php,v 1.14 2004/10/05 18:22:45 mlemos Exp $</tt></p> +<h2><a name="table_of_contents">Contents</a></h2> +<ul> +<li><a href="#2.1.1">Summary</a></li> +<ul> +<li><a href="#3.2.0">Name</a></li> +<li><a href="#3.2.0.0">Author</a></li> +<li><a href="#3.2.0.1">Copyright</a></li> +<li><a href="#3.2.0.2">Version</a></li> +<li><a href="#3.2.0.3">Parent classes</a></li> +<li><a href="#4.2.0">Purpose</a></li> +<li><a href="#4.2.0.0">Usage</a></li> +</ul> +<li><a href="#5.1.1">Variables</a></li> +<ul> +<li><a href="#6.2.6">sendmail_path</a></li> +<li><a href="#6.2.7">delivery_mode</a></li> +<li><a href="#6.2.8">bulk_mail_delivery_mode</a></li> +<li><a href="#6.2.9">sendmail_arguments</a></li> +<li><a href="#6.2.10">mailer_delivery</a></li> +</ul> +<li><a href="#7.1.1">Inherited variables</a></li> +<li><a href="#9.1.1">Functions</a></li> +<li><a href="#11.1.1">Inherited functions</a></li> +</ul> +<p><a href="#table_of_contents">Top of the table of contents</a></p> +</ul> +<hr /> +<ul> +<h2><li><a name="2.1.1">Summary</a></li></h2> +<ul> +<h3><a name="3.2.0">Name</a></h3> +<p>MIME E-mail message composing and sending using Sendmail</p> +<h3><a name="3.2.0.0">Author</a></h3> +<p>Manuel Lemos (<a href="mailto:mlemos-at-acm.org">mlemos-at-acm.org</a>)</p> +<h3><a name="3.2.0.1">Copyright</a></h3> +<p>Copyright © (C) Manuel Lemos 1999-2004</p> +<h3><a name="3.2.0.2">Version</a></h3> +<p>@(#) $Id: sendmail_message.php,v 1.14 2004/10/05 18:22:45 mlemos Exp $</p> +<h3><a name="3.2.0.3">Parent classes</a></h3> +<ul> +<p><li>MIME E-mail message composing and sending</li></p> +<p><b>Version:</b> <tt>@(#) $Id: email_message.php,v 1.54 2004/10/02 05:57:23 mlemos Exp $</tt></p> +</ul> +<h3><a name="4.2.0">Purpose</a></h3> +<p>Implement an alternative message delivery method using <a href="http://www.sendmail.org/">Sendmail</a> MTA (Mail Transfer Agent). This class can also be used with other MTAs like <a href="http://www.exim.org/">Exim</a>, <a href="http://www.postfix.org/">Postfix</a> and <a href="http://www.qmail.org/">Qmail</a>, as they provide a wrapper commands that emulate the <tt>sendmail</tt> command.</p> +<h3><a name="4.2.0.0">Usage</a></h3> +<p>This class should be used exactly the same way as the base class for composing and sending messages. Just create a new object of this class as follows and set only the necessary variables to configure details of delivery using Sendmail.</p> +<p> <tt>require('email_message.php');<br /> + require('sendmail_message.php');<br /> + <br /> + $message_object = new sendmail_message_class;<br /> +</tt></p> +<p> <b>- Tuning the delivery mode for mass mailing</b></p> +<p> Sendmail supports several message delivery modes. In many installations the default is to attempt to deliver the message right away when the message is handed by the applications to Sendmail.</p> +<p> This may be an inconvenient because it makes PHP scripts wait for the message to be delivered to the destination SMTP server. If the SMTP connection with that server is slow, it may stall the delivery for a long while.</p> +<p> Under Unix/Linux, PHP defaults to using Sendmail or equivalent to deliver messages sent with the <tt>mail()</tt> function. Some people assume that it is faster to queue messages by relaying to an intermediate SMTP server than to use the <tt>mail()</tt> function that uses Sendmail. This is not accurate.</p> +<p> Sendmail supports other message delivery modes that can be used for much faster message queueing. These modes are more recommended for mass mailing. Adjust the value of the <tt><a href="#variable_delivery_mode">delivery_mode</a></tt> variable to improve the message queueing rate if you want to use this class for mass mailing.</p> +<p> Alternatively, you may also call the <tt><a href="#function_SetBulkMail">SetBulkMail</a></tt> to hint this class to use a delivery mode more suitable for bulk mailing. Currently, this makes this class use the delivery mode specified by the <tt><a href="#variable_bulk_mail_delivery_mode">bulk_mail_delivery_mode</a></tt> variable, which defaults to make the messages be queued for later delivery, thus making the calling script execute much faster.</p> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> +<hr /> +<ul> +<h2><li><a name="variables"></a><a name="5.1.1">Variables</a></li></h2> +<ul> +<li><tt><a href="#variable_sendmail_path">sendmail_path</a></tt></li><br /> +<li><tt><a href="#variable_delivery_mode">delivery_mode</a></tt></li><br /> +<li><tt><a href="#variable_bulk_mail_delivery_mode">bulk_mail_delivery_mode</a></tt></li><br /> +<li><tt><a href="#variable_sendmail_arguments">sendmail_arguments</a></tt></li><br /> +<li><tt><a href="#variable_mailer_delivery">mailer_delivery</a></tt></li><br /> +<p><a href="#table_of_contents">Table of contents</a></p> +<h3><a name="variable_sendmail_path"></a><li><a name="6.2.6">sendmail_path</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>'/usr/lib/sendmail'</tt></p> +<h3>Purpose</h3> +<p>Specifying the path of the <tt>sendmail</tt> executable program.</p> +<h3>Usage</h3> +<p>The original default path of the <tt>sendmail</tt> used to be <tt>/usr/lib/sendmail</tt>. However, currently it is usually located in <tt>/usr/sbin/sendmail</tt> having a symbolic link pointing to that path from <tt>/usr/lib/sendmail</tt>.</p> +<p> If this symbolic link does not exist or the <tt>sendmail</tt> is different in your installation, you need to change this variable.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_delivery_mode"></a><li><a name="6.2.7">delivery_mode</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>''</tt></p> +<h3>Purpose</h3> +<p>Specify the Sendmail message delivery mode. These delivery modes are only supported by Sendmail and Exim MTAs.</p> +<p> Current versions of Qmail and Postfix do not support configurable delivery modes. They always inject the messages in the local queue and let their queue management system take care of the delivery as soon as possible. Just leave this variable with the default value when using this class with these MTAs.</p> +<h3>Usage</h3> +<p>Sendmail supports several different delivery modes:</p> +<p> SENDMAIL_DELIVERY_DEFAULT - <tt>''</tt></p> +<p> Does not override the default mode.</p> +<p> SENDMAIL_DELIVERY_INTERACTIVE - <tt>'i'</tt></p> +<p> Attempt to send the messages synchronously to the recipient's SMTP server and only returns when it succeeds or fails. This is usually the default mode. It stalls the delivery of messages but it may be safer to preserve disk space because the successfully delivered messages are not stored.</p> +<p> SENDMAIL_DELIVERY_BACKGROUND - <tt>'b'</tt></p> +<p> Creates a background process that attempts to deliver the message and returns immediately. This mode is recommended when you want to send a few messages as soon as possible. It is not recommended for sending messages to many recipients as it may consume too much memory and CPU that result from creating excessive background processes.</p> +<p> SENDMAIL_DELIVERY_QUEUE - <tt>'q'</tt></p> +<p> Just drop the message in the queue and leave it there until next time the queue is run. It is recommended for deliverying messages to many recipients as long as there is enough disk space to store all the messages in the queue.</p> +<p> SENDMAIL_DELIVERY_DEFERRED - <tt>'d'</tt></p> +<p> The same as the queue mode except for a few verifications that are skipped.</p> +<p> </p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_bulk_mail_delivery_mode"></a><li><a name="6.2.8">bulk_mail_delivery_mode</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>'q'</tt></p> +<h3>Purpose</h3> +<p>Specify the Sendmail message delivery mode when the class is in bulk mail mode that is set with the <tt><a href="#function_SetBulkMail">SetBulkMail</a></tt> function.</p> +<h3>Usage</h3> +<p>The available delivery modes are the same as those used to set the <tt><a href="#variable_delivery_mode">delivery_mode</a></tt> variable. The default bulk mail delivery mode is to just queue the message (<tt>SENDMAIL_DELIVERY_QUEUE</tt>) without waiting for sendmail to deliver the message.</p> +<p> Note that some MTAs that emulate Sendmail return an error when set to queue delivery mode. If this happens with the MTA that you are using, change this variable to the <tt>SENDMAIL_DELIVERY_DEFAULT</tt>.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_sendmail_arguments"></a><li><a name="6.2.9">sendmail_arguments</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>''</tt></p> +<h3>Purpose</h3> +<p>Specify additional <tt>sendmail</tt> program arguments.</p> +<h3>Usage</h3> +<p>Use this to to pass additional arguments that are not supported by this class.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_mailer_delivery"></a><li><a name="6.2.10">mailer_delivery</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>'sendmail $Revision: 1.14 $'</tt></p> +<h3>Purpose</h3> +<p>Specify the text that is used to identify the mail delivery class or sub-class. This text is appended to the <tt>X-Mailer</tt> header text defined by the mailer variable.</p> +<h3>Usage</h3> +<p>Do not change this variable.</p> +<p><a href="#variables">Variables</a></p> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> +<ul> +<h2><li><a name="functions"></a><a name="7.1.1">Inherited variables</a></li></h2> +<ul> +<li>email_regular_expression</li><br /> +<li>mailer</li><br /> +<li>default_charset</li><br /> +<li>line_quote_prefix</li><br /> +<li>file_buffer_length</li><br /> +<li>debug</li><br /> +<li>cache_body</li><br /> +<li>error</li><br /> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> +<hr /> +<ul> +<h2><li><a name="functions"></a><a name="9.1.1">Functions</a></li></h2> +<ul> +<p><a href="#table_of_contents">Table of contents</a></p> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> +<ul> +<h2><li><a name="functions"></a><a name="11.1.1">Inherited functions</a></li></h2> +<ul> +<li>ValidateEmailAddress</li><br /> +<li>WrapText</li><br /> +<li>QuoteText</li><br /> +<li>SetHeader</li><br /> +<li>SetEncodedHeader</li><br /> +<li>SetEncodedEmailHeader</li><br /> +<li>SetMultipleEncodedEmailHeader</li><br /> +<li>ResetMessage</li><br /> +<li>AddPart</li><br /> +<li>ReplacePart</li><br /> +<li>CreatePlainTextPart</li><br /> +<li>AddPlainTextPart</li><br /> +<li>CreateQuotedPrintableTextPart</li><br /> +<li>AddQuotedPrintableTextPart</li><br /> +<li>CreateHTMLPart</li><br /> +<li>AddHTMLPart</li><br /> +<li>CreateQuotedPrintableHTMLPart</li><br /> +<li>AddQuotedPrintableHTMLPart</li><br /> +<li>CreateFilePart</li><br /> +<li>AddFilePart</li><br /> +<li>CreateMessagePart</li><br /> +<li>AddMessagePart</li><br /> +<li>CreateAlternativeMultipart</li><br /> +<li>AddAlternativeMultipart</li><br /> +<li>CreateRelatedMultipart</li><br /> +<li>AddRelatedMultipart</li><br /> +<li>CreateMixedMultipart</li><br /> +<li>AddMixedMultipart</li><br /> +<li>GetPartContentID</li><br /> +<li>GetDataURL</li><br /> +<li>Send</li><br /> +<li>Mail</li><br /> +<li>SetBulkMail</li><br /> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> + +<hr /> +<address>Manuel Lemos (<a href="mailto:mlemos-at-acm.org">mlemos-at-acm.org</a>)</address> +</body> +</html> diff --git a/vendor/email_message/documentation/smtp_class.html b/vendor/email_message/documentation/smtp_class.html new file mode 100644 index 0000000..4caa60d --- /dev/null +++ b/vendor/email_message/documentation/smtp_class.html @@ -0,0 +1,584 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> +<head> +<title>Class: Sending e-mail messages via SMTP protocol</title> +</head> +<body> +<center><h1>Class: Sending e-mail messages via SMTP protocol</h1></center> +<hr /> +<ul> +<p><b>Version:</b> <tt>@(#) $Id: smtp.php,v 1.45 2011/02/03 08:11:30 mlemos Exp $</tt></p> +<h2><a name="table_of_contents">Contents</a></h2> +<ul> +<li><a href="#2.1.1">Summary</a></li> +<ul> +<li><a href="#3.2.0">Name</a></li> +<li><a href="#3.2.0.0">Author</a></li> +<li><a href="#3.2.0.1">Copyright</a></li> +<li><a href="#3.2.0.2">Version</a></li> +<li><a href="#3.2.0.3">Purpose</a></li> +<li><a href="#3.2.0.4">Translation</a></li> +<li><a href="#3.2.0.5">Support</a></li> +<li><a href="#3.2.0.6">Usage</a></li> +</ul> +<li><a href="#4.1.1">Variables</a></li> +<ul> +<li><a href="#5.2.29">user</a></li> +<li><a href="#5.2.30">realm</a></li> +<li><a href="#5.2.31">password</a></li> +<li><a href="#5.2.32">workstation</a></li> +<li><a href="#5.2.33">authentication_mechanism</a></li> +<li><a href="#5.2.34">host_name</a></li> +<li><a href="#5.2.35">host_port</a></li> +<li><a href="#5.2.36">socks_host_name</a></li> +<li><a href="#5.2.37">socks_host_port</a></li> +<li><a href="#5.2.38">socks_version</a></li> +<li><a href="#5.2.39">http_proxy_host_name</a></li> +<li><a href="#5.2.40">http_proxy_host_port</a></li> +<li><a href="#5.2.41">user_agent</a></li> +<li><a href="#5.2.42">ssl</a></li> +<li><a href="#5.2.43">start_tls</a></li> +<li><a href="#5.2.44">localhost</a></li> +<li><a href="#5.2.45">timeout</a></li> +<li><a href="#5.2.46">data_timeout</a></li> +<li><a href="#5.2.47">direct_delivery</a></li> +<li><a href="#5.2.48">error</a></li> +<li><a href="#5.2.49">debug</a></li> +<li><a href="#5.2.50">html_debug</a></li> +<li><a href="#5.2.51">esmtp</a></li> +<li><a href="#5.2.52">esmtp_extensions</a></li> +<li><a href="#5.2.53">exclude_address</a></li> +<li><a href="#5.2.54">getmxrr</a></li> +<li><a href="#5.2.55">pop3_auth_host</a></li> +<li><a href="#5.2.56">pop3_auth_port</a></li> +</ul> +<li><a href="#6.1.1">Functions</a></li> +<ul> +<li><a href="#7.2.11">Connect</a></li> +<li><a href="#9.2.12">MailFrom</a></li> +<li><a href="#11.2.13">SetRecipient</a></li> +<li><a href="#13.2.14">StartData</a></li> +<li><a href="#13.2.15">PrepareData</a></li> +<li><a href="#15.2.16">SendData</a></li> +<li><a href="#17.2.17">EndSendingData</a></li> +<li><a href="#17.2.18">ResetConnection</a></li> +<li><a href="#17.2.19">Disconnect</a></li> +<li><a href="#19.2.20">SendMessage</a></li> +</ul> +</ul> +<p><a href="#table_of_contents">Top of the table of contents</a></p> +</ul> +<hr /> +<ul> +<h2><li><a name="2.1.1">Summary</a></li></h2> +<ul> +<h3><a name="3.2.0">Name</a></h3> +<p>Sending e-mail messages via SMTP protocol</p> +<h3><a name="3.2.0.0">Author</a></h3> +<p>Manuel Lemos (<a href="mailto:mlemos-at-acm.org">mlemos-at-acm.org</a>)</p> +<h3><a name="3.2.0.1">Copyright</a></h3> +<p>Copyright (C) Manuel Lemos 1999-2011</p> +<h3><a name="3.2.0.2">Version</a></h3> +<p>@(#) $Id: smtp.php,v 1.45 2011/02/03 08:11:30 mlemos Exp $</p> +<h3><a name="3.2.0.3">Purpose</a></h3> +<p>Sending e-mail messages via SMTP protocol</p> +<h3><a name="3.2.0.4">Translation</a></h3> +<p>If you are interested in translating the documentation of this class to your own idiom, please <a href="mailto:mlemos-at-acm.org">contact the author</a>.</p> +<h3><a name="3.2.0.5">Support</a></h3> +<p>Technical support for using this class may be obtained in the <tt>smtpclass</tt> support forum. Just go to the support forum pages page to browse the forum archives and post support request messages:</p> +<p> <a href="http://www.phpclasses.org/discuss/package/14/">http://www.phpclasses.org/discuss/package/14/</a></p> +<h3><a name="3.2.0.6">Usage</a></h3> +<p>To use this class just create a new object, set any variables to configure its options and call the <tt><a href="#function_SendMessage">SendMessage</a></tt> function to send a message.</p> +<p>It is not recommended that you use this class alone unless you have deep understanding of Internet mail standards on how to compose compliant e-mail messages. Instead, use the <a href="http://www.phpclasses.org/mimemessage">MIME message composing and sending class</a> and its sub-class SMTP message together with this SMTP class to properly compose e-mail messages, so your messages are not discarded for not being correctly composed.</p> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> +<hr /> +<ul> +<h2><li><a name="variables"></a><a name="4.1.1">Variables</a></li></h2> +<ul> +<li><tt><a href="#variable_user">user</a></tt></li><br /> +<li><tt><a href="#variable_realm">realm</a></tt></li><br /> +<li><tt><a href="#variable_password">password</a></tt></li><br /> +<li><tt><a href="#variable_workstation">workstation</a></tt></li><br /> +<li><tt><a href="#variable_authentication_mechanism">authentication_mechanism</a></tt></li><br /> +<li><tt><a href="#variable_host_name">host_name</a></tt></li><br /> +<li><tt><a href="#variable_host_port">host_port</a></tt></li><br /> +<li><tt><a href="#variable_socks_host_name">socks_host_name</a></tt></li><br /> +<li><tt><a href="#variable_socks_host_port">socks_host_port</a></tt></li><br /> +<li><tt><a href="#variable_socks_version">socks_version</a></tt></li><br /> +<li><tt><a href="#variable_http_proxy_host_name">http_proxy_host_name</a></tt></li><br /> +<li><tt><a href="#variable_http_proxy_host_port">http_proxy_host_port</a></tt></li><br /> +<li><tt><a href="#variable_user_agent">user_agent</a></tt></li><br /> +<li><tt><a href="#variable_ssl">ssl</a></tt></li><br /> +<li><tt><a href="#variable_start_tls">start_tls</a></tt></li><br /> +<li><tt><a href="#variable_localhost">localhost</a></tt></li><br /> +<li><tt><a href="#variable_timeout">timeout</a></tt></li><br /> +<li><tt><a href="#variable_data_timeout">data_timeout</a></tt></li><br /> +<li><tt><a href="#variable_direct_delivery">direct_delivery</a></tt></li><br /> +<li><tt><a href="#variable_error">error</a></tt></li><br /> +<li><tt><a href="#variable_debug">debug</a></tt></li><br /> +<li><tt><a href="#variable_html_debug">html_debug</a></tt></li><br /> +<li><tt><a href="#variable_esmtp">esmtp</a></tt></li><br /> +<li><tt><a href="#variable_esmtp_extensions">esmtp_extensions</a></tt></li><br /> +<li><tt><a href="#variable_exclude_address">exclude_address</a></tt></li><br /> +<li><tt><a href="#variable_getmxrr">getmxrr</a></tt></li><br /> +<li><tt><a href="#variable_pop3_auth_host">pop3_auth_host</a></tt></li><br /> +<li><tt><a href="#variable_pop3_auth_port">pop3_auth_port</a></tt></li><br /> +<p><a href="#table_of_contents">Table of contents</a></p> +<h3><a name="variable_user"></a><li><a name="5.2.29">user</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>''</tt></p> +<h3>Purpose</h3> +<p>Define the authorized user when sending messages to a SMTP server.</p> +<h3>Usage</h3> +<p>Set this variable to the user name when the SMTP server requires authentication.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_realm"></a><li><a name="5.2.30">realm</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>''</tt></p> +<h3>Purpose</h3> +<p>Define the authentication realm when sending messages to a SMTP server.</p> +<h3>Usage</h3> +<p>Set this variable when the SMTP server requires authentication and if more than one authentication realm is supported.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_password"></a><li><a name="5.2.31">password</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>''</tt></p> +<h3>Purpose</h3> +<p>Define the authorized user password when sending messages to a SMTP server.</p> +<h3>Usage</h3> +<p>Set this variable to the user password when the SMTP server requires authentication.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_workstation"></a><li><a name="5.2.32">workstation</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>''</tt></p> +<h3>Purpose</h3> +<p>Define the client workstation name when sending messages to a SMTP server.</p> +<h3>Usage</h3> +<p>Set this variable to the client workstation when the SMTP server requires authentication identifiying the origin workstation name.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_authentication_mechanism"></a><li><a name="5.2.33">authentication_mechanism</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>''</tt></p> +<h3>Purpose</h3> +<p>Force the use of a specific authentication mechanism.</p> +<h3>Usage</h3> +<p>Set it to an empty string to let the class determine the authentication mechanism to use automatically based on the supported mechanisms by the server and by the SASL client library classes.</p> +<p> Set this variable to a specific mechanism name if you want to override the automatic authentication mechanism selection.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_host_name"></a><li><a name="5.2.34">host_name</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>''</tt></p> +<h3>Purpose</h3> +<p>Define the SMTP server host name.</p> +<h3>Usage</h3> +<p>Set to the host name of the SMTP server to which you want to relay the messages.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_host_port"></a><li><a name="5.2.35">host_port</a></li></h3> +<h3>Type</h3> +<p><tt><i>int</i></tt></p> +<h3>Default value</h3> +<p><tt>25</tt></p> +<h3>Purpose</h3> +<p>Define the SMTP server host port.</p> +<h3>Usage</h3> +<p>Set to the TCP port of the SMTP server host to connect.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_socks_host_name"></a><li><a name="5.2.36">socks_host_name</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>''</tt></p> +<h3>Purpose</h3> +<p>Define the SOCKS server host name.</p> +<h3>Usage</h3> +<p>Set to the SOCKS server host name through which the SMTP connection should be routed. Leave it empty if you do not want the connections to be established through a SOCKS server.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_socks_host_port"></a><li><a name="5.2.37">socks_host_port</a></li></h3> +<h3>Type</h3> +<p><tt><i>int</i></tt></p> +<h3>Default value</h3> +<p><tt>1080</tt></p> +<h3>Purpose</h3> +<p>Define the SOCKS server host port.</p> +<h3>Usage</h3> +<p>Set to the port of the SOCKS server host through which the the SMTP connection should be routed.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_socks_version"></a><li><a name="5.2.38">socks_version</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>'5'</tt></p> +<h3>Purpose</h3> +<p>Set the SOCKS protocol version.</p> +<h3>Usage</h3> +<p>Change this value if SOCKS server you want to use is listening to a different port.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_http_proxy_host_name"></a><li><a name="5.2.39">http_proxy_host_name</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>''</tt></p> +<h3>Purpose</h3> +<p>Define the HTTP proxy server host name.</p> +<h3>Usage</h3> +<p>Set to the HTTP proxy server host name through which the SMTP connection should be routed. Leave it empty if you do not want the connections to be established through an HTTP proxy.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_http_proxy_host_port"></a><li><a name="5.2.40">http_proxy_host_port</a></li></h3> +<h3>Type</h3> +<p><tt><i>int</i></tt></p> +<h3>Default value</h3> +<p><tt>80</tt></p> +<h3>Purpose</h3> +<p>Define the HTTP proxy server host port.</p> +<h3>Usage</h3> +<p>Set to the port of the HTTP proxy server host through which the SMTP connection should be routed.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_user_agent"></a><li><a name="5.2.41">user_agent</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>'SMTP Class (http://www.phpclasses.org/smtpclass $Revision: 1.45 $)'</tt></p> +<h3>Purpose</h3> +<p>Set the user agent used when connecting via an HTTP proxy.</p> +<h3>Usage</h3> +<p>Change this value only if for some reason you want emulate a certain e-mail client.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_ssl"></a><li><a name="5.2.42">ssl</a></li></h3> +<h3>Type</h3> +<p><tt><i>bool</i></tt></p> +<h3>Default value</h3> +<p><tt>0</tt></p> +<h3>Purpose</h3> +<p>Define whether the connection to the SMTP server should be established securely using SSL protocol.</p> +<h3>Usage</h3> +<p>Set to 1 if the SMTP server requires secure connections using SSL protocol.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_start_tls"></a><li><a name="5.2.43">start_tls</a></li></h3> +<h3>Type</h3> +<p><tt><i>bool</i></tt></p> +<h3>Default value</h3> +<p><tt>0</tt></p> +<h3>Purpose</h3> +<p>Define whether the connection to the SMTP server should use encryption after the connection is established using TLS protocol.</p> +<h3>Usage</h3> +<p>Set to 1 if the SMTP server requires that authentication be done securely starting the TLS protocol after the connection is established.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_localhost"></a><li><a name="5.2.44">localhost</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>''</tt></p> +<h3>Purpose</h3> +<p>Name of the local host computer</p> +<h3>Usage</h3> +<p>Set to the name of the computer connecting to the SMTP server from the local network.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_timeout"></a><li><a name="5.2.45">timeout</a></li></h3> +<h3>Type</h3> +<p><tt><i>int</i></tt></p> +<h3>Default value</h3> +<p><tt>0</tt></p> +<h3>Purpose</h3> +<p>Specify the connection timeout period in seconds.</p> +<h3>Usage</h3> +<p>Leave it set to 0 if you want the connection attempts to wait forever. Change this value if for some reason the timeout period seems insufficient or otherwise it seems too long.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_data_timeout"></a><li><a name="5.2.46">data_timeout</a></li></h3> +<h3>Type</h3> +<p><tt><i>int</i></tt></p> +<h3>Default value</h3> +<p><tt>0</tt></p> +<h3>Purpose</h3> +<p>Specify the timeout period in seconds to wait for data from the server.</p> +<h3>Usage</h3> +<p>Leave it set to 0 if you want to use the same value defined in the <tt><a href="#variable_timeout">timeout</a></tt> variable. Change this value if for some reason the default data timeout period seems insufficient or otherwise it seems too long.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_direct_delivery"></a><li><a name="5.2.47">direct_delivery</a></li></h3> +<h3>Type</h3> +<p><tt><i>bool</i></tt></p> +<h3>Default value</h3> +<p><tt>0</tt></p> +<h3>Purpose</h3> +<p>Boolean flag that indicates whether the message should be sent in direct delivery mode, i.e. the message is sent to the SMTP server associated to the domain of the recipient instead of relaying to the server specified by the <tt><a href="#variable_host_name">host_name</a></tt> variable.</p> +<h3>Usage</h3> +<p>Set this to <tt>1</tt> if you want to send urgent messages directly to the recipient domain SMTP server.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_error"></a><li><a name="5.2.48">error</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>''</tt></p> +<h3>Purpose</h3> +<p>Message that describes the error when a call to a class function fails.</p> +<h3>Usage</h3> +<p>Check this variable when an error occurs to understand what happened.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_debug"></a><li><a name="5.2.49">debug</a></li></h3> +<h3>Type</h3> +<p><tt><i>bool</i></tt></p> +<h3>Default value</h3> +<p><tt>0</tt></p> +<h3>Purpose</h3> +<p>Specify whether it is necessary to output SMTP connection debug information.</p> +<h3>Usage</h3> +<p>Set this variable to <tt>1</tt> if you need to see the progress of the SMTP connection and protocol dialog when you need to understand the reason for delivery problems.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_html_debug"></a><li><a name="5.2.50">html_debug</a></li></h3> +<h3>Type</h3> +<p><tt><i>bool</i></tt></p> +<h3>Default value</h3> +<p><tt>0</tt></p> +<h3>Purpose</h3> +<p>Specify whether the debug information should be outputted in HTML format.</p> +<h3>Usage</h3> +<p>Set this variable to <tt>1</tt> if you need to see the debug output in a Web page.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_esmtp"></a><li><a name="5.2.51">esmtp</a></li></h3> +<h3>Type</h3> +<p><tt><i>bool</i></tt></p> +<h3>Default value</h3> +<p><tt>1</tt></p> +<h3>Purpose</h3> +<p>Specify whether the class should attempt to use ESMTP extensions supported by the server.</p> +<h3>Usage</h3> +<p>Set this variable to <tt>0</tt> if for some reason you want to avoid benefitting from ESMTP extensions.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_esmtp_extensions"></a><li><a name="5.2.52">esmtp_extensions</a></li></h3> +<h3>Type</h3> +<p><tt><i>array</i></tt></p> +<h3>Default value</h3> +<p><tt>array()</tt></p> +<h3>Purpose</h3> +<p>Associative array with the list of ESMTP extensions supported by the SMTP server.</p> +<h3>Usage</h3> +<p>Check this variable after connecting to the SMTP server to determine which ESMTP extensions are supported.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_exclude_address"></a><li><a name="5.2.53">exclude_address</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>''</tt></p> +<h3>Purpose</h3> +<p>Specify an address that should be considered invalid when resolving host name addresses.</p> +<h3>Usage</h3> +<p>In some networks any domain name that does not exist is resolved as a sub-domain of the default local domain. If the DNS is configured in such way that it always resolves any sub-domain of the default local domain to a given address, it is hard to determine whether a given domain does not exist.</p> +<p> If your network is configured this way, you may set this variable to the address that all sub-domains of the default local domain resolves, so the class can assume that such address is invalid.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_getmxrr"></a><li><a name="5.2.54">getmxrr</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>'getmxrr'</tt></p> +<h3>Purpose</h3> +<p>Specify the name of the function that is called to determine the SMTP server address of a given domain.</p> +<h3>Usage</h3> +<p>Change this to a working replacement of the PHP <tt>getmxrr()</tt> function if this is not working in your system and you want to send messages in direct delivery mode.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_pop3_auth_host"></a><li><a name="5.2.55">pop3_auth_host</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>''</tt></p> +<h3>Purpose</h3> +<p>Specify the server address for POP3 based authentication.</p> +<h3>Usage</h3> +<p>Set this variable to the address of the POP3 server if the SMTP server requires POP3 based authentication.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_pop3_auth_port"></a><li><a name="5.2.56">pop3_auth_port</a></li></h3> +<h3>Type</h3> +<p><tt><i>int</i></tt></p> +<h3>Default value</h3> +<p><tt>110</tt></p> +<h3>Purpose</h3> +<p>Specify the server port for POP3 based authentication.</p> +<h3>Usage</h3> +<p>Set this variable to the port of the POP3 server if the SMTP server requires POP3 based authentication.</p> +<p><a href="#variables">Variables</a></p> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> +<hr /> +<ul> +<h2><li><a name="functions"></a><a name="6.1.1">Functions</a></li></h2> +<ul> +<li><tt><a href="#function_Connect">Connect</a></tt></li><br /> +<li><tt><a href="#function_MailFrom">MailFrom</a></tt></li><br /> +<li><tt><a href="#function_SetRecipient">SetRecipient</a></tt></li><br /> +<li><tt><a href="#function_StartData">StartData</a></tt></li><br /> +<li><tt><a href="#function_PrepareData">PrepareData</a></tt></li><br /> +<li><tt><a href="#function_SendData">SendData</a></tt></li><br /> +<li><tt><a href="#function_EndSendingData">EndSendingData</a></tt></li><br /> +<li><tt><a href="#function_ResetConnection">ResetConnection</a></tt></li><br /> +<li><tt><a href="#function_Disconnect">Disconnect</a></tt></li><br /> +<li><tt><a href="#function_SendMessage">SendMessage</a></tt></li><br /> +<p><a href="#table_of_contents">Table of contents</a></p> +<h3><a name="function_Connect"></a><li><a name="7.2.11">Connect</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>bool</i> Connect(</tt><ul> +<tt><i>string</i> </tt><tt><a href="#argument_Connect_domain">domain</a></tt> [default '']</ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Connect to an SMTP server.</p> +<h3>Usage</h3> +<p>Call this function as first step to send e-mail messages.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_Connect_domain">domain</a></b></tt> - Specify the domain of the recipient when using the direct delivery mode.</p> +</ul> +<h3>Return value</h3> +<p>The function returns <tt>1</tt> if the connection is successfully established.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_MailFrom"></a><li><a name="9.2.12">MailFrom</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>bool</i> MailFrom(</tt><ul> +<tt><i>string</i> </tt><tt><a href="#argument_MailFrom_sender">sender</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Set the address of the message sender.</p> +<h3>Usage</h3> +<p>Call this function right after establishing a connection with the <tt><a href="#function_Connect">Connect</a></tt> function.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_MailFrom_sender">sender</a></b></tt> - E-mail address of the sender.</p> +</ul> +<h3>Return value</h3> +<p>The function returns <tt>1</tt> if the sender address is successfully set.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_SetRecipient"></a><li><a name="11.2.13">SetRecipient</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>bool</i> SetRecipient(</tt><ul> +<tt><i>string</i> </tt><tt><a href="#argument_SetRecipient_recipient">recipient</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Set the address of a message recipient.</p> +<h3>Usage</h3> +<p>Call this function repeatedly for each recipient right after setting the message sender with the <tt><a href="#function_MailFrom">MailFrom</a></tt> function.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_SetRecipient_recipient">recipient</a></b></tt> - E-mail address of a recipient.</p> +</ul> +<h3>Return value</h3> +<p>The function returns <tt>1</tt> if the recipient address is successfully set.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_StartData"></a><li><a name="13.2.14">StartData</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>bool</i> StartData(</tt><tt>)</tt></p> +<h3>Purpose</h3> +<p>Tell the SMTP server that the message data will start being sent.</p> +<h3>Usage</h3> +<p>Call this function right after you are done setting all the message recipients with the <tt><a href="#function_SetRecipient">SetRecipient</a></tt> function.</p> +<h3>Return value</h3> +<p>The function returns <tt>1</tt> if the server is ready to start receiving the message data.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_PrepareData"></a><li><a name="13.2.15">PrepareData</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>string</i> PrepareData(</tt><ul> +<tt><i>string</i> </tt><tt><a href="#argument_PrepareData_data">data</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Prepare message data to normalize line breaks and escaping lines that contain single dots.</p> +<h3>Usage</h3> +<p>Call this function if the message data you want to send may contain line breaks that are not the "\r\n" sequence or it may contain lines that just have a single dot.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_PrepareData_data">data</a></b></tt> - Message data to be prepared.</p> +</ul> +<h3>Return value</h3> +<p>Resulting normalized messages data.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_SendData"></a><li><a name="15.2.16">SendData</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>bool</i> SendData(</tt><ul> +<tt><i>string</i> </tt><tt><a href="#argument_SendData_data">data</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Send message data.</p> +<h3>Usage</h3> +<p>Call this function repeatedly for all message data blocks to be sent right after start sending message data with the <tt><a href="#function_StartData">StartData</a></tt> function.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_SendData_data">data</a></b></tt> - Message data to be sent.</p> +</ul> +<h3>Return value</h3> +<p>The function returns <tt>1</tt> if the message data was sent to the SMTP server successfully.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_EndSendingData"></a><li><a name="17.2.17">EndSendingData</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>bool</i> EndSendingData(</tt><tt>)</tt></p> +<h3>Purpose</h3> +<p>Tell the server that all the message data was sent.</p> +<h3>Usage</h3> +<p>Call this function when you are done with sending the message data with the <tt><a href="#function_SendData">SendData</a></tt> function.</p> +<h3>Return value</h3> +<p>The function returns <tt>1</tt> if the server accepted the message.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_ResetConnection"></a><li><a name="17.2.18">ResetConnection</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>bool</i> ResetConnection(</tt><tt>)</tt></p> +<h3>Purpose</h3> +<p>Reset an already established SMTP connection to the initial state.</p> +<h3>Usage</h3> +<p>Call this function when there was an error sending a message and you need to skip to sending another message without disconnecting.</p> +<h3>Return value</h3> +<p>The function returns <tt>1</tt> if the connection was resetted successfully.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_Disconnect"></a><li><a name="17.2.19">Disconnect</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>bool</i> Disconnect(</tt><ul> +<tt><i>bool</i> </tt><tt><a href="#argument_Disconnect_quit">quit</a></tt> [default 1]</ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Terminate a previously opened connection.</p> +<h3>Usage</h3> +<p>Call this function after you are done sending your messages.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_Disconnect_quit">quit</a></b></tt> - Boolean option that tells whether the class should perform the final connection quit handshake, or just close the connection without waiting.</p> +</ul> +<h3>Return value</h3> +<p>The function returns <tt>1</tt> if the connection was successfully closed.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_SendMessage"></a><li><a name="19.2.20">SendMessage</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>bool</i> SendMessage(</tt><ul> +<tt><i>string</i> </tt><tt><a href="#argument_SendMessage_sender">sender</a></tt><tt>,</tt><br /> +<tt><i>string</i> </tt><tt><a href="#argument_SendMessage_recipients">recipients</a></tt><tt>,</tt><br /> +<tt>(input and output) <i>array</i> </tt><tt><a href="#argument_SendMessage_headers">headers</a></tt><tt>,</tt><br /> +<tt><i>string</i> </tt><tt><a href="#argument_SendMessage_body">body</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Send a message in a single call.</p> +<h3>Usage</h3> +<p>Call this function if you want to send a single messages to a small number of recipients in a single call.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_SendMessage_sender">sender</a></b></tt> - E-mail address of the sender.</p> +<p><tt><b><a name="argument_SendMessage_recipients">recipients</a></b></tt> - Array with a list of the e-mail addresses of the recipients of the message.</p> +<p><tt><b><a name="argument_SendMessage_headers">headers</a></b></tt> - Array with a list of the header lines of the message.</p> +<p><tt><b><a name="argument_SendMessage_body">body</a></b></tt> - Body data of the message.</p> +</ul> +<h3>Return value</h3> +<p>The function returns <tt>1</tt> if the message was sent successfully.</p> +<p><a href="#functions">Functions</a></p> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> + +<hr /> +<address>Manuel Lemos (<a href="mailto:mlemos-at-acm.org">mlemos-at-acm.org</a>)</address> +</body> +</html> diff --git a/vendor/email_message/documentation/smtp_message_class.html b/vendor/email_message/documentation/smtp_message_class.html new file mode 100644 index 0000000..3bec125 --- /dev/null +++ b/vendor/email_message/documentation/smtp_message_class.html @@ -0,0 +1,417 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> +<head> +<title>Class: MIME E-mail message composing and sending via SMTP</title> +</head> +<body> +<center><h1>Class: MIME E-mail message composing and sending via SMTP</h1></center> +<hr /> +<ul> +<p><b>Version:</b> <tt>@(#) $Id: smtp_message.php,v 1.34 2009/04/12 08:20:39 mlemos Exp $</tt></p> +<h2><a name="table_of_contents">Contents</a></h2> +<ul> +<li><a href="#2.1.1">Summary</a></li> +<ul> +<li><a href="#3.2.0">Name</a></li> +<li><a href="#3.2.0.0">Author</a></li> +<li><a href="#3.2.0.1">Copyright</a></li> +<li><a href="#3.2.0.2">Version</a></li> +<li><a href="#3.2.0.3">Parent classes</a></li> +<li><a href="#4.2.0">Purpose</a></li> +<li><a href="#4.2.0.0">Usage</a></li> +</ul> +<li><a href="#5.1.1">Variables</a></li> +<ul> +<li><a href="#6.2.22">localhost</a></li> +<li><a href="#6.2.23">smtp_host</a></li> +<li><a href="#6.2.24">smtp_port</a></li> +<li><a href="#6.2.25">smtp_ssl</a></li> +<li><a href="#6.2.26">smtp_start_tls</a></li> +<li><a href="#6.2.27">smtp_direct_delivery</a></li> +<li><a href="#6.2.28">smtp_getmxrr</a></li> +<li><a href="#6.2.29">smtp_exclude_address</a></li> +<li><a href="#6.2.30">smtp_user</a></li> +<li><a href="#6.2.31">smtp_realm</a></li> +<li><a href="#6.2.32">smtp_workstation</a></li> +<li><a href="#6.2.33">smtp_authentication_mechanism</a></li> +<li><a href="#6.2.34">smtp_password</a></li> +<li><a href="#6.2.35">smtp_pop3_auth_host</a></li> +<li><a href="#6.2.36">smtp_debug</a></li> +<li><a href="#6.2.37">smtp_html_debug</a></li> +<li><a href="#6.2.38">esmtp</a></li> +<li><a href="#6.2.39">timeout</a></li> +<li><a href="#6.2.40">invalid_recipients</a></li> +<li><a href="#6.2.41">mailer_delivery</a></li> +<li><a href="#6.2.42">maximum_bulk_deliveries</a></li> +</ul> +<li><a href="#7.1.1">Inherited variables</a></li> +<li><a href="#9.1.1">Functions</a></li> +<li><a href="#11.1.1">Inherited functions</a></li> +</ul> +<p><a href="#table_of_contents">Top of the table of contents</a></p> +</ul> +<hr /> +<ul> +<h2><li><a name="2.1.1">Summary</a></li></h2> +<ul> +<h3><a name="3.2.0">Name</a></h3> +<p>MIME E-mail message composing and sending via SMTP</p> +<h3><a name="3.2.0.0">Author</a></h3> +<p>Manuel Lemos (<a href="mailto:mlemos-at-acm.org">mlemos-at-acm.org</a>)</p> +<h3><a name="3.2.0.1">Copyright</a></h3> +<p>Copyright © (C) Manuel Lemos 1999-2004</p> +<h3><a name="3.2.0.2">Version</a></h3> +<p>@(#) $Id: smtp_message.php,v 1.34 2009/04/12 08:20:39 mlemos Exp $</p> +<h3><a name="3.2.0.3">Parent classes</a></h3> +<ul> +<p><li>MIME E-mail message composing and sending</li></p> +<p><b>Version:</b> <tt>@(#) $Id: email_message.php,v 1.87 2009/02/16 22:27:25 mlemos Exp $</tt></p> +</ul> +<h3><a name="4.2.0">Purpose</a></h3> +<p>Implement an alternative message delivery method via SMTP protocol, overriding the method of using the PHP <tt>mail()</tt> function implemented by the base class.</p> +<h3><a name="4.2.0.0">Usage</a></h3> +<p>This class should be used exactly the same way as the base class for composing and sending messages. Just create a new object of this class as follows and set only the necessary variables to configure details of the SMTP delivery.</p> +<p> <tt>require('email_message.php');<br /> + require('smtp.php');<br /> + require('smtp_message.php');<br /> + <br /> + $message_object = new smtp_message_class;<br /> +</tt></p> +<p> <b>- Requirements</b></p> +<p> You need the <a href="http://freshmeat.net/projects/smtpclass/">SMTP E-mail sending class</a> to perform the actual message delivery via the SMTP protocol.</p> +<p> <b>- SMTP connection</b></p> +<p> Before sending a message by relaying it to a given SMTP server you need set the <tt><a href="#variable_smtp_host">smtp_host</a></tt> variable to that server address. The <tt><a href="#variable_localhost">localhost</a></tt> variable needs to be set to the sending computer address.</p> +<p> You may also adjust the time the class will wait for establishing a connection by changing the <tt><a href="#variable_timeout">timeout</a></tt> variable.</p> +<p> <b>- Secure SMTP connections with SSL</b></p> +<p> Some SMTP servers, like for instance Gmail, require secure connections via SSL. In that case it is necessary to set the <tt><a href="#variable_smtp_ssl">smtp_ssl</a></tt> variable to 1. In the case of Gmail, it is also necessary to set the connection port changing the <tt><a href="#variable_smtp_port">smtp_port</a></tt> variable to 465.</p> +<p> SSL support requires at least PHP 4.3.0 with OpenSSL extension enabled.</p> +<p> <b>- Secure SMTP connections starting TLS after connections is established</b></p> +<p> Some SMTP servers, like for instance Hotmail, require starting the TLS protocol after the connection is already established to exchange data securely. In that case it is necessary to set the <tt><a href="#variable_smtp_start_tls">smtp_start_tls</a></tt> variable to 1.</p> +<p> Starting TLS protocol on an already established connection requires at least PHP 5.1.0 with OpenSSL extension enabled.</p> +<p> <b>- Authentication</b></p> +<p> Most servers only allow relaying messages sent by authorized users. If the SMTP server that you want to use requires authentication, you need to set the variables <tt><a href="#variable_smtp_user">smtp_user</a></tt>, <tt><a href="#variable_smtp_realm">smtp_realm</a></tt> and <tt><a href="#variable_smtp_password">smtp_password</a></tt>.</p> +<p> The way these values need to be set depends on the server. Usually the realm value is empty and only the user and password need to be set. If the server requires authentication via <tt>NTLM</tt> mechanism (Windows or Samba), you need to set the <tt><a href="#variable_smtp_realm">smtp_realm</a></tt> to the Windows domain name and also set the variable <tt><a href="#variable_smtp_workstation">smtp_workstation</a></tt> to the user workstation name.</p> +<p> Some servers require that the authentication be done on a separate server using the POP3 protocol before connecting to the SMTP server. In this case you need to specify the address of the POP3 server setting the <tt><a href="#variable_smtp_pop3_auth_host">smtp_pop3_auth_host</a></tt> variable.</p> +<p> <b>- Sending urgent messages with direct delivery</b></p> +<p> If you need to send urgent messages or obtain immediate confirmation that a message is accepted by the recipient SMTP server, you can use the direct delivery mode setting the <tt><a href="#variable_direct_delivery">direct_delivery</a></tt> variable to <tt>1</tt>. This mode can be used to send a message to only one recipient.</p> +<p> To use this mode, it is necessary to have a way to determine the recipient domain SMTP server address. The class uses the PHP <tt>getmxrr()</tt> function, but on some systems like for instance under Windows, this function does not work. In this case you may specify an equivalent alternative by setting the <tt><a href="#variable_smtp_getmxrr">smtp_getmxrr</a></tt> variable. See the SMTP class page for available alternatives.</p> +<p> <b>- Troubleshooting and debugging</b></p> +<p> If for some reason the delivery via SMTP is not working and the error messages are not self-explanatory, you may set the <tt><a href="#variable_smtp_debug">smtp_debug</a></tt> to <tt>1</tt> to make the class output the SMTP protocol dialog with the server. If you want to display this dialog properly formatted in an HTML page, also set the <tt><a href="#variable_smtp_debug">smtp_debug</a></tt> to <tt>1</tt>.</p> +<p> <b>- Optimizing the delivery of messages to many recipients</b></p> +<p> When sending messages to many recipients, this class can hinted to optimize its behavior by using the <tt><a href="#function_SetBulkMail">SetBulkMail</a></tt> function. After calling this function passing 1 to the <tt><a href="#argument_SetBulkMail_on">on</a></tt> argument, when the message is sent this class opens a TCP connection to the SMTP server but will not close it. This avoids the overhead of opening and closing connections.</p> +<p> When the delivery of the messages to all recipients is done, the connection may be closed implicitly by calling the <tt><a href="#function_SetBulkMail">SetBulkMail</a></tt> function again passing 0 to the <tt><a href="#argument_SetBulkMail_on">on</a></tt> argument.</p> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> +<hr /> +<ul> +<h2><li><a name="variables"></a><a name="5.1.1">Variables</a></li></h2> +<ul> +<li><tt><a href="#variable_localhost">localhost</a></tt></li><br /> +<li><tt><a href="#variable_smtp_host">smtp_host</a></tt></li><br /> +<li><tt><a href="#variable_smtp_port">smtp_port</a></tt></li><br /> +<li><tt><a href="#variable_smtp_ssl">smtp_ssl</a></tt></li><br /> +<li><tt><a href="#variable_smtp_start_tls">smtp_start_tls</a></tt></li><br /> +<li><tt><a href="#variable_smtp_direct_delivery">smtp_direct_delivery</a></tt></li><br /> +<li><tt><a href="#variable_smtp_getmxrr">smtp_getmxrr</a></tt></li><br /> +<li><tt><a href="#variable_smtp_exclude_address">smtp_exclude_address</a></tt></li><br /> +<li><tt><a href="#variable_smtp_user">smtp_user</a></tt></li><br /> +<li><tt><a href="#variable_smtp_realm">smtp_realm</a></tt></li><br /> +<li><tt><a href="#variable_smtp_workstation">smtp_workstation</a></tt></li><br /> +<li><tt><a href="#variable_smtp_authentication_mechanism">smtp_authentication_mechanism</a></tt></li><br /> +<li><tt><a href="#variable_smtp_password">smtp_password</a></tt></li><br /> +<li><tt><a href="#variable_smtp_pop3_auth_host">smtp_pop3_auth_host</a></tt></li><br /> +<li><tt><a href="#variable_smtp_debug">smtp_debug</a></tt></li><br /> +<li><tt><a href="#variable_smtp_html_debug">smtp_html_debug</a></tt></li><br /> +<li><tt><a href="#variable_esmtp">esmtp</a></tt></li><br /> +<li><tt><a href="#variable_timeout">timeout</a></tt></li><br /> +<li><tt><a href="#variable_invalid_recipients">invalid_recipients</a></tt></li><br /> +<li><tt><a href="#variable_mailer_delivery">mailer_delivery</a></tt></li><br /> +<li><tt><a href="#variable_maximum_bulk_deliveries">maximum_bulk_deliveries</a></tt></li><br /> +<p><a href="#table_of_contents">Table of contents</a></p> +<h3><a name="variable_localhost"></a><li><a name="6.2.22">localhost</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>''</tt></p> +<h3>Purpose</h3> +<p>Specify the domain name of the computer sending the message.</p> +<h3>Usage</h3> +<p>This value is used to identify the sending machine to the SMTP server. When using the direct delivery mode, if this variable is set to a non-empty string it used to generate the <tt>Recieved</tt> header to show that the message passed by the specified host address. To prevent confusing directly delivered messages with spam, it is strongly recommended that you set this variable to you server host name.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_smtp_host"></a><li><a name="6.2.23">smtp_host</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>''</tt></p> +<h3>Purpose</h3> +<p>Specify the address of the SMTP server.</p> +<h3>Usage</h3> +<p>Set to the address of the SMTP server that will relay the messages. This variable is not used in direct delivery mode.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_smtp_port"></a><li><a name="6.2.24">smtp_port</a></li></h3> +<h3>Type</h3> +<p><tt><i>int</i></tt></p> +<h3>Default value</h3> +<p><tt>25</tt></p> +<h3>Purpose</h3> +<p>Specify the TCP/IP port of SMTP server to connect.</p> +<h3>Usage</h3> +<p>Most servers work on port 25 . Certain e-mail services use alternative ports to avoid firewall blocking. Gmail uses port 465.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_smtp_ssl"></a><li><a name="6.2.25">smtp_ssl</a></li></h3> +<h3>Type</h3> +<p><tt><i>bool</i></tt></p> +<h3>Default value</h3> +<p><tt>0</tt></p> +<h3>Purpose</h3> +<p>Specify whether it should use secure connections with SSL to connect to the SMTP server.</p> +<h3>Usage</h3> +<p>Certain e-mail services like Gmail require SSL connections.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_smtp_start_tls"></a><li><a name="6.2.26">smtp_start_tls</a></li></h3> +<h3>Type</h3> +<p><tt><i>bool</i></tt></p> +<h3>Default value</h3> +<p><tt>0</tt></p> +<h3>Purpose</h3> +<p>Specify whether it should use secure connections starting TLS protocol after connecting to the SMTP server.</p> +<h3>Usage</h3> +<p>Certain e-mail services like Hotmail require starting TLS protocol after the connection to the SMTP server is already established.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_smtp_direct_delivery"></a><li><a name="6.2.27">smtp_direct_delivery</a></li></h3> +<h3>Type</h3> +<p><tt><i>bool</i></tt></p> +<h3>Default value</h3> +<p><tt>0</tt></p> +<h3>Purpose</h3> +<p>Boolean flag that indicates whether the message should be sent in direct delivery mode.</p> +<h3>Usage</h3> +<p>Set this to <tt>1</tt> if you want to send urgent messages directly to the recipient domain SMTP server.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_smtp_getmxrr"></a><li><a name="6.2.28">smtp_getmxrr</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>'getmxrr'</tt></p> +<h3>Purpose</h3> +<p>Specify the name of the function that is called to determine the SMTP server address of a given domain.</p> +<h3>Usage</h3> +<p>Change this to a working replacement of the PHP <tt>getmxrr()</tt> function if this is not working in your system and you want to send messages in direct delivery mode.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_smtp_exclude_address"></a><li><a name="6.2.29">smtp_exclude_address</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>''</tt></p> +<h3>Purpose</h3> +<p>Specify an address that should be considered invalid when resolving host name addresses.</p> +<h3>Usage</h3> +<p>In some networks any domain name that does not exist is resolved as a sub-domain of the default local domain. If the DNS is configured in such way that it always resolves any sub-domain of the default local domain to a given address, it is hard to determine whether a given domain does not exist.</p> +<p> If your network is configured this way, you may set this variable to the address that all sub-domains of the default local domain resolves, so the class can assume that such address is invalid.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_smtp_user"></a><li><a name="6.2.30">smtp_user</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>''</tt></p> +<h3>Purpose</h3> +<p>Specify the user name for authentication.</p> +<h3>Usage</h3> +<p>Set this variable if you need to authenticate before sending a message.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_smtp_realm"></a><li><a name="6.2.31">smtp_realm</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>''</tt></p> +<h3>Purpose</h3> +<p>Specify the user authentication realm.</p> +<h3>Usage</h3> +<p>Set this variable if you need to authenticate before sending a message.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_smtp_workstation"></a><li><a name="6.2.32">smtp_workstation</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>''</tt></p> +<h3>Purpose</h3> +<p>Specify the user authentication workstation needed when using the <tt>NTLM</tt> authentication (Windows or Samba).</p> +<h3>Usage</h3> +<p>Set this variable if you need to authenticate before sending a message.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_smtp_authentication_mechanism"></a><li><a name="6.2.33">smtp_authentication_mechanism</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>''</tt></p> +<h3>Purpose</h3> +<p>Specify the user authentication mechanism that should be used when authenticating with the SMTP server.</p> +<h3>Usage</h3> +<p>Set this variable if you need to force the SMTP connection to authenticate with a specific authentication mechanism. Leave this variable with an empty string if you want the authentication mechanism be determined automatically from the list of mechanisms supported by the server.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_smtp_password"></a><li><a name="6.2.34">smtp_password</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>''</tt></p> +<h3>Purpose</h3> +<p>Specify the user authentication password.</p> +<h3>Usage</h3> +<p>Set this variable if you need to authenticate before sending a message.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_smtp_pop3_auth_host"></a><li><a name="6.2.35">smtp_pop3_auth_host</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>''</tt></p> +<h3>Purpose</h3> +<p>Specify the server address for POP3 based authentication.</p> +<h3>Usage</h3> +<p>Set this variable to the address of the POP3 server if the SMTP server requires POP3 based authentication.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_smtp_debug"></a><li><a name="6.2.36">smtp_debug</a></li></h3> +<h3>Type</h3> +<p><tt><i>bool</i></tt></p> +<h3>Default value</h3> +<p><tt>0</tt></p> +<h3>Purpose</h3> +<p>Specify whether it is necessary to output SMTP connection debug information.</p> +<h3>Usage</h3> +<p>Set this variable to <tt>1</tt> if you need to see the progress of the SMTP connection and protocol dialog when you need to understand the reason for delivery problems.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_smtp_html_debug"></a><li><a name="6.2.37">smtp_html_debug</a></li></h3> +<h3>Type</h3> +<p><tt><i>bool</i></tt></p> +<h3>Default value</h3> +<p><tt>0</tt></p> +<h3>Purpose</h3> +<p>Specify whether the debug information should be outputted in HTML format.</p> +<h3>Usage</h3> +<p>Set this variable to <tt>1</tt> if you need to see the debug output in a Web page.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_esmtp"></a><li><a name="6.2.38">esmtp</a></li></h3> +<h3>Type</h3> +<p><tt><i>bool</i></tt></p> +<h3>Default value</h3> +<p><tt>1</tt></p> +<h3>Purpose</h3> +<p>Specify whether the class should try to use Enhanced SMTP protocol features.</p> +<h3>Usage</h3> +<p>It is recommended to leave this variable set to <tt>1</tt> so the class can take advantage of Enhanced SMTP protocol features.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_timeout"></a><li><a name="6.2.39">timeout</a></li></h3> +<h3>Type</h3> +<p><tt><i>int</i></tt></p> +<h3>Default value</h3> +<p><tt>25</tt></p> +<h3>Purpose</h3> +<p>Specify the connection timeout period in seconds.</p> +<h3>Usage</h3> +<p>Change this value if for some reason the timeout period seems insufficient or otherwise it seems too long.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_invalid_recipients"></a><li><a name="6.2.40">invalid_recipients</a></li></h3> +<h3>Type</h3> +<p><tt><i>array</i></tt></p> +<h3>Default value</h3> +<p><tt>array()</tt></p> +<h3>Purpose</h3> +<p>Return the list of recipient addresses that were not accepted by the SMTP server.</p> +<h3>Usage</h3> +<p>Check this variable after attempting to send a message to figure whether there were any recipients that were rejected by the SMTP server.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_mailer_delivery"></a><li><a name="6.2.41">mailer_delivery</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>'smtp $Revision: 1.34 $'</tt></p> +<h3>Purpose</h3> +<p>Specify the text that is used to identify the mail delivery class or sub-class. This text is appended to the <tt>X-Mailer</tt> header text defined by the mailer variable.</p> +<h3>Usage</h3> +<p>Do not change this variable.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_maximum_bulk_deliveries"></a><li><a name="6.2.42">maximum_bulk_deliveries</a></li></h3> +<h3>Type</h3> +<p><tt><i>int</i></tt></p> +<h3>Default value</h3> +<p><tt>100</tt></p> +<h3>Purpose</h3> +<p>Specify the number of consecutive bulk mail deliveries without disconnecting.</p> +<h3>Usage</h3> +<p>Lower this value if you have enabled the bulk mail mode but the SMTP server does not accept sending more than a number of messages within the same SMTP connection.</p> +<p> Set this value to 0 to never disconnect during bulk mail mode unless an error occurs.</p> +<p><a href="#variables">Variables</a></p> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> +<ul> +<h2><li><a name="functions"></a><a name="7.1.1">Inherited variables</a></li></h2> +<ul> +<li>email_regular_expression</li><br /> +<li>mailer</li><br /> +<li>default_charset</li><br /> +<li>line_quote_prefix</li><br /> +<li>break_long_lines</li><br /> +<li>file_buffer_length</li><br /> +<li>debug</li><br /> +<li>cache_body</li><br /> +<li>error</li><br /> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> +<hr /> +<ul> +<h2><li><a name="functions"></a><a name="9.1.1">Functions</a></li></h2> +<ul> +<p><a href="#table_of_contents">Table of contents</a></p> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> +<ul> +<h2><li><a name="functions"></a><a name="11.1.1">Inherited functions</a></li></h2> +<ul> +<li>ValidateEmailAddress</li><br /> +<li>WrapText</li><br /> +<li>CenterText</li><br /> +<li>Ruler</li><br /> +<li>QuoteText</li><br /> +<li>SetHeader</li><br /> +<li>SetEncodedHeader</li><br /> +<li>SetEncodedEmailHeader</li><br /> +<li>SetMultipleEncodedEmailHeader</li><br /> +<li>ResetMessage</li><br /> +<li>AddPart</li><br /> +<li>ReplacePart</li><br /> +<li>CreatePlainTextPart</li><br /> +<li>AddPlainTextPart</li><br /> +<li>CreateQuotedPrintableTextPart</li><br /> +<li>AddQuotedPrintableTextPart</li><br /> +<li>CreateHTMLPart</li><br /> +<li>AddHTMLPart</li><br /> +<li>CreateQuotedPrintableHTMLPart</li><br /> +<li>AddQuotedPrintableHTMLPart</li><br /> +<li>CreateFilePart</li><br /> +<li>AddFilePart</li><br /> +<li>CreateMessagePart</li><br /> +<li>AddMessagePart</li><br /> +<li>CreateAlternativeMultipart</li><br /> +<li>AddAlternativeMultipart</li><br /> +<li>CreateRelatedMultipart</li><br /> +<li>AddRelatedMultipart</li><br /> +<li>CreateMixedMultipart</li><br /> +<li>AddMixedMultipart</li><br /> +<li>GetPartContentID</li><br /> +<li>GetDataURL</li><br /> +<li>Send</li><br /> +<li>GetMessage</li><br /> +<li>GetMessageSize</li><br /> +<li>Mail</li><br /> +<li>SetBulkMail</li><br /> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> + +<hr /> +<address>Manuel Lemos (<a href="mailto:mlemos-at-acm.org">mlemos-at-acm.org</a>)</address> +</body> +</html> diff --git a/vendor/email_message/email_message.php b/vendor/email_message/email_message.php new file mode 100644 index 0000000..dcef90c --- /dev/null +++ b/vendor/email_message/email_message.php @@ -0,0 +1,3870 @@ +<?php +/* + * email_message.php + * + * @(#) $Id: email_message.php,v 1.99 2013/09/08 22:44:46 mlemos Exp $ + * + * + */ + +/* +{metadocument}<?xml version="1.0" encoding="ISO-8859-1"?> +<class> + + <package>net.manuellemos.mimemessage</package> + + <version>@(#) $Id: email_message.php,v 1.99 2013/09/08 22:44:46 mlemos Exp $</version> + <copyright>Copyright © (C) Manuel Lemos 1999-2004</copyright> + <title>MIME E-mail message composing and sending</title> + <author>Manuel Lemos</author> + <authoraddress>mlemos-at-acm.org</authoraddress> + + <documentation> + <idiom>en</idiom> + <purpose>Compose and send e-mail messages according to the MIME + standards.</purpose> + <translation>If you are interested in translating the documentation of + this class to your own idiom, please <link> + <data>contact the author</data> + <url>mailto:<getclassproperty>authoraddress</getclassproperty></url> + </link>.</translation> + <support>Technical support for using this class may be obtained in the + <tt>mimemessage-dev</tt> mailing list. Just go to the mailing list + page to browse the list archives, learn how to to join and post + support request messages:<paragraphbreak /> + <link> + <data>http://groups-beta.google.com/group/mimemessage-dev</data> + <url>http://groups-beta.google.com/group/mimemessage-dev</url> + </link></support> + <usage>To used this class just create a new object as follows, set any + variables to configure its behavior and call the functions you need + to compose and send your messages.<paragraphbreak /> + <tt>require('email_message.php');<br /> + <br /> + $message_object = new email_message_class;<br /></tt><paragraphbreak /> + <b>- Set the sender and recipients</b><paragraphbreak /> + You can set the message sender and one or more recipient addresses + using the <functionlink>SetHeader</functionlink> or the + <functionlink>SetEncodedEmailHeader</functionlink> functions + specifying the addresses for the <tt>From</tt>, <tt>To</tt>, + <tt>Cc</tt> and <tt>Bcc</tt> headers.<paragraphbreak /> + + <b>- Formatting text messages</b><paragraphbreak /> + You can use the <functionlink>WrapText</functionlink> to assure that + a text message does not have more than 75 columns by breaking the + longer lines between words.<paragraphbreak /> + <paragraphbreak /> + If you are composing a reply to another text message, you can use the + <functionlink>QuoteText</functionlink> function to conveniently mark + the text quoted from the original message.<paragraphbreak /> + + <b>- Add a plain text message body</b><paragraphbreak /> + If the text of the message that you want to send only contains ASCII + characters (7 bits), use the + <functionlink>AddPlainTextPart</functionlink> function to add the + text to the message.<paragraphbreak /> + + <b>- Add a text message with non-ASCII characters</b><paragraphbreak /> + If your message text may contains non-ASCII characters (8 bits or + more), use the + <functionlink>AddQuotedPrintableTextPart</functionlink> function + to add the text to the message.<paragraphbreak /> + <paragraphbreak /> + If the text uses a character set other than + <i>ISO-8859-1</i> (ISO Latin 1), set the + <variablelink>default_charset</variablelink> variable to change the + default character set.<paragraphbreak /> + + <b>- Setting the error message bounce address</b><paragraphbreak /> + This class provides a means to specify the address where error + messages should be bounced in case it is not possible to deliver a + message. That can be done by setting the header <tt>Return-Path</tt> + with the <functionlink>SetHeader</functionlink> + function.<paragraphbreak /> + + <b>- Request message receipt notification</b><paragraphbreak /> + If you would like to be receive an notification when a message that + is sent is received, just use the + <functionlink>SetHeader</functionlink> function with the + <tt>Disposition-Notification-To</tt> header to specify the address to + where you want to receive the notification message.<paragraphbreak /> + Keep in mind that this header just expresses that you want to get a + receipt notification, but it may be denied or ignored by the + recipient, which does not mean the message was not + received.<paragraphbreak /> + + <b>- Avoding temporary delivery failure warning messages</b><paragraphbreak /> + Sometimes it is not possible to deliver a message immediately due + to a networking failure or some other problem. In that case, the mail + transfer system usually leaves the message in a queue and keeps + retrying to deliver the message until it succeeds or it has reached + the limit number of days before it gives up. When it gives up the + the message is bounced to the return-path address.<paragraphbreak /> + However some systems send a warning message to the original sender + when it is not delivered after the first few hours. This may be an + useful notification when the message is sent by a human but it maybe + inconvinient when you are sending messages to many users like for + instance newsletters or messages to subscribers of mailing lists.<paragraphbreak /> + If you want to hint the mail transfer system to not send temporary + delivery failure warning messages, just use the + <functionlink>SetHeader</functionlink> function to set the + <tt>Precedence</tt> header to <tt>bulk</tt>.<paragraphbreak /> + Setting this header this way is a convention used by mailing list + manager programs precisely for this purpose. It may also hint some + mail receiving systems to not send auto-response messages, for + instance when the recipient user is away on vaction. However, not all + systems are aware of this convention and still send auto-response + messages when you set this header.<paragraphbreak /> + + <b>- Send the message</b><paragraphbreak /> + Once you have set the message sender, the recipients and added the + message text, use the <functionlink>Send</functionlink> function + to send the message. This class uses the PHP function <tt>mail()</tt> + to send messages.<paragraphbreak /> + <paragraphbreak /> + If for some reason you need to use a different message delivery + method, you may use one of the existing sub-classes that are + specialized in delivering messages by connecting to an SMTP server or + using directly the programs sendmail and qmail.<paragraphbreak /> + + <b>- Add an HTML message body</b><paragraphbreak /> + If you want to send an HTML message you can use the + <functionlink>AddHTMLPart</functionlink> function if it contains + only ASCII characters. If it contains non-ASCII characters, you + should the <functionlink>AddQuotedPrintableHTMLPart</functionlink> + function instead.<paragraphbreak /> + + <b>- Add alternative text body for HTML messages</b><paragraphbreak /> + Not every e-mail program can display HTML messages. Therefore, when + you send an HTML message, you should also include an alternative text + part to be displayed by programs that do not support HTML + messages.<paragraphbreak /> + <paragraphbreak /> + This is achieved by composing <tt>multipart/alternative</tt> + messages. This type of message is composed by creating the HTML + message part with the <functionlink>CreateHTMLPart</functionlink> or + the <functionlink>CreateQuotedPrintableHTMLPart</functionlink> + functions, then create the alternative text part with the + <functionlink>CreatePlainTextPart</functionlink> or the + <functionlink>CreateQuotedPrintableTextPart</functionlink> + functions, and finally use the + <functionlink>AddAlternativeMultipart</functionlink> function to add + an assembly of both message parts.<paragraphbreak /> + Note that the text part should be the first to be specified in the + array of parts passed to the + <functionlink>AddAlternativeMultipart</functionlink> function, or + else it will not appear correctly.<paragraphbreak /> + Despite this procedure adds a little complexity to the process of + sending HTML messages, it is the same procedure that is followed by + e-mail programs that are used by most people to send HTML + messages.<paragraphbreak /> + Therefore, you are strongly recommended to follow the same procedure + because some of the modern spam filter programs discard HTML messages + without an alternative plain text part, as it constitutes a pattern + that identifies messages composed by some of the spam sending + programs.<paragraphbreak /> + + <b><link> + <data>- Embed images in HTML messages</data> + <anchor>embed-image</anchor> + </link></b><paragraphbreak /> + One way to show an image in an HTML message is to use + <tt><img></tt> tag with <tt>src</tt> attribute set to the + remote site URL of the image that is meant to be displayed. + However, since the message recipient user may not be online when + they will check their e-mail, an image referenced this way may not + appear.<paragraphbreak /> + Alternatively, an image file can be embedded in an HTML message using + <tt>multipart/related</tt> message parts. This type of message part + is composed by creating the image file part with the + <functionlink>CreateFilePart</functionlink> function.<paragraphbreak /> + Then use the <functionlink>GetPartContentID</functionlink> function + the image part identifier text. Prepend the string + <stringvalue>cid:</stringvalue> to this identifier to form a special + URL that should be used in the HTML part to reference the image part + like this:<paragraphbreak /> + <tt>$image_tag = <stringvalue><img src="cid:</stringvalue> . + $message_object->GetPartContentID($image_part) . + <stringvalue>"></stringvalue> ;</tt><paragraphbreak /> + When you have composed the whole HTML document, create the HTML + message part with the <functionlink>CreateHTMLPart</functionlink> or + the <functionlink>CreateQuotedPrintableHTMLPart</functionlink> + functions, and finally use the + <functionlink>CreateRelatedMultipart</functionlink> function to + create a message part that can be added to the message with the + function <functionlink>AddAlternativeMultipart</functionlink> like + HTML messages with alternative text parts described + before.<paragraphbreak /> + Note that the HTML part must be the first listed in the parts array + argument that is passed to the function + <functionlink>CreateRelatedMultipart</functionlink>, or else the + message may not appear correctly.<paragraphbreak /> + Note also that when you are composing an HTML message with embedded + images and an alternative text part, first you need to create the + <tt>multipart/alternative</tt> part with the HTML and the text parts + using the <functionlink>CreateAlternativeMultipart</functionlink> + function, and then you add the <tt>multipart/related</tt> part to + the message with the + <functionlink>AddRelatedMultipart</functionlink> function, + passing an array of parts that lists first the + <tt>multipart/alternative</tt> part and then the image part created + before.<paragraphbreak /> + + <b>- Attach files to messages</b><paragraphbreak /> + To send a message with attached files, it is necessary to compose a + <tt>multipart/mixed</tt> message. This is a type of message made by a + text or HTML part followed by one or more file + parts.<paragraphbreak /> + If you add multiple parts to a message, this class implicitly turns + it into a <tt>multipart/mixed</tt> message. Therefore you only need + to use the function <functionlink>AddFilePart</functionlink> for each + file that you want to attach and the class will automatically + generate the message treating any parts added after the first as + attachments.<paragraphbreak /> + + <b>- Forward received messages</b><paragraphbreak /> + To forward an e-mail message received from somewhere, just use the + function <functionlink>AddMessagePart</functionlink> passing the + message complete with the original headers and body data. The message + is forwarded as an attachment that most mail programs can + display.<paragraphbreak /> + + <b>- Sending messages to many recipients (mass or bulk mailing)</b><paragraphbreak /> + Sending messages to many recipients is an activity also known as + mass or bulk mailing. There are several alternatives for mass + mailing. One way consists on specifying all recipient addresses + with the <tt>Bcc</tt> header, separating the addresses with commas + (,), or using the + <functionlink>SetMultipleEncodedEmailHeader</functionlink> function. + This way you only need to send one message that is distributed to all + recipients by your mail transfer system.<paragraphbreak /> + Unfortunately, many mail account providers like Hotmail, tend to + consider messages sent this way as spam because the real recipients + addresses are not visible in <tt>To</tt> of <tt>Cc</tt> headers. + So, this method is no longer a good solution these + days.<paragraphbreak /> + The alternative is to send a separate message to each recipient by + iteratively setting the <tt>To</tt> header with each recipient + address and calling the <functionlink>Send</functionlink> function. + This way tends to take too much time and CPU as the number of + recipients grow.<paragraphbreak /> + When sending messages to many recipients, call the + <functionlink>SetBulkMail</functionlink> function to hint the class + to optimize the way it works to make the delivery of the messages + more efficient and eventually faster.<paragraphbreak /> + The actual optimizations that are performed depend on the delivery + method that is used by this class or any of its subclasses + specialized on the different delivery methods that are supported. + Check the documentation of the subclass that you use to learn about + the optimizations that are performed, if any.<paragraphbreak /> + If you intend to send messages with the same body to all recipients, + the class can optimize the generation of the messages and reduce + significantly the composition time if you set the + <variablelink>cache_body</variablelink> variable to + <tt><booleanvalue>1</booleanvalue></tt>.<paragraphbreak /> + If you really need to personalize the content of a message part with + different text, HTML or file to each recipient, you should use the + <functionlink>ReplacePart</functionlink> function to avoid as much + as possible the overhead of composing a new message to each of the + recipients of the mailing.<paragraphbreak /> + If you are sending personalized messages to multiple recipients but + the messages include attached or embedded files that are the same + for all recipients, you should also set the + <stringvalue>Cached</stringvalue> option of the <argumentlink> + <argument>file</argument> + <function>CreateFilePart</function> + </argumentlink> parameter of the + <functionlink>CreateFilePart</functionlink> function.<paragraphbreak /> + Other than that, take a look at the documentation of the this class + sub-classes that may be used in your PHP environment, as these may + provide more efficient delivery solutions for mass mailing.<paragraphbreak /> + + <b>- Error handling</b><paragraphbreak /> + Most of the functions of this class that may fail, return an error + message string that describes the error that has occurred. If there + was no error, the functions return an empty string.<paragraphbreak /> + Verifying the return value of all the functions to determine + whether there was an error is a tedious task to implement for most + developers. To avoid this problem, this class supports <i>cumulative + error checking</i>.<paragraphbreak /> + Cumulative error checking means that when an error occurs, the class + stores the error message in the <variablelink>error</variablelink> + variable. Then, when another function that may fail is called, it + does nothing and immediately returns the same error + message.<paragraphbreak /> + This way, the developers only need to check the return value of the + last function that is called, which is usually the + <functionlink>Send</functionlink> function. + </usage> + </documentation> + +{/metadocument} +*/ + +class email_message_class +{ + /* Private variables */ + + var $headers=array("To"=>"","Subject"=>""); + var $body=-1; + var $body_parts=0; + var $parts=array(); + var $total_parts=0; + var $free_parts=array(); + var $total_free_parts=0; + var $delivery=array("State"=>""); + var $next_token=""; + var $php_version=0; + var $mailings=array(); + var $last_mailing=0; + var $header_length_limit=512; + var $auto_message_id=1; + var $mailing_path=""; + var $body_cache=array(); + var $line_break="\n"; + var $line_length=76; + var $ruler="_"; + var $email_address_pattern="([-!#\$%&'*+./0-9=?A-Z^_`a-z{|}~])+@([-!#\$%&'*+/0-9=?A-Z^_`a-z{|}~]+\\.)+[a-zA-Z]{2,6}"; + var $bulk_mail=0; + + /* Public variables */ + +/* +{metadocument} + <variable> + <name>email_regular_expression</name> + <type>STRING</type> + <value>^([-!#$%&'*+./0-9=?A-Z^_`a-z{|}~])+@([-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+\.)+[a-zA-Z]{2,6}$</value> + <documentation> + <purpose>Specify the regular expression that is used by the + <functionlink>ValidateEmailAddress</functionlink> function to + verify whether a given e-mail address may be valid.</purpose> + <usage>Do not change this variable unless you have reason to believe + that it is rejecting existing e-mail addresses that are known to be + valid.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $email_regular_expression="^([-!#\$%&'*+./0-9=?A-Z^_`a-z{|}~])+@([-!#\$%&'*+/0-9=?A-Z^_`a-z{|}~]+\\.)+[a-zA-Z]{2,6}\$"; + +/* +{metadocument} + <variable> + <name>mailer</name> + <type>STRING</type> + <value>http://www.phpclasses.org/mimemessage $Revision: 1.99 $</value> + <documentation> + <purpose>Specify the base text that is used identify the name and the + version of the class that is used to send the message by setting an + implicit the <tt>X-Mailer</tt> message header. This is meant + mostly to assist on the debugging of delivery problems.</purpose> + <usage>Change this to set another mailer identification string or + leave it to an empty string to prevent that the <tt>X-Mailer</tt> + header be added to the message.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $mailer=''; + +/* +{metadocument} + <variable> + <name>mailer_delivery</name> + <type>STRING</type> + <value>mail</value> + <documentation> + <purpose>Specify the text that is used to identify the mail + delivery class or sub-class. This text is appended to the + <tt>X-Mailer</tt> header text defined by the + <variablelink>mailer</variablelink> variable.</purpose> + <usage>This variable should only be redefined by the different mail + delivery sub-classes.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $mailer_delivery='mail'; + +/* +{metadocument} + <variable> + <name>default_charset</name> + <type>STRING</type> + <value>ISO-8859-1</value> + <documentation> + <purpose>Specify the default character set to be assumed for the + message headers and body text.</purpose> + <usage>Change this variable to the correct character set name if it + is different than the default.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $default_charset="ISO-8859-1"; + +/* +{metadocument} + <variable> + <name>line_quote_prefix</name> + <type>STRING</type> + <value>> </value> + <documentation> + <purpose>Specify the default line quote prefix text used by the + <functionlink>QuoteText</functionlink> function.</purpose> + <usage>Change it only if you prefer to quote lines marking them with + a different line prefix.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $line_quote_prefix="> "; + +/* +{metadocument} + <variable> + <name>break_long_lines</name> + <type>BOOLEAN</type> + <value>1</value> + <documentation> + <purpose>Determine whether lines exceeding the length limit will be + broken by the line break character when using the + <functionlink>WrapText</functionlink> function.</purpose> + <usage>Change it only if you want to avoid breaking long lines + without any space characters, like for instance of messages with + long URLs.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $break_long_lines=1; + +/* +{metadocument} + <variable> + <name>file_buffer_length</name> + <type>INTEGER</type> + <value>8000</value> + <documentation> + <purpose>Specify the length of the buffer that is used to read + files in chunks of limited size.</purpose> + <usage>The default value may be increased if you have plenty of + memory and want to benefit from additional speed when processing + the files that are used to compose messages.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $file_buffer_length=8000; + +/* +{metadocument} + <variable> + <name>debug</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Specify the name of a function that is called whenever an + error occurs.</purpose> + <usage>If you need to track the errors that may happen during the use + of the class, set this variable to the name of a callback function. + It should take only one argument that is the error message. When + this variable is set to an empty string, no debug callback function + is called.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $debug=""; + +/* +{metadocument} + <variable> + <name>cache_body</name> + <type>BOOLEAN</type> + <value>0</value> + <documentation> + <purpose>Specify whether the message bodies that are generated by the + class before sending, should be cached in memory to be reused on + the next message delivery.</purpose> + <usage>Set this variable to <tt><booleanvalue>1</booleanvalue></tt> + if you intend to send the a message with the same body to many + recipients, so the class avoids the overhead of regenerating + messages with the same content.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $cache_body=0; + +/* +{metadocument} + <variable> + <name>error</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Store the last error return by any function that may fail + due to some error.</purpose> + <usage>Do not change this variable value unless you intend to clear + the error status by setting it to an empty string.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $error=""; + +/* +{metadocument} + <variable> + <name>localhost</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Specify the domain name of the computer sending the + message.</purpose> + <usage>This value is used as default domain of the sender e-mail + address when generating automatic <tt>Message-Id</tt> + headers.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $localhost=""; + + /* Private methods */ + + Function Tokenize($string,$separator="") + { + if(!strcmp($separator,"")) + { + $separator=$string; + $string=$this->next_token; + } + for($character=0;$character<strlen($separator);++$character) + { + if(GetType($position=strpos($string,$separator[$character]))=="integer") + $found=(IsSet($found) ? min($found,$position) : $position); + } + if(IsSet($found)) + { + $this->next_token=substr($string,$found+1); + return(substr($string,0,$found)); + } + else + { + $this->next_token=""; + return($string); + } + } + + Function GetFilenameExtension($filename) + { + return(GetType($dot=strrpos($filename,"."))=="integer" ? substr($filename,$dot) : ""); + } + + Function OutputError($error) + { + if(strcmp($function=$this->debug,"") + && strcmp($error,"")) + $function($error); + return($this->error=$error); + } + + Function OutputPHPError($error, &$php_error_message) + { + if(IsSet($php_error_message) + && strlen($php_error_message)) + $error.=": ".$php_error_message; + return($this->OutputError($error)); + } + + Function GetPHPVersion() + { + if($this->php_version==0) + { + $version=explode(".",function_exists("phpversion") ? phpversion() : "3.0.7"); + $this->php_version=$version[0]*1000000+$version[1]*1000+$version[2]; + } + return($this->php_version); + } + + Function EscapePattern($pattern) + { + return('/'.str_replace('/', '\\/', $pattern).'/'); + } + + Function GetRFC822Addresses($address,&$addresses) + { + if(function_exists("imap_rfc822_parse_adrlist")) + { + if(GetType($parsed_addresses=@imap_rfc822_parse_adrlist($address,$this->localhost))!="array") + return("it was not specified a valid address list"); + for($entry=0;$entry<count($parsed_addresses);++$entry) + { + if(!IsSet($parsed_addresses[$entry]->host) + || $parsed_addresses[$entry]->host==".SYNTAX-ERROR.") + return($parsed_addresses[$entry]->mailbox." .SYNTAX-ERROR."); + $parsed_address=$parsed_addresses[$entry]->mailbox."@".$parsed_addresses[$entry]->host; + if(IsSet($addresses[$parsed_address])) + ++$addresses[$parsed_address]; + else + $addresses[$parsed_address]=1; + } + } + else + { + $length=strlen($address); + for($position=0;$position<$length;) + { + $match=preg_split($this->EscapePattern($this->email_address_pattern),strtolower(substr($address,$position)),2); + if(count($match)<2) + break; + $position+=strlen($match[0]); + $next_position=$length-strlen($match[1]); + $found=substr($address,$position,$next_position-$position); + if(!strcmp($found,"")) + break; + if(IsSet($addresses[$found])) + ++$addresses[$found]; + else + $addresses[$found]=1; + $position=$next_position; + } + } + return(""); + } + + Function FormatHeader($header_name,$header_value) + { + $length=strlen($header_value); + for($header_data="",$header_line=$header_name.": ",$line_length=strlen($header_line),$position=0;$position<$length;) + { + for($space=$position,$line_length=strlen($header_line);$space<$length;) + { + if(GetType($next=strpos($header_value," ",$space+1))!="integer") + $next=$length; + if($next-$position+$line_length>$this->header_length_limit) + { + if($space==$position) + $space=$next; + break; + } + $space=$next; + } + $header_data.=$header_line.substr($header_value,$position,$space-$position); + if($space<$length) + $header_line=""; + $position=$space; + if($position<$length) + $header_data.=$this->line_break; + } + return($header_data); + } + + Function GenerateMessageID($sender) + { + $micros=$this->Tokenize(microtime()," "); + $seconds=$this->Tokenize(""); + $local=$this->Tokenize($sender,"@"); + $host=$this->Tokenize(" @"); + if(strlen($host) + && $host[strlen($host)-1]=="-") + $host=substr($host,0,strlen($host)-1); + return($this->FormatHeader("Message-ID", "<".strftime("%Y%m%d%H%M%S", $seconds).substr($micros,1,5).".".preg_replace('/[^A-Za-z]/', '-', $local)."@".preg_replace('/[^.A-Za-z_-]/', '', $host).">")); + } + + Function SendMail($to, $subject, $body, $headers, $return_path) + { + $ini_safemode_chk = false; + if (version_compare(PHP_VERSION, '5.3.0', '<')) { + $ini_safemode_chk = ini_get('safe_mode'); + } + if(!function_exists("mail")) + return($this->OutputError("the mail() function is not available in this PHP installation")); + if(strlen($return_path)) + { + if(!defined("PHP_OS")) + return($this->OutputError("it is not possible to set the Return-Path header with your PHP version")); + if(!strcmp(substr(PHP_OS,0,3),"WIN")) + return($this->OutputError("it is not possible to set the Return-Path header directly from a PHP script on Windows")); + if($this->GetPHPVersion()<4000005) + return($this->OutputError("it is not possible to set the Return-Path header in PHP version older than 4.0.5")); + if(function_exists("ini_get") + && $ini_safemode_chk) + return($this->OutputError("it is not possible to set the Return-Path header due to PHP safe mode restrictions")); + $success=@mail($to,$subject,$body,$headers,"-f".$return_path); + } + else + $success=@mail($to,$subject,$body,$headers); + return($success ? "" : $this->OutputPHPError("it was not possible to send e-mail message", error_get_last()['message'])); + } + + Function StartSendingMessage() + { + if(strcmp($this->delivery["State"],"")) + return($this->OutputError("the message was already started to be sent")); + $this->delivery=array("State"=>"SendingHeaders"); + return(""); + } + + Function SendMessageHeaders($headers) + { + if(strcmp($this->delivery["State"],"SendingHeaders")) + { + if(!strcmp($this->delivery["State"],"")) + return($this->OutputError("the message was not yet started to be sent")); + else + return($this->OutputError("the message headers were already sent")); + } + $this->delivery["Headers"]=$headers; + $this->delivery["State"]="SendingBody"; + return(""); + } + + Function SendMessageBody($data) + { + if(strcmp($this->delivery["State"],"SendingBody")) + return($this->OutputError("the message headers were not yet sent")); + if(IsSet($this->delivery["Body"])) + $this->delivery["Body"].=$data; + else + $this->delivery["Body"]=$data; + return(""); + } + + Function EndSendingMessage() + { + if(strcmp($this->delivery["State"],"SendingBody")) + return($this->OutputError("the message body data was not yet sent")); + if(!IsSet($this->delivery["Headers"]) + || count($this->delivery["Headers"])==0) + return($this->OutputError("message has no headers")); + $line_break=((defined("PHP_OS") && !strcmp(substr(PHP_OS,0,3),"WIN")) ? "\r\n" : $this->line_break); + $headers=$this->delivery["Headers"]; + for($has=array(),$headers_text="",$header=0,Reset($headers);$header<count($headers);Next($headers),++$header) + { + $header_name=Key($headers); + switch(strtolower($header_name)) + { + case "to": + case "subject": + $has[strtolower($header_name)]=$headers[$header_name]; + break; + case "cc": + case "bcc": + case "from": + case "return-path": + case "message-id": + $has[strtolower($header_name)]=$headers[$header_name]; + default: + $header_line=$header_name.": ".$headers[$header_name]; + if(strlen($headers_text)) + $headers_text.=$this->line_break.$header_line; + else + $headers_text=$header_line; + } + } + if(strlen($has["to"])==0 + && !IsSet($has["cc"]) + && !IsSet($has["bcc"])) + return($this->OutputError("it were not specified a valid To:, Cc: or Bcc: headers")); + if(!IsSet($has["subject"])) + return($this->OutputError("it was not specified a valid Subject: header")); + if(!IsSet($has["message-id"]) + && $this->auto_message_id) + { + $sender = $senders = array(); + if(IsSet($has["return-path"])) + $sender[] = $has["return-path"]; + if(IsSet($has["from"])) + $sender[] = $has["from"]; + $sender[] = $has["to"]; + $ts = count($sender); + for($s = 0; $s < $ts; ++$s) + { + $error = $this->GetRFC822Addresses($sender[$s], $senders); + if(strlen($error) == 0 + && count($senders)) + break; + } + if(count($senders) == 0) + return('it was not specified a valid sender address'.(strlen($error) ? ': '.$error : '')); + Reset($senders); + $sender=Key($senders); + $header_line=$this->GenerateMessageID($sender); + if(strlen($headers_text)) + $headers_text.=$this->line_break.$header_line; + else + $headers_text=$header_line; + } + if(strcmp($error=$this->SendMail(strlen($has["to"]) ? $has["to"] : (IsSet($has["cc"]) ? "" : "undisclosed-recipients: ;"), $has["subject"], $this->delivery["Body"], $headers_text, IsSet($has["return-path"]) ? $has["return-path"] : ""),"")) + return($error); + $this->delivery=array("State"=>""); + return(""); + } + + Function StopSendingMessage() + { + $this->delivery=array("State"=>""); + return(""); + } + + Function GetPartBoundary($part) + { + if(!IsSet($this->parts[$part]["BOUNDARY"])) + $this->parts[$part]["BOUNDARY"]=md5(uniqid($part.time())); + } + + Function GetPartHeaders(&$headers,$part) + { + if(IsSet($this->parts[$part]['CachedHeaders'])) + { + $headers = $this->parts[$part]['CachedHeaders']; + return(''); + } + if(!IsSet($this->parts[$part]["Content-Type"])) + return($this->OutputError("it was added a part without Content-Type: defined")); + $type=$this->Tokenize($full_type=strtolower($this->parts[$part]["Content-Type"]),"/"); + $sub_type=$this->Tokenize(""); + switch($type) + { + case "text": + case "image": + case "audio": + case "video": + case "application": + case "message": + if(IsSet($this->parts[$part]["NAME"])) + $filename = $this->QuotedPrintableEncode($this->parts[$part]["NAME"], $this->default_charset, 1, 1); + $headers["Content-Type"]=$full_type.(IsSet($this->parts[$part]["CHARSET"]) ? "; charset=".$this->parts[$part]["CHARSET"] : "").(IsSet($this->parts[$part]["NAME"]) ? "; name=\"".$filename."\"" : ""); + if(IsSet($this->parts[$part]["Content-Transfer-Encoding"])) + $headers["Content-Transfer-Encoding"]=$this->parts[$part]["Content-Transfer-Encoding"]; + if(IsSet($this->parts[$part]["DISPOSITION"]) + && strlen($this->parts[$part]["DISPOSITION"])) + $headers["Content-Disposition"]=$this->parts[$part]["DISPOSITION"].(IsSet($this->parts[$part]["NAME"]) ? "; filename=\"".$filename."\"" : ""); + break; + case "multipart": + switch($sub_type) + { + case "alternative": + case "related": + case "mixed": + case "parallel": + $this->GetPartBoundary($part); + $headers["Content-Type"]=$full_type."; boundary=\"".$this->parts[$part]["BOUNDARY"]."\""; + break; + default: + return($this->OutputError("multipart Content-Type sub_type $sub_type not yet supported")); + } + break; + default: + return($this->OutputError("Content-Type: $full_type not yet supported")); + } + if(IsSet($this->parts[$part]["Content-ID"])) + $headers["Content-ID"]="<".$this->parts[$part]["Content-ID"].">"; + if(IsSet($this->parts[$part]['Cache']) + && $this->parts[$part]['Cache']) + $this->parts[$part]['CachedHeaders'] = $headers; + return(""); + } + + Function GetPartBody(&$body,$part) + { + if(IsSet($this->parts[$part]['CachedBody'])) + { + $body = $this->parts[$part]['CachedBody']; + return(''); + } + if(!IsSet($this->parts[$part]["Content-Type"])) + return($this->OutputError("it was added a part without Content-Type: defined")); + $type=$this->Tokenize($full_type=strtolower($this->parts[$part]["Content-Type"]),"/"); + $sub_type=$this->Tokenize(""); + $body=""; + switch($type) + { + case "text": + case "image": + case "audio": + case "video": + case "application": + case "message": + if(IsSet($this->parts[$part]["FILENAME"])) + { + $size=@filesize($this->parts[$part]["FILENAME"]); + if(!($file=@fopen($this->parts[$part]["FILENAME"],"rb"))) + return($this->OutputPHPError("could not open part file ".$this->parts[$part]["FILENAME"], error_get_last()['message'])); + while(!feof($file)) + { + if(GetType($block=@fread($file,$this->file_buffer_length))!="string") + { + fclose($file); + return($this->OutputPHPError("could not read part file", error_get_last()['message'])); + } + $body.=$block; + } + fclose($file); + if((GetType($size)=="integer" + && strlen($body)>$size) + || (function_exists("get_magic_quotes_runtime") + && get_magic_quotes_runtime())) + $body=StripSlashes($body); + if(GetType($size)=="integer" + && strlen($body)!=$size) + return($this->OutputError("the length of the file that was read does not match the size of the part file ".$this->parts[$part]["FILENAME"]." due to possible data corruption")); + } + else + { + if(!IsSet($this->parts[$part]["DATA"])) + return($this->OutputError("it was added a part without a body PART")); + $body=$this->parts[$part]["DATA"]; + } + $encoding=(IsSet($this->parts[$part]["Content-Transfer-Encoding"]) ? strtolower($this->parts[$part]["Content-Transfer-Encoding"]) : ""); + switch($encoding) + { + case "base64": + $body=chunk_split(base64_encode($body), $this->line_length, $this->line_break); + break; + case "": + case "quoted-printable": + case "7bit": + break; + default: + return($this->OutputError($encoding." is not yet a supported encoding type")); + } + break; + case "multipart": + switch($sub_type) + { + case "alternative": + case "related": + case "mixed": + case "parallel": + $this->GetPartBoundary($part); + $boundary="--".$this->parts[$part]["BOUNDARY"]; + $parts=count($this->parts[$part]["PARTS"]); + $b = $this->line_break; + $lb = strlen($b); + for($multipart=0;$multipart<$parts;$multipart++) + { + if(strlen($body) >= $lb + && strcmp(substr($body, -$lb), $b)) + $body.=$b; + $body.=$boundary.$this->line_break; + $part_headers=array(); + $sub_part=$this->parts[$part]["PARTS"][$multipart]; + if(strlen($error=$this->GetPartHeaders($part_headers,$sub_part))) + return($error); + for($part_header=0,Reset($part_headers);$part_header<count($part_headers);$part_header++,Next($part_headers)) + { + $header=Key($part_headers); + $body.=$header.": ".$part_headers[$header].$b; + } + $body.=$b; + if(strlen($error=$this->GetPartBody($part_body,$sub_part))) + return($error); + $body.=$part_body; + } + if(strlen($body) >= $lb + && strcmp(substr($body, -$lb), $b)) + $body.=$b; + $body.=$boundary."--".$b; + break; + default: + return($this->OutputError("multipart Content-Type sub_type $sub_type not yet supported")); + } + break; + default: + return($this->OutputError("Content-Type: $full_type not yet supported")); + } + if(IsSet($this->parts[$part]['Cache']) + && $this->parts[$part]['Cache']) + $this->parts[$part]['CachedBody'] = $body; + return(""); + } + + /* Public functions */ + +/* +{metadocument} + <function> + <name>ValidateEmailAddress</name> + <type>BOOLEAN</type> + <documentation> + <purpose>Determine whether a given e-mail address may be + valid.</purpose> + <usage>Just pass the e-mail <argumentlink> + <function>ValidateEmailAddress</function> + <argument>address</argument> + </argumentlink> to be checked as function argument. This function + uses the regular expression defined by the + <variablelink>email_regular_expression</variablelink> variable to + check the address.</usage> + <returnvalue>The function returns + <tt><booleanvalue>1</booleanvalue></tt> if the specified address + may be valid.</returnvalue> + </documentation> + <argument> + <name>address</name> + <type>STRING</type> + <documentation> + <purpose>Specify the e-mail address to be validated.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function ValidateEmailAddress($address) + { + return(preg_match('/'.str_replace('/', '\\/', $this->email_regular_expression).'/i',$address)); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + + Function EncodeCharacter($matches) + { + return sprintf('=%02X', Ord($matches[1])); + } + + Function QuotedPrintableEncode($text, $header_charset='', $break_lines=1, $email_header = 0) + { + $ln=strlen($text); + $h=(strlen($header_charset)>0); + if($h) + { + $encode = array( + '='=>1, + '?'=>1, + '_'=>1, + '('=>1, + ')'=>1, + '<'=>1, + '>'=>1, + '@'=>1, + ','=>1, + ';'=>1, + '"'=>1, + '\\'=>1, + '['=>1, + ']'=>1, + ':'=>1, +/* + '/'=>1, + '.'=>1, +*/ + ); + $s=($email_header ? $encode : array()); + $b=$space=$break_lines=0; + for($i=0; $i<$ln; ++$i) + { + $c = $text[$i]; + if(IsSet($s[$c])) + { + $b=1; + break; + } + switch($o=Ord($c)) + { + case 9: + case 32: + $space=$i+1; + $b=1; + break 2; + case 10: + case 13: + break 2; + default: + if($o<32 + || $o>127) + { + $b=1; + $s = $encode; + break 2; + } + } + } + if($i==$ln) + return($text); + if($space>0) + return(substr($text,0,$space).($space<$ln ? $this->QuotedPrintableEncode(substr($text,$space), $header_charset, $break_lines, $email_header) : "")); + } + elseif(function_exists('quoted_printable_encode')) + { + $different = strcmp($this->line_break, "\r\n"); + if($different) + $text = str_replace($this->line_break, "\r\n", str_replace("\r\n", $this->line_break, $text)); + $encoded = preg_replace_callback('/^(f|F|\\.)/m', array($this, 'EncodeCharacter'), quoted_printable_encode($text)); + if($different) + $encoded = str_replace("\r\n", $this->line_break, $encoded); + return $encoded; + } + for($w=$e='',$n=0, $l=0,$i=0;$i<$ln; ++$i) + { + $c = $text[$i]; + $o=Ord($c); + $en=0; + switch($o) + { + case 9: + case 32: + if(!$h) + { + $w=$c; + $c=''; + } + else + { + if($b) + { + if($o==32) + $c='_'; + else + $en=1; + } + } + break; + case 10: + case 13: + if(strlen($w)) + { + if($break_lines + && $l+3>75) + { + $e.='='.$this->line_break; + $l=0; + } + $e.=sprintf('=%02X',Ord($w)); + $l+=3; + $w=''; + } + $e.=$c; + if($h) + $e.="\t"; + $l=0; + continue 2; + case 46: + case 70: + case 102: + $en=(!$h && ($l==0 || $l+1>75)); + break; + default: + if($o>127 + || $o<32 + || !strcmp($c,'=')) + $en=1; + elseif($h + && IsSet($s[$c])) + $en=1; + break; + } + if(strlen($w)) + { + if($break_lines + && $l+1>75) + { + $e.='='.$this->line_break; + $l=0; + } + $e.=$w; + ++$l; + $w=''; + } + if(strlen($c)) + { + if($en) + { + $c=sprintf('=%02X',$o); + $el=3; + $n=1; + $b=1; + } + else + $el=1; + if($break_lines + && $l+$el>75) + { + $e.='='.$this->line_break; + $l=0; + } + $e.=$c; + $l+=$el; + } + } + if(strlen($w)) + { + if($break_lines + && $l+3>75) + $e.='='.$this->line_break; + $e.=sprintf('=%02X',Ord($w)); + } + if($h + && $n) + return('=?'.$header_charset.'?q?'.$e.'?='); + else + return($e); + } + +/* +{metadocument} + <function> + <name>WrapText</name> + <type>STRING</type> + <documentation> + <purpose>Split a text in lines that do not exceed the length limit + avoiding to break it in the middle of any words.</purpose> + <usage>Just pass the <argumentlink> + <function>WrapText</function> + <argument>text</argument> + </argumentlink> to be wrapped.</usage> + <returnvalue>The wrapped text eventually broken in multiple lines + that do not exceed the line length limit.</returnvalue> + </documentation> + <argument> + <name>text</name> + <type>STRING</type> + <documentation> + <purpose>Text to be wrapped.</purpose> + </documentation> + </argument> + <argument> + <name>line_length</name> + <type>INTEGER</type> + <defaultvalue>0</defaultvalue> + <documentation> + <purpose>Line length limit. Pass a value different than + <tt><integervalue>0</integervalue></tt> to use a line length + limit other than the default of 75 characters.</purpose> + </documentation> + </argument> + <argument> + <name>line_break</name> + <type>STRING</type> + <defaultvalue></defaultvalue> + <documentation> + <purpose>Character sequence that is used to break the lines longer + than the length limit. Pass a non-empty to use a line breaking + sequence other than the default + <tt><stringvalue> </stringvalue></tt>.</purpose> + </documentation> + </argument> + <argument> + <name>line_prefix</name> + <type>STRING</type> + <defaultvalue></defaultvalue> + <documentation> + <purpose>Character sequence that is used to insert in the beginning + of all lines.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function WrapText($text,$line_length=0,$line_break="",$line_prefix="") + { + if(strlen($line_break)==0) + $line_break=$this->line_break; + if($line_length==0) + $line_length=$this->line_length; + $lines=explode("\n",str_replace("\r","\n",str_replace("\r\n","\n",$text))); + for($wrapped="",$line=0;$line<count($lines);++$line) + { + if(strlen($text_line=$lines[$line])) + { + for(;strlen($text_line=$line_prefix.$text_line)>$line_length;) + { + if(GetType($cut=strrpos(substr($text_line,0,$line_length)," "))!="integer" + || $cut<strlen($line_prefix)) + { + if($this->break_long_lines) + { + $wrapped.=substr($text_line,0,$line_length).$line_break; + $cut=$line_length; + } + elseif(GetType($cut=strpos($text_line," ",$line_length))=="integer") + { + $wrapped.=substr($text_line, 0, $cut).$line_break; + ++$cut; + } + else + { + $wrapped.=$text_line.$line_break; + $cut=strlen($text_line); + } + } + else + { + $wrapped.=substr($text_line,0,$cut).$line_break; + ++$cut; + } + $text_line=substr($text_line,$cut); + } + } + $wrapped.=$text_line.$line_break; + } + return($wrapped); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>CenterText</name> + <type>STRING</type> + <documentation> + <purpose>Center a text in the middle of line.</purpose> + <usage>Just pass the <argumentlink> + <function>CenterText</function> + <argument>text</argument> + </argumentlink> to be centered.</usage> + <returnvalue>The centered text.</returnvalue> + </documentation> + <argument> + <name>text</name> + <type>STRING</type> + <documentation> + <purpose>Text to be centered.</purpose> + </documentation> + </argument> + <argument> + <name>line_length</name> + <type>INTEGER</type> + <defaultvalue>0</defaultvalue> + <documentation> + <purpose>Line length limit. Pass a value different than + <tt><integervalue>0</integervalue></tt> to use a line length + limit other than the default of 75 characters.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function CenterText($text, $line_length=0) + { + if($line_length==0) + $line_length=$this->line_length; + $length = strlen($text); + if($length<$line_length) + $text = str_repeat(' ', ($line_length-$length)/2).$text; + return($text); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>Ruler</name> + <type>STRING</type> + <documentation> + <purpose>Generate a line with characters that can be displayed as a + separator ruler in a text message.</purpose> + <returnvalue>The ruler line string.</returnvalue> + </documentation> + <argument> + <name>line_length</name> + <type>INTEGER</type> + <defaultvalue>0</defaultvalue> + <documentation> + <purpose>Line length limit. Pass a value different than + <tt><integervalue>0</integervalue></tt> to use a line length + limit other than the default of 75 characters.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function Ruler($line_length=0) + { + if($line_length==0) + $line_length=$this->line_length; + return(str_repeat($this->ruler, $line_length)); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>QuoteText</name> + <type>STRING</type> + <documentation> + <purpose>Mark a text block to appear like in reply messages composed + with common e-mail programs that include text from the original + message being replied.</purpose> + <usage>Just pass the <argumentlink> + <function>QuoteText</function> + <argument>text</argument> + </argumentlink> to be marked as a quote.</usage> + <returnvalue>The quoted text with all lines prefixed with a quote + prefix mark.</returnvalue> + </documentation> + <argument> + <name>text</name> + <type>STRING</type> + <documentation> + <purpose>Text to be quoted.</purpose> + </documentation> + </argument> + <argument> + <name>quote_prefix</name> + <type>STRING</type> + <defaultvalue></defaultvalue> + <documentation> + <purpose>Character sequence that is inserted in the beginning of + all lines as a quote mark. Set to an empty string to tell the + function to use the default specified by the + <variablelink>line_quote_prefix</variablelink> variable.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function QuoteText($text,$quote_prefix="") + { + if(strlen($quote_prefix)==0) + $quote_prefix=$this->line_quote_prefix; + return($this->WrapText($text,$line_length=0,$line_break="",$quote_prefix)); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>SetHeader</name> + <type>STRING</type> + <documentation> + <purpose>Set the value of a message header.</purpose> + <usage>Use this function to set the values of the headers of the + message that may be needed. There are some message headers that are + automatically set by the class when the message is sent. Others + must be defined before sending. Here follows the list of the names + of the headers that must be set before sending:<paragraphbreak /> + <paragraphbreak /> + <b>Message subject</b> - <tt>Subject</tt><paragraphbreak /> + <b>Sender address</b> - <tt>From</tt><paragraphbreak /> + <b>Recipient addresses</b> - <tt>To</tt>, <tt>Cc</tt> and + <tt>Bcc</tt><paragraphbreak /> + Each of the recipient address headers may contain one or more + addresses. Multiple addresses must be separated by a comma and a + space.<paragraphbreak /> + <b>Return path address</b> - <tt>Return-Path</tt><paragraphbreak /> + Optional header to specify the address where the message should be + bounced in case it is not possible to deliver it.<paragraphbreak /> + In reality this is a virtual header. This means that adding this + header to a message will not do anything by itself. However, this + class looks for this header to adjust the message delivery + procedure in such way that the Message Transfer Agent (MTA) system + is hinted to direct any bounced messages to the address specified + by this header.<paragraphbreak /> + Note that under some systems there is no way to set the return path + address programmatically. This is the case when using the PHP + <tt>mail()</tt> function under Windows where the return path + address should be set in the <tt>php.ini</tt> configuration + file.<paragraphbreak /> + Keep in mind that even when it is possible to set the return path + address, the systems of some e-mail account providers may ignore + this address and send bounced messages to the sender address. This + is a bug of those systems. There is nothing that can be done other + than complain.</usage> + <returnvalue>An error message in case there was an error or an empty + string otherwise. This return value may be safely ignored if the + function parameters are set correctly.</returnvalue> + </documentation> + <argument> + <name>header</name> + <type>STRING</type> + <documentation> + <purpose>Name of the header.</purpose> + </documentation> + </argument> + <argument> + <name>value</name> + <type>STRING</type> + <documentation> + <purpose>Text value for the header.</purpose> + </documentation> + </argument> + <argument> + <name>encoding_charset</name> + <type>STRING</type> + <defaultvalue></defaultvalue> + <documentation> + <purpose>Character set used in the header value. If it is set to an + empty string, it is assumed the character set defined by the + <variablelink>default_charset</variablelink> variable.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function SetHeader($header, $value, $encoding_charset="") + { + if(strlen($this->error)) + return($this->error); + $this->headers[strval($header)]=(!strcmp($encoding_charset,"") ? strval($value) : $this->QuotedPrintableEncode($value, $encoding_charset, 1, 0)); + return(""); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>SetEncodedHeader</name> + <type>STRING</type> + <documentation> + <purpose>The same as the <functionlink>SetHeader</functionlink> + function assuming the default character set specified by the + <variablelink>default_charset</variablelink> variable.</purpose> + <usage>See the <functionlink>SetHeader</functionlink> function.</usage> + <returnvalue>An error message in case there was an error or an empty + string otherwise. This return value may be safely ignored if the + function parameters are set correctly.</returnvalue> + </documentation> + <argument> + <name>header</name> + <type>STRING</type> + <documentation> + <purpose>Name of the header.</purpose> + </documentation> + </argument> + <argument> + <name>value</name> + <type>STRING</type> + <documentation> + <purpose>Text value for the header.</purpose> + </documentation> + </argument> + <argument> + <name>encoding_charset</name> + <type>STRING</type> + <defaultvalue></defaultvalue> + <documentation> + <purpose>Character set used in the header value. If it is set to an + empty string, it is assumed the character set defined by the + <variablelink>default_charset</variablelink> variable.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function SetEncodedHeader($header,$value, $encoding_charset = '') + { + return($this->SetHeader($header,$value,strlen($encoding_charset) ? $encoding_charset : $this->default_charset)); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>SetEncodedEmailHeader</name> + <type>STRING</type> + <documentation> + <purpose>Set the value of an header that is meant to represent the + e-mail address of a person or entity with a known name. This is + meant mostly to set the <tt>From</tt>, <tt>To</tt>, <tt>Cc</tt> and + <tt>Bcc</tt> headers.</purpose> + <usage>Use this function like the + <functionlink>SetHeader</functionlink> specifying the e-mail + <argumentlink> + <function>SetEncodedEmailHeader</function> + <argument>address</argument> + </argumentlink> as header value and also specifying the + <argumentlink> + <function>SetEncodedEmailHeader</function> + <argument>name</argument> + </argumentlink> of the known person or entity.</usage> + <returnvalue>An error message in case there was an error or an empty + string otherwise. This return value may be safely ignored if the + function parameters are set correctly.</returnvalue> + </documentation> + <argument> + <name>header</name> + <type>STRING</type> + <documentation> + <purpose>Name of the header.</purpose> + </documentation> + </argument> + <argument> + <name>address</name> + <type>STRING</type> + <documentation> + <purpose>E-mail address value.</purpose> + </documentation> + </argument> + <argument> + <name>name</name> + <type>STRING</type> + <documentation> + <purpose>Person or entity name associated with the specified e-mail + address.</purpose> + </documentation> + </argument> + <argument> + <name>encoding_charset</name> + <type>STRING</type> + <defaultvalue></defaultvalue> + <documentation> + <purpose>Character set used in the header value. If it is set to an + empty string, it is assumed the character set defined by the + <variablelink>default_charset</variablelink> variable.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function SetEncodedEmailHeader($header, $address, $name, $encoding_charset = '') + { + return($this->SetHeader($header,$this->QuotedPrintableEncode($name, strlen($encoding_charset) ? $encoding_charset : $this->default_charset, 1, 1).' <'.$address.'>')); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>SetMultipleEncodedEmailHeader</name> + <type>STRING</type> + <documentation> + <purpose>Set the value of an header that is meant to represent a list + of e-mail addresses of names of people or entities. This is meant + mostly to set the <tt>To</tt>, <tt>Cc</tt> and <tt>Bcc</tt> + headers.</purpose> + <usage>Use this function specifying the <argumentlink> + <function>SetMultipleEncodedEmailHeader</function> + <argument>header</argument> + </argumentlink> and all the <argumentlink> + <function>SetMultipleEncodedEmailHeader</function> + <argument>addresses</argument> + </argumentlink> in an associative array that should have + the email addresses as entry indexes and the name of the respective + people or entities as entry values.</usage> + <returnvalue>An error message in case there was an error or an empty + string otherwise. This return value may be safely ignored if the + function parameters are set correctly.</returnvalue> + <example><pre>$message_object->SetMultipleEncodedEmailHeader('Bcc', array( + 'peter@gabriel.org' => 'Peter Gabriel', + 'paul@simon.net' => 'Paul Simon', + 'mary@chain.com' => 'Mary Chain' +));</pre></example> + </documentation> + <argument> + <name>header</name> + <type>STRING</type> + <documentation> + <purpose>Name of the header.</purpose> + </documentation> + </argument> + <argument> + <name>addresses</name> + <type>HASH</type> + <documentation> + <purpose>List of all email addresses and associated person or + entity names.</purpose> + </documentation> + </argument> + <argument> + <name>encoding_charset</name> + <type>STRING</type> + <defaultvalue></defaultvalue> + <documentation> + <purpose>Character set used in the header value. If it is set to an + empty string, it is assumed the character set defined by the + <variablelink>default_charset</variablelink> variable.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function SetMultipleEncodedEmailHeader($header,$addresses, $encoding_charset = '') + { + Reset($addresses); + $end=(GetType($address=Key($addresses))!="string"); + for($value="";!$end;) + { + if(strlen($value)) + $value.=", "; + $value.=$this->QuotedPrintableEncode($addresses[$address], strlen($encoding_charset) ? $encoding_charset : $this->default_charset, 1, 1).' <'.$address.'>'; + Next($addresses); + $end=(GetType($address=Key($addresses))!="string"); + } + return($this->SetHeader($header,$value)); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>ResetMessage</name> + <type>VOID</type> + <documentation> + <purpose>Restore the content of the message to the initial state when + the class object is created, i.e. without any headers or body + parts.</purpose> + <usage>Use this function if you want to start composing a completely + new message.</usage> + </documentation> + <do> +{/metadocument} +*/ + Function ResetMessage() + { + $this->headers=array(); + $this->body=-1; + $this->body_parts=0; + $this->parts=array(); + $this->total_parts=0; + $this->free_parts=array(); + $this->total_free_parts=0; + $this->delivery=array("State"=>""); + $this->error=""; + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + + Function CreatePart(&$definition,&$part) + { + $part=-1; + if(strlen($this->error)) + return($this->error); + if($this->total_free_parts) + { + $this->total_free_parts--; + $part=$this->free_parts[$this->total_free_parts]; + Unset($this->free_parts[$this->total_free_parts]); + } + else + { + $part=$this->total_parts; + ++$this->total_parts; + } + $this->parts[$part]=$definition; + return(""); + } + +/* +{metadocument} + <function> + <name>AddPart</name> + <type>STRING</type> + <documentation> + <purpose>Add a previously created part to the message.</purpose> + <usage>Use any of the functions to create standalone message parts + and then use this function to add them to the message.</usage> + <returnvalue>An error message in case there was an error or an empty + string otherwise. This return value may be safely ignored if the + function parameters are set correctly.</returnvalue> + </documentation> + <argument> + <name>part</name> + <type>INTEGER</type> + <documentation> + <purpose>Number of the part as returned by the function that + originally created it.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function AddPart($part) + { + if(strlen($this->error)) + return($this->error); + switch($this->body_parts) + { + case 0; + $this->body=$part; + break; + case 1: + $parts=array( + $this->body, + $part + ); + if(strlen($error=$this->CreateMixedMultipart($parts,$body))) + return($error); + $this->body=$body; + break; + default: + $this->parts[$this->body]["PARTS"][]=$part; + break; + } + ++$this->body_parts; + return(""); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>ReplacePart</name> + <type>STRING</type> + <documentation> + <purpose>Replace a message part already added to the message with a + newly created part. The replaced part gets the definition of the + replacing part. The replacing part is discarded and its part number + becomes free for creation of a new part.</purpose> + <usage>Use one of the functions to create message parts and then pass + the returned part numbers to this function.</usage> + <returnvalue>An error message in case there was an error or an empty + string otherwise. This return value may be safely ignored if the + function parameters are set correctly.</returnvalue> + </documentation> + <argument> + <name>old_part</name> + <type>INTEGER</type> + <documentation> + <purpose>Number of the previously added part.</purpose> + </documentation> + </argument> + <argument> + <name>new_part</name> + <type>INTEGER</type> + <documentation> + <purpose>Number of the replacing part.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function ReplacePart($old_part,$new_part) + { + if(strlen($this->error)) + return($this->error); + if(!IsSet($this->parts[$old_part])) + return($this->error="it was attempted to replace an invalid message part"); + if(IsSet($this->parts[$old_part]["FREE"])) + return($this->error="it was attempted to replace a message part that is no longer valid"); + if(!IsSet($this->parts[$new_part])) + return($this->error="it was attempted to use an invalid message replacecement part"); + if(IsSet($this->parts[$new_part]["FREE"])) + return($this->error="it was attempted to use a message replacecement part that is no longer valid"); + $this->parts[$old_part]=$this->parts[$new_part]; + $this->parts[$new_part]=array("FREE"=>1); + $this->free_parts[$this->total_free_parts]=$new_part; + ++$this->total_free_parts; + return(""); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + + Function CreateAndAddPart(&$definition) + { + if(strlen($error=$this->CreatePart($definition,$part)) + || strlen($error=$this->AddPart($part))) + return($error); + return(""); + } + +/* +{metadocument} + <function> + <name>CreatePlainTextPart</name> + <type>STRING</type> + <documentation> + <purpose>Create a plain text message part.</purpose> + <usage>Pass an ASCII (7 bits) <argumentlink> + <function>CreatePlainTextPart</function> + <argument>text</argument> + </argumentlink> string and get the created part number in the + <argumentlink> + <function>CreatePlainTextPart</function> + <argument>part</argument> + </argumentlink> that is returned by reference.</usage> + <returnvalue>An error message in case there was an error or an empty + string otherwise. This return value may be safely ignored if the + function parameters are set correctly.</returnvalue> + </documentation> + <argument> + <name>text</name> + <type>STRING</type> + <documentation> + <purpose>Text of the message part to create.</purpose> + </documentation> + </argument> + <argument> + <name>charset</name> + <type>STRING</type> + <documentation> + <purpose>Character set used in the part text. If it is set to an + empty string, it is assumed the character set defined by the + <variablelink>default_charset</variablelink> variable.</purpose> + </documentation> + </argument> + <argument> + <name>part</name> + <type>INTEGER</type> + <out /> + <documentation> + <purpose>Number of the created part that is returned by reference.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function CreatePlainTextPart($text,$charset,&$part) + { + if(!strcmp($charset,"")) + $charset=$this->default_charset; + $definition=array( + "Content-Type"=>"text/plain", + "DATA"=>$text + ); + if(strcmp(strtoupper($charset),"ASCII")) + $definition["CHARSET"]=$charset; + return($this->CreatePart($definition,$part)); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>AddPlainTextPart</name> + <type>STRING</type> + <documentation> + <purpose>Add a plain text part to the message.</purpose> + <usage>Pass an ASCII (7 bits) <argumentlink> + <function>AddPlainTextPart</function> + <argument>text</argument> + </argumentlink> string.</usage> + <returnvalue>An error message in case there was an error or an empty + string otherwise. This return value may be safely ignored if the + function parameters are set correctly.</returnvalue> + </documentation> + <argument> + <name>text</name> + <type>STRING</type> + <documentation> + <purpose>Text of the message part to add.</purpose> + </documentation> + </argument> + <argument> + <name>charset</name> + <type>STRING</type> + <defaultvalue></defaultvalue> + <documentation> + <purpose>Character set used in the part text. If it is set to an + empty string, it is assumed the character set defined by the + <variablelink>default_charset</variablelink> variable.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function AddPlainTextPart($text,$charset="") + { + if(strlen($error=$this->CreatePlainTextPart($text,$charset,$part)) + || strlen($error=$this->AddPart($part))) + return($error); + return(""); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + + Function CreateEncodedQuotedPrintableTextPart($text,$charset,&$part) + { + if(!strcmp($charset,"")) + $charset=$this->default_charset; + $definition=array( + "Content-Type"=>"text/plain", + "Content-Transfer-Encoding"=>"quoted-printable", + "CHARSET"=>$charset, + "DATA"=>$text + ); + return($this->CreatePart($definition,$part)); + } + + Function AddEncodedQuotedPrintableTextPart($text,$charset="") + { + if(strlen($error=$this->CreateEncodedQuotedPrintableTextPart($text,$charset,$part)) + || strlen($error=$this->AddPart($part))) + return($error); + return(""); + } + +/* +{metadocument} + <function> + <name>CreateQuotedPrintableTextPart</name> + <type>STRING</type> + <documentation> + <purpose>Create a text message part that may contain non-ASCII + characters (8 bits or more).</purpose> + <usage>Pass a <argumentlink> + <function>CreateQuotedPrintableTextPart</function> + <argument>text</argument> + </argumentlink> string and get the created part number in the + <argumentlink> + <function>CreateQuotedPrintableTextPart</function> + <argument>part</argument> + </argumentlink> that is returned by reference.</usage> + <returnvalue>An error message in case there was an error or an empty + string otherwise. This return value may be safely ignored if the + function parameters are set correctly.</returnvalue> + </documentation> + <argument> + <name>text</name> + <type>STRING</type> + <documentation> + <purpose>Text of the message part to create.</purpose> + </documentation> + </argument> + <argument> + <name>charset</name> + <type>STRING</type> + <documentation> + <purpose>Character set used in the part text. If it is set to an + empty string, it is assumed the character set defined by the + <variablelink>default_charset</variablelink> variable.</purpose> + </documentation> + </argument> + <argument> + <name>part</name> + <type>INTEGER</type> + <out /> + <documentation> + <purpose>Number of the created part that is returned by reference.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function CreateQuotedPrintableTextPart($text,$charset,&$part) + { + return($this->CreateEncodedQuotedPrintableTextPart($this->QuotedPrintableEncode($text),$charset,$part)); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>AddQuotedPrintableTextPart</name> + <type>STRING</type> + <documentation> + <purpose>Add a text part to the message that may contain non-ASCII + characters (8 bits or more).</purpose> + <usage>Pass a <argumentlink> + <function>AddQuotedPrintableTextPart</function> + <argument>text</argument> + </argumentlink> string.</usage> + <returnvalue>An error message in case there was an error or an empty + string otherwise. This return value may be safely ignored if the + function parameters are set correctly.</returnvalue> + </documentation> + <argument> + <name>text</name> + <type>STRING</type> + <documentation> + <purpose>Text of the message part to create.</purpose> + </documentation> + </argument> + <argument> + <name>charset</name> + <type>STRING</type> + <defaultvalue></defaultvalue> + <documentation> + <purpose>Character set used in the part text. If it is set to an + empty string, it is assumed the character set defined by the + <variablelink>default_charset</variablelink> variable.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function AddQuotedPrintableTextPart($text,$charset="") + { + return($this->AddEncodedQuotedPrintableTextPart($this->QuotedPrintableEncode($text),$charset)); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>CreateHTMLPart</name> + <type>STRING</type> + <documentation> + <purpose>Create an HTML message part only with ASCII characters (7 bit).</purpose> + <usage>Pass an ASCII (7 bits) <argumentlink> + <function>CreateHTMLPart</function> + <argument>html</argument> + </argumentlink> text string and get the created part number in the + <argumentlink> + <function>CreateHTMLPart</function> + <argument>part</argument> + </argumentlink> that is returned by reference.</usage> + <returnvalue>An error message in case there was an error or an empty + string otherwise. This return value may be safely ignored if the + function parameters are set correctly.</returnvalue> + </documentation> + <argument> + <name>html</name> + <type>STRING</type> + <documentation> + <purpose>HTML of the message part to create.</purpose> + </documentation> + </argument> + <argument> + <name>charset</name> + <type>STRING</type> + <documentation> + <purpose>Character set used in the part text. If it is set to an + empty string, it is assumed the character set defined by the + <variablelink>default_charset</variablelink> variable.</purpose> + </documentation> + </argument> + <argument> + <name>part</name> + <type>INTEGER</type> + <out /> + <documentation> + <purpose>Number of the created part that is returned by reference.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function CreateHTMLPart($html,$charset,&$part) + { + if(!strcmp($charset,"")) + $charset=$this->default_charset; + $definition=array( + "Content-Type"=>"text/html", + "CHARSET"=>$charset, + "DATA"=>$html + ); + return($this->CreatePart($definition,$part)); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>AddHTMLPart</name> + <type>STRING</type> + <documentation> + <purpose>Add an HTML part to the message only with ASCII characters.</purpose> + <usage>Pass an <argumentlink> + <function>AddHTMLPart</function> + <argument>html</argument> + </argumentlink> text string.</usage> + <returnvalue>An error message in case there was an error or an empty + string otherwise. This return value may be safely ignored if the + function parameters are set correctly.</returnvalue> + </documentation> + <argument> + <name>html</name> + <type>STRING</type> + <documentation> + <purpose>HTML of the message part to create.</purpose> + </documentation> + </argument> + <argument> + <name>charset</name> + <type>STRING</type> + <defaultvalue></defaultvalue> + <documentation> + <purpose>Character set used in the part text. If it is set to an + empty string, it is assumed the character set defined by the + <variablelink>default_charset</variablelink> variable.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function AddHTMLPart($html,$charset="") + { + if(strlen($error=$this->CreateHTMLPart($html,$charset,$part)) + || strlen($error=$this->AddPart($part))) + return($error); + return(""); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + + Function CreateEncodedQuotedPrintableHTMLPart($html,$charset,&$part) + { + if(!strcmp($charset,"")) + $charset=$this->default_charset; + $definition=array( + "Content-Type"=>"text/html", + "Content-Transfer-Encoding"=>"quoted-printable", + "CHARSET"=>$charset, + "DATA"=>$html + ); + return($this->CreatePart($definition,$part)); + } + + Function AddEncodedQuotedPrintableHTMLPart($html,$charset="") + { + if(strlen($error=$this->CreateEncodedQuotedPrintableHTMLPart($html,$charset,$part)) + || strlen($error=$this->AddPart($part))) + return($error); + return(""); + } + +/* +{metadocument} + <function> + <name>CreateQuotedPrintableHTMLPart</name> + <type>STRING</type> + <documentation> + <purpose>Create an HTML message part that may contain non-ASCII + characters (8 bits or more).</purpose> + <usage>Pass a <argumentlink> + <function>CreateQuotedPrintableHTMLPart</function> + <argument>html</argument> + </argumentlink> text string and get the created part number in the + <argumentlink> + <function>CreateQuotedPrintableHTMLPart</function> + <argument>part</argument> + </argumentlink> that is returned by reference.</usage> + <returnvalue>An error message in case there was an error or an empty + string otherwise. This return value may be safely ignored if the + function parameters are set correctly.</returnvalue> + </documentation> + <argument> + <name>html</name> + <type>STRING</type> + <documentation> + <purpose>HTML of the message part to create.</purpose> + </documentation> + </argument> + <argument> + <name>charset</name> + <type>STRING</type> + <documentation> + <purpose>Character set used in the part text. If it is set to an + empty string, it is assumed the character set defined by the + <variablelink>default_charset</variablelink> variable.</purpose> + </documentation> + </argument> + <argument> + <name>part</name> + <type>INTEGER</type> + <out /> + <documentation> + <purpose>Number of the created part that is returned by reference.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function CreateQuotedPrintableHTMLPart($html,$charset,&$part) + { + return($this->CreateEncodedQuotedPrintableHTMLPart($this->QuotedPrintableEncode($html),$charset,$part)); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + + +/* +{metadocument} + <function> + <name>AddQuotedPrintableHTMLPart</name> + <type>STRING</type> + <documentation> + <purpose>Add an HTML part to the message that may contain non-ASCII + characters (8 bits or more).</purpose> + <usage>Pass a <argumentlink> + <function>AddQuotedPrintableHTMLPart</function> + <argument>html</argument> + </argumentlink> text string.</usage> + <returnvalue>An error message in case there was an error or an empty + string otherwise. This return value may be safely ignored if the + function parameters are set correctly.</returnvalue> + </documentation> + <argument> + <name>html</name> + <type>STRING</type> + <documentation> + <purpose>HTML of the message part to create.</purpose> + </documentation> + </argument> + <argument> + <name>charset</name> + <type>STRING</type> + <defaultvalue></defaultvalue> + <documentation> + <purpose>Character set used in the part text. If it is set to an + empty string, it is assumed the character set defined by the + <variablelink>default_charset</variablelink> variable.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function AddQuotedPrintableHTMLPart($html,$charset="") + { + return($this->AddEncodedQuotedPrintableHTMLPart($this->QuotedPrintableEncode($html),$charset)); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + + Function GetFileDefinition($file, &$definition, $require_name=1) + { + if(strlen($this->error)) + return($this->error); + $name=""; + if(IsSet($file["FileName"])) + $name=basename($file["FileName"]); + else + { + if(!IsSet($file["Data"])) + return($this->OutputError("it was not specified the file part file name")); + } + if(IsSet($file["Name"])) + $name=$file["Name"]; + if($require_name + && strlen($name)==0) + return($this->OutputError("it was not specified the file part name")); + $encoding="base64"; + if(IsSet($file["Content-Type"])) + { + $content_type=$file["Content-Type"]; + $type=$this->Tokenize(strtolower($content_type),"/"); + $sub_type=$this->Tokenize(""); + switch($type) + { + case "text": + case "image": + case "audio": + case "video": + case "application": + break; + case "message": + $encoding="7bit"; + break; + case "automatic": + switch($sub_type) + { + case "name": + if(strlen($name)==0) + return($this->OutputError("it is not possible to determine content type from the name")); + switch(strtolower($this->GetFilenameExtension($name))) + { + case ".xls": + $content_type="application/excel"; + break; + case ".hqx": + $content_type="application/macbinhex40"; + break; + case ".doc": + case ".dot": + case ".wrd": + $content_type="application/msword"; + break; + case ".pdf": + $content_type="application/pdf"; + break; + case ".pgp": + $content_type="application/pgp"; + break; + case ".ps": + case ".eps": + case ".ai": + $content_type="application/postscript"; + break; + case ".ppt": + $content_type="application/powerpoint"; + break; + case ".rtf": + $content_type="application/rtf"; + break; + case ".tgz": + case ".gtar": + $content_type="application/x-gtar"; + break; + case ".gz": + $content_type="application/x-gzip"; + break; + case ".php": + case ".php3": + $content_type="application/x-httpd-php"; + break; + case ".js": + $content_type="application/x-javascript"; + break; + case ".ppd": + case ".psd": + $content_type="application/x-photoshop"; + break; + case ".swf": + case ".swc": + case ".rf": + $content_type="application/x-shockwave-flash"; + break; + case ".tar": + $content_type="application/x-tar"; + break; + case ".zip": + $content_type="application/zip"; + break; + case ".mid": + case ".midi": + case ".kar": + $content_type="audio/midi"; + break; + case ".mp2": + case ".mp3": + case ".mpga": + $content_type="audio/mpeg"; + break; + case ".ra": + $content_type="audio/x-realaudio"; + break; + case ".wav": + $content_type="audio/wav"; + break; + case ".bmp": + $content_type="image/bitmap"; + break; + case ".gif": + $content_type="image/gif"; + break; + case ".iff": + $content_type="image/iff"; + break; + case ".jb2": + $content_type="image/jb2"; + break; + case ".jpg": + case ".jpe": + case ".jpeg": + $content_type="image/jpeg"; + break; + case ".jpx": + $content_type="image/jpx"; + break; + case ".png": + $content_type="image/png"; + break; + case ".tif": + case ".tiff": + $content_type="image/tiff"; + break; + case ".wbmp": + $content_type="image/vnd.wap.wbmp"; + break; + case ".xbm": + $content_type="image/xbm"; + break; + case ".css": + $content_type="text/css"; + break; + case ".txt": + $content_type="text/plain"; + break; + case ".htm": + case ".html": + $content_type="text/html"; + break; + case ".xml": + $content_type="text/xml"; + break; + case ".mpg": + case ".mpe": + case ".mpeg": + $content_type="video/mpeg"; + break; + case ".qt": + case ".mov": + $content_type="video/quicktime"; + break; + case ".avi": + $content_type="video/x-ms-video"; + break; + case ".eml": + $content_type="message/rfc822"; + $encoding="7bit"; + break; + default: + $content_type="application/octet-stream"; + break; + } + break; + default: + return($this->OutputError($content_type." is not a supported automatic content type detection method")); + } + break; + default: + return($this->OutputError($content_type." is not a supported file content type")); + } + } + else + $content_type="application/octet-stream"; + $definition=array( + "Content-Type"=>$content_type, + "Content-Transfer-Encoding"=>$encoding, + "NAME"=>$name + ); + if(IsSet($file["Disposition"])) + { + switch(strtolower($file["Disposition"])) + { + case "inline": + case "attachment": + break; + default: + return($this->OutputError($file["Disposition"]." is not a supported message part content disposition")); + } + $definition["DISPOSITION"]=$file["Disposition"]; + } + if(IsSet($file["FileName"])) + $definition["FILENAME"]=$file["FileName"]; + else + { + if(IsSet($file["Data"])) + $definition["DATA"]=$file["Data"]; + } + if(IsSet($file['Cache']) + && $file['Cache']) + $definition['Cache'] = 1; + return(""); + } + +/* +{metadocument} + <function> + <name>CreateFilePart</name> + <type>STRING</type> + <documentation> + <purpose>Create a message part to be handled as a file.</purpose> + <usage>Pass a <argumentlink> + <function>CreateFilePart</function> + <argument>file</argument> + </argumentlink> definition associative array and get the created + part number in the <argumentlink> + <function>CreateFilePart</function> + <argument>part</argument> + </argumentlink> that is returned by reference.</usage> + <returnvalue>An error message in case there was an error or an empty + string otherwise. This return value may be safely ignored if the + function parameters are set correctly.</returnvalue> + </documentation> + <argument> + <name>file</name> + <type>HASH</type> + <documentation> + <purpose>Associative array to specify parameters that describe the + file part. Here follows the list of supported parameters that + should be used as indexes of the array:<paragraphbreak /> + <tt>FileName</tt><paragraphbreak /> + Name of the file from which the part data will be read when the + message is generated. It may be a remote URL as long as your PHP + installation is configured to allow accessing remote files with + the <tt>fopen()</tt> function.<paragraphbreak /> + <tt>Data</tt><paragraphbreak /> + String that specifies the data of the file. This should be used + as alternative data source to <tt>FileName</tt> for passing data + available in memory, like for instance files stored in a database + that was queried dynamically and the file contents was fetched + into a string variable.<paragraphbreak /> + <tt>Name</tt><paragraphbreak /> + Name of the file that will appear in the message. If this + parameter is missing the base name of the <tt>FileName</tt> + parameter is used, if present.<paragraphbreak /> + <tt>Content-Type</tt><paragraphbreak /> + Content type of the part: <tt>text/plain</tt> for text, + <tt>text/html</tt> for HTML, <tt>image/gif</tt> for GIF images, + etc..<paragraphbreak /> + There is one special type named <tt>automatic/name</tt> that may + be used to tell the class to try to guess the content type from + the file name. Many file types are recognized from the file name + extension. If the file name extension is not recognized, the + default for binary data <tt>application/octet-stream</tt> is + assumed.<paragraphbreak /> + <tt>Disposition</tt><paragraphbreak /> + Information to whether this file part is meant to be used as a + file <tt>attachment</tt> or as a part meant to be displayed + <tt>inline</tt>, eventually integrated with another related + part.<paragraphbreak /> + <tt>Cache</tt><paragraphbreak /> + Boolean flag that indicates that this message part should be + cached when generating the message body. Use only when sending + many messages to multiple recipients, but this part does not + change between each of the messages that are sent.<paragraphbreak /> + Note that it is also not worth using this option when setting the + <variablelink>cache_body</variablelink>, as that variable makes + the class cache the whole message body and the internal message + parts will not be rebuilt.</purpose> + </documentation> + </argument> + <argument> + <name>part</name> + <type>INTEGER</type> + <out /> + <documentation> + <purpose>Number of the created part that is returned by reference.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function CreateFilePart(&$file,&$part) + { + if(strlen($this->GetFileDefinition($file,$definition))) + return($this->error); + return($this->CreatePart($definition,$part)); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>AddFilePart</name> + <type>STRING</type> + <documentation> + <purpose>Add a message part to be handled as a file.</purpose> + <usage>Pass a <argumentlink> + <function>AddFilePart</function> + <argument>file</argument> + </argumentlink> definition associative array.</usage> + <returnvalue>An error message in case there was an error or an empty + string otherwise. This return value may be safely ignored if the + function parameters are set correctly.</returnvalue> + </documentation> + <argument> + <name>file</name> + <type>HASH</type> + <documentation> + <purpose>Associative array to specify parameters that describe the + file part. See the <argumentlink> + <function>CreateFilePart</function> + <argument>file</argument> + </argumentlink> argument description of the + <functionlink>CreateFilePart</functionlink> function for an + explanation about the supported file parameters.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function AddFilePart(&$file) + { + if(strlen($error=$this->CreateFilePart($file,$part)) + || strlen($error=$this->AddPart($part))) + return($error); + return(""); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>CreateMessagePart</name> + <type>STRING</type> + <documentation> + <purpose>Create a message part to encapsulate another message. This + is usually meant to create an attachment that contains a message + that was received and is being forwarded intact with the original + the headers and body data.</purpose> + <usage>This function should be used like the + <functionlink>CreateFilePart</functionlink> function, passing the + same parameters to the <argumentlink> + <function>CreateMessagePart</function> + <argument>message</argument> + </argumentlink> argument.<paragraphbreak /> + The message to be encapsulated can be specified either as an + existing file with the <tt>FileName</tt> parameter, or as string + of data in memory with the <tt>Data</tt> + parameter.<paragraphbreak /> + The <tt>Content-Type</tt> and <tt>Disposition</tt> file parameters + do not need to be specified because they are overridden by this + function.</usage> + <returnvalue>An error message in case there was an error or an empty + string otherwise. This return value may be safely ignored if the + function parameters are set correctly.</returnvalue> + </documentation> + <argument> + <name>message</name> + <type>HASH</type> + <documentation> + <purpose>Associative array that specifies definition parameters of + the message file part.</purpose> + </documentation> + </argument> + <argument> + <name>part</name> + <type>INTEGER</type> + <out /> + <documentation> + <purpose>Number of the created part that is returned by reference.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function CreateMessagePart(&$message,&$part) + { + $message["Content-Type"]="message/rfc822"; + $message["Disposition"]="inline"; + if(strlen($this->GetFileDefinition($message,$definition))) + return($this->error); + return($this->CreatePart($definition,$part)); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>AddMessagePart</name> + <type>STRING</type> + <documentation> + <purpose>Add a message part that encapsulates another message. This + is usually meant to add an attachment that contains a message that + was received and is being forwarded intact with the original the + headers and body data.</purpose> + <usage>This function should be used like the + <functionlink>AddFilePart</functionlink> function, passing the + same parameters to the <argumentlink> + <function>AddMessagePart</function> + <argument>message</argument> + </argumentlink> argument. See the + <functionlink>CreateFilePart</functionlink> function for more + details.</usage> + <returnvalue>An error message in case there was an error or an empty + string otherwise. This return value may be safely ignored if the + function parameters are set correctly.</returnvalue> + </documentation> + <argument> + <name>message</name> + <type>HASH</type> + <documentation> + <purpose>Associative array that specifies definition parameters of + the message file part.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function AddMessagePart(&$message) + { + if(strlen($error=$this->CreateMessagePart($message,$part)) + || strlen($error=$this->AddPart($part))) + return($error); + return(""); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + + Function CreateMultipart(&$parts,&$part,$type) + { + $definition=array( + "Content-Type"=>"multipart/".$type, + "PARTS"=>$parts + ); + return($this->CreatePart($definition,$part)); + } + + Function AddMultipart(&$parts,$type) + { + if(strlen($error=$this->CreateMultipart($parts,$part,$type)) + || strlen($error=$this->AddPart($part))) + return($error); + return(""); + } + +/* +{metadocument} + <function> + <name>CreateAlternativeMultipart</name> + <type>STRING</type> + <documentation> + <purpose>Create a message part composed of multiple parts that can be + displayed by the recipient e-mail program in alternative + formats.<paragraphbreak /> + This is usually meant to create HTML messages with an alternative + text part to be displayed by programs that cannot display HTML + messages.</purpose> + <usage>Create all the alternative message parts that are going to be + sent and pass their numbers to the <argumentlink> + <function>CreateAlternativeMultipart</function> + <argument>parts</argument> + </argumentlink> array argument.<paragraphbreak /> + The least sophisticated part, usually the text part, should appear + first in the parts array because the e-mail programs that support + displaying more sophisticated message parts will pick the last part + in the message that is supported.</usage> + <returnvalue>An error message in case there was an error or an empty + string otherwise. This return value may be safely ignored if the + function parameters are set correctly.</returnvalue> + </documentation> + <argument> + <name>parts</name> + <type>ARRAY</type> + <documentation> + <purpose>Array with the numbers with all the alternative parts.</purpose> + </documentation> + </argument> + <argument> + <name>part</name> + <type>INTEGER</type> + <out /> + <documentation> + <purpose>Number of the created part that is returned by reference.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function CreateAlternativeMultipart(&$parts,&$part) + { + return($this->CreateMultiPart($parts,$part,"alternative")); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>AddAlternativeMultipart</name> + <type>STRING</type> + <documentation> + <purpose>Add a message part composed of multiple parts that can be + displayed by the recipient e-mail program in alternative + formats.<paragraphbreak /> + This is usually meant to create HTML messages with an alternative + text part to be displayed by programs that cannot display HTML + messages.</purpose> + <usage>Create all the alternative message parts that are going to be + sent and pass their numbers to the <argumentlink> + <function>AddAlternativeMultipart</function> + <argument>parts</argument> + </argumentlink> array argument.<paragraphbreak /> + The least sophisticated part, usually the text part, should appear + first in the parts array because the e-mail programs that support + displaying more sophisticated message parts will pick the last part + in the message that is supported.</usage> + <returnvalue>An error message in case there was an error or an empty + string otherwise. This return value may be safely ignored if the + function parameters are set correctly.</returnvalue> + </documentation> + <argument> + <name>parts</name> + <type>ARRAY</type> + <documentation> + <purpose>Array with the numbers with all the alternative parts.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function AddAlternativeMultipart(&$parts) + { + return($this->AddMultipart($parts,"alternative")); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>CreateRelatedMultipart</name> + <type>STRING</type> + <documentation> + <purpose>Create a message part that groups several related + parts.<paragraphbreak /> + This is usually meant to group an HTML message part with images or + other types of files that should be embedded in the same message + and be displayed as a single part by the recipient e-mail + program.</purpose> + <usage>Create all the related message parts that are going to be + sent and pass their numbers to the <argumentlink> + <function>CreateRelatedMultipart</function> + <argument>parts</argument> + </argumentlink> array argument.<paragraphbreak /> + When using this function to group an HTML message with embedded + images or other related files, make sure that the HTML part number + is the first listed in the <argumentlink> + <function>CreateRelatedMultipart</function> + <argument>parts</argument> + </argumentlink> array argument, or else the message may not appear + correctly.</usage> + <returnvalue>An error message in case there was an error or an empty + string otherwise. This return value may be safely ignored if the + function parameters are set correctly.</returnvalue> + </documentation> + <argument> + <name>parts</name> + <type>ARRAY</type> + <documentation> + <purpose>Array with the numbers with all the related parts.</purpose> + </documentation> + </argument> + <argument> + <name>part</name> + <type>INTEGER</type> + <out /> + <documentation> + <purpose>Number of the created part that is returned by reference.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function CreateRelatedMultipart(&$parts,&$part) + { + return($this->CreateMultipart($parts,$part,"related")); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>AddRelatedMultipart</name> + <type>STRING</type> + <documentation> + <purpose>Add a message part that groups several related + parts.<paragraphbreak /> + This is usually meant to group an HTML message part with images or + other types of files that should be embedded in the same message + and be displayed as a single part by the recipient e-mail + program.</purpose> + <usage>Create all the related message parts that are going to be + sent and pass their numbers to the <argumentlink> + <function>AddRelatedMultipart</function> + <argument>parts</argument> + </argumentlink> array argument.<paragraphbreak /> + When using this function to group an HTML message with embedded + images or other related files, make sure that the HTML part number + is the first listed in the <argumentlink> + <function>AddRelatedMultipart</function> + <argument>parts</argument> + </argumentlink> array argument, or else the message may not appear + correctly.</usage> + <returnvalue>An error message in case there was an error or an empty + string otherwise. This return value may be safely ignored if the + function parameters are set correctly.</returnvalue> + </documentation> + <argument> + <name>parts</name> + <type>ARRAY</type> + <documentation> + <purpose>Array with the numbers with all the related parts.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function AddRelatedMultipart(&$parts) + { + return($this->AddMultipart($parts,"related")); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>CreateMixedMultipart</name> + <type>STRING</type> + <documentation> + <purpose>Create a message part that groups several independent + parts.<paragraphbreak /> + Usually this is meant compose messages with one or more file + attachments. However, it is not necessary to use this function as + the class implicitly creates a <tt>multipart/mixed</tt> message + when more than one part is added to the message.</purpose> + <usage>Create all the independent message parts that are going to be + sent and pass their numbers to the <argumentlink> + <function>CreateMixedMultipart</function> + <argument>parts</argument> + </argumentlink> array argument.</usage> + <returnvalue>An error message in case there was an error or an empty + string otherwise. This return value may be safely ignored if the + function parameters are set correctly.</returnvalue> + </documentation> + <argument> + <name>parts</name> + <type>ARRAY</type> + <documentation> + <purpose>Array with the numbers with all the related parts.</purpose> + </documentation> + </argument> + <argument> + <name>part</name> + <type>INTEGER</type> + <out /> + <documentation> + <purpose>Number of the created part that is returned by reference.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function CreateMixedMultipart(&$parts,&$part) + { + return($this->CreateMultipart($parts,$part,"mixed")); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>AddMixedMultipart</name> + <type>STRING</type> + <documentation> + <purpose>Add a message part that groups several independent + parts.<paragraphbreak /> + Usually this is meant compose messages with one or more file + attachments. However, it is not necessary to use this function as + the class implicitly creates a <tt>multipart/mixed</tt> message + when more than one part is added to the message.</purpose> + <usage>Create all the independent message parts that are going to be + sent and pass their numbers to the <argumentlink> + <function>AddMixedMultipart</function> + <argument>parts</argument> + </argumentlink> array argument.</usage> + <returnvalue>An error message in case there was an error or an empty + string otherwise. This return value may be safely ignored if the + function parameters are set correctly.</returnvalue> + </documentation> + <argument> + <name>parts</name> + <type>ARRAY</type> + <documentation> + <purpose>Array with the numbers with all the related parts.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function AddMixedMultipart(&$parts) + { + return($this->AddMultipart($parts,"mixed")); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + + Function CreateParallelMultipart(&$parts,&$part) + { + return($this->CreateMultipart($parts,$part,"paralell")); + } + + Function AddParalellMultipart(&$parts) + { + return($this->AddMultipart($parts,"paralell")); + } + +/* +{metadocument} + <function> + <name>GetPartContentID</name> + <type>STRING</type> + <documentation> + <purpose>Retrieve the content identifier associated to a given + message part.</purpose> + <usage>Create a message part and pass its number to the <argumentlink> + <function>GetPartContentID</function> + <argument>part</argument> + </argumentlink> argument.<paragraphbreak /> + This function is usually meant to create an URL that can be used + in an HTML message part to reference related parts like images, CSS + (Cascaded Style Sheets), or any other type of files related to the + HTML part that are embedded in the same message as part of a + <tt>multipart/related</tt> composite part.<paragraphbreak /> + To use the part content identifier returned by this function you + need to prepend the string <tt><stringvalue>cid:</stringvalue></tt> + to form a special URL that can be used in the HTML document this + part file.<paragraphbreak /> + You may read more about using this function in the class usage + section about <link> + <data>embedding images in HTML messages</data> + <name>embed-image</name> + </link>.</usage> + <returnvalue>The content identifier text string.<paragraphbreak /> + If it is specified an invalid message part, this function returns + an empty string.</returnvalue> + </documentation> + <argument> + <name>part</name> + <type>INTEGER</type> + <documentation> + <purpose>Number of the part as returned by the function that + originally created it.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function GetPartContentID($part) + { + if(!IsSet($this->parts[$part])) + return(""); + if(!IsSet($this->parts[$part]["Content-ID"])) + { + $extension=(IsSet($this->parts[$part]["NAME"]) ? $this->GetFilenameExtension($this->parts[$part]["NAME"]) : ""); + $this->parts[$part]["Content-ID"]=md5(uniqid($part.time())).$extension; + } + return($this->parts[$part]["Content-ID"]); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>GetDataURL</name> + <type>STRING</type> + <documentation> + <purpose>Generate a <tt>data:</tt> URL according to the <link> + <data>RFC 2397</data> + <url>http://www.ietf.org/rfc/rfc2397.txt</url> + </link> suitable for using in HTML messages to represent an image + or other type of file on which the data is directly embedded in the + HTML code instead of being fetched from a separate file or remote + URL.<paragraphbreak /> + Note that not all e-mail programs are capable of displaying images + or other types of files embedded in HTML messages this way.</purpose> + <usage>Pass a <argumentlink> + <function>GetDataURL</function> + <argument>file</argument> + </argumentlink> part definition array like for the + <functionlink>CreateFilePart</functionlink> function.</usage> + <returnvalue>The <tt>data:</tt> representing the described file or an + empty string in case there was an error.</returnvalue> + </documentation> + <argument> + <name>file</name> + <type>HASH</type> + <documentation> + <purpose>File definition.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function GetDataURL($file) + { + $ini_magic_quotes_runtime_chk = false; + if (version_compare(PHP_VERSION, '5.3.0', '<')) { + $ini_magic_quotes_runtime_chk = ini_get('magic_quotes_runtime'); + } + if(strlen($this->GetFileDefinition($file,$definition,0))) + return($this->error); + if(IsSet($definition["FILENAME"])) + { + $size=@filesize($definition["FILENAME"]); + if(!($file=@fopen($definition["FILENAME"],"rb"))) + return($this->OutputPHPError("could not open data file ".$definition["FILENAME"], error_get_last()['message'])); + for($body="";!feof($file);) + { + if(GetType($block=@fread($file,$this->file_buffer_length))!="string") + { + $this->OutputPHPError("could not read data file", error_get_last()['message']); + fclose($file); + return(""); + } + $body.=$block; + } + fclose($file); + if(GetType($size)=="integer" + && strlen($body)!=$size) + { + $this->OutputError("the length of the file that was read does not match the size of the part file ".$definition["FILENAME"]." due to possible data corruption"); + return(""); + } + if(function_exists("ini_get") + && $ini_magic_quotes_runtime_chk) + $body=StripSlashes($body); + $body=chunk_split(base64_encode($body), $this->line_length, $this->line_break); + } + else + { + if(!IsSet($definition["DATA"])) + { + $this->OutputError("it was not specified a file or data block"); + return(""); + } + $body=chunk_split(base64_encode($definition["DATA"]), $this->line_length, $this->line_break); + } + return("data:".$definition["Content-Type"].";base64,".$body); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + + Function GetHeadersAndBody(&$headers, &$body) + { + $headers=$this->headers; + if(strcmp($this->mailer,"")) + { + $headers["X-Mailer"]=$this->mailer; + if(strlen($this->mailer_delivery)) + $headers["X-Mailer"].=' ('.$this->mailer_delivery.')'; + } + $headers["MIME-Version"]="1.0"; + if($this->body_parts==0) + return($this->OutputError("message has no body parts")); + if(strlen($error=$this->GetPartHeaders($headers,$this->body))) + return($error); + if($this->cache_body + && IsSet($this->body_cache[$this->body])) + $body=$this->body_cache[$this->body]; + else + { + if(strlen($error=$this->GetPartBody($body,$this->body))) + return($error); + if($this->cache_body) + $this->body_cache[$this->body]=$body; + } + return(""); + } + +/* +{metadocument} + <function> + <name>Send</name> + <type>STRING</type> + <documentation> + <purpose>Send a composed message.</purpose> + <usage>Use this function after you have set the necessary message + headers and added the message body parts.</usage> + <returnvalue>An error message in case there was an error or an empty + string otherwise. This return value may be safely ignored if the + function parameters are set correctly.</returnvalue> + </documentation> + <do> +{/metadocument} +*/ + Function Send() + { + if(strlen($this->error)) + return($this->error); + if(strlen($error=$this->GetHeadersAndBody($headers, $body))) + return($error); + if(strcmp($error=$this->StartSendingMessage(),"")) + return($error); + if(strlen($error=$this->SendMessageHeaders($headers))==0 + && strlen($error=$this->SendMessageBody($body))==0) + $error=$this->EndSendingMessage(); + $this->StopSendingMessage(); + return($error); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>GetMessage</name> + <type>STRING</type> + <documentation> + <purpose>Get the whole message headers and body.</purpose> + <usage>Use this function to retrieve the message headers and body + without sending it.</usage> + <returnvalue>An error message in case there was an error or an empty + string otherwise. This return value may be safely ignored if the + function parameters are set correctly.</returnvalue> + </documentation> + <argument> + <name>message</name> + <type>STRING</type> + <out /> + <documentation> + <purpose>Reference to a string variable to store the text of the + message headers and body.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function GetMessage(&$message) + { + if(strlen($this->error)) + return($this->error); + if(strlen($error=$this->GetHeadersAndBody($headers, $body))) + return($error); + for($message="", $h=0, Reset($headers); $h<count($headers); ++$h, Next($headers)) + { + $name=Key($headers); + $message.=$name.": ".$headers[$name].$this->line_break; + } + $message.=$this->line_break; + $message.=$body; + return(""); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>GetMessageSize</name> + <type>STRING</type> + <documentation> + <purpose>Get the size of the whole message headers and body.</purpose> + <usage>Use this function to retrieve the size in bytes of the + message headers and body without sending it.</usage> + <returnvalue>An error message in case there was an error or an empty + string otherwise. This return value may be safely ignored if the + function parameters are set correctly.</returnvalue> + </documentation> + <argument> + <name>message</name> + <type>STRING</type> + <out /> + <documentation> + <purpose>Reference to an integer variable to store the size of the + message headers and body.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function GetMessageSize(&$size) + { + if(strlen($error=$this->GetMessage($message))) + return($error); + $size=strlen($message); + return(""); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>Mail</name> + <type>BOOLEAN</type> + <documentation> + <purpose>Emulate the PHP <tt>mail()</tt> function by composing and + sending a message given the same arguments.<paragraphbreak /> + This is mostly meant to provide a solution for sending messages + with alternative delivery methods provided by this class + sub-classes. It uses the same arguments as the PHP <tt>mail()</tt> + function. Developers willing to use this alternative do not need to + change much their scripts that already use the <tt>mail()</tt> + function.</purpose> + <usage>Use this function passing the same arguments as to PHP + <tt><link> + <data>mail()</data> + <url>http://www.php.net/manual/en/function.mail.php</url> + </link></tt> function.</usage> + <returnvalue>If this function succeeds, it returns + <tt><booleanvalue>1</booleanvalue></tt>.</returnvalue> + </documentation> + <argument> + <name>to</name> + <type>STRING</type> + <documentation> + <purpose>Recipient e-mail address.</purpose> + </documentation> + </argument> + <argument> + <name>subject</name> + <type>STRING</type> + <documentation> + <purpose>Message subject.</purpose> + </documentation> + </argument> + <argument> + <name>message</name> + <type>STRING</type> + <documentation> + <purpose>Message body.</purpose> + </documentation> + </argument> + <argument> + <name>additional_headers</name> + <type>STRING</type> + <defaultvalue></defaultvalue> + <documentation> + <purpose>Text string headers and the respective values. There + should be one header and value per line with line breaks + separating each line.</purpose> + </documentation> + </argument> + <argument> + <name>additional_parameters</name> + <type>STRING</type> + <defaultvalue></defaultvalue> + <documentation> + <purpose>Text string with additional parameters. In the original + PHP <tt>mail()</tt> function these were actual switches to be + passed in the sendmail program invocation command line. This + function only supports the <tt>-f</tt> switch followed by an + e-mail address meant to specify the message bounce return path + address.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function Mail($to, $subject, $message, $additional_headers="", $additional_parameters="") + { + $this->ResetMessage(); + $this->headers=array("To"=>$to,"Subject"=>$subject); + $content_type=""; + while(strlen($additional_headers)) + { + preg_match("/([^\r\n]+)(\r?\n)?(.*)\$/",$additional_headers,$matches); + $header=$matches[1]; + $additional_headers=$matches[3]; + if(!preg_match("/^([^:]+):[ \t]+(.+)\$/",$header,$matches)) + { + $this->error="invalid header \"$header\""; + return(0); + } + if(strtolower($matches[1])=="content-type") + { + if(strlen($content_type)) + { + $this->error="the content-type header was specified more than once."; + return(0); + } + $content_type=$matches[2]; + } + else + $this->SetHeader($matches[1],$matches[2]); + } + if(strlen($additional_parameters)) + { + if(preg_match("/^[ \t]*-f[ \t]*([^@]+@[^ \t]+)[ \t]*(.*)\$/", $additional_parameters, $matches)) + { + if(!preg_match('/'.str_replace('/', '\\/', $this->email_regular_expression).'/i', $matches[1])) + { + $this->error="it was specified an invalid e-mail address for the additional parameter -f"; + return(0); + } + if(strlen($matches[2])) + { + $this->error="it were specified some additional parameters after -f e-mail address parameter that are not supported"; + return(0); + } + $this->SetHeader("Return-Path",$matches[1]); + } + else + { + $this->error="the additional parameters that were specified are not supported"; + return(0); + } + } + if(strlen($content_type)==0) + $content_type="text/plain"; + $definition=array( + "Content-Type"=>$content_type, + "DATA"=>$message + ); + $this->CreateAndAddPart($definition); + $this->Send(); + return(strlen($this->error)==0); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + + Function ChangeBulkMail($on) + { + return(1); + } + +/* +{metadocument} + <function> + <name>SetBulkMail</name> + <type>BOOLEAN</type> + <documentation> + <purpose>Hint the class to adjust itself in order to send individual + messages to many recipients more efficiently.</purpose> + <usage>Call this function before starting sending messages to many + recipients passing <booleanvalue>1</booleanvalue> to the + <argumentlink> + <function>SetBulkMail</function> + <argument>on</argument> + </argumentlink> argument. Then call this function again after the + bulk mailing delivery has ended passing passing + <booleanvalue>1</booleanvalue> to the <argumentlink> + <function>SetBulkMail</function> + <argument>on</argument> + </argumentlink> argument.</usage> + <returnvalue>If this function succeeds, it returns + <tt><booleanvalue>1</booleanvalue></tt>.</returnvalue> + </documentation> + <argument> + <name>on</name> + <type>BOOLEAN</type> + <documentation> + <purpose>Boolean flag that indicates whether a bulk delivery is + going to start if set to <booleanvalue>1</booleanvalue> or that + the bulk delivery has ended if set to + <booleanvalue>0</booleanvalue>.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function SetBulkMail($on) + { + if(strlen($this->error)) + return(0); + if(!$this->bulk_mail==!$on) + return(1); + if(!$this->ChangeBulkMail($on)) + return(0); + $this->bulk_mail=!!$on; + return(1); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + + Function OpenMailing(&$mailing,&$mailing_properties) + { + if(strlen($this->error)) + return($this->error); + if(!IsSet($mailing_properties["Name"]) + || strlen($mailing_properties["Name"])==0) + return($this->OutputError("it was not specified a valid mailing Name")); + if(!IsSet($mailing_properties["Return-Path"]) + || strlen($mailing_properties["Return-Path"])==0) + return($this->OutputError("it was not specified a valid mailing Return-Path")); + $separator=""; + $directory_separator=(defined("DIRECTORY_SEPARATOR") ? DIRECTORY_SEPARATOR : ((defined("PHP_OS") && !strcmp(substr(PHP_OS,0,3),"WIN")) ? "\\" : "/")); + $length=strlen($this->mailing_path); + if($length) + { + if($this->mailing_path[$length-1]!=$directory_separator) + $separator=$directory_separator; + } + $base_path=$this->mailing_path.$separator.$mailing_properties["Name"]; + if($this->body_parts==0) + return($this->OutputError("message has no body parts")); + $line_break="\n"; + $headers=$this->headers; + if(strlen($this->mailer)) + $headers["X-Mailer"]=$this->mailer; + $headers["MIME-Version"]="1.0"; + if(strlen($error=$this->GetPartHeaders($headers,$this->body))) + return($error); + if(!($header_file=@fopen($base_path.".h","wb"))) + return($this->OutputPHPError("could not open mailing headers file ".$base_path.".h", error_get_last()['message'])); + for($header=0,Reset($headers);$header<count($headers);Next($headers),++$header) + { + $header_name=Key($headers); + if(!@fwrite($header_file,$header_name.": ".$headers[$header_name].$line_break)) + { + fclose($header_file); + return($this->OutputPHPError("could not write to the mailing headers file ".$base_path.".h", error_get_last()['message'])); + } + } + if(!@fflush($header_file)) + { + fclose($header_file); + @unlink($base_path.".h"); + return($this->OutputPHPError("could not write to the mailing headers file ".$base_path.".h", error_get_last()['message'])); + } + fclose($header_file); + if(strlen($error=$this->GetPartBody($body,$this->body))) + { + @unlink($base_path.".h"); + return($error); + } + if(!($body_file=@fopen($base_path.".b","wb"))) + { + @unlink($base_path.".h"); + return($this->OutputPHPError("could not open mailing body file ".$base_path.".b", error_get_last()['message'])); + } + if(!@fwrite($body_file,$body) + || !@fflush($body_file)) + { + fclose($body_file); + @unlink($base_path.".b"); + @unlink($base_path.".h"); + return($this->OutputPHPError("could not write to the mailing body file ".$base_path.".b", error_get_last()['message'])); + } + fclose($body_file); + if(!($envelope=@fopen($base_path.".e","wb"))) + { + @unlink($base_path.".b"); + @unlink($base_path.".h"); + return($this->OutputPHPError("could not open mailing envelope file ".$base_path.".e", error_get_last()['message'])); + } + if(!@fwrite($envelope,"F".$mailing_properties["Return-Path"].chr(0)) + || !@fflush($envelope)) + { + @fclose($envelope); + @unlink($base_path.".e"); + @unlink($base_path.".b"); + @unlink($base_path.".h"); + return($this->OutputPHPError("could not write to the return path to the mailing envelope file ".$base_path.".e", error_get_last()['message'])); + } + $mailing=++$this->last_mailing; + $this->mailings[$mailing]=array( + "Envelope"=>$envelope, + "BasePath"=>$base_path + ); + return(""); + } + + Function AddMailingRecipient($mailing,&$recipient_properties) + { + if(strlen($this->error)) + return($this->error); + if(!IsSet($this->mailings[$mailing])) + return($this->OutputError("it was not specified a valid mailing")); + if(!IsSet($recipient_properties["Address"]) + || strlen($recipient_properties["Address"])==0) + return($this->OutputError("it was not specified a valid mailing recipient Address")); + if(!@fwrite($this->mailings[$mailing]["Envelope"],"T".$recipient_properties["Address"].chr(0))) + return($this->OutputPHPError("could not write recipient address to the mailing envelope file", error_get_last()['message'])); + return(""); + } + + Function EndMailing($mailing) + { + if(strlen($this->error)) + return($this->error); + if(!IsSet($this->mailings[$mailing])) + return($this->OutputError("it was not specified a valid mailing")); + if(!IsSet($this->mailings[$mailing]["Envelope"])) + return($this->OutputError("the mailing was already ended")); + if(!@fwrite($this->mailings[$mailing]["Envelope"],chr(0)) + || !@fflush($this->mailings[$mailing]["Envelope"])) + return($this->OutputPHPError("could not end writing to the mailing envelope file", error_get_last()['message'])); + fclose($this->mailings[$mailing]["Envelope"]); + Unset($this->mailings[$mailing]["Envelope"]); + return(""); + } + + Function SendMailing($mailing) + { + if(strlen($this->error)) + return($this->error); + if(!IsSet($this->mailings[$mailing])) + return($this->OutputError("it was not specified a valid mailing")); + if(IsSet($this->mailings[$mailing]["Envelope"])) + return($this->OutputError("the mailing was not yet ended")); + $this->ResetMessage(); + $base_path=$this->mailings[$mailing]["BasePath"]; + if(GetType($header_lines=@File($base_path.".h"))!="array") + return($this->OutputPHPError("could not read the mailing headers file ".$base_path.".h", error_get_last()['message'])); + for($line=0;$line<count($header_lines);++$line) + { + $header_name=$this->Tokenize($header_lines[$line],": "); + $this->headers[$header_name]=trim($this->Tokenize("\n")); + } + if(!($envelope_file=@fopen($base_path.".e","rb"))) + return($this->OutputPHPError("could not open the mailing envelope file ".$base_path.".e", error_get_last()['message'])); + for($bcc=$data="",$position=0;!feof($envelope_file) || strlen($data);) + { + if(GetType($break=strpos($data,chr(0),$position))!="integer") + { + if(GetType($chunk=@fread($envelope_file,$this->file_buffer_length))!="string") + { + fclose($envelope_file); + return($this->OutputPHPError("could not read the mailing envelop file ".$base_path.".e", error_get_last()['message'])); + } + $data=substr($data,$position).$chunk; + $position=0; + continue; + } + if($break==$position) + break; + switch($data[$position]) + { + case "F": + $this->headers["Return-Path"]=substr($data,$position+1,$break-$position-1); + break; + case "T": + $bcc.=(strlen($bcc)==0 ? "" : ", ").substr($data,$position+1,$break-$position-1); + break; + default: + return($this->OutputError("invalid mailing envelope file ".$base_path.".e")); + } + $position=$break+1; + } + fclose($envelope_file); + if(strlen($bcc)==0) + return($this->OutputError("the mailing envelop file ".$base_path.".e does not contain any recipients")); + $this->headers["Bcc"]=$bcc; + if(!($body_file=@fopen($base_path.".b","rb"))) + return($this->OutputPHPError("could not open the mailing body file ".$base_path.".b", error_get_last()['message'])); + for($data="";!feof($body_file);) + { + if(GetType($chunk=@fread($body_file,$this->file_buffer_length))!="string") + { + fclose($body_file); + return($this->OutputPHPError("could not read the mailing body file ".$base_path.".b", error_get_last()['message'])); + } + $data.=$chunk; + } + fclose($body_file); + if(strlen($error=$this->StartSendingMessage())) + return($error); + if(strlen($error=$this->SendMessageHeaders($this->headers))==0 + && strlen($error=$this->SendMessageBody($data))==0) + $error=$this->EndSendingMessage(); + $this->StopSendingMessage(); + return($error); + } +}; + +/* + +{metadocument} +</class> +{/metadocument} + +*/ + +?> diff --git a/vendor/email_message/php_message.php b/vendor/email_message/php_message.php new file mode 100644 index 0000000..7ffdad8 --- /dev/null +++ b/vendor/email_message/php_message.php @@ -0,0 +1,13 @@ +<?php +/* + * php_message.php + * + * + * + */ + + +class php_message_class extends email_message_class +{ +} +?>
\ No newline at end of file diff --git a/vendor/email_message/pickup_message.php b/vendor/email_message/pickup_message.php new file mode 100644 index 0000000..f690295 --- /dev/null +++ b/vendor/email_message/pickup_message.php @@ -0,0 +1,275 @@ +<?php +/* + * pickup_message.php + * + * @(#) $Header: /opt2/ena/metal/mimemessage/pickup_message.php,v 1.4 2006/05/04 01:24:35 mlemos Exp $ + * + * + */ + +/* +{metadocument}<?xml version="1.0" encoding="ISO-8859-1"?> +<class> + + <package>net.manuellemos.mimemessage</package> + + <name>pickup_message_class</name> + <version>@(#) $Id: pickup_message.php,v 1.4 2006/05/04 01:24:35 mlemos Exp $</version> + <copyright>Copyright � (C) Manuel Lemos 1999-2004</copyright> + <title>MIME E-mail message composing and sending using a Windows mail + server pickup directory</title> + <author>Manuel Lemos</author> + <authoraddress>mlemos-at-acm.org</authoraddress> + + <documentation> + <idiom>en</idiom> + <purpose>Implement an alternative message delivery method by dropping + messages in a Windows mail server pickup directory, thus overriding + the method of using the PHP <tt>mail()</tt> function implemented by + the base class.<paragraphbreak /> + It is meant to be used by on Windows 2000 or later with IIS or + Exchange mail servers because since this release the pickup directory + started being supported.<paragraphbreak /> + It is much faster than relaying messages to an SMTP server because + it works simply by storing messages in a special directory. This + delivery method does not have the overhead of the SMTP protocol. The + class does not need to wait for the mail server to pickup the + messages and deliver them to the destination recipients. Therefore, + it is recommended for bulk mailing.</purpose> + <usage>This class should be used exactly the same way as the base + class for composing and sending messages. Just create a new object of + this class as follows and set only the necessary variables to + configure details of the message pickup.<paragraphbreak /> + <tt>require('email_message.php');<br /> + require('pickup_message.php');<br /> + <br /> + $message_object = new pickup_message_class;<br /></tt><paragraphbreak /> + <b>- Requirements</b><paragraphbreak /> + You need to use at least Windows 2000 with IIS mail server or + Exchange 2000 or later.<paragraphbreak /> + The PHP script using this class must also run in the same Windows + machine on which the mail server is running. The current user must + have sufficient privileges to write to the mail server pickup + directory.<paragraphbreak /> + <b>- Pickup directory</b><paragraphbreak /> + Before sending a message you need set the + <variablelink>mailroot_directory</variablelink> variable to specify + the path of the mail server directory, so the class knows where the + messages must be dropped for subsequent pickup and delivery by the + mail server.</usage> + </documentation> + +{/metadocument} +*/ + +class pickup_message_class extends email_message_class +{ + /* Private variables */ + + var $line_break="\r\n"; + var $pickup_file=0; + var $pickup_file_name=""; + + /* Public variables */ + +/* +{metadocument} + <variable> + <name>mailroot_directory</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Specify the path of the directory where the <tt>Pickup</tt> + sub-directory is located. This sub-directory is used by the mail + server to pickup the messages to deliver.</purpose> + <usage>If this variable is set to an empty string, the class attempts + to locate the directory automatically checking the registry.<paragraphbreak /> + If the class is not able to determine the mailroot directory path + and you are certain that IIS or Exchange programs are installed in + your Windows 2000 or later machine, set this variable to the + correct path of your mail server root directory.<paragraphbreak /> + Usually it is located inside the <tt>Inetpub</tt> directory of IIS + or Exchange installation path, but it may also be located in a + slightly different path.</usage> + <example><tt><stringvalue>C:\Inetpub\mailroot\</stringvalue></tt></example> + </documentation> + </variable> +{/metadocument} +*/ + var $mailroot_directory=""; + +/* +{metadocument} + <variable> + <name>mailer_delivery</name> + <value>pickup $Revision: 1.4 $</value> + <documentation> + <purpose>Specify the text that is used to identify the mail + delivery class or sub-class. This text is appended to the + <tt>X-Mailer</tt> header text defined by the + mailer variable.</purpose> + <usage>Do not change this variable.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $mailer_delivery='pickup $Revision: 1.4 $'; + + Function CleanupMessageFile() + { + if($this->pickup_file) + { + fclose($this->pickup_file); + $this->pickup_file=0; + unlink($this->pickup_file_name); + } + } + + Function StartSendingMessage() + { + if(strlen($this->mailroot_directory)==0) + { + if(function_exists("class_exists") + && class_exists("COM")) + { + $shell=new COM("WScript.Shell"); + $wwwroot=$shell->RegRead("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\InetStp\\PathWWWRoot"); + if(is_dir($wwwroot)) + { + $mailroot=$wwwroot."\\..\\mailroot"; + if(is_dir($mailroot."\\Pickup")) + $this->mailroot_directory=$mailroot; + else + { + $mailroot=$wwwroot."\\mailroot"; + if(is_dir($mailroot."\\Pickup")) + $this->mailroot_directory=$mailroot; + } + } + } + } + if(strlen($this->mailroot_directory)==0) + return($this->OutputError("it was not specified the mailroot directory path")); + if(!is_dir($this->mailroot_directory."\\Pickup")) + return($this->OutputError("the specified mailroot path ".$this->mailroot_directory." does not contain a Pickup directory")); + $this->pickup_file_name=tempnam(GetEnv("TMP"),"eml"); + if(!($this->pickup_file=@fopen($this->pickup_file_name,"w"))) + return($this->OutputPHPError("could not create a pickup message file ".$this->pickup_file_name, error_get_last()['message'])); + return(""); + } + + Function SendMessageHeaders($headers) + { + for($error=$header_data="",$message_id_set=$date_set=0,$header=0,$return_path=$sender=$receivers=array(),Reset($headers);$header<count($headers);$header++,Next($headers)) + { + $header_name=Key($headers); + switch(strtolower($header_name)) + { + case "return-path": + $return_path[$headers[$header_name]]=1; + break; + case "from": + $error=$this->GetRFC822Addresses($headers[$header_name],$sender); + break; + case "to": + case "cc": + case "bcc": + $this->GetRFC822Addresses($headers[$header_name],$receivers); + break; + case "date": + $date_set=1; + break; + case "message-id": + $message_id_set=1; + break; + } + if(strlen($error)) + { + $this->CleanupMessageFile(); + return($this->OutputError($error)); + } + switch(strtolower($header_name)) + { + case "x-sender": + case "x-receiver": + case "bcc": + break; + default: + $header_data.=$this->FormatHeader($header_name,$headers[$header_name])."\r\n"; + } + } + if(count($sender)==0) + { + $this->CleanupMessageFile(); + return($this->OutputError("it was not specified a valid From header")); + } + if(count($receivers)==0) + { + $this->CleanupMessageFile(); + return($this->OutputError("it was not specified a valid To header")); + } + Reset($return_path); + Reset($sender); + $envelop="x-sender: ".(count($return_path) ? Key($return_path) : Key($sender)).$this->line_break; + for($receiver=0,Reset($receivers);$receiver<count($receivers);Next($receivers),$receiver++) + $envelop.="x-receiver: ".Key($receivers).$this->line_break; + if(!$date_set) + $header_data.="Date: ".date("D, d M Y H:i:s T").$this->line_break; + if(!$message_id_set + && $this->auto_message_id) + { + $sender=(count($return_path) ? Key($return_path) : Key($sender)); + $header_data.=$this->GenerateMessageID($sender).$this->line_break; + } + if(!@fputs($this->pickup_file, $envelop.$header_data.$this->line_break)) + { + $this->CleanupMessageFile(); + return($this->OutputPHPError("could not write the message headers to the pickup file", error_get_last()['message'])); + } + return(""); + } + + Function SendMessageBody($data) + { + if(!@fputs($this->pickup_file, $data)) + { + $this->CleanupMessageFile(); + return($this->OutputPHPError("could not write the message body to the pickup file", error_get_last()['message'])); + } + return(""); + } + + Function EndSendingMessage() + { + if(!@fflush($this->pickup_file)) + return($this->OutputPHPError("could not flush the message body to the pickup file", error_get_last()['message'])); + fclose($this->pickup_file); + $this->pickup_file=0; + $pickup_file_path=$this->mailroot_directory; + if(strcmp(substr($pickup_file_path,strlen($pickup_file_path)-1,1),"\\")) + $pickup_file_path.="\\"; + $pickup_file_path.="Pickup\\"; + if(!@copy($this->pickup_file_name,$pickup_file_path.basename($this->pickup_file_name))) + $error=$this->OutputPHPError("could not copy the message file to the pickup directory", error_get_last()['message']); + else + $error=""; + unlink($this->pickup_file_name); + return($error); + } + + Function StopSendingMessage() + { + return(""); + } + +}; + +/* + +{metadocument} +</class> +{/metadocument} + +*/ + +?> diff --git a/vendor/email_message/qmail_message.php b/vendor/email_message/qmail_message.php new file mode 100644 index 0000000..edc1ad2 --- /dev/null +++ b/vendor/email_message/qmail_message.php @@ -0,0 +1,207 @@ +<?php +/* + * qmail_message.php + * + * @(#) $Header: /opt2/ena/metal/mimemessage/qmail_message.php,v 1.11 2009/07/27 22:07:23 mlemos Exp $ + * + * + */ + +/* +{metadocument}<?xml version="1.0" encoding="ISO-8859-1"?> +<class> + + <package>net.manuellemos.mimemessage</package> + + <name>qmail_message_class</name> + <version>@(#) $Id: qmail_message.php,v 1.11 2009/07/27 22:07:23 mlemos Exp $</version> + <copyright>Copyright � (C) Manuel Lemos 2001-2004</copyright> + <title>MIME E-mail message composing and sending using Qmail</title> + <author>Manuel Lemos</author> + <authoraddress>mlemos-at-acm.org</authoraddress> + + <documentation> + <idiom>en</idiom> + <purpose>Implement an alternative message delivery method using + <link> + <data>Qmail</data> + <url>http://www.qmail.org/</url> + </link> MTA (Mail Transfer Agent).</purpose> + <usage>This class should be used exactly the same way as the base + class for composing and sending messages. Just create a new object of + this class as follows and set only the necessary variables to + configure details of delivery using Qmail.<paragraphbreak /> + <tt>require('email_message.php');<br /> + require('qmail_message.php');<br /> + <br /> + $message_object = new qmail_message_class;<br /></tt><paragraphbreak /> + </usage> + </documentation> + +{/metadocument} +*/ + +class qmail_message_class extends email_message_class +{ + /* Private variables */ + + var $line_break="\n"; + + /* Public variables */ + +/* +{metadocument} + <variable> + <name>qmail_path</name> + <type>STRING</type> + <value>/var/qmail/bin</value> + <documentation> + <purpose>Specifying the path of the Qmail programs.</purpose> + <usage>Usually the default is correct.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $qmail_path="/var/qmail/bin"; + +/* +{metadocument} + <variable> + <name>mailer_delivery</name> + <value>qmail $Revision: 1.11 $</value> + <documentation> + <purpose>Specify the text that is used to identify the mail + delivery class or sub-class. This text is appended to the + <tt>X-Mailer</tt> header text defined by the + mailer variable.</purpose> + <usage>Do not change this variable.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $mailer_delivery='qmail $Revision: 1.11 $'; + + Function SendMail($to,$subject,$body,$headers,$return_path) + { + $command=$this->qmail_path."/qmail-inject"; + if(strcmp($return_path,"")) + $command.=" '".preg_replace("/'/", "'\\''","-f$return_path")."'"; + if(!($pipe=@popen($command,"w"))) + return($this->OutputPHPError("it was not possible to open qmail-inject input pipe", error_get_last()['message'])); + if(strlen($headers)) + $headers.="\n"; + if(!@fputs($pipe,"To: ".$to."\nSubject: ".$subject."\n".$headers."\n") + || !@fputs($pipe,$body) + || !@fflush($pipe)) + return($this->OutputPHPError("it was not possible to write qmail-inject input pipe", error_get_last()['message'])); + return(($rc=(pclose($pipe)>>8)) ? "qmail-inject error ".$rc : ""); + } + + Function SendMailing($mailing) + { + if(strlen($this->error)) + return($this->error); + if(!IsSet($this->mailings[$mailing])) + return($this->OutputError("it was not specified a valid mailing")); + if(IsSet($this->mailings[$mailing]["Envelope"])) + return($this->OutputError("the mailing was not yet ended")); + $this->ResetMessage(); + $base_path=$this->mailings[$mailing]["BasePath"]; + if(GetType($header_lines=@File($base_path.".h"))!="array") + return($this->OutputPHPError("could not read the mailing headers file ".$base_path.".h", error_get_last()['message'])); + if(!($envelope_file=@fopen($base_path.".e","rb"))) + return($this->OutputPHPError("could not open the mailing envelope file ".$base_path.".e", error_get_last()['message'])); + for($return_path=$data="";!feof($envelope_file) || strlen($data);) + { + if(GetType($break=strpos($data,chr(0)))!="integer") + { + if(GetType($chunk=@fread($envelope_file,$this->file_buffer_length))!="string") + { + fclose($envelope_file); + return($this->OutputPHPError("could not read the mailing envelop file ".$base_path.".e", error_get_last()['message'])); + } + $data.=$chunk; + continue; + } + if($break==0) + break; + switch($data[0]) + { + case "F": + $return_path=substr($data,1,$break-1); + break; + default: + return($this->OutputError("invalid mailing envelope file ".$base_path.".e")); + } + break; + } + fclose($envelope_file); + if(strlen($return_path)==0) + return($this->OutputError("envelope file ".$base_path.".e does not contain a return path")); + $headers=$this->FormatHeader("Date",gmdate("D, d M Y H:i:s -0000"))."\n"; + for($has=array(),$line=0;$line<count($header_lines);$line++) + { + $header=$this->Tokenize($header_lines[$line],":"); + switch(strtolower($header)) + { + case "return-path": + case "bcc": + case "date": + case "content-length": + break; + case "from": + case "to": + case "cc": + case "message-id": + $has[strtolower($header)]=1; + default: + $headers.=$this->FormatHeader($header,trim($this->Tokenize("\n")))."\n"; + break; + } + } + if(!IsSet($has["from"])) + $headers.=$this->FormatHeader("From","<".$return_path.">")."\n"; + if(!IsSet($has["to"]) + && !IsSet($has["cc"])) + $headers.=$this->FormatHeader("Cc","recipient list not shown: ;")."\n"; + if(!IsSet($has["message-id"])) + { + $micros=$this->Tokenize(microtime()," "); + $seconds=$this->Tokenize(""); + $this->Tokenize($return_path,"@"); + $host=$this->Tokenize("@"); + if($host[strlen($host)-1]=="-") + $host=substr($host,0,strlen($host)-1); + $headers.=$this->FormatHeader("Message-ID","<".strftime("%Y%m%d%H%M%S",$seconds).substr($micros,1,5).".qmail@".$host.">")."\n"; + } + if(!($body_file=@fopen($base_path.".b","rb"))) + return($this->OutputPHPError("could not open the mailing body file ".$base_path.".b", error_get_last()['message'])); + for($body="";!feof($body_file);) + { + if(GetType($chunk=@fread($body_file,$this->file_buffer_length))!="string") + { + fclose($body_file); + return($this->OutputPHPError("could not read the mailing body file ".$base_path.".b", error_get_last()['message'])); + } + $body.=$chunk; + } + fclose($body_file); + $command=$this->qmail_path."/qmail-queue 1<".$base_path.".e"; + if(!($pipe=@popen($command,"w"))) + return($this->OutputPHPError("it was not possible to open qmail-queue input pipe", error_get_last()['message'])); + if(!@fputs($pipe,$headers."\n".$body) + || !@fflush($pipe)) + return($this->OutputPHPError("it was not possible to write qmail-queue input pipe", error_get_last()['message'])); + return(($rc=(pclose($pipe)>>8)) ? "qmail-queue error ".$rc : ""); + } +}; + +/* + +{metadocument} +</class> +{/metadocument} + +*/ + +?> diff --git a/vendor/email_message/sendmail_message.php b/vendor/email_message/sendmail_message.php new file mode 100644 index 0000000..2ac2ce0 --- /dev/null +++ b/vendor/email_message/sendmail_message.php @@ -0,0 +1,268 @@ +<?php +/* + * sendmail_message.php + * + * @(#) $Header: /opt2/ena/metal/mimemessage/sendmail_message.php,v 1.19 2011/11/26 09:41:09 mlemos Exp $ + * + * + */ + +/* +{metadocument}<?xml version="1.0" encoding="ISO-8859-1"?> +<class> + + <package>net.manuellemos.mimemessage</package> + + <name>sendmail_message_class</name> + <version>@(#) $Id: sendmail_message.php,v 1.19 2011/11/26 09:41:09 mlemos Exp $</version> + <copyright>Copyright � (C) Manuel Lemos 1999-2004</copyright> + <title>MIME E-mail message composing and sending using Sendmail</title> + <author>Manuel Lemos</author> + <authoraddress>mlemos-at-acm.org</authoraddress> + + <documentation> + <idiom>en</idiom> + <purpose>Implement an alternative message delivery method using + <link> + <data>Sendmail</data> + <url>http://www.sendmail.org/</url> + </link> MTA (Mail Transfer Agent). This class can also be used with + other MTAs like <link> + <data>Exim</data> + <url>http://www.exim.org/</url> + </link>, <link> + <data>Postfix</data> + <url>http://www.postfix.org/</url> + </link> and <link> + <data>Qmail</data> + <url>http://www.qmail.org/</url> + </link>, as they provide a wrapper + commands that emulate the <tt>sendmail</tt> command.</purpose> + <usage>This class should be used exactly the same way as the base + class for composing and sending messages. Just create a new object of + this class as follows and set only the necessary variables to + configure details of delivery using Sendmail.<paragraphbreak /> + <tt>require('email_message.php');<br /> + require('sendmail_message.php');<br /> + <br /> + $message_object = new sendmail_message_class;<br /></tt><paragraphbreak /> + <b>- Tuning the delivery mode for mass mailing</b><paragraphbreak /> + Sendmail supports several message delivery modes. In many + installations the default is to attempt to deliver the message right + away when the message is handed by the applications to + Sendmail.<paragraphbreak /> + This may be an inconvenient because it makes PHP scripts wait for the + message to be delivered to the destination SMTP server. If the SMTP + connection with that server is slow, it may stall the delivery for a + long while.<paragraphbreak /> + Under Unix/Linux, PHP defaults to using Sendmail or equivalent to + deliver messages sent with the <tt>mail()</tt> function. Some people + assume that it is faster to queue messages by relaying to an + intermediate SMTP server than to use the <tt>mail()</tt> function + that uses Sendmail. This is not accurate.<paragraphbreak /> + Sendmail supports other message delivery modes that can be used for + much faster message queueing. These modes are more recommended for + mass mailing. Adjust the value of the + <variablelink>delivery_mode</variablelink> variable to improve the + message queueing rate if you want to use this class for mass + mailing.<paragraphbreak /> + Alternatively, you may also call the + <functionlink>SetBulkMail</functionlink> to hint this class to use + a delivery mode more suitable for bulk mailing. Currently, this makes + this class use the delivery mode specified by the + <variablelink>bulk_mail_delivery_mode</variablelink> variable, which + defaults to make the messages be queued for later delivery, thus + making the calling script execute much faster.</usage> + </documentation> + +{/metadocument} +*/ + +define("SENDMAIL_DELIVERY_DEFAULT", ""); +define("SENDMAIL_DELIVERY_INTERACTIVE", "i"); +define("SENDMAIL_DELIVERY_BACKGROUND", "b"); +define("SENDMAIL_DELIVERY_QUEUE", "q"); +define("SENDMAIL_DELIVERY_DEFERRED", "d"); + +class sendmail_message_class extends email_message_class +{ + /* Private variables */ + + var $line_break="\n"; + + /* Public variables */ + +/* +{metadocument} + <variable> + <name>sendmail_path</name> + <type>STRING</type> + <value>/usr/lib/sendmail</value> + <documentation> + <purpose>Specifying the path of the <tt>sendmail</tt> executable + program.</purpose> + <usage>The original default path of the <tt>sendmail</tt> used to be + <tt>/usr/lib/sendmail</tt>. However, currently it is usually + located in <tt>/usr/sbin/sendmail</tt> having a symbolic link + pointing to that path from + <tt>/usr/lib/sendmail</tt>.<paragraphbreak /> + If this symbolic link does not exist or the <tt>sendmail</tt> is + different in your installation, you need to change this + variable.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $sendmail_path="/usr/lib/sendmail"; + +/* +{metadocument} + <variable> + <name>delivery_mode</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Specify the Sendmail message delivery mode. These delivery + modes are only supported by Sendmail and Exim MTAs.<paragraphbreak /> + Current versions of Qmail and Postfix do not support configurable + delivery modes. They always inject the messages in the local queue + and let their queue management system take care of the delivery as + soon as possible. Just leave this variable with the default value + when using this class with these MTAs.</purpose> + <usage>Sendmail supports several different delivery + modes:<paragraphbreak /> + SENDMAIL_DELIVERY_DEFAULT - + <tt><stringvalue></stringvalue></tt><paragraphbreak /> + Does not override the default mode.<paragraphbreak /> + SENDMAIL_DELIVERY_INTERACTIVE - + <tt><stringvalue>i</stringvalue></tt><paragraphbreak /> + Attempt to send the messages synchronously to the recipient's SMTP + server and only returns when it succeeds or fails. This is usually + the default mode. It stalls the delivery of messages but it may be + safer to preserve disk space because the successfully delivered + messages are not stored.<paragraphbreak /> + SENDMAIL_DELIVERY_BACKGROUND - + <tt><stringvalue>b</stringvalue></tt><paragraphbreak /> + Creates a background process that attempts to deliver the message + and returns immediately. This mode is recommended when you want + to send a few messages as soon as possible. It is not recommended + for sending messages to many recipients as it may consume too much + memory and CPU that result from creating excessive background + processes.<paragraphbreak /> + SENDMAIL_DELIVERY_QUEUE - + <tt><stringvalue>q</stringvalue></tt><paragraphbreak /> + Just drop the message in the queue and leave it there until next + time the queue is run. It is recommended for deliverying messages + to many recipients as long as there is enough disk space to store + all the messages in the queue.<paragraphbreak /> + SENDMAIL_DELIVERY_DEFERRED - + <tt><stringvalue>d</stringvalue></tt><paragraphbreak /> + The same as the queue mode except for a few verifications that are + skipped.<paragraphbreak /> + </usage> + </documentation> + </variable> +{/metadocument} +*/ + var $delivery_mode=SENDMAIL_DELIVERY_DEFAULT; + +/* +{metadocument} + <variable> + <name>bulk_mail_delivery_mode</name> + <type>STRING</type> + <value>q</value> + <documentation> + <purpose>Specify the Sendmail message delivery mode when the class is + in bulk mail mode that is set with the + <functionlink>SetBulkMail</functionlink> function.</purpose> + <usage>The available delivery modes are the same as those used to set + the <variablelink>delivery_mode</variablelink> variable. The + default bulk mail delivery mode is to just queue the message + (<tt>SENDMAIL_DELIVERY_QUEUE</tt>) without waiting for sendmail to + deliver the message.<paragraphbreak /> + Note that some MTAs that emulate Sendmail return an error when set + to queue delivery mode. If this happens with the MTA that you are + using, change this variable to the + <tt>SENDMAIL_DELIVERY_DEFAULT</tt>.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $bulk_mail_delivery_mode=SENDMAIL_DELIVERY_QUEUE; + +/* +{metadocument} + <variable> + <name>sendmail_arguments</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Specify additional <tt>sendmail</tt> program arguments.</purpose> + <usage>Use this to to pass additional arguments that are not + supported by this class.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $sendmail_arguments=""; + +/* +{metadocument} + <variable> + <name>mailer_delivery</name> + <value>sendmail $Revision: 1.19 $</value> + <documentation> + <purpose>Specify the text that is used to identify the mail + delivery class or sub-class. This text is appended to the + <tt>X-Mailer</tt> header text defined by the + mailer variable.</purpose> + <usage>Do not change this variable.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $mailer_delivery='sendmail $Revision: 1.19 $'; + + Function SendMail($to, $subject, $body, $headers, $return_path) + { + $command=$this->sendmail_path." -t -i"; + switch($this->bulk_mail ? $this->bulk_mail_delivery_mode : $this->delivery_mode) + { + case SENDMAIL_DELIVERY_DEFAULT: + case SENDMAIL_DELIVERY_INTERACTIVE: + case SENDMAIL_DELIVERY_BACKGROUND: + case SENDMAIL_DELIVERY_QUEUE: + case SENDMAIL_DELIVERY_DEFERRED: + break; + default: + return($this->OutputError("it was specified an unknown sendmail delivery mode")); + } + if($this->delivery_mode!=SENDMAIL_DELIVERY_DEFAULT) + $command.=" -od".$this->delivery_mode; + if(strlen($return_path)) + $command.=" -f '".preg_replace("/'/", "'\\''",$return_path)."'"; + if(strlen($this->sendmail_arguments)) + $command.=" ".$this->sendmail_arguments; + if(!($pipe=@popen($command,"w"))) + return($this->OutputPHPError("it was not possible to open sendmail input pipe", error_get_last()['message'])); + if(strlen($headers)) + $headers.="\n"; + if(!@fputs($pipe,"To: ".$to."\nSubject: ".$subject."\n".$headers."\n") + || !@fputs($pipe,$body) + || !@fflush($pipe)) + return($this->OutputPHPError("it was not possible to write sendmail input pipe", error_get_last()['message'])); + pclose($pipe); + return(""); + } +}; + +/* + +{metadocument} +</class> +{/metadocument} + +*/ + +?> diff --git a/vendor/email_message/smtp.php b/vendor/email_message/smtp.php new file mode 100644 index 0000000..0fb4b2f --- /dev/null +++ b/vendor/email_message/smtp.php @@ -0,0 +1,1912 @@ +<?php +/* + * smtp.php + * + * @(#) $Header: /opt2/ena/metal/smtp/smtp.php,v 1.51 2016/08/23 04:55:14 mlemos Exp $ + * + */ + +/* +{metadocument}<?xml version="1.0" encoding="ISO-8859-1"?> +<class> + + <package>net.manuellemos.smtp</package> + + <version>@(#) $Id: smtp.php,v 1.51 2016/08/23 04:55:14 mlemos Exp $</version> + <copyright>Copyright (C) Manuel Lemos 1999-2011</copyright> + <title>Sending e-mail messages via SMTP protocol</title> + <author>Manuel Lemos</author> + <authoraddress>mlemos-at-acm.org</authoraddress> + + <documentation> + <idiom>en</idiom> + <purpose>Sending e-mail messages via SMTP protocol</purpose> + <translation>If you are interested in translating the documentation of + this class to your own idiom, please <link> + <data>contact the author</data> + <url>mailto:<getclassproperty>authoraddress</getclassproperty></url> + </link>.</translation> + <support>Technical support for using this class may be obtained in the + <tt>smtpclass</tt> support forum. Just go to the support forum pages + page to browse the forum archives and post support request + messages:<paragraphbreak /> + <link> + <data>http://www.phpclasses.org/discuss/package/14/</data> + <url>http://www.phpclasses.org/discuss/package/14/</url> + </link></support> + <usage>To use this class just create a new object, set any variables + to configure its options and call the + <functionlink>SendMessage</functionlink> function to send a + message.<paragraphbreak />It is not recommended that you use this + class alone unless you have deep understanding of Internet mail + standards on how to compose compliant e-mail messages. Instead, use + the <link> + <data>MIME message composing and sending class</data> + <url>http://www.phpclasses.org/mimemessage</url> + </link> and its sub-class SMTP message together with this SMTP class + to properly compose e-mail messages, so your messages are not + discarded for not being correctly composed.</usage> + </documentation> + +{/metadocument} +*/ + +class smtp_class +{ +/* +{metadocument} + <variable> + <name>user</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Define the authorized user when sending messages to a SMTP + server.</purpose> + <usage>Set this variable to the user name when the SMTP server + requires authentication.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $user=""; + +/* +{metadocument} + <variable> + <name>realm</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Define the authentication realm when sending messages to a + SMTP server.</purpose> + <usage>Set this variable when the SMTP server requires + authentication and if more than one authentication realm is + supported.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $realm=""; + +/* +{metadocument} + <variable> + <name>password</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Define the authorized user password when sending messages + to a SMTP server.</purpose> + <usage>Set this variable to the user password when the SMTP server + requires authentication.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $password=""; + +/* +{metadocument} + <variable> + <name>workstation</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Define the client workstation name when sending messages + to a SMTP server.</purpose> + <usage>Set this variable to the client workstation when the SMTP + server requires authentication identifiying the origin workstation + name.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $workstation=""; + +/* +{metadocument} + <variable> + <name>authentication_mechanism</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Force the use of a specific authentication mechanism.</purpose> + <usage>Set it to an empty string to let the class determine the + authentication mechanism to use automatically based on the + supported mechanisms by the server and by the SASL client library + classes.<paragraphbreak /> + Set this variable to a specific mechanism name if you want to + override the automatic authentication mechanism selection.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $authentication_mechanism=""; + +/* +{metadocument} + <variable> + <name>sasl_autoload</name> + <type>BOOLEAN</type> + <value>0</value> + <documentation> + <purpose>Specify whether the class should check if the SASL classes + exists or should they be loaded with an autoloader.</purpose> + <usage>Set this variable to + <tt><booleanvalue>1</booleanvalue></tt> if you are using an + autoloader to load the SASL classes.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $sasl_autoload=0; + + +/* +{metadocument} + <variable> + <name>host_name</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Define the SMTP server host name.</purpose> + <usage>Set to the host name of the SMTP server to which you want to + relay the messages.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $host_name=""; + +/* +{metadocument} + <variable> + <name>host_port</name> + <type>INTEGER</type> + <value>25</value> + <documentation> + <purpose>Define the SMTP server host port.</purpose> + <usage>Set to the TCP port of the SMTP server host to connect.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $host_port=25; + +/* +{metadocument} + <variable> + <name>socks_host_name</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Define the SOCKS server host name.</purpose> + <usage>Set to the SOCKS server host name through which the SMTP + connection should be routed. Leave it empty if you do not want the + connections to be established through a SOCKS server.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $socks_host_name = ''; + +/* +{metadocument} + <variable> + <name>socks_host_port</name> + <type>INTEGER</type> + <value>1080</value> + <documentation> + <purpose>Define the SOCKS server host port.</purpose> + <usage>Set to the port of the SOCKS server host through which the + the SMTP connection should be routed.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $socks_host_port=1080; + +/* +{metadocument} + <variable> + <name>socks_version</name> + <type>STRING</type> + <value>5</value> + <documentation> + <purpose>Set the SOCKS protocol version.</purpose> + <usage>Change this value if SOCKS server you want to use is + listening to a different port.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $socks_version='5'; + +/* +{metadocument} + <variable> + <name>http_proxy_host_name</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Define the HTTP proxy server host name.</purpose> + <usage>Set to the HTTP proxy server host name through which the + SMTP connection should be routed. Leave it empty if you do not + want the connections to be established through an HTTP proxy.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $http_proxy_host_name = ''; + +/* +{metadocument} + <variable> + <name>http_proxy_host_port</name> + <type>INTEGER</type> + <value>80</value> + <documentation> + <purpose>Define the HTTP proxy server host port.</purpose> + <usage>Set to the port of the HTTP proxy server host through which + the SMTP connection should be routed.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $http_proxy_host_port=80; + +/* +{metadocument} + <variable> + <name>user_agent</name> + <type>STRING</type> + <value>SMTP Class (http://www.phpclasses.org/smtpclass $Revision: 1.51 $)</value> + <documentation> + <purpose>Set the user agent used when connecting via an HTTP proxy.</purpose> + <usage>Change this value only if for some reason you want emulate a + certain e-mail client.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $user_agent='SMTP Class (http://www.phpclasses.org/smtpclass $Revision: 1.51 $)'; + +/* +{metadocument} + <variable> + <name>ssl</name> + <type>BOOLEAN</type> + <value>0</value> + <documentation> + <purpose>Define whether the connection to the SMTP server should be + established securely using SSL protocol.</purpose> + <usage>Set to <booleanvalue>1</booleanvalue> if the SMTP server + requires secure connections using SSL protocol.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $ssl=0; + +/* +{metadocument} + <variable> + <name>start_tls</name> + <type>BOOLEAN</type> + <value>0</value> + <documentation> + <purpose>Define whether the connection to the SMTP server should use + encryption after the connection is established using TLS + protocol.</purpose> + <usage>Set to <booleanvalue>1</booleanvalue> if the SMTP server + requires that authentication be done securely starting the TLS + protocol after the connection is established.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $start_tls = 0; + +/* +{metadocument} + <variable> + <name>localhost</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Name of the local host computer</purpose> + <usage>Set to the name of the computer connecting to the SMTP + server from the local network.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $localhost=""; + +/* +{metadocument} + <variable> + <name>timeout</name> + <type>INTEGER</type> + <value>0</value> + <documentation> + <purpose>Specify the connection timeout period in seconds.</purpose> + <usage>Leave it set to <integervalue>0</integervalue> if you want + the connection attempts to wait forever. Change this value if for + some reason the timeout period seems insufficient or otherwise it + seems too long.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $timeout=0; + +/* +{metadocument} + <variable> + <name>data_timeout</name> + <type>INTEGER</type> + <value>0</value> + <documentation> + <purpose>Specify the timeout period in seconds to wait for data from + the server.</purpose> + <usage>Leave it set to <integervalue>0</integervalue> if you want + to use the same value defined in the + <variablelink>timeout</variablelink> variable. Change this value + if for some reason the default data timeout period seems + insufficient or otherwise it seems too long.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $data_timeout=0; + +/* +{metadocument} + <variable> + <name>direct_delivery</name> + <type>BOOLEAN</type> + <value>0</value> + <documentation> + <purpose>Boolean flag that indicates whether the message should be + sent in direct delivery mode, i.e. the message is sent to the SMTP + server associated to the domain of the recipient instead of + relaying to the server specified by the + <variablelink>host_name</variablelink> variable.</purpose> + <usage>Set this to <tt><booleanvalue>1</booleanvalue></tt> if you + want to send urgent messages directly to the recipient domain SMTP + server.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $direct_delivery=0; + +/* +{metadocument} + <variable> + <name>error</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Message that describes the error when a call to a class + function fails.</purpose> + <usage>Check this variable when an error occurs to understand what + happened.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $error=""; + +/* +{metadocument} + <variable> + <name>debug</name> + <type>BOOLEAN</type> + <value>0</value> + <documentation> + <purpose>Specify whether it is necessary to output SMTP connection + debug information.</purpose> + <usage>Set this variable to + <tt><booleanvalue>1</booleanvalue></tt> if you need to see + the progress of the SMTP connection and protocol dialog when you + need to understand the reason for delivery problems.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $debug=0; + +/* +{metadocument} + <variable> + <name>html_debug</name> + <type>BOOLEAN</type> + <value>0</value> + <documentation> + <purpose>Specify whether the debug information should be outputted in + HTML format.</purpose> + <usage>Set this variable to + <tt><booleanvalue>1</booleanvalue></tt> if you need to see + the debug output in a Web page.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $html_debug=0; + +/* +{metadocument} + <variable> + <name>esmtp</name> + <type>BOOLEAN</type> + <value>1</value> + <documentation> + <purpose>Specify whether the class should attempt to use ESMTP + extensions supported by the server.</purpose> + <usage>Set this variable to + <tt><booleanvalue>0</booleanvalue></tt> if for some reason you + want to avoid benefitting from ESMTP extensions.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $esmtp=1; + +/* +{metadocument} + <variable> + <name>esmtp_extensions</name> + <type>HASH</type> + <value></value> + <documentation> + <purpose>Associative array with the list of ESMTP extensions + supported by the SMTP server.</purpose> + <usage>Check this variable after connecting to the SMTP server to + determine which ESMTP extensions are supported.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $esmtp_extensions=array(); + +/* +{metadocument} + <variable> + <name>exclude_address</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Specify an address that should be considered invalid + when resolving host name addresses.</purpose> + <usage>In some networks any domain name that does not exist is + resolved as a sub-domain of the default local domain. If the DNS is + configured in such way that it always resolves any sub-domain of + the default local domain to a given address, it is hard to + determine whether a given domain does not exist.<paragraphbreak /> + If your network is configured this way, you may set this variable + to the address that all sub-domains of the default local domain + resolves, so the class can assume that such address is invalid.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $exclude_address=""; + +/* +{metadocument} + <variable> + <name>getmxrr</name> + <type>STRING</type> + <value>getmxrr</value> + <documentation> + <purpose>Specify the name of the function that is called to determine + the SMTP server address of a given domain.</purpose> + <usage>Change this to a working replacement of the PHP + <tt>getmxrr()</tt> function if this is not working in your system + and you want to send messages in direct delivery mode.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $getmxrr="GetMXRR"; + +/* +{metadocument} + <variable> + <name>pop3_auth_host</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Specify the server address for POP3 based authentication.</purpose> + <usage>Set this variable to the address of the POP3 server if the + SMTP server requires POP3 based authentication.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $pop3_auth_host=""; + +/* +{metadocument} + <variable> + <name>pop3_auth_port</name> + <type>INTEGER</type> + <value>110</value> + <documentation> + <purpose>Specify the server port for POP3 based authentication.</purpose> + <usage>Set this variable to the port of the POP3 server if the + SMTP server requires POP3 based authentication.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $pop3_auth_port=110; + + /* private variables - DO NOT ACCESS */ + + var $state="Disconnected"; + var $connection=0; + var $pending_recipients=0; + var $next_token=""; + var $direct_sender=""; + var $connected_domain=""; + var $result_code; + var $disconnected_error=0; + var $esmtp_host=""; + var $maximum_piped_recipients=100; + + /* Private methods - DO NOT CALL */ + + Function Tokenize($string,$separator="") + { + if(!strcmp($separator,"")) + { + $separator=$string; + $string=$this->next_token; + } + for($character=0;$character<strlen($separator);$character++) + { + if(GetType($position=strpos($string,$separator[$character]))=="integer") + $found=(IsSet($found) ? min($found,$position) : $position); + } + if(IsSet($found)) + { + $this->next_token=substr($string,$found+1); + return(substr($string,0,$found)); + } + else + { + $this->next_token=""; + return($string); + } + } + + Function OutputDebug($message) + { + $message.="\n"; + if($this->html_debug) + $message=str_replace("\n","<br />\n",HtmlEntities($message)); + echo $message; + flush(); + } + + Function SetDataAccessError($error) + { + $this->error=$error; + if(function_exists("socket_get_status")) + { + $status=socket_get_status($this->connection); + if($status["timed_out"]) + $this->error.=": data access time out"; + elseif($status["eof"]) + { + $this->error.=": the server disconnected"; + $this->disconnected_error=1; + } + } + return($this->error); + } + + Function SetError($error) + { + return($this->error=$error); + } + + Function GetLine() + { + for($line="";;) + { + if(feof($this->connection)) + { + $this->error="reached the end of data while reading from the SMTP server conection"; + return(""); + } + if(GetType($data=@fgets($this->connection,100))!="string" + || strlen($data)==0) + { + $this->SetDataAccessError("it was not possible to read line from the SMTP server"); + return(""); + } + $line.=$data; + $length=strlen($line); + if($length>=2 + && substr($line,$length-2,2)=="\r\n") + { + $line=substr($line,0,$length-2); + if($this->debug) + $this->OutputDebug("S $line"); + return($line); + } + } + } + + Function PutLine($line) + { + if($this->debug) + $this->OutputDebug("C $line"); + if(!@fputs($this->connection,"$line\r\n")) + { + $this->SetDataAccessError("it was not possible to send a line to the SMTP server"); + return(0); + } + return(1); + } + + Function PutData(&$data) + { + if(strlen($data)) + { + if($this->debug) + $this->OutputDebug("C $data"); + if(!@fputs($this->connection,$data)) + { + $this->SetDataAccessError("it was not possible to send data to the SMTP server"); + return(0); + } + } + return(1); + } + + Function VerifyResultLines($code,&$responses) + { + $responses=array(); + Unset($this->result_code); + while(strlen($line=$this->GetLine($this->connection))) + { + if(IsSet($this->result_code)) + { + if(strcmp($this->Tokenize($line," -"),$this->result_code)) + { + $this->error=$line; + return(0); + } + } + else + { + $this->result_code=$this->Tokenize($line," -"); + if(GetType($code)=="array") + { + for($codes=0;$codes<count($code) && strcmp($this->result_code,$code[$codes]);$codes++); + if($codes>=count($code)) + { + $this->error=$line; + return(0); + } + } + else + { + if(strcmp($this->result_code,$code)) + { + $this->error=$line; + return(0); + } + } + } + $responses[]=$this->Tokenize(""); + if(!strcmp($this->result_code,$this->Tokenize($line," "))) + return(1); + } + return(-1); + } + + Function FlushRecipients() + { + if($this->pending_sender) + { + if($this->VerifyResultLines("250",$responses)<=0) + return(0); + $this->pending_sender=0; + } + for(;$this->pending_recipients;$this->pending_recipients--) + { + if($this->VerifyResultLines(array("250","251"),$responses)<=0) + return(0); + } + return(1); + } + + Function Resolve($domain, &$ip, $server_type) + { + if(preg_match('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/',$domain)) + $ip=$domain; + else + { + if($this->debug) + $this->OutputDebug('Resolving '.$server_type.' server domain "'.$domain.'"...'); + if(!strcmp($ip=@gethostbyname($domain),$domain)) + $ip=""; + } + if(strlen($ip)==0 + || (strlen($this->exclude_address) + && !strcmp(@gethostbyname($this->exclude_address),$ip))) + return($this->SetError("could not resolve the host domain \"".$domain."\"")); + return(''); + } + + Function ConnectToHost($domain, $port, $resolve_message) + { + if($this->ssl) + { + $version=explode(".",function_exists("phpversion") ? phpversion() : "3.0.7"); + $php_version=intval($version[0])*1000000+intval($version[1])*1000+intval($version[2]); + if($php_version<4003000) + return("establishing SSL connections requires at least PHP version 4.3.0"); + if(!function_exists("extension_loaded") + || !extension_loaded("openssl")) + return("establishing SSL connections requires the OpenSSL extension enabled"); + } + if(strlen($this->Resolve($domain, $ip, 'SMTP'))) + return($this->error); + if(strlen($this->socks_host_name)) + { + switch($this->socks_version) + { + case '4': + $version = 4; + break; + case '5': + $version = 5; + break; + default: + return('it was not specified a supported SOCKS protocol version'); + break; + } + $host_ip = $ip; + $host_port = $port; + if(strlen($this->error = $this->Resolve($this->socks_host_name, $ip, 'SOCKS'))) + return($this->error); + if($this->ssl) + $ip="ssl://".($socks_host = $this->socks_host_name); + else + $socks_host = $ip; + if($this->debug) + $this->OutputDebug("Connecting to SOCKS server \"".$socks_host."\" port ".$this->http_proxy_host_port."..."); + if(($this->connection=($this->timeout ? fsockopen($ip, $this->socks_host_port, $errno, $error, $this->timeout) : fsockopen($ip, $this->socks_host_port, $errno, $error)))) + { + $timeout=($this->data_timeout ? $this->data_timeout : $this->timeout); + if($timeout + && function_exists("socket_set_timeout")) + socket_set_timeout($this->connection,$timeout,0); + if(strlen($this->socks_host_name)) + { + if($this->debug) + $this->OutputDebug('Connected to the SOCKS server '.$this->socks_host_name); + $send_error = 'it was not possible to send data to the SOCKS server'; + $receive_error = 'it was not possible to receive data from the SOCKS server'; + switch($version) + { + case 4: + $command = 1; + $user = ''; + if(!fputs($this->connection, chr($version).chr($command).pack('nN', $host_port, ip2long($host_ip)).$user.Chr(0))) + $error = $this->SetDataAccessError($send_error); + else + { + $response = fgets($this->connection, 9); + if(strlen($response) != 8) + $error = $this->SetDataAccessError($receive_error); + else + { + $socks_errors = array( + "\x5a"=>'', + "\x5b"=>'request rejected', + "\x5c"=>'request failed because client is not running identd (or not reachable from the server)', + "\x5d"=>'request failed because client\'s identd could not confirm the user ID string in the request', + ); + $error_code = $response[1]; + $error = (IsSet($socks_errors[$error_code]) ? $socks_errors[$error_code] : 'unknown'); + if(strlen($error)) + $error = 'SOCKS error: '.$error; + } + } + break; + case 5: + if($this->debug) + $this->OutputDebug('Negotiating the authentication method ...'); + $methods = 1; + $method = 0; + if(!fputs($this->connection, chr($version).chr($methods).chr($method))) + $error = $this->SetDataAccessError($send_error); + else + { + $response = fgets($this->connection, 3); + if(strlen($response) != 2) + $error = $this->SetDataAccessError($receive_error); + elseif(Ord($response[1]) != $method) + $error = 'the SOCKS server requires an authentication method that is not yet supported'; + else + { + if($this->debug) + $this->OutputDebug('Connecting to SMTP server IP '.$host_ip.' port '.$host_port.'...'); + $command = 1; + $address_type = 1; + if(!fputs($this->connection, chr($version).chr($command)."\x00".chr($address_type).pack('Nn', ip2long($host_ip), $host_port))) + $error = $this->SetDataAccessError($send_error); + else + { + $response = fgets($this->connection, 11); + if(strlen($response) != 10) + $error = $this->SetDataAccessError($receive_error); + else + { + $socks_errors = array( + "\x00"=>'', + "\x01"=>'general SOCKS server failure', + "\x02"=>'connection not allowed by ruleset', + "\x03"=>'Network unreachable', + "\x04"=>'Host unreachable', + "\x05"=>'Connection refused', + "\x06"=>'TTL expired', + "\x07"=>'Command not supported', + "\x08"=>'Address type not supported' + ); + $error_code = $response[1]; + $error = (IsSet($socks_errors[$error_code]) ? $socks_errors[$error_code] : 'unknown'); + if(strlen($error)) + $error = 'SOCKS error: '.$error; + } + } + } + } + break; + default: + $error = 'support for SOCKS protocol version '.$this->socks_version.' is not yet implemented'; + break; + } + if(strlen($this->error = $error)) + { + fclose($this->connection); + return($error); + } + } + return(''); + } + } + elseif(strlen($this->http_proxy_host_name)) + { + if(strlen($error = $this->Resolve($this->http_proxy_host_name, $ip, 'SMTP'))) + return($error); + if($this->ssl) + $ip = 'ssl://'.($proxy_host = $this->http_proxy_host_name); + else + $proxy_host = $ip; + if($this->debug) + $this->OutputDebug("Connecting to HTTP proxy server \"".$ip."\" port ".$this->http_proxy_host_port."..."); + if(($this->connection=($this->timeout ? @fsockopen($ip, $this->http_proxy_host_port, $errno, $error, $this->timeout) : @fsockopen($ip, $this->http_proxy_host_port, $errno, $error)))) + { + if($this->debug) + $this->OutputDebug('Connected to HTTP proxy host "'.$this->http_proxy_host_name.'".'); + $timeout=($this->data_timeout ? $this->data_timeout : $this->timeout); + if($timeout + && function_exists("socket_set_timeout")) + socket_set_timeout($this->connection,$timeout,0); + if($this->PutLine('CONNECT '.$domain.':'.$port.' HTTP/1.0') + && $this->PutLine('User-Agent: '.$this->user_agent) + && $this->PutLine('')) + { + if(GetType($response = $this->GetLine()) == 'string') + { + if(!preg_match('/^http\\/[0-9]+\\.[0-9]+[ \t]+([0-9]+)[ \t]*(.*)$/i', $response,$matches)) + return($this->SetError("3 it was received an unexpected HTTP response status")); + $error = $matches[1]; + switch($error) + { + case '200': + for(;;) + { + if(GetType($response = $this->GetLine()) != 'string') + break; + if(strlen($response) == 0) + return(''); + } + break; + default: + $this->error = 'the HTTP proxy returned error '.$error.' '.$matches[2]; + break; + } + } + } + if($this->debug) + $this->OutputDebug("Disconnected."); + fclose($this->connection); + $this->connection = 0; + return($this->error); + } + } + else + { + if($this->ssl) + $ip = 'ssl://'.($host = $domain); + elseif($this->start_tls) + $ip = $host = $domain; + else + $host = $ip; + if($this->debug) + $this->OutputDebug("Connecting to SMTP server \"".$host."\" port ".$port."..."); + if(($this->connection=($this->timeout ? @fsockopen($ip, $port, $errno, $error, $this->timeout) : @fsockopen($ip, $port, $errno, $error)))) + return(""); + } + $error=($this->timeout ? strval($error) : "??"); + switch($error) + { + case "-3": + return("-3 socket could not be created"); + case "-4": + return("-4 dns lookup on hostname \"".$domain."\" failed"); + case "-5": + return("-5 connection refused or timed out"); + case "-6": + return("-6 fdopen() call failed"); + case "-7": + return("-7 setvbuf() call failed"); + } + return("could not connect to the host \"".$domain."\": ".$error); + } + + Function SASLAuthenticate($mechanisms, $credentials, &$authenticated, &$mechanism) + { + $authenticated=0; + if(!$this->sasl_autoload + && (!function_exists("class_exists") + || !class_exists("sasl_client_class"))) + { + $this->error="it is not possible to authenticate using the specified mechanism because the SASL library class is not loaded"; + return(0); + } + $sasl=new sasl_client_class; + $sasl->SetCredential("user",$credentials["user"]); + $sasl->SetCredential("password",$credentials["password"]); + if(IsSet($credentials["realm"])) + $sasl->SetCredential("realm",$credentials["realm"]); + if(IsSet($credentials["workstation"])) + $sasl->SetCredential("workstation",$credentials["workstation"]); + if(IsSet($credentials["mode"])) + $sasl->SetCredential("mode",$credentials["mode"]); + do + { + $status=$sasl->Start($mechanisms,$message,$interactions); + } + while($status==SASL_INTERACT); + switch($status) + { + case SASL_CONTINUE: + break; + case SASL_NOMECH: + if(strlen($this->authentication_mechanism)) + { + $this->error="authenticated mechanism ".$this->authentication_mechanism." may not be used: ".$sasl->error; + return(0); + } + break; + default: + $this->error="Could not start the SASL authentication client: ".$sasl->error; + return(0); + } + if(strlen($mechanism=$sasl->mechanism)) + { + if($this->PutLine("AUTH ".$sasl->mechanism.(IsSet($message) ? " ".base64_encode($message) : ""))==0) + { + $this->error="Could not send the AUTH command"; + return(0); + } + if(!$this->VerifyResultLines(array("235","334"),$responses)) + return(0); + switch($this->result_code) + { + case "235": + $response=""; + $authenticated=1; + break; + case "334": + $response=base64_decode($responses[0]); + break; + default: + $this->error="Authentication error: ".$responses[0]; + return(0); + } + for(;!$authenticated;) + { + do + { + $status=$sasl->Step($response,$message,$interactions); + } + while($status==SASL_INTERACT); + switch($status) + { + case SASL_CONTINUE: + if($this->PutLine(base64_encode($message))==0) + { + $this->error="Could not send the authentication step message"; + return(0); + } + if(!$this->VerifyResultLines(array("235","334"),$responses)) + return(0); + switch($this->result_code) + { + case "235": + $response=""; + $authenticated=1; + break; + case "334": + $response=base64_decode($responses[0]); + break; + default: + $this->error="Authentication error: ".$responses[0]; + return(0); + } + break; + default: + $this->error="Could not process the SASL authentication step: ".$sasl->error; + return(0); + } + } + } + return(1); + } + + Function StartSMTP($localhost) + { + $success = 1; + $this->esmtp_extensions = array(); + $fallback=1; + if($this->esmtp + || strlen($this->user)) + { + if($this->PutLine('EHLO '.$localhost)) + { + if(($success_code=$this->VerifyResultLines('250',$responses))>0) + { + $this->esmtp_host=$this->Tokenize($responses[0]," "); + for($response=1;$response<count($responses);$response++) + { + $extension=strtoupper($this->Tokenize($responses[$response]," ")); + $this->esmtp_extensions[$extension]=$this->Tokenize(""); + } + $success=1; + $fallback=0; + } + else + { + if($success_code==0) + { + $code=$this->Tokenize($this->error," -"); + switch($code) + { + case "421": + $fallback=0; + break; + } + } + } + } + else + $fallback=0; + } + if($fallback) + { + if($this->PutLine("HELO $localhost") + && $this->VerifyResultLines("250",$responses)>0) + $success=1; + } + return($success); + } + + /* Public methods */ + +/* +{metadocument} + <function> + <name>Connect</name> + <type>BOOLEAN</type> + <documentation> + <purpose>Connect to an SMTP server.</purpose> + <usage>Call this function as first step to send e-mail messages.</usage> + <returnvalue>The function returns + <tt><booleanvalue>1</booleanvalue></tt> if the connection is + successfully established.</returnvalue> + </documentation> + <argument> + <name>domain</name> + <type>STRING</type> + <defaultvalue></defaultvalue> + <documentation> + <purpose>Specify the domain of the recipient when using the direct + delivery mode.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function Connect($domain="") + { + if(strcmp($this->state,"Disconnected")) + { + $this->error="connection is already established"; + return(0); + } + $this->disconnected_error=0; + $this->error=$error=""; + $this->esmtp_host=""; + $this->esmtp_extensions=array(); + $hosts=array(); + if($this->direct_delivery) + { + if(strlen($domain)==0) + return(1); + $hosts=$weights=$mxhosts=array(); + $getmxrr=$this->getmxrr; + if(function_exists($getmxrr) + && $getmxrr($domain,$hosts,$weights)) + { + for($host=0;$host<count($hosts);$host++) + $mxhosts[$weights[$host]]=$hosts[$host]; + KSort($mxhosts); + for(Reset($mxhosts),$host=0;$host<count($mxhosts);Next($mxhosts),$host++) + $hosts[$host]=$mxhosts[Key($mxhosts)]; + } + else + { + if(strcmp(@gethostbyname($domain),$domain)!=0) + $hosts[]=$domain; + } + } + else + { + if(strlen($this->host_name)) + $hosts[]=$this->host_name; + if(strlen($this->pop3_auth_host)) + { + $user=$this->user; + if(strlen($user)==0) + { + $this->error="it was not specified the POP3 authentication user"; + return(0); + } + $password=$this->password; + if(strlen($password)==0) + { + $this->error="it was not specified the POP3 authentication password"; + return(0); + } + $domain=$this->pop3_auth_host; + $this->error=$this->ConnectToHost($domain, $this->pop3_auth_port, "Resolving POP3 authentication host \"".$domain."\"..."); + if(strlen($this->error)) + return(0); + if(strlen($response=$this->GetLine())==0) + return(0); + if(strcmp($this->Tokenize($response," "),"+OK")) + { + $this->error="POP3 authentication server greeting was not found"; + return(0); + } + if(!$this->PutLine("USER ".$this->user) + || strlen($response=$this->GetLine())==0) + return(0); + if(strcmp($this->Tokenize($response," "),"+OK")) + { + $this->error="POP3 authentication user was not accepted: ".$this->Tokenize("\r\n"); + return(0); + } + if(!$this->PutLine("PASS ".$password) + || strlen($response=$this->GetLine())==0) + return(0); + if(strcmp($this->Tokenize($response," "),"+OK")) + { + $this->error="POP3 authentication password was not accepted: ".$this->Tokenize("\r\n"); + return(0); + } + fclose($this->connection); + $this->connection=0; + } + } + if(count($hosts)==0) + { + $this->error="could not determine the SMTP to connect"; + return(0); + } + for($host=0, $error="not connected";strlen($error) && $host<count($hosts);$host++) + { + $domain=$hosts[$host]; + $error=$this->ConnectToHost($domain, $this->host_port, "Resolving SMTP server domain \"$domain\"..."); + } + if(strlen($error)) + { + $this->error=$error; + return(0); + } + $timeout=($this->data_timeout ? $this->data_timeout : $this->timeout); + if($timeout + && function_exists("socket_set_timeout")) + socket_set_timeout($this->connection,$timeout,0); + if($this->debug) + $this->OutputDebug("Connected to SMTP server \"".$domain."\"."); + if(!strcmp($localhost=$this->localhost,"") + && !strcmp($localhost=getenv("SERVER_NAME"),"") + && !strcmp($localhost=getenv("HOST"),"")) + $localhost="localhost"; + $success=0; + if($this->VerifyResultLines("220",$responses)>0) + { + $success = $this->StartSMTP($localhost); + if($this->start_tls) + { + if(!IsSet($this->esmtp_extensions["STARTTLS"])) + { + $this->error="server does not support starting TLS"; + $success=0; + } + elseif(!function_exists('stream_socket_enable_crypto')) + { + $this->error="this PHP installation or version does not support starting TLS"; + $success=0; + } + elseif($success = ($this->PutLine('STARTTLS') + && $this->VerifyResultLines('220',$responses)>0)) + { + if($this->debug) + $this->OutputDebug('Starting TLS cryptograpic protocol'); + if(!($success = @stream_socket_enable_crypto($this->connection, 1, STREAM_CRYPTO_METHOD_TLS_CLIENT))) + $this->error = 'could not start TLS connection encryption protocol'; + else + { + if($this->debug) + $this->OutputDebug('TLS started'); + $success = $this->StartSMTP($localhost); + } + } + } + if($success + && strlen($this->user) + && strlen($this->pop3_auth_host)==0) + { + if(!IsSet($this->esmtp_extensions["AUTH"])) + { + $this->error="server does not require authentication"; + if(IsSet($this->esmtp_extensions["STARTTLS"])) + $this->error .= ', it probably requires starting TLS'; + $success=0; + } + else + { + if(strlen($this->authentication_mechanism)) + $mechanisms=array($this->authentication_mechanism); + else + { + $mechanisms=array(); + for($authentication=$this->Tokenize($this->esmtp_extensions["AUTH"]," ");strlen($authentication);$authentication=$this->Tokenize(" ")) + $mechanisms[]=$authentication; + } + $credentials=array( + "user"=>$this->user, + "password"=>$this->password + ); + if(strlen($this->realm)) + $credentials["realm"]=$this->realm; + if(strlen($this->workstation)) + $credentials["workstation"]=$this->workstation; + $success=$this->SASLAuthenticate($mechanisms,$credentials,$authenticated,$mechanism); + if(!$success + && !strcmp($mechanism,"PLAIN")) + { + /* + * Author: Russell Robinson, 25 May 2003, http://www.tectite.com/ + * Purpose: Try various AUTH PLAIN authentication methods. + */ + $mechanisms=array("PLAIN"); + $credentials=array( + "user"=>$this->user, + "password"=>$this->password + ); + if(strlen($this->realm)) + { + /* + * According to: http://www.sendmail.org/~ca/email/authrealms.html#authpwcheck_method + * some sendmails won't accept the realm, so try again without it + */ + $success=$this->SASLAuthenticate($mechanisms,$credentials,$authenticated,$mechanism); + } + if(!$success) + { + /* + * It was seen an EXIM configuration like this: + * user^password^unused + */ + $credentials["mode"]=SASL_PLAIN_EXIM_DOCUMENTATION_MODE; + $success=$this->SASLAuthenticate($mechanisms,$credentials,$authenticated,$mechanism); + } + if(!$success) + { + /* + * ... though: http://exim.work.de/exim-html-3.20/doc/html/spec_36.html + * specifies: ^user^password + */ + $credentials["mode"]=SASL_PLAIN_EXIM_MODE; + $success=$this->SASLAuthenticate($mechanisms,$credentials,$authenticated,$mechanism); + } + } + if($success + && strlen($mechanism)==0) + { + $this->error="it is not supported any of the authentication mechanisms required by the server"; + $success=0; + } + } + } + } + if($success) + { + $this->state="Connected"; + $this->connected_domain=$domain; + } + else + { + fclose($this->connection); + $this->connection=0; + } + return($success); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>MailFrom</name> + <type>BOOLEAN</type> + <documentation> + <purpose>Set the address of the message sender.</purpose> + <usage>Call this function right after establishing a connection with + the <functionlink>Connect</functionlink> function.</usage> + <returnvalue>The function returns + <tt><booleanvalue>1</booleanvalue></tt> if the sender address is + successfully set.</returnvalue> + </documentation> + <argument> + <name>sender</name> + <type>STRING</type> + <documentation> + <purpose>E-mail address of the sender.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function MailFrom($sender) + { + if($this->direct_delivery) + { + switch($this->state) + { + case "Disconnected": + $this->direct_sender=$sender; + return(1); + case "Connected": + $sender=$this->direct_sender; + break; + default: + $this->error="direct delivery connection is already established and sender is already set"; + return(0); + } + } + else + { + if(strcmp($this->state,"Connected")) + { + $this->error="connection is not in the initial state"; + return(0); + } + } + $this->error=""; + if(!$this->PutLine("MAIL FROM:<$sender>")) + return(0); + if(!IsSet($this->esmtp_extensions["PIPELINING"]) + && $this->VerifyResultLines("250",$responses)<=0) + return(0); + $this->state="SenderSet"; + if(IsSet($this->esmtp_extensions["PIPELINING"])) + $this->pending_sender=1; + $this->pending_recipients=0; + return(1); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>SetRecipient</name> + <type>BOOLEAN</type> + <documentation> + <purpose>Set the address of a message recipient.</purpose> + <usage>Call this function repeatedly for each recipient right after + setting the message sender with the + <functionlink>MailFrom</functionlink> function.</usage> + <returnvalue>The function returns + <tt><booleanvalue>1</booleanvalue></tt> if the recipient address is + successfully set.</returnvalue> + </documentation> + <argument> + <name>recipient</name> + <type>STRING</type> + <documentation> + <purpose>E-mail address of a recipient.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function SetRecipient($recipient) + { + if($this->direct_delivery) + { + if(GetType($at=strrpos($recipient,"@"))!="integer") + return("it was not specified a valid direct recipient"); + $domain=substr($recipient,$at+1); + switch($this->state) + { + case "Disconnected": + if(!$this->Connect($domain)) + return(0); + if(!$this->MailFrom("")) + { + $error=$this->error; + $this->Disconnect(); + $this->error=$error; + return(0); + } + break; + case "SenderSet": + case "RecipientSet": + if(strcmp($this->connected_domain,$domain)) + { + $this->error="it is not possible to deliver directly to recipients of different domains"; + return(0); + } + break; + default: + $this->error="connection is already established and the recipient is already set"; + return(0); + } + } + else + { + switch($this->state) + { + case "SenderSet": + case "RecipientSet": + break; + default: + $this->error="connection is not in the recipient setting state"; + return(0); + } + } + $this->error=""; + if(!$this->PutLine("RCPT TO:<$recipient>")) + return(0); + if(IsSet($this->esmtp_extensions["PIPELINING"])) + { + $this->pending_recipients++; + if($this->pending_recipients>=$this->maximum_piped_recipients) + { + if(!$this->FlushRecipients()) + return(0); + } + } + else + { + if($this->VerifyResultLines(array("250","251"),$responses)<=0) + return(0); + } + $this->state="RecipientSet"; + return(1); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>StartData</name> + <type>BOOLEAN</type> + <documentation> + <purpose>Tell the SMTP server that the message data will start being + sent.</purpose> + <usage>Call this function right after you are done setting all the + message recipients with the + <functionlink>SetRecipient</functionlink> function.</usage> + <returnvalue>The function returns + <tt><booleanvalue>1</booleanvalue></tt> if the server is ready to + start receiving the message data.</returnvalue> + </documentation> + <do> +{/metadocument} +*/ + Function StartData() + { + if(strcmp($this->state,"RecipientSet")) + { + $this->error="connection is not in the start sending data state"; + return(0); + } + $this->error=""; + if(!$this->PutLine("DATA")) + return(0); + if($this->pending_recipients) + { + if(!$this->FlushRecipients()) + return(0); + } + if($this->VerifyResultLines("354",$responses)<=0) + return(0); + $this->state="SendingData"; + return(1); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>PrepareData</name> + <type>STRING</type> + <documentation> + <purpose>Prepare message data to normalize line breaks and escaping + lines that contain single dots.</purpose> + <usage>Call this function if the message data you want to send may + contain line breaks that are not the + <stringvalue> </stringvalue> sequence or it may contain + lines that just have a single dot.</usage> + <returnvalue>Resulting normalized messages data.</returnvalue> + </documentation> + <argument> + <name>data</name> + <type>STRING</type> + <documentation> + <purpose>Message data to be prepared.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function PrepareData($data) + { + return(preg_replace(array("/\n\n|\r\r/","/(^|[^\r])\n/","/\r([^\n]|\$)/D","/(^|\n)\\./"),array("\r\n\r\n","\\1\r\n","\r\n\\1","\\1.."),$data)); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>SendData</name> + <type>BOOLEAN</type> + <documentation> + <purpose>Send message data.</purpose> + <usage>Call this function repeatedly for all message data blocks + to be sent right after start sending message data with the + <functionlink>StartData</functionlink> function.</usage> + <returnvalue>The function returns + <tt><booleanvalue>1</booleanvalue></tt> if the message data was + sent to the SMTP server successfully.</returnvalue> + </documentation> + <argument> + <name>data</name> + <type>STRING</type> + <documentation> + <purpose>Message data to be sent.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function SendData($data) + { + if(strcmp($this->state,"SendingData")) + { + $this->error="connection is not in the sending data state"; + return(0); + } + $this->error=""; + return($this->PutData($data)); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>EndSendingData</name> + <type>BOOLEAN</type> + <documentation> + <purpose>Tell the server that all the message data was sent.</purpose> + <usage>Call this function when you are done with sending the message + data with the <functionlink>SendData</functionlink> function.</usage> + <returnvalue>The function returns + <tt><booleanvalue>1</booleanvalue></tt> if the server accepted the + message.</returnvalue> + </documentation> + <do> +{/metadocument} +*/ + Function EndSendingData() + { + if(strcmp($this->state,"SendingData")) + { + $this->error="connection is not in the sending data state"; + return(0); + } + $this->error=""; + if(!$this->PutLine("\r\n.") + || $this->VerifyResultLines("250",$responses)<=0) + return(0); + $this->state="Connected"; + return(1); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>ResetConnection</name> + <type>BOOLEAN</type> + <documentation> + <purpose>Reset an already established SMTP connection to the initial + state.</purpose> + <usage>Call this function when there was an error sending a message + and you need to skip to sending another message without + disconnecting.</usage> + <returnvalue>The function returns + <tt><booleanvalue>1</booleanvalue></tt> if the connection was + resetted successfully.</returnvalue> + </documentation> + <do> +{/metadocument} +*/ + Function ResetConnection() + { + switch($this->state) + { + case "Connected": + return(1); + case "SendingData": + $this->error="can not reset the connection while sending data"; + return(0); + case "Disconnected": + $this->error="can not reset the connection before it is established"; + return(0); + } + $this->error=""; + if(!$this->PutLine("RSET") + || $this->VerifyResultLines("250",$responses)<=0) + return(0); + $this->state="Connected"; + return(1); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>Disconnect</name> + <type>BOOLEAN</type> + <documentation> + <purpose>Terminate a previously opened connection.</purpose> + <usage>Call this function after you are done sending your + messages.</usage> + <returnvalue>The function returns + <tt><booleanvalue>1</booleanvalue></tt> if the connection was + successfully closed.</returnvalue> + </documentation> + <argument> + <name>quit</name> + <type>BOOLEAN</type> + <defaultvalue>1</defaultvalue> + <documentation> + <purpose>Boolean option that tells whether the class should + perform the final connection quit handshake, or just close the + connection without waiting.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function Disconnect($quit=1) + { + if(!strcmp($this->state,"Disconnected")) + { + $this->error="it was not previously established a SMTP connection"; + return(0); + } + $this->error=""; + if(!strcmp($this->state,"Connected") + && $quit + && (!$this->PutLine("QUIT") + || ($this->VerifyResultLines("221",$responses)<=0 + && !$this->disconnected_error))) + return(0); + if($this->disconnected_error) + $this->disconnected_error=0; + else + fclose($this->connection); + $this->connection=0; + $this->state="Disconnected"; + if($this->debug) + $this->OutputDebug("Disconnected."); + return(1); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>SendMessage</name> + <type>BOOLEAN</type> + <documentation> + <purpose>Send a message in a single call.</purpose> + <usage>Call this function if you want to send a single messages to a + small number of recipients in a single call.</usage> + <returnvalue>The function returns + <tt><booleanvalue>1</booleanvalue></tt> if the message was sent + successfully.</returnvalue> + </documentation> + <argument> + <name>sender</name> + <type>STRING</type> + <documentation> + <purpose>E-mail address of the sender.</purpose> + </documentation> + </argument> + <argument> + <name>recipients</name> + <type>STRING</type> + <documentation> + <purpose>Array with a list of the e-mail addresses of the + recipients of the message.</purpose> + </documentation> + </argument> + <argument> + <name>headers</name> + <type>ARRAY</type> + <documentation> + <purpose>Array with a list of the header lines of the message.</purpose> + </documentation> + </argument> + <argument> + <name>body</name> + <type>STRING</type> + <documentation> + <purpose>Body data of the message.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function SendMessage($sender,$recipients,$headers,$body) + { + if(($success=$this->Connect())) + { + if(($success=$this->MailFrom($sender))) + { + for($recipient=0;$recipient<count($recipients);$recipient++) + { + if(!($success=$this->SetRecipient($recipients[$recipient]))) + break; + } + if($success + && ($success=$this->StartData())) + { + for($header_data="",$header=0;$header<count($headers);$header++) + $header_data.=$headers[$header]."\r\n"; + $success=($this->SendData($header_data."\r\n") + && $this->SendData($this->PrepareData($body)) + && $this->EndSendingData()); + } + } + $error=$this->error; + $disconnect_success=$this->Disconnect($success); + if($success) + $success=$disconnect_success; + else + $this->error=$error; + } + return($success); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +}; + +/* + +{metadocument} +</class> +{/metadocument} + +*/ + +?>
\ No newline at end of file diff --git a/vendor/email_message/smtp_message.php b/vendor/email_message/smtp_message.php new file mode 100644 index 0000000..218502d --- /dev/null +++ b/vendor/email_message/smtp_message.php @@ -0,0 +1,782 @@ +<?php +/* + * smtp_message.php + * + * @(#) $Header: /opt2/ena/metal/mimemessage/smtp_message.php,v 1.36 2011/03/09 07:48:52 mlemos Exp $ + * + * + */ + +/* +{metadocument}<?xml version="1.0" encoding="ISO-8859-1"?> +<class> + + <package>net.manuellemos.mimemessage</package> + + <name>smtp_message_class</name> + <version>@(#) $Id: smtp_message.php,v 1.36 2011/03/09 07:48:52 mlemos Exp $</version> + <copyright>Copyright © (C) Manuel Lemos 1999-2004</copyright> + <title>MIME E-mail message composing and sending via SMTP</title> + <author>Manuel Lemos</author> + <authoraddress>mlemos-at-acm.org</authoraddress> + + <documentation> + <idiom>en</idiom> + <purpose>Implement an alternative message delivery method via SMTP + protocol, overriding the method of using the PHP <tt>mail()</tt> + function implemented by the base class.</purpose> + <usage>This class should be used exactly the same way as the base + class for composing and sending messages. Just create a new object of + this class as follows and set only the necessary variables to + configure details of the SMTP delivery.<paragraphbreak /> + <tt>require('email_message.php');<br /> + require('smtp.php');<br /> + require('smtp_message.php');<br /> + <br /> + $message_object = new smtp_message_class;<br /></tt><paragraphbreak /> + <b>- Requirements</b><paragraphbreak /> + You need the <link> + <data>SMTP E-mail sending class</data> + <url>http://freshmeat.net/projects/smtpclass/</url> + </link> to perform the actual message delivery via the SMTP + protocol.<paragraphbreak /> + <b>- SMTP connection</b><paragraphbreak /> + Before sending a message by relaying it to a given SMTP server you + need set the <variablelink>smtp_host</variablelink> variable to that + server address. The <variablelink>localhost</variablelink> variable + needs to be set to the sending computer address.<paragraphbreak /> + You may also adjust the time the class will wait for establishing + a connection by changing the <variablelink>timeout</variablelink> + variable.<paragraphbreak /> + <b>- Secure SMTP connections with SSL</b><paragraphbreak /> + Some SMTP servers, like for instance Gmail, require secure + connections via SSL. In that case it is necessary to set the + <variablelink>smtp_ssl</variablelink> variable to + <booleanvalue>1</booleanvalue>. In the case of Gmail, it is also + necessary to set the connection port changing the + <variablelink>smtp_port</variablelink> variable to + <integervalue>465</integervalue>.<paragraphbreak /> + SSL support requires at least PHP 4.3.0 with OpenSSL extension + enabled.<paragraphbreak /> + <b>- Secure SMTP connections starting TLS after connections is established</b><paragraphbreak /> + Some SMTP servers, like for instance Hotmail, require starting the + TLS protocol after the connection is already established to exchange + data securely. In that case it is necessary to set the + <variablelink>smtp_start_tls</variablelink> variable to + <booleanvalue>1</booleanvalue>.<paragraphbreak /> + Starting TLS protocol on an already established connection requires + at least PHP 5.1.0 with OpenSSL extension enabled.<paragraphbreak /> + <b>- Authentication</b><paragraphbreak /> + Most servers only allow relaying messages sent by authorized + users. If the SMTP server that you want to use requires + authentication, you need to set the variables + <variablelink>smtp_user</variablelink>, + <variablelink>smtp_realm</variablelink> and + <variablelink>smtp_password</variablelink>.<paragraphbreak /> + The way these values need to be set depends on the server. Usually + the realm value is empty and only the user and password need to be + set. If the server requires authentication via <tt>NTLM</tt> + mechanism (Windows or Samba), you need to set the + <variablelink>smtp_realm</variablelink> to the Windows domain name + and also set the variable + <variablelink>smtp_workstation</variablelink> to the user workstation + name.<paragraphbreak /> + Some servers require that the authentication be done on a separate + server using the POP3 protocol before connecting to the SMTP server. + In this case you need to specify the address of the POP3 server + setting the <variablelink>smtp_pop3_auth_host</variablelink> + variable.<paragraphbreak /> + <b>- Sending urgent messages with direct delivery</b><paragraphbreak /> + If you need to send urgent messages or obtain immediate confirmation + that a message is accepted by the recipient SMTP server, you can use + the direct delivery mode setting the + <variablelink>direct_delivery</variablelink> variable to + <tt><booleanvalue>1</booleanvalue></tt>. This mode can be used to + send a message to only one recipient.<paragraphbreak /> + To use this mode, it is necessary to have a way to determine the + recipient domain SMTP server address. The class uses the PHP + <tt>getmxrr()</tt> function, but on some systems like for instance + under Windows, this function does not work. In this case you may + specify an equivalent alternative by setting the + <variablelink>smtp_getmxrr</variablelink> variable. See the SMTP + class page for available alternatives.<paragraphbreak /> + <b>- Troubleshooting and debugging</b><paragraphbreak /> + If for some reason the delivery via SMTP is not working and the error + messages are not self-explanatory, you may set the + <variablelink>smtp_debug</variablelink> to + <tt><booleanvalue>1</booleanvalue></tt> to make the class output the + SMTP protocol dialog with the server. If you want to display this + dialog properly formatted in an HTML page, also set the + <variablelink>smtp_debug</variablelink> to + <tt><booleanvalue>1</booleanvalue></tt>.<paragraphbreak /> + <b>- Optimizing the delivery of messages to many recipients</b><paragraphbreak /> + When sending messages to many recipients, this class can hinted to + optimize its behavior by using the + <functionlink>SetBulkMail</functionlink> function. After calling this + function passing <booleanvalue>1</booleanvalue> to the <argumentlink> + <function>SetBulkMail</function> + <argument>on</argument> + </argumentlink> argument, when the message is sent this class opens + a TCP connection to the SMTP server but will not close it. This + avoids the overhead of opening and closing connections.<paragraphbreak /> + When the delivery of the messages to all recipients is done, the + connection may be closed implicitly by calling the + <functionlink>SetBulkMail</functionlink> function again passing + <booleanvalue>0</booleanvalue> to the <argumentlink> + <function>SetBulkMail</function> + <argument>on</argument> + </argumentlink> argument.</usage> + </documentation> + +{/metadocument} +*/ + +class smtp_message_class extends email_message_class +{ + /* Private variables */ + + var $smtp; + var $line_break="\r\n"; + var $delivery = 0; + + /* Public variables */ + +/* +{metadocument} + <variable> + <name>localhost</name> + <value></value> + <documentation> + <purpose>Specify the domain name of the computer sending the + message.</purpose> + <usage>This value is used to identify the sending machine to the + SMTP server. When using the direct delivery mode, if this variable + is set to a non-empty string it used to generate the + <tt>Recieved</tt> header to show that the message passed by the + specified host address. To prevent confusing directly delivered + messages with spam, it is strongly recommended that you set this + variable to you server host name.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $localhost=""; + +/* +{metadocument} + <variable> + <name>smtp_host</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Specify the address of the SMTP server.</purpose> + <usage>Set to the address of the SMTP server that will relay the + messages. This variable is not used in direct delivery mode.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $smtp_host="localhost"; + +/* +{metadocument} + <variable> + <name>smtp_port</name> + <type>INTEGER</type> + <value>25</value> + <documentation> + <purpose>Specify the TCP/IP port of SMTP server to connect.</purpose> + <usage>Most servers work on port 25 . Certain e-mail services use + alternative ports to avoid firewall blocking. Gmail uses port + <integervalue>465</integervalue>.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $smtp_port=25; + +/* +{metadocument} + <variable> + <name>smtp_ssl</name> + <type>BOOLEAN</type> + <value>0</value> + <documentation> + <purpose>Specify whether it should use secure connections with SSL + to connect to the SMTP server.</purpose> + <usage>Certain e-mail services like Gmail require SSL connections.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $smtp_ssl=0; + +/* +{metadocument} + <variable> + <name>smtp_start_tls</name> + <type>BOOLEAN</type> + <value>0</value> + <documentation> + <purpose>Specify whether it should use secure connections starting + TLS protocol after connecting to the SMTP server.</purpose> + <usage>Certain e-mail services like Hotmail require starting TLS + protocol after the connection to the SMTP server is already + established.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $smtp_start_tls=0; + +/* +{metadocument} + <variable> + <name>smtp_http_proxy_host_name</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Specify name of the host when the connection should be + routed via an HTTP proxy.</purpose> + <usage>Leave empty if no proxy should be used.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $smtp_http_proxy_host_name=''; + +/* +{metadocument} + <variable> + <name>smtp_http_proxy_host_port</name> + <type>INTEGER</type> + <value>3128</value> + <documentation> + <purpose>Specify proxy port when the connection should be routed via + an HTTP proxy.</purpose> + <usage>Change this variable if you need to use a proxy with a + specific port.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $smtp_http_proxy_host_port=3128; + +/* +{metadocument} + <variable> + <name>smtp_socks_host_name</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Specify name of the host when the connection should be + routed via a SOCKS protocol proxy.</purpose> + <usage>Leave empty if no proxy should be used.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $smtp_socks_host_name = ''; + +/* +{metadocument} + <variable> + <name>smtp_socks_host_port</name> + <type>INTEGER</type> + <value>1080</value> + <documentation> + <purpose>Specify proxy port when the connection should be routed via + a SOCKS protocol proxy.</purpose> + <usage>Change this variable if you need to use a proxy with a + specific port.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $smtp_socks_host_port = 1080; + +/* +{metadocument} + <variable> + <name>smtp_socks_version</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Specify protocol version when the connection should be + routed via a SOCKS protocol proxy.</purpose> + <usage>Change this variable if you need to use a proxy with a + specific SOCKS protocol version.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $smtp_socks_version = '5'; + +/* +{metadocument} + <variable> + <name>smtp_direct_delivery</name> + <type>BOOLEAN</type> + <value>0</value> + <documentation> + <purpose>Boolean flag that indicates whether the message should be + sent in direct delivery mode.</purpose> + <usage>Set this to <tt><booleanvalue>1</booleanvalue></tt> if you + want to send urgent messages directly to the recipient domain SMTP + server.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $smtp_direct_delivery=0; + +/* +{metadocument} + <variable> + <name>smtp_getmxrr</name> + <type>STRING</type> + <value>getmxrr</value> + <documentation> + <purpose>Specify the name of the function that is called to determine + the SMTP server address of a given domain.</purpose> + <usage>Change this to a working replacement of the PHP + <tt>getmxrr()</tt> function if this is not working in your system + and you want to send messages in direct delivery mode.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $smtp_getmxrr="getmxrr"; + +/* +{metadocument} + <variable> + <name>smtp_exclude_address</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Specify an address that should be considered invalid + when resolving host name addresses.</purpose> + <usage>In some networks any domain name that does not exist is + resolved as a sub-domain of the default local domain. If the DNS is + configured in such way that it always resolves any sub-domain of + the default local domain to a given address, it is hard to + determine whether a given domain does not exist.<paragraphbreak /> + If your network is configured this way, you may set this variable + to the address that all sub-domains of the default local domain + resolves, so the class can assume that such address is invalid.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $smtp_exclude_address=""; + +/* +{metadocument} + <variable> + <name>smtp_user</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Specify the user name for authentication.</purpose> + <usage>Set this variable if you need to authenticate before sending + a message.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $smtp_user=""; + +/* +{metadocument} + <variable> + <name>smtp_realm</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Specify the user authentication realm.</purpose> + <usage>Set this variable if you need to authenticate before sending + a message.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $smtp_realm=""; + +/* +{metadocument} + <variable> + <name>smtp_workstation</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Specify the user authentication workstation needed when + using the <tt>NTLM</tt> authentication (Windows or Samba).</purpose> + <usage>Set this variable if you need to authenticate before sending + a message.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $smtp_workstation=""; + +/* +{metadocument} + <variable> + <name>smtp_authentication_mechanism</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Specify the user authentication mechanism that should be + used when authenticating with the SMTP server.</purpose> + <usage>Set this variable if you need to force the SMTP connection to + authenticate with a specific authentication mechanism. Leave this + variable with an empty string if you want the authentication + mechanism be determined automatically from the list of mechanisms + supported by the server.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $smtp_authentication_mechanism=""; + +/* +{metadocument} + <variable> + <name>smtp_password</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Specify the user authentication password.</purpose> + <usage>Set this variable if you need to authenticate before sending + a message.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $smtp_password=""; + +/* +{metadocument} + <variable> + <name>smtp_pop3_auth_host</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Specify the server address for POP3 based authentication.</purpose> + <usage>Set this variable to the address of the POP3 server if the + SMTP server requires POP3 based authentication.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $smtp_pop3_auth_host=""; + +/* +{metadocument} + <variable> + <name>smtp_debug</name> + <type>BOOLEAN</type> + <value>0</value> + <documentation> + <purpose>Specify whether it is necessary to output SMTP connection + debug information.</purpose> + <usage>Set this variable to + <tt><booleanvalue>1</booleanvalue></tt> if you need to see + the progress of the SMTP connection and protocol dialog when you + need to understand the reason for delivery problems.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $smtp_debug=0; + +/* +{metadocument} + <variable> + <name>smtp_html_debug</name> + <type>BOOLEAN</type> + <value>0</value> + <documentation> + <purpose>Specify whether the debug information should be outputted in + HTML format.</purpose> + <usage>Set this variable to + <tt><booleanvalue>1</booleanvalue></tt> if you need to see + the debug output in a Web page.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $smtp_html_debug=0; + +/* +{metadocument} + <variable> + <name>esmtp</name> + <type>BOOLEAN</type> + <value>1</value> + <documentation> + <purpose>Specify whether the class should try to use Enhanced SMTP + protocol features.</purpose> + <usage>It is recommended to leave this variable set to + <tt><booleanvalue>1</booleanvalue></tt> so the class can take + advantage of Enhanced SMTP protocol features.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $esmtp=1; + +/* +{metadocument} + <variable> + <name>timeout</name> + <type>INTEGER</type> + <value>25</value> + <documentation> + <purpose>Specify the connection timeout period in seconds.</purpose> + <usage>Change this value if for some reason the timeout period seems + insufficient or otherwise it seems too long.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $timeout=25; + +/* +{metadocument} + <variable> + <name>invalid_recipients</name> + <type>ARRAY</type> + <value></value> + <documentation> + <purpose>Return the list of recipient addresses that were not + accepted by the SMTP server.</purpose> + <usage>Check this variable after attempting to send a message to + figure whether there were any recipients that were rejected by the + SMTP server.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $invalid_recipients=array(); + +/* +{metadocument} + <variable> + <name>mailer_delivery</name> + <value>smtp $Revision: 1.36 $</value> + <documentation> + <purpose>Specify the text that is used to identify the mail + delivery class or sub-class. This text is appended to the + <tt>X-Mailer</tt> header text defined by the + mailer variable.</purpose> + <usage>Do not change this variable.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $mailer_delivery='smtp $Revision: 1.36 $'; + +/* +{metadocument} + <variable> + <name>maximum_bulk_deliveries</name> + <type>INTEGER</type> + <value>100</value> + <documentation> + <purpose>Specify the number of consecutive bulk mail deliveries + without disconnecting.</purpose> + <usage>Lower this value if you have enabled the bulk mail mode but + the SMTP server does not accept sending more than a number of + messages within the same SMTP connection.<paragraphbreak /> + Set this value to <integervalue>0</integervalue> to never + disconnect during bulk mail mode unless an error occurs.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $maximum_bulk_deliveries=100; + + Function SetRecipients(&$recipients,&$valid_recipients) + { + for($valid_recipients=$recipient=0,Reset($recipients);$recipient<count($recipients);Next($recipients),$recipient++) + { + $address=Key($recipients); + if($this->smtp->SetRecipient($address)) + $valid_recipients++; + else + $this->invalid_recipients[$address]=$this->smtp->error; + } + return(1); + } + + Function ResetConnection($error) + { + if(IsSet($this->smtp)) + { + if(!$this->smtp->Disconnect() + && strlen($error) == 0) + $error = $this->smtp->error; + UnSet($this->smtp); + } + if(strlen($error)) + $this->OutputError($error); + return($error); + } + + Function StartSendingMessage() + { + if(function_exists("class_exists") + && !class_exists("smtp_class")) + return("the smtp_class class was not included"); + if(IsSet($this->smtp)) + return(""); + $this->smtp=new smtp_class; + $this->smtp->localhost=$this->localhost; + $this->smtp->host_name=$this->smtp_host; + $this->smtp->host_port=$this->smtp_port; + $this->smtp->ssl=$this->smtp_ssl; + $this->smtp->start_tls=$this->smtp_start_tls; + $this->smtp->http_proxy_host_name=$this->smtp_http_proxy_host_name; + $this->smtp->http_proxy_host_port=$this->smtp_http_proxy_host_port; + $this->smtp->socks_host_name=$this->smtp_socks_host_name; + $this->smtp->socks_host_port=$this->smtp_socks_host_port; + $this->smtp->socks_version=$this->smtp_socks_version; + $this->smtp->timeout=$this->timeout; + $this->smtp->debug=$this->smtp_debug; + $this->smtp->html_debug=$this->smtp_html_debug; + $this->smtp->direct_delivery=$this->smtp_direct_delivery; + $this->smtp->getmxrr=$this->smtp_getmxrr; + $this->smtp->exclude_address=$this->smtp_exclude_address; + $this->smtp->pop3_auth_host=$this->smtp_pop3_auth_host; + $this->smtp->user=$this->smtp_user; + $this->smtp->realm=$this->smtp_realm; + $this->smtp->workstation=$this->smtp_workstation; + $this->smtp->authentication_mechanism=$this->smtp_authentication_mechanism; + $this->smtp->password=$this->smtp_password; + $this->smtp->esmtp=$this->esmtp; + if($this->smtp->Connect()) + { + $this->delivery = 0; + return(""); + } + return($this->ResetConnection($this->smtp->error)); + } + + Function SendMessageHeaders($headers) + { + $header_data=""; + $date=date("r"); + if($this->smtp_direct_delivery + && strlen($this->localhost)) + { + $local_ip=gethostbyname($this->localhost); + $header_data.=$this->FormatHeader("Received","FROM ".$this->localhost." ([".$local_ip."]) BY ".$this->localhost." ([".$local_ip."]) WITH SMTP; ".$date)."\r\n"; + } + for($message_id_set=$date_set=0,$header=0,$return_path=$from=$to=$recipients=array(),Reset($headers);$header<count($headers);$header++,Next($headers)) + { + $header_name=Key($headers); + switch(strtolower($header_name)) + { + case "return-path": + $return_path[$headers[$header_name]]=1; + break; + case "from": + $error=$this->GetRFC822Addresses($headers[$header_name],$from); + break; + case "to": + $error=$this->GetRFC822Addresses($headers[$header_name],$to); + break; + case "cc": + case "bcc": + $this->GetRFC822Addresses($headers[$header_name],$recipients); + break; + case "date": + $date_set=1; + break; + case "message-id": + $message_id_set=1; + break; + } + if(strcmp($error,"")) + return($this->ResetConnection($error)); + if(strtolower($header_name)=="bcc") + continue; + $header_data.=$this->FormatHeader($header_name,$headers[$header_name])."\r\n"; + } + if(count($from)==0) + return($this->ResetConnection("it was not specified a valid From header")); + Reset($return_path); + Reset($from); + $this->invalid_recipients=array(); + if(!$this->smtp->MailFrom(count($return_path) ? Key($return_path) : Key($from))) + return($this->ResetConnection($this->smtp->error)); + $r = 0; + if(count($to)) + { + if(!$this->SetRecipients($to,$valid_recipients)) + return($this->ResetConnection($this->smtp->error)); + $r += $valid_recipients; + } + if(!$date_set) + $header_data.="Date: ".$date."\r\n"; + if(!$message_id_set + && $this->auto_message_id) + { + $sender=(count($return_path) ? Key($return_path) : Key($from)); + $header_data.=$this->GenerateMessageID($sender)."\r\n"; + } + if(count($recipients)) + { + if(!$this->SetRecipients($recipients,$valid_recipients)) + return($this->ResetConnection($this->smtp->error)); + $r += $valid_recipients; + } + if($r==0) + return($this->ResetConnection("it were not specified any valid recipients")); + if(!$this->smtp->StartData() + || !$this->smtp->SendData($header_data."\r\n")) + return($this->ResetConnection($this->smtp->error)); + return(""); + } + + Function SendMessageBody($data) + { + return($this->smtp->SendData($this->smtp->PrepareData($data)) ? "" : $this->ResetConnection($this->smtp->error)); + } + + Function EndSendingMessage() + { + return($this->smtp->EndSendingData() ? "" : $this->ResetConnection($this->smtp->error)); + } + + Function StopSendingMessage() + { + ++$this->delivery; + if($this->bulk_mail + && !$this->smtp_direct_delivery + && ($this->maximum_bulk_deliveries == 0 + || $this->delivery < $this->maximum_bulk_deliveries)) + return(""); + return($this->ResetConnection('')); + } + + Function ChangeBulkMail($on) + { + if($on + || !IsSet($this->smtp)) + return(1); + return($this->smtp->Disconnect() ? "" : $this->ResetConnection($this->smtp->error)); + } +}; + +/* + +{metadocument} +</class> +{/metadocument} + +*/ + +?> diff --git a/vendor/exTpl/Context.php b/vendor/exTpl/Context.php new file mode 100644 index 0000000..222c17f --- /dev/null +++ b/vendor/exTpl/Context.php @@ -0,0 +1,53 @@ +<?php +/* + * Context.php - template parser symbol table + * + * Copyright (c) 2013 Elmar Ludwig + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +namespace exTpl; + +/** + * A Context object represents the symbol table used to resolve + * symbol names to their values in the local scope. Each context + * may inherit symbol definitions from its parent context. + */ +class Context +{ + private $bindings; + private $parent; + + /** + * Initializes a new Context instance with the given bindings. + * + * @param array $bindings symbol table + * @param Context $parent parent context (or NULL) + */ + public function __construct($bindings, Context $parent = NULL) + { + $this->bindings = $bindings; + $this->parent = $parent; + } + + /** + * Looks up the value of a symbol in this context and returns it. + * The reserved symbol "this" is an alias for the current context. + * + * @param string $key symbol name + */ + public function lookup($key) + { + if (isset($this->bindings[$key])) { + return $this->bindings[$key]; + } else if ($this->parent) { + return $this->parent->lookup($key); + } + + return NULL; + } +} diff --git a/vendor/exTpl/Expression.php b/vendor/exTpl/Expression.php new file mode 100644 index 0000000..0885f4e --- /dev/null +++ b/vendor/exTpl/Expression.php @@ -0,0 +1,263 @@ +<?php +/* + * Expression.php - template parser expression interface and classes + * + * Copyright (c) 2013 Elmar Ludwig + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +namespace exTpl; + +/** + * Basic interface for expressions in the template parse tree. The + * only required method is "value" to get the expression's value. + */ +interface Expression +{ + /** + * Returns the value of this expression. + * + * @param Context $context symbol table + */ + public function value($context); +} + +/** + * ConstantExpression represents a literal value. + */ +class ConstantExpression implements Expression +{ + protected $value; + + /** + * Initializes a new Expression instance. + * + * @param mixed $value expression value + */ + public function __construct($value) + { + $this->value = $value; + } + + /** + * Returns the value of this expression. + * + * @param Context $context symbol table + */ + public function value($context) + { + return $this->value; + } +} + +/** + * SymbolExpression represents a symbol (template variable). + */ +class SymbolExpression implements Expression +{ + protected $name; + + /** + * Initializes a new Expression instance. + * + * @param string $name symbol name + */ + public function __construct($name) + { + $this->name = $name; + } + + /** + * Returns the value of this expression. + * + * @param Context $context symbol table + */ + public function value($context) + { + return $context->lookup($this->name); + } +} + +/** + * UnaryExpression represents a unary operator. + */ +abstract class UnaryExpression implements Expression +{ + protected $expr; + + /** + * Initializes a new Expression instance. + * + * @param Expression $expr expression object + */ + public function __construct(Expression $expr) + { + $this->expr = $expr; + } +} + +/** + * MinusExpression represents the unary minus operator ('-'). + */ +class MinusExpression extends UnaryExpression +{ + /** + * Returns the value of this expression. + * + * @param Context $context symbol table + */ + public function value($context) + { + return -$this->expr->value($context); + } +} + +/** + * NotExpression represents the logical negation operator ('!'). + */ +class NotExpression extends UnaryExpression +{ + /** + * Returns the value of this expression. + * + * @param Context $context symbol table + */ + public function value($context) + { + return !$this->expr->value($context); + } +} + +/** + * BinaryExpression represents a binary operator. + */ +abstract class BinaryExpression implements Expression +{ + protected $left, $right; + protected $operator; + + /** + * Initializes a new Expression instance. + * + * @param Expression $left left operand + * @param Expression $right right operand + * @param mixed $operator operator token + */ + public function __construct(Expression $left, Expression $right, $operator) + { + $this->left = $left; + $this->right = $right; + $this->operator = $operator; + } +} + +/** + * ArithExpression represents an arithmetic operator. + */ +class ArithExpression extends BinaryExpression +{ + /** + * Returns the value of this expression. + * + * @param Context $context symbol table + */ + public function value($context) + { + $left = $this->left->value($context); + $right = $this->right->value($context); + + switch ($this->operator) { + case '+': return $left + $right; + case '-': return $left - $right; + case '*': return $left * $right; + case '/': return $left / $right; + case '%': return $left % $right; + case '~': return $left . $right; + } + } +} + +/** + * IndexExpression represents the array index operator. + */ +class IndexExpression extends BinaryExpression +{ + /** + * Returns the value of this expression. + * + * @param Context $context symbol table + */ + public function value($context) + { + $left = $this->left->value($context); + $right = $this->right->value($context); + + return $left[$right]; + } +} + +/** + * BooleanExpression represents a boolean operator. + */ +class BooleanExpression extends BinaryExpression +{ + /** + * Returns the value of this expression. + * + * @param Context $context symbol table + */ + public function value($context) + { + $left = $this->left->value($context); + $right = $this->right->value($context); + + switch ($this->operator) { + case T_IS_EQUAL : return $left == $right; + case T_IS_NOT_EQUAL : return $left != $right; + case '<' : return $left < $right; + case T_IS_SMALLER_OR_EQUAL: return $left <= $right; + case '>' : return $left > $right; + case T_IS_GREATER_OR_EQUAL: return $left >= $right; + case T_BOOLEAN_AND : return $left && $right; + case T_BOOLEAN_OR : return $left || $right; + } + } +} + +/** + * ConditionExpression represents the conditional operator ('?:'). + */ +class ConditionExpression implements Expression +{ + protected $condition; + protected $left, $right; + + /** + * Initializes a new Expression instance. + * + * @param Expression $condition expression + * @param Expression $left left alternative + * @param Expression $right right alternative + */ + public function __construct($condition, $left, $right) + { + $this->condition = $condition; + $this->left = $left; + $this->right = $right; + } + + /** + * Returns the value of this expression. + * + * @param Context $context symbol table + */ + public function value($context) + { + return $this->condition->value($context) ? + $this->left->value($context) : $this->right->value($context); + } +} diff --git a/vendor/exTpl/Node.php b/vendor/exTpl/Node.php new file mode 100644 index 0000000..5fdada2 --- /dev/null +++ b/vendor/exTpl/Node.php @@ -0,0 +1,222 @@ +<?php +/* + * Node.php - template parser node interface and classes + * + * Copyright (c) 2013 Elmar Ludwig + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +namespace exTpl; + +require_once 'Context.php'; +require_once 'Expression.php'; + +/** + * Basic interface for nodes in the template parse tree. The only + * required method is "render" to render a node and its children. + */ +interface Node +{ + /** + * Returns a string representation of this node. + * + * @param Context $context symbol table + */ + public function render($context); +} + +/** + * TextNode represents a verbatim text segment. + */ +class TextNode implements Node +{ + protected $text; + + /** + * Initializes a new Node instance with the given text. + * + * @param string $text verbatim text + */ + public function __construct($text) + { + $this->text = $text; + } + + /** + * Returns a string representation of this node. + * + * @param Context $context symbol table + */ + public function render($context) + { + return $this->text; + } +} + +/** + * ExpressionNode represents an expression tag: "{...}". + */ +class ExpressionNode implements Node +{ + protected $expr; + + /** + * Initializes a new Node instance with the given expression. + * + * @param Expression $expr expression object + */ + public function __construct(Expression $expr) + { + $this->expr = $expr; + } + + /** + * Returns a string representation of this node. + * + * @param Context $context symbol table + */ + public function render($context) + { + return $this->expr->value($context); + } +} + +/** + * ArrayNode represents a sequence of arbitrary nodes. + */ +class ArrayNode implements Node +{ + protected $nodes = array(); + + /** + * Adds a child node to this sequence node. + * + * @param Node $node child node to add + */ + public function addChild(Node $node) + { + $this->nodes[] = $node; + } + + /** + * Returns a string representation of this node. + * + * @param Context $context symbol table + */ + public function render($context) + { + $result = ''; + + foreach ($this->nodes as $node) { + $result .= $node->render($context); + } + + return $result; + } +} + +/** + * IteratorNode represents a single iterator tag: + * "{foreach ARRAY}...{endforeach}". + */ +class IteratorNode extends ArrayNode +{ + protected $expr; + + /** + * Initializes a new Node instance with the given expression. + * + * @param Expression $expr expression object + */ + public function __construct(Expression $expr) + { + $this->expr = $expr; + } + + /** + * Returns a string representation of this node. The IteratorNode + * renders the node sequence for each value in the expression list. + * + * @param Context $context symbol table + */ + public function render($context) + { + $values = $this->expr->value($context); + $result = ''; + + if (is_array($values) && is_int(key($values))) { + $bindings = array('index' => &$key, 'this' => &$value); + $context = new Context($bindings, $context); + + foreach ($values as $key => $value) { + $result .= parent::render(new Context($value, $context)); + } + } else if (is_array($values) && count($values)) { + return parent::render(new Context($values, $context)); + } else if ($values) { + return parent::render($context); + } + + return $result; + } +} + +/** + * ConditionNode represents a single condition tag: + * "{if CONDITION}...{else}...{endif}". + */ +class ConditionNode extends ArrayNode +{ + protected $condition; + protected $else_node; + + /** + * Initializes a new Node instance with the given expression. + * + * @param Expression $condition expression object + */ + public function __construct(Expression $condition) + { + $this->condition = $condition; + } + + /** + * Adds an else block to this condition node. + */ + public function addElse() + { + $this->else_node = new ArrayNode(); + } + + /** + * Adds a child node to this condition node. + * + * @param Node $node child node to add + */ + public function addChild(Node $node) + { + if ($this->else_node) { + $this->else_node->addChild($node); + } else { + parent::addChild($node); + } + } + + /** + * Returns a string representation of this node. + * + * @param Context $context symbol table + */ + public function render($context) + { + if ($this->condition->value($context)) { + return parent::render($context); + } + + return $this->else_node ? $this->else_node->render($context) : ''; + } +} diff --git a/vendor/exTpl/Scanner.php b/vendor/exTpl/Scanner.php new file mode 100644 index 0000000..d6ec2e6 --- /dev/null +++ b/vendor/exTpl/Scanner.php @@ -0,0 +1,96 @@ +<?php +/* + * Scanner.php - template parser lexical scanner + * + * Copyright (c) 2013 Elmar Ludwig + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +namespace exTpl; + +/** + * Simple wrapper class around the Zend engine's lexical scanner. It + * automatically skips whitespace and offers an interator interface. + */ +class Scanner +{ + private $tokens; + private $token_type; + private $token_value; + + /** + * Initializes a new Scanner instance for the given text. + * + * @param string $text string to parse + */ + public function __construct($text) + { + $this->tokens = token_get_all('<?php ' . $text); + } + + /** + * Advances the scanner to the next token and returns its token type. + * The valid token types are those defined for token_get_all() in the + * PHP documentation. Returns false when the end of input is reached. + */ + public function nextToken() + { + do { + $token = next($this->tokens); + $key = key($this->tokens); + + while ($token[0] === T_STRING && + $this->tokens[++$key] === '-' && + $this->tokens[++$key][0] === T_STRING) { + $token[1] .= '-' . $this->tokens[$key][1]; + next($this->tokens); + next($this->tokens); + } + } while (is_array($token) && $token[0] === T_WHITESPACE); + + if (is_string($token) || $token === false) { + $this->token_type = $token; + $this->token_value = NULL; + } else { + $this->token_type = $token[0]; + + switch ($token[0]) { + case T_CONSTANT_ENCAPSED_STRING: + $this->token_value = stripcslashes(substr($token[1], 1, -1)); + break; + case T_DNUMBER: + $this->token_value = (double) $token[1]; + break; + case T_LNUMBER: + $this->token_value = (int) $token[1]; + break; + default: + $this->token_value = $token[1]; + } + } + + return $this->token_type; + } + + /** + * Returns the current token type. The valid token types are + * those defined for token_get_all() in the PHP documentation. + */ + public function tokenType() + { + return $this->token_type; + } + + /** + * Returns the current token value if the token type supports + * a value (T_STRING, T_LNUMBER etc.). Returns NULL otherwise. + */ + public function tokenValue() + { + return $this->token_value; + } +} diff --git a/vendor/exTpl/Template.php b/vendor/exTpl/Template.php new file mode 100644 index 0000000..28db8fe --- /dev/null +++ b/vendor/exTpl/Template.php @@ -0,0 +1,390 @@ +<?php +/* + * Template.php - expression template parser + * + * Copyright (c) 2013 Elmar Ludwig + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +namespace exTpl; + +require_once 'Scanner.php'; +require_once 'Node.php'; + +/** + * The Template class is the only externally visible API of this + * template implementation. It can be used to create and render + * template objects. + */ +class Template +{ + private static $tag_start = '{'; + private static $tag_end = '}'; + private $template; + + /** + * Sets the delimiter strings used for the template tags, the + * default delimiters are: $tag_start = '{', $tag_end = '}'. + * + * @param string $tag_start tag start marker + * @param string $tag_end tag end marker + */ + public static function setTagMarkers($tag_start, $tag_end) + { + self::$tag_start = $tag_start; + self::$tag_end = $tag_end; + } + + /** + * Initializes a new Template instance from the given string. + * + * @param string $string template text + */ + public function __construct($string) + { + $this->template = new ArrayNode(); + self::parseTemplate($this->template, $string, 0); + } + + /** + * Renders the template to a string using the given array of + * bindings to resolve symbol references inside the template. + * + * @param array $bindings symbol table + * + * @return string string representation of the template + */ + public function render(array $bindings) + { + return $this->template->render(new Context($bindings)); + } + + /** + * Skips tokens until the end of the current tag is reached. + * + * @param string $string template text + * @param int $pos offset in string + * + * @return int new offset in the string + */ + private static function skipTokens($string, $pos) + { + for ($len = strlen($string); $pos < $len && + substr_compare($string, self::$tag_end, $pos, strlen(self::$tag_end)); ++$pos) { + $chr = $string[$pos]; + if ($chr === '"' || $chr === "'") { + while (++$pos < $len && $string[$pos] !== $chr) { + if ($string[$pos] === '\\') { + ++$pos; + } + } + } + } + + return $pos; + } + + /** + * Parses a template string into a template node tree, starting + * at the specified offset. All created nodes are added to the + * given sequence node. + * + * @param ArrayNode $node template node to build + * @param string $string string to parse + * @param int $pos offset in string + * + * @return int new offset in the string + */ + private static function parseTemplate(ArrayNode $node, $string, $pos) + { + $len = strlen($string); + + while ($pos < $len) { + $next_pos = strpos($string, self::$tag_start, $pos); + + if ($next_pos === false) { + $child = new TextNode(substr($string, $pos)); + $node->addChild($child); + break; + } + + if ($next_pos > $pos) { + $child = new TextNode(substr($string, $pos, $next_pos - $pos)); + $node->addChild($child); + } + + $pos = $next_pos + strlen(self::$tag_start); + $next_pos = self::skipTokens($string, $pos); + $scanner = new Scanner(substr($string, $pos, $next_pos - $pos)); + $pos = $next_pos + strlen(self::$tag_end); + + switch ($scanner->nextToken()) { + case T_FOREACH: + $scanner->nextToken(); + $child = new IteratorNode(self::parseExpr($scanner)); + $pos = self::parseTemplate($child, $string, $pos); + $node->addChild($child); + break; + case T_ENDFOREACH: + return $pos; + case T_IF: + $scanner->nextToken(); + $child = new ConditionNode(self::parseExpr($scanner)); + $pos = self::parseTemplate($child, $string, $pos); + $node->addChild($child); + break; + case T_ELSEIF: + $scanner->nextToken(); + $child = new ConditionNode(self::parseExpr($scanner)); + $node->addElse(); + $node->addChild($child); + return self::parseTemplate($child, $string, $pos); + case T_ELSE: + $scanner->nextToken(); + $node->addElse(); + break; + case T_ENDIF: + return $pos; + default: + $child = new ExpressionNode(self::parseExpr($scanner)); + $node->addChild($child); + } + + if ($scanner->tokenType() !== false) { + throw new TemplateParserException('syntax error', $scanner); + } + } + + return $pos; + } + + /** + * value: NUMBER | STRING | SYMBOL | '(' expr ')' + */ + private static function parseValue(Scanner $scanner) + { + switch ($scanner->tokenType()) { + case T_CONSTANT_ENCAPSED_STRING: + case T_DNUMBER: + case T_LNUMBER: + $result = new ConstantExpression($scanner->tokenValue()); + break; + case T_STRING: + $result = new SymbolExpression($scanner->tokenValue()); + break; + case '(': + $scanner->nextToken(); + $result = self::parseExpr($scanner); + + if ($scanner->tokenType() !== ')') { + throw new TemplateParserException('missing ")"', $scanner); + } + break; + default: + throw new TemplateParserException('syntax error', $scanner); + } + + $scanner->nextToken(); + return $result; + } + + /** + * index: value | index '[' expr ']' | index '.' SYMBOL + */ + private static function parseIndex(Scanner $scanner) + { + $result = self::parseValue($scanner); + $type = $scanner->tokenType(); + + while ($type === '[' || $type === '.') { + $scanner->nextToken(); + + if ($type === '[') { + $expr = self::parseExpr($scanner); + + if ($scanner->tokenType() !== ']') { + throw new TemplateParserException('missing "]"', $scanner); + } + } else if ($scanner->tokenType() === T_STRING) { + $expr = new ConstantExpression($scanner->tokenValue()); + } else { + throw new TemplateParserException('symbol expected', $scanner); + } + + $scanner->nextToken(); + $result = new IndexExpression($result, $expr, $type); + $type = $scanner->tokenType(); + } + + return $result; + } + + /** + * sign: '!' sign | '+' sign | '-' sign | index + */ + private static function parseSign(Scanner $scanner) + { + switch ($scanner->tokenType()) { + case '!': + $scanner->nextToken(); + $result = new NotExpression(self::parseSign($scanner)); + break; + case '+': + $scanner->nextToken(); + $result = self::parseSign($scanner); + break; + case '-': + $scanner->nextToken(); + $result = new MinusExpression(self::parseSign($scanner)); + break; + default: + $result = self::parseIndex($scanner); + } + + return $result; + } + + /** + * product: sign | product '*' sign | product '/' sign | product '%' sign + */ + private static function parseProduct(Scanner $scanner) + { + $result = self::parseSign($scanner); + $type = $scanner->tokenType(); + + while ($type === '*' || $type === '/' || $type === '%') { + $scanner->nextToken(); + $result = new ArithExpression($result, self::parseSign($scanner), $type); + $type = $scanner->tokenType(); + } + + return $result; + } + + /** + * sum: product | sum '+' product | sum '-' product | sum '~' product + */ + private static function parseSum(Scanner $scanner) + { + $result = self::parseProduct($scanner); + $type = $scanner->tokenType(); + + while ($type === '+' || $type === '-' || $type === '~') { + $scanner->nextToken(); + $result = new ArithExpression($result, self::parseProduct($scanner), $type); + $type = $scanner->tokenType(); + } + + return $result; + } + + /** + * lt_gt: sum | lt_gt '<' concat | lt_gt IS_SMALLER_OR_EQUAL concat + * | lt_gt '>' concat | lt_gt IS_GREATER_OR_EQUAL concat + */ + private static function parseLtGt(Scanner $scanner) + { + $result = self::parseSum($scanner); + $type = $scanner->tokenType(); + + while ($type === '<' || $type === T_IS_SMALLER_OR_EQUAL || + $type === '>' || $type === T_IS_GREATER_OR_EQUAL) { + $scanner->nextToken(); + $result = new BooleanExpression($result, self::parseSum($scanner), $type); + $type = $scanner->tokenType(); + } + + return $result; + } + + /** + * cmp: lt_gt | cmp IS_EQUAL lt_gt | cmp IS_NOT_EQUAL lt_gt + */ + private static function parseCmp(Scanner $scanner) + { + $result = self::parseLtGt($scanner); + $type = $scanner->tokenType(); + + while ($type === T_IS_EQUAL || $type === T_IS_NOT_EQUAL) { + $scanner->nextToken(); + $result = new BooleanExpression($result, self::parseLtGt($scanner), $type); + $type = $scanner->tokenType(); + } + + return $result; + } + + /** + * and: cmp | and BOOLEAN_AND cmp + */ + private static function parseAnd(Scanner $scanner) + { + $result = self::parseCmp($scanner); + $type = $scanner->tokenType(); + + while ($type === T_BOOLEAN_AND) { + $scanner->nextToken(); + $result = new BooleanExpression($result, self::parseCmp($scanner), $type); + $type = $scanner->tokenType(); + } + + return $result; + } + + /** + * or: and | or BOOLEAN_OR and + */ + private static function parseOr(Scanner $scanner) + { + $result = self::parseAnd($scanner); + $type = $scanner->tokenType(); + + while ($type === T_BOOLEAN_OR) { + $scanner->nextToken(); + $result = new BooleanExpression($result, self::parseAnd($scanner), $type); + $type = $scanner->tokenType(); + } + + return $result; + } + + /** + * expr: or | or '?' expr ':' expr + */ + private static function parseExpr(Scanner $scanner) + { + $result = self::parseOr($scanner); + + if ($scanner->tokenType() === '?') { + $scanner->nextToken(); + $expr = self::parseExpr($scanner); + + if ($scanner->tokenType() !== ':') { + throw new TemplateParserException('missing ":"', $scanner); + } + + $scanner->nextToken(); + $result = new ConditionExpression($result, $expr, self::parseExpr($scanner)); + } + + return $result; + } +} + +/** + * Exception class used to report template parse errors. + */ +class TemplateParserException extends \Exception +{ + public function __construct($message, $scanner) + { + $type = $scanner->tokenType(); + $value = is_int($type) ? $scanner->tokenValue() : $type; + + return parent::__construct("$message at \"$value\""); + } +} diff --git a/vendor/exTpl/template_test.php b/vendor/exTpl/template_test.php new file mode 100644 index 0000000..5adbb13 --- /dev/null +++ b/vendor/exTpl/template_test.php @@ -0,0 +1,148 @@ +<?php +/* + * template_test.php - expression template unit tests + * + * Copyright (c) 2013 Elmar Ludwig + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +require 'Template.php'; + +use exTpl\Template; + +class TemplateTest extends PHPUnit_Framework_TestCase +{ + public function testSimpleString() + { + $bindings = array(); + $template = 'The quick brown fox jumps over the layz dog.'; + $expected = $template; + $tmpl_obj = new Template($template); + + $this->assertEquals($expected, $tmpl_obj->render($bindings)); + } + + public function testConstantExpression() + { + $bindings = array(); + $template = '17 + 4 = {"foo" != "bar" ? 17 + 4 : 42.0}'; + $expected = '17 + 4 = 21'; + $tmpl_obj = new Template($template); + + $this->assertEquals($expected, $tmpl_obj->render($bindings)); + } + + public function testStringEscapes() + { + $bindings = array(); + $template = '"{"\\tfoo\'\\"\\n"}{\'{"bar"}\'}"'; + $expected = "\"\tfoo'\"\n{\"bar\"}\""; + $tmpl_obj = new Template($template); + + $this->assertEquals($expected, $tmpl_obj->render($bindings)); + } + + public function testOperatorPrecedence() + { + $bindings = array('val' => array(array(42))); + $template = '{-val[0][0] / (17+4) + 8 > 6 && "foo" == "f"~"o"~"o" ? 1 : 2}'; + $expected = '2'; + $tmpl_obj = new Template($template); + + $this->assertEquals($expected, $tmpl_obj->render($bindings)); + } + + public function testSimpleBindings() + { + $bindings = array('foo' => 'bar', 'val' => array(17, 4), 'pi' => 3.14159); + $template = 'foo = "{foo}", sum = {val[0] + val[1]}, pi^2 = {pi * pi}, x = {x}'; + $expected = 'foo = "bar", sum = 21, pi^2 = 9.8695877281, x = '; + $tmpl_obj = new Template($template); + + $this->assertEquals($expected, $tmpl_obj->render($bindings)); + } + + public function testConditional() + { + $bindings = array('foo' => 'bar', 'pi' => 3.14159); + $template = '{if foo == "foo"}NO{elseif foo == "bar"}pi = {pi}{else}NO{endif}'; + $expected = 'pi = 3.14159'; + $tmpl_obj = new Template($template); + + $this->assertEquals($expected, $tmpl_obj->render($bindings)); + } + + public function testConditionalIteration() + { + $bindings = array('foo' => 'bar', 'pi' => 3.14159); + $template = '{foreach foo}{if foo}{foo}{endif}{endforeach}'; + $expected = 'bar'; + $tmpl_obj = new Template($template); + + $this->assertEquals($expected, $tmpl_obj->render($bindings)); + } + + public function testIteration() + { + $bindings = array('persons' => array( + 1 => array('user' => 'jane', 'phone' => '555-81281'), + 2 => array('user' => 'mike', 'phone' => '230-28382'), + 3 => array('user' => 'john', 'phone' => '911-19212') + )); + $template = '<ul>{foreach persons}<li>{index~":"~this.user~":"~phone}</li>{endforeach}</ul>'; + $expected = '<ul>' . + '<li>1:jane:555-81281</li>' . + '<li>2:mike:230-28382</li>' . + '<li>3:john:911-19212</li>' . + '</ul>'; + $tmpl_obj = new Template($template); + + $this->assertEquals($expected, $tmpl_obj->render($bindings)); + } + + public function testEmptyIteration() + { + $bindings = array('foo' => array(), 'bar' => false); + $template = '{foreach foo}foo{endforeach}:{foreach bar}bar{endforeach}'; + $expected = ':'; + $tmpl_obj = new Template($template); + + $this->assertEquals($expected, $tmpl_obj->render($bindings)); + } + + public function testVariableScope() + { + $bindings = array('value' => 42, 'test' => array( + array(), + array('value' => 17), + array('test' => array( + array(), + array('value' => 4) + )) + )); + $template = '{foreach test}{value}:{foreach test}{value}~{endforeach}{endforeach}'; + $expected = '42:42~17~42~17:17~17~17~42:42~4~'; + $tmpl_obj = new Template($template); + + $this->assertEquals($expected, $tmpl_obj->render($bindings)); + } + + public function testNestedStatements() + { + foreach (range(0, 9) as $i) { + $bindings['loop'][$i]['i'] = "$i"; + } + $template = '{foreach loop}' . + '{if i+1>4 && i<(1+10/2)}{i==4*1 ? \'foo\'~i : "bar"}' . + '{elseif !(i<=+4)}+{elseif i==""}..{else}{"-"}{endif}' . + '{endforeach}'; + $expected = '----foo4bar++++'; + $tmpl_obj = new Template($template); + + $this->assertEquals($expected, $tmpl_obj->render($bindings)); + } +} diff --git a/vendor/flexi/LICENSE b/vendor/flexi/LICENSE new file mode 100644 index 0000000..e845366 --- /dev/null +++ b/vendor/flexi/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2007 Marcus Lunzenauer <mlunzena@uos.de> + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/flexi/README b/vendor/flexi/README new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/vendor/flexi/README diff --git a/vendor/flexi/Rakefile b/vendor/flexi/Rakefile new file mode 100644 index 0000000..5c951fc --- /dev/null +++ b/vendor/flexi/Rakefile @@ -0,0 +1,22 @@ +require 'rake/clean' +require 'rake/contrib/sys' + +SRC = FileList['lib/**/*'] +CLEAN.include('doc') + +desc 'Create documentation' +file 'doc' => SRC do + sh "phpdoc --sourcecode on -t `pwd`/doc -d `pwd`/lib -ti 'trails documentation' -o 'HTML:frames:earthli'" +end + +desc 'Run all unit tests' +task 'test' do + sh "php test/all_tests.php" +end + +desc 'Run coverage' +task 'coverage' do + Sys.indir "test" do + sh "php coverage.php" + end +end diff --git a/vendor/flexi/examples/01_hello_world/index.php b/vendor/flexi/examples/01_hello_world/index.php new file mode 100644 index 0000000..1d04136 --- /dev/null +++ b/vendor/flexi/examples/01_hello_world/index.php @@ -0,0 +1,39 @@ +<?php + +# Copyright (c) 2008 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# load flexi lib +require_once dirname(__FILE__) . '/../../lib/flexi.php'; + +# where are the templates +$path_to_the_templates = dirname(__FILE__) . '/templates'; + +# we need a template factory +$factory = new Flexi_TemplateFactory($path_to_the_templates); + +# open template +$template = $factory->open('hello_world'); + +# set name of the greetee +$template->set_attribute('name', 'Axel'); + +# render template +echo $template->render(); diff --git a/vendor/flexi/examples/01_hello_world/templates/hello_world.php b/vendor/flexi/examples/01_hello_world/templates/hello_world.php new file mode 100644 index 0000000..9560915 --- /dev/null +++ b/vendor/flexi/examples/01_hello_world/templates/hello_world.php @@ -0,0 +1 @@ +<h1>Hello, <?= $name ?>!</h1> diff --git a/vendor/flexi/examples/02_quotes/index.php b/vendor/flexi/examples/02_quotes/index.php new file mode 100644 index 0000000..8b6e134 --- /dev/null +++ b/vendor/flexi/examples/02_quotes/index.php @@ -0,0 +1,63 @@ +<?php + +# Copyright (c) 2008 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# load flexi lib +require_once dirname(__FILE__) . '/../../lib/flexi.php'; + +# where are the templates +$path_to_the_templates = dirname(__FILE__) . '/templates'; + +# we need a template factory +$factory = new Flexi_TemplateFactory($path_to_the_templates); + +# open template +$template = $factory->open('quotes'); + + +# set quotes +$quotes = array( + array('author' => 'August Strindberg', + 'quote' => 'Der Mensch ist ein wunderliches Tier.'), + array('author' => 'Pierre Reverdy', + 'quote' => 'Der Mensch ist ein Tier, das sich selbst gezähmt hat.'), + array('author' => 'Thomas Niederreuther', + 'quote' => 'Der Mensch ist das einzige Tier, das sich für einen Menschen hält.'), + array('author' => 'Durs Grünbein', + 'quote' => 'Der Mensch ist das Tier, das Kaugummi kaut.'), + array('author' => 'Mark Twain', + 'quote' => 'Der Mensch ist das einzige Tier, das erröten kann - oder sollte.')); + +# select one randomly +shuffle($quotes); +$quote_of_the_day = array_shift($quotes); + +$template->set_attributes(array('quotes' => $quotes, + 'quote_of_the_day' => $quote_of_the_day)); + + +# set current time +$time = time(); +$template->set_attribute('time', $time); + + +# render template +echo $template->render(); diff --git a/vendor/flexi/examples/02_quotes/templates/quotes.php b/vendor/flexi/examples/02_quotes/templates/quotes.php new file mode 100644 index 0000000..b494cca --- /dev/null +++ b/vendor/flexi/examples/02_quotes/templates/quotes.php @@ -0,0 +1,23 @@ +<? $title = 'Zitate'; ?> +<div id="header">Zitate <span class="amp">&</span> mehr</div> + +<h2>Zitat des Tages (<?= date('d.m.Y', $time) ?>)</h2> +<p> + <em> + „<?= $quote_of_the_day['quote'] ?>“ + </em> + (<?= $quote_of_the_day['author'] ?>) +</p> + + +<? if (sizeof($quotes)) : ?> + <h2>Mehr Zitate</h2> + <? foreach ($quotes as $quote) : ?> + <p> + <em> + „<?= $quote['quote'] ?>“ + </em> + (<?= $quote['author'] ?>) + </p> + <? endforeach ?> +<? endif ?> diff --git a/vendor/flexi/examples/03_layout/bgbody.gif b/vendor/flexi/examples/03_layout/bgbody.gif Binary files differnew file mode 100644 index 0000000..d868c71 --- /dev/null +++ b/vendor/flexi/examples/03_layout/bgbody.gif diff --git a/vendor/flexi/examples/03_layout/index.php b/vendor/flexi/examples/03_layout/index.php new file mode 100644 index 0000000..34f9449 --- /dev/null +++ b/vendor/flexi/examples/03_layout/index.php @@ -0,0 +1,66 @@ +<?php + +# Copyright (c) 2008 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# load flexi lib +require_once dirname(__FILE__) . '/../../lib/flexi.php'; + +# where are the templates +$path_to_the_templates = dirname(__FILE__) . '/templates'; + +# we need a template factory +$factory = new Flexi_TemplateFactory($path_to_the_templates); + +# open template +$template = $factory->open('quotes'); + + +# set layout +$template->set_layout('layout'); + +# set quotes +$quotes = array( + array('author' => 'August Strindberg', + 'quote' => 'Der Mensch ist ein wunderliches Tier.'), + array('author' => 'Pierre Reverdy', + 'quote' => 'Der Mensch ist ein Tier, das sich selbst gezähmt hat.'), + array('author' => 'Thomas Niederreuther', + 'quote' => 'Der Mensch ist das einzige Tier, das sich für einen Menschen hält.'), + array('author' => 'Durs Grünbein', + 'quote' => 'Der Mensch ist das Tier, das Kaugummi kaut.'), + array('author' => 'Mark Twain', + 'quote' => 'Der Mensch ist das einzige Tier, das erröten kann - oder sollte.')); + +# select one randomly +shuffle($quotes); +$quote_of_the_day = array_shift($quotes); + +$template->set_attributes(array('quotes' => $quotes, + 'quote_of_the_day' => $quote_of_the_day)); + + +# set current time +$time = time(); +$template->set_attribute('time', $time); + + +# render template +echo $template->render(); diff --git a/vendor/flexi/examples/03_layout/style.css b/vendor/flexi/examples/03_layout/style.css new file mode 100644 index 0000000..e8f2bfb --- /dev/null +++ b/vendor/flexi/examples/03_layout/style.css @@ -0,0 +1,18 @@ +body { + background: white url("bgbody.gif") repeat-x; + color: #555; + font: 1.5em Verdana, Arial, 'Bitstream Vera Sans', sans-serif; +} + +#header { + font: 1.8em Verdana, Arial, 'Bitstream Vera Sans', sans-serif; + color: white; + height: 101px; + width: 100%; + margin-bottom: 1em; +} + +.amp { + font-style: italic; + font-family: 'Goudy Old Style', 'Palatino', 'Book Antiqua', serif; +} diff --git a/vendor/flexi/examples/03_layout/templates/layout.php b/vendor/flexi/examples/03_layout/templates/layout.php new file mode 100644 index 0000000..3d025aa --- /dev/null +++ b/vendor/flexi/examples/03_layout/templates/layout.php @@ -0,0 +1,10 @@ +<html> +<head> + <meta http-equiv="Content-type" content="text/html; charset=utf-8" /> + <title><?= $title ?></title> + <link rel="stylesheet" type="text/css" href="style.css" media="screen"/> +</head> +<body> + <?= $content_for_layout ?> +</body> +</html> diff --git a/vendor/flexi/examples/03_layout/templates/quotes.php b/vendor/flexi/examples/03_layout/templates/quotes.php new file mode 100644 index 0000000..9d9b96d --- /dev/null +++ b/vendor/flexi/examples/03_layout/templates/quotes.php @@ -0,0 +1,20 @@ +<h2>Zitat des Tages (<?= date('d.m.Y', $time) ?>)</h2> +<p> + <em> + „<?= $quote_of_the_day['quote'] ?>“ + </em> + (<?= $quote_of_the_day['author'] ?>) +</p> + + +<? if (sizeof($quotes)) : ?> + <h2>Mehr Zitate</h2> + <? foreach ($quotes as $quote) : ?> + <p> + <em> + „<?= $quote['quote'] ?>“ + </em> + (<?= $quote['author'] ?>) + </p> + <? endforeach ?> +<? endif ?> diff --git a/vendor/flexi/examples/04_handler_plugin/mustache.php b/vendor/flexi/examples/04_handler_plugin/mustache.php new file mode 100644 index 0000000..604c994 --- /dev/null +++ b/vendor/flexi/examples/04_handler_plugin/mustache.php @@ -0,0 +1,32 @@ +<?php +error_reporting(E_ALL ^ E_NOTICE); + +// load flexi lib +require_once dirname(__FILE__) . '/../../lib/flexi.php'; + +// where are the templates +$path_to_the_templates = dirname(__FILE__) . '/templates'; + +// we need a template factory +$factory = new Flexi_TemplateFactory($path_to_the_templates); + +// load haml plugin + +require_once dirname(__FILE__) . '/../../lib/mustache_template.php'; +$factory->add_handler('mustache', 'Flexi_MustacheTemplate'); + +// open template +$template = $factory->open('mustache'); + +$template->set_attributes(array( + "name" => "Chris", + "value" => 10000, + "taxed_value" => 10000 - (10000 * 0.4), + "in_ca" => true +)); + +// test mix of different template engines +$template->set_layout("layout"); + +// render template +echo $template->render(); diff --git a/vendor/flexi/examples/04_handler_plugin/templates/layout.php b/vendor/flexi/examples/04_handler_plugin/templates/layout.php new file mode 100644 index 0000000..60d102c --- /dev/null +++ b/vendor/flexi/examples/04_handler_plugin/templates/layout.php @@ -0,0 +1,3 @@ +<layout> +<?= $content_for_layout ?> +</layout> diff --git a/vendor/flexi/examples/04_handler_plugin/templates/mustache.mustache b/vendor/flexi/examples/04_handler_plugin/templates/mustache.mustache new file mode 100644 index 0000000..de4a2be --- /dev/null +++ b/vendor/flexi/examples/04_handler_plugin/templates/mustache.mustache @@ -0,0 +1,5 @@ +Hello {{name}} +You have just won ${{value}}! +{{#in_ca}} +Well, ${{taxed_value}}, after taxes. +{{/in_ca}} diff --git a/vendor/flexi/examples/xx_js_templates/index.php b/vendor/flexi/examples/xx_js_templates/index.php new file mode 100644 index 0000000..b158f82 --- /dev/null +++ b/vendor/flexi/examples/xx_js_templates/index.php @@ -0,0 +1,39 @@ +<?php + +# Copyright (c) 2008 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# load flexi lib +require_once dirname(__FILE__) . '/../../lib/flexi.php'; + +# where are the templates +$path_to_the_templates = dirname(__FILE__) . '/templates'; + +# we need a template factory +$factory = new Flexi_TemplateFactory($path_to_the_templates); + +# open template +$template = $factory->open('js_template'); + +# set name of the greetee +$template->set_attribute('name', 'Axel'); + +# render template +echo $template->render(); diff --git a/vendor/flexi/examples/xx_js_templates/templates/js_template.pjs b/vendor/flexi/examples/xx_js_templates/templates/js_template.pjs new file mode 100644 index 0000000..56ab8a3 --- /dev/null +++ b/vendor/flexi/examples/xx_js_templates/templates/js_template.pjs @@ -0,0 +1,3 @@ +<? +$update_page->append(var_export(get_defined_vars(), 1)); + diff --git a/vendor/flexi/flexi.php b/vendor/flexi/flexi.php new file mode 100644 index 0000000..22c21d8 --- /dev/null +++ b/vendor/flexi/flexi.php @@ -0,0 +1,24 @@ +<?php + +# Copyright (c) 2012 - <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# Stud.IP workaround for legacy plugins +require_once 'lib/flexi.php'; diff --git a/vendor/flexi/lib/exceptions.php b/vendor/flexi/lib/exceptions.php new file mode 100644 index 0000000..befb789 --- /dev/null +++ b/vendor/flexi/lib/exceptions.php @@ -0,0 +1,23 @@ +<?php + +# Copyright (c) 2009 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +class Flexi_TemplateNotFoundException extends Exception {} diff --git a/vendor/flexi/lib/flexi.php b/vendor/flexi/lib/flexi.php new file mode 100644 index 0000000..ab94661 --- /dev/null +++ b/vendor/flexi/lib/flexi.php @@ -0,0 +1,41 @@ +<?php + +# Copyright (c) 2008 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +define('FLEXI_VERSION', '0.5.0'); + +/** + * Bootstrapping file for flexi. Just include this to get going. + * + * @package flexi + */ + +require_once 'exceptions.php'; +require_once 'template.php'; +require_once 'template_factory.php'; +require_once 'php_template.php'; +require_once 'js_template.php'; + +require_once 'helper/js_helper.php'; +require_once 'helper/prototype_helper.php'; +require_once 'helper/scriptaculous_helper.php'; +require_once 'helper/tag_helper.php'; +require_once 'helper/text_helper.php'; diff --git a/vendor/flexi/lib/helper/js_helper.php b/vendor/flexi/lib/helper/js_helper.php new file mode 100644 index 0000000..b641f4e --- /dev/null +++ b/vendor/flexi/lib/helper/js_helper.php @@ -0,0 +1,135 @@ +<?php + +# Copyright (c) 2008 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +/** + * Provides functionality for working with JavaScript in your views. + * + * @package flexi + * @subpackage helper + * + * @author Marcus Lunzenauer (mlunzena@uos.de) + * @author David Heinemeier Hansson + * @copyright (c) Authors + * @version $Id: js_helper.php 3437 2006-05-27 11:38:58Z mlunzena $ + */ + +class JsHelper { + + /** + * Returns a link that'll trigger a javascript function using the + * onclick handler and return false after the fact. + * + * Example: + * JsHelper::link_to_function('Greeting', "alert('Hello world!')"); + * + * @param type <description> + * @param type <description> + * @param type <description> + * + * @return type <description> + */ + function link_to_function($name, $function, $html = array()) { + $html['href'] = isset($html['href']) ? $html['href'] : '#'; + $html['onclick'] = $function.'; return false;'; + return TagHelper::content_tag('a', $name, $html); + } + + /** + * Returns a link that'll trigger a JavaScript function using the onclick + * handler. + * + * Examples: + * JsHelper::button_to_function("Greeting", "alert('Hello world!')"); + * + * @param type <description> + * @param type <description> + * @param type <description> + * + * @return type <description> + */ + function button_to_function($name, $function, $html_options = array()) { + + $html_options['type'] = 'button'; + $html_options['value'] = $name; + $html_options['onclick'] = sprintf('%s%s;', + isset($html_options['onclick']) ? $html_options['onclick'] . '; ' : '', + $function); + return TagHelper::tag('input', $html_options); + } + + /** + * Escape carrier returns and single and double quotes for Javascript + * segments. + * + * @param type <description> + * + * @return type <description> + */ + function escape_javascript($javascript = '') { + $pattern = array(); $replace = array(); + $pattern[] = '/\\\\/'; $replace[] = '\\\\\\'; + $pattern[] = '/<\//'; $replace[] = '<\\/'; + $pattern[] = "/\r\n/"; $replace[] = '\n'; + $pattern[] = "/\n/"; $replace[] = '\n'; + $pattern[] = "/\r/"; $replace[] = '\n'; + $pattern[] = '/"/'; $replace[] = '\\"'; + $pattern[] = "/'/"; $replace[] = "\\'"; + + $javascript = preg_replace($pattern, $replace, $javascript); + return $javascript; + } + + /** + * Returns a JavaScript tag with the '$content' inside. + * Example: + * JsHelper::javascript_tag("alert('All is good')"); + * => <script type="text/javascript">alert('All is good')</script> + * + * @param type <description> + * + * @return type <description> + */ + function javascript_tag($content) { + return TagHelper::content_tag('script', + JsHelper::js_cdata_section($content), + array('type' => 'text/javascript')); + } + + /** + * @ignore + */ + function js_cdata_section($content) { + return "\n//".TagHelper::cdata_section("\n$content\n//")."\n"; + } + + /** + * @ignore + */ + function options_for_javascript($opt) { + $opts = array(); + foreach ($opt as $key => $value) + $opts[] = "$key:$value"; + sort($opts); + + return '{'.join(', ', $opts).'}'; + } +} diff --git a/vendor/flexi/lib/helper/prototype_helper.php b/vendor/flexi/lib/helper/prototype_helper.php new file mode 100644 index 0000000..33f8821 --- /dev/null +++ b/vendor/flexi/lib/helper/prototype_helper.php @@ -0,0 +1,764 @@ +<?php + +# Copyright (c) 2008 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +/** + * PrototypeHelper. + * + * + * @package flexi + * @subpackage helper + * + * @author Marcus Lunzenauer (mlunzena@uos.de) + * @author David Heinemeier Hansson + * @copyright (c) Authors + * @version $Id: prototype_helper.php 3437 2006-05-27 11:38:58Z mlunzena $ + */ + +class PrototypeHelper { + + /** + * Returns a link to a remote action defined by 'url' (using the 'url_for()' + * format) that's called in the background using XMLHttpRequest. The result of + * that request can then be inserted into a DOM object whose id can be + * specified with 'update'. + * + * Examples: + * link_to_remote('Delete this post', + * array('update' => 'posts', 'url' => 'destroy?id='.$id)) + * + * You can also specify a hash for 'update' to allow for + * easy redirection of output to an other DOM element if a server-side error + * occurs: + * + * Example: + * link_to_remote('Delete this post', + * array('update' => array('success' => 'posts', + * 'failure' => 'error'), + * 'url' => 'destroy?id='.$id)) + * + * Optionally, you can use the 'position' parameter to influence + * how the target DOM element is updated. It must be one of 'before', 'top', + * 'bottom', or 'after'. + * + * By default, these remote requests are processed asynchronous during + * which various JavaScript callbacks can be triggered (for progress + * indicators and the likes). All callbacks get access to the 'request' + * object, which holds the underlying XMLHttpRequest. + * + * The callbacks that may be specified are (in order): + * + * 'loading' Called when the remote document is being + * loaded with data by the browser. + * 'loaded' Called when the browser has finished loading + * the remote document. + * 'interactive' Called when the user can interact with the + * remote document, even though it has not + * finished loading. + * 'success' Called when the XMLHttpRequest is completed, + * and the HTTP status code is in the 2XX range. + * 'failure' Called when the XMLHttpRequest is completed, + * and the HTTP status code is not in the 2XX + * range. + * 'complete' Called when the XMLHttpRequest is complete + * (fires after success/failure if present). + * + * You can further refine 'success' and 'failure' by adding additional + * callbacks for specific status codes: + * + * Example: + * link_to_remote($word, array('url' => $rule, + * '404' => "alert('Not found...?')", + * 'failure' => "alert('HTTPError!')")) + * + * A status code callback overrides the success/failure handlers if present. + * + * If you for some reason or another need synchronous processing (that'll + * block the browser while the request is happening), you can specify + * 'type' => 'synchronous'. + * + * You can customize further browser side call logic by passing + * in JavaScript code snippets via some optional parameters. In + * their order of use these are: + * + * 'confirm' Adds confirmation dialog. + * 'condition' Perform remote request conditionally + * by this expression. Use this to + * describe browser-side conditions when + * request should not be initiated. + * 'before' Called before request is initiated. + * 'after' Called immediately after request was + * initiated and before 'loading'. + * 'submit' Specifies the DOM element ID that's used + * as the parent of the form elements. By + * default this is the current form, but + * it could just as well be the ID of a + * table row or any other DOM element. + * + * @param type <description> + * @param type <description> + * @param type <description> + * + * @return type <description> + */ + function link_to_remote($name, $options = array(), $html_options = array()) { + return JsHelper::link_to_function($name, + PrototypeHelper::remote_function($options), $html_options); + } + + /** + * Periodically calls the specified url ['url'] every ['frequency'] seconds + * (default is 10). Usually used to update a specified div ['update'] with the + * results of the remote call. The options for specifying the target with + * 'url' and defining callbacks is the same as 'link_to_remote()'. + * + * @param type <description> + * + * @return type <description> + */ + function periodically_call_remote($options = array()) { + $frequency = isset($options['frequency']) ? $options['frequency'] : 10; + $code = sprintf('new PeriodicalExecuter(function() {%s}, %d)', + PrototypeHelper::remote_function($options), $frequency); + return JsHelper::javascript_tag($code); + } + + /** + * Returns a form tag that will submit using XMLHttpRequest in the background + * instead of the regular reloading POST arrangement. Even though it's using + * JavaScript to serialize the form elements, the form submission will work + * just like a regular submission as viewed by the receiving side + * (all elements available in 'params'). The options for specifying the target + * with 'url' and defining callbacks are the same as 'link_to_remote()'. + * + * A "fall-through" target for browsers that don't do JavaScript can be + * specified with the 'action' and 'method' options on '$html_options'. + * + * Example: + * form_remote_tag(array( + * 'url' => 'tag_add', + * 'update' => 'question_tags', + * 'loading' => "Element.show('indicator'); tag.value = ''", + * 'complete' => "Element.hide('indicator');". + * visual_effect('highlight', 'question_tags'))) + * + * The hash passed as a second argument is equivalent to the options (2nd) + * argument in the form_tag() helper. + * + * @param type <description> + * + * @return type <description> + */ + function form_remote_tag($options = array(), $html_options = array()) { + + $options = TagHelper::_parse_attributes($options); + $html_options = TagHelper::_parse_attributes($html_options); + + $options['form'] = TRUE; + + $html_options['onsubmit'] = PrototypeHelper::remote_function($options). + '; return false;'; + $html_options['action'] = isset($html_options['action']) + ? $html_options['action'] + : $options['url']; + + $html_options['method'] = isset($html_options['method']) + ? $html_options['method'] + : 'post'; + + return TagHelper::tag('form', $html_options, TRUE); + } + + /** + * Returns a button input tag that will submit form using XMLHttpRequest in + * the background instead of regular reloading POST arrangement. The '$opt' + * argument is the same as in 'form_remote_tag()'. + * + * @param type <description> + * @param type <description> + * @param type <description> + * + * @return type <description> + */ + function submit_to_remote($name, $value, $options = array()) { + + if (!isset($options['with'])) + $options['with'] = 'Form.serialize(this.form)'; + + if (!isset($options['html'])) + $options['html'] = array(); + + $options['html']['type'] = 'button'; + $options['html']['onclick'] = PrototypeHelper::remote_function($options). + '; return false;'; + $options['html']['name'] = $name; + $options['html']['value'] = $value; + + return TagHelper::tag('input', $options['html']); + } + + /** + * Returns a Javascript function (or expression) that will update a DOM + * element '$element_id' according to the '$opt' passed. + * + * Possible '$opt' are: + * 'content' The content to use for updating. + * 'action' Valid options are 'update' (default), 'empty', 'remove' + * 'position' If the 'action' is 'update', you can optionally specify one of + * the following positions: 'before', 'top', 'bottom', 'after'. + * + * Example: + * update_element_function('products', + * array('position' => 'bottom', + * 'content' => "<p>New product!</p>")); + * + * + * This method can also be used in combination with remote method call + * where the result is evaluated afterwards to cause multiple updates on a + * page. + * + * @param type <description> + * @param type <description> + * + * @return type <description> + */ + function update_element_function($element_id, $options = array()) { + + $content = JsHelper::escape_javascript(isset($options['content']) + ? $options['content'] + : ''); + + $value = isset($options['action']) ? $options['action'] : 'update'; + + switch ($value) { + + case 'update': + $js_func = $options['position'] + ? sprintf("new Insertion.%s('%s','%s')", + TextHelper::camelize($options['position']), + $element_id, $content) + : "\$('$element_id').innerHTML = '$content'"; + break; + + case 'empty': + $js_func = "\$('$element_id').innerHTML = ''"; + break; + + case 'remove': + $js_func = "Element.remove('$element_id')"; + break; + + default: + trigger_error('Invalid action, choose one of update, remove, empty'); + exit; + } + + $js_func .= ";\n"; + + return isset($options['binding']) ? $js_func.$options['binding'] : $js_func; + } + + /** + * Returns 'eval(request.responseText)', which is the Javascript function that + * 'form_remote_tag()' can call in 'complete' to evaluate a multiple update + * return document using 'update_element_function()' calls. + * + * @return type <description> + */ + function evaluate_remote_response() { + return 'eval(request.responseText)'; + } + + /** + * Returns the javascript needed for a remote function. + * Takes the same arguments as 'link_to_remote()'. + * + * Example: + * <select id="options" onchange="<?= + * remote_function(array('update' => 'options', + * 'url' => '@update_options')) ?>"> + * <option value="0">Hello</option> + * <option value="1">World</option> + * </select> + * + * @param type <description> + * + * @return type <description> + */ + function remote_function($options) { + $javascript_options = PrototypeHelper::options_for_ajax($options); + + $update = ''; + if (isset($options['update']) && is_array($options['update'])) { + + $update = array(); + if (isset($options['update']['success'])) + $update[] = "success:'".$options['update']['success']."'"; + + if (isset($options['update']['failure'])) + $update[] = "failure:'".$options['update']['failure']."'"; + + $update = '{'.join(',', $update).'}'; + + } else if (isset($options['update'])) { + $update .= "'".$options['update']."'"; + } + + $function = sprintf("new Ajax.%s(%s'%s', %s)", + $update ? 'Updater' : 'Request', + $update ? "$update, " : '', + $options['url'], + $javascript_options); + + if (isset($options['before'])) + $function = $options['before'].'; '.$function; + + if (isset($options['after'])) + $function = $function.'; '.$options['after']; + + if (isset($options['condition'])) + $function = 'if ('.$options['condition'].') { '.$function.'; }'; + + if (isset($options['confirm'])) { + $function = "if (confirm('" . + JsHelper::escape_javascript($options['confirm']) . + "')) { $function; }"; + if (isset($options['cancel'])) + $function .= ' else { '.$options['cancel'].' }'; + } + + return $function; + } + + /** + * Observes the field with the DOM ID specified by '$field_id' and makes + * an AJAX call when its contents have changed. + * + * Required '$options' are: + * 'url' 'url_for()'-style options for the action to call + * when the field has changed. + * + * Additional options are: + * 'frequency' The frequency (in seconds) at which changes to + * this field will be detected. Not setting this + * option at all or to a value equal to or less than + * zero will use event based observation instead of + * time based observation. + * 'update' Specifies the DOM ID of the element whose + * innerHTML should be updated with the + * XMLHttpRequest response text. + * 'with' A JavaScript expression specifying the + * parameters for the XMLHttpRequest. This defaults + * to 'value', which in the evaluated context + * refers to the new field value. + * + * Additionally, you may specify any of the options documented in + * link_to_remote(). + * + * @param type <description> + * @param type <description> + * + * @return type <description> + */ + function observe_field($field_id, $options = array()) { + $name = isset($options['frequency']) && $options['frequency'] > 0 + ? 'Form.Element.Observer' + : 'Form.Element.EventObserver'; + return PrototypeHelper::build_observer($name, $field_id, $options); + } + + /** + * Like 'observe_field()', but operates on an entire form identified by the + * DOM ID '$form_id'. '$options' are the same as 'observe_field()', except + * the default value of the 'with' option evaluates to the + * serialized (request string) value of the form. + * + * @param type <description> + * @param type <description> + * + * @return type <description> + */ + function observe_form($form_id, $options = array()) { + $name = isset($options['frequency']) && $options['frequency'] > 0 + ? 'Form.Observer' + : 'Form.EventObserver'; + return PrototypeHelper::build_observer($name, $form_id, $options); + } + + /** + * @ignore + */ + function options_for_ajax($options) { + $js_opt = PrototypeHelper::build_callbacks($options); + + $js_opt['asynchronous'] = isset($options['type']) + ? $options['type'] != 'synchronous' : 'true'; + + if (isset($options['method'])) + $js_opt['method'] = PrototypeHelper::method_option_to_s($options['method']); + + if (isset($options['position'])) + $js_opt['insertion'] = 'Insertion.'.TextHelper::camelize($options['position']); + + $js_opt['evalScripts'] = !isset($options['script']) || + $options['script'] == '0' || + $options['script'] == 'false' + ? 'false' : 'true'; + + if (isset($options['form'])) + $js_opt['parameters'] = 'Form.serialize(this)'; + else if (isset($options['submit'])) + $js_opt['parameters'] = "Form.serialize(document.getElementById('{$options['submit']}'))"; + else if (isset($options['with'])) + $js_opt['parameters'] = $options['with']; + + return JsHelper::options_for_javascript($js_opt); + } + + /** + * @ignore + */ + function method_option_to_s($method) { + return is_string($method) && $method[0] == "'" ? $method : "'$method'"; + } + + /** + * @ignore + */ + function build_observer($klass, $name, $options = array()) { + if (!isset($options['with']) && $options['update']) + $options['with'] = 'value'; + + $callback = PrototypeHelper::remote_function($options); + + $javascript = 'new '.$klass.'("'.$name.'", '; + if (isset($options['frequency'])) + $javascript .= $options['frequency'].", "; + + $javascript .= 'function(element, value) {'; + $javascript .= $callback.'});'; + + return JsHelper::javascript_tag($javascript); + } + + /** + * @ignore + */ + function build_callbacks($options) { + $callbacks = array(); + foreach (PrototypeHelper::get_callbacks() as $callback) { + if (isset($options[$callback])) { + $name = 'on'.ucfirst($callback); + $code = $options[$callback]; + $callbacks[$name] = 'function(request, json){'.$code.'}'; + } + } + + return $callbacks; + } + + /** + * @ignore + */ + function get_callbacks() { + static $callbacks; + if (!$callbacks) + $callbacks = array_merge(range(100, 599), + array('uninitialized', 'loading', 'loaded', + 'interactive', 'complete', 'failure', + 'success')); + return $callbacks; + } + + /** + * @ignore + */ + function get_ajax_options() { + static $ajax_options; + if (!$ajax_options) + $ajax_options = array('before', 'after', 'condition', 'url', + 'asynchronous', 'method', 'insertion', 'position', + 'form', 'with', 'update', 'script') + + PrototypeHelper::get_callbacks(); + return $ajax_options; + } +} + +/** + * JavaScriptGenerator generates blocks of JavaScript code that allow you to + * change the content and presentation of multiple DOM elements. Use this in + * your Ajax response bodies, either in a <script> tag or as plain JavaScript + * sent with a Content-type of "text/javascript". + * + * @package flexi + * + * @author Marcus Lunzenauer (mlunzena@uos.de) + * @copyright (c) Authors + * @version $Id: prototype_helper.php 3437 2006-05-27 11:38:58Z mlunzena $ + */ + +class Flexi_JavascriptGenerator { + + /** + * internal variables + * @ignore + */ + var $lines = array(); + + /** + * @ignore + */ + function to_s() { + $javascript = implode("\n", $this->lines); + return "try {\n".$javascript."\n} catch (e) { ". + "alert('JS error:\\n\\n' + e.toString()); throw e }"; + } + +################################################################################ +# function [] +# function select +# function draggable +# function drop_receiving +# function sortable +################################################################################ + + /** + * Inserts HTML at the specified 'position' relative to the DOM element + * identified by the given 'id'. + * + * 'position' may be one of: + * + * 'top':: HTML is inserted inside the element, before the + * element's existing content. + * 'bottom':: HTML is inserted inside the element, after the + * element's existing content. + * 'before':: HTML is inserted immediately preceeding the element. + * 'after':: HTML is inserted immediately following the element. + * + * @param type <description> + * @param type <description> + * @param type <description> + * + * @return type <description> + */ + function insert_html($position, $id, $content) { + $insertion = TextHelper::camelize($position); + $this->call('new Insertion.'.$insertion, $id, $content); + } + + /** + * Replaces the inner HTML of the DOM element with the given 'id'. + * + * @param type <description> + * @param type <description> + * + * @return type <description> + */ + function replace_html($id, $content) { + $this->call('Element.update', $id, $content); + } + + /** + * Replaces the "outer HTML" (i.e., the entire element, not just its + * contents) of the DOM element with the given 'id'. + * + * @param type <description> + * @param type <description> + * + * @return void + */ + function replace($id, $content) { + $this->call('Element.replace', $id, $content); + } + + /** + * Removes the DOM elements with the given 'ids' from the page. + * + * @param type <description> + * + * @return void + */ + function remove($ids) { + $ids = func_get_args(); + $this->record($this->javascript_object_for($ids).".each(Element.remove)"); + } + + /** + * Shows hidden DOM elements with the given 'ids'. + * + * @param type <description> + * + * @return void + */ + function show($ids) { + $ids = func_get_args(); + array_unshift($ids, 'Element.show'); + call_user_func_array(array(&$this, 'call'), $ids); + } + + /** + * Hides the visible DOM elements with the given 'ids'. + * + * @param type <description> + * + * @return void + */ + function hide($ids) { + $ids = func_get_args(); + array_unshift($ids, 'Element.hide'); + call_user_func_array(array(&$this, 'call'), $ids); + } + + /** + * Toggles the visibility of the DOM elements with the given 'ids'. + * + * @param type <description> + * + * @return void + */ + function toggle($ids) { + $ids = func_get_args(); + array_unshift($ids, 'Element.toggle'); + call_user_func_array(array(&$this, 'call'), $ids); + } + + /** + * Displays an alert dialog with the given 'message'. + * + * @param string the given message. + * + * @return void + */ + function alert($message) { + $this->call('alert', $message); + } + + /** + * Redirects the browser to the given 'location'. + * + * @param type <description> + * + * @return void + */ + function redirect_to($location) { + $this->assign('window.location.href', $location); + } + + /** + * Calls the JavaScript 'function', optionally with the given 'arguments'. + * + * @param type <description> + * + * @return void + */ + function call($function) { + $arguments = func_get_args(); + array_shift($arguments); + $this->record($function.'('.$this->arguments_for_call($arguments).')'); + } + + /** + * Assigns the JavaScript 'variable' the given 'value'. + * + * @param type <description> + * @param type <description> + * + * @return void + */ + function assign($variable, $value) { + $this->record($variable.' = '.$this->javascript_object_for($value)); + } + + /** + * Writes raw JavaScript to the page. + * + * @param string the raw JavaScript + * + * @return void + */ + function append($javascript) { + $this->lines[] = $javascript; + } + + /** + * Executes the given javascript after a delay of 'seconds'. + * + * # TODO (mlunzena) this function has side effects and has to be explained or deleted + * + * @param type <description> + * + * @return void + */ + function delay($seconds = 1) { + static $in_delay = FALSE; + + if (!$in_delay) { + $in_delay = TRUE; + $this->record("setTimeout(function() {\n\n"); + } + + else { + $in_delay = FALSE; + $this->record(sprintf("\n}, %d)", $seconds * 1000)); + } + + return $in_delay; + } + + /** + * Starts a script.aculo.us visual effect. See + * ScriptaculousHelper for more information. + * + * @param type <description> + * @param type <description> + * @param type <description> + * + * @return void + */ + function visual_effect($name, $id = FALSE, $js_opt = array()) { + $this->record(ScriptaculousHelper::visual_effect($name, $id, $js_opt)); + } + + /** + * @ignore + */ + function record($line) { + $line = preg_replace('/\;$/', '', rtrim($line)) . ';'; + $this->append($line); + } + + /** + * @ignore + */ + function javascript_object_for($object) { + return json_encode($object); + } + + /** + * @ignore + */ + function arguments_for_call($arguments) { + $mapped = array(); + foreach ($arguments as $argument) + $mapped[] = $this->javascript_object_for($argument); + return join(',', $mapped); + } +} diff --git a/vendor/flexi/lib/helper/scriptaculous_helper.php b/vendor/flexi/lib/helper/scriptaculous_helper.php new file mode 100644 index 0000000..643ad24 --- /dev/null +++ b/vendor/flexi/lib/helper/scriptaculous_helper.php @@ -0,0 +1,142 @@ +<?php + +# Copyright (c) 2008 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +/** + * ScriptaculousHelper. + * + * + * @package flexi + * @subpackage helper + * + * @author Marcus Lunzenauer (mlunzena@uos.de) + * @author David Heinemeier Hansson + * @copyright (c) Authors + * @version $Id: scriptaculous_helper.php 3437 2006-05-27 11:38:58Z mlunzena $ + */ + +class ScriptaculousHelper { + + /** + * Returns a JavaScript snippet to be used on the AJAX callbacks for starting + * visual effects. + * + * Example: + * ScriptaculousHelper::visual_effect('highlight', 'posts', + * array('duration' => 0.5 )); + * + * If no '$element_id' is given, it assumes "element" which should be a local + * variable in the generated JavaScript execution context. This can be used + * for example with drop_receiving_element(): + * + * ScriptaculousHelper::drop_receving_element(..., array(... + * 'loading' => ScriptaculousHelper::visual_effect('fade'))); + * + * This would fade the element that was dropped on the drop receiving element. + * + * You can change the behaviour with various options, see + * http://script.aculo.us for more documentation. + * + * @param type <description> + * @param type <description> + * @param type <description> + * + * @return string <description> + */ + function visual_effect($name, $element_id = FALSE, $js_opt = array()) { + + $element = $element_id ? "'$element_id'" : 'element'; + + switch ($name) { + case 'toggle_appear': + case 'toggle_blind': + case 'toggle_slide': + return sprintf("new Effect.toggle(%s, '%s', %s)", + $element, substr($name, 7), + JsHelper::options_for_javascript($js_opt)); + } + + return sprintf("new Effect.%s(%s, %s)", + TextHelper::camelize($name), + $element, JsHelper::options_for_javascript($js_opt)); + } + + + /** + * Makes the elements with the DOM ID specified by '$element_id' sortable + * by drag-and-drop and make an AJAX call whenever the sort order has + * changed. By default, the action called gets the serialized sortable + * element as parameters. + * + * Example: + * <php echo sortable_element($my_list, array( + * 'url' => '@order', + * )) ?> + * + * In the example, the action gets a '$my_list' array parameter + * containing the values of the ids of elements the sortable consists + * of, in the current order. + * + * You can change the behaviour with various options, see + * http://script.aculo.us for more documentation. + */ + function sortable_element($element_id, $options = array()) { + + if (!isset($options['with'])) + $options['with'] = "Sortable.serialize('$element_id')"; + + if (!isset($options['onUpdate'])) + $options['onUpdate'] = + sprintf('function(){%s}', PrototypeHelper::remote_function($options)); + + foreach (PrototypeHelper::get_ajax_options() as $key) + unset($options[$key]); + + foreach (array('tag', 'overlap', 'constraint', 'handle') as $option) + if (isset($options[$option])) + $options[$option] = "'{$options[$option]}'"; + + if (isset($options['containment'])) + $options['containment'] = self::array_or_string_for_javascript($options['containment']); + + if (isset($options['hoverclass'])) + $options['hoverclass'] = "'{$options['hoverclass']}'"; + + if (isset($options['only'])) + $options['only'] = self::array_or_string_for_javascript($options['only']); + + return JsHelper::javascript_tag( + sprintf("Sortable.create('%s', %s)", + $element_id, JsHelper::options_for_javascript($options))); + } + + + /** + * @ignore + */ + function array_or_string_for_javascript($option) { + if (is_array($option)) { + return "['".join("','", $option)."']"; + } else if ($option) { + return "'$option'"; + } + } +} diff --git a/vendor/flexi/lib/helper/tag_helper.php b/vendor/flexi/lib/helper/tag_helper.php new file mode 100644 index 0000000..673c5c1 --- /dev/null +++ b/vendor/flexi/lib/helper/tag_helper.php @@ -0,0 +1,122 @@ +<?php + +# Copyright (c) 2008 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +/** + * TagHelper defines some base helpers to construct html tags. + * This is poor man’s Builder for the rare cases where you need to + * programmatically make tags but can’t use Builder. + * + * @package flexi + * @subpackage helper + * + * @author Marcus Lunzenauer (mlunzena@uos.de) + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + * @author David Heinemeier Hansson + * @copyright (c) Authors + * @version $Id: tag_helper.php 3437 2006-05-27 11:38:58Z mlunzena $ + */ + +class TagHelper { + + /** + * Constructs an html tag. + * + * @param $name string tag name + * @param $options array tag options + * @param $open boolean true to leave tag open + * + * @return string + */ + function tag($name, $options = array(), $open = false) { + if (!$name) return ''; + return '<'.$name.TagHelper::_tag_options($options).($open ? '>' : ' />'); + } + + /** + * Helper function for content tags. + * + * @param type <description> + * @param type <description> + * @param type <description> + * + * @return type <description> + */ + function content_tag($name, $content = '', $options = array()) { + if (!$name) return ''; + return '<'.$name.TagHelper::_tag_options($options).'>'.$content.'</'.$name.'>'; + } + + /** + * Helper function for CDATA sections. + * + * @param type <description> + * + * @return type <description> + */ + function cdata_section($content) { + return '<![CDATA['.$content.']]>'; + } + + /** + * @ignore + */ + function _tag_options($options = array()) { + $options = TagHelper::_parse_attributes($options); + $html = ''; + foreach ($options as $key => $value) + $html .= ' '.$key.'="'.$value.'"'; + return $html; + } + + /** + * @ignore + */ + function _parse_attributes($string) { + return is_array($string) ? $string : TagHelper::string_to_array($string); + } + + /** + * <MethodDescription> + * + * @param string <description> + * + * @return array <description> + */ + function string_to_array($string) { + preg_match_all('/ + \s*(\w+) # key \\1 + \s*=\s* # = + (\'|")? # values may be included in \' or " \\2 + (.*?) # value \\3 + (?(2) \\2) # matching \' or " if needed \\4 + \s*(?: + (?=\w+\s*=) | \s*$ # followed by another key= or the end of the string + ) + /x', $string, $matches, PREG_SET_ORDER); + + $attributes = array(); + foreach ($matches as $val) + $attributes[$val[1]] = $val[3]; + + return $attributes; + } +} diff --git a/vendor/flexi/lib/helper/text_helper.php b/vendor/flexi/lib/helper/text_helper.php new file mode 100644 index 0000000..c596727 --- /dev/null +++ b/vendor/flexi/lib/helper/text_helper.php @@ -0,0 +1,183 @@ +<?php + +# Copyright (c) 2008 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +/** + * TextHelper. + * + * + * @package flexi + * @subpackage helper + * + * @author Marcus Lunzenauer (mlunzena@uos.de) + * @copyright (c) Authors + * @version $Id$ + */ + +class TextHelper { + + /** + * Holds the internal cycles. + * + * @ignore + */ + private static $cycles = array(); + + + /** + * Returns a camelized string from a lower case and underscored string by + * replacing slash with underscore and upper-casing each letter preceded + * by an underscore. + * + * @param string String to camelize. + * + * @return string Camelized string. + */ + function camelize($word) { + return str_replace(' ', '', + ucwords(str_replace(array('_', '/'), + array(' ', ' '), $word))); + } + + + /** + * Creates a Cycle object whose +__toString method cycles through elements of + * an array every time it is called. This can be used for example, to + * alternate classes for table rows: + * + * <? foreach ($items as $item) : ?> + * <tr class="<?= TextHelper::cycle('odd', 'even') ?>"> + * <td><?= item ?></td> + * </tr> + * <? endforeach ?> + * + * You can use named cycles to allow nesting in loops. Passing a single array + * as the only parameter with a <tt>name</tt> key will create a named cycle. + * You can manually reset a cycle by calling reset_cycle and passing the + * name of the cycle. + * + * <? foreach($items as $item) : ?> + * <tr class="<?= TextHelper::cycle(array("even", "odd", "name" => "row_class")) ?>"> + * <td> + * <? foreach ($item->values as $value) : ?> + * <span style="color:<?= TextHelper::cycle(array("red", "green", "blue", "name" => "colors")) : ?>"> + * <?= $value ?> + * </span> + * <? endforeach ?> + * <? TextHelper::reset_cycle("colors") ?> + * </td> + * </tr> + * <? endforeach ?> + */ + static function cycle($first_value) { + + $values = func_get_args(); + + + if (sizeof($values) == 1 && is_array($values[0])) { + $values = $values[0]; + $name = isset($values['name']) ? $values['name'] : 'default'; + unset($values['name']); + } + else { + $name = 'default'; + } + + $cycle = self::get_cycle($name); + if (is_null($cycle) || $cycle->values !== $values) { + $cycle = self::set_cycle($name, new TextHelperCycle($values)); + } + + return (string) $cycle; + } + + + /** + * Resets a cycle so that it starts from the first element the next time + * it is called. Pass in +name+ to reset a named cycle. + * + * @param string an optional name of a cycle + * + * @return void + */ + public static function reset_cycle($name = 'default') { + $cycle = self::get_cycle($name); + if (isset($cycle)) { + $cycle->reset(); + } + } + + + /** + * @ignore + */ + private static function get_cycle($name) { + return isset(self::$cycles[$name]) ? self::$cycles[$name] : NULL; + } + + /** + * @ignore + */ + private static function set_cycle($name, $cycle) { + return self::$cycles[$name] = $cycle; + } +} + + +/** + * This class holds an array of string and cycles through them. + * + * @package flexi + * @subpackage helper + * + * @author Marcus Lunzenauer (mlunzena@uos.de) + * @copyright (c) Authors + * @version $Id$ + */ + +class TextHelperCycle { + + + public $values; + + + function __construct($values) { + $this->values = (array) $values; + } + + + function cycle() { + $result = current($this->values); + if (next($this->values) === FALSE) + $this->reset(); + return $result; + } + + + function reset() { + reset($this->values); + } + + + function __toString() { + return $this->cycle(); + } +} diff --git a/vendor/flexi/lib/js_template.php b/vendor/flexi/lib/js_template.php new file mode 100644 index 0000000..d2113a6 --- /dev/null +++ b/vendor/flexi/lib/js_template.php @@ -0,0 +1,55 @@ +<?php + +# Copyright (c) 2008 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +/** + * A template engine that renders Javascript templates. + * + * @package flexi + * + * @author Marcus Lunzenauer (mlunzena@uos.de) + * @copyright (c) Authors + * @version $Id$ + */ + +class Flexi_JsTemplate extends Flexi_PhpTemplate { + + /** + * Parse, render and return the presentation. + * + * @return string A string representing the rendered presentation. + */ + function _render() { + + # put attributes into scope + extract($this->_attributes); + + # get generator object + $update_page = new Flexi_JavascriptGenerator(); + + # include template, parse it and remove output + ob_start(); + require $this->_template; + ob_end_clean(); + + return $update_page->to_s(); + } +} diff --git a/vendor/flexi/lib/mustache_template.php b/vendor/flexi/lib/mustache_template.php new file mode 100644 index 0000000..2051a2e --- /dev/null +++ b/vendor/flexi/lib/mustache_template.php @@ -0,0 +1,85 @@ +<?php + +# Copyright (c) 2008 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +require_once dirname(__FILE__) . '/../vendor/mustache.php/Mustache.php'; + +/** + * A template engine that uses PHP to render templates. + * + * @package flexi + * + * @author Marcus Lunzenauer (mlunzena@uos.de) + * @copyright (c) Authors + * @version $Id$ + */ + +class Flexi_MustacheTemplate extends Flexi_Template { + + /** + * Parse, render and return the presentation. + * + * @return string A string representing the rendered presentation. + */ + function _render() { + ${0} = new MyMustache($this); + $content_for_layout = ${0}->render(); + + // include layout, parse it and get output + if (isset($this->_layout)) { + $defined = get_defined_vars(); + unset($defined['this'], $defined['0']); + $content_for_layout = $this->_layout->render($defined); + } + + return $content_for_layout; + } + + function getPartial($tag_name) { + $file = dirname($this->_template) . '/' . $tag_name . '.mustache'; + var_dump($file); + if (file_exists($file)) { + return file_get_contents($file); + } + return ''; + } +} + +class MyMustache extends Mustache { + + public function __construct($flexi) { + parent::__construct(file_get_contents($flexi->_template), $flexi->_attributes); + $this->_flexi = $flexi; + } + + protected function _getPartial($tag_name) { + + if ($partial = $this->_flexi->getPartial($tag_name)) { + return $partial; + } + + if ($this->_throwsException(MustacheException::UNKNOWN_PARTIAL)) { + throw new MustacheException('Unknown partial: ' . $tag_name, MustacheException::UNKNOWN_PARTIAL); + } else { + return ''; + } + } +} diff --git a/vendor/flexi/lib/php_template.php b/vendor/flexi/lib/php_template.php new file mode 100644 index 0000000..af808b4 --- /dev/null +++ b/vendor/flexi/lib/php_template.php @@ -0,0 +1,119 @@ +<?php + +# Copyright (c) 2008 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +/** + * A template engine that uses PHP to render templates. + * + * @package flexi + * + * @author Marcus Lunzenauer (mlunzena@uos.de) + * @copyright (c) Authors + * @version $Id$ + */ + +class Flexi_PhpTemplate extends Flexi_Template { + + /** + * Parse, render and return the presentation. + * + * @return string A string representing the rendered presentation. + */ + function _render() { + + extract($this->_attributes); + + # include template, parse it and get output + ob_start(); + require $this->_template; + $content_for_layout = ob_get_clean(); + + + # include layout, parse it and get output + if (isset($this->_layout)) { + $defined = get_defined_vars(); + unset($defined['this']); + $content_for_layout = $this->_layout->render($defined); + } + + return $content_for_layout; + } + + + /** + * Parse, render and return the presentation of a partial template. + * + * @param string A name of a partial template. + * @param array An optional associative array of attributes and their + * associated values. + * + * @return string A string representing the rendered presentation. + */ + function render_partial($partial, $attributes = array()) { + return $this->_factory->render($partial, $attributes + $this->_attributes); + } + + + /** + * Renders a partial template with every member of a collection. This member + * can be accessed by a template variable with the same name as the name of + * the partial template. + * + * Example: + * + * # template entry.php contains: + * <li><?= $entry ?></li> + * + * + * $entries = array('lorem', 'ipsum'); + * $template->render_partial_collection('entry', $entries); + * + * # results in: + * <li>lorem</li> + * <li>ipsum</li> + * + * TODO (mlunzena) spacer and attributes must be described + * + * @param string A name of a partial template. + * @param array The collection to be rendered. + * @param string Optional a name of a partial template used as spacer. + * @param array An optional associative array of attributes and their + * associated values. + * + * @return string A string representing the rendered presentation. + */ + function render_partial_collection($partial, $collection, + $spacer = NULL, $attributes = array()) { + + $template = $this->_factory->open($partial); + $template->set_attributes($this->_attributes); + $template->set_attributes($attributes); + + $collected = array(); + $iterator_name = pathinfo($partial, PATHINFO_FILENAME); + foreach ($collection as $element) + $collected[] = $template->render(array($iterator_name => $element)); + + $spacer = isset($spacer) ? $this->render_partial($spacer, $attributes) : ''; + + return join($spacer, $collected); + } +} diff --git a/vendor/flexi/lib/template.php b/vendor/flexi/lib/template.php new file mode 100644 index 0000000..b4d97e8 --- /dev/null +++ b/vendor/flexi/lib/template.php @@ -0,0 +1,251 @@ +<?php + +# Copyright (c) 2008 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +/** + * Abstract template class representing the presentation layer of an action. + * Output can be customized by supplying attributes, which a template can + * manipulate and display. + * + * @package flexi + * + * @author Marcus Lunzenauer (mlunzena@uos.de) + * @copyright (c) Authors + * @version $Id$ + */ + +abstract class Flexi_Template { + + /** + * @ignore + */ + public $_attributes, $_factory, $_options, $_layout, $_template; + + + /** + * Constructor + * + * @param string the path of the template. + * @param Flexi_TemplateFactory the factory creating this template + * @param array optional array of options + * + * @return void + */ + function __construct($template, &$factory, $options = array()) { + + # set template + $this->_template = $template; + + # set factory + $this->_factory = $factory; + + # set options + $this->_options = $options; + + # init attributes + $this->clear_attributes(); + + # set layout + $this->set_layout(NULL); + } + + + /** + * __set() is a magic method run when writing data to inaccessible members. + * In this class it is used to set attributes for the template in a + * comfortable way. + * + * @see http://php.net/__set + * + * @param string the name of the member field + * @param mixed the value for the member field + * + * @return void + */ + function __set($name, $value) { + $this->set_attribute($name, $value); + } + + + /** + * __get() is a magic method utilized for reading data from inaccessible + * members. + * In this class it is used to get attributes for the template in a + * comfortable way. + * + * @see http://php.net/__set + * + * @param string the name of the member field + * + * @return mixed the value for the member field + */ + function __get($name) { + return $this->get_attribute($name); + } + + + /** + * __isset() is a magic method triggered by calling isset() or empty() on + * inaccessible members. + * In this class it is used to check for attributes for the template in a + * comfortable way. + * + * @see http://php.net/__set + * + * @param string the name of the member field + * + * @return bool TRUE if that attribute exists, FALSE otherwise + */ + function __isset($name) { + return isset($this->_attributes[$name]); + } + + + /** + * __unset() is a magic method invoked when unset() is used on inaccessible + * members. + * In this class it is used to check for attributes for the template in a + * comfortable way. + * + * @see http://php.net/__set + * + * @param string the name of the member field + * + * @return void + */ + function __unset($name) { + $this->clear_attribute($name); + } + + + /** + * Parse, render and return the presentation. + * + * @param array An optional associative array of attributes and their + * associated values. + * @param string A name of a layout template. + * + * @return string A string representing the rendered presentation. + */ + function render($attributes = null, $layout = null) { + + if (isset($layout)) { + $this->set_layout($layout); + } + + # merge attributes + $this->set_attributes($attributes); + + return $this->_render(); + } + + + /** + * Parse, render and return the presentation. + * + * @return string A string representing the rendered presentation. + */ + abstract function _render(); + + + /** + * Returns the value of an attribute. + * + * @param string An attribute name. + * @param mixed An attribute value. + * + * @return mixed An attribute value. + */ + function get_attribute($name) { + return isset($this->_attributes[$name]) ? $this->_attributes[$name] : NULL; + } + + + /** + * Set an array of attributes. + * + * @return array An associative array of attributes and their associated + * values. + */ + function get_attributes() { + return $this->_attributes; + } + + + /** + * Set an attribute. + * + * @param string An attribute name. + * @param mixed An attribute value. + * + * @return void + */ + function set_attribute($name, $value) { + $this->_attributes[$name] = $value; + } + + + /** + * Set an array of attributes. + * + * @param array An associative array of attributes and their associated + * values. + * + * @return void + */ + function set_attributes($attributes) { + $this->_attributes = (array)$attributes + (array)$this->_attributes; + } + + + /** + * Clear all attributes associated with this template. + * + * @return void + */ + function clear_attributes() { + $this->_attributes = array(); + } + + + /** + * Clear an attribute associated with this template. + * + * @param string The name of the attribute to be cleared. + * + * @return void + */ + function clear_attribute($name) { + unset($this->_attributes[$name]); + } + + + /** + * Set the template's layout. + * + * @param mixed A name of a layout template or a layout template. + * + * @return void + */ + function set_layout($layout) { + $this->_layout = $this->_factory->open($layout); + } +} diff --git a/vendor/flexi/lib/template_factory.php b/vendor/flexi/lib/template_factory.php new file mode 100644 index 0000000..dfdc74e --- /dev/null +++ b/vendor/flexi/lib/template_factory.php @@ -0,0 +1,281 @@ +<?php + +# Copyright (c) 2008 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +/** + * Using this factory you can create new Template objects. + * + * @package flexi + * + * @author Marcus Lunzenauer (mlunzena@uos.de) + * @copyright (c) Authors + * @version $Id$ + */ + +class Flexi_TemplateFactory { + + + /** + * include path for templates + * + * @var string + */ + protected $path; + + + /** + * mapping of file extensions to supported template classes + * + * @var array + */ + protected $handlers = array( + 'php' => array('Flexi_PhpTemplate', array()), + 'pjs' => array('Flexi_JsTemplate', array()) + ); + + + /** + * Constructor of TemplateFactory. + * + * @param string the template include path + * + * @return void + */ + function __construct($path) { + $this->set_path($path); + } + + + /** + * Sets a new include path for the factory and returns the old one. + * + * @param string the new path + * + * @return string the old path + */ + function set_path($path) { + + $old_path = $this->get_path(); + + if (substr($path, -1) != '/') + $path .= '/'; + + $this->path = $path; + + return $old_path; + } + + + /** + * Returns the include path of the factory + * + * @return string the current include path + */ + function get_path() { + return $this->path; + } + + + /** + * Open a template of the given name using the factory method pattern. + * If a string was given, the path of the factory is searched for a matching + * template. + * If this string starts with a slash or with /\w+:\/\//, the string is + * interpreted as an absolute path. Otherwise the path of the factory will be + * prepended. + * After that the factory searches for a file extension in this string. If + * there is none, the directory where the template is supposed to live is + * searched for a file starting with the template string and a supported + * file extension. + * At last the factory instantiates a template object of the matching template + * class. + * + * Examples: + * + * $factory->open('/path/to/template') + * does not prepend the factory's path but searches for "template.*" in + * "/path/to" + * + * $factory->open('template') + * prepends the factory's path and searches there for "template.*" + * + * $factory->open('template.php') + * prepends the factory's path but does not search and instantiates a + * PHPTemplate instead + * + * This method returns it's parameter, if it is not a string. This + * functionality is useful for helper methods like #render_partial + * + * @throws Flexi_TemplateNotFoundException + * + * @param string A name of a template. + * + * @return mixed the factored object + * + * @throws Flexi_TemplateNotFoundException if the template could not be found + */ + function open($template) { + + # if it is not a string, this method behaves like identity + if (!is_string($template)) { + return $template; + } + + # get file + $file = $this->get_template_file($template); + + # retrieve handler + list($class, $options) = $this->get_template_handler($file); + + return new $class($file, $this, $options); + } + + + /** + * This method returns the absolute filename of the template + * + * @param string a template string + * + * @return string an absolute filename + * + * @throws Flexi_TemplateNotFoundException if the template could not be found + */ + function get_template_file($template0) { + + $template = $this->get_absolute_path($template0); + $extension = $this->get_extension($template); + + # extension defined, is there a matching template class? + if ($extension !== NULL) { + if (file_exists($template)) { + return $template; + } + } + + # no extension defined, find it + else { + $file = $this->find_template($template); + if ($file !== NULL) { + return $file; + } + } + + # falling through to throw exception + throw new Flexi_TemplateNotFoundException( + sprintf('Missing template "%s" in "%s".', $template0, $this->path)); + } + + + /** + * Matches an extension to a template handler. + * + * @param string the template + * + * @return array an array containing the class name and an array of + * options of the matched extension; + * or NULL if the extension did not match + */ + function get_template_handler($template) { + $extension = $this->get_extension($template); + return isset($this->handlers[$extension]) + ? $this->handlers[$extension] + : NULL; + } + + + /** + * Registers a handler for templates with a matching extension. + * + * @param string the extension of the templates to handle + * @param string the name of the already loaded class + * @param array optional; an array of options which is used + * when constructing a new instance + */ + function add_handler($extension, $class, $options = array()) + { + $this->handlers[$extension] = array($class, $options); + } + + + /** + * Returns the absolute path to the template. If the given argument starts + * with a slash or with a protocoll, this method just returns its arguments. + * + * @param string an incomplete template name + * + * @return string an absolute path to the incomplete template name + */ + function get_absolute_path($template) { + return preg_match('#^(/|\w+://)#', $template) + ? $template + : $this->get_path() . $template; + } + + + /** + * Find template given w/o extension. + * + * @param string the template's filename w/o extension + * + * @return mixed NULL if there no such file could be found, a string + * containing the complete file name otherwise + */ + function find_template($template) { + foreach ($this->handlers as $ext => $handler) { + $file = "$template.$ext"; + if (file_exists($file)) { + return $file; + } + } + return NULL; + } + + + /** + * Returns the file extension if there is one. + * + * @param string an possibly incomplete template file name + * + * @return mixed a string containing the file extension if there is one, + * NULL otherwise + */ + function get_extension($file) { + $matches = array(); + $matched = preg_match('/\.([^\/.]+)$/', $file, $matches); + return $matched ? $matches[1] : NULL; + } + + + /** + * Class method to parse, render and return the presentation of a + * template. + * + * @param string A name of a template. + * @param array An associative array of attributes and their associated + * values. + * @param string A name of a layout template. + * + * @return string A string representing the rendered presentation. + */ + function render($name, $attributes = null, $layout = null) { + return $this->open($name)->render($attributes, $layout); + } +} diff --git a/vendor/flexi/test/all_tests.php b/vendor/flexi/test/all_tests.php new file mode 100644 index 0000000..939413a --- /dev/null +++ b/vendor/flexi/test/all_tests.php @@ -0,0 +1,41 @@ +<?php + +# Copyright (c) 2008 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# load required files +require_once 'simpletest/unit_tester.php'; +require_once 'simpletest/reporter.php'; +require_once 'simpletest/collector.php'; + + +# collect all tests +$all = new TestSuite('All tests'); + +$all->collect(dirname(__FILE__).'/lib', new SimplePatternCollector('/test.php$/')); +$all->collect(dirname(__FILE__).'/lib/helper', new SimplePatternCollector('/test.php$/')); + +# use text reporter if cli +if (sizeof($_SERVER['argv'])) + $all->run(new TextReporter()); + +# use html reporter if cgi +else + $all->run(new HtmlReporter()); diff --git a/vendor/flexi/test/coverage.php b/vendor/flexi/test/coverage.php new file mode 100644 index 0000000..cbf694a --- /dev/null +++ b/vendor/flexi/test/coverage.php @@ -0,0 +1,43 @@ +<?php + +# Copyright (c) 2007 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +$PHPCOVERAGE_HOME = '/usr/share/php/spikephpcoverage/src/'; +$PHPCOVERAGE_REPORT_DIR = '/tmp/report/'; +$PHPCOVERAGE_APPBASE_PATH = '/tmp/report/'; + +require_once $PHPCOVERAGE_HOME . 'phpcoverage.inc.php'; +require_once $PHPCOVERAGE_HOME . 'CoverageRecorder.php'; +require_once $PHPCOVERAGE_HOME . 'reporter/HtmlCoverageReporter.php'; + +$reporter = new HtmlCoverageReporter('Code Coverage Report', '', $PHPCOVERAGE_REPORT_DIR); + +$includePaths = array('../lib'); +$excludePaths = array(); +$cov = new CoverageRecorder($includePaths, $excludePaths, $reporter); + +$cov->startInstrumentation(); +include 'all_tests.php'; +$cov->stopInstrumentation(); + +$cov->generateReport(); +$reporter->printTextSummary(); diff --git a/vendor/flexi/test/flexi_tests.php b/vendor/flexi/test/flexi_tests.php new file mode 100644 index 0000000..d012ffc --- /dev/null +++ b/vendor/flexi/test/flexi_tests.php @@ -0,0 +1,61 @@ +<?php + +# Copyright (c) 2008 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +require_once 'simpletest/unit_tester.php'; +require_once 'simpletest/mock_objects.php'; +require_once 'simpletest/autorun.php'; + +require_once 'varstream.php'; + +/** + * Setting up tests for flexi. + * + * @package flexi + * @subpackage test + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id$ + */ + +class Flexi_Tests { + + /** + * Calling this function initializes everything necessary to test flexi. + * + * @return void + */ + function setup() { + static $once; + + if (!$once) { + + # define TEST_DIR + define('TEST_DIR', dirname(__FILE__)); + + # load required files + require_once TEST_DIR . '/../lib/flexi.php'; + + $once = TRUE; + } + } +} diff --git a/vendor/flexi/test/lib/helper/js_helper_test.php b/vendor/flexi/test/lib/helper/js_helper_test.php new file mode 100644 index 0000000..f7c36ff --- /dev/null +++ b/vendor/flexi/test/lib/helper/js_helper_test.php @@ -0,0 +1,93 @@ +<?php + +# Copyright (c) 2008 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +require_once dirname(__FILE__) . '/../../flexi_tests.php'; +Flexi_Tests::setup(); +require_once dirname(__FILE__) . '/../../../lib/helper/js_helper.php'; + +/** + * Testcase for JsHelper. + * + * @package flexi + * @subpackage test + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id: js_helper_test.php 4194 2006-10-24 14:52:31Z mlunzena $ + */ + +class JsHelperTestCase extends UnitTestCase { + + function setUp() { + } + + function tearDown() { + } + + + function test_link_to_function() { + $foo = JsHelper::link_to_function('Greeting', "alert('Hello world!')", + array('title' => 'hello')); + $exp = '<a title="hello" href="#" onclick="alert(\'Hello world!\'); '. + 'return false;">Greeting</a>'; + $this->assertEqual($exp, $foo); + } + + function test_button_to_function() { + $foo = JsHelper::button_to_function("Greeting", "alert('Hello world!')"); + $exp = '<input type="button" value="Greeting" '. + 'onclick="alert(\'Hello world!\');" />'; + $this->assertEqual($exp, $foo); + + $foo = JsHelper::button_to_function("Greeting", "alert('Hello world!')", + array('onclick' => 'alert(\'One\')')); + $exp = '<input onclick="alert(\'One\'); alert(\'Hello world!\');" '. + 'type="button" value="Greeting" />'; + $this->assertEqual($exp, $foo); + } + + function test_escape_javascript() { + $strings = array( + "\r" => '\n', + "\n" => '\n', + '\\' => '\\\\', + '</' => '<\\/', + '"' => '\\"', + "'" => "\\'" + ); + foreach ($strings as $string => $expect) { + $this->assertEqual(JsHelper::escape_javascript($string), $expect); + } + } + + function test_javascript_tag() { + $foo = JsHelper::javascript_tag("alert('All is good')"); + $exp = <<<EXPECTED +<script type="text/javascript"> +//<![CDATA[ +alert('All is good') +//]]> +</script> +EXPECTED; + $this->assertEqual($exp, $foo); + } +} diff --git a/vendor/flexi/test/lib/helper/prototype_helper_test.php b/vendor/flexi/test/lib/helper/prototype_helper_test.php new file mode 100644 index 0000000..3307f6a --- /dev/null +++ b/vendor/flexi/test/lib/helper/prototype_helper_test.php @@ -0,0 +1,180 @@ +<?php + +# Copyright (c) 2008 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +require_once dirname(__FILE__) . '/../../flexi_tests.php'; +Flexi_Tests::setup(); +require_once dirname(__FILE__) . '/../../../lib/helper/prototype_helper.php'; + +/** + * Testcase for JsHelper. + * + * @package flexi + * @subpackage test + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id: prototype_helper_test.php 4194 2006-10-24 14:52:31Z mlunzena $ + */ + +class PrototypeHelperTestCase extends UnitTestCase { + + function setUp() { + } + + function tearDown() { + } + + function test_link_to_remote() { + + $foo = PrototypeHelper::link_to_remote('Delete this post', + array('update' => 'posts', + 'url' => 'http://destroy?id=1')); + $exp = '<a href="#" onclick="new Ajax.Updater(\'posts\', \'http://destroy?id=1\', {asynchronous:true, evalScripts:false}); return false;">Delete this post</a>'; + $this->assertEqual($exp, $foo); + + + $foo = PrototypeHelper::link_to_remote('Delete this post', + array('update' => array('success' => 'posts', + 'failure' => 'error'), + 'url' => 'http://destroy?id=1')); + $exp = '<a href="#" onclick="new Ajax.Updater({success:\'posts\',failure:\'error\'}, \'http://destroy?id=1\', {asynchronous:true, evalScripts:false}); return false;">Delete this post</a>'; + $this->assertEqual($exp, $foo); + + + $foo = PrototypeHelper::link_to_remote('Delete this post', + array('url' => 'http://destroy?id=1', + 404 => "alert('Not found...?')", + 'failure' => "alert('HTTPError!')")); + $exp = '<a href="#" onclick="new Ajax.Request(\'http://destroy?id=1\', {asynchronous:true, evalScripts:false, on404:function(request, json){alert(\'Not found...?\')}, onFailure:function(request, json){alert(\'HTTPError!\')}}); return false;">Delete this post</a>'; + $this->assertEqual($exp, $foo); + } + + function test_periodically_call_remote() { + $foo = PrototypeHelper::periodically_call_remote( + array('url' => 'http://clock', + 'frequency' => 2, + 'update' => 'clock')); + $exp = <<<EXPECTED +<script type="text/javascript"> +//<![CDATA[ +new PeriodicalExecuter(function() {new Ajax.Updater('clock', 'http://clock', {asynchronous:true, evalScripts:false})}, 2) +//]]> +</script> +EXPECTED; + $this->assertEqual($exp, $foo); + } + + function test_form_remote_tag() { + + $foo = PrototypeHelper::form_remote_tag(array( + 'url' => 'http://tag_add', + 'update' => 'question_tags', + 'loading' => "Element.show('indicator'); tag.value = '';", + 'complete' => "Element.hide('indicator');")); + $exp = <<<EXPECTED +<form onsubmit="new Ajax.Updater('question_tags', 'http://tag_add', {asynchronous:true, evalScripts:false, onComplete:function(request, json){Element.hide('indicator');}, onLoading:function(request, json){Element.show('indicator'); tag.value = '';}, parameters:Form.serialize(this)}); return false;" action="http://tag_add" method="post"> +EXPECTED; + $this->assertEqual($exp, $foo); + } + + function test_submit_to_remote() { + $foo = PrototypeHelper::submit_to_remote('name', 'value', + array( + 'url' => 'http://tag_add', + 'update' => 'question_tags', + 'loading' => "Element.show('indicator'); tag.value = '';", + 'complete' => "Element.hide('indicator');")); + $exp = <<<EXPECTED +<input type="button" onclick="new Ajax.Updater('question_tags', 'http://tag_add', {asynchronous:true, evalScripts:false, onComplete:function(request, json){Element.hide('indicator');}, onLoading:function(request, json){Element.show('indicator'); tag.value = '';}, parameters:Form.serialize(this.form)}); return false;" name="name" value="value" /> +EXPECTED; + $this->assertEqual($exp, $foo); + } + + function test_update_element_function() { + $foo = PrototypeHelper::update_element_function('element_id', + array('position' => 'bottom', 'content' => "<p>New product!</p>")); + $exp = "new Insertion.Bottom('element_id','<p>New product!<\/p>');\n"; + $this->assertEqual($exp, $foo); + } + + function test_evaluate_remote_response() { + $foo = PrototypeHelper::evaluate_remote_response(); + $exp = 'eval(request.responseText)'; + $this->assertEqual($exp, $foo); + } + + function test_remote_function() { + $foo = PrototypeHelper::remote_function(array('update' => 'options', + 'url' => 'http://update')); + $exp = <<<EXPECTED +new Ajax.Updater('options', 'http://update', {asynchronous:true, evalScripts:false}) +EXPECTED; + $this->assertEqual($exp, $foo); + } + + function test_observe_field() { + $foo = PrototypeHelper::observe_field('element_id', + array('url' => 'http://update', + 'frequency' => 0, + 'update' => 'update_id')); + $exp = <<<EXPECTED +<script type="text/javascript"> +//<![CDATA[ +new Form.Element.EventObserver("element_id", 0, function(element, value) {new Ajax.Updater('update_id', 'http://update', {asynchronous:true, evalScripts:false, parameters:value})}); +//]]> +</script> +EXPECTED; + $this->assertEqual($exp, $foo); + } + + function test_observe_form() { + $foo = PrototypeHelper::observe_form('form_id', + array('url' => 'http://update', + 'frequency' => 0, + 'update' => 'update_id')); + $exp = <<<EXPECTED +<script type="text/javascript"> +//<![CDATA[ +new Form.EventObserver("form_id", 0, function(element, value) {new Ajax.Updater('update_id', 'http://update', {asynchronous:true, evalScripts:false, parameters:value})}); +//]]> +</script> +EXPECTED; + $this->assertEqual($exp, $foo); + } + + function test_jsgen() { + $page = new Flexi_JavascriptGenerator(); + $page->insert_html('bottom', 'list', '<li>Last item</li>'); + $page->replace_html('person-45', 'Deleted.'); + $page->replace('person-45', 'Deleted.'); + $page->remove('id-1', 'id-2', 'id-3'); + $page->show('id-1', 'id-2', 'id-3'); + $page->hide('id-1', 'id-2', 'id-3'); + $page->toggle('id-1', 'id-2', 'id-3'); + $page->alert('Some "Message"!\/'); + $page->redirect_to('http://www.google.com'); + $page->delay('alert("Hallo Welt")', 23); + $page->visual_effect('highlight', 'id-42'); + # TODO (mlunzena) no asserts + # var_dump($page->to_s()); + } +} diff --git a/vendor/flexi/test/lib/helper/scriptaculous_helper_test.php b/vendor/flexi/test/lib/helper/scriptaculous_helper_test.php new file mode 100644 index 0000000..30531f9 --- /dev/null +++ b/vendor/flexi/test/lib/helper/scriptaculous_helper_test.php @@ -0,0 +1,67 @@ +<?php + +# Copyright (c) 2008 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +require_once dirname(__FILE__) . '/../../flexi_tests.php'; +Flexi_Tests::setup(); +require_once dirname(__FILE__) . '/../../../lib/helper/scriptaculous_helper.php'; + +/** + * Testcase for ScriptaculousHelper. + * + * @package flexi + * @subpackage test + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id$ + */ + +class ScriptaculousHelperTestCase extends UnitTestCase { + + function setUp() { + } + + function tearDown() { + } + + function test_visual_effect() { + $result = ScriptaculousHelper::visual_effect('toggle_blind', 'id'); + $expected = 'new Effect.toggle(\'id\', \'blind\', {})'; + $this->assertEqual($result, $expected); + } + + function test_sortable_element() { + $result = ScriptaculousHelper::sortable_element('list', array( + 'url' => '/', + 'containment' => array('left', 'right'), + 'only' => 'middle', + )); + $expected = <<<SCRIPT +<script type="text/javascript"> +//<![CDATA[ +Sortable.create('list', {containment:['left','right'], onUpdate:function(){new Ajax.Request('/', {asynchronous:true, evalScripts:false, parameters:Sortable.serialize('list')})}, only:'middle'}) +//]]> +</script> +SCRIPT; + $this->assertEqual($result, $expected); + } +} diff --git a/vendor/flexi/test/lib/helper/tag_helper_test.php b/vendor/flexi/test/lib/helper/tag_helper_test.php new file mode 100644 index 0000000..bd872f7 --- /dev/null +++ b/vendor/flexi/test/lib/helper/tag_helper_test.php @@ -0,0 +1,65 @@ +<?php + +# Copyright (c) 2008 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +require_once dirname(__FILE__) . '/../../flexi_tests.php'; +Flexi_Tests::setup(); +require_once dirname(__FILE__) . '/../../../lib/helper/tag_helper.php'; + +/** + * Testcase for TagHelper. + * + * @package flexi + * @subpackage test + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id: tag_helper_test.php 4194 2006-10-24 14:52:31Z mlunzena $ + */ + +class TagHelperTestCase extends UnitTestCase { + + function setUp() { + } + + function tearDown() { + } + + function test_tag() { + $foo = TagHelper::tag('img', array('src' => 'test.gif')); + $this->assertEqual('<img src="test.gif" />', $foo); + } + + function test_content_tag() { + $foo = TagHelper::content_tag('a', '#', array('href' => '#')); + $this->assertEqual('<a href="#">#</a>', $foo); + } + + function test_cdata_section() { + $foo = TagHelper::cdata_section('foo'); + $this->assertEqual('<![CDATA[foo]]>', $foo); + } + + function test_string_to_array() { + $array = TagHelper::string_to_array('id="anId" class=\'aClass\''); + $this->assertEqual(array('id' => 'anId', 'class' => 'aClass'), $array); + } +} diff --git a/vendor/flexi/test/lib/helper/text_helper_test.php b/vendor/flexi/test/lib/helper/text_helper_test.php new file mode 100644 index 0000000..1ac7834 --- /dev/null +++ b/vendor/flexi/test/lib/helper/text_helper_test.php @@ -0,0 +1,80 @@ +<?php + +# Copyright (c) 2008 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +require_once dirname(__FILE__) . '/../../flexi_tests.php'; +Flexi_Tests::setup(); +require_once dirname(__FILE__) . '/../../../lib/helper/text_helper.php'; + +/** + * Testcase for TextHelper. + * + * @package flexi + * @subpackage test + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id$ + */ + +class TextHelperTestCase extends UnitTestCase { + + function setUp() { + } + + function tearDown() { + TextHelper::reset_cycle(); + } + + function test_camelize() { + $foo = TextHelper::camelize('foo_bar_baz/FOO is great'); + $this->assertEqual('FooBarBazFOOIsGreat', $foo); + } + + function test_simple_cycle() { + $this->assertEqual('odd', TextHelper::cycle('odd', 'even')); + $this->assertEqual('even', TextHelper::cycle('odd', 'even')); + $this->assertEqual('odd', TextHelper::cycle('odd', 'even')); + $this->assertEqual('even', TextHelper::cycle('odd', 'even')); + } + + function test_reset_cycle() { + $this->assertEqual('odd', TextHelper::cycle('odd', 'even')); + $this->assertEqual('even', TextHelper::cycle('odd', 'even')); + $this->assertEqual('odd', TextHelper::cycle('odd', 'even')); + TextHelper::reset_cycle(); + $this->assertEqual('odd', TextHelper::cycle('odd', 'even')); + } + + function test_double_cycle_should_reset_each_time() { + $this->assertEqual('odd', TextHelper::cycle('odd', 'even')); + $this->assertEqual('red', TextHelper::cycle('red', 'black')); + $this->assertEqual('odd', TextHelper::cycle('odd', 'even')); + $this->assertEqual('red', TextHelper::cycle('red', 'black')); + } + + function test_named_cycles() { + $this->assertEqual('odd', TextHelper::cycle(array('odd', 'even', 'name' => '1st'))); + $this->assertEqual('red', TextHelper::cycle('red', 'black')); + $this->assertEqual('even', TextHelper::cycle(array('odd', 'even', 'name' => '1st'))); + $this->assertEqual('black', TextHelper::cycle('red', 'black')); + } +} diff --git a/vendor/flexi/test/lib/js_template_test.php b/vendor/flexi/test/lib/js_template_test.php new file mode 100644 index 0000000..77f577b --- /dev/null +++ b/vendor/flexi/test/lib/js_template_test.php @@ -0,0 +1,56 @@ +<?php + +# Copyright (c) 2008 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +require_once dirname(__FILE__) . '/../flexi_tests.php'; +Flexi_Tests::setup(); + +class JsTemplateTestCase extends UnitTestCase { + + var $factory; + + function setUp() { + $this->setUpFS(); + $this->factory = new Flexi_TemplateFactory('var://templates/'); + } + + function tearDown() { + unset($this->factory); + + stream_wrapper_unregister("var"); + } + + function setUpFS() { + ArrayFileStream::set_filesystem(array( + 'templates' => array( + 'foo.pjs' => '', + 'layout.pjs' => '' + ))); + stream_wrapper_register("var", "ArrayFileStream") or die("Failed to register protocol"); + } + + + function test_something() { + $template = $this->factory->open('foo'); + $template->render(array('whom' => 'world'), 'layout'); + } +} + diff --git a/vendor/flexi/test/lib/php_template_test.php b/vendor/flexi/test/lib/php_template_test.php new file mode 100644 index 0000000..1c6d85c --- /dev/null +++ b/vendor/flexi/test/lib/php_template_test.php @@ -0,0 +1,182 @@ +<?php + +# Copyright (c) 2008 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +require_once dirname(__FILE__) . '/../flexi_tests.php'; +Flexi_Tests::setup(); + +class PhpTemplateTestCase extends UnitTestCase { + + var $factory; + + + function setUp() { + $this->setUpFS(); + $this->factory = new Flexi_TemplateFactory('var://templates/'); + } + + + function tearDown() { + unset($this->factory); + + stream_wrapper_unregister("var"); + } + + function setUpFS() { + ArrayFileStream::set_filesystem(array( + 'templates' => array( + + 'foo_using_partial.php' => + 'Hello, <?= $this->render_partial("foos_partial") ?>!', + + 'foos_partial.php' => + '<h1><?= $whom ?> at <?= $when ?></h1>', + + 'foo_with_partial_collection.php' => + '[<?= $this->render_partial_collection("item", $items, "spacer") ?>]', + + 'item.php' => + '"<?= $item ?>"', + + 'spacer.php' => + ', ', + + 'attributes.php' => + '<? foreach (get_defined_vars() as $name => $value) : ?>' . + '<?= $name ?><?= $value ?>'. + '<? endforeach ?>', + + 'foo.php' => + 'Hello, <?= $whom ?>!', + + 'layout.php' => + '[<?= $content_for_layout ?>]', + ))); + stream_wrapper_register("var", "ArrayFileStream") or die("Failed to register protocol"); + } + + function test_render_partial() { + $template = $this->factory->open('foo_using_partial'); + $template->set_attribute('whom', 'bar'); + $output = $template->render(array('when' => 'now')); + $spec = "Hello, <h1>bar at now</h1>!"; + $this->assertEqual($output, $spec); + } + + + function test_render_partial_collection() { + $template = $this->factory->open('foo_with_partial_collection'); + $result = $template->render_partial_collection('item', + range(1, 3), + 'spacer'); + $this->assertEqual('"1", "2", "3"', $result); + } + + + function test_should_override_attributes_with_those_passed_to_render() { + + $template = $this->factory->open('attributes'); + $template->set_attribute('foo', 'baz'); + + $template->render(array('foo' => 'bar')); + + $bar = $template->get_attribute('foo'); + $this->assertEqual($bar, 'bar'); + + $out = $template->render(); + + $bar = $template->get_attribute('foo'); + $this->assertEqual($bar, 'bar'); + } + + function test_render_without_layout() { + $foo = $this->factory->open('foo'); + $foo->set_attribute('whom', 'bar'); + $out = $foo->render(); + $this->assertEqual('Hello, bar!', $out); + } + + function test_render_with_layout() { + $foo = $this->factory->open('foo'); + $foo->set_attribute('whom', 'bar'); + $foo->set_layout('layout'); + $out = $foo->render(); + $this->assertEqual('[Hello, bar!]', $out); + } + + function test_render_with_layout_inline() { + $out = $this->factory->render('foo', array('whom' => 'bar'), 'layout'); + $this->assertEqual('[Hello, bar!]', $out); + } + + function test_render_with_missing_layout() { + $foo = $this->factory->open('foo'); + $this->expectException("Flexi_TemplateNotFoundException"); + $foo->set_layout('nosuchlayout'); + } + + function test_render_with_attributes() { + $foo = $this->factory->open('foo'); + $foo->set_attribute('whom', 'bar'); + $foo->set_layout('layout'); + $foo_out = $foo->render(); + + $bar = $this->factory->open('foo'); + $bar_out = $bar->render(array('whom' => 'bar'), 'layout'); + + $this->assertEqual($foo_out, $bar_out); + } +} + + +class PhpTemplatePartialBugTestCase extends UnitTestCase { + function setUp() { + $this->setUpFS(); + $this->factory = new Flexi_TemplateFactory('var://templates/'); + } + + function tearDown() { + unset($this->factory); + + stream_wrapper_unregister("var"); + } + + function setUpFS() { + ArrayFileStream::set_filesystem(array( + 'templates' => array( + 'layout.php' => + '<? $do_not_echo_this = $this->render_partial_collection("partial", range(1, 5));'. + 'echo $content_for_layout;', + 'partial.php' => + 'partial', + 'template.php' => + 'template', + ))); + stream_wrapper_register("var", "ArrayFileStream") or die("Failed to register protocol"); + } + + function test_partial_bug() { + $template = $this->factory->open('template'); + $template->set_layout('layout'); + $result = $template->render(); + $this->assertEqual($result, "template"); + } +} diff --git a/vendor/flexi/test/lib/template_factory_test.php b/vendor/flexi/test/lib/template_factory_test.php new file mode 100644 index 0000000..c2166da --- /dev/null +++ b/vendor/flexi/test/lib/template_factory_test.php @@ -0,0 +1,117 @@ +<?php + +# Copyright (c) 2008 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +require_once dirname(__FILE__) . '/../flexi_tests.php'; +Flexi_Tests::setup(); + +/** + * Testcase for TemplateFactory.php. + * + * @package flexi + * @subpackage test + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id: template_test.php 4194 2006-10-24 14:52:31Z mlunzena $ + */ + +class TemplateFactoryTestCase extends UnitTestCase { + + var $factory; + + function setUp() { + $this->setUpFS(); + $this->factory = new Flexi_TemplateFactory('var://templates'); + } + + function tearDown() { + unset($this->factory); + stream_wrapper_unregister("var"); + } + + function setUpFS() { + ArrayFileStream::set_filesystem(array( + 'templates' => array( + 'foo.php' => 'some content', + 'baz.unknown' => 'some content', + 'multiplebasenames' => array( + 'foo.txt' => 'there is no matching template class', + 'foo.php' => 'some content', + 'bar.txt' => 'there is no matching template class' + ) + ) + )); + stream_wrapper_register("var", "ArrayFileStream") + or die("Failed to register protocol"); + } + + function test_should_create_factory() { + $factory = new Flexi_TemplateFactory('.'); + $this->assertNotNull($factory); + } + + function test_should_create_factory_using_path() { + $path = 'var://'; + $factory = new Flexi_TemplateFactory($path); + $this->assertNotNull($factory); + } + + function test_should_open_template_using_relative_path() { + $foo = $this->factory->open('foo'); + $this->assertNotNull($foo); + } + + function test_should_open_template_using_absolute_path() { + $foo = $this->factory->open('var://templates/foo'); + $this->assertNotNull($foo); + } + + function test_should_throw_an_exception_opening_a_missing_template_without_file_extension() { + $this->expectException('Flexi_TemplateNotFoundException'); + $bar = $this->factory->open('bar'); + } + + function test_should_throw_an_exception_opening_a_missing_template_with_file_extension() { + $this->expectException('Flexi_TemplateNotFoundException'); + $bar = $this->factory->open('bar.php'); + } + + function test_should_open_template_using_extension() { + $foo = $this->factory->open('foo.php'); + $this->assertIsA($foo, 'Flexi_PhpTemplate'); + } + + function test_should_throw_an_exception_when_opening_a_template_with_unknown_extension() { + $this->expectException('Flexi_TemplateNotFoundException'); + $baz = $this->factory->open('baz'); + } + + function test_should_throw_an_exception_opening_a_template_in_a_non_existing_directory() { + $this->expectException('Flexi_TemplateNotFoundException'); + $this->factory->open('doesnotexist/foo'); + } + + function test_should_search_for_a_supported_template() { + $template = $this->factory->open('multiplebasenames/foo'); + $this->assertIsA($template, 'Flexi_PhpTemplate'); + } +} diff --git a/vendor/flexi/test/lib/template_test.php b/vendor/flexi/test/lib/template_test.php new file mode 100644 index 0000000..e0f2e0b --- /dev/null +++ b/vendor/flexi/test/lib/template_test.php @@ -0,0 +1,215 @@ +<?php + +# Copyright (c) 2008 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +require_once dirname(__FILE__) . '/../flexi_tests.php'; +Flexi_Tests::setup(); + +Mock::generate('Flexi_TemplateFactory'); +Mock::generatePartial('Flexi_Template', 'MockTemplate', array('_render')); + +class AnEmptyTemplate extends UnitTestCase { + + + var $factory; + + + function setUp() { + $this->factory = new MockFlexi_TemplateFactory(TEST_DIR . '/templates/template_tests'); + $template = new MockTemplate(); + $template->__construct('foo', $this->factory); + $this->factory->setReturnValue('open', $template); + } + + + function tearDown() { + unset($this->factory); + } + + + function test_should_have_no_attributes() { + $template = $this->factory->open(''); + $this->assertEqual(0, sizeof($template->get_attributes())); + } + + + function test_should_not_be_empty_after_setting_an_attribute() { + $template = $this->factory->open(''); + $template->set_attribute('foo', 'bar'); + $this->assertNotEqual(0, sizeof($template->get_attributes())); + } + + + function test_should_be_empty_after_clear() { + + $template = $this->factory->open('foo'); + + $this->assertEqual(0, sizeof($template->get_attributes())); + + $template->clear_attributes(); + $this->assertEqual(0, sizeof($template->get_attributes())); + } +} + + +class ATemplate extends UnitTestCase { + + + var $factory; + + + function setUp() { + $this->factory = new MockFlexi_TemplateFactory(TEST_DIR . '/templates/template_tests'); + $template = new MockTemplate(); + $template->__construct('foo', $this->factory); + $this->factory->setReturnValue('open', $template); + } + + + function tearDown() { + unset($this->factory); + } + + + function test_should_return_an_previously_set_attribute() { + $template = $this->factory->open('foo'); + $template->set_attribute('whom', 'bar'); + $this->assertEqual('bar', $template->get_attribute('whom')); + } + + + function test_should_return_previously_set_attributes() { + + $template = $this->factory->open('foo'); + $template->set_attributes(array('whom' => 'bar', 'foo' => 'baz')); + + $attributes = $template->get_attributes(); + $this->assertIsA($attributes, 'array'); + $this->assertEqual(2, sizeof($attributes)); + $this->assertEqual('bar', $attributes['whom']); + $this->assertEqual('baz', $attributes['foo']); + } + + + function test_should_merge_attributes_with_set_attributes() { + + $template = $this->factory->open('foo'); + $template->set_attributes(array('a' => 1, 'b' => 2)); + + $this->assertEqual(2, sizeof($template->get_attributes())); + $this->assertEqual(1, $template->get_attribute('a')); + $this->assertEqual(2, $template->get_attribute('b')); + + $template->set_attributes(array('b' => 8, 'c' => 9)); + + $this->assertEqual(3, sizeof($template->get_attributes())); + $this->assertEqual(1, $template->get_attribute('a')); + $this->assertEqual(8, $template->get_attribute('b')); + $this->assertEqual(9, $template->get_attribute('c')); + } + + + function test_should_be_empty_after_clear() { + + $template = $this->factory->open('foo'); + + $template->set_attributes(array('a' => 1, 'b' => 2)); + $this->assertNotEqual(0, sizeof($template->get_attributes())); + + $template->clear_attributes(); + $this->assertEqual(0, sizeof($template->get_attributes())); + } +} + + + +class MagicMethodsTemplate extends UnitTestCase { + + + var $factory; + + + function setUp() { + $this->factory = new MockFlexi_TemplateFactory(TEST_DIR . '/templates/template_tests'); + $this->template = new MockTemplate(); + $this->template->__construct('foo', $this->factory); + } + + + function tearDown() { + unset($this->factory); + unset($this->template); + } + + + function test_should_set_an_attribute_using_the_magic_methods() { + $this->template->foo = 'bar'; + $this->assertEqual('bar', $this->template->get_attribute('foo')); + } + + + function test_should_not_set_a_protected_member_field_as_an_attribute() { + $this->template->_layout = 'bar'; + $this->assertNull($this->template->get_attribute('_layout')); + $this->assertEqual('bar', $this->template->_layout); + } + + function test_should_overwrite_an_attribute() { + $this->template->set_attribute('foo', 'bar'); + $this->template->foo = 'baz'; + $this->assertEqual('baz', $this->template->get_attribute('foo')); + } + + function test_should_return_an_existing_attribute_using_the_magic_methods() { + $this->template->set_attribute('foo', 'bar'); + $this->assertEqual('bar', $this->template->foo); + } + + + function test_should_return_null_for_a_non_existing_attribute_using_the_magic_methods() { + $this->assertNull($this->template->foo); + } + + + function test_should_unset_an_attribute_using_the_magic_methods() { + $this->template->foo = 'bar'; + unset($this->template->foo); + $this->assertNull($this->template->foo); + } + + + function test_should_return_null_on_unsetting_a_non_attribute() { + unset($this->template->foo); + $this->assertNull($this->template->foo); + } + + + function test_should_return_true_on_isset_for_an_attribute() { + $this->template->foo = 'bar'; + $this->assertTrue(isset($this->template->foo)); + } + + + function test_should_return_false_on_isset_for_a_non_existing_attribute() { + $this->assertFalse(isset($this->template->foo)); + } +} + diff --git a/vendor/flexi/test/templates/factory_tests/baz.unknown_extension b/vendor/flexi/test/templates/factory_tests/baz.unknown_extension new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/vendor/flexi/test/templates/factory_tests/baz.unknown_extension diff --git a/vendor/flexi/test/templates/factory_tests/foo.php b/vendor/flexi/test/templates/factory_tests/foo.php new file mode 100644 index 0000000..0150355 --- /dev/null +++ b/vendor/flexi/test/templates/factory_tests/foo.php @@ -0,0 +1 @@ +Hallo, <?= $whom ?>!
\ No newline at end of file diff --git a/vendor/flexi/test/templates/layout_with_partials/layout.php b/vendor/flexi/test/templates/layout_with_partials/layout.php new file mode 100644 index 0000000..a037f25 --- /dev/null +++ b/vendor/flexi/test/templates/layout_with_partials/layout.php @@ -0,0 +1,3 @@ +<? +$do_not_echo_this = $this->render_partial_collection('partial', range(1, 5)); +echo $content_for_layout ?> diff --git a/vendor/flexi/test/templates/layout_with_partials/partial.php b/vendor/flexi/test/templates/layout_with_partials/partial.php new file mode 100644 index 0000000..b648ac9 --- /dev/null +++ b/vendor/flexi/test/templates/layout_with_partials/partial.php @@ -0,0 +1 @@ +partial diff --git a/vendor/flexi/test/templates/layout_with_partials/template.php b/vendor/flexi/test/templates/layout_with_partials/template.php new file mode 100644 index 0000000..3236cba --- /dev/null +++ b/vendor/flexi/test/templates/layout_with_partials/template.php @@ -0,0 +1 @@ +template diff --git a/vendor/flexi/test/templates/template_tests/attributes.php b/vendor/flexi/test/templates/template_tests/attributes.php new file mode 100644 index 0000000..8ee269a --- /dev/null +++ b/vendor/flexi/test/templates/template_tests/attributes.php @@ -0,0 +1,3 @@ +<? foreach (get_defined_vars() as $name => $value) : ?> +<?= $name ?><?= $value ?> +<? endforeach ?> diff --git a/vendor/flexi/test/templates/template_tests/foo.php b/vendor/flexi/test/templates/template_tests/foo.php new file mode 100644 index 0000000..0150355 --- /dev/null +++ b/vendor/flexi/test/templates/template_tests/foo.php @@ -0,0 +1 @@ +Hallo, <?= $whom ?>!
\ No newline at end of file diff --git a/vendor/flexi/test/templates/template_tests/foo_using_partial.php b/vendor/flexi/test/templates/template_tests/foo_using_partial.php new file mode 100644 index 0000000..813a0b6 --- /dev/null +++ b/vendor/flexi/test/templates/template_tests/foo_using_partial.php @@ -0,0 +1 @@ +Hallo, <?= $this->render_partial('foos_partial') ?>! diff --git a/vendor/flexi/test/templates/template_tests/foo_with_partial_collection.php b/vendor/flexi/test/templates/template_tests/foo_with_partial_collection.php new file mode 100644 index 0000000..3deeeed --- /dev/null +++ b/vendor/flexi/test/templates/template_tests/foo_with_partial_collection.php @@ -0,0 +1 @@ +[<?= $this->render_partial_collection('item', $items, 'spacer') ?>] diff --git a/vendor/flexi/test/templates/template_tests/foos_partial.php b/vendor/flexi/test/templates/template_tests/foos_partial.php new file mode 100644 index 0000000..8746c07 --- /dev/null +++ b/vendor/flexi/test/templates/template_tests/foos_partial.php @@ -0,0 +1 @@ +<h1><?= $whom ?> at <?= $when ?></h1> diff --git a/vendor/flexi/test/templates/template_tests/item.php b/vendor/flexi/test/templates/template_tests/item.php new file mode 100644 index 0000000..6cf5418 --- /dev/null +++ b/vendor/flexi/test/templates/template_tests/item.php @@ -0,0 +1 @@ +"<?= $item ?>" diff --git a/vendor/flexi/test/templates/template_tests/layouts/layout.php b/vendor/flexi/test/templates/template_tests/layouts/layout.php new file mode 100644 index 0000000..860be85 --- /dev/null +++ b/vendor/flexi/test/templates/template_tests/layouts/layout.php @@ -0,0 +1 @@ +[<?=$content_for_layout?>]
\ No newline at end of file diff --git a/vendor/flexi/test/templates/template_tests/spacer.php b/vendor/flexi/test/templates/template_tests/spacer.php new file mode 100644 index 0000000..41622b4 --- /dev/null +++ b/vendor/flexi/test/templates/template_tests/spacer.php @@ -0,0 +1 @@ +,
\ No newline at end of file diff --git a/vendor/flexi/test/varstream.php b/vendor/flexi/test/varstream.php new file mode 100644 index 0000000..2dd7c68 --- /dev/null +++ b/vendor/flexi/test/varstream.php @@ -0,0 +1,244 @@ +<?php + +class ArrayFileStream { + + private $open_file, $position; + + private static $fs; + + static function set_filesystem(array $fs) { + ArrayFileStream::$fs = $fs; + } + + private static function &get_element($path) { + $result =& ArrayFileStream::$fs; + foreach (preg_split('/\//', $path, -1, PREG_SPLIT_NO_EMPTY) as $element) { + if (!isset($result[$element])) { + $null = NULL; + return $null; + } + $result =& $result[$element]; + } + return $result; + } + + private static function &get_file($path) { + $url = parse_url($path); + $file =& self::get_element($url['host'] . $url['path']); + + if (is_null($file)) { + throw new Exception("file not found."); + } + return $file; + } + + function stream_close() { + + # nothing to do + } + + function stream_flush() { + + # nothing to do + } + + function stream_open($path, $mode, $options, $opened_path) { + + try { + $this->open_file =& self::get_file($path); + $this->position = 0; + return true; + } catch (Exception $e) { + return false; + } + } + + function stream_read($count) { + + $ret = substr($this->open_file, $this->position, $count); + $this->position += strlen($ret); + return $ret; + } + + function stream_write($data) { + + $left = substr($this->open_file, 0, $this->position); + $right = substr($this->open_file, $this->position + strlen($data)); + $this->open_file = $left . $data . $right; + $this->position += strlen($data); + return strlen($data); + } + + function stream_tell() { + + return $this->position; + } + + function stream_eof() { + + return $this->position >= strlen($this->open_file); + } + + function stream_seek($offset, $whence) { + + switch ($whence) { + case SEEK_SET: + if ($offset < strlen($this->open_file) && $offset >= 0) { + $this->position = $offset; + return true; + } + else { + return false; + } + break; + + case SEEK_CUR: + if ($offset >= 0) { + $this->position += $offset; + return true; + } + else { + return false; + } + break; + + case SEEK_END: + if (strlen($this->open_file) + $offset >= 0) { + $this->position = strlen($this->open_file) + $offset; + return true; + } + else { + return false; + } + break; + + default: + return false; + } + } + + function stream_stat() { + + return array('size' => is_array($this->open_file) + ? sizeof($this->open_file) + : strlen($this->open_file)); + } + + function unlink($path) { + + $parent =& self::get_file(dirname($path)); + + if (is_array($parent) && isset($parent[basename($path)])) { + unset($parent[basename($path)]); + return TRUE; + } + + return FALSE; + } + + function rename($path_from, $path_to) { + + throw new Exception('not implemented yet'); + } + + function mkdir($path, $mode, $options) { + + throw new Exception('not implemented yet'); + } + + function rmdir($path, $options) { + + throw new Exception('not implemented yet'); + } + + function dir_opendir($path, $options) { + try { + $this->open_file =& self::get_file($path); + } catch (Exception $e) { + return FALSE; + } + + return is_array($this->open_file); + } + + function url_stat($path, $flags) { + + try { + $file =& self::get_file($path); + } catch (Exception $e) { + return FALSE; + } + + $time = time(); + $keys = array( + 'dev' => 0, + 'ino' => 0, + 'mode' => is_array($file) ? 16832 : 33216, // chmod 700 + 'nlink' => 0, + 'uid' => function_exists('posix_getuid') ? posix_getuid() : 0, + 'gid' => function_exists('posix_getgid') ? posix_getgid() : 0, + 'rdev' => 0, + 'size' => $flags & STREAM_URL_STAT_QUIET + ? @strlen($file) : strlen($file), + 'atime' => $time, + 'mtime' => $time, + 'ctime' => $time, + 'blksize' => 0, + 'blocks' => 0 + ); + return array_merge(array_values($keys), $keys); + } + + function dir_readdir() { + if (!is_array($this->open_file)) { + return FALSE; + } + $result = key($this->open_file); + if ($result === NULL) { + return FALSE; + } + next($this->open_file); + return $result; + } + + function dir_rewinddir() { + + throw new Exception('not implemented yet'); + } + + function dir_closedir() { + reset($this->open_file); + unset($this->open_file); + } +} + + +#ArrayFileStream::set_filesystem( +# array('tmp' => +# array('a' => 'my content', +# 'b' => '<? echo "hallo welt!";'))); + + +#stream_wrapper_register("var", "ArrayFileStream") +# or die("Failed to register protocol"); + +#$fp = fopen("var://tmp/a", "rw+"); + + +#while (!feof($fp)) { +# var_dump(fgets($fp)); +#} + +#fwrite($fp, "line1\n"); +#fwrite($fp, "line2\n"); +#fwrite($fp, "line3\n"); + +#rewind($fp); + +#var_dump(file_get_contents('var://tmp/a')); + +#fclose($fp); +#unlink('var://tmp/a'); + +#var_dump(include 'var://tmp/b'); + diff --git a/vendor/flexi/vendor/mustache.php/LICENSE b/vendor/flexi/vendor/mustache.php/LICENSE new file mode 100644 index 0000000..63c95ac --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2010 Justin Hileman + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/flexi/vendor/mustache.php/Mustache.php b/vendor/flexi/vendor/mustache.php/Mustache.php new file mode 100644 index 0000000..18236aa --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/Mustache.php @@ -0,0 +1,931 @@ +<?php + +/** + * A Mustache implementation in PHP. + * + * {@link http://defunkt.github.com/mustache} + * + * Mustache is a framework-agnostic logic-less templating language. It enforces separation of view + * logic from template files. In fact, it is not even possible to embed logic in the template. + * + * This is very, very rad. + * + * @author Justin Hileman {@link http://justinhileman.com} + */ +class Mustache { + + const VERSION = '1.1.0'; + const SPEC_VERSION = '1.1.2'; + + /** + * Should this Mustache throw exceptions when it finds unexpected tags? + * + * @see self::_throwsException() + */ + protected $_throwsExceptions = array( + MustacheException::UNKNOWN_VARIABLE => false, + MustacheException::UNCLOSED_SECTION => true, + MustacheException::UNEXPECTED_CLOSE_SECTION => true, + MustacheException::UNKNOWN_PARTIAL => false, + MustacheException::UNKNOWN_PRAGMA => true, + ); + + // Override the escaper function. Defaults to `htmlspecialchars`. + protected $_escape; + + // Override charset passed to htmlentities() and htmlspecialchars(). Defaults to UTF-8. + protected $_charset = 'UTF-8'; + + /** + * Pragmas are macro-like directives that, when invoked, change the behavior or + * syntax of Mustache. + * + * They should be considered extremely experimental. Most likely their implementation + * will change in the future. + */ + + /** + * The {{%UNESCAPED}} pragma swaps the meaning of the {{normal}} and {{{unescaped}}} + * Mustache tags. That is, once this pragma is activated the {{normal}} tag will not be + * escaped while the {{{unescaped}}} tag will be escaped. + * + * Pragmas apply only to the current template. Partials, even those included after the + * {{%UNESCAPED}} call, will need their own pragma declaration. + * + * This may be useful in non-HTML Mustache situations. + */ + const PRAGMA_UNESCAPED = 'UNESCAPED'; + + /** + * Constants used for section and tag RegEx + */ + const SECTION_TYPES = '\^#\/'; + const TAG_TYPES = '#\^\/=!<>\\{&'; + + protected $_otag = '{{'; + protected $_ctag = '}}'; + + protected $_tagRegEx; + + protected $_template = ''; + protected $_context = array(); + protected $_partials = array(); + protected $_pragmas = array(); + + protected $_pragmasImplemented = array( + self::PRAGMA_UNESCAPED + ); + + protected $_localPragmas = array(); + + /** + * Mustache class constructor. + * + * This method accepts a $template string and a $view object. Optionally, pass an associative + * array of partials as well. + * + * Passing an $options array allows overriding certain Mustache options during instantiation: + * + * $options = array( + * // `escape` -- custom escaper callback; must be callable. + * 'escape' => function($text) { + * return htmlspecialchars($text, ENT_COMPAT, 'UTF-8'); + * }, + * + * // `charset` -- must be supported by `htmlspecialentities()`. defaults to 'UTF-8' + * 'charset' => 'ISO-8859-1', + * + * // opening and closing delimiters, as an array or a space-separated string + * 'delimiters' => '<% %>', + * + * // an array of pragmas to enable/disable + * 'pragmas' => array( + * Mustache::PRAGMA_UNESCAPED => true + * ), + * + * // an array of thrown exceptions to enable/disable + * 'throws_exceptions' => array( + * MustacheException::UNKNOWN_VARIABLE => false, + * MustacheException::UNCLOSED_SECTION => true, + * MustacheException::UNEXPECTED_CLOSE_SECTION => true, + * MustacheException::UNKNOWN_PARTIAL => false, + * MustacheException::UNKNOWN_PRAGMA => true, + * ), + * ); + * + * @access public + * @param string $template (default: null) + * @param mixed $view (default: null) + * @param array $partials (default: null) + * @param array $options (default: array()) + * @return void + */ + public function __construct($template = null, $view = null, $partials = null, array $options = null) { + if ($template !== null) $this->_template = $template; + if ($partials !== null) $this->_partials = $partials; + if ($view !== null) $this->_context = array($view); + if ($options !== null) $this->_setOptions($options); + } + + /** + * Helper function for setting options from constructor args. + * + * @access protected + * @param array $options + * @return void + */ + protected function _setOptions(array $options) { + if (isset($options['escape'])) { + if (!is_callable($options['escape'])) { + throw new InvalidArgumentException('Mustache constructor "escape" option must be callable'); + } + $this->_escape = $options['escape']; + } + + if (isset($options['charset'])) { + $this->_charset = $options['charset']; + } + + if (isset($options['delimiters'])) { + $delims = $options['delimiters']; + if (!is_array($delims)) { + $delims = array_map('trim', explode(' ', $delims, 2)); + } + $this->_otag = $delims[0]; + $this->_ctag = $delims[1]; + } + + if (isset($options['pragmas'])) { + foreach ($options['pragmas'] as $pragma_name => $pragma_value) { + if (!in_array($pragma_name, $this->_pragmasImplemented, true)) { + throw new MustacheException('Unknown pragma: ' . $pragma_name, MustacheException::UNKNOWN_PRAGMA); + } + } + $this->_pragmas = $options['pragmas']; + } + + if (isset($options['throws_exceptions'])) { + foreach ($options['throws_exceptions'] as $exception => $value) { + $this->_throwsExceptions[$exception] = $value; + } + } + } + + /** + * Mustache class clone method. + * + * A cloned Mustache instance should have pragmas, delimeters and root context + * reset to default values. + * + * @access public + * @return void + */ + public function __clone() { + $this->_otag = '{{'; + $this->_ctag = '}}'; + $this->_localPragmas = array(); + + if ($keys = array_keys($this->_context)) { + $last = array_pop($keys); + if ($this->_context[$last] instanceof Mustache) { + $this->_context[$last] =& $this; + } + } + } + + /** + * Render the given template and view object. + * + * Defaults to the template and view passed to the class constructor unless a new one is provided. + * Optionally, pass an associative array of partials as well. + * + * @access public + * @param string $template (default: null) + * @param mixed $view (default: null) + * @param array $partials (default: null) + * @return string Rendered Mustache template. + */ + public function render($template = null, $view = null, $partials = null) { + if ($template === null) $template = $this->_template; + if ($partials !== null) $this->_partials = $partials; + + $otag_orig = $this->_otag; + $ctag_orig = $this->_ctag; + + if ($view) { + $this->_context = array($view); + } else if (empty($this->_context)) { + $this->_context = array($this); + } + + $template = $this->_renderPragmas($template); + $template = $this->_renderTemplate($template); + + $this->_otag = $otag_orig; + $this->_ctag = $ctag_orig; + + return $template; + } + + /** + * Wrap the render() function for string conversion. + * + * @access public + * @return string + */ + public function __toString() { + // PHP doesn't like exceptions in __toString. + // catch any exceptions and convert them to strings. + try { + $result = $this->render(); + return $result; + } catch (Exception $e) { + return "Error rendering mustache: " . $e->getMessage(); + } + } + + /** + * Internal render function, used for recursive calls. + * + * @access protected + * @param string $template + * @return string Rendered Mustache template. + */ + protected function _renderTemplate($template) { + if ($section = $this->_findSection($template)) { + list($before, $type, $tag_name, $content, $after) = $section; + + $rendered_before = $this->_renderTags($before); + + $rendered_content = ''; + $val = $this->_getVariable($tag_name); + switch($type) { + // inverted section + case '^': + if (empty($val)) { + $rendered_content = $this->_renderTemplate($content); + } + break; + + // regular section + case '#': + // higher order sections + if ($this->_varIsCallable($val)) { + $rendered_content = $this->_renderTemplate(call_user_func($val, $content)); + } else if ($this->_varIsIterable($val)) { + foreach ($val as $local_context) { + $this->_pushContext($local_context); + $rendered_content .= $this->_renderTemplate($content); + $this->_popContext(); + } + } else if ($val) { + if (is_array($val) || is_object($val)) { + $this->_pushContext($val); + $rendered_content = $this->_renderTemplate($content); + $this->_popContext(); + } else { + $rendered_content = $this->_renderTemplate($content); + } + } + break; + } + + return $rendered_before . $rendered_content . $this->_renderTemplate($after); + } + + return $this->_renderTags($template); + } + + /** + * Prepare a section RegEx string for the given opening/closing tags. + * + * @access protected + * @param string $otag + * @param string $ctag + * @return string + */ + protected function _prepareSectionRegEx($otag, $ctag) { + return sprintf( + '/(?:(?<=\\n)[ \\t]*)?%s(?:(?P<type>[%s])(?P<tag_name>.+?)|=(?P<delims>.*?)=)%s\\n?/s', + preg_quote($otag, '/'), + self::SECTION_TYPES, + preg_quote($ctag, '/') + ); + } + + /** + * Extract the first section from $template. + * + * @access protected + * @param string $template + * @return array $before, $type, $tag_name, $content and $after + */ + protected function _findSection($template) { + $regEx = $this->_prepareSectionRegEx($this->_otag, $this->_ctag); + + $section_start = null; + $section_type = null; + $content_start = null; + + $search_offset = 0; + + $section_stack = array(); + $matches = array(); + while (preg_match($regEx, $template, $matches, PREG_OFFSET_CAPTURE, $search_offset)) { + if (isset($matches['delims'][0])) { + list($otag, $ctag) = explode(' ', $matches['delims'][0]); + $regEx = $this->_prepareSectionRegEx($otag, $ctag); + $search_offset = $matches[0][1] + strlen($matches[0][0]); + continue; + } + + $match = $matches[0][0]; + $offset = $matches[0][1]; + $type = $matches['type'][0]; + $tag_name = trim($matches['tag_name'][0]); + + $search_offset = $offset + strlen($match); + + switch ($type) { + case '^': + case '#': + if (empty($section_stack)) { + $section_start = $offset; + $section_type = $type; + $content_start = $search_offset; + } + array_push($section_stack, $tag_name); + break; + case '/': + if (empty($section_stack) || ($tag_name !== array_pop($section_stack))) { + if ($this->_throwsException(MustacheException::UNEXPECTED_CLOSE_SECTION)) { + throw new MustacheException('Unexpected close section: ' . $tag_name, MustacheException::UNEXPECTED_CLOSE_SECTION); + } + } + + if (empty($section_stack)) { + // $before, $type, $tag_name, $content, $after + return array( + substr($template, 0, $section_start), + $section_type, + $tag_name, + substr($template, $content_start, $offset - $content_start), + substr($template, $search_offset), + ); + } + break; + } + } + + if (!empty($section_stack)) { + if ($this->_throwsException(MustacheException::UNCLOSED_SECTION)) { + throw new MustacheException('Unclosed section: ' . $section_stack[0], MustacheException::UNCLOSED_SECTION); + } + } + } + + /** + * Prepare a pragma RegEx for the given opening/closing tags. + * + * @access protected + * @param string $otag + * @param string $ctag + * @return string + */ + protected function _preparePragmaRegEx($otag, $ctag) { + return sprintf( + '/%s%%\\s*(?P<pragma_name>[\\w_-]+)(?P<options_string>(?: [\\w]+=[\\w]+)*)\\s*%s\\n?/s', + preg_quote($otag, '/'), + preg_quote($ctag, '/') + ); + } + + /** + * Initialize pragmas and remove all pragma tags. + * + * @access protected + * @param string $template + * @return string + */ + protected function _renderPragmas($template) { + $this->_localPragmas = $this->_pragmas; + + // no pragmas + if (strpos($template, $this->_otag . '%') === false) { + return $template; + } + + $regEx = $this->_preparePragmaRegEx($this->_otag, $this->_ctag); + return preg_replace_callback($regEx, array($this, '_renderPragma'), $template); + } + + /** + * A preg_replace helper to remove {{%PRAGMA}} tags and enable requested pragma. + * + * @access protected + * @param mixed $matches + * @return void + * @throws MustacheException unknown pragma + */ + protected function _renderPragma($matches) { + $pragma = $matches[0]; + $pragma_name = $matches['pragma_name']; + $options_string = $matches['options_string']; + + if (!in_array($pragma_name, $this->_pragmasImplemented)) { + if ($this->_throwsException(MustacheException::UNKNOWN_PRAGMA)) { + throw new MustacheException('Unknown pragma: ' . $pragma_name, MustacheException::UNKNOWN_PRAGMA); + } else { + return ''; + } + } + + $options = array(); + foreach (explode(' ', trim($options_string)) as $o) { + if ($p = trim($o)) { + $p = explode('=', $p); + $options[$p[0]] = $p[1]; + } + } + + if (empty($options)) { + $this->_localPragmas[$pragma_name] = true; + } else { + $this->_localPragmas[$pragma_name] = $options; + } + + return ''; + } + + /** + * Check whether this Mustache has a specific pragma. + * + * @access protected + * @param string $pragma_name + * @return bool + */ + protected function _hasPragma($pragma_name) { + if (array_key_exists($pragma_name, $this->_localPragmas) && $this->_localPragmas[$pragma_name]) { + return true; + } else { + return false; + } + } + + /** + * Return pragma options, if any. + * + * @access protected + * @param string $pragma_name + * @return mixed + * @throws MustacheException Unknown pragma + */ + protected function _getPragmaOptions($pragma_name) { + if (!$this->_hasPragma($pragma_name)) { + if ($this->_throwsException(MustacheException::UNKNOWN_PRAGMA)) { + throw new MustacheException('Unknown pragma: ' . $pragma_name, MustacheException::UNKNOWN_PRAGMA); + } + } + + return (is_array($this->_localPragmas[$pragma_name])) ? $this->_localPragmas[$pragma_name] : array(); + } + + /** + * Check whether this Mustache instance throws a given exception. + * + * Expects exceptions to be MustacheException error codes (i.e. class constants). + * + * @access protected + * @param mixed $exception + * @return void + */ + protected function _throwsException($exception) { + return (isset($this->_throwsExceptions[$exception]) && $this->_throwsExceptions[$exception]); + } + + /** + * Prepare a tag RegEx for the given opening/closing tags. + * + * @access protected + * @param string $otag + * @param string $ctag + * @return string + */ + protected function _prepareTagRegEx($otag, $ctag, $first = false) { + return sprintf( + '/(?P<leading>(?:%s\\r?\\n)[ \\t]*)?%s(?P<type>[%s]?)(?P<tag_name>.+?)(?:\\2|})?%s(?P<trailing>\\s*(?:\\r?\\n|\\Z))?/s', + ($first ? '\\A|' : ''), + preg_quote($otag, '/'), + self::TAG_TYPES, + preg_quote($ctag, '/') + ); + } + + /** + * Loop through and render individual Mustache tags. + * + * @access protected + * @param string $template + * @return void + */ + protected function _renderTags($template) { + if (strpos($template, $this->_otag) === false) { + return $template; + } + + $first = true; + $this->_tagRegEx = $this->_prepareTagRegEx($this->_otag, $this->_ctag, true); + + $html = ''; + $matches = array(); + while (preg_match($this->_tagRegEx, $template, $matches, PREG_OFFSET_CAPTURE)) { + $tag = $matches[0][0]; + $offset = $matches[0][1]; + $modifier = $matches['type'][0]; + $tag_name = trim($matches['tag_name'][0]); + + if (isset($matches['leading']) && $matches['leading'][1] > -1) { + $leading = $matches['leading'][0]; + } else { + $leading = null; + } + + if (isset($matches['trailing']) && $matches['trailing'][1] > -1) { + $trailing = $matches['trailing'][0]; + } else { + $trailing = null; + } + + $html .= substr($template, 0, $offset); + + $next_offset = $offset + strlen($tag); + if ((substr($html, -1) == "\n") && (substr($template, $next_offset, 1) == "\n")) { + $next_offset++; + } + $template = substr($template, $next_offset); + + $html .= $this->_renderTag($modifier, $tag_name, $leading, $trailing); + + if ($first == true) { + $first = false; + $this->_tagRegEx = $this->_prepareTagRegEx($this->_otag, $this->_ctag); + } + } + + return $html . $template; + } + + /** + * Render the named tag, given the specified modifier. + * + * Accepted modifiers are `=` (change delimiter), `!` (comment), `>` (partial) + * `{` or `&` (don't escape output), or none (render escaped output). + * + * @access protected + * @param string $modifier + * @param string $tag_name + * @param string $leading Whitespace + * @param string $trailing Whitespace + * @throws MustacheException Unmatched section tag encountered. + * @return string + */ + protected function _renderTag($modifier, $tag_name, $leading, $trailing) { + switch ($modifier) { + case '=': + return $this->_changeDelimiter($tag_name, $leading, $trailing); + break; + case '!': + return $this->_renderComment($tag_name, $leading, $trailing); + break; + case '>': + case '<': + return $this->_renderPartial($tag_name, $leading, $trailing); + break; + case '{': + // strip the trailing } ... + if ($tag_name[(strlen($tag_name) - 1)] == '}') { + $tag_name = substr($tag_name, 0, -1); + } + case '&': + if ($this->_hasPragma(self::PRAGMA_UNESCAPED)) { + return $this->_renderEscaped($tag_name, $leading, $trailing); + } else { + return $this->_renderUnescaped($tag_name, $leading, $trailing); + } + break; + case '#': + case '^': + case '/': + // remove any leftover section tags + return $leading . $trailing; + break; + default: + if ($this->_hasPragma(self::PRAGMA_UNESCAPED)) { + return $this->_renderUnescaped($modifier . $tag_name, $leading, $trailing); + } else { + return $this->_renderEscaped($modifier . $tag_name, $leading, $trailing); + } + break; + } + } + + /** + * Returns true if any of its args contains the "\r" character. + * + * @access protected + * @param string $str + * @return boolean + */ + protected function _stringHasR($str) { + foreach (func_get_args() as $arg) { + if (strpos($arg, "\r") !== false) { + return true; + } + } + return false; + } + + /** + * Escape and return the requested tag. + * + * @access protected + * @param string $tag_name + * @param string $leading Whitespace + * @param string $trailing Whitespace + * @return string + */ + protected function _renderEscaped($tag_name, $leading, $trailing) { + $value = $this->_renderUnescaped($tag_name, '', ''); + if (isset($this->_escape)) { + $rendered = call_user_func($this->_escape, $value); + } else { + $rendered = htmlentities($value, ENT_COMPAT, $this->_charset); + } + + return $leading . $rendered . $trailing; + } + + /** + * Render a comment (i.e. return an empty string). + * + * @access protected + * @param string $tag_name + * @param string $leading Whitespace + * @param string $trailing Whitespace + * @return string + */ + protected function _renderComment($tag_name, $leading, $trailing) { + if ($leading !== null && $trailing !== null) { + if (strpos($leading, "\n") === false) { + return ''; + } + return $this->_stringHasR($leading, $trailing) ? "\r\n" : "\n"; + } + return $leading . $trailing; + } + + /** + * Return the requested tag unescaped. + * + * @access protected + * @param string $tag_name + * @param string $leading Whitespace + * @param string $trailing Whitespace + * @return string + */ + protected function _renderUnescaped($tag_name, $leading, $trailing) { + $val = $this->_getVariable($tag_name); + + if ($this->_varIsCallable($val)) { + $val = $this->_renderTemplate(call_user_func($val)); + } + + return $leading . $val . $trailing; + } + + /** + * Render the requested partial. + * + * @access protected + * @param string $tag_name + * @param string $leading Whitespace + * @param string $trailing Whitespace + * @return string + */ + protected function _renderPartial($tag_name, $leading, $trailing) { + $partial = $this->_getPartial($tag_name); + if ($leading !== null && $trailing !== null) { + $whitespace = trim($leading, "\r\n"); + $partial = preg_replace('/(\\r?\\n)(?!$)/s', "\\1" . $whitespace, $partial); + } + + $view = clone($this); + + if ($leading !== null && $trailing !== null) { + return $leading . $view->render($partial); + } else { + return $leading . $view->render($partial) . $trailing; + } + } + + /** + * Change the Mustache tag delimiter. This method also replaces this object's current + * tag RegEx with one using the new delimiters. + * + * @access protected + * @param string $tag_name + * @param string $leading Whitespace + * @param string $trailing Whitespace + * @return string + */ + protected function _changeDelimiter($tag_name, $leading, $trailing) { + list($otag, $ctag) = explode(' ', $tag_name); + $this->_otag = $otag; + $this->_ctag = $ctag; + + $this->_tagRegEx = $this->_prepareTagRegEx($this->_otag, $this->_ctag); + + if ($leading !== null && $trailing !== null) { + if (strpos($leading, "\n") === false) { + return ''; + } + return $this->_stringHasR($leading, $trailing) ? "\r\n" : "\n"; + } + return $leading . $trailing; + } + + /** + * Push a local context onto the stack. + * + * @access protected + * @param array &$local_context + * @return void + */ + protected function _pushContext(&$local_context) { + $new = array(); + $new[] =& $local_context; + foreach (array_keys($this->_context) as $key) { + $new[] =& $this->_context[$key]; + } + $this->_context = $new; + } + + /** + * Remove the latest context from the stack. + * + * @access protected + * @return void + */ + protected function _popContext() { + $new = array(); + + $keys = array_keys($this->_context); + array_shift($keys); + foreach ($keys as $key) { + $new[] =& $this->_context[$key]; + } + $this->_context = $new; + } + + /** + * Get a variable from the context array. + * + * If the view is an array, returns the value with array key $tag_name. + * If the view is an object, this will check for a public member variable + * named $tag_name. If none is available, this method will execute and return + * any class method named $tag_name. Failing all of the above, this method will + * return an empty string. + * + * @access protected + * @param string $tag_name + * @throws MustacheException Unknown variable name. + * @return string + */ + protected function _getVariable($tag_name) { + if ($tag_name === '.') { + return $this->_context[0]; + } else if (strpos($tag_name, '.') !== false) { + $chunks = explode('.', $tag_name); + $first = array_shift($chunks); + + $ret = $this->_findVariableInContext($first, $this->_context); + foreach ($chunks as $next) { + // Slice off a chunk of context for dot notation traversal. + $c = array($ret); + $ret = $this->_findVariableInContext($next, $c); + } + return $ret; + } else { + return $this->_findVariableInContext($tag_name, $this->_context); + } + } + + /** + * Get a variable from the context array. Internal helper used by getVariable() to abstract + * variable traversal for dot notation. + * + * @access protected + * @param string $tag_name + * @param array $context + * @throws MustacheException Unknown variable name. + * @return string + */ + protected function _findVariableInContext($tag_name, $context) { + foreach ($context as $view) { + if (is_object($view)) { + if (method_exists($view, $tag_name)) { + return $view->$tag_name(); + } else if (isset($view->$tag_name)) { + return $view->$tag_name; + } + } else if (is_array($view) && array_key_exists($tag_name, $view)) { + return $view[$tag_name]; + } + } + + if ($this->_throwsException(MustacheException::UNKNOWN_VARIABLE)) { + throw new MustacheException("Unknown variable: " . $tag_name, MustacheException::UNKNOWN_VARIABLE); + } else { + return ''; + } + } + + /** + * Retrieve the partial corresponding to the requested tag name. + * + * Silently fails (i.e. returns '') when the requested partial is not found. + * + * @access protected + * @param string $tag_name + * @throws MustacheException Unknown partial name. + * @return string + */ + protected function _getPartial($tag_name) { + if ((is_array($this->_partials) || $this->_partials instanceof ArrayAccess) && isset($this->_partials[$tag_name])) { + return $this->_partials[$tag_name]; + } + + if ($this->_throwsException(MustacheException::UNKNOWN_PARTIAL)) { + throw new MustacheException('Unknown partial: ' . $tag_name, MustacheException::UNKNOWN_PARTIAL); + } else { + return ''; + } + } + + /** + * Check whether the given $var should be iterated (i.e. in a section context). + * + * @access protected + * @param mixed $var + * @return bool + */ + protected function _varIsIterable($var) { + return $var instanceof Traversable || (is_array($var) && !array_diff_key($var, array_keys(array_keys($var)))); + } + + /** + * Higher order sections helper: tests whether the section $var is a valid callback. + * + * In Mustache.php, a variable is considered 'callable' if the variable is: + * + * 1. an anonymous function. + * 2. an object and the name of a public function, i.e. `array($SomeObject, 'methodName')` + * 3. a class name and the name of a public static function, i.e. `array('SomeClass', 'methodName')` + * + * @access protected + * @param mixed $var + * @return bool + */ + protected function _varIsCallable($var) { + return !is_string($var) && is_callable($var); + } +} + + +/** + * MustacheException class. + * + * @extends Exception + */ +class MustacheException extends Exception { + + // An UNKNOWN_VARIABLE exception is thrown when a {{variable}} is not found + // in the current context. + const UNKNOWN_VARIABLE = 0; + + // An UNCLOSED_SECTION exception is thrown when a {{#section}} is not closed. + const UNCLOSED_SECTION = 1; + + // An UNEXPECTED_CLOSE_SECTION exception is thrown when {{/section}} appears + // without a corresponding {{#section}} or {{^section}}. + const UNEXPECTED_CLOSE_SECTION = 2; + + // An UNKNOWN_PARTIAL exception is thrown whenever a {{>partial}} tag appears + // with no associated partial. + const UNKNOWN_PARTIAL = 3; + + // An UNKNOWN_PRAGMA exception is thrown whenever a {{%PRAGMA}} tag appears + // which can't be handled by this Mustache instance. + const UNKNOWN_PRAGMA = 4; + +} diff --git a/vendor/flexi/vendor/mustache.php/MustacheLoader.php b/vendor/flexi/vendor/mustache.php/MustacheLoader.php new file mode 100644 index 0000000..9c4b386 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/MustacheLoader.php @@ -0,0 +1,85 @@ +<?php + +/** + * A Mustache Partial filesystem loader. + * + * @author Justin Hileman {@link http://justinhileman.com} + */ +class MustacheLoader implements ArrayAccess { + + protected $baseDir; + protected $partialsCache = array(); + protected $extension; + + /** + * MustacheLoader constructor. + * + * @access public + * @param string $baseDir Base template directory. + * @param string $extension File extension for Mustache files (default: 'mustache') + * @return void + */ + public function __construct($baseDir, $extension = 'mustache') { + if (!is_dir($baseDir)) { + throw new InvalidArgumentException('$baseDir must be a valid directory, ' . $baseDir . ' given.'); + } + + $this->baseDir = $baseDir; + $this->extension = $extension; + } + + /** + * @param string $offset Name of partial + * @return boolean + */ + public function offsetExists($offset) { + return (isset($this->partialsCache[$offset]) || file_exists($this->pathName($offset))); + } + + /** + * @throws InvalidArgumentException if the given partial doesn't exist + * @param string $offset Name of partial + * @return string Partial template contents + */ + public function offsetGet($offset) { + if (!$this->offsetExists($offset)) { + throw new InvalidArgumentException('Partial does not exist: ' . $offset); + } + + if (!isset($this->partialsCache[$offset])) { + $this->partialsCache[$offset] = file_get_contents($this->pathName($offset)); + } + + return $this->partialsCache[$offset]; + } + + /** + * MustacheLoader is an immutable filesystem loader. offsetSet throws a LogicException if called. + * + * @throws LogicException + * @return void + */ + public function offsetSet($offset, $value) { + throw new LogicException('Unable to set offset: MustacheLoader is an immutable ArrayAccess object.'); + } + + /** + * MustacheLoader is an immutable filesystem loader. offsetUnset throws a LogicException if called. + * + * @throws LogicException + * @return void + */ + public function offsetUnset($offset) { + throw new LogicException('Unable to unset offset: MustacheLoader is an immutable ArrayAccess object.'); + } + + /** + * An internal helper for generating path names. + * + * @param string $file Partial name + * @return string File path + */ + protected function pathName($file) { + return $this->baseDir . '/' . $file . '.' . $this->extension; + } +} diff --git a/vendor/flexi/vendor/mustache.php/README.markdown b/vendor/flexi/vendor/mustache.php/README.markdown new file mode 100644 index 0000000..989ff97 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/README.markdown @@ -0,0 +1,98 @@ +Mustache.php +============ + +A [Mustache](http://defunkt.github.com/mustache/) implementation in PHP. + + +Usage +----- + +A quick example: + +```php +<?php +include('Mustache.php'); +$m = new Mustache; +echo $m->render('Hello {{planet}}', array('planet' => 'World!')); +// "Hello World!" +``` + + +And a more in-depth example--this is the canonical Mustache template: + +``` +Hello {{name}} +You have just won ${{value}}! +{{#in_ca}} +Well, ${{taxed_value}}, after taxes. +{{/in_ca}} +``` + + +Along with the associated Mustache class: + +```php +<?php +class Chris extends Mustache { + public $name = "Chris"; + public $value = 10000; + + public function taxed_value() { + return $this->value - ($this->value * 0.4); + } + + public $in_ca = true; +} +``` + + +Render it like so: + +```php +<?php +$chris = new Chris; +echo $chris->render($template); +``` + + +Here's the same thing, a different way: + +Create a view object--which could also be an associative array, but those don't do functions quite as well: + +```php +<?php +class Chris { + public $name = "Chris"; + public $value = 10000; + + public function taxed_value() { + return $this->value - ($this->value * 0.4); + } + + public $in_ca = true; +} +``` + + +And render it: + +```php +<?php +$chris = new Chris; +$m = new Mustache; +echo $m->render($template, $chris); +``` + + +Known Issues +------------ + + * As of Mustache spec v1.1.2, there are a couple of whitespace bugs around section tags... Despite these failing tests, this + version is actually *closer* to correct than previous releases. + + +See Also +-------- + + * [Readme for the Ruby Mustache implementation](http://github.com/defunkt/mustache/blob/master/README.md). + * [mustache(1)](http://mustache.github.com/mustache.1.html) and [mustache(5)](http://mustache.github.com/mustache.5.html) man pages. diff --git a/vendor/flexi/vendor/mustache.php/bin/create_example.php b/vendor/flexi/vendor/mustache.php/bin/create_example.php new file mode 100755 index 0000000..1f0b894 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/bin/create_example.php @@ -0,0 +1,172 @@ +#!/usr/bin/env php +<?php + +/** + * A commandline script to create an example and the needed files: + * + * $ bin/create_example.php my_new_example + * + * ... and the folder my_new_example will be created in the examples/ folder containing 3 files: + * + * my_new_example/my_new_example.mustache + * my_new_example/my_new_example.txt + * my_new_example/MyNewExample.php + */ + +// some constants +define('USAGE', <<<USAGE +USAGE: {$argv[0]} example_name + +This creates a new example and the corresponding files in the examples/ directory + +USAGE +); + +define('DS', DIRECTORY_SEPARATOR); +define('EXAMPLE_PATH', realpath(dirname(__FILE__) . DS . ".." . DS . "examples")); + + +/** + * transform a string to lowercase using underlines. + * Examples: + * String -> string + * AString -> a_string + * SomeStrings -> some_strings + * AStringMore -> a_string_more + * + * @param string $name + * @access public + * @return string + */ +function getLowerCaseName($name) { + return preg_replace_callback("/([A-Z])/", create_function ( + '$match', + 'return "_" . strtolower($match[1]);' + ), lcfirst($name)); +} + +/** + * transform a string to Uppercase (camelcase) + * Examples + * string -> String + * a_string -> AString + * some_strings -> SomeStrings + * a_string_more -> AStringMore -> a_string_more + * + * @param string $name + * @access public + * @return string + */ +function getUpperCaseName($name) { + return preg_replace_callback("/_([a-z])/", create_function ( + '$match', + 'return strtoupper($match{1});' + ), ucfirst($name)); +} + + +/** + * return the given value and echo it out appending "\n" + * + * @param mixed $value + * @access public + * @return mixed + */ +function out($value) { + echo $value . "\n"; + return $value; +} + +/** + * create Path for certain files in an example + * returns the directory name if only $directory is given. + * if an extension is given a complete filename is returned. + * the returned filename will be echoed out + * + * @param string $directory directory without / at the end + * @param string $filename filename without path and extension + * @param string $extension extension of the file without "." + * @access public + * @return string + */ +function buildPath($directory, $filename = null, $extension = null) { + return out(EXAMPLE_PATH . DS . $directory. + ($extension !== null && $filename !== null ? DS . $filename. "." . $extension : "")); +} + +/** + * creates the directory for the example + * the script die()'s if mkdir() fails + * + * @param string $directory + * @access public + * @return void + */ +function createDirectory($directory) { + if(!@mkdir(buildPath($directory))) { + die("FAILED to create directory\n"); + } +} + +/** + * create a file for the example with the given $content + * the script die()'s if fopen() fails + * + * @param string $directory directory without / at the end + * @param string $filename filename without path and extension + * @param string $extension extension of the file without "." + * @param string $content the content of the file + * @access public + * @return void + */ +function createFile($directory, $filename, $extension, $content = "") { + $handle = @fopen(buildPath($directory, $filename, $extension), "w"); + if($handle) { + fwrite($handle, $content); + fclose($handle); + } else { + die("FAILED to create file\n"); + } +} + + +/** + * routine to create the example directory and 3 files + * + * if the $example_name is "SomeThing" the following files will be created + * examples/some_thing + * examples/some_thing/some_thing.mustache + * examples/some_thing/some_thing.txt + * examples/some_thing/SomeThing.php + * + * @param mixed $example_name + * @access public + * @return void + */ +function main($example_name) { + $lowercase = getLowerCaseName($example_name); + $uppercase = getUpperCaseName($example_name); + createDirectory($lowercase); + createFile($lowercase, $lowercase, "mustache"); + createFile($lowercase, $lowercase, "txt"); + createFile($lowercase, $uppercase, "php", <<<CONTENT +<?php + +class {$uppercase} extends Mustache { + +} + +CONTENT + ); +} + +// check if enougth arguments are given +if(count($argv) > 1) { + // get the name of the example + $example_name = $argv[1]; + + main($example_name); + +} else { + echo USAGE; +} diff --git a/vendor/flexi/vendor/mustache.php/examples/child_context/ChildContext.php b/vendor/flexi/vendor/mustache.php/examples/child_context/ChildContext.php new file mode 100644 index 0000000..b652356 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/child_context/ChildContext.php @@ -0,0 +1,13 @@ +<?php + +class ChildContext extends Mustache { + public $parent = array( + 'child' => 'child works', + ); + + public $grandparent = array( + 'parent' => array( + 'child' => 'grandchild works', + ), + ); +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/child_context/child_context.mustache b/vendor/flexi/vendor/mustache.php/examples/child_context/child_context.mustache new file mode 100644 index 0000000..e1f2ebc --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/child_context/child_context.mustache @@ -0,0 +1,2 @@ +<h1>{{#parent}}{{child}}{{/parent}}</h1> +<h2>{{#grandparent}}{{#parent}}{{child}}{{/parent}}{{/grandparent}}</h2>
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/child_context/child_context.txt b/vendor/flexi/vendor/mustache.php/examples/child_context/child_context.txt new file mode 100644 index 0000000..cfb76bf --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/child_context/child_context.txt @@ -0,0 +1,2 @@ +<h1>child works</h1> +<h2>grandchild works</h2>
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/comments/Comments.php b/vendor/flexi/vendor/mustache.php/examples/comments/Comments.php new file mode 100644 index 0000000..7f028ba --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/comments/Comments.php @@ -0,0 +1,7 @@ +<?php + +class Comments extends Mustache { + public function title() { + return 'A Comedy of Errors'; + } +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/comments/comments.mustache b/vendor/flexi/vendor/mustache.php/examples/comments/comments.mustache new file mode 100644 index 0000000..846e449 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/comments/comments.mustache @@ -0,0 +1 @@ +<h1>{{title}}{{! just something interesting... #or ^not... }}</h1>
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/comments/comments.txt b/vendor/flexi/vendor/mustache.php/examples/comments/comments.txt new file mode 100644 index 0000000..9f40e77 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/comments/comments.txt @@ -0,0 +1 @@ +<h1>A Comedy of Errors</h1>
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/complex/complex.mustache b/vendor/flexi/vendor/mustache.php/examples/complex/complex.mustache new file mode 100644 index 0000000..807c201 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/complex/complex.mustache @@ -0,0 +1,16 @@ +<h1>{{header}}</h1> +{{#notEmpty}} +<ul> +{{#item}} +{{#current}} + <li><strong>{{name}}</strong></li> +{{/current}} +{{^current}} + <li><a href="{{url}}">{{name}}</a></li> +{{/current}} +{{/item}} +</ul> +{{/notEmpty}} +{{#isEmpty}} +<p>The list is empty.</p> +{{/isEmpty}}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/complex/complex.php b/vendor/flexi/vendor/mustache.php/examples/complex/complex.php new file mode 100644 index 0000000..32b0917 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/complex/complex.php @@ -0,0 +1,19 @@ +<?php + +class Complex extends Mustache { + public $header = 'Colors'; + + public $item = array( + array('name' => 'red', 'current' => true, 'url' => '#Red'), + array('name' => 'green', 'current' => false, 'url' => '#Green'), + array('name' => 'blue', 'current' => false, 'url' => '#Blue'), + ); + + public function notEmpty() { + return !($this->isEmpty()); + } + + public function isEmpty() { + return count($this->item) === 0; + } +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/complex/complex.txt b/vendor/flexi/vendor/mustache.php/examples/complex/complex.txt new file mode 100644 index 0000000..facee6d --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/complex/complex.txt @@ -0,0 +1,6 @@ +<h1>Colors</h1> +<ul> + <li><strong>red</strong></li> + <li><a href="#Green">green</a></li> + <li><a href="#Blue">blue</a></li> +</ul> diff --git a/vendor/flexi/vendor/mustache.php/examples/delimiters/Delimiters.php b/vendor/flexi/vendor/mustache.php/examples/delimiters/Delimiters.php new file mode 100644 index 0000000..be372fa --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/delimiters/Delimiters.php @@ -0,0 +1,14 @@ +<?php + +class Delimiters extends Mustache { + public $start = "It worked the first time."; + + public function middle() { + return array( + array('item' => "And it worked the second time."), + array('item' => "As well as the third."), + ); + } + + public $final = "Then, surprisingly, it worked the final time."; +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/delimiters/delimiters.mustache b/vendor/flexi/vendor/mustache.php/examples/delimiters/delimiters.mustache new file mode 100644 index 0000000..e9b0332 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/delimiters/delimiters.mustache @@ -0,0 +1,8 @@ +{{=<% %>=}} +* <% start %> +<%=| |=%> +|# middle | +* | item | +|/ middle | +|={{ }}=| +* {{ final }}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/delimiters/delimiters.txt b/vendor/flexi/vendor/mustache.php/examples/delimiters/delimiters.txt new file mode 100644 index 0000000..e6b2d7a --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/delimiters/delimiters.txt @@ -0,0 +1,4 @@ +* It worked the first time. +* And it worked the second time. +* As well as the third. +* Then, surprisingly, it worked the final time.
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/dot_notation/DotNotation.php b/vendor/flexi/vendor/mustache.php/examples/dot_notation/DotNotation.php new file mode 100644 index 0000000..7dd0a4e --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/dot_notation/DotNotation.php @@ -0,0 +1,20 @@ +<?php + +/** + * DotNotation example class. Uses DOT_NOTATION pragma. + * + * @extends Mustache + */ +class DotNotation extends Mustache { + public $person = array( + 'name' => array('first' => 'Chris', 'last' => 'Firescythe'), + 'age' => 24, + 'hobbies' => array('Cycling', 'Fishing'), + 'hometown' => array( + 'city' => 'Cincinnati', + 'state' => 'OH', + ), + ); + + public $normal = 'Normal'; +} diff --git a/vendor/flexi/vendor/mustache.php/examples/dot_notation/dot_notation.mustache b/vendor/flexi/vendor/mustache.php/examples/dot_notation/dot_notation.mustache new file mode 100644 index 0000000..0135a2a --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/dot_notation/dot_notation.mustache @@ -0,0 +1,5 @@ +* {{person.name.first}} {{person.name.last}} +* {{person.age}} +* {{person.hobbies.0}}, {{person.hobbies.1}} +* {{person.hometown.city}}, {{person.hometown.state}} +* {{normal}} diff --git a/vendor/flexi/vendor/mustache.php/examples/dot_notation/dot_notation.txt b/vendor/flexi/vendor/mustache.php/examples/dot_notation/dot_notation.txt new file mode 100644 index 0000000..e5c1ed9 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/dot_notation/dot_notation.txt @@ -0,0 +1,5 @@ +* Chris Firescythe +* 24 +* Cycling, Fishing +* Cincinnati, OH +* Normal diff --git a/vendor/flexi/vendor/mustache.php/examples/double_section/DoubleSection.php b/vendor/flexi/vendor/mustache.php/examples/double_section/DoubleSection.php new file mode 100644 index 0000000..f9d3dbb --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/double_section/DoubleSection.php @@ -0,0 +1,9 @@ +<?php + +class DoubleSection extends Mustache { + public function t() { + return true; + } + + public $two = "second"; +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/double_section/double_section.mustache b/vendor/flexi/vendor/mustache.php/examples/double_section/double_section.mustache new file mode 100644 index 0000000..c831645 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/double_section/double_section.mustache @@ -0,0 +1,7 @@ +{{#t}} +* first +{{/t}} +* {{two}} +{{#t}} +* third +{{/t}}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/double_section/double_section.txt b/vendor/flexi/vendor/mustache.php/examples/double_section/double_section.txt new file mode 100644 index 0000000..5433688 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/double_section/double_section.txt @@ -0,0 +1,3 @@ +* first +* second +* third diff --git a/vendor/flexi/vendor/mustache.php/examples/escaped/Escaped.php b/vendor/flexi/vendor/mustache.php/examples/escaped/Escaped.php new file mode 100644 index 0000000..2852196 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/escaped/Escaped.php @@ -0,0 +1,5 @@ +<?php + +class Escaped extends Mustache { + public $title = '"Bear" > "Shark"'; +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/escaped/escaped.mustache b/vendor/flexi/vendor/mustache.php/examples/escaped/escaped.mustache new file mode 100644 index 0000000..8be4ccb --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/escaped/escaped.mustache @@ -0,0 +1 @@ +<h1>{{title}}</h1>
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/escaped/escaped.txt b/vendor/flexi/vendor/mustache.php/examples/escaped/escaped.txt new file mode 100644 index 0000000..6ba3657 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/escaped/escaped.txt @@ -0,0 +1 @@ +<h1>"Bear" > "Shark"</h1>
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/grand_parent_context/GrandParentContext.php b/vendor/flexi/vendor/mustache.php/examples/grand_parent_context/GrandParentContext.php new file mode 100644 index 0000000..5a59ed9 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/grand_parent_context/GrandParentContext.php @@ -0,0 +1,24 @@ +<?php + +class GrandParentContext extends Mustache { + public $grand_parent_id = 'grand_parent1'; + public $parent_contexts = array(); + + public function __construct() { + parent::__construct(); + + $this->parent_contexts[] = array('parent_id' => 'parent1', 'child_contexts' => array( + array('child_id' => 'parent1-child1'), + array('child_id' => 'parent1-child2') + )); + + $parent2 = new stdClass(); + $parent2->parent_id = 'parent2'; + $parent2->child_contexts = array( + array('child_id' => 'parent2-child1'), + array('child_id' => 'parent2-child2') + ); + + $this->parent_contexts[] = $parent2; + } +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/grand_parent_context/grand_parent_context.mustache b/vendor/flexi/vendor/mustache.php/examples/grand_parent_context/grand_parent_context.mustache new file mode 100644 index 0000000..6d03ddf --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/grand_parent_context/grand_parent_context.mustache @@ -0,0 +1,7 @@ +{{grand_parent_id}} +{{#parent_contexts}} + {{parent_id}} ({{grand_parent_id}}) + {{#child_contexts}} + {{child_id}} ({{parent_id}} << {{grand_parent_id}}) + {{/child_contexts}} +{{/parent_contexts}} diff --git a/vendor/flexi/vendor/mustache.php/examples/grand_parent_context/grand_parent_context.txt b/vendor/flexi/vendor/mustache.php/examples/grand_parent_context/grand_parent_context.txt new file mode 100644 index 0000000..2687f84 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/grand_parent_context/grand_parent_context.txt @@ -0,0 +1,7 @@ +grand_parent1 + parent1 (grand_parent1) + parent1-child1 (parent1 << grand_parent1) + parent1-child2 (parent1 << grand_parent1) + parent2 (grand_parent1) + parent2-child1 (parent2 << grand_parent1) + parent2-child2 (parent2 << grand_parent1) diff --git a/vendor/flexi/vendor/mustache.php/examples/i18n/I18n.php b/vendor/flexi/vendor/mustache.php/examples/i18n/I18n.php new file mode 100644 index 0000000..7e0fcce --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/i18n/I18n.php @@ -0,0 +1,20 @@ +<?php + +class I18n extends Mustache { + + // Variable to be interpolated + public $name = 'Bob'; + + // Add a {{#__}} lambda for i18n + public $__ = array(__CLASS__, '__trans'); + + // A *very* small i18n dictionary :) + private static $dictionary = array( + 'Hello.' => 'Hola.', + 'My name is {{ name }}.' => 'Me llamo {{ name }}.', + ); + + public static function __trans($text) { + return isset(self::$dictionary[$text]) ? self::$dictionary[$text] : $text; + } +} diff --git a/vendor/flexi/vendor/mustache.php/examples/i18n/i18n.mustache b/vendor/flexi/vendor/mustache.php/examples/i18n/i18n.mustache new file mode 100644 index 0000000..eef169b --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/i18n/i18n.mustache @@ -0,0 +1 @@ +{{#__}}Hello.{{/__}} {{#__}}My name is {{ name }}.{{/__}}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/i18n/i18n.txt b/vendor/flexi/vendor/mustache.php/examples/i18n/i18n.txt new file mode 100644 index 0000000..650a71a --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/i18n/i18n.txt @@ -0,0 +1 @@ +Hola. Me llamo Bob.
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/implicit_iterator/ImplicitIterator.php b/vendor/flexi/vendor/mustache.php/examples/implicit_iterator/ImplicitIterator.php new file mode 100644 index 0000000..c01fef0 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/implicit_iterator/ImplicitIterator.php @@ -0,0 +1,5 @@ +<?php + +class ImplicitIterator extends Mustache { + protected $data = array('Donkey Kong', 'Luigi', 'Mario', 'Peach', 'Yoshi'); +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/implicit_iterator/implicit_iterator.mustache b/vendor/flexi/vendor/mustache.php/examples/implicit_iterator/implicit_iterator.mustache new file mode 100644 index 0000000..b8d58ff --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/implicit_iterator/implicit_iterator.mustache @@ -0,0 +1,3 @@ +{{#data}} +* {{.}} +{{/data}}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/implicit_iterator/implicit_iterator.txt b/vendor/flexi/vendor/mustache.php/examples/implicit_iterator/implicit_iterator.txt new file mode 100644 index 0000000..bd7e945 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/implicit_iterator/implicit_iterator.txt @@ -0,0 +1,5 @@ +* Donkey Kong +* Luigi +* Mario +* Peach +* Yoshi diff --git a/vendor/flexi/vendor/mustache.php/examples/inverted_double_section/InvertedDoubleSection.php b/vendor/flexi/vendor/mustache.php/examples/inverted_double_section/InvertedDoubleSection.php new file mode 100644 index 0000000..3dc2231 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/inverted_double_section/InvertedDoubleSection.php @@ -0,0 +1,6 @@ +<?php + +class InvertedDoubleSection extends Mustache { + public $t = false; + public $two = 'second'; +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/inverted_double_section/inverted_double_section.mustache b/vendor/flexi/vendor/mustache.php/examples/inverted_double_section/inverted_double_section.mustache new file mode 100644 index 0000000..acc3ae0 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/inverted_double_section/inverted_double_section.mustache @@ -0,0 +1,7 @@ +{{^t}} +* first +{{/t}} +* {{two}} +{{^t}} +* third +{{/t}}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/inverted_double_section/inverted_double_section.txt b/vendor/flexi/vendor/mustache.php/examples/inverted_double_section/inverted_double_section.txt new file mode 100644 index 0000000..5433688 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/inverted_double_section/inverted_double_section.txt @@ -0,0 +1,3 @@ +* first +* second +* third diff --git a/vendor/flexi/vendor/mustache.php/examples/inverted_section/InvertedSection.php b/vendor/flexi/vendor/mustache.php/examples/inverted_section/InvertedSection.php new file mode 100644 index 0000000..18eeb86 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/inverted_section/InvertedSection.php @@ -0,0 +1,5 @@ +<?php + +class InvertedSection extends Mustache { + public $repo = array(); +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/inverted_section/inverted_section.mustache b/vendor/flexi/vendor/mustache.php/examples/inverted_section/inverted_section.mustache new file mode 100644 index 0000000..bee60ff --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/inverted_section/inverted_section.mustache @@ -0,0 +1,2 @@ +{{#repo}}<b>{{name}}</b>{{/repo}} +{{^repo}}No repos :({{/repo}}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/inverted_section/inverted_section.txt b/vendor/flexi/vendor/mustache.php/examples/inverted_section/inverted_section.txt new file mode 100644 index 0000000..2b9ed3f --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/inverted_section/inverted_section.txt @@ -0,0 +1 @@ +No repos :(
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/partials/Partials.php b/vendor/flexi/vendor/mustache.php/examples/partials/Partials.php new file mode 100644 index 0000000..093257b --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/partials/Partials.php @@ -0,0 +1,13 @@ +<?php + +class Partials extends Mustache { + public $name = 'ilmich'; + public $data = array( + array('name' => 'federica', 'age' => 27, 'gender' => 'female'), + array('name' => 'marco', 'age' => 32, 'gender' => 'male'), + ); + + protected $_partials = array( + 'children' => "{{#data}}{{name}} - {{age}} - {{gender}}\n{{/data}}", + ); +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/partials/partials.mustache b/vendor/flexi/vendor/mustache.php/examples/partials/partials.mustache new file mode 100644 index 0000000..037e1b3 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/partials/partials.mustache @@ -0,0 +1,2 @@ +Children of {{name}}: +{{>children}}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/partials/partials.txt b/vendor/flexi/vendor/mustache.php/examples/partials/partials.txt new file mode 100644 index 0000000..d967e15 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/partials/partials.txt @@ -0,0 +1,3 @@ +Children of ilmich: +federica - 27 - female +marco - 32 - male diff --git a/vendor/flexi/vendor/mustache.php/examples/partials_with_view_class/PartialsWithViewClass.php b/vendor/flexi/vendor/mustache.php/examples/partials_with_view_class/PartialsWithViewClass.php new file mode 100644 index 0000000..56e0d86 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/partials_with_view_class/PartialsWithViewClass.php @@ -0,0 +1,19 @@ +<?php + +class PartialsWithViewClass extends Mustache { + public function __construct($template = null, $view = null, $partials = null) { + // Use an object of an arbitrary class as a View for this Mustache instance: + $view = new StdClass(); + $view->name = 'ilmich'; + $view->data = array( + array('name' => 'federica', 'age' => 27, 'gender' => 'female'), + array('name' => 'marco', 'age' => 32, 'gender' => 'male'), + ); + + $partials = array( + 'children' => "{{#data}}{{name}} - {{age}} - {{gender}}\n{{/data}}", + ); + + parent::__construct($template, $view, $partials); + } +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/partials_with_view_class/partials_with_view_class.mustache b/vendor/flexi/vendor/mustache.php/examples/partials_with_view_class/partials_with_view_class.mustache new file mode 100644 index 0000000..037e1b3 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/partials_with_view_class/partials_with_view_class.mustache @@ -0,0 +1,2 @@ +Children of {{name}}: +{{>children}}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/partials_with_view_class/partials_with_view_class.txt b/vendor/flexi/vendor/mustache.php/examples/partials_with_view_class/partials_with_view_class.txt new file mode 100644 index 0000000..d967e15 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/partials_with_view_class/partials_with_view_class.txt @@ -0,0 +1,3 @@ +Children of ilmich: +federica - 27 - female +marco - 32 - male diff --git a/vendor/flexi/vendor/mustache.php/examples/pragma_unescaped/PragmaUnescaped.php b/vendor/flexi/vendor/mustache.php/examples/pragma_unescaped/PragmaUnescaped.php new file mode 100644 index 0000000..b4e0e21 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/pragma_unescaped/PragmaUnescaped.php @@ -0,0 +1,5 @@ +<?php + +class PragmaUnescaped extends Mustache { + public $vs = 'Bear > Shark'; +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/pragma_unescaped/pragma_unescaped.mustache b/vendor/flexi/vendor/mustache.php/examples/pragma_unescaped/pragma_unescaped.mustache new file mode 100644 index 0000000..76095d7 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/pragma_unescaped/pragma_unescaped.mustache @@ -0,0 +1,3 @@ +{{%UNESCAPED}} +{{vs}} +{{{vs}}}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/pragma_unescaped/pragma_unescaped.txt b/vendor/flexi/vendor/mustache.php/examples/pragma_unescaped/pragma_unescaped.txt new file mode 100644 index 0000000..2860f61 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/pragma_unescaped/pragma_unescaped.txt @@ -0,0 +1,2 @@ +Bear > Shark +Bear > Shark
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/pragmas_in_partials/PragmasInPartials.php b/vendor/flexi/vendor/mustache.php/examples/pragmas_in_partials/PragmasInPartials.php new file mode 100644 index 0000000..7458289 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/pragmas_in_partials/PragmasInPartials.php @@ -0,0 +1,8 @@ +<?php + +class PragmasInPartials extends Mustache { + public $say = '< RAWR!! >'; + protected $_partials = array( + 'dinosaur' => '{{say}}' + ); +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/pragmas_in_partials/pragmas_in_partials.mustache b/vendor/flexi/vendor/mustache.php/examples/pragmas_in_partials/pragmas_in_partials.mustache new file mode 100644 index 0000000..abd6ef4 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/pragmas_in_partials/pragmas_in_partials.mustache @@ -0,0 +1,3 @@ +{{%UNESCAPED}} +{{say}} +{{>dinosaur}}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/pragmas_in_partials/pragmas_in_partials.txt b/vendor/flexi/vendor/mustache.php/examples/pragmas_in_partials/pragmas_in_partials.txt new file mode 100644 index 0000000..c8e77e3 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/pragmas_in_partials/pragmas_in_partials.txt @@ -0,0 +1,2 @@ +< RAWR!! > +< RAWR!! >
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/recursive_partials/RecursivePartials.php b/vendor/flexi/vendor/mustache.php/examples/recursive_partials/RecursivePartials.php new file mode 100644 index 0000000..04e8af8 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/recursive_partials/RecursivePartials.php @@ -0,0 +1,16 @@ +<?php + +class RecursivePartials extends Mustache { + protected $_partials = array( + 'child' => " > {{ name }}{{#child}}{{>child}}{{/child}}", + ); + + public $name = 'George'; + public $child = array( + 'name' => 'Dan', + 'child' => array( + 'name' => 'Justin', + 'child' => false, + ) + ); +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/recursive_partials/recursive_partials.mustache b/vendor/flexi/vendor/mustache.php/examples/recursive_partials/recursive_partials.mustache new file mode 100644 index 0000000..0bc5d03 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/recursive_partials/recursive_partials.mustache @@ -0,0 +1 @@ +{{name}}{{#child}}{{>child}}{{/child}}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/recursive_partials/recursive_partials.txt b/vendor/flexi/vendor/mustache.php/examples/recursive_partials/recursive_partials.txt new file mode 100644 index 0000000..681cdef --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/recursive_partials/recursive_partials.txt @@ -0,0 +1 @@ +George > Dan > Justin
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/section_iterator_objects/SectionIteratorObjects.php b/vendor/flexi/vendor/mustache.php/examples/section_iterator_objects/SectionIteratorObjects.php new file mode 100644 index 0000000..7b65597 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/section_iterator_objects/SectionIteratorObjects.php @@ -0,0 +1,16 @@ +<?php + +class SectionIteratorObjects extends Mustache { + public $start = "It worked the first time."; + + protected $_data = array( + array('item' => 'And it worked the second time.'), + array('item' => 'As well as the third.'), + ); + + public function middle() { + return new ArrayIterator($this->_data); + } + + public $final = "Then, surprisingly, it worked the final time."; +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/section_iterator_objects/section_iterator_objects.mustache b/vendor/flexi/vendor/mustache.php/examples/section_iterator_objects/section_iterator_objects.mustache new file mode 100644 index 0000000..44dfce4 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/section_iterator_objects/section_iterator_objects.mustache @@ -0,0 +1,5 @@ +* {{ start }} +{{# middle }} +* {{ item }} +{{/ middle }} +* {{ final }}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/section_iterator_objects/section_iterator_objects.txt b/vendor/flexi/vendor/mustache.php/examples/section_iterator_objects/section_iterator_objects.txt new file mode 100644 index 0000000..e6b2d7a --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/section_iterator_objects/section_iterator_objects.txt @@ -0,0 +1,4 @@ +* It worked the first time. +* And it worked the second time. +* As well as the third. +* Then, surprisingly, it worked the final time.
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/section_magic_objects/SectionMagicObjects.php b/vendor/flexi/vendor/mustache.php/examples/section_magic_objects/SectionMagicObjects.php new file mode 100644 index 0000000..ebb0031 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/section_magic_objects/SectionMagicObjects.php @@ -0,0 +1,26 @@ +<?php + +class SectionMagicObjects extends Mustache { + public $start = "It worked the first time."; + + public function middle() { + return new MagicObject(); + } + + public $final = "Then, surprisingly, it worked the final time."; +} + +class MagicObject { + protected $_data = array( + 'foo' => 'And it worked the second time.', + 'bar' => 'As well as the third.' + ); + + public function __get($key) { + return isset($this->_data[$key]) ? $this->_data[$key] : NULL; + } + + public function __isset($key) { + return isset($this->_data[$key]); + } +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/section_magic_objects/section_magic_objects.mustache b/vendor/flexi/vendor/mustache.php/examples/section_magic_objects/section_magic_objects.mustache new file mode 100644 index 0000000..9119608 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/section_magic_objects/section_magic_objects.mustache @@ -0,0 +1,6 @@ +* {{ start }} +{{# middle }} +* {{ foo }} +* {{ bar }} +{{/ middle }} +* {{ final }}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/section_magic_objects/section_magic_objects.txt b/vendor/flexi/vendor/mustache.php/examples/section_magic_objects/section_magic_objects.txt new file mode 100644 index 0000000..e6b2d7a --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/section_magic_objects/section_magic_objects.txt @@ -0,0 +1,4 @@ +* It worked the first time. +* And it worked the second time. +* As well as the third. +* Then, surprisingly, it worked the final time.
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/section_objects/SectionObjects.php b/vendor/flexi/vendor/mustache.php/examples/section_objects/SectionObjects.php new file mode 100644 index 0000000..41b7d84 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/section_objects/SectionObjects.php @@ -0,0 +1,16 @@ +<?php + +class SectionObjects extends Mustache { + public $start = "It worked the first time."; + + public function middle() { + return new SectionObject; + } + + public $final = "Then, surprisingly, it worked the final time."; +} + +class SectionObject { + public $foo = 'And it worked the second time.'; + public $bar = 'As well as the third.'; +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/section_objects/section_objects.mustache b/vendor/flexi/vendor/mustache.php/examples/section_objects/section_objects.mustache new file mode 100644 index 0000000..9119608 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/section_objects/section_objects.mustache @@ -0,0 +1,6 @@ +* {{ start }} +{{# middle }} +* {{ foo }} +* {{ bar }} +{{/ middle }} +* {{ final }}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/section_objects/section_objects.txt b/vendor/flexi/vendor/mustache.php/examples/section_objects/section_objects.txt new file mode 100644 index 0000000..e6b2d7a --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/section_objects/section_objects.txt @@ -0,0 +1,4 @@ +* It worked the first time. +* And it worked the second time. +* As well as the third. +* Then, surprisingly, it worked the final time.
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/sections/Sections.php b/vendor/flexi/vendor/mustache.php/examples/sections/Sections.php new file mode 100644 index 0000000..fb78354 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/sections/Sections.php @@ -0,0 +1,14 @@ +<?php + +class Sections extends Mustache { + public $start = "It worked the first time."; + + public function middle() { + return array( + array('item' => "And it worked the second time."), + array('item' => "As well as the third."), + ); + } + + public $final = "Then, surprisingly, it worked the final time."; +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/sections/sections.mustache b/vendor/flexi/vendor/mustache.php/examples/sections/sections.mustache new file mode 100644 index 0000000..44dfce4 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/sections/sections.mustache @@ -0,0 +1,5 @@ +* {{ start }} +{{# middle }} +* {{ item }} +{{/ middle }} +* {{ final }}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/sections/sections.txt b/vendor/flexi/vendor/mustache.php/examples/sections/sections.txt new file mode 100644 index 0000000..e6b2d7a --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/sections/sections.txt @@ -0,0 +1,4 @@ +* It worked the first time. +* And it worked the second time. +* As well as the third. +* Then, surprisingly, it worked the final time.
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/sections_nested/SectionsNested.php b/vendor/flexi/vendor/mustache.php/examples/sections_nested/SectionsNested.php new file mode 100644 index 0000000..ec01b75 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/sections_nested/SectionsNested.php @@ -0,0 +1,33 @@ +<?php + +class SectionsNested extends Mustache { + public $name = 'Little Mac'; + + public function enemies() { + return array( + array( + 'name' => 'Von Kaiser', + 'enemies' => array( + array('name' => 'Super Macho Man'), + array('name' => 'Piston Honda'), + array('name' => 'Mr. Sandman'), + ) + ), + array( + 'name' => 'Mike Tyson', + 'enemies' => array( + array('name' => 'Soda Popinski'), + array('name' => 'King Hippo'), + array('name' => 'Great Tiger'), + array('name' => 'Glass Joe'), + ) + ), + array( + 'name' => 'Don Flamenco', + 'enemies' => array( + array('name' => 'Bald Bull'), + ) + ), + ); + } +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/sections_nested/sections_nested.mustache b/vendor/flexi/vendor/mustache.php/examples/sections_nested/sections_nested.mustache new file mode 100644 index 0000000..9f8007d --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/sections_nested/sections_nested.mustache @@ -0,0 +1,7 @@ +Enemies of {{ name }}: +{{# enemies }} +{{ name }} ... who also has enemies: +{{# enemies }} +--> {{ name }} +{{/ enemies }} +{{/ enemies }}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/sections_nested/sections_nested.txt b/vendor/flexi/vendor/mustache.php/examples/sections_nested/sections_nested.txt new file mode 100644 index 0000000..72c44d0 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/sections_nested/sections_nested.txt @@ -0,0 +1,12 @@ +Enemies of Little Mac: +Von Kaiser ... who also has enemies: +--> Super Macho Man +--> Piston Honda +--> Mr. Sandman +Mike Tyson ... who also has enemies: +--> Soda Popinski +--> King Hippo +--> Great Tiger +--> Glass Joe +Don Flamenco ... who also has enemies: +--> Bald Bull diff --git a/vendor/flexi/vendor/mustache.php/examples/simple/Simple.php b/vendor/flexi/vendor/mustache.php/examples/simple/Simple.php new file mode 100644 index 0000000..6d07dac --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/simple/Simple.php @@ -0,0 +1,12 @@ +<?php + +class Simple extends Mustache { + public $name = "Chris"; + public $value = 10000; + + public function taxed_value() { + return $this->value - ($this->value * 0.4); + } + + public $in_ca = true; +};
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/simple/simple.mustache b/vendor/flexi/vendor/mustache.php/examples/simple/simple.mustache new file mode 100644 index 0000000..03df206 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/simple/simple.mustache @@ -0,0 +1,5 @@ +Hello {{name}} +You have just won ${{value}}! +{{#in_ca}} +Well, ${{ taxed_value }}, after taxes. +{{/in_ca}}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/simple/simple.txt b/vendor/flexi/vendor/mustache.php/examples/simple/simple.txt new file mode 100644 index 0000000..5d75d65 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/simple/simple.txt @@ -0,0 +1,3 @@ +Hello Chris +You have just won $10000! +Well, $6000, after taxes. diff --git a/vendor/flexi/vendor/mustache.php/examples/unescaped/Unescaped.php b/vendor/flexi/vendor/mustache.php/examples/unescaped/Unescaped.php new file mode 100644 index 0000000..41b10cb --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/unescaped/Unescaped.php @@ -0,0 +1,5 @@ +<?php + +class Unescaped extends Mustache { + public $title = "Bear > Shark"; +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/unescaped/unescaped.mustache b/vendor/flexi/vendor/mustache.php/examples/unescaped/unescaped.mustache new file mode 100644 index 0000000..9982708 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/unescaped/unescaped.mustache @@ -0,0 +1 @@ +<h1>{{{title}}}</h1>
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/unescaped/unescaped.txt b/vendor/flexi/vendor/mustache.php/examples/unescaped/unescaped.txt new file mode 100644 index 0000000..01fa404 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/unescaped/unescaped.txt @@ -0,0 +1 @@ +<h1>Bear > Shark</h1>
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/utf8/UTF8.php b/vendor/flexi/vendor/mustache.php/examples/utf8/UTF8.php new file mode 100644 index 0000000..7843f53 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/utf8/UTF8.php @@ -0,0 +1,5 @@ +<?php + +class UTF8Unescaped extends Mustache { + public $test = '䏿–‡åˆæ¥å•¦'; +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/utf8/utf8.mustache b/vendor/flexi/vendor/mustache.php/examples/utf8/utf8.mustache new file mode 100644 index 0000000..6954d47 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/utf8/utf8.mustache @@ -0,0 +1 @@ +<h1>䏿–‡ {{test}}</h1>
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/utf8/utf8.txt b/vendor/flexi/vendor/mustache.php/examples/utf8/utf8.txt new file mode 100644 index 0000000..bf17971 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/utf8/utf8.txt @@ -0,0 +1 @@ +<h1>䏿–‡ 䏿–‡åˆæ¥å•¦</h1>
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/utf8_unescaped/UTF8Unescaped.php b/vendor/flexi/vendor/mustache.php/examples/utf8_unescaped/UTF8Unescaped.php new file mode 100644 index 0000000..d097cbe --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/utf8_unescaped/UTF8Unescaped.php @@ -0,0 +1,5 @@ +<?php + +class UTF8 extends Mustache { + public $test = '䏿–‡åˆæ¥å•¦'; +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/utf8_unescaped/utf8_unescaped.mustache b/vendor/flexi/vendor/mustache.php/examples/utf8_unescaped/utf8_unescaped.mustache new file mode 100644 index 0000000..fd7fe4b --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/utf8_unescaped/utf8_unescaped.mustache @@ -0,0 +1 @@ +<h1>䏿–‡ {{{test}}}</h1>
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/utf8_unescaped/utf8_unescaped.txt b/vendor/flexi/vendor/mustache.php/examples/utf8_unescaped/utf8_unescaped.txt new file mode 100644 index 0000000..bf17971 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/utf8_unescaped/utf8_unescaped.txt @@ -0,0 +1 @@ +<h1>䏿–‡ 䏿–‡åˆæ¥å•¦</h1>
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/whitespace/Whitespace.php b/vendor/flexi/vendor/mustache.php/examples/whitespace/Whitespace.php new file mode 100644 index 0000000..3be9689 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/whitespace/Whitespace.php @@ -0,0 +1,37 @@ +<?php + +/** + * Whitespace test for tag names. + * + * Per http://github.com/janl/mustache.js/issues/issue/34/#comment_244396 + * tags should strip leading and trailing whitespace in key names. + * + * `{{> tag }}` and `{{> tag}}` and `{{>tag}}` should all be equivalent. + * + * @extends Mustache + */ +class Whitespace extends Mustache { + public $foo = 'alpha'; + + public $bar = 'beta'; + + public function baz() { + return 'gamma'; + } + + public function qux() { + return array( + array('key with space' => 'A'), + array('key with space' => 'B'), + array('key with space' => 'C'), + array('key with space' => 'D'), + array('key with space' => 'E'), + array('key with space' => 'F'), + array('key with space' => 'G'), + ); + } + + protected $_partials = array( + 'alphabet' => " * {{.}}\n", + ); +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/whitespace/whitespace.mustache b/vendor/flexi/vendor/mustache.php/examples/whitespace/whitespace.mustache new file mode 100644 index 0000000..0b3ba00 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/whitespace/whitespace.mustache @@ -0,0 +1,10 @@ +{{^ inverted section test }} +These are some things: +{{/inverted section test }} +* {{ foo }} +* {{ bar}} +* {{ baz }} +{{# qux }} +* {{ key with space }} +{{/ qux }} +{{#qux}}.{{/qux}}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/examples/whitespace/whitespace.txt b/vendor/flexi/vendor/mustache.php/examples/whitespace/whitespace.txt new file mode 100644 index 0000000..5226c69 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/examples/whitespace/whitespace.txt @@ -0,0 +1,12 @@ +These are some things: +* alpha +* beta +* gamma +* A +* B +* C +* D +* E +* F +* G +.......
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/test/MustacheCallTest.php b/vendor/flexi/vendor/mustache.php/test/MustacheCallTest.php new file mode 100644 index 0000000..366d2c4 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/test/MustacheCallTest.php @@ -0,0 +1,24 @@ +<?php + +require_once '../Mustache.php'; + +class MustacheCallTest extends PHPUnit_Framework_TestCase { + + public function testCallEatsContext() { + $foo = new ClassWithCall(); + $foo->name = 'Bob'; + + $template = '{{# foo }}{{ label }}: {{ name }}{{/ foo }}'; + $data = array('label' => 'name', 'foo' => $foo); + $m = new Mustache($template, $data); + + $this->assertEquals('name: Bob', $m->render()); + } +} + +class ClassWithCall { + public $name; + public function __call($method, $args) { + return 'unknown value'; + } +} diff --git a/vendor/flexi/vendor/mustache.php/test/MustacheExceptionTest.php b/vendor/flexi/vendor/mustache.php/test/MustacheExceptionTest.php new file mode 100644 index 0000000..2a40223 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/test/MustacheExceptionTest.php @@ -0,0 +1,152 @@ +<?php + +require_once '../Mustache.php'; + +class MustacheExceptionTest extends PHPUnit_Framework_TestCase { + + const TEST_CLASS = 'Mustache'; + + protected $pickyMustache; + protected $slackerMustache; + + public function setUp() { + $this->pickyMustache = new PickyMustache(); + $this->slackerMustache = new SlackerMustache(); + } + + /** + * @group interpolation + * @expectedException MustacheException + */ + public function testThrowsUnknownVariableException() { + $this->pickyMustache->render('{{not_a_variable}}'); + } + + /** + * @group sections + * @expectedException MustacheException + */ + public function testThrowsUnclosedSectionException() { + $this->pickyMustache->render('{{#unclosed}}'); + } + + /** + * @group sections + * @expectedException MustacheException + */ + public function testThrowsUnclosedInvertedSectionException() { + $this->pickyMustache->render('{{^unclosed}}'); + } + + /** + * @group sections + * @expectedException MustacheException + */ + public function testThrowsUnexpectedCloseSectionException() { + $this->pickyMustache->render('{{/unopened}}'); + } + + /** + * @group partials + * @expectedException MustacheException + */ + public function testThrowsUnknownPartialException() { + $this->pickyMustache->render('{{>impartial}}'); + } + + /** + * @group pragmas + * @expectedException MustacheException + */ + public function testThrowsUnknownPragmaException() { + $this->pickyMustache->render('{{%SWEET-MUSTACHE-BRO}}'); + } + + /** + * @group sections + */ + public function testDoesntThrowUnclosedSectionException() { + $this->assertEquals('', $this->slackerMustache->render('{{#unclosed}}')); + } + + /** + * @group sections + */ + public function testDoesntThrowUnexpectedCloseSectionException() { + $this->assertEquals('', $this->slackerMustache->render('{{/unopened}}')); + } + + /** + * @group partials + */ + public function testDoesntThrowUnknownPartialException() { + $this->assertEquals('', $this->slackerMustache->render('{{>impartial}}')); + } + + /** + * @group pragmas + * @expectedException MustacheException + */ + public function testGetPragmaOptionsThrowsExceptionsIfItThinksYouHaveAPragmaButItTurnsOutYouDont() { + $mustache = new TestableMustache(); + $mustache->testableGetPragmaOptions('PRAGMATIC'); + } + + public function testOverrideThrownExceptionsViaConstructorOptions() { + $exceptions = array( + MustacheException::UNKNOWN_VARIABLE, + MustacheException::UNCLOSED_SECTION, + MustacheException::UNEXPECTED_CLOSE_SECTION, + MustacheException::UNKNOWN_PARTIAL, + MustacheException::UNKNOWN_PRAGMA, + ); + + $one = new TestableMustache(null, null, null, array( + 'throws_exceptions' => array_fill_keys($exceptions, true) + )); + + $thrownExceptions = $one->getThrownExceptions(); + foreach ($exceptions as $exception) { + $this->assertTrue($thrownExceptions[$exception]); + } + + $two = new TestableMustache(null, null, null, array( + 'throws_exceptions' => array_fill_keys($exceptions, false) + )); + + $thrownExceptions = $two->getThrownExceptions(); + foreach ($exceptions as $exception) { + $this->assertFalse($thrownExceptions[$exception]); + } + } +} + +class PickyMustache extends Mustache { + protected $_throwsExceptions = array( + MustacheException::UNKNOWN_VARIABLE => true, + MustacheException::UNCLOSED_SECTION => true, + MustacheException::UNEXPECTED_CLOSE_SECTION => true, + MustacheException::UNKNOWN_PARTIAL => true, + MustacheException::UNKNOWN_PRAGMA => true, + ); +} + +class SlackerMustache extends Mustache { + protected $_throwsExceptions = array( + MustacheException::UNKNOWN_VARIABLE => false, + MustacheException::UNCLOSED_SECTION => false, + MustacheException::UNEXPECTED_CLOSE_SECTION => false, + MustacheException::UNKNOWN_PARTIAL => false, + MustacheException::UNKNOWN_PRAGMA => false, + ); +} + +class TestableMustache extends Mustache { + public function testableGetPragmaOptions($pragma_name) { + return $this->_getPragmaOptions($pragma_name); + } + + public function getThrownExceptions() { + return $this->_throwsExceptions; + } +} diff --git a/vendor/flexi/vendor/mustache.php/test/MustacheHigherOrderSectionsTest.php b/vendor/flexi/vendor/mustache.php/test/MustacheHigherOrderSectionsTest.php new file mode 100644 index 0000000..87e4142 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/test/MustacheHigherOrderSectionsTest.php @@ -0,0 +1,114 @@ +<?php + +require_once '../Mustache.php'; + +class MustacheHigherOrderSectionsTest extends PHPUnit_Framework_TestCase { + + public function setUp() { + $this->foo = new Foo(); + } + + public function testAnonymousFunctionSectionCallback() { + if (version_compare(PHP_VERSION, '5.3.0', '<')) { + $this->markTestSkipped('Unable to test anonymous function section callbacks in PHP < 5.3'); + return; + } + + $this->foo->wrapper = function($text) { + return sprintf('<div class="anonymous">%s</div>', $text); + }; + + $this->assertEquals( + sprintf('<div class="anonymous">%s</div>', $this->foo->name), + $this->foo->render('{{#wrapper}}{{name}}{{/wrapper}}') + ); + } + + public function testSectionCallback() { + $this->assertEquals(sprintf('%s', $this->foo->name), $this->foo->render('{{name}}')); + $this->assertEquals(sprintf('<em>%s</em>', $this->foo->name), $this->foo->render('{{#wrap}}{{name}}{{/wrap}}')); + } + + public function testRuntimeSectionCallback() { + $this->foo->double_wrap = array($this->foo, 'wrapWithBoth'); + $this->assertEquals( + sprintf('<strong><em>%s</em></strong>', $this->foo->name), + $this->foo->render('{{#double_wrap}}{{name}}{{/double_wrap}}') + ); + } + + public function testStaticSectionCallback() { + $this->foo->trimmer = array(get_class($this->foo), 'staticTrim'); + $this->assertEquals($this->foo->name, $this->foo->render('{{#trimmer}} {{name}} {{/trimmer}}')); + } + + public function testViewArraySectionCallback() { + $data = array( + 'name' => 'Bob', + 'trim' => array(get_class($this->foo), 'staticTrim'), + ); + $this->assertEquals($data['name'], $this->foo->render('{{#trim}} {{name}} {{/trim}}', $data)); + } + + public function testViewArrayAnonymousSectionCallback() { + if (version_compare(PHP_VERSION, '5.3.0', '<')) { + $this->markTestSkipped('Unable to test anonymous function section callbacks in PHP < 5.3'); + return; + } + $data = array( + 'name' => 'Bob', + 'wrap' => function($text) { + return sprintf('[[%s]]', $text); + } + ); + $this->assertEquals( + sprintf('[[%s]]', $data['name']), + $this->foo->render('{{#wrap}}{{name}}{{/wrap}}', $data) + ); + } + + public function testMonsters() { + $frank = new Monster(); + $frank->title = 'Dr.'; + $frank->name = 'Frankenstein'; + $this->assertEquals('Dr. Frankenstein', $frank->render()); + + $dracula = new Monster(); + $dracula->title = 'Count'; + $dracula->name = 'Dracula'; + $this->assertEquals('Count Dracula', $dracula->render()); + } +} + +class Foo extends Mustache { + public $name = 'Justin'; + public $lorem = 'Lorem ipsum dolor sit amet,'; + public $wrap; + + public function __construct($template = null, $view = null, $partials = null) { + $this->wrap = array($this, 'wrapWithEm'); + parent::__construct($template, $view, $partials); + } + + public function wrapWithEm($text) { + return sprintf('<em>%s</em>', $text); + } + + public function wrapWithStrong($text) { + return sprintf('<strong>%s</strong>', $text); + } + + public function wrapWithBoth($text) { + return self::wrapWithStrong(self::wrapWithEm($text)); + } + + public static function staticTrim($text) { + return trim($text); + } +} + +class Monster extends Mustache { + public $_template = '{{#title}}{{title}} {{/title}}{{name}}'; + public $title; + public $name; +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/test/MustacheInjectionTest.php b/vendor/flexi/vendor/mustache.php/test/MustacheInjectionTest.php new file mode 100644 index 0000000..de74943 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/test/MustacheInjectionTest.php @@ -0,0 +1,127 @@ +<?php + +require_once '../Mustache.php'; + +/** + * @group mustache_injection + */ +class MustacheInjectionSectionTest extends PHPUnit_Framework_TestCase { + + // interpolation + + public function testInterpolationInjection() { + $data = array( + 'a' => '{{ b }}', + 'b' => 'FAIL' + ); + $template = '{{ a }}'; + $output = '{{ b }}'; + $m = new Mustache(); + $this->assertEquals($output, $m->render($template, $data)); + } + + public function testUnescapedInterpolationInjection() { + $data = array( + 'a' => '{{ b }}', + 'b' => 'FAIL' + ); + $template = '{{{ a }}}'; + $output = '{{ b }}'; + $m = new Mustache(); + $this->assertEquals($output, $m->render($template, $data)); + } + + + // sections + + public function testSectionInjection() { + $data = array( + 'a' => true, + 'b' => '{{ c }}', + 'c' => 'FAIL' + ); + $template = '{{# a }}{{ b }}{{/ a }}'; + $output = '{{ c }}'; + $m = new Mustache(); + $this->assertEquals($output, $m->render($template, $data)); + } + + public function testUnescapedSectionInjection() { + $data = array( + 'a' => true, + 'b' => '{{ c }}', + 'c' => 'FAIL' + ); + $template = '{{# a }}{{{ b }}}{{/ a }}'; + $output = '{{ c }}'; + $m = new Mustache(); + $this->assertEquals($output, $m->render($template, $data)); + } + + + // partials + + public function testPartialInjection() { + $data = array( + 'a' => '{{ b }}', + 'b' => 'FAIL' + ); + $template = '{{> partial }}'; + $partials = array( + 'partial' => '{{ a }}', + ); + $output = '{{ b }}'; + $m = new Mustache(); + $this->assertEquals($output, $m->render($template, $data, $partials)); + } + + public function testPartialUnescapedInjection() { + $data = array( + 'a' => '{{ b }}', + 'b' => 'FAIL' + ); + $template = '{{> partial }}'; + $partials = array( + 'partial' => '{{{ a }}}', + ); + $output = '{{ b }}'; + $m = new Mustache(); + $this->assertEquals($output, $m->render($template, $data, $partials)); + } + + + // lambdas + + public function testLambdaInterpolationInjection() { + $data = array( + 'a' => array($this, 'interpolationLambda'), + 'b' => '{{ c }}', + 'c' => 'FAIL' + ); + $template = '{{ a }}'; + $output = '{{ c }}'; + $m = new Mustache(); + $this->assertEquals($output, $m->render($template, $data)); + } + + public function interpolationLambda() { + return '{{ b }}'; + } + + public function testLambdaSectionInjection() { + $data = array( + 'a' => array($this, 'sectionLambda'), + 'b' => '{{ c }}', + 'c' => 'FAIL' + ); + $template = '{{# a }}b{{/ a }}'; + $output = '{{ c }}'; + $m = new Mustache(); + $this->assertEquals($output, $m->render($template, $data)); + } + + public function sectionLambda($content) { + return '{{ ' . $content . ' }}'; + } + +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/test/MustacheLoaderTest.php b/vendor/flexi/vendor/mustache.php/test/MustacheLoaderTest.php new file mode 100644 index 0000000..2674a0f --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/test/MustacheLoaderTest.php @@ -0,0 +1,60 @@ +<?php + +require_once '../Mustache.php'; +require_once '../MustacheLoader.php'; + +/** + * @group loader + */ +class MustacheLoaderTest extends PHPUnit_Framework_TestCase { + + public function testTheActualFilesystemLoader() { + $loader = new MustacheLoader(dirname(__FILE__).'/fixtures'); + $this->assertEquals(file_get_contents(dirname(__FILE__).'/fixtures/foo.mustache'), $loader['foo']); + $this->assertEquals(file_get_contents(dirname(__FILE__).'/fixtures/bar.mustache'), $loader['bar']); + } + + public function testMustacheUsesFilesystemLoader() { + $template = '{{> foo }} {{> bar }}'; + $data = array( + 'truthy' => true, + 'foo' => 'FOO', + 'bar' => 'BAR', + ); + $output = 'FOO BAR'; + $m = new Mustache(); + $partials = new MustacheLoader(dirname(__FILE__).'/fixtures'); + $this->assertEquals($output, $m->render($template, $data, $partials)); + } + + public function testMustacheUsesDifferentLoadersToo() { + $template = '{{> foo }} {{> bar }}'; + $data = array( + 'truthy' => true, + 'foo' => 'FOO', + 'bar' => 'BAR', + ); + $output = 'FOO BAR'; + $m = new Mustache(); + $partials = new DifferentMustacheLoader(); + $this->assertEquals($output, $m->render($template, $data, $partials)); + } +} + +class DifferentMustacheLoader implements ArrayAccess { + protected $partials = array( + 'foo' => '{{ foo }}', + 'bar' => '{{# truthy }}{{ bar }}{{/ truthy }}', + ); + + public function offsetExists($offset) { + return isset($this->partials[$offset]); + } + + public function offsetGet($offset) { + return $this->partials[$offset]; + } + + public function offsetSet($offset, $value) {} + public function offsetUnset($offset) {} +} diff --git a/vendor/flexi/vendor/mustache.php/test/MustacheObjectSectionTest.php b/vendor/flexi/vendor/mustache.php/test/MustacheObjectSectionTest.php new file mode 100644 index 0000000..82405d6 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/test/MustacheObjectSectionTest.php @@ -0,0 +1,73 @@ +<?php + +require_once '../Mustache.php'; + +/** + * @group sections + */ +class MustacheObjectSectionTest extends PHPUnit_Framework_TestCase { + public function testBasicObject() { + $alpha = new Alpha(); + $this->assertEquals('Foo', $alpha->render('{{#foo}}{{name}}{{/foo}}')); + } + + public function testObjectWithGet() { + $beta = new Beta(); + $this->assertEquals('Foo', $beta->render('{{#foo}}{{name}}{{/foo}}')); + } + + public function testSectionObjectWithGet() { + $gamma = new Gamma(); + $this->assertEquals('Foo', $gamma->render('{{#bar}}{{#foo}}{{name}}{{/foo}}{{/bar}}')); + } + + public function testSectionObjectWithFunction() { + $alpha = new Alpha(); + $alpha->foo = new Delta(); + $this->assertEquals('Foo', $alpha->render('{{#foo}}{{name}}{{/foo}}')); + } +} + +class Alpha extends Mustache { + public $foo; + + public function __construct() { + $this->foo = new StdClass(); + $this->foo->name = 'Foo'; + $this->foo->number = 1; + } +} + +class Beta extends Mustache { + protected $_data = array(); + + public function __construct() { + $this->_data['foo'] = new StdClass(); + $this->_data['foo']->name = 'Foo'; + $this->_data['foo']->number = 1; + } + + public function __isset($name) { + return array_key_exists($name, $this->_data); + } + + public function __get($name) { + return $this->_data[$name]; + } +} + +class Gamma extends Mustache { + public $bar; + + public function __construct() { + $this->bar = new Beta(); + } +} + +class Delta extends Mustache { + protected $_name = 'Foo'; + + public function name() { + return $this->_name; + } +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/test/MustachePragmaTest.php b/vendor/flexi/vendor/mustache.php/test/MustachePragmaTest.php new file mode 100644 index 0000000..c00441c --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/test/MustachePragmaTest.php @@ -0,0 +1,74 @@ +<?php + +require_once '../Mustache.php'; + +/** + * @group pragmas + */ +class MustachePragmaTest extends PHPUnit_Framework_TestCase { + + public function testUnknownPragmaException() { + $m = new Mustache(); + + try { + $m->render('{{%I-HAVE-THE-GREATEST-MUSTACHE}}'); + } catch (MustacheException $e) { + $this->assertEquals(MustacheException::UNKNOWN_PRAGMA, $e->getCode(), 'Caught exception code was not MustacheException::UNKNOWN_PRAGMA'); + return; + } + + $this->fail('Mustache should have thrown an unknown pragma exception'); + } + + public function testSuppressUnknownPragmaException() { + $m = new LessWhinyMustache(); + + try { + $this->assertEquals('', $m->render('{{%I-HAVE-THE-GREATEST-MUSTACHE}}')); + } catch (MustacheException $e) { + if ($e->getCode() == MustacheException::UNKNOWN_PRAGMA) { + $this->fail('Mustache should have thrown an unknown pragma exception'); + } else { + throw $e; + } + } + } + + public function testPragmaReplace() { + $m = new Mustache(); + $this->assertEquals('', $m->render('{{%UNESCAPED}}'), 'Pragma tag not removed'); + } + + public function testPragmaReplaceMultiple() { + $m = new Mustache(); + + $this->assertEquals('', $m->render('{{% UNESCAPED }}'), 'Pragmas should allow whitespace'); + $this->assertEquals('', $m->render('{{% UNESCAPED foo=bar }}'), 'Pragmas should allow whitespace'); + $this->assertEquals('', $m->render("{{%UNESCAPED}}\n{{%UNESCAPED}}"), 'Multiple pragma tags not removed'); + $this->assertEquals(' ', $m->render('{{%UNESCAPED}} {{%UNESCAPED}}'), 'Multiple pragma tags not removed'); + } + + public function testPragmaReplaceNewline() { + $m = new Mustache(); + $this->assertEquals('', $m->render("{{%UNESCAPED}}\n"), 'Trailing newline after pragma tag not removed'); + $this->assertEquals("\n", $m->render("\n{{%UNESCAPED}}\n"), 'Too many newlines removed with pragma tag'); + $this->assertEquals("1\n23", $m->render("1\n2{{%UNESCAPED}}\n3"), 'Wrong newline removed with pragma tag'); + } + + public function testPragmaReset() { + $m = new Mustache('', array('symbol' => '>>>')); + $this->assertEquals('>>>', $m->render('{{{symbol}}}')); + $this->assertEquals('>>>', $m->render('{{%UNESCAPED}}{{symbol}}')); + $this->assertEquals('>>>', $m->render('{{{symbol}}}')); + } +} + +class LessWhinyMustache extends Mustache { + protected $_throwsExceptions = array( + MustacheException::UNKNOWN_VARIABLE => false, + MustacheException::UNCLOSED_SECTION => true, + MustacheException::UNEXPECTED_CLOSE_SECTION => true, + MustacheException::UNKNOWN_PARTIAL => false, + MustacheException::UNKNOWN_PRAGMA => false, + ); +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/test/MustachePragmaUnescapedTest.php b/vendor/flexi/vendor/mustache.php/test/MustachePragmaUnescapedTest.php new file mode 100644 index 0000000..df6d94c --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/test/MustachePragmaUnescapedTest.php @@ -0,0 +1,19 @@ +<?php + +require_once '../Mustache.php'; + +/** + * @group pragmas + */ +class MustachePragmaUnescapedTest extends PHPUnit_Framework_TestCase { + + public function testPragmaUnescaped() { + $m = new Mustache(null, array('title' => 'Bear > Shark')); + + $this->assertEquals('Bear > Shark', $m->render('{{%UNESCAPED}}{{title}}')); + $this->assertEquals('Bear > Shark', $m->render('{{title}}')); + $this->assertEquals('Bear > Shark', $m->render('{{%UNESCAPED}}{{{title}}}')); + $this->assertEquals('Bear > Shark', $m->render('{{{title}}}')); + } + +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/test/MustacheSpecTest.php b/vendor/flexi/vendor/mustache.php/test/MustacheSpecTest.php new file mode 100644 index 0000000..d3a3413 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/test/MustacheSpecTest.php @@ -0,0 +1,174 @@ +<?php + +require_once '../Mustache.php'; +require_once './lib/yaml/lib/sfYamlParser.php'; + +/** + * A PHPUnit test case wrapping the Mustache Spec + * + * @group mustache-spec + */ +class MustacheSpecTest extends PHPUnit_Framework_TestCase { + + /** + * For some reason data providers can't mark tests skipped, so this test exists + * simply to provide a 'skipped' test if the `spec` submodule isn't initialized. + */ + public function testSpecInitialized() { + $spec_dir = dirname(__FILE__) . '/spec/specs/'; + if (!file_exists($spec_dir)) { + $this->markTestSkipped('Mustache spec submodule not initialized: run "git submodule update --init"'); + } + } + + /** + * @group comments + * @dataProvider loadCommentSpec + */ + public function testCommentSpec($desc, $template, $data, $partials, $expected) { + $m = new Mustache($template, $data, $partials); + $this->assertEquals($expected, $m->render(), $desc); + } + + /** + * @group delimiters + * @dataProvider loadDelimitersSpec + */ + public function testDelimitersSpec($desc, $template, $data, $partials, $expected) { + $m = new Mustache($template, $data, $partials); + $this->assertEquals($expected, $m->render(), $desc); + } + + /** + * @group interpolation + * @dataProvider loadInterpolationSpec + */ + public function testInterpolationSpec($desc, $template, $data, $partials, $expected) { + $m = new Mustache($template, $data, $partials); + $this->assertEquals($expected, $m->render(), $desc); + } + + /** + * @group inverted-sections + * @dataProvider loadInvertedSpec + */ + public function testInvertedSpec($desc, $template, $data, $partials, $expected) { + $m = new Mustache($template, $data, $partials); + $this->assertEquals($expected, $m->render(), $desc); + } + + /** + * @group lambdas + * @dataProvider loadLambdasSpec + */ + public function testLambdasSpec($desc, $template, $data, $partials, $expected) { + if (!version_compare(PHP_VERSION, '5.3.0', '>=')) { + $this->markTestSkipped('Unable to test Lambdas spec with PHP < 5.3.'); + } + + $data = $this->prepareLambdasSpec($data); + $m = new Mustache($template, $data, $partials); + $this->assertEquals($expected, $m->render(), $desc); + } + + /** + * Extract and lambdafy any 'lambda' values found in the $data array. + */ + protected function prepareLambdasSpec($data) { + foreach ($data as $key => $val) { + if ($key === 'lambda') { + if (!isset($val['php'])) { + $this->markTestSkipped(sprintf('PHP lambda test not implemented for this test.')); + } + + $func = $val['php']; + $data[$key] = function($text = null) use ($func) { return eval($func); }; + } else if (is_array($val)) { + $data[$key] = $this->prepareLambdasSpec($val); + } + } + return $data; + } + + /** + * @group partials + * @dataProvider loadPartialsSpec + */ + public function testPartialsSpec($desc, $template, $data, $partials, $expected) { + $m = new Mustache($template, $data, $partials); + $this->assertEquals($expected, $m->render(), $desc); + } + + /** + * @group sections + * @dataProvider loadSectionsSpec + */ + public function testSectionsSpec($desc, $template, $data, $partials, $expected) { + $m = new Mustache($template, $data, $partials); + $this->assertEquals($expected, $m->render(), $desc); + } + + public function loadCommentSpec() { + return $this->loadSpec('comments'); + } + + public function loadDelimitersSpec() { + return $this->loadSpec('delimiters'); + } + + public function loadInterpolationSpec() { + return $this->loadSpec('interpolation'); + } + + public function loadInvertedSpec() { + return $this->loadSpec('inverted'); + } + + public function loadLambdasSpec() { + return $this->loadSpec('~lambdas'); + } + + public function loadPartialsSpec() { + return $this->loadSpec('partials'); + } + + public function loadSectionsSpec() { + return $this->loadSpec('sections'); + } + + /** + * Data provider for the mustache spec test. + * + * Loads YAML files from the spec and converts them to PHPisms. + * + * @access public + * @return array + */ + protected function loadSpec($name) { + $filename = dirname(__FILE__) . '/spec/specs/' . $name . '.yml'; + if (!file_exists($filename)) { + return array(); + } + + $data = array(); + $yaml = new sfYamlParser(); + $file = file_get_contents($filename); + + // @hack: pre-process the 'lambdas' spec so the Symfony YAML parser doesn't complain. + if ($name === '~lambdas') { + $file = str_replace(" !code\n", "\n", $file); + } + + $spec = $yaml->parse($file); + foreach ($spec['tests'] as $test) { + $data[] = array( + $test['name'] . ': ' . $test['desc'], + $test['template'], + $test['data'], + isset($test['partials']) ? $test['partials'] : array(), + $test['expected'], + ); + } + return $data; + } +}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/test/MustacheTest.php b/vendor/flexi/vendor/mustache.php/test/MustacheTest.php new file mode 100644 index 0000000..0c0bb13 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/test/MustacheTest.php @@ -0,0 +1,464 @@ +<?php + +require_once '../Mustache.php'; + +/** + * A PHPUnit test case for Mustache.php. + * + * This is a very basic, very rudimentary unit test case. It's probably more important to have tests + * than to have elegant tests, so let's bear with it for a bit. + * + * This class assumes an example directory exists at `../examples` with the following structure: + * + * @code + * examples + * foo + * Foo.php + * foo.mustache + * foo.txt + * bar + * Bar.php + * bar.mustache + * bar.txt + * @endcode + * + * To use this test: + * + * 1. {@link http://www.phpunit.de/manual/current/en/installation.html Install PHPUnit} + * 2. run phpunit from the `test` directory: + * `phpunit MustacheTest` + * 3. Fix bugs. Lather, rinse, repeat. + * + * @extends PHPUnit_Framework_TestCase + */ +class MustacheTest extends PHPUnit_Framework_TestCase { + + const TEST_CLASS = 'Mustache'; + + protected $knownIssues = array( + // Just the whitespace ones... + ); + + /** + * Test Mustache constructor. + * + * @access public + * @return void + */ + public function test__construct() { + $template = '{{#mustaches}}{{#last}}and {{/last}}{{type}}{{^last}}, {{/last}}{{/mustaches}}'; + $data = array( + 'mustaches' => array( + array('type' => 'Natural'), + array('type' => 'Hungarian'), + array('type' => 'Dali'), + array('type' => 'English'), + array('type' => 'Imperial'), + array('type' => 'Freestyle', 'last' => 'true'), + ) + ); + $output = 'Natural, Hungarian, Dali, English, Imperial, and Freestyle'; + + $m1 = new Mustache(); + $this->assertEquals($output, $m1->render($template, $data)); + + $m2 = new Mustache($template); + $this->assertEquals($output, $m2->render(null, $data)); + + $m3 = new Mustache($template, $data); + $this->assertEquals($output, $m3->render()); + + $m4 = new Mustache(null, $data); + $this->assertEquals($output, $m4->render($template)); + } + + /** + * @dataProvider constructorOptions + */ + public function testConstructorOptions($options, $charset, $delimiters, $pragmas) { + $mustache = new MustacheExposedOptionsStub(null, null, null, $options); + $this->assertEquals($charset, $mustache->getCharset()); + $this->assertEquals($delimiters, $mustache->getDelimiters()); + $this->assertEquals($pragmas, $mustache->getPragmas()); + } + + public function constructorOptions() { + return array( + array( + array(), + 'UTF-8', + array('{{', '}}'), + array(), + ), + array( + array( + 'charset' => 'UTF-8', + 'delimiters' => '<< >>', + 'pragmas' => array(Mustache::PRAGMA_UNESCAPED => true) + ), + 'UTF-8', + array('<<', '>>'), + array(Mustache::PRAGMA_UNESCAPED => true), + ), + array( + array( + 'charset' => 'cp866', + 'delimiters' => array('[[[[', ']]]]'), + 'pragmas' => array(Mustache::PRAGMA_UNESCAPED => true) + ), + 'cp866', + array('[[[[', ']]]]'), + array(Mustache::PRAGMA_UNESCAPED => true), + ), + ); + } + + /** + * @expectedException MustacheException + */ + public function testConstructorInvalidPragmaOptionsThrowExceptions() { + $mustache = new Mustache(null, null, null, array('pragmas' => array('banana phone' => true))); + } + + /** + * Test __toString() function. + * + * @access public + * @return void + */ + public function test__toString() { + $m = new Mustache('{{first_name}} {{last_name}}', array('first_name' => 'Karl', 'last_name' => 'Marx')); + + $this->assertEquals('Karl Marx', $m->__toString()); + $this->assertEquals('Karl Marx', (string) $m); + + $m2 = $this->getMock(self::TEST_CLASS, array('render'), array()); + $m2->expects($this->once()) + ->method('render') + ->will($this->returnValue('foo')); + + $this->assertEquals('foo', $m2->render()); + } + + public function test__toStringException() { + $m = $this->getMock(self::TEST_CLASS, array('render'), array()); + $m->expects($this->once()) + ->method('render') + ->will($this->throwException(new Exception)); + + try { + $out = (string) $m; + } catch (Exception $e) { + $this->fail('__toString should catch all exceptions'); + } + } + + /** + * Test render(). + * + * @access public + * @return void + */ + public function testRender() { + $m = new Mustache(); + + $this->assertEquals('', $m->render('')); + $this->assertEquals('foo', $m->render('foo')); + $this->assertEquals('', $m->render(null)); + + $m2 = new Mustache('foo'); + $this->assertEquals('foo', $m2->render()); + + $m3 = new Mustache(''); + $this->assertEquals('', $m3->render()); + + $m3 = new Mustache(); + $this->assertEquals('', $m3->render(null)); + } + + /** + * Test render() with data. + * + * @group interpolation + */ + public function testRenderWithData() { + $m = new Mustache('{{first_name}} {{last_name}}'); + $this->assertEquals('Charlie Chaplin', $m->render(null, array('first_name' => 'Charlie', 'last_name' => 'Chaplin'))); + $this->assertEquals('Zappa, Frank', $m->render('{{last_name}}, {{first_name}}', array('first_name' => 'Frank', 'last_name' => 'Zappa'))); + } + + /** + * @group partials + */ + public function testRenderWithPartials() { + $m = new Mustache('{{>stache}}', null, array('stache' => '{{first_name}} {{last_name}}')); + $this->assertEquals('Charlie Chaplin', $m->render(null, array('first_name' => 'Charlie', 'last_name' => 'Chaplin'))); + $this->assertEquals('Zappa, Frank', $m->render('{{last_name}}, {{first_name}}', array('first_name' => 'Frank', 'last_name' => 'Zappa'))); + } + + /** + * @group interpolation + * @dataProvider interpolationData + */ + public function testDoubleRenderMustacheTags($template, $context, $expected) { + $m = new Mustache($template, $context); + $this->assertEquals($expected, $m->render()); + } + + public function interpolationData() { + return array( + array( + '{{#a}}{{=<% %>=}}{{b}} c<%={{ }}=%>{{/a}}', + array('a' => array(array('b' => 'Do Not Render'))), + '{{b}} c' + ), + array( + '{{#a}}{{b}}{{/a}}', + array('a' => array('b' => '{{c}}'), 'c' => 'FAIL'), + '{{c}}' + ), + ); + } + + /** + * Mustache should allow newlines (and other whitespace) in comments and all other tags. + * + * @group comments + */ + public function testNewlinesInComments() { + $m = new Mustache("{{! comment \n \t still a comment... }}"); + $this->assertEquals('', $m->render()); + } + + /** + * Mustache should return the same thing when invoked multiple times. + */ + public function testMultipleInvocations() { + $m = new Mustache('x'); + $first = $m->render(); + $second = $m->render(); + + $this->assertEquals('x', $first); + $this->assertEquals($first, $second); + } + + /** + * Mustache should return the same thing when invoked multiple times. + * + * @group interpolation + */ + public function testMultipleInvocationsWithTags() { + $m = new Mustache('{{one}} {{two}}', array('one' => 'foo', 'two' => 'bar')); + $first = $m->render(); + $second = $m->render(); + + $this->assertEquals('foo bar', $first); + $this->assertEquals($first, $second); + } + + /** + * Mustache should not use templates passed to the render() method for subsequent invocations. + */ + public function testResetTemplateForMultipleInvocations() { + $m = new Mustache('Sirve.'); + $this->assertEquals('No sirve.', $m->render('No sirve.')); + $this->assertEquals('Sirve.', $m->render()); + + $m2 = new Mustache(); + $this->assertEquals('No sirve.', $m2->render('No sirve.')); + $this->assertEquals('', $m2->render()); + } + + /** + * Test the __clone() magic function. + * + * @group examples + * @dataProvider getExamples + * + * @param string $class + * @param string $template + * @param string $output + */ + public function test__clone($class, $template, $output) { + if (isset($this->knownIssues[$class])) { + return $this->markTestSkipped($this->knownIssues[$class]); + } + + $m = new $class; + $n = clone $m; + + $n_output = $n->render($template); + + $o = clone $n; + + $this->assertEquals($m->render($template), $n_output); + $this->assertEquals($n_output, $o->render($template)); + + $this->assertNotSame($m, $n); + $this->assertNotSame($n, $o); + $this->assertNotSame($m, $o); + } + + /** + * Test everything in the `examples` directory. + * + * @group examples + * @dataProvider getExamples + * + * @param string $class + * @param string $template + * @param string $output + */ + public function testExamples($class, $template, $output) { + if (isset($this->knownIssues[$class])) { + return $this->markTestSkipped($this->knownIssues[$class]); + } + + $m = new $class; + $this->assertEquals($output, $m->render($template)); + } + + /** + * Data provider for testExamples method. + * + * Assumes that an `examples` directory exists inside parent directory. + * This examples directory should contain any number of subdirectories, each of which contains + * three files: one Mustache class (.php), one Mustache template (.mustache), and one output file + * (.txt). + * + * This whole mess will be refined later to be more intuitive and less prescriptive, but it'll + * do for now. Especially since it means we can have unit tests :) + * + * @return array + */ + public function getExamples() { + $basedir = dirname(__FILE__) . '/../examples/'; + + $ret = array(); + + $files = new RecursiveDirectoryIterator($basedir); + while ($files->valid()) { + + if ($files->hasChildren() && $children = $files->getChildren()) { + $example = $files->getSubPathname(); + $class = null; + $template = null; + $output = null; + + foreach ($children as $file) { + if (!$file->isFile()) continue; + + $filename = $file->getPathname(); + $info = pathinfo($filename); + + if (isset($info['extension'])) { + switch($info['extension']) { + case 'php': + $class = $info['filename']; + include_once($filename); + break; + + case 'mustache': + $template = file_get_contents($filename); + break; + + case 'txt': + $output = file_get_contents($filename); + break; + } + } + } + + if (!empty($class)) { + $ret[$example] = array($class, $template, $output); + } + } + + $files->next(); + } + return $ret; + } + + /** + * @group delimiters + */ + public function testCrazyDelimiters() { + $m = new Mustache(null, array('result' => 'success')); + $this->assertEquals('success', $m->render('{{=[[ ]]=}}[[ result ]]')); + $this->assertEquals('success', $m->render('{{=(( ))=}}(( result ))')); + $this->assertEquals('success', $m->render('{{={$ $}=}}{$ result $}')); + $this->assertEquals('success', $m->render('{{=<.. ..>=}}<.. result ..>')); + $this->assertEquals('success', $m->render('{{=^^ ^^}}^^ result ^^')); + $this->assertEquals('success', $m->render('{{=// \\\\}}// result \\\\')); + } + + /** + * @group delimiters + */ + public function testResetDelimiters() { + $m = new Mustache(null, array('result' => 'success')); + $this->assertEquals('success', $m->render('{{=[[ ]]=}}[[ result ]]')); + $this->assertEquals('success', $m->render('{{=<< >>=}}<< result >>')); + $this->assertEquals('success', $m->render('{{=<% %>=}}<% result %>')); + } + + /** + * @group delimiters + */ + public function testStickyDelimiters() { + $m = new Mustache(null, array('result' => 'FAIL')); + $this->assertEquals('{{ result }}', $m->render('{{=[[ ]]=}}{{ result }}[[={{ }}=]]')); + $this->assertEquals('{{#result}}{{/result}}', $m->render('{{=[[ ]]=}}{{#result}}{{/result}}[[={{ }}=]]')); + $this->assertEquals('{{ result }}', $m->render('{{=[[ ]]=}}[[#result]]{{ result }}[[/result]][[={{ }}=]]')); + $this->assertEquals('{{ result }}', $m->render('{{#result}}{{=[[ ]]=}}{{ result }}[[/result]][[^result]][[={{ }}=]][[ result ]]{{/result}}')); + } + + /** + * @group sections + * @dataProvider poorlyNestedSections + * @expectedException MustacheException + */ + public function testPoorlyNestedSections($template) { + $m = new Mustache($template); + $m->render(); + } + + public function poorlyNestedSections() { + return array( + array('{{#foo}}'), + array('{{#foo}}{{/bar}}'), + array('{{#foo}}{{#bar}}{{/foo}}'), + array('{{#foo}}{{#bar}}{{/foo}}{{/bar}}'), + array('{{#foo}}{{/bar}}{{/foo}}'), + ); + } + + /** + * Ensure that Mustache doesn't double-render sections (allowing mustache injection). + * + * @group sections + */ + public function testMustacheInjection() { + $template = '{{#foo}}{{bar}}{{/foo}}'; + $view = array( + 'foo' => true, + 'bar' => '{{win}}', + 'win' => 'FAIL', + ); + + $m = new Mustache($template, $view); + $this->assertEquals('{{win}}', $m->render()); + } +} + +class MustacheExposedOptionsStub extends Mustache { + public function getPragmas() { + return $this->_pragmas; + } + public function getCharset() { + return $this->_charset; + } + public function getDelimiters() { + return array($this->_otag, $this->_ctag); + } +} diff --git a/vendor/flexi/vendor/mustache.php/test/fixtures/bar.mustache b/vendor/flexi/vendor/mustache.php/test/fixtures/bar.mustache new file mode 100644 index 0000000..139594d --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/test/fixtures/bar.mustache @@ -0,0 +1 @@ +{{# truthy }}{{ bar }}{{/ truthy }}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/test/fixtures/foo.mustache b/vendor/flexi/vendor/mustache.php/test/fixtures/foo.mustache new file mode 100644 index 0000000..008c714 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/test/fixtures/foo.mustache @@ -0,0 +1 @@ +{{ foo }}
\ No newline at end of file diff --git a/vendor/flexi/vendor/mustache.php/test/lib/yaml/LICENSE b/vendor/flexi/vendor/mustache.php/test/lib/yaml/LICENSE new file mode 100644 index 0000000..3cef853 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/test/lib/yaml/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2008-2009 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/flexi/vendor/mustache.php/test/lib/yaml/README.markdown b/vendor/flexi/vendor/mustache.php/test/lib/yaml/README.markdown new file mode 100644 index 0000000..e4f80cf --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/test/lib/yaml/README.markdown @@ -0,0 +1,15 @@ +Symfony YAML: A PHP library that speaks YAML +============================================ + +Symfony YAML is a PHP library that parses YAML strings and converts them to +PHP arrays. It can also converts PHP arrays to YAML strings. Its official +website is at http://components.symfony-project.org/yaml/. + +The documentation is to be found in the `doc/` directory. + +Symfony YAML is licensed under the MIT license (see LICENSE file). + +The Symfony YAML library is developed and maintained by the +[symfony](http://www.symfony-project.org/) project team. It has been extracted +from symfony to be used as a standalone library. Symfony YAML is part of the +[symfony components project](http://components.symfony-project.org/). diff --git a/vendor/flexi/vendor/mustache.php/test/lib/yaml/doc/00-Introduction.markdown b/vendor/flexi/vendor/mustache.php/test/lib/yaml/doc/00-Introduction.markdown new file mode 100644 index 0000000..e592758 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/test/lib/yaml/doc/00-Introduction.markdown @@ -0,0 +1,143 @@ +Introduction +============ + +This book is about *Symfony YAML*, a PHP library part of the Symfony +Components project. Its official website is at +http://components.symfony-project.org/yaml/. + +>**SIDEBAR** +>About the Symfony Components +> +>[Symfony Components](http://components.symfony-project.org/) are +>standalone PHP classes that can be easily used in any +>PHP project. Most of the time, they have been developed as part of the +>[Symfony framework](http://www.symfony-project.org/), and decoupled from the +>main framework later on. You don't need to use the Symfony MVC framework to use +>the components. + +What is it? +----------- + +Symfony YAML is a PHP library that parses YAML strings and converts them to +PHP arrays. It can also converts PHP arrays to YAML strings. + +[YAML](http://www.yaml.org/), YAML Ain't Markup Language, is a human friendly +data serialization standard for all programming languages. YAML is a great +format for your configuration files. YAML files are as expressive as XML files +and as readable as INI files. + +### Easy to use + +There is only one archive to download, and you are ready to go. No +configuration, No installation. Drop the files in a directory and start using +it today in your projects. + +### Open-Source + +Released under the MIT license, you are free to do whatever you want, even in +a commercial environment. You are also encouraged to contribute. + + +### Used by popular Projects + +Symfony YAML was initially released as part of the symfony framework, one of +the most popular PHP web framework. It is also embedded in other popular +projects like PHPUnit or Doctrine. + +### Documented + +Symfony YAML is fully documented, with a dedicated online book, and of course +a full API documentation. + +### Fast + +One of the goal of Symfony YAML is to find the right balance between speed and +features. It supports just the needed feature to handle configuration files. + +### Unit tested + +The library is fully unit-tested. With more than 400 unit tests, the library +is stable and is already used in large projects. + +### Real Parser + +It sports a real parser and is able to parse a large subset of the YAML +specification, for all your configuration needs. It also means that the parser +is pretty robust, easy to understand, and simple enough to extend. + +### Clear error messages + +Whenever you have a syntax problem with your YAML files, the library outputs a +helpful message with the filename and the line number where the problem +occurred. It eases the debugging a lot. + +### Dump support + +It is also able to dump PHP arrays to YAML with object support, and inline +level configuration for pretty outputs. + +### Types Support + +It supports most of the YAML built-in types like dates, integers, octals, +booleans, and much more... + + +### Full merge key support + +Full support for references, aliases, and full merge key. Don't repeat +yourself by referencing common configuration bits. + +### PHP Embedding + +YAML files are dynamic. By embedding PHP code inside a YAML file, you have +even more power for your configuration files. + +Installation +------------ + +Symfony YAML can be installed by downloading the source code as a +[tar](http://github.com/fabpot/yaml/tarball/master) archive or a +[zip](http://github.com/fabpot/yaml/zipball/master) one. + +To stay up-to-date, you can also use the official Subversion +[repository](http://svn.symfony-project.com/components/yaml/). + +If you are a Git user, there is an official +[mirror](http://github.com/fabpot/yaml), which is updated every 10 minutes. + +If you prefer to install the component globally on your machine, you can use +the symfony [PEAR](http://pear.symfony-project.com/) channel server. + +Support +------- + +Support questions and enhancements can be discussed on the +[mailing-list](http://groups.google.com/group/symfony-components). + +If you find a bug, you can create a ticket at the symfony +[trac](http://trac.symfony-project.org/newticket) under the *YAML* component. + +License +------- + +The Symfony YAML component is licensed under the *MIT license*: + +>Copyright (c) 2008-2009 Fabien Potencier +> +>Permission is hereby granted, free of charge, to any person obtaining a copy +>of this software and associated documentation files (the "Software"), to deal +>in the Software without restriction, including without limitation the rights +>to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +>copies of the Software, and to permit persons to whom the Software is furnished +>to do so, subject to the following conditions: +> +>The above copyright notice and this permission notice shall be included in all +>copies or substantial portions of the Software. +> +>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +>IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +>FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +>AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +>LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +>OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +>THE SOFTWARE. diff --git a/vendor/flexi/vendor/mustache.php/test/lib/yaml/doc/01-Usage.markdown b/vendor/flexi/vendor/mustache.php/test/lib/yaml/doc/01-Usage.markdown new file mode 100644 index 0000000..644cf11 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/test/lib/yaml/doc/01-Usage.markdown @@ -0,0 +1,110 @@ +Using Symfony YAML +================== + +The Symfony YAML library is very simple and consists of two main classes: one +to parse YAML strings (`sfYamlParser`), and the other to dump a PHP array to +a YAML string (`sfYamlDumper`). + +On top of these two core classes, the main `sfYaml` class acts as a thin +wrapper and simplifies common uses. + +Reading YAML Files +------------------ + +The `sfYamlParser::parse()` method parses a YAML string and converts it to a +PHP array: + + [php] + $yaml = new sfYamlParser(); + $value = $yaml->parse(file_get_contents('/path/to/file.yaml')); + +If an error occurs during parsing, the parser throws an exception indicating +the error type and the line in the original YAML string where the error +occurred: + + [php] + try + { + $value = $yaml->parse(file_get_contents('/path/to/file.yaml')); + } + catch (InvalidArgumentException $e) + { + // an error occurred during parsing + echo "Unable to parse the YAML string: ".$e->getMessage(); + } + +>**TIP** +>As the parser is reentrant, you can use the same parser object to load +>different YAML strings. + +When loading a YAML file, it is sometimes better to use the `sfYaml::load()` +wrapper method: + + [php] + $loader = sfYaml::load('/path/to/file.yml'); + +The `sfYaml::load()` static method takes a YAML string or a file containing +YAML. Internally, it calls the `sfYamlParser::parse()` method, but with some +added bonuses: + + * It executes the YAML file as if it was a PHP file, so that you can embed + PHP commands in YAML files; + + * When a file cannot be parsed, it automatically adds the file name to the + error message, simplifying debugging when your application is loading + several YAML files. + +Writing YAML Files +------------------ + +The `sfYamlDumper` dumps any PHP array to its YAML representation: + + [php] + $array = array('foo' => 'bar', 'bar' => array('foo' => 'bar', 'bar' => 'baz')); + + $dumper = new sfYamlDumper(); + $yaml = $dumper->dump($array); + file_put_contents('/path/to/file.yaml', $yaml); + +>**NOTE** +>Of course, the Symfony YAML dumper is not able to dump resources. Also, +>even if the dumper is able to dump PHP objects, it is to be considered +>an alpha feature. + +If you only need to dump one array, you can use the `sfYaml::dump()` static +method shortcut: + + [php] + $yaml = sfYaml::dump($array, $inline); + +The YAML format supports two kind of representation for arrays, the expanded +one, and the inline one. By default, the dumper uses the inline +representation: + + [yml] + { foo: bar, bar: { foo: bar, bar: baz } } + +The second argument of the `dump()` method customizes the level at which the +output switches from the expanded representation to the inline one: + + [php] + echo $dumper->dump($array, 1); + +- + + [yml] + foo: bar + bar: { foo: bar, bar: baz } + +- + + [php] + echo $dumper->dump($array, 2); + +- + + [yml] + foo: bar + bar: + foo: bar + bar: baz diff --git a/vendor/flexi/vendor/mustache.php/test/lib/yaml/doc/02-YAML.markdown b/vendor/flexi/vendor/mustache.php/test/lib/yaml/doc/02-YAML.markdown new file mode 100644 index 0000000..a64c19e --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/test/lib/yaml/doc/02-YAML.markdown @@ -0,0 +1,312 @@ +The YAML Format +=============== + +According to the official [YAML](http://yaml.org/) website, YAML is "a human +friendly data serialization standard for all programming languages". + +Even if the YAML format can describe complex nested data structure, this +chapter only describes the minimum set of features needed to use YAML as a +configuration file format. + +YAML is a simple language that describes data. As PHP, it has a syntax for +simple types like strings, booleans, floats, or integers. But unlike PHP, it +makes a difference between arrays (sequences) and hashes (mappings). + +Scalars +------- + +The syntax for scalars is similar to the PHP syntax. + +### Strings + + [yml] + A string in YAML + +- + + [yml] + 'A singled-quoted string in YAML' + +>**TIP** +>In a single quoted string, a single quote `'` must be doubled: +> +> [yml] +> 'A single quote '' in a single-quoted string' + + [yml] + "A double-quoted string in YAML\n" + +Quoted styles are useful when a string starts or ends with one or more +relevant spaces. + +>**TIP** +>The double-quoted style provides a way to express arbitrary strings, by +>using `\` escape sequences. It is very useful when you need to embed a +>`\n` or a unicode character in a string. + +When a string contains line breaks, you can use the literal style, indicated +by the pipe (`|`), to indicate that the string will span several lines. In +literals, newlines are preserved: + + [yml] + | + \/ /| |\/| | + / / | | | |__ + +Alternatively, strings can be written with the folded style, denoted by `>`, +where each line break is replaced by a space: + + [yml] + > + This is a very long sentence + that spans several lines in the YAML + but which will be rendered as a string + without carriage returns. + +>**NOTE** +>Notice the two spaces before each line in the previous examples. They +>won't appear in the resulting PHP strings. + +### Numbers + + [yml] + # an integer + 12 + +- + + [yml] + # an octal + 014 + +- + + [yml] + # an hexadecimal + 0xC + +- + + [yml] + # a float + 13.4 + +- + + [yml] + # an exponential number + 1.2e+34 + +- + + [yml] + # infinity + .inf + +### Nulls + +Nulls in YAML can be expressed with `null` or `~`. + +### Booleans + +Booleans in YAML are expressed with `true` and `false`. + +>**NOTE** +>The symfony YAML parser also recognize `on`, `off`, `yes`, and `no` but +>it is strongly discouraged to use them as it has been removed from the +>1.2 YAML specifications. + +### Dates + +YAML uses the ISO-8601 standard to express dates: + + [yml] + 2001-12-14t21:59:43.10-05:00 + +- + + [yml] + # simple date + 2002-12-14 + +Collections +----------- + +A YAML file is rarely used to describe a simple scalar. Most of the time, it +describes a collection. A collection can be a sequence or a mapping of +elements. Both sequences and mappings are converted to PHP arrays. + +Sequences use a dash followed by a space (`- `): + + [yml] + - PHP + - Perl + - Python + +The previous YAML file is equivalent to the following PHP code: + + [php] + array('PHP', 'Perl', 'Python'); + +Mappings use a colon followed by a space (`: `) to mark each key/value pair: + + [yml] + PHP: 5.2 + MySQL: 5.1 + Apache: 2.2.20 + +which is equivalent to this PHP code: + + [php] + array('PHP' => 5.2, 'MySQL' => 5.1, 'Apache' => '2.2.20'); + +>**NOTE** +>In a mapping, a key can be any valid scalar. + +The number of spaces between the colon and the value does not matter: + + [yml] + PHP: 5.2 + MySQL: 5.1 + Apache: 2.2.20 + +YAML uses indentation with one or more spaces to describe nested collections: + + [yml] + "symfony 1.0": + PHP: 5.0 + Propel: 1.2 + "symfony 1.2": + PHP: 5.2 + Propel: 1.3 + +The following YAML is equivalent to the following PHP code: + + [php] + array( + 'symfony 1.0' => array( + 'PHP' => 5.0, + 'Propel' => 1.2, + ), + 'symfony 1.2' => array( + 'PHP' => 5.2, + 'Propel' => 1.3, + ), + ); + +There is one important thing you need to remember when using indentation in a +YAML file: *Indentation must be done with one or more spaces, but never with +tabulations*. + +You can nest sequences and mappings as you like: + + [yml] + 'Chapter 1': + - Introduction + - Event Types + 'Chapter 2': + - Introduction + - Helpers + +YAML can also use flow styles for collections, using explicit indicators +rather than indentation to denote scope. + +A sequence can be written as a comma separated list within square brackets +(`[]`): + + [yml] + [PHP, Perl, Python] + +A mapping can be written as a comma separated list of key/values within curly +braces (`{}`): + + [yml] + { PHP: 5.2, MySQL: 5.1, Apache: 2.2.20 } + +You can mix and match styles to achieve a better readability: + + [yml] + 'Chapter 1': [Introduction, Event Types] + 'Chapter 2': [Introduction, Helpers] + +- + + [yml] + "symfony 1.0": { PHP: 5.0, Propel: 1.2 } + "symfony 1.2": { PHP: 5.2, Propel: 1.3 } + +Comments +-------- + +Comments can be added in YAML by prefixing them with a hash mark (`#`): + + [yml] + # Comment on a line + "symfony 1.0": { PHP: 5.0, Propel: 1.2 } # Comment at the end of a line + "symfony 1.2": { PHP: 5.2, Propel: 1.3 } + +>**NOTE** +>Comments are simply ignored by the YAML parser and do not need to be +>indented according to the current level of nesting in a collection. + +Dynamic YAML files +------------------ + +In symfony, a YAML file can contain PHP code that is evaluated just before the +parsing occurs: + + [php] + 1.0: + version: <?php echo file_get_contents('1.0/VERSION')."\n" ?> + 1.1: + version: "<?php echo file_get_contents('1.1/VERSION') ?>" + +Be careful to not mess up with the indentation. Keep in mind the following +simple tips when adding PHP code to a YAML file: + + * The `<?php ?>` statements must always start the line or be embedded in a + value. + + * If a `<?php ?>` statement ends a line, you need to explicitly output a new + line ("\n"). + +<div class="pagebreak"></div> + +A Full Length Example +--------------------- + +The following example illustrates most YAML notations explained in this +document: + + [yml] + "symfony 1.0": + end_of_maintainance: 2010-01-01 + is_stable: true + release_manager: "Grégoire Hubert" + description: > + This stable version is the right choice for projects + that need to be maintained for a long period of time. + latest_beta: ~ + latest_minor: 1.0.20 + supported_orms: [Propel] + archives: { source: [zip, tgz], sandbox: [zip, tgz] } + + "symfony 1.2": + end_of_maintainance: 2008-11-01 + is_stable: true + release_manager: 'Fabian Lange' + description: > + This stable version is the right choice + if you start a new project today. + latest_beta: null + latest_minor: 1.2.5 + supported_orms: + - Propel + - Doctrine + archives: + source: + - zip + - tgz + sandbox: + - zip + - tgz diff --git a/vendor/flexi/vendor/mustache.php/test/lib/yaml/doc/A-License.markdown b/vendor/flexi/vendor/mustache.php/test/lib/yaml/doc/A-License.markdown new file mode 100644 index 0000000..49cdd7c --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/test/lib/yaml/doc/A-License.markdown @@ -0,0 +1,108 @@ +Appendix A - License +==================== + +Attribution-Share Alike 3.0 Unported License +-------------------------------------------- + +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. + +BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. + +1. Definitions + + a. **"Adaptation"** means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License. + + b. **"Collection"** means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined below) for the purposes of this License. + + c. **"Creative Commons Compatible License"** means a license that is listed at http://creativecommons.org/compatiblelicenses that has been approved by Creative Commons as being essentially equivalent to this License, including, at a minimum, because that license: (i) contains terms that have the same purpose, meaning and effect as the License Elements of this License; and, (ii) explicitly permits the relicensing of adaptations of works made available under that license under this License or a Creative Commons jurisdiction license with the same License Elements as this License. + + d. **"Distribute"** means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership. + + e. **"License Elements"** means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike. + + f. **"Licensor"** means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License. + + g. **"Original Author"** means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast. + + h. **"Work"** means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work. + + i. **"You"** means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. + + j. **"Publicly Perform"** means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images. + + k. **"Reproduce"** means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium. + +2. Fair Dealing Rights + + Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws. + +3. License Grant + + Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: + + a. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; + + b. to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified."; + + c. to Distribute and Publicly Perform the Work including as incorporated in Collections; and, + + d. to Distribute and Publicly Perform Adaptations. + + e. For the avoidance of doubt: + + i. **Non-waivable Compulsory License Schemes**. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; + + ii. **Waivable Compulsory License Schemes**. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and, + + iii. **Voluntary License Schemes**. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License. + + The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved. + +4. Restrictions + + The license granted in Section 3 above is expressly made subject to and limited by the following restrictions: + + a. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(c), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(c), as requested. + + b. You may Distribute or Publicly Perform an Adaptation only under the terms of: (i) this License; (ii) a later version of this License with the same License Elements as this License; (iii) a Creative Commons jurisdiction license (either this or a later license version) that contains the same License Elements as this License (e.g., Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible License. If you license the Adaptation under one of the licenses mentioned in (iv), you must comply with the terms of that license. If you license the Adaptation under the terms of any of the licenses mentioned in (i), (ii) or (iii) (the "Applicable License"), you must comply with the terms of the Applicable License generally and the following provisions: (I) You must include a copy of, or the URI for, the Applicable License with every copy of each Adaptation You Distribute or Publicly Perform; (II) You may not offer or impose any terms on the Adaptation that restrict the terms of the Applicable License or the ability of the recipient of the Adaptation to exercise the rights granted to that recipient under the terms of the Applicable License; (III) You must keep intact all notices that refer to the Applicable License and to the disclaimer of warranties with every copy of the Work as included in the Adaptation You Distribute or Publicly Perform; (IV) when You Distribute or Publicly Perform the Adaptation, You may not impose any effective technological measures on the Adaptation that restrict the ability of a recipient of the Adaptation from You to exercise the rights granted to that recipient under the terms of the Applicable License. This Section 4(b) applies to the Adaptation as incorporated in a Collection, but this does not require the Collection apart from the Adaptation itself to be made subject to the terms of the Applicable License. + + c. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Ssection 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties. + + d. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise. + +5. Representations, Warranties and Disclaimer + + UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. + +6. Limitation on Liability + + EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. Termination + + a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. + + b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. + +8. Miscellaneous + + a. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License. + + b. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License. + + c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + + d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. + + e. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You. + + f. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law. + +>**SIDEBAR** +>Creative Commons Notice +> +>Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor. +> +>Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of the License. +> +>Creative Commons may be contacted at http://creativecommons.org/. diff --git a/vendor/flexi/vendor/mustache.php/test/lib/yaml/lib/sfYaml.php b/vendor/flexi/vendor/mustache.php/test/lib/yaml/lib/sfYaml.php new file mode 100644 index 0000000..1d89ccc --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/test/lib/yaml/lib/sfYaml.php @@ -0,0 +1,135 @@ +<?php + +/* + * This file is part of the symfony package. + * (c) 2004-2006 Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * sfYaml offers convenience methods to load and dump YAML. + * + * @package symfony + * @subpackage yaml + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + * @version SVN: $Id: sfYaml.class.php 8988 2008-05-15 20:24:26Z fabien $ + */ +class sfYaml +{ + static protected + $spec = '1.2'; + + /** + * Sets the YAML specification version to use. + * + * @param string $version The YAML specification version + */ + static public function setSpecVersion($version) + { + if (!in_array($version, array('1.1', '1.2'))) + { + throw new InvalidArgumentException(sprintf('Version %s of the YAML specifications is not supported', $version)); + } + + self::$spec = $version; + } + + /** + * Gets the YAML specification version to use. + * + * @return string The YAML specification version + */ + static public function getSpecVersion() + { + return self::$spec; + } + + /** + * Loads YAML into a PHP array. + * + * The load method, when supplied with a YAML stream (string or file), + * will do its best to convert YAML in a file into a PHP array. + * + * Usage: + * <code> + * $array = sfYaml::load('config.yml'); + * print_r($array); + * </code> + * + * @param string $input Path of YAML file or string containing YAML + * + * @return array The YAML converted to a PHP array + * + * @throws InvalidArgumentException If the YAML is not valid + */ + public static function load($input) + { + $file = ''; + + // if input is a file, process it + if (strpos($input, "\n") === false && is_file($input)) + { + $file = $input; + + ob_start(); + $retval = include($input); + $content = ob_get_clean(); + + // if an array is returned by the config file assume it's in plain php form else in YAML + $input = is_array($retval) ? $retval : $content; + } + + // if an array is returned by the config file assume it's in plain php form else in YAML + if (is_array($input)) + { + return $input; + } + + require_once dirname(__FILE__).'/sfYamlParser.php'; + + $yaml = new sfYamlParser(); + + try + { + $ret = $yaml->parse($input); + } + catch (Exception $e) + { + throw new InvalidArgumentException(sprintf('Unable to parse %s: %s', $file ? sprintf('file "%s"', $file) : 'string', $e->getMessage())); + } + + return $ret; + } + + /** + * Dumps a PHP array to a YAML string. + * + * The dump method, when supplied with an array, will do its best + * to convert the array into friendly YAML. + * + * @param array $array PHP array + * @param integer $inline The level where you switch to inline YAML + * + * @return string A YAML string representing the original PHP array + */ + public static function dump($array, $inline = 2) + { + require_once dirname(__FILE__).'/sfYamlDumper.php'; + + $yaml = new sfYamlDumper(); + + return $yaml->dump($array, $inline); + } +} + +/** + * Wraps echo to automatically provide a newline. + * + * @param string $string The string to echo with new line + */ +function echoln($string) +{ + echo $string."\n"; +} diff --git a/vendor/flexi/vendor/mustache.php/test/lib/yaml/lib/sfYamlDumper.php b/vendor/flexi/vendor/mustache.php/test/lib/yaml/lib/sfYamlDumper.php new file mode 100644 index 0000000..0ada2b3 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/test/lib/yaml/lib/sfYamlDumper.php @@ -0,0 +1,60 @@ +<?php + +/* + * This file is part of the symfony package. + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require_once(dirname(__FILE__).'/sfYamlInline.php'); + +/** + * sfYamlDumper dumps PHP variables to YAML strings. + * + * @package symfony + * @subpackage yaml + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + * @version SVN: $Id: sfYamlDumper.class.php 10575 2008-08-01 13:08:42Z nicolas $ + */ +class sfYamlDumper +{ + /** + * Dumps a PHP value to YAML. + * + * @param mixed $input The PHP value + * @param integer $inline The level where you switch to inline YAML + * @param integer $indent The level o indentation indentation (used internally) + * + * @return string The YAML representation of the PHP value + */ + public function dump($input, $inline = 0, $indent = 0) + { + $output = ''; + $prefix = $indent ? str_repeat(' ', $indent) : ''; + + if ($inline <= 0 || !is_array($input) || empty($input)) + { + $output .= $prefix.sfYamlInline::dump($input); + } + else + { + $isAHash = array_keys($input) !== range(0, count($input) - 1); + + foreach ($input as $key => $value) + { + $willBeInlined = $inline - 1 <= 0 || !is_array($value) || empty($value); + + $output .= sprintf('%s%s%s%s', + $prefix, + $isAHash ? sfYamlInline::dump($key).':' : '-', + $willBeInlined ? ' ' : "\n", + $this->dump($value, $inline - 1, $willBeInlined ? 0 : $indent + 2) + ).($willBeInlined ? "\n" : ''); + } + } + + return $output; + } +} diff --git a/vendor/flexi/vendor/mustache.php/test/lib/yaml/lib/sfYamlInline.php b/vendor/flexi/vendor/mustache.php/test/lib/yaml/lib/sfYamlInline.php new file mode 100644 index 0000000..a88cbb3 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/test/lib/yaml/lib/sfYamlInline.php @@ -0,0 +1,442 @@ +<?php + +/* + * This file is part of the symfony package. + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require_once dirname(__FILE__).'/sfYaml.php'; + +/** + * sfYamlInline implements a YAML parser/dumper for the YAML inline syntax. + * + * @package symfony + * @subpackage yaml + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + * @version SVN: $Id: sfYamlInline.class.php 16177 2009-03-11 08:32:48Z fabien $ + */ +class sfYamlInline +{ + const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\']*(?:\'\'[^\']*)*)\')'; + + /** + * Convert a YAML string to a PHP array. + * + * @param string $value A YAML string + * + * @return array A PHP array representing the YAML string + */ + static public function load($value) + { + $value = trim($value); + + if (0 == strlen($value)) + { + return ''; + } + + if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) + { + $mbEncoding = mb_internal_encoding(); + mb_internal_encoding('ASCII'); + } + + switch ($value[0]) + { + case '[': + $result = self::parseSequence($value); + break; + case '{': + $result = self::parseMapping($value); + break; + default: + $result = self::parseScalar($value); + } + + if (isset($mbEncoding)) + { + mb_internal_encoding($mbEncoding); + } + + return $result; + } + + /** + * Dumps a given PHP variable to a YAML string. + * + * @param mixed $value The PHP variable to convert + * + * @return string The YAML string representing the PHP array + */ + static public function dump($value) + { + if ('1.1' === sfYaml::getSpecVersion()) + { + $trueValues = array('true', 'on', '+', 'yes', 'y'); + $falseValues = array('false', 'off', '-', 'no', 'n'); + } + else + { + $trueValues = array('true'); + $falseValues = array('false'); + } + + switch (true) + { + case is_resource($value): + throw new InvalidArgumentException('Unable to dump PHP resources in a YAML file.'); + case is_object($value): + return '!!php/object:'.serialize($value); + case is_array($value): + return self::dumpArray($value); + case null === $value: + return 'null'; + case true === $value: + return 'true'; + case false === $value: + return 'false'; + case ctype_digit($value): + return is_string($value) ? "'$value'" : (int) $value; + case is_numeric($value): + return is_infinite($value) ? str_ireplace('INF', '.Inf', strval($value)) : (is_string($value) ? "'$value'" : $value); + case false !== strpos($value, "\n") || false !== strpos($value, "\r"): + return sprintf('"%s"', str_replace(array('"', "\n", "\r"), array('\\"', '\n', '\r'), $value)); + case preg_match('/[ \s \' " \: \{ \} \[ \] , & \* \# \?] | \A[ - ? | < > = ! % @ ` ]/x', $value): + return sprintf("'%s'", str_replace('\'', '\'\'', $value)); + case '' == $value: + return "''"; + case preg_match(self::getTimestampRegex(), $value): + return "'$value'"; + case in_array(strtolower($value), $trueValues): + return "'$value'"; + case in_array(strtolower($value), $falseValues): + return "'$value'"; + case in_array(strtolower($value), array('null', '~')): + return "'$value'"; + default: + return $value; + } + } + + /** + * Dumps a PHP array to a YAML string. + * + * @param array $value The PHP array to dump + * + * @return string The YAML string representing the PHP array + */ + static protected function dumpArray($value) + { + // array + $keys = array_keys($value); + if ( + (1 == count($keys) && '0' == $keys[0]) + || + (count($keys) > 1 && array_reduce($keys, create_function('$v,$w', 'return (integer) $v + $w;'), 0) == count($keys) * (count($keys) - 1) / 2)) + { + $output = array(); + foreach ($value as $val) + { + $output[] = self::dump($val); + } + + return sprintf('[%s]', implode(', ', $output)); + } + + // mapping + $output = array(); + foreach ($value as $key => $val) + { + $output[] = sprintf('%s: %s', self::dump($key), self::dump($val)); + } + + return sprintf('{ %s }', implode(', ', $output)); + } + + /** + * Parses a scalar to a YAML string. + * + * @param scalar $scalar + * @param string $delimiters + * @param array $stringDelimiter + * @param integer $i + * @param boolean $evaluate + * + * @return string A YAML string + */ + static public function parseScalar($scalar, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true) + { + if (in_array($scalar[$i], $stringDelimiters)) + { + // quoted scalar + $output = self::parseQuotedScalar($scalar, $i); + } + else + { + // "normal" string + if (!$delimiters) + { + $output = substr($scalar, $i); + $i += strlen($output); + + // remove comments + if (false !== $strpos = strpos($output, ' #')) + { + $output = rtrim(substr($output, 0, $strpos)); + } + } + else if (preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) + { + $output = $match[1]; + $i += strlen($output); + } + else + { + throw new InvalidArgumentException(sprintf('Malformed inline YAML string (%s).', $scalar)); + } + + $output = $evaluate ? self::evaluateScalar($output) : $output; + } + + return $output; + } + + /** + * Parses a quoted scalar to YAML. + * + * @param string $scalar + * @param integer $i + * + * @return string A YAML string + */ + static protected function parseQuotedScalar($scalar, &$i) + { + if (!preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) + { + throw new InvalidArgumentException(sprintf('Malformed inline YAML string (%s).', substr($scalar, $i))); + } + + $output = substr($match[0], 1, strlen($match[0]) - 2); + + if ('"' == $scalar[$i]) + { + // evaluate the string + $output = str_replace(array('\\"', '\\n', '\\r'), array('"', "\n", "\r"), $output); + } + else + { + // unescape ' + $output = str_replace('\'\'', '\'', $output); + } + + $i += strlen($match[0]); + + return $output; + } + + /** + * Parses a sequence to a YAML string. + * + * @param string $sequence + * @param integer $i + * + * @return string A YAML string + */ + static protected function parseSequence($sequence, &$i = 0) + { + $output = array(); + $len = strlen($sequence); + $i += 1; + + // [foo, bar, ...] + while ($i < $len) + { + switch ($sequence[$i]) + { + case '[': + // nested sequence + $output[] = self::parseSequence($sequence, $i); + break; + case '{': + // nested mapping + $output[] = self::parseMapping($sequence, $i); + break; + case ']': + return $output; + case ',': + case ' ': + break; + default: + $isQuoted = in_array($sequence[$i], array('"', "'")); + $value = self::parseScalar($sequence, array(',', ']'), array('"', "'"), $i); + + if (!$isQuoted && false !== strpos($value, ': ')) + { + // embedded mapping? + try + { + $value = self::parseMapping('{'.$value.'}'); + } + catch (InvalidArgumentException $e) + { + // no, it's not + } + } + + $output[] = $value; + + --$i; + } + + ++$i; + } + + throw new InvalidArgumentException(sprintf('Malformed inline YAML string %s', $sequence)); + } + + /** + * Parses a mapping to a YAML string. + * + * @param string $mapping + * @param integer $i + * + * @return string A YAML string + */ + static protected function parseMapping($mapping, &$i = 0) + { + $output = array(); + $len = strlen($mapping); + $i += 1; + + // {foo: bar, bar:foo, ...} + while ($i < $len) + { + switch ($mapping[$i]) + { + case ' ': + case ',': + ++$i; + continue 2; + case '}': + return $output; + } + + // key + $key = self::parseScalar($mapping, array(':', ' '), array('"', "'"), $i, false); + + // value + $done = false; + while ($i < $len) + { + switch ($mapping[$i]) + { + case '[': + // nested sequence + $output[$key] = self::parseSequence($mapping, $i); + $done = true; + break; + case '{': + // nested mapping + $output[$key] = self::parseMapping($mapping, $i); + $done = true; + break; + case ':': + case ' ': + break; + default: + $output[$key] = self::parseScalar($mapping, array(',', '}'), array('"', "'"), $i); + $done = true; + --$i; + } + + ++$i; + + if ($done) + { + continue 2; + } + } + } + + throw new InvalidArgumentException(sprintf('Malformed inline YAML string %s', $mapping)); + } + + /** + * Evaluates scalars and replaces magic values. + * + * @param string $scalar + * + * @return string A YAML string + */ + static protected function evaluateScalar($scalar) + { + $scalar = trim($scalar); + + if ('1.1' === sfYaml::getSpecVersion()) + { + $trueValues = array('true', 'on', '+', 'yes', 'y'); + $falseValues = array('false', 'off', '-', 'no', 'n'); + } + else + { + $trueValues = array('true'); + $falseValues = array('false'); + } + + switch (true) + { + case 'null' == strtolower($scalar): + case '' == $scalar: + case '~' == $scalar: + return null; + case 0 === strpos($scalar, '!str'): + return (string) substr($scalar, 5); + case 0 === strpos($scalar, '! '): + return intval(self::parseScalar(substr($scalar, 2))); + case 0 === strpos($scalar, '!!php/object:'): + return unserialize(substr($scalar, 13)); + case ctype_digit($scalar): + $raw = $scalar; + $cast = intval($scalar); + return '0' == $scalar[0] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw); + case in_array(strtolower($scalar), $trueValues): + return true; + case in_array(strtolower($scalar), $falseValues): + return false; + case is_numeric($scalar): + return '0x' == $scalar[0].$scalar[1] ? hexdec($scalar) : floatval($scalar); + case 0 == strcasecmp($scalar, '.inf'): + case 0 == strcasecmp($scalar, '.NaN'): + return -log(0); + case 0 == strcasecmp($scalar, '-.inf'): + return log(0); + case preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar): + return floatval(str_replace(',', '', $scalar)); + case preg_match(self::getTimestampRegex(), $scalar): + return strtotime($scalar); + default: + return (string) $scalar; + } + } + + static protected function getTimestampRegex() + { + return <<<EOF + ~^ + (?P<year>[0-9][0-9][0-9][0-9]) + -(?P<month>[0-9][0-9]?) + -(?P<day>[0-9][0-9]?) + (?:(?:[Tt]|[ \t]+) + (?P<hour>[0-9][0-9]?) + :(?P<minute>[0-9][0-9]) + :(?P<second>[0-9][0-9]) + (?:\.(?P<fraction>[0-9]*))? + (?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?) + (?::(?P<tz_minute>[0-9][0-9]))?))?)? + $~x +EOF; + } +} diff --git a/vendor/flexi/vendor/mustache.php/test/lib/yaml/lib/sfYamlParser.php b/vendor/flexi/vendor/mustache.php/test/lib/yaml/lib/sfYamlParser.php new file mode 100644 index 0000000..91da2dc --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/test/lib/yaml/lib/sfYamlParser.php @@ -0,0 +1,622 @@ +<?php + +/* + * This file is part of the symfony package. + * (c) Fabien Potencier <fabien.potencier@symfony-project.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require_once(dirname(__FILE__).'/sfYamlInline.php'); + +if (!defined('PREG_BAD_UTF8_OFFSET_ERROR')) +{ + define('PREG_BAD_UTF8_OFFSET_ERROR', 5); +} + +/** + * sfYamlParser parses YAML strings to convert them to PHP arrays. + * + * @package symfony + * @subpackage yaml + * @author Fabien Potencier <fabien.potencier@symfony-project.com> + * @version SVN: $Id: sfYamlParser.class.php 10832 2008-08-13 07:46:08Z fabien $ + */ +class sfYamlParser +{ + protected + $offset = 0, + $lines = array(), + $currentLineNb = -1, + $currentLine = '', + $refs = array(); + + /** + * Constructor + * + * @param integer $offset The offset of YAML document (used for line numbers in error messages) + */ + public function __construct($offset = 0) + { + $this->offset = $offset; + } + + /** + * Parses a YAML string to a PHP value. + * + * @param string $value A YAML string + * + * @return mixed A PHP value + * + * @throws InvalidArgumentException If the YAML is not valid + */ + public function parse($value) + { + $this->currentLineNb = -1; + $this->currentLine = ''; + $this->lines = explode("\n", $this->cleanup($value)); + + if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) + { + $mbEncoding = mb_internal_encoding(); + mb_internal_encoding('UTF-8'); + } + + $data = array(); + while ($this->moveToNextLine()) + { + if ($this->isCurrentLineEmpty()) + { + continue; + } + + // tab? + if (preg_match('#^\t+#', $this->currentLine)) + { + throw new InvalidArgumentException(sprintf('A YAML file cannot contain tabs as indentation at line %d (%s).', $this->getRealCurrentLineNb() + 1, $this->currentLine)); + } + + $isRef = $isInPlace = $isProcessed = false; + if (preg_match('#^\-((?P<leadspaces>\s+)(?P<value>.+?))?\s*$#u', $this->currentLine, $values)) + { + if (isset($values['value']) && preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) + { + $isRef = $matches['ref']; + $values['value'] = $matches['value']; + } + + // array + if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) + { + $c = $this->getRealCurrentLineNb() + 1; + $parser = new sfYamlParser($c); + $parser->refs =& $this->refs; + $data[] = $parser->parse($this->getNextEmbedBlock()); + } + else + { + if (isset($values['leadspaces']) + && ' ' == $values['leadspaces'] + && preg_match('#^(?P<key>'.sfYamlInline::REGEX_QUOTED_STRING.'|[^ \'"\{].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $values['value'], $matches)) + { + // this is a compact notation element, add to next block and parse + $c = $this->getRealCurrentLineNb(); + $parser = new sfYamlParser($c); + $parser->refs =& $this->refs; + + $block = $values['value']; + if (!$this->isNextLineIndented()) + { + $block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + 2); + } + + $data[] = $parser->parse($block); + } + else + { + $data[] = $this->parseValue($values['value']); + } + } + } + else if (preg_match('#^(?P<key>'.sfYamlInline::REGEX_QUOTED_STRING.'|[^ \'"].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $this->currentLine, $values)) + { + $key = sfYamlInline::parseScalar($values['key']); + + if ('<<' === $key) + { + if (isset($values['value']) && '*' === substr($values['value'], 0, 1)) + { + $isInPlace = substr($values['value'], 1); + if (!array_key_exists($isInPlace, $this->refs)) + { + throw new InvalidArgumentException(sprintf('Reference "%s" does not exist at line %s (%s).', $isInPlace, $this->getRealCurrentLineNb() + 1, $this->currentLine)); + } + } + else + { + if (isset($values['value']) && $values['value'] !== '') + { + $value = $values['value']; + } + else + { + $value = $this->getNextEmbedBlock(); + } + $c = $this->getRealCurrentLineNb() + 1; + $parser = new sfYamlParser($c); + $parser->refs =& $this->refs; + $parsed = $parser->parse($value); + + $merged = array(); + if (!is_array($parsed)) + { + throw new InvalidArgumentException(sprintf("YAML merge keys used with a scalar value instead of an array at line %s (%s)", $this->getRealCurrentLineNb() + 1, $this->currentLine)); + } + else if (isset($parsed[0])) + { + // Numeric array, merge individual elements + foreach (array_reverse($parsed) as $parsedItem) + { + if (!is_array($parsedItem)) + { + throw new InvalidArgumentException(sprintf("Merge items must be arrays at line %s (%s).", $this->getRealCurrentLineNb() + 1, $parsedItem)); + } + $merged = array_merge($parsedItem, $merged); + } + } + else + { + // Associative array, merge + $merged = array_merge($merge, $parsed); + } + + $isProcessed = $merged; + } + } + else if (isset($values['value']) && preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) + { + $isRef = $matches['ref']; + $values['value'] = $matches['value']; + } + + if ($isProcessed) + { + // Merge keys + $data = $isProcessed; + } + // hash + else if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) + { + // if next line is less indented or equal, then it means that the current value is null + if ($this->isNextLineIndented()) + { + $data[$key] = null; + } + else + { + $c = $this->getRealCurrentLineNb() + 1; + $parser = new sfYamlParser($c); + $parser->refs =& $this->refs; + $data[$key] = $parser->parse($this->getNextEmbedBlock()); + } + } + else + { + if ($isInPlace) + { + $data = $this->refs[$isInPlace]; + } + else + { + $data[$key] = $this->parseValue($values['value']); + } + } + } + else + { + // 1-liner followed by newline + if (2 == count($this->lines) && empty($this->lines[1])) + { + $value = sfYamlInline::load($this->lines[0]); + if (is_array($value)) + { + $first = reset($value); + if ('*' === substr($first, 0, 1)) + { + $data = array(); + foreach ($value as $alias) + { + $data[] = $this->refs[substr($alias, 1)]; + } + $value = $data; + } + } + + if (isset($mbEncoding)) + { + mb_internal_encoding($mbEncoding); + } + + return $value; + } + + switch (preg_last_error()) + { + case PREG_INTERNAL_ERROR: + $error = 'Internal PCRE error on line'; + break; + case PREG_BACKTRACK_LIMIT_ERROR: + $error = 'pcre.backtrack_limit reached on line'; + break; + case PREG_RECURSION_LIMIT_ERROR: + $error = 'pcre.recursion_limit reached on line'; + break; + case PREG_BAD_UTF8_ERROR: + $error = 'Malformed UTF-8 data on line'; + break; + case PREG_BAD_UTF8_OFFSET_ERROR: + $error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point on line'; + break; + default: + $error = 'Unable to parse line'; + } + + throw new InvalidArgumentException(sprintf('%s %d (%s).', $error, $this->getRealCurrentLineNb() + 1, $this->currentLine)); + } + + if ($isRef) + { + $this->refs[$isRef] = end($data); + } + } + + if (isset($mbEncoding)) + { + mb_internal_encoding($mbEncoding); + } + + return empty($data) ? null : $data; + } + + /** + * Returns the current line number (takes the offset into account). + * + * @return integer The current line number + */ + protected function getRealCurrentLineNb() + { + return $this->currentLineNb + $this->offset; + } + + /** + * Returns the current line indentation. + * + * @return integer The current line indentation + */ + protected function getCurrentLineIndentation() + { + return strlen($this->currentLine) - strlen(ltrim($this->currentLine, ' ')); + } + + /** + * Returns the next embed block of YAML. + * + * @param integer $indentation The indent level at which the block is to be read, or null for default + * + * @return string A YAML string + */ + protected function getNextEmbedBlock($indentation = null) + { + $this->moveToNextLine(); + + if (null === $indentation) + { + $newIndent = $this->getCurrentLineIndentation(); + + if (!$this->isCurrentLineEmpty() && 0 == $newIndent) + { + throw new InvalidArgumentException(sprintf('Indentation problem at line %d (%s)', $this->getRealCurrentLineNb() + 1, $this->currentLine)); + } + } + else + { + $newIndent = $indentation; + } + + $data = array(substr($this->currentLine, $newIndent)); + + while ($this->moveToNextLine()) + { + if ($this->isCurrentLineEmpty()) + { + if ($this->isCurrentLineBlank()) + { + $data[] = substr($this->currentLine, $newIndent); + } + + continue; + } + + $indent = $this->getCurrentLineIndentation(); + + if (preg_match('#^(?P<text> *)$#', $this->currentLine, $match)) + { + // empty line + $data[] = $match['text']; + } + else if ($indent >= $newIndent) + { + $data[] = substr($this->currentLine, $newIndent); + } + else if (0 == $indent) + { + $this->moveToPreviousLine(); + + break; + } + else + { + throw new InvalidArgumentException(sprintf('Indentation problem at line %d (%s)', $this->getRealCurrentLineNb() + 1, $this->currentLine)); + } + } + + return implode("\n", $data); + } + + /** + * Moves the parser to the next line. + */ + protected function moveToNextLine() + { + if ($this->currentLineNb >= count($this->lines) - 1) + { + return false; + } + + $this->currentLine = $this->lines[++$this->currentLineNb]; + + return true; + } + + /** + * Moves the parser to the previous line. + */ + protected function moveToPreviousLine() + { + $this->currentLine = $this->lines[--$this->currentLineNb]; + } + + /** + * Parses a YAML value. + * + * @param string $value A YAML value + * + * @return mixed A PHP value + */ + protected function parseValue($value) + { + if ('*' === substr($value, 0, 1)) + { + if (false !== $pos = strpos($value, '#')) + { + $value = substr($value, 1, $pos - 2); + } + else + { + $value = substr($value, 1); + } + + if (!array_key_exists($value, $this->refs)) + { + throw new InvalidArgumentException(sprintf('Reference "%s" does not exist (%s).', $value, $this->currentLine)); + } + return $this->refs[$value]; + } + + if (preg_match('/^(?P<separator>\||>)(?P<modifiers>\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P<comments> +#.*)?$/', $value, $matches)) + { + $modifiers = isset($matches['modifiers']) ? $matches['modifiers'] : ''; + + return $this->parseFoldedScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), intval(abs($modifiers))); + } + else + { + return sfYamlInline::load($value); + } + } + + /** + * Parses a folded scalar. + * + * @param string $separator The separator that was used to begin this folded scalar (| or >) + * @param string $indicator The indicator that was used to begin this folded scalar (+ or -) + * @param integer $indentation The indentation that was used to begin this folded scalar + * + * @return string The text value + */ + protected function parseFoldedScalar($separator, $indicator = '', $indentation = 0) + { + $separator = '|' == $separator ? "\n" : ' '; + $text = ''; + + $notEOF = $this->moveToNextLine(); + + while ($notEOF && $this->isCurrentLineBlank()) + { + $text .= "\n"; + + $notEOF = $this->moveToNextLine(); + } + + if (!$notEOF) + { + return ''; + } + + if (!preg_match('#^(?P<indent>'.($indentation ? str_repeat(' ', $indentation) : ' +').')(?P<text>.*)$#u', $this->currentLine, $matches)) + { + $this->moveToPreviousLine(); + + return ''; + } + + $textIndent = $matches['indent']; + $previousIndent = 0; + + $text .= $matches['text'].$separator; + while ($this->currentLineNb + 1 < count($this->lines)) + { + $this->moveToNextLine(); + + if (preg_match('#^(?P<indent> {'.strlen($textIndent).',})(?P<text>.+)$#u', $this->currentLine, $matches)) + { + if (' ' == $separator && $previousIndent != $matches['indent']) + { + $text = substr($text, 0, -1)."\n"; + } + $previousIndent = $matches['indent']; + + $text .= str_repeat(' ', $diff = strlen($matches['indent']) - strlen($textIndent)).$matches['text'].($diff ? "\n" : $separator); + } + else if (preg_match('#^(?P<text> *)$#', $this->currentLine, $matches)) + { + $text .= preg_replace('#^ {1,'.strlen($textIndent).'}#', '', $matches['text'])."\n"; + } + else + { + $this->moveToPreviousLine(); + + break; + } + } + + if (' ' == $separator) + { + // replace last separator by a newline + $text = preg_replace('/ (\n*)$/', "\n$1", $text); + } + + switch ($indicator) + { + case '': + $text = preg_replace('#\n+$#s', "\n", $text); + break; + case '+': + break; + case '-': + $text = preg_replace('#\n+$#s', '', $text); + break; + } + + return $text; + } + + /** + * Returns true if the next line is indented. + * + * @return Boolean Returns true if the next line is indented, false otherwise + */ + protected function isNextLineIndented() + { + $currentIndentation = $this->getCurrentLineIndentation(); + $notEOF = $this->moveToNextLine(); + + while ($notEOF && $this->isCurrentLineEmpty()) + { + $notEOF = $this->moveToNextLine(); + } + + if (false === $notEOF) + { + return false; + } + + $ret = false; + if ($this->getCurrentLineIndentation() <= $currentIndentation) + { + $ret = true; + } + + $this->moveToPreviousLine(); + + return $ret; + } + + /** + * Returns true if the current line is blank or if it is a comment line. + * + * @return Boolean Returns true if the current line is empty or if it is a comment line, false otherwise + */ + protected function isCurrentLineEmpty() + { + return $this->isCurrentLineBlank() || $this->isCurrentLineComment(); + } + + /** + * Returns true if the current line is blank. + * + * @return Boolean Returns true if the current line is blank, false otherwise + */ + protected function isCurrentLineBlank() + { + return '' == trim($this->currentLine, ' '); + } + + /** + * Returns true if the current line is a comment line. + * + * @return Boolean Returns true if the current line is a comment line, false otherwise + */ + protected function isCurrentLineComment() + { + //checking explicitly the first char of the trim is faster than loops or strpos + $ltrimmedLine = ltrim($this->currentLine, ' '); + return $ltrimmedLine[0] === '#'; + } + + /** + * Cleanups a YAML string to be parsed. + * + * @param string $value The input YAML string + * + * @return string A cleaned up YAML string + */ + protected function cleanup($value) + { + $value = str_replace(array("\r\n", "\r"), "\n", $value); + + if (!preg_match("#\n$#", $value)) + { + $value .= "\n"; + } + + // strip YAML header + $count = 0; + $value = preg_replace('#^\%YAML[: ][\d\.]+.*\n#su', '', $value, -1, $count); + $this->offset += $count; + + // remove leading comments + $trimmedValue = preg_replace('#^(\#.*?\n)+#s', '', $value, -1, $count); + if ($count == 1) + { + // items have been removed, update the offset + $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); + $value = $trimmedValue; + } + + // remove start of the document marker (---) + $trimmedValue = preg_replace('#^\-\-\-.*?\n#s', '', $value, -1, $count); + if ($count == 1) + { + // items have been removed, update the offset + $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); + $value = $trimmedValue; + + // remove end of the document marker (...) + $value = preg_replace('#\.\.\.\s*$#s', '', $value); + } + + return $value; + } +} diff --git a/vendor/flexi/vendor/mustache.php/test/lib/yaml/package.xml b/vendor/flexi/vendor/mustache.php/test/lib/yaml/package.xml new file mode 100644 index 0000000..1869ae9 --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/test/lib/yaml/package.xml @@ -0,0 +1,102 @@ +<?xml version="1.0" encoding="UTF-8"?> +<package packagerversion="1.4.1" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd"> + <name>YAML</name> + <channel>pear.symfony-project.com</channel> + <summary>The Symfony YAML Component.</summary> + <description>The Symfony YAML Component.</description> + <lead> + <name>Fabien Potencier</name> + <user>fabpot</user> + <email>fabien.potencier@symfony-project.org</email> + <active>yes</active> + </lead> + <date>2009-12-01</date> + <version> + <release>1.0.2</release> + <api>1.0.0</api> + </version> + <stability> + <release>stable</release> + <api>stable</api> + </stability> + <license uri="http://www.symfony-project.com/license">MIT license</license> + <notes>-</notes> + <contents> + <dir name="/"> + <file name="README.markdown" role="doc" /> + <file name="LICENSE" role="doc" /> + + <dir name="lib"> + <file install-as="SymfonyComponents/YAML/sfYaml.php" name="sfYaml.php" role="php" /> + <file install-as="SymfonyComponents/YAML/sfYamlDumper.php" name="sfYamlDumper.php" role="php" /> + <file install-as="SymfonyComponents/YAML/sfYamlInline.php" name="sfYamlInline.php" role="php" /> + <file install-as="SymfonyComponents/YAML/sfYamlParser.php" name="sfYamlParser.php" role="php" /> + </dir> + </dir> + </contents> + + <dependencies> + <required> + <php> + <min>5.2.4</min> + </php> + <pearinstaller> + <min>1.4.1</min> + </pearinstaller> + </required> + </dependencies> + + <phprelease> + </phprelease> + + <changelog> + <release> + <version> + <release>1.0.2</release> + <api>1.0.0</api> + </version> + <stability> + <release>stable</release> + <api>stable</api> + </stability> + <license uri="http://www.symfony-project.com/license">MIT license</license> + <date>2009-12-01</date> + <license>MIT</license> + <notes> + * fabien: fixed \ usage in quoted string + </notes> + </release> + <release> + <version> + <release>1.0.1</release> + <api>1.0.0</api> + </version> + <stability> + <release>stable</release> + <api>stable</api> + </stability> + <license uri="http://www.symfony-project.com/license">MIT license</license> + <date>2009-12-01</date> + <license>MIT</license> + <notes> + * fabien: fixed a possible loop in parsing a non-valid quoted string + </notes> + </release> + <release> + <version> + <release>1.0.0</release> + <api>1.0.0</api> + </version> + <stability> + <release>stable</release> + <api>stable</api> + </stability> + <license uri="http://www.symfony-project.com/license">MIT license</license> + <date>2009-11-30</date> + <license>MIT</license> + <notes> + * fabien: first stable release as a Symfony Component + </notes> + </release> + </changelog> +</package> diff --git a/vendor/flexi/vendor/mustache.php/test/phpunit.xml b/vendor/flexi/vendor/mustache.php/test/phpunit.xml new file mode 100644 index 0000000..891fa8e --- /dev/null +++ b/vendor/flexi/vendor/mustache.php/test/phpunit.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<phpunit colors="true"> + <testsuite name="Mustache"> + <directory>./</directory> + </testsuite> + <blacklist> + <directory suffix=".php">../examples</directory> + <directory suffix=".php">./</directory> + </blacklist> +</phpunit>
\ No newline at end of file diff --git a/vendor/ilias/class.ilObjectXMLParser.php b/vendor/ilias/class.ilObjectXMLParser.php new file mode 100755 index 0000000..f5e6368 --- /dev/null +++ b/vendor/ilias/class.ilObjectXMLParser.php @@ -0,0 +1,322 @@ +<?php +/* + +-----------------------------------------------------------------------------+ + | ILIAS open source | + +-----------------------------------------------------------------------------+ + | Copyright (c) 1998-2001 ILIAS open source, University of Cologne | + | | + | This program is free software; you can redistribute it and/or | + | modify it under the terms of the GNU General Public License | + | as published by the Free Software Foundation; either version 2 | + | of the License, or (at your option) any later version. | + | | + | This program is distributed in the hope that it will be useful, | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | + | GNU General Public License for more details. | + | | + | You should have received a copy of the GNU General Public License | + | along with this program; if not, write to the Free Software | + | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | + +-----------------------------------------------------------------------------+ +*/ + + +/** +* Object XML Parser +* +* @author Stefan Meyer <meyer@leifos.com> +* @version $Id$ +* +* @extends ilSaxParser +*/ + +//include_once './Services/Xml/classes/class.ilSaxParser.php'; +//include_once('./webservice/soap/classes/class.ilObjectXMLException.php'); + +class ilObjectXMLParser extends ilSaxParser +{ + var $object_data = array(); + + private $ref_id; + private $parent_id; + + /** + * Constructor + * + * @param object $a_content_object must be of type ilObjContentObject + * ilObjTest or ilObjQuestionPool + * @param string $a_xml_file xml data + * @param string $a_subdir subdirectory in import directory + * @access public + */ + function __construct($a_xml_data = '', $throwException = false) + { + parent::__construct('', $throwException); + $this->setXMLContent($a_xml_data); + } + + function getObjectData() + { + return $this->object_data ? $this->object_data : array(); + } + + /** + * parse xml file + * + * @access private + * @throws ilSaxParserException + * @throws ilObjectXMLException + */ + function parse($a_xml_parser,$a_fp = null) + { + parent::parse($a_xml_parser,$a_fp); + } + + + /** + * set event handlers + * + * @param resource reference to the xml parser + * @access private + */ + function setHandlers($a_xml_parser) + { + xml_set_object($a_xml_parser,$this); + xml_set_element_handler($a_xml_parser,'handlerBeginTag','handlerEndTag'); + xml_set_character_data_handler($a_xml_parser,'handlerCharacterData'); + } + + + + + /** + * handler for begin of element + * + * @param resource $a_xml_parser xml parser + * @param string $a_name element name + * @param array $a_attribs element attributes array + */ + function handlerBeginTag($a_xml_parser,$a_name,$a_attribs) + { + switch($a_name) + { + case 'Objects': + $this->curr_obj = -1; + break; + + case 'Object': + ++$this->curr_obj; + + $this->addProperty__('type',$a_attribs['type']); + $this->addProperty__('obj_id',is_numeric($a_attribs['obj_id'])?(int) $a_attribs["obj_id"] : ilUtil::__extractId($a_attribs["obj_id"], IL_INST_ID)); + break; + + case 'Title': + break; + + case 'Description': + break; + + case 'Owner': + break; + + case 'CreateDate': + break; + + case 'LastUpdate': + break; + + case 'ImportId': + break; + + case 'References': + $this->time_target = array(); + $this->ref_id = $a_attribs["ref_id"]; + $this->parent_id = $a_attribs['parent_id']; + $this->operations = array(); + break; + + case 'TimeTarget': + $this->time_target['timing_type'] = $a_attribs['type']; + break; + + case 'Timing': + $this->time_target['timing_visibility'] = $a_attribs['visibility']; + if(isset($a_attribs['starting_time'])) + { + $this->time_target['starting_time'] = $a_attribs['starting_time']; + } + if(isset($a_attribs['ending_time'])) + { + $this->time_target['ending_time'] = $a_attribs['ending_time']; + } + + if($a_attribs['ending_time'] < $a_attribs['starting_time']) + throw new ilObjectXMLException('Starting time must be earlier than ending time.'); + break; + + case 'Suggestion': + $this->time_target['changeable'] = $a_attribs['changeable']; + + + if(isset($a_attribs['starting_time'])) + { + $this->time_target['suggestion_start'] = $a_attribs['starting_time']; + } + if(isset($a_attribs['ending_time'])) + { + $this->time_target['suggestion_end'] = $a_attribs['ending_time']; + } + if(isset($a_attribs['earliest_start'])) + { + $this->time_target['earliest_start'] = $a_attribs['earliest_start']; + } + if(isset($a_attribs['latest_end'])) + { + $this->time_target['latest_end'] = $a_attribs['latest_end']; + } + + if($a_attribs['latest_end'] < $a_attribs['earliest_start']) + throw new ilObjectXMLException('Earliest start time must be earlier than latest ending time.'); + if($a_attribs['ending_time'] < $a_attribs['starting_time']) + throw new ilObjectXMLException('Starting time must be earlier than ending time.'); + break; + + //////////////////////////////////////////// + ///////////// MODIFIED ///////////////////// + //////////////////////////////////////////// + case 'Operation': + $this->operations[] = $a_attribs['operations']; + break; + //////////////////////////////////////////// + } + } + + /** + * handler for end of element + * + * @param resource $a_xml_parser xml parser + * @param string $a_name element name + */ + function handlerEndTag($a_xml_parser,$a_name) + { + switch($a_name) + { + case 'Objects': + break; + + case 'Object': + break; + + case 'Title': + $this->addProperty__('title',trim($this->cdata)); + break; + + case 'Description': + $this->addProperty__('description',trim($this->cdata)); + break; + + case 'Owner': + $this->addProperty__('owner',trim($this->cdata)); + break; + + case 'CreateDate': + $this->addProperty__('create_date',trim($this->cdata)); + break; + + case 'LastUpdate': + $this->addProperty__('last_update',trim($this->cdata)); + break; + + case 'ImportId': + $this->addProperty__('import_id',trim($this->cdata)); + break; + + case 'References': + $this->addReference__($this->ref_id,$this->parent_id,$this->time_target); + break; + //////////////////////////////////////////// + ///////////// MODIFIED ///////////////////// + //////////////////////////////////////////// + case 'Operation': + $this->addOperation__(trim($this->cdata)); + break; + //////////////////////////////////////////// + } + + $this->cdata = ''; + + return; + } + + /** + * handler for character data + * + * @param resource $a_xml_parser xml parser + * @param string $a_data character data + */ + function handlerCharacterData($a_xml_parser,$a_data) + { + if($a_data != "\n") + { + // Replace multiple tabs with one space + $a_data = preg_replace("/\t+/"," ",$a_data); + + $this->cdata .= $a_data; + } + + + } + + // PRIVATE + function addProperty__($a_name,$a_value) + { + $this->object_data[$this->curr_obj][$a_name] = $a_value; + } + + /** + * @throws ilObjectXMLException + */ + function addReference__($a_ref_id,$a_parent_id,$a_time_target) + { + $reference['ref_id'] = $a_ref_id; + $reference['parent_id'] = $a_parent_id; + $reference['time_target'] = $a_time_target; + + if(isset($reference['time_target']['changeable']) and $reference['time_target']['changeable']) + { + if(!isset($reference['time_target']['earliest_start']) or !isset($reference['time_target']['latest_end'])) + { + throw new ilObjectXMLException('Missing attributes: "earliest_start" and "latest_end" required for attribute "changeable"'); + } + if(!isset($reference['time_target']['suggestion_start']) or !isset($reference['time_target']['suggestion_end'])) + { + throw new ilObjectXMLException('Missing attributes: "starting_time" and "ending_time" required for attribute "changeable"'); + } + if(($reference['time_target']['earliest_start'] < $reference['time_target']['suggestion_start']) or + ($reference['time_target']['earliest_start'] > $reference['time_target']['suggestion_end'])) + { + throw new ilObjectXMLException('Invalid attributes: "earliest_start" must be within "starting_time" and "ending_time"'); + } + if(($reference['time_target']['latest_end'] < $reference['time_target']['suggestion_start']) or + ($reference['time_target']['latest_end'] > $reference['time_target']['suggestion_end'])) + { + throw new ilObjectXMLException('Invalid attributes: "latest_end" must be within "starting_time" and "ending_time"'); + } + } + + $this->object_data[$this->curr_obj]['references'][] = $reference; + } + + //////////////////////////////////////////// + ///////////// MODIFIED ///////////////////// + //////////////////////////////////////////// + function addOperation__($a_value) + { + if($a_value) + $this->object_data[$this->curr_obj]['references'][$this->reference_count]["operations"][] = $a_value; + } + //////////////////////////////////////////// +} +?> diff --git a/vendor/ilias/class.ilSaxParser.php b/vendor/ilias/class.ilSaxParser.php new file mode 100755 index 0000000..a963f1b --- /dev/null +++ b/vendor/ilias/class.ilSaxParser.php @@ -0,0 +1,257 @@ +<?php +/* Copyright (c) 1998-2009 ILIAS open source, Extended GPL, see docs/LICENSE */ + + +/** +* Base class for sax-based expat parsing +* extended classes need to overwrite the method setHandlers and implement their own handler methods +* +* +* @author Stefan Meyer <smeyer@databay> +* @version $Id$ +* +* @extends PEAR +*/ + + +class ilSaxParser +{ + /** + * XML-Content type 'file' or 'string' + * If you choose file set the filename in constructor + * If you choose 'String' call the constructor with no argument and use setXMLContent() + * @var string + * @access private + */ + var $input_type = null; + + /** + * XML-Content in case of content type 'string' + + * @var string + * @access private + */ + var $xml_content = ''; + + /** + * ilias object + * @var object ilias + * @access private + */ + var $ilias; + + /** + * language object + * @var object language + * @access private + */ + var $lng; + + /** + * xml filename + * @var filename + * @access private + */ + var $xml_file; + + /** + * error handler which handles error messages (used if parsers are called from soap) + * + * @var boolean + */ + var $throwException = false; + /** + * Constructor + * setup ILIAS global object + * @access public + */ + function __construct($a_xml_file = '', $throwException = false) + { + global $ilias, $lng; + + if($a_xml_file) + { + $this->xml_file = $a_xml_file; + $this->input_type = 'file'; + } + + $this->throwException = $throwException; + $this->ilias = &$ilias; + $this->lng = &$lng; + } + + function setXMLContent($a_xml_content) + { + $this->xml_content = $a_xml_content; + $this->input_type = 'string'; + } + + function getXMLContent() + { + return $this->xml_content; + } + + function getInputType() + { + return $this->input_type; + } + + /** + * stores xml data in array + * + * @access private + * @throws ilSaxParserException or ILIAS Error + */ + function startParsing() + { + $xml_parser = $this->createParser(); + $this->setOptions($xml_parser); + $this->setHandlers($xml_parser); + + switch($this->getInputType()) + { + case 'file': + $fp = $this->openXMLFile(); + $this->parse($xml_parser,$fp); + break; + + case 'string': + $this->parse($xml_parser); + break; + + default: + $this->handleError("No input type given. Set filename in constructor or choose setXMLContent()", + $this->ilias->error_obj->FATAL); + break; + } + $this->freeParser($xml_parser); + } + /** + * create parser + * + * @access private + * @throws ilSaxParserException or ILIAS Error + */ + function createParser() + { + $xml_parser = xml_parser_create("UTF-8"); + + if($xml_parser == false) + { + $this->handleError("Cannot create an XML parser handle",$this->ilias->error_obj->FATAL); + } + return $xml_parser; + } + /** + * set parser options + * + * @access private + */ + function setOptions($a_xml_parser) + { + xml_parser_set_option($a_xml_parser,XML_OPTION_CASE_FOLDING,false); + } + /** + * set event handler + * should be overwritten by inherited class + * @access private + */ + function setHandlers($a_xml_parser) + { + echo 'ilSaxParser::setHandlers() must be overwritten'; + } + /** + * open xml file + * + * @access private + * @throws ilSaxParserException or ILIAS Error + */ + function openXMLFile() + { + if(!($fp = fopen($this->xml_file,'r'))) + { + $this->handleError("Cannot open xml file \"".$this->xml_file."\"",$this->ilias->error_obj->FATAL); + } + return $fp; + } + /** + * parse xml file + * + * @access private + * @throws ilSaxParserException or ILIAS Error + */ + function parse($a_xml_parser,$a_fp = null) + { + switch($this->getInputType()) + { + case 'file': + + while($data = fread($a_fp,4096)) + { + $parseOk = xml_parse($a_xml_parser,$data,feof($a_fp)); + } + break; + + case 'string': + $parseOk = xml_parse($a_xml_parser,$this->getXMLContent()); + break; + } + if(!$parseOk + && (xml_get_error_code($a_xml_parser) != XML_ERROR_NONE)) + { + $errorCode = xml_get_error_code($a_xml_parser); + $line = xml_get_current_line_number($a_xml_parser); + $col = xml_get_current_column_number($a_xml_parser); + $this->handleError("XML Parse Error: ".xml_error_string($errorCode)." at line ".$line.", col ". $col . " (Code: ".$errorCode.")" ,$this->ilias->error_obj->FATAL); + } + return true; + + } + + /** + * use given error handler to handle error message or internal ilias error message handle + * + * @param string $message + * @param string $code + * @throws ilSaxParserException or ILIAS Error + */ + protected function handleError($message, $code) { + if ($this->throwException) { + require_once ('./Services/Xml/exceptions/class.ilSaxParserException.php'); + throw new ilSaxParserException ($message, $code); + } else { + if (is_object($this->ilias)) + { + $this->ilias->raiseError($message, $code); + } + else + { + die($message); + } + } + return false; + } + /** + * free xml parser handle + * + * @access private + */ + function freeParser($a_xml_parser) + { + if(!xml_parser_free($a_xml_parser)) + { + $this->ilias->raiseError("Error freeing xml parser handle ",$this->ilias->error_obj->FATAL); + } + } + + /** + * set error handling + * + * @param $error_handler + */ + public function setThrowException ($throwException) + { + $this->throwException = $throwException; + } +} +?> diff --git a/vendor/nusoap/class.delegating_soap_server.php b/vendor/nusoap/class.delegating_soap_server.php new file mode 100644 index 0000000..840e257 --- /dev/null +++ b/vendor/nusoap/class.delegating_soap_server.php @@ -0,0 +1,138 @@ +<?php + +/* + * class.delegating_soap_server.php - A SOAP server that delegates invocation. + * + * Copyright (C) 2006 - Marcus Lunzenauer <mlunzena@uos.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + + +/** + * DelegatingSoapServer extends the basic soap_server and delegates the + * actual invocation of php functions and methods to a another object. + * + * + * @package nusoap + * + * @author Marcus Lunzenauer <mlunzena@uos.de> + * @author Dietrich Ayala <dietrich@ganx4.com> + * @copyright (c) Authors + * @version $Id: class.delegating_soap_server.php 3823 2006-08-29 10:52:08Z mlunzena $ + */ + +class DelegatingSoapServer extends soap_server { + + /** + * The delegate which invokes soap functions. + * + * @access private + * @var mixed + */ + var $delegate; + + + /** + * Constructor. + * The optional parameter is a path to a WSDL file that you'd like to bind the + * server instance to. + * + * @param mixed file path or URL (string), or wsdl instance (object) + * + * @return void + */ + function __construct(&$delegate, $wsdl = FALSE) { + parent::__construct($wsdl); + $this->delegate =& $delegate; + } + + + /** + * Invokes a PHP function for the requested SOAP method. + * + * The following fields are set by this function (when successful): + * - methodreturn + * + * Note that the PHP function that is called may also set the following + * fields to affect the response sent to the client: + * - responseHeaders + * - outgoing_headers + * + * @return void + */ + function invoke_method() { + + $this->debug( + sprintf('in invoke_method, methodname=%s methodURI=%s SOAPAction=%s', + $this->methodname, $this->methodURI, $this->SOAPAction)); + + if ($this->wsdl) { + + if ($this->opData = $this->wsdl->getOperationData($this->methodname)) { + $this->debug('in invoke_method, found WSDL operation=' . + $this->methodname); + $this->appendDebug('opData=' . $this->varDump($this->opData)); + } + + else if ($this->opData = $this->wsdl->getOperationDataForSoapAction($this->SOAPAction)) { + + # Note: hopefully this case will only be used for doc/lit, + # since rpc services should have wrapper element + $this->debug('in invoke_method, found WSDL soapAction=' . + $this->SOAPAction . ' for operation=' . + $this->opData['name']); + $this->appendDebug('opData=' . $this->varDump($this->opData)); + $this->methodname = $this->opData['name']; + } + + else { + $this->debug('in invoke_method, no WSDL for operation=' . + $this->methodname); + $this->fault('Client', "Operation '" . $this->methodname . + "' is not defined in the WSDL for this service"); + return; + } + } + + else { + $this->debug('in invoke_method, no WSDL to validate method'); + } + + # does method exist? + if (!$this->delegate->responds_to($this->methodname)) { + $this->debug("in invoke_method, function '$this->methodname' not found!"); + $this->result = 'fault: method not found'; + $this->fault('Client', + "method '$this->methodname' not defined in service"); + return; + } + + # evaluate message, getting back parameters + # verify that request parameters match the method's signature + if (!$this->verify_method($this->methodname, $this->methodparams)){ + # debug + $this->debug('ERROR: request not verified against method signature'); + $this->result = 'fault: request failed validation against method '. + 'signature'; + # return fault + $this->fault('Client', + "Operation '$this->methodname' not defined in service."); + return; + } + + # if there are parameters to pass + $this->debug('in invoke_method, params:'); + $this->appendDebug($this->varDump($this->methodparams)); + $this->debug("in invoke_method, calling '$this->methodname'"); + + $this->methodreturn = $this->delegate->invoke($this->methodname, + $this->methodparams); + + $this->debug("in invoke_method, received ".$this->varDump($this->methodreturn)." of type " . + gettype($this->methodreturn)); + } +} diff --git a/vendor/nusoap/class.soap_server_delegate.php b/vendor/nusoap/class.soap_server_delegate.php new file mode 100644 index 0000000..25cf02f --- /dev/null +++ b/vendor/nusoap/class.soap_server_delegate.php @@ -0,0 +1,70 @@ +<?php + +/* + * class.soap_server_delegate.php - Delegate for the DelegatingSoapServer. + * + * Copyright (C) 2006 - Marcus Lunzenauer <mlunzena@uos.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + + +/** + * SoapServerDelegate is the abstract super class for all delegates of the + * DelegatingSoapServer. A sub class has to implement the functions: + * - boolean responds_to(string); + * - mixed invoke(string, array); + * + * @abstract + * + * @package nusoap + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id: class.soap_server_delegate.php 3823 2006-08-29 10:52:08Z mlunzena $ + */ + +class SoapServerDelegate { + + + /** + * This method is called to verify the existence of a soap-mapped function. + * + * @param string the soap-mapped function's name + * + * @return boolean returns TRUE, if the delegate can invoke the given + * function, FALSE otherwise + */ + function responds_to($function) { + return FALSE; + } + + + /** + * This method is responsible to call the given function with the given + * arguments. + * + * @param string the name of the function to invoke + * @param array an array of arguments + * + * @return mixed the return value of the invoked function + */ + function invoke($function, $argument_array) { + return new soap_fault('Server', '', 'SoapServerDelegate is abstract.'); + } + + + /** + * <MethodDescription> + * + * @param mixed <description> + * + * @return void + */ + function register_operations(&$server) { + return; + } +} diff --git a/vendor/nusoap/nusoap.php b/vendor/nusoap/nusoap.php new file mode 100644 index 0000000..e658ff0 --- /dev/null +++ b/vendor/nusoap/nusoap.php @@ -0,0 +1,8166 @@ +<?php
+
+/*
+$Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
+
+NuSOAP - Web Services Toolkit for PHP
+
+Copyright (c) 2002 NuSphere Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+The NuSOAP project home is:
+http://sourceforge.net/projects/nusoap/
+
+The primary support for NuSOAP is the Help forum on the project home page.
+
+If you have any questions or comments, please email:
+
+Dietrich Ayala
+dietrich@ganx4.com
+http://dietrich.ganx4.com/nusoap
+
+NuSphere Corporation
+http://www.nusphere.com
+
+*/
+
+/*
+ * Some of the standards implmented in whole or part by NuSOAP:
+ *
+ * SOAP 1.1 (http://www.w3.org/TR/2000/NOTE-SOAP-20000508/)
+ * WSDL 1.1 (http://www.w3.org/TR/2001/NOTE-wsdl-20010315)
+ * SOAP Messages With Attachments (http://www.w3.org/TR/SOAP-attachments)
+ * XML 1.0 (http://www.w3.org/TR/2006/REC-xml-20060816/)
+ * Namespaces in XML 1.0 (http://www.w3.org/TR/2006/REC-xml-names-20060816/)
+ * XML Schema 1.0 (http://www.w3.org/TR/xmlschema-0/)
+ * RFC 2045 Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies
+ * RFC 2068 Hypertext Transfer Protocol -- HTTP/1.1
+ * RFC 2617 HTTP Authentication: Basic and Digest Access Authentication
+ */
+
+/* load classes
+
+// necessary classes
+require_once('class.soapclient.php');
+require_once('class.soap_val.php');
+require_once('class.soap_parser.php');
+require_once('class.soap_fault.php');
+
+// transport classes
+require_once('class.soap_transport_http.php');
+
+// optional add-on classes
+require_once('class.xmlschema.php');
+require_once('class.wsdl.php');
+
+// server class
+require_once('class.soap_server.php');*/
+
+// class variable emulation
+// cf. http://www.webkreator.com/php/techniques/php-static-class-variables.html
+$GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'] = 9;
+
+/**
+*
+* nusoap_base
+*
+* @author Dietrich Ayala <dietrich@ganx4.com>
+* @author Scott Nichol <snichol@users.sourceforge.net>
+* @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
+* @access public
+*/
+class nusoap_base {
+ /**
+ * Identification for HTTP headers.
+ *
+ * @var string
+ * @access private
+ */
+ var $title = 'NuSOAP';
+ /**
+ * Version for HTTP headers.
+ *
+ * @var string
+ * @access private
+ */
+ var $version = '0.9.5';
+ /**
+ * CVS revision for HTTP headers.
+ *
+ * @var string
+ * @access private
+ */
+ var $revision = '$Revision: 1.123 $';
+ /**
+ * Current error string (manipulated by getError/setError)
+ *
+ * @var string
+ * @access private
+ */
+ var $error_str = '';
+ /**
+ * Current debug string (manipulated by debug/appendDebug/clearDebug/getDebug/getDebugAsXMLComment)
+ *
+ * @var string
+ * @access private
+ */
+ var $debug_str = '';
+ /**
+ * toggles automatic encoding of special characters as entities
+ * (should always be true, I think)
+ *
+ * @var boolean
+ * @access private
+ */
+ var $charencoding = true;
+ /**
+ * the debug level for this instance
+ *
+ * @var integer
+ * @access private
+ */
+ var $debugLevel;
+
+ /**
+ * set schema version
+ *
+ * @var string
+ * @access public
+ */
+ var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
+
+ /**
+ * charset encoding for outgoing messages
+ *
+ * @var string
+ * @access public
+ */
+ var $soap_defencoding = 'ISO-8859-1';
+ //var $soap_defencoding = 'UTF-8';
+
+ /**
+ * namespaces in an array of prefix => uri
+ *
+ * this is "seeded" by a set of constants, but it may be altered by code
+ *
+ * @var array
+ * @access public
+ */
+ var $namespaces = array(
+ 'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
+ 'xsd' => 'http://www.w3.org/2001/XMLSchema',
+ 'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
+ 'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/'
+ );
+
+ /**
+ * namespaces used in the current context, e.g. during serialization
+ *
+ * @var array
+ * @access private
+ */
+ var $usedNamespaces = array();
+
+ /**
+ * XML Schema types in an array of uri => (array of xml type => php type)
+ * is this legacy yet?
+ * no, this is used by the nusoap_xmlschema class to verify type => namespace mappings.
+ * @var array
+ * @access public
+ */
+ var $typemap = array(
+ 'http://www.w3.org/2001/XMLSchema' => array(
+ 'string'=>'string','boolean'=>'boolean','float'=>'double','double'=>'double','decimal'=>'double',
+ 'duration'=>'','dateTime'=>'string','time'=>'string','date'=>'string','gYearMonth'=>'',
+ 'gYear'=>'','gMonthDay'=>'','gDay'=>'','gMonth'=>'','hexBinary'=>'string','base64Binary'=>'string',
+ // abstract "any" types
+ 'anyType'=>'string','anySimpleType'=>'string',
+ // derived datatypes
+ 'normalizedString'=>'string','token'=>'string','language'=>'','NMTOKEN'=>'','NMTOKENS'=>'','Name'=>'','NCName'=>'','ID'=>'',
+ 'IDREF'=>'','IDREFS'=>'','ENTITY'=>'','ENTITIES'=>'','integer'=>'integer','nonPositiveInteger'=>'integer',
+ 'negativeInteger'=>'integer','long'=>'integer','int'=>'integer','short'=>'integer','byte'=>'integer','nonNegativeInteger'=>'integer',
+ 'unsignedLong'=>'','unsignedInt'=>'','unsignedShort'=>'','unsignedByte'=>'','positiveInteger'=>''),
+ 'http://www.w3.org/2000/10/XMLSchema' => array(
+ 'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
+ 'float'=>'double','dateTime'=>'string',
+ 'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
+ 'http://www.w3.org/1999/XMLSchema' => array(
+ 'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
+ 'float'=>'double','dateTime'=>'string',
+ 'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
+ 'http://soapinterop.org/xsd' => array('SOAPStruct'=>'struct'),
+ 'http://schemas.xmlsoap.org/soap/encoding/' => array('base64'=>'string','array'=>'array','Array'=>'array'),
+ 'http://xml.apache.org/xml-soap' => array('Map')
+ );
+
+ /**
+ * XML entities to convert
+ *
+ * @var array
+ * @access public
+ * @deprecated
+ * @see expandEntities
+ */
+ var $xmlEntities = array('quot' => '"','amp' => '&',
+ 'lt' => '<','gt' => '>','apos' => "'");
+
+ /**
+ * constructor
+ *
+ * @access public
+ */
+ function __construct() {
+ $this->debugLevel = $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'];
+ }
+
+ /**
+ * gets the global debug level, which applies to future instances
+ *
+ * @return integer Debug level 0-9, where 0 turns off
+ * @access public
+ */
+ function getGlobalDebugLevel() {
+ return $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'];
+ }
+
+ /**
+ * sets the global debug level, which applies to future instances
+ *
+ * @param int $level Debug level 0-9, where 0 turns off
+ * @access public
+ */
+ function setGlobalDebugLevel($level) {
+ $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'] = $level;
+ }
+
+ /**
+ * gets the debug level for this instance
+ *
+ * @return int Debug level 0-9, where 0 turns off
+ * @access public
+ */
+ function getDebugLevel() {
+ return $this->debugLevel;
+ }
+
+ /**
+ * sets the debug level for this instance
+ *
+ * @param int $level Debug level 0-9, where 0 turns off
+ * @access public
+ */
+ function setDebugLevel($level) {
+ $this->debugLevel = $level;
+ }
+
+ /**
+ * adds debug data to the instance debug string with formatting
+ *
+ * @param string $string debug data
+ * @access private
+ */
+ function debug($string){
+ if ($this->debugLevel > 0) {
+ $this->appendDebug($this->getmicrotime().' '.get_class($this).": $string\n");
+ }
+ }
+
+ /**
+ * adds debug data to the instance debug string without formatting
+ *
+ * @param string $string debug data
+ * @access public
+ */
+ function appendDebug($string){
+ if ($this->debugLevel > 0) {
+ // it would be nice to use a memory stream here to use
+ // memory more efficiently
+ $this->debug_str .= $string;
+ }
+ }
+
+ /**
+ * clears the current debug data for this instance
+ *
+ * @access public
+ */
+ function clearDebug() {
+ // it would be nice to use a memory stream here to use
+ // memory more efficiently
+ $this->debug_str = '';
+ }
+
+ /**
+ * gets the current debug data for this instance
+ *
+ * @return debug data
+ * @access public
+ */
+ function &getDebug() {
+ // it would be nice to use a memory stream here to use
+ // memory more efficiently
+ return $this->debug_str;
+ }
+
+ /**
+ * gets the current debug data for this instance as an XML comment
+ * this may change the contents of the debug data
+ *
+ * @return debug data as an XML comment
+ * @access public
+ */
+ function &getDebugAsXMLComment() {
+ // it would be nice to use a memory stream here to use
+ // memory more efficiently
+ while (strpos($this->debug_str, '--')) {
+ $this->debug_str = str_replace('--', '- -', $this->debug_str);
+ }
+ $ret = "<!--\n" . $this->debug_str . "\n-->";
+ return $ret;
+ }
+
+ /**
+ * expands entities, e.g. changes '<' to '<'.
+ *
+ * @param string $val The string in which to expand entities.
+ * @access private
+ */
+ function expandEntities($val) {
+ if ($this->charencoding) {
+ $val = str_replace('&', '&', $val);
+ $val = str_replace("'", ''', $val);
+ $val = str_replace('"', '"', $val);
+ $val = str_replace('<', '<', $val);
+ $val = str_replace('>', '>', $val);
+ }
+ return $val;
+ }
+
+ /**
+ * returns error string if present
+ *
+ * @return mixed error string or false
+ * @access public
+ */
+ function getError(){
+ if($this->error_str != ''){
+ return $this->error_str;
+ }
+ return false;
+ }
+
+ /**
+ * sets error string
+ *
+ * @return boolean $string error string
+ * @access private
+ */
+ function setError($str){
+ $this->error_str = $str;
+ }
+
+ /**
+ * detect if array is a simple array or a struct (associative array)
+ *
+ * @param mixed $val The PHP array
+ * @return string (arraySimple|arrayStruct)
+ * @access private
+ */
+ function isArraySimpleOrStruct($val) {
+ $keyList = array_keys($val);
+ foreach ($keyList as $keyListValue) {
+ if (!is_int($keyListValue)) {
+ return 'arrayStruct';
+ }
+ }
+ return 'arraySimple';
+ }
+
+ /**
+ * serializes PHP values in accordance w/ section 5. Type information is
+ * not serialized if $use == 'literal'.
+ *
+ * @param mixed $val The value to serialize
+ * @param string $name The name (local part) of the XML element
+ * @param string $type The XML schema type (local part) for the element
+ * @param string $name_ns The namespace for the name of the XML element
+ * @param string $type_ns The namespace for the type of the element
+ * @param array $attributes The attributes to serialize as name=>value pairs
+ * @param string $use The WSDL "use" (encoded|literal)
+ * @param boolean $soapval Whether this is called from soapval.
+ * @return string The serialized element, possibly with child elements
+ * @access public
+ */
+ function serialize_val($val,$name=false,$type=false,$name_ns=false,$type_ns=false,$attributes=false,$use='encoded',$soapval=false) {
+ $this->debug("in serialize_val: name=$name, type=$type, name_ns=$name_ns, type_ns=$type_ns, use=$use, soapval=$soapval");
+ $this->appendDebug('value=' . $this->varDump($val));
+ $this->appendDebug('attributes=' . $this->varDump($attributes));
+
+ if (is_object($val) && get_class($val) == 'soapval' && (! $soapval)) {
+ $this->debug("serialize_val: serialize soapval");
+ $xml = $val->serialize($use);
+ $this->appendDebug($val->getDebug());
+ $val->clearDebug();
+ $this->debug("serialize_val of soapval returning $xml");
+ return $xml;
+ }
+ // force valid name if necessary
+ if (is_numeric($name)) {
+ $name = '__numeric_' . $name;
+ } elseif (! $name) {
+ $name = 'noname';
+ }
+ // if name has ns, add ns prefix to name
+ $xmlns = '';
+ if($name_ns){
+ $prefix = 'nu'.rand(1000,9999);
+ $name = $prefix.':'.$name;
+ $xmlns .= " xmlns:$prefix=\"$name_ns\"";
+ }
+ // if type is prefixed, create type prefix
+ if($type_ns != '' && $type_ns == $this->namespaces['xsd']){
+ // need to fix this. shouldn't default to xsd if no ns specified
+ // w/o checking against typemap
+ $type_prefix = 'xsd';
+ } elseif($type_ns){
+ $type_prefix = 'ns'.rand(1000,9999);
+ $xmlns .= " xmlns:$type_prefix=\"$type_ns\"";
+ }
+ // serialize attributes if present
+ $atts = '';
+ if($attributes){
+ foreach($attributes as $k => $v){
+ $atts .= " $k=\"".$this->expandEntities($v).'"';
+ }
+ }
+ // serialize null value
+ if (is_null($val)) {
+ $this->debug("serialize_val: serialize null");
+ if ($use == 'literal') {
+ // TODO: depends on minOccurs
+ $xml = "<$name$xmlns$atts/>";
+ $this->debug("serialize_val returning $xml");
+ return $xml;
+ } else {
+ if (isset($type) && isset($type_prefix)) {
+ $type_str = " xsi:type=\"$type_prefix:$type\"";
+ } else {
+ $type_str = '';
+ }
+ $xml = "<$name$xmlns$type_str$atts xsi:nil=\"true\"/>";
+ $this->debug("serialize_val returning $xml");
+ return $xml;
+ }
+ }
+ // serialize if an xsd built-in primitive type
+ if($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])){
+ $this->debug("serialize_val: serialize xsd built-in primitive type");
+ if (is_bool($val)) {
+ if ($type == 'boolean') {
+ $val = $val ? 'true' : 'false';
+ } elseif (! $val) {
+ $val = 0;
+ }
+ } else if (is_string($val)) {
+ $val = $this->expandEntities($val);
+ }
+ if ($use == 'literal') {
+ $xml = "<$name$xmlns$atts>$val</$name>";
+ $this->debug("serialize_val returning $xml");
+ return $xml;
+ } else {
+ $xml = "<$name$xmlns xsi:type=\"xsd:$type\"$atts>$val</$name>";
+ $this->debug("serialize_val returning $xml");
+ return $xml;
+ }
+ }
+ // detect type and serialize
+ $xml = '';
+ switch(true) {
+ case (is_bool($val) || $type == 'boolean'):
+ $this->debug("serialize_val: serialize boolean");
+ if ($type == 'boolean') {
+ $val = $val ? 'true' : 'false';
+ } elseif (! $val) {
+ $val = 0;
+ }
+ if ($use == 'literal') {
+ $xml .= "<$name$xmlns$atts>$val</$name>";
+ } else {
+ $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>";
+ }
+ break;
+ case (is_int($val) || is_long($val) || $type == 'int'):
+ $this->debug("serialize_val: serialize int");
+ if ($use == 'literal') {
+ $xml .= "<$name$xmlns$atts>$val</$name>";
+ } else {
+ $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val</$name>";
+ }
+ break;
+ case (is_float($val)|| is_double($val) || $type == 'float'):
+ $this->debug("serialize_val: serialize float");
+ if ($use == 'literal') {
+ $xml .= "<$name$xmlns$atts>$val</$name>";
+ } else {
+ $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val</$name>";
+ }
+ break;
+ case (is_string($val) || $type == 'string'):
+ $this->debug("serialize_val: serialize string");
+ $val = $this->expandEntities($val);
+ if ($use == 'literal') {
+ $xml .= "<$name$xmlns$atts>$val</$name>";
+ } else {
+ $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>";
+ }
+ break;
+ case is_object($val):
+ $this->debug("serialize_val: serialize object");
+ if (get_class($val) == 'soapval') {
+ $this->debug("serialize_val: serialize soapval object");
+ $pXml = $val->serialize($use);
+ $this->appendDebug($val->getDebug());
+ $val->clearDebug();
+ } else {
+ if (! $name) {
+ $name = get_class($val);
+ $this->debug("In serialize_val, used class name $name as element name");
+ } else {
+ $this->debug("In serialize_val, do not override name $name for element name for class " . get_class($val));
+ }
+ foreach(get_object_vars($val) as $k => $v){
+ $pXml = isset($pXml) ? $pXml.$this->serialize_val($v,$k,false,false,false,false,$use) : $this->serialize_val($v,$k,false,false,false,false,$use);
+ }
+ }
+ if(isset($type) && isset($type_prefix)){
+ $type_str = " xsi:type=\"$type_prefix:$type\"";
+ } else {
+ $type_str = '';
+ }
+ if ($use == 'literal') {
+ $xml .= "<$name$xmlns$atts>$pXml</$name>";
+ } else {
+ $xml .= "<$name$xmlns$type_str$atts>$pXml</$name>";
+ }
+ break;
+ break;
+ case (is_array($val) || $type):
+ // detect if struct or array
+ $valueType = $this->isArraySimpleOrStruct($val);
+ if($valueType=='arraySimple' || preg_match('/^ArrayOf/',$type)){
+ $this->debug("serialize_val: serialize array");
+ $i = 0;
+ if(is_array($val) && count($val)> 0){
+ foreach($val as $v){
+ if(is_object($v) && get_class($v) == 'soapval'){
+ $tt_ns = $v->type_ns;
+ $tt = $v->type;
+ } elseif (is_array($v)) {
+ $tt = $this->isArraySimpleOrStruct($v);
+ } else {
+ $tt = gettype($v);
+ }
+ $array_types[$tt] = 1;
+ // TODO: for literal, the name should be $name
+ $xml .= $this->serialize_val($v,'item',false,false,false,false,$use);
+ ++$i;
+ }
+ if(count($array_types) > 1){
+ $array_typename = 'xsd:anyType';
+ } elseif(isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) {
+ if ($tt == 'integer') {
+ $tt = 'int';
+ }
+ $array_typename = 'xsd:'.$tt;
+ } elseif(isset($tt) && $tt == 'arraySimple'){
+ $array_typename = 'SOAP-ENC:Array';
+ } elseif(isset($tt) && $tt == 'arrayStruct'){
+ $array_typename = 'unnamed_struct_use_soapval';
+ } else {
+ // if type is prefixed, create type prefix
+ if ($tt_ns != '' && $tt_ns == $this->namespaces['xsd']){
+ $array_typename = 'xsd:' . $tt;
+ } elseif ($tt_ns) {
+ $tt_prefix = 'ns' . rand(1000, 9999);
+ $array_typename = "$tt_prefix:$tt";
+ $xmlns .= " xmlns:$tt_prefix=\"$tt_ns\"";
+ } else {
+ $array_typename = $tt;
+ }
+ }
+ $array_type = $i;
+ if ($use == 'literal') {
+ $type_str = '';
+ } else if (isset($type) && isset($type_prefix)) {
+ $type_str = " xsi:type=\"$type_prefix:$type\"";
+ } else {
+ $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"".$array_typename."[$array_type]\"";
+ }
+ // empty array
+ } else {
+ if ($use == 'literal') {
+ $type_str = '';
+ } else if (isset($type) && isset($type_prefix)) {
+ $type_str = " xsi:type=\"$type_prefix:$type\"";
+ } else {
+ $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"xsd:anyType[0]\"";
+ }
+ }
+ // TODO: for array in literal, there is no wrapper here
+ $xml = "<$name$xmlns$type_str$atts>".$xml."</$name>";
+ } else {
+ // got a struct
+ $this->debug("serialize_val: serialize struct");
+ if(isset($type) && isset($type_prefix)){
+ $type_str = " xsi:type=\"$type_prefix:$type\"";
+ } else {
+ $type_str = '';
+ }
+ if ($use == 'literal') {
+ $xml .= "<$name$xmlns$atts>";
+ } else {
+ $xml .= "<$name$xmlns$type_str$atts>";
+ }
+ foreach($val as $k => $v){
+ // Apache Map
+ if ($type == 'Map' && $type_ns == 'http://xml.apache.org/xml-soap') {
+ $xml .= '<item>';
+ $xml .= $this->serialize_val($k,'key',false,false,false,false,$use);
+ $xml .= $this->serialize_val($v,'value',false,false,false,false,$use);
+ $xml .= '</item>';
+ } else {
+ $xml .= $this->serialize_val($v,$k,false,false,false,false,$use);
+ }
+ }
+ $xml .= "</$name>";
+ }
+ break;
+ default:
+ $this->debug("serialize_val: serialize unknown");
+ $xml .= 'not detected, got '.gettype($val).' for '.$val;
+ break;
+ }
+ $this->debug("serialize_val returning $xml");
+ return $xml;
+ }
+
+ /**
+ * serializes a message
+ *
+ * @param string $body the XML of the SOAP body
+ * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers, or associative array
+ * @param array $namespaces optional the namespaces used in generating the body and headers
+ * @param string $style optional (rpc|document)
+ * @param string $use optional (encoded|literal)
+ * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
+ * @return string the message
+ * @access public
+ */
+ function serializeEnvelope($body,$headers=false,$namespaces=array(),$style='rpc',$use='encoded',$encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'){
+ // TODO: add an option to automatically run utf8_encode on $body and $headers
+ // if $this->soap_defencoding is UTF-8. Not doing this automatically allows
+ // one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1
+
+ $this->debug("In serializeEnvelope length=" . strlen($body) . " body (max 1000 characters)=" . substr($body, 0, 1000) . " style=$style use=$use encodingStyle=$encodingStyle");
+ $this->debug("headers:");
+ $this->appendDebug($this->varDump($headers));
+ $this->debug("namespaces:");
+ $this->appendDebug($this->varDump($namespaces));
+
+ // serialize namespaces
+ $ns_string = '';
+ foreach(array_merge($this->namespaces,$namespaces) as $k => $v){
+ $ns_string .= " xmlns:$k=\"$v\"";
+ }
+ if($encodingStyle) {
+ $ns_string = " SOAP-ENV:encodingStyle=\"$encodingStyle\"$ns_string";
+ }
+
+ // serialize headers
+ if($headers){
+ if (is_array($headers)) {
+ $xml = '';
+ foreach ($headers as $k => $v) {
+ if (is_object($v) && get_class($v) == 'soapval') {
+ $xml .= $this->serialize_val($v, false, false, false, false, false, $use);
+ } else {
+ $xml .= $this->serialize_val($v, $k, false, false, false, false, $use);
+ }
+ }
+ $headers = $xml;
+ $this->debug("In serializeEnvelope, serialized array of headers to $headers");
+ }
+ $headers = "<SOAP-ENV:Header>".$headers."</SOAP-ENV:Header>";
+ }
+ // serialize envelope
+ return
+ '<?xml version="1.0" encoding="'.$this->soap_defencoding .'"?'.">".
+ '<SOAP-ENV:Envelope'.$ns_string.">".
+ $headers.
+ "<SOAP-ENV:Body>".
+ $body.
+ "</SOAP-ENV:Body>".
+ "</SOAP-ENV:Envelope>";
+ }
+
+ /**
+ * formats a string to be inserted into an HTML stream
+ *
+ * @param string $str The string to format
+ * @return string The formatted string
+ * @access public
+ * @deprecated
+ */
+ function formatDump($str){
+ $str = htmlspecialchars($str);
+ return nl2br($str);
+ }
+
+ /**
+ * contracts (changes namespace to prefix) a qualified name
+ *
+ * @param string $qname qname
+ * @return string contracted qname
+ * @access private
+ */
+ function contractQname($qname){
+ // get element namespace
+ //$this->xdebug("Contract $qname");
+ if (strrpos($qname, ':')) {
+ // get unqualified name
+ $name = substr($qname, strrpos($qname, ':') + 1);
+ // get ns
+ $ns = substr($qname, 0, strrpos($qname, ':'));
+ $p = $this->getPrefixFromNamespace($ns);
+ if ($p) {
+ return $p . ':' . $name;
+ }
+ return $qname;
+ } else {
+ return $qname;
+ }
+ }
+
+ /**
+ * expands (changes prefix to namespace) a qualified name
+ *
+ * @param string $qname qname
+ * @return string expanded qname
+ * @access private
+ */
+ function expandQname($qname){
+ // get element prefix
+ if(strpos($qname,':') && !preg_match('/^http:\/\//',$qname)){
+ // get unqualified name
+ $name = substr(strstr($qname,':'),1);
+ // get ns prefix
+ $prefix = substr($qname,0,strpos($qname,':'));
+ if(isset($this->namespaces[$prefix])){
+ return $this->namespaces[$prefix].':'.$name;
+ } else {
+ return $qname;
+ }
+ } else {
+ return $qname;
+ }
+ }
+
+ /**
+ * returns the local part of a prefixed string
+ * returns the original string, if not prefixed
+ *
+ * @param string $str The prefixed string
+ * @return string The local part
+ * @access public
+ */
+ function getLocalPart($str){
+ if($sstr = strrchr($str,':')){
+ // get unqualified name
+ return substr( $sstr, 1 );
+ } else {
+ return $str;
+ }
+ }
+
+ /**
+ * returns the prefix part of a prefixed string
+ * returns false, if not prefixed
+ *
+ * @param string $str The prefixed string
+ * @return mixed The prefix or false if there is no prefix
+ * @access public
+ */
+ function getPrefix($str){
+ if($pos = strrpos($str,':')){
+ // get prefix
+ return substr($str,0,$pos);
+ }
+ return false;
+ }
+
+ /**
+ * pass it a prefix, it returns a namespace
+ *
+ * @param string $prefix The prefix
+ * @return mixed The namespace, false if no namespace has the specified prefix
+ * @access public
+ */
+ function getNamespaceFromPrefix($prefix){
+ if (isset($this->namespaces[$prefix])) {
+ return $this->namespaces[$prefix];
+ }
+ //$this->setError("No namespace registered for prefix '$prefix'");
+ return false;
+ }
+
+ /**
+ * returns the prefix for a given namespace (or prefix)
+ * or false if no prefixes registered for the given namespace
+ *
+ * @param string $ns The namespace
+ * @return mixed The prefix, false if the namespace has no prefixes
+ * @access public
+ */
+ function getPrefixFromNamespace($ns) {
+ foreach ($this->namespaces as $p => $n) {
+ if ($ns == $n || $ns == $p) {
+ $this->usedNamespaces[$p] = $n;
+ return $p;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * returns the time in ODBC canonical form with microseconds
+ *
+ * @return string The time in ODBC canonical form with microseconds
+ * @access public
+ */
+ function getmicrotime() {
+ if (function_exists('gettimeofday')) {
+ $tod = gettimeofday();
+ $sec = $tod['sec'];
+ $usec = $tod['usec'];
+ } else {
+ $sec = time();
+ $usec = 0;
+ }
+ return strftime('%Y-%m-%d %H:%M:%S', $sec) . '.' . sprintf('%06d', $usec);
+ }
+
+ /**
+ * Returns a string with the output of var_dump
+ *
+ * @param mixed $data The variable to var_dump
+ * @return string The output of var_dump
+ * @access public
+ */
+ function varDump($data) {
+ ob_start();
+ var_dump($data);
+ $ret_val = ob_get_contents();
+ ob_end_clean();
+ return $ret_val;
+ }
+
+ /**
+ * represents the object as a string
+ *
+ * @return string
+ * @access public
+ */
+ function __toString() {
+ return $this->varDump($this);
+ }
+}
+
+// XML Schema Datatype Helper Functions
+
+//xsd:dateTime helpers
+
+/**
+* convert unix timestamp to ISO 8601 compliant date string
+*
+* @param int $timestamp Unix time stamp
+* @param boolean $utc Whether the time stamp is UTC or local
+* @return mixed ISO 8601 date string or false
+* @access public
+*/
+function timestamp_to_iso8601($timestamp,$utc=true){
+ $datestr = date('Y-m-d\TH:i:sO',$timestamp);
+ $pos = strrpos($datestr, "+");
+ if ($pos === FALSE) {
+ $pos = strrpos($datestr, "-");
+ }
+ if ($pos !== FALSE) {
+ if (strlen($datestr) == $pos + 5) {
+ $datestr = substr($datestr, 0, $pos + 3) . ':' . substr($datestr, -2);
+ }
+ }
+ if($utc){
+ $pattern = '/'.
+ '([0-9]{4})-'. // centuries & years CCYY-
+ '([0-9]{2})-'. // months MM-
+ '([0-9]{2})'. // days DD
+ 'T'. // separator T
+ '([0-9]{2}):'. // hours hh:
+ '([0-9]{2}):'. // minutes mm:
+ '([0-9]{2})(\.[0-9]*)?'. // seconds ss.ss...
+ '(Z|[+\-][0-9]{2}:?[0-9]{2})?'. // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
+ '/';
+
+ if(preg_match($pattern,$datestr,$regs)){
+ return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',$regs[1],$regs[2],$regs[3],$regs[4],$regs[5],$regs[6]);
+ }
+ return false;
+ } else {
+ return $datestr;
+ }
+}
+
+/**
+* convert ISO 8601 compliant date string to unix timestamp
+*
+* @param string $datestr ISO 8601 compliant date string
+* @return mixed Unix timestamp (int) or false
+* @access public
+*/
+function iso8601_to_timestamp($datestr){
+ $pattern = '/'.
+ '([0-9]{4})-'. // centuries & years CCYY-
+ '([0-9]{2})-'. // months MM-
+ '([0-9]{2})'. // days DD
+ 'T'. // separator T
+ '([0-9]{2}):'. // hours hh:
+ '([0-9]{2}):'. // minutes mm:
+ '([0-9]{2})(\.[0-9]+)?'. // seconds ss.ss...
+ '(Z|[+\-][0-9]{2}:?[0-9]{2})?'. // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
+ '/';
+ if(preg_match($pattern,$datestr,$regs)){
+ // not utc
+ if($regs[8] != 'Z'){
+ $op = substr($regs[8],0,1);
+ $h = substr($regs[8],1,2);
+ $m = substr($regs[8],strlen($regs[8])-2,2);
+ if($op == '-'){
+ $regs[4] = $regs[4] + $h;
+ $regs[5] = $regs[5] + $m;
+ } elseif($op == '+'){
+ $regs[4] = $regs[4] - $h;
+ $regs[5] = $regs[5] - $m;
+ }
+ }
+ return gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
+// return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
+ } else {
+ return false;
+ }
+}
+
+/**
+* sleeps some number of microseconds
+*
+* @param string $usec the number of microseconds to sleep
+* @access public
+* @deprecated
+*/
+function usleepWindows($usec)
+{
+ $start = gettimeofday();
+
+ do
+ {
+ $stop = gettimeofday();
+ $timePassed = 1000000 * ($stop['sec'] - $start['sec'])
+ + $stop['usec'] - $start['usec'];
+ }
+ while ($timePassed < $usec);
+}
+
+?><?php
+
+
+
+/**
+* Contains information for a SOAP fault.
+* Mainly used for returning faults from deployed functions
+* in a server instance.
+* @author Dietrich Ayala <dietrich@ganx4.com>
+* @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
+* @access public
+*/
+class nusoap_fault extends nusoap_base {
+ /**
+ * The fault code (client|server)
+ * @var string
+ * @access private
+ */
+ var $faultcode;
+ /**
+ * The fault actor
+ * @var string
+ * @access private
+ */
+ var $faultactor;
+ /**
+ * The fault string, a description of the fault
+ * @var string
+ * @access private
+ */
+ var $faultstring;
+ /**
+ * The fault detail, typically a string or array of string
+ * @var mixed
+ * @access private
+ */
+ var $faultdetail;
+
+ /**
+ * constructor
+ *
+ * @param string $faultcode (SOAP-ENV:Client | SOAP-ENV:Server)
+ * @param string $faultactor only used when msg routed between multiple actors
+ * @param string $faultstring human readable error message
+ * @param mixed $faultdetail detail, typically a string or array of string
+ */
+ function __construct($faultcode,$faultactor='',$faultstring='',$faultdetail=''){
+ parent::__construct();
+ $this->faultcode = $faultcode;
+ $this->faultactor = $faultactor;
+ $this->faultstring = $faultstring;
+ $this->faultdetail = $faultdetail;
+ }
+
+ /**
+ * serialize a fault
+ *
+ * @return string The serialization of the fault instance.
+ * @access public
+ */
+ function serialize(){
+ $ns_string = '';
+ foreach($this->namespaces as $k => $v){
+ $ns_string .= "\n xmlns:$k=\"$v\"";
+ }
+ $return_msg =
+ '<?xml version="1.0" encoding="'.$this->soap_defencoding.'"?>'.
+ '<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"'.$ns_string.">\n".
+ '<SOAP-ENV:Body>'.
+ '<SOAP-ENV:Fault>'.
+ $this->serialize_val($this->faultcode, 'faultcode').
+ $this->serialize_val($this->faultactor, 'faultactor').
+ $this->serialize_val($this->faultstring, 'faultstring').
+ $this->serialize_val($this->faultdetail, 'detail').
+ '</SOAP-ENV:Fault>'.
+ '</SOAP-ENV:Body>'.
+ '</SOAP-ENV:Envelope>';
+ return $return_msg;
+ }
+}
+
+/**
+ * Backward compatibility
+ */
+class soap_fault extends nusoap_fault {
+}
+
+?><?php
+
+
+
+/**
+* parses an XML Schema, allows access to it's data, other utility methods.
+* imperfect, no validation... yet, but quite functional.
+*
+* @author Dietrich Ayala <dietrich@ganx4.com>
+* @author Scott Nichol <snichol@users.sourceforge.net>
+* @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
+* @access public
+*/
+class nusoap_xmlschema extends nusoap_base {
+
+ // files
+ var $schema = '';
+ var $xml = '';
+ // namespaces
+ var $enclosingNamespaces;
+ // schema info
+ var $schemaInfo = array();
+ var $schemaTargetNamespace = '';
+ // types, elements, attributes defined by the schema
+ var $attributes = array();
+ var $complexTypes = array();
+ var $complexTypeStack = array();
+ var $currentComplexType = null;
+ var $elements = array();
+ var $elementStack = array();
+ var $currentElement = null;
+ var $simpleTypes = array();
+ var $simpleTypeStack = array();
+ var $currentSimpleType = null;
+ // imports
+ var $imports = array();
+ // parser vars
+ var $parser;
+ var $position = 0;
+ var $depth = 0;
+ var $depth_array = array();
+ var $message = array();
+ var $defaultNamespace = array();
+
+ /**
+ * constructor
+ *
+ * @param string $schema schema document URI
+ * @param string $xml xml document URI
+ * @param string $namespaces namespaces defined in enclosing XML
+ * @access public
+ */
+ function __construct($schema='',$xml='',$namespaces=array()){
+ parent::__construct();
+ $this->debug('nusoap_xmlschema class instantiated, inside constructor');
+ // files
+ $this->schema = $schema;
+ $this->xml = $xml;
+
+ // namespaces
+ $this->enclosingNamespaces = $namespaces;
+ $this->namespaces = array_merge($this->namespaces, $namespaces);
+
+ // parse schema file
+ if($schema != ''){
+ $this->debug('initial schema file: '.$schema);
+ $this->parseFile($schema, 'schema');
+ }
+
+ // parse xml file
+ if($xml != ''){
+ $this->debug('initial xml file: '.$xml);
+ $this->parseFile($xml, 'xml');
+ }
+
+ }
+
+ /**
+ * parse an XML file
+ *
+ * @param string $xml path/URL to XML file
+ * @param string $type (schema | xml)
+ * @return boolean
+ * @access public
+ */
+ function parseFile($xml,$type){
+ // parse xml file
+ if($xml != ""){
+ $xmlStr = @join("",@file($xml));
+ if($xmlStr == ""){
+ $msg = 'Error reading XML from '.$xml;
+ $this->setError($msg);
+ $this->debug($msg);
+ return false;
+ } else {
+ $this->debug("parsing $xml");
+ $this->parseString($xmlStr,$type);
+ $this->debug("done parsing $xml");
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * parse an XML string
+ *
+ * @param string $xml path or URL
+ * @param string $type (schema|xml)
+ * @access private
+ */
+ function parseString($xml,$type){
+ // parse xml string
+ if($xml != ""){
+
+ // Create an XML parser.
+ $this->parser = xml_parser_create();
+ // Set the options for parsing the XML data.
+ xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
+
+ // Set the object for the parser.
+ xml_set_object($this->parser, $this);
+
+ // Set the element handlers for the parser.
+ if($type == "schema"){
+ xml_set_element_handler($this->parser, 'schemaStartElement','schemaEndElement');
+ xml_set_character_data_handler($this->parser,'schemaCharacterData');
+ } elseif($type == "xml"){
+ xml_set_element_handler($this->parser, 'xmlStartElement','xmlEndElement');
+ xml_set_character_data_handler($this->parser,'xmlCharacterData');
+ }
+
+ // Parse the XML file.
+ if(!xml_parse($this->parser,$xml,true)){
+ // Display an error message.
+ $errstr = sprintf('XML error parsing XML schema on line %d: %s',
+ xml_get_current_line_number($this->parser),
+ xml_error_string(xml_get_error_code($this->parser))
+ );
+ $this->debug($errstr);
+ $this->debug("XML payload:\n" . $xml);
+ $this->setError($errstr);
+ }
+
+ xml_parser_free($this->parser);
+ } else{
+ $this->debug('no xml passed to parseString()!!');
+ $this->setError('no xml passed to parseString()!!');
+ }
+ }
+
+ /**
+ * gets a type name for an unnamed type
+ *
+ * @param string Element name
+ * @return string A type name for an unnamed type
+ * @access private
+ */
+ function CreateTypeName($ename) {
+ $scope = '';
+ for ($i = 0; $i < count($this->complexTypeStack); $i++) {
+ $scope .= $this->complexTypeStack[$i] . '_';
+ }
+ return $scope . $ename . '_ContainedType';
+ }
+
+ /**
+ * start-element handler
+ *
+ * @param string $parser XML parser object
+ * @param string $name element name
+ * @param string $attrs associative array of attributes
+ * @access private
+ */
+ function schemaStartElement($parser, $name, $attrs) {
+
+ // position in the total number of elements, starting from 0
+ $pos = $this->position++;
+ $depth = $this->depth++;
+ // set self as current value for this depth
+ $this->depth_array[$depth] = $pos;
+ $this->message[$pos] = array('cdata' => '');
+ if ($depth > 0) {
+ $this->defaultNamespace[$pos] = $this->defaultNamespace[$this->depth_array[$depth - 1]];
+ } else {
+ $this->defaultNamespace[$pos] = false;
+ }
+
+ // get element prefix
+ if($prefix = $this->getPrefix($name)){
+ // get unqualified name
+ $name = $this->getLocalPart($name);
+ } else {
+ $prefix = '';
+ }
+
+ // loop thru attributes, expanding, and registering namespace declarations
+ if(count($attrs) > 0){
+ foreach($attrs as $k => $v){
+ // if ns declarations, add to class level array of valid namespaces
+ if(preg_match('/^xmlns/',$k)){
+ //$this->xdebug("$k: $v");
+ //$this->xdebug('ns_prefix: '.$this->getPrefix($k));
+ if($ns_prefix = substr(strrchr($k,':'),1)){
+ //$this->xdebug("Add namespace[$ns_prefix] = $v");
+ $this->namespaces[$ns_prefix] = $v;
+ } else {
+ $this->defaultNamespace[$pos] = $v;
+ if (! $this->getPrefixFromNamespace($v)) {
+ $this->namespaces['ns'.(count($this->namespaces)+1)] = $v;
+ }
+ }
+ if($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema'){
+ $this->XMLSchemaVersion = $v;
+ $this->namespaces['xsi'] = $v.'-instance';
+ }
+ }
+ }
+ foreach($attrs as $k => $v){
+ // expand each attribute
+ $k = strpos($k,':') ? $this->expandQname($k) : $k;
+ $v = strpos($v,':') ? $this->expandQname($v) : $v;
+ $eAttrs[$k] = $v;
+ }
+ $attrs = $eAttrs;
+ } else {
+ $attrs = array();
+ }
+ // find status, register data
+ switch($name){
+ case 'all': // (optional) compositor content for a complexType
+ case 'choice':
+ case 'group':
+ case 'sequence':
+ //$this->xdebug("compositor $name for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement");
+ $this->complexTypes[$this->currentComplexType]['compositor'] = $name;
+ //if($name == 'all' || $name == 'sequence'){
+ // $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
+ //}
+ break;
+ case 'attribute': // complexType attribute
+ //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']);
+ $this->xdebug("parsing attribute:");
+ $this->appendDebug($this->varDump($attrs));
+ if (!isset($attrs['form'])) {
+ // TODO: handle globals
+ $attrs['form'] = $this->schemaInfo['attributeFormDefault'];
+ }
+ if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
+ $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
+ if (!strpos($v, ':')) {
+ // no namespace in arrayType attribute value...
+ if ($this->defaultNamespace[$pos]) {
+ // ...so use the default
+ $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'] = $this->defaultNamespace[$pos] . ':' . $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
+ }
+ }
+ }
+ if(isset($attrs['name'])){
+ $this->attributes[$attrs['name']] = $attrs;
+ $aname = $attrs['name'];
+ } elseif(isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType'){
+ if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
+ $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
+ } else {
+ $aname = '';
+ }
+ } elseif(isset($attrs['ref'])){
+ $aname = $attrs['ref'];
+ $this->attributes[$attrs['ref']] = $attrs;
+ }
+
+ if($this->currentComplexType){ // This should *always* be
+ $this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs;
+ }
+ // arrayType attribute
+ if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType'){
+ $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
+ $prefix = $this->getPrefix($aname);
+ if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])){
+ $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
+ } else {
+ $v = '';
+ }
+ if(strpos($v,'[,]')){
+ $this->complexTypes[$this->currentComplexType]['multidimensional'] = true;
+ }
+ $v = substr($v,0,strpos($v,'[')); // clip the []
+ if(!strpos($v,':') && isset($this->typemap[$this->XMLSchemaVersion][$v])){
+ $v = $this->XMLSchemaVersion.':'.$v;
+ }
+ $this->complexTypes[$this->currentComplexType]['arrayType'] = $v;
+ }
+ break;
+ case 'complexContent': // (optional) content for a complexType
+ $this->xdebug("do nothing for element $name");
+ break;
+ case 'complexType':
+ array_push($this->complexTypeStack, $this->currentComplexType);
+ if(isset($attrs['name'])){
+ // TODO: what is the scope of named complexTypes that appear
+ // nested within other c complexTypes?
+ $this->xdebug('processing named complexType '.$attrs['name']);
+ //$this->currentElement = false;
+ $this->currentComplexType = $attrs['name'];
+ $this->complexTypes[$this->currentComplexType] = $attrs;
+ $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
+ // This is for constructs like
+ // <complexType name="ListOfString" base="soap:Array">
+ // <sequence>
+ // <element name="string" type="xsd:string"
+ // minOccurs="0" maxOccurs="unbounded" />
+ // </sequence>
+ // </complexType>
+ if(isset($attrs['base']) && preg_match('/:Array$/',$attrs['base'])){
+ $this->xdebug('complexType is unusual array');
+ $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
+ } else {
+ $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
+ }
+ } else {
+ $name = $this->CreateTypeName($this->currentElement);
+ $this->xdebug('processing unnamed complexType for element ' . $this->currentElement . ' named ' . $name);
+ $this->currentComplexType = $name;
+ //$this->currentElement = false;
+ $this->complexTypes[$this->currentComplexType] = $attrs;
+ $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
+ // This is for constructs like
+ // <complexType name="ListOfString" base="soap:Array">
+ // <sequence>
+ // <element name="string" type="xsd:string"
+ // minOccurs="0" maxOccurs="unbounded" />
+ // </sequence>
+ // </complexType>
+ if(isset($attrs['base']) && preg_match('/:Array$/',$attrs['base'])){
+ $this->xdebug('complexType is unusual array');
+ $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
+ } else {
+ $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
+ }
+ }
+ $this->complexTypes[$this->currentComplexType]['simpleContent'] = 'false';
+ break;
+ case 'element':
+ array_push($this->elementStack, $this->currentElement);
+ if (!isset($attrs['form'])) {
+ if ($this->currentComplexType) {
+ $attrs['form'] = $this->schemaInfo['elementFormDefault'];
+ } else {
+ // global
+ $attrs['form'] = 'qualified';
+ }
+ }
+ if(isset($attrs['type'])){
+ $this->xdebug("processing typed element ".$attrs['name']." of type ".$attrs['type']);
+ if (! $this->getPrefix($attrs['type'])) {
+ if ($this->defaultNamespace[$pos]) {
+ $attrs['type'] = $this->defaultNamespace[$pos] . ':' . $attrs['type'];
+ $this->xdebug('used default namespace to make type ' . $attrs['type']);
+ }
+ }
+ // This is for constructs like
+ // <complexType name="ListOfString" base="soap:Array">
+ // <sequence>
+ // <element name="string" type="xsd:string"
+ // minOccurs="0" maxOccurs="unbounded" />
+ // </sequence>
+ // </complexType>
+ if ($this->currentComplexType && $this->complexTypes[$this->currentComplexType]['phpType'] == 'array') {
+ $this->xdebug('arrayType for unusual array is ' . $attrs['type']);
+ $this->complexTypes[$this->currentComplexType]['arrayType'] = $attrs['type'];
+ }
+ $this->currentElement = $attrs['name'];
+ $ename = $attrs['name'];
+ } elseif(isset($attrs['ref'])){
+ $this->xdebug("processing element as ref to ".$attrs['ref']);
+ $this->currentElement = "ref to ".$attrs['ref'];
+ $ename = $this->getLocalPart($attrs['ref']);
+ } else {
+ $type = $this->CreateTypeName($this->currentComplexType . '_' . $attrs['name']);
+ $this->xdebug("processing untyped element " . $attrs['name'] . ' type ' . $type);
+ $this->currentElement = $attrs['name'];
+ $attrs['type'] = $this->schemaTargetNamespace . ':' . $type;
+ $ename = $attrs['name'];
+ }
+ if (isset($ename) && $this->currentComplexType) {
+ $this->xdebug("add element $ename to complexType $this->currentComplexType");
+ $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs;
+ } elseif (!isset($attrs['ref'])) {
+ $this->xdebug("add element $ename to elements array");
+ $this->elements[ $attrs['name'] ] = $attrs;
+ $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
+ }
+ break;
+ case 'enumeration': // restriction value list member
+ $this->xdebug('enumeration ' . $attrs['value']);
+ if ($this->currentSimpleType) {
+ $this->simpleTypes[$this->currentSimpleType]['enumeration'][] = $attrs['value'];
+ } elseif ($this->currentComplexType) {
+ $this->complexTypes[$this->currentComplexType]['enumeration'][] = $attrs['value'];
+ }
+ break;
+ case 'extension': // simpleContent or complexContent type extension
+ $this->xdebug('extension ' . $attrs['base']);
+ if ($this->currentComplexType) {
+ $ns = $this->getPrefix($attrs['base']);
+ if ($ns == '') {
+ $this->complexTypes[$this->currentComplexType]['extensionBase'] = $this->schemaTargetNamespace . ':' . $attrs['base'];
+ } else {
+ $this->complexTypes[$this->currentComplexType]['extensionBase'] = $attrs['base'];
+ }
+ } else {
+ $this->xdebug('no current complexType to set extensionBase');
+ }
+ break;
+ case 'import':
+ if (isset($attrs['schemaLocation'])) {
+ $this->xdebug('import namespace ' . $attrs['namespace'] . ' from ' . $attrs['schemaLocation']);
+ $this->imports[$attrs['namespace']][] = array('location' => $attrs['schemaLocation'], 'loaded' => false);
+ } else {
+ $this->xdebug('import namespace ' . $attrs['namespace']);
+ $this->imports[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
+ if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
+ $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace'];
+ }
+ }
+ break;
+ case 'include':
+ if (isset($attrs['schemaLocation'])) {
+ $this->xdebug('include into namespace ' . $this->schemaTargetNamespace . ' from ' . $attrs['schemaLocation']);
+ $this->imports[$this->schemaTargetNamespace][] = array('location' => $attrs['schemaLocation'], 'loaded' => false);
+ } else {
+ $this->xdebug('ignoring invalid XML Schema construct: include without schemaLocation attribute');
+ }
+ break;
+ case 'list': // simpleType value list
+ $this->xdebug("do nothing for element $name");
+ break;
+ case 'restriction': // simpleType, simpleContent or complexContent value restriction
+ $this->xdebug('restriction ' . $attrs['base']);
+ if($this->currentSimpleType){
+ $this->simpleTypes[$this->currentSimpleType]['type'] = $attrs['base'];
+ } elseif($this->currentComplexType){
+ $this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base'];
+ if(strstr($attrs['base'],':') == ':Array'){
+ $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
+ }
+ }
+ break;
+ case 'schema':
+ $this->schemaInfo = $attrs;
+ $this->schemaInfo['schemaVersion'] = $this->getNamespaceFromPrefix($prefix);
+ if (isset($attrs['targetNamespace'])) {
+ $this->schemaTargetNamespace = $attrs['targetNamespace'];
+ }
+ if (!isset($attrs['elementFormDefault'])) {
+ $this->schemaInfo['elementFormDefault'] = 'unqualified';
+ }
+ if (!isset($attrs['attributeFormDefault'])) {
+ $this->schemaInfo['attributeFormDefault'] = 'unqualified';
+ }
+ break;
+ case 'simpleContent': // (optional) content for a complexType
+ if ($this->currentComplexType) { // This should *always* be
+ $this->complexTypes[$this->currentComplexType]['simpleContent'] = 'true';
+ } else {
+ $this->xdebug("do nothing for element $name because there is no current complexType");
+ }
+ break;
+ case 'simpleType':
+ array_push($this->simpleTypeStack, $this->currentSimpleType);
+ if(isset($attrs['name'])){
+ $this->xdebug("processing simpleType for name " . $attrs['name']);
+ $this->currentSimpleType = $attrs['name'];
+ $this->simpleTypes[ $attrs['name'] ] = $attrs;
+ $this->simpleTypes[ $attrs['name'] ]['typeClass'] = 'simpleType';
+ $this->simpleTypes[ $attrs['name'] ]['phpType'] = 'scalar';
+ } else {
+ $name = $this->CreateTypeName($this->currentComplexType . '_' . $this->currentElement);
+ $this->xdebug('processing unnamed simpleType for element ' . $this->currentElement . ' named ' . $name);
+ $this->currentSimpleType = $name;
+ //$this->currentElement = false;
+ $this->simpleTypes[$this->currentSimpleType] = $attrs;
+ $this->simpleTypes[$this->currentSimpleType]['phpType'] = 'scalar';
+ }
+ break;
+ case 'union': // simpleType type list
+ $this->xdebug("do nothing for element $name");
+ break;
+ default:
+ $this->xdebug("do not have any logic to process element $name");
+ }
+ }
+
+ /**
+ * end-element handler
+ *
+ * @param string $parser XML parser object
+ * @param string $name element name
+ * @access private
+ */
+ function schemaEndElement($parser, $name) {
+ // bring depth down a notch
+ $this->depth--;
+ // position of current element is equal to the last value left in depth_array for my depth
+ if(isset($this->depth_array[$this->depth])){
+ $pos = $this->depth_array[$this->depth];
+ }
+ // get element prefix
+ if ($prefix = $this->getPrefix($name)){
+ // get unqualified name
+ $name = $this->getLocalPart($name);
+ } else {
+ $prefix = '';
+ }
+ // move on...
+ if($name == 'complexType'){
+ $this->xdebug('done processing complexType ' . ($this->currentComplexType ? $this->currentComplexType : '(unknown)'));
+ $this->xdebug($this->varDump($this->complexTypes[$this->currentComplexType]));
+ $this->currentComplexType = array_pop($this->complexTypeStack);
+ //$this->currentElement = false;
+ }
+ if($name == 'element'){
+ $this->xdebug('done processing element ' . ($this->currentElement ? $this->currentElement : '(unknown)'));
+ $this->currentElement = array_pop($this->elementStack);
+ }
+ if($name == 'simpleType'){
+ $this->xdebug('done processing simpleType ' . ($this->currentSimpleType ? $this->currentSimpleType : '(unknown)'));
+ $this->xdebug($this->varDump($this->simpleTypes[$this->currentSimpleType]));
+ $this->currentSimpleType = array_pop($this->simpleTypeStack);
+ }
+ }
+
+ /**
+ * element content handler
+ *
+ * @param string $parser XML parser object
+ * @param string $data element content
+ * @access private
+ */
+ function schemaCharacterData($parser, $data){
+ $pos = $this->depth_array[$this->depth - 1];
+ $this->message[$pos]['cdata'] .= $data;
+ }
+
+ /**
+ * serialize the schema
+ *
+ * @access public
+ */
+ function serializeSchema(){
+
+ $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion);
+ $xml = '';
+ // imports
+ if (sizeof($this->imports) > 0) {
+ foreach($this->imports as $ns => $list) {
+ foreach ($list as $ii) {
+ if ($ii['location'] != '') {
+ $xml .= " <$schemaPrefix:import location=\"" . $ii['location'] . '" namespace="' . $ns . "\" />\n";
+ } else {
+ $xml .= " <$schemaPrefix:import namespace=\"" . $ns . "\" />\n";
+ }
+ }
+ }
+ }
+ // complex types
+ foreach($this->complexTypes as $typeName => $attrs){
+ $contentStr = '';
+ // serialize child elements
+ if(isset($attrs['elements']) && (count($attrs['elements']) > 0)){
+ foreach($attrs['elements'] as $element => $eParts){
+ if(isset($eParts['ref'])){
+ $contentStr .= " <$schemaPrefix:element ref=\"$element\"/>\n";
+ } else {
+ $contentStr .= " <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\"";
+ foreach ($eParts as $aName => $aValue) {
+ // handle, e.g., abstract, default, form, minOccurs, maxOccurs, nillable
+ if ($aName != 'name' && $aName != 'type') {
+ $contentStr .= " $aName=\"$aValue\"";
+ }
+ }
+ $contentStr .= "/>\n";
+ }
+ }
+ // compositor wraps elements
+ if (isset($attrs['compositor']) && ($attrs['compositor'] != '')) {
+ $contentStr = " <$schemaPrefix:$attrs[compositor]>\n".$contentStr." </$schemaPrefix:$attrs[compositor]>\n";
+ }
+ }
+ // attributes
+ if(isset($attrs['attrs']) && (count($attrs['attrs']) >= 1)){
+ foreach($attrs['attrs'] as $attr => $aParts){
+ $contentStr .= " <$schemaPrefix:attribute";
+ foreach ($aParts as $a => $v) {
+ if ($a == 'ref' || $a == 'type') {
+ $contentStr .= " $a=\"".$this->contractQName($v).'"';
+ } elseif ($a == 'http://schemas.xmlsoap.org/wsdl/:arrayType') {
+ $this->usedNamespaces['wsdl'] = $this->namespaces['wsdl'];
+ $contentStr .= ' wsdl:arrayType="'.$this->contractQName($v).'"';
+ } else {
+ $contentStr .= " $a=\"$v\"";
+ }
+ }
+ $contentStr .= "/>\n";
+ }
+ }
+ // if restriction
+ if (isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != ''){
+ $contentStr = " <$schemaPrefix:restriction base=\"".$this->contractQName($attrs['restrictionBase'])."\">\n".$contentStr." </$schemaPrefix:restriction>\n";
+ // complex or simple content
+ if ((isset($attrs['elements']) && count($attrs['elements']) > 0) || (isset($attrs['attrs']) && count($attrs['attrs']) > 0)){
+ $contentStr = " <$schemaPrefix:complexContent>\n".$contentStr." </$schemaPrefix:complexContent>\n";
+ }
+ }
+ // finalize complex type
+ if($contentStr != ''){
+ $contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n".$contentStr." </$schemaPrefix:complexType>\n";
+ } else {
+ $contentStr = " <$schemaPrefix:complexType name=\"$typeName\"/>\n";
+ }
+ $xml .= $contentStr;
+ }
+ // simple types
+ if(isset($this->simpleTypes) && count($this->simpleTypes) > 0){
+ foreach($this->simpleTypes as $typeName => $eParts){
+ $xml .= " <$schemaPrefix:simpleType name=\"$typeName\">\n <$schemaPrefix:restriction base=\"".$this->contractQName($eParts['type'])."\">\n";
+ if (isset($eParts['enumeration'])) {
+ foreach ($eParts['enumeration'] as $e) {
+ $xml .= " <$schemaPrefix:enumeration value=\"$e\"/>\n";
+ }
+ }
+ $xml .= " </$schemaPrefix:restriction>\n </$schemaPrefix:simpleType>";
+ }
+ }
+ // elements
+ if(isset($this->elements) && count($this->elements) > 0){
+ foreach($this->elements as $element => $eParts){
+ $xml .= " <$schemaPrefix:element name=\"$element\" type=\"".$this->contractQName($eParts['type'])."\"/>\n";
+ }
+ }
+ // attributes
+ if(isset($this->attributes) && count($this->attributes) > 0){
+ foreach($this->attributes as $attr => $aParts){
+ $xml .= " <$schemaPrefix:attribute name=\"$attr\" type=\"".$this->contractQName($aParts['type'])."\"\n/>";
+ }
+ }
+ // finish 'er up
+ $attr = '';
+ foreach ($this->schemaInfo as $k => $v) {
+ if ($k == 'elementFormDefault' || $k == 'attributeFormDefault') {
+ $attr .= " $k=\"$v\"";
+ }
+ }
+ $el = "<$schemaPrefix:schema$attr targetNamespace=\"$this->schemaTargetNamespace\"\n";
+ foreach (array_diff($this->usedNamespaces, $this->enclosingNamespaces) as $nsp => $ns) {
+ $el .= " xmlns:$nsp=\"$ns\"";
+ }
+ $xml = $el . ">\n".$xml."</$schemaPrefix:schema>\n";
+ return $xml;
+ }
+
+ /**
+ * adds debug data to the clas level debug string
+ *
+ * @param string $string debug data
+ * @access private
+ */
+ function xdebug($string){
+ $this->debug('<' . $this->schemaTargetNamespace . '> '.$string);
+ }
+
+ /**
+ * get the PHP type of a user defined type in the schema
+ * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays
+ * returns false if no type exists, or not w/ the given namespace
+ * else returns a string that is either a native php type, or 'struct'
+ *
+ * @param string $type name of defined type
+ * @param string $ns namespace of type
+ * @return mixed
+ * @access public
+ * @deprecated
+ */
+ function getPHPType($type,$ns){
+ if(isset($this->typemap[$ns][$type])){
+ //print "found type '$type' and ns $ns in typemap<br>";
+ return $this->typemap[$ns][$type];
+ } elseif(isset($this->complexTypes[$type])){
+ //print "getting type '$type' and ns $ns from complexTypes array<br>";
+ return $this->complexTypes[$type]['phpType'];
+ }
+ return false;
+ }
+
+ /**
+ * returns an associative array of information about a given type
+ * returns false if no type exists by the given name
+ *
+ * For a complexType typeDef = array(
+ * 'restrictionBase' => '',
+ * 'phpType' => '',
+ * 'compositor' => '(sequence|all)',
+ * 'elements' => array(), // refs to elements array
+ * 'attrs' => array() // refs to attributes array
+ * ... and so on (see addComplexType)
+ * )
+ *
+ * For simpleType or element, the array has different keys.
+ *
+ * @param string $type
+ * @return mixed
+ * @access public
+ * @see addComplexType
+ * @see addSimpleType
+ * @see addElement
+ */
+ function getTypeDef($type){
+ //$this->debug("in getTypeDef for type $type");
+ if (substr($type, -1) == '^') {
+ $is_element = 1;
+ $type = substr($type, 0, -1);
+ } else {
+ $is_element = 0;
+ }
+
+ if((! $is_element) && isset($this->complexTypes[$type])){
+ $this->xdebug("in getTypeDef, found complexType $type");
+ return $this->complexTypes[$type];
+ } elseif((! $is_element) && isset($this->simpleTypes[$type])){
+ $this->xdebug("in getTypeDef, found simpleType $type");
+ if (!isset($this->simpleTypes[$type]['phpType'])) {
+ // get info for type to tack onto the simple type
+ // TODO: can this ever really apply (i.e. what is a simpleType really?)
+ $uqType = substr($this->simpleTypes[$type]['type'], strrpos($this->simpleTypes[$type]['type'], ':') + 1);
+ $ns = substr($this->simpleTypes[$type]['type'], 0, strrpos($this->simpleTypes[$type]['type'], ':'));
+ $etype = $this->getTypeDef($uqType);
+ if ($etype) {
+ $this->xdebug("in getTypeDef, found type for simpleType $type:");
+ $this->xdebug($this->varDump($etype));
+ if (isset($etype['phpType'])) {
+ $this->simpleTypes[$type]['phpType'] = $etype['phpType'];
+ }
+ if (isset($etype['elements'])) {
+ $this->simpleTypes[$type]['elements'] = $etype['elements'];
+ }
+ }
+ }
+ return $this->simpleTypes[$type];
+ } elseif(isset($this->elements[$type])){
+ $this->xdebug("in getTypeDef, found element $type");
+ if (!isset($this->elements[$type]['phpType'])) {
+ // get info for type to tack onto the element
+ $uqType = substr($this->elements[$type]['type'], strrpos($this->elements[$type]['type'], ':') + 1);
+ $ns = substr($this->elements[$type]['type'], 0, strrpos($this->elements[$type]['type'], ':'));
+ $etype = $this->getTypeDef($uqType);
+ if ($etype) {
+ $this->xdebug("in getTypeDef, found type for element $type:");
+ $this->xdebug($this->varDump($etype));
+ if (isset($etype['phpType'])) {
+ $this->elements[$type]['phpType'] = $etype['phpType'];
+ }
+ if (isset($etype['elements'])) {
+ $this->elements[$type]['elements'] = $etype['elements'];
+ }
+ if (isset($etype['extensionBase'])) {
+ $this->elements[$type]['extensionBase'] = $etype['extensionBase'];
+ }
+ } elseif ($ns == 'http://www.w3.org/2001/XMLSchema') {
+ $this->xdebug("in getTypeDef, element $type is an XSD type");
+ $this->elements[$type]['phpType'] = 'scalar';
+ }
+ }
+ return $this->elements[$type];
+ } elseif(isset($this->attributes[$type])){
+ $this->xdebug("in getTypeDef, found attribute $type");
+ return $this->attributes[$type];
+ } elseif (preg_match('/_ContainedType$/', $type)) {
+ $this->xdebug("in getTypeDef, have an untyped element $type");
+ $typeDef['typeClass'] = 'simpleType';
+ $typeDef['phpType'] = 'scalar';
+ $typeDef['type'] = 'http://www.w3.org/2001/XMLSchema:string';
+ return $typeDef;
+ }
+ $this->xdebug("in getTypeDef, did not find $type");
+ return false;
+ }
+
+ /**
+ * returns a sample serialization of a given type, or false if no type by the given name
+ *
+ * @param string $type name of type
+ * @return mixed
+ * @access public
+ * @deprecated
+ */
+ function serializeTypeDef($type){
+ //print "in sTD() for type $type<br>";
+ if($typeDef = $this->getTypeDef($type)){
+ $str .= '<'.$type;
+ if(is_array($typeDef['attrs'])){
+ foreach($typeDef['attrs'] as $attName => $data){
+ $str .= " $attName=\"{type = ".$data['type']."}\"";
+ }
+ }
+ $str .= " xmlns=\"".$this->schema['targetNamespace']."\"";
+ if(count($typeDef['elements']) > 0){
+ $str .= ">";
+ foreach($typeDef['elements'] as $element => $eData){
+ $str .= $this->serializeTypeDef($element);
+ }
+ $str .= "</$type>";
+ } elseif($typeDef['typeClass'] == 'element') {
+ $str .= "></$type>";
+ } else {
+ $str .= "/>";
+ }
+ return $str;
+ }
+ return false;
+ }
+
+ /**
+ * returns HTML form elements that allow a user
+ * to enter values for creating an instance of the given type.
+ *
+ * @param string $name name for type instance
+ * @param string $type name of type
+ * @return string
+ * @access public
+ * @deprecated
+ */
+ function typeToForm($name,$type){
+ // get typedef
+ if($typeDef = $this->getTypeDef($type)){
+ // if struct
+ if($typeDef['phpType'] == 'struct'){
+ $buffer .= '<table>';
+ foreach($typeDef['elements'] as $child => $childDef){
+ $buffer .= "
+ <tr><td align='right'>$childDef[name] (type: ".$this->getLocalPart($childDef['type'])."):</td>
+ <td><input type='text' name='parameters[".$name."][$childDef[name]]'></td></tr>";
+ }
+ $buffer .= '</table>';
+ // if array
+ } elseif($typeDef['phpType'] == 'array'){
+ $buffer .= '<table>';
+ for($i=0;$i < 3; $i++){
+ $buffer .= "
+ <tr><td align='right'>array item (type: $typeDef[arrayType]):</td>
+ <td><input type='text' name='parameters[".$name."][]'></td></tr>";
+ }
+ $buffer .= '</table>';
+ // if scalar
+ } else {
+ $buffer .= "<input type='text' name='parameters[$name]'>";
+ }
+ } else {
+ $buffer .= "<input type='text' name='parameters[$name]'>";
+ }
+ return $buffer;
+ }
+
+ /**
+ * adds a complex type to the schema
+ *
+ * example: array
+ *
+ * addType(
+ * 'ArrayOfstring',
+ * 'complexType',
+ * 'array',
+ * '',
+ * 'SOAP-ENC:Array',
+ * array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'),
+ * 'xsd:string'
+ * );
+ *
+ * example: PHP associative array ( SOAP Struct )
+ *
+ * addType(
+ * 'SOAPStruct',
+ * 'complexType',
+ * 'struct',
+ * 'all',
+ * array('myVar'=> array('name'=>'myVar','type'=>'string')
+ * );
+ *
+ * @param name
+ * @param typeClass (complexType|simpleType|attribute)
+ * @param phpType: currently supported are array and struct (php assoc array)
+ * @param compositor (all|sequence|choice)
+ * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
+ * @param elements = array ( name = array(name=>'',type=>'') )
+ * @param attrs = array(
+ * array(
+ * 'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType",
+ * "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]"
+ * )
+ * )
+ * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string)
+ * @access public
+ * @see getTypeDef
+ */
+ function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType=''){
+ $this->complexTypes[$name] = array(
+ 'name' => $name,
+ 'typeClass' => $typeClass,
+ 'phpType' => $phpType,
+ 'compositor'=> $compositor,
+ 'restrictionBase' => $restrictionBase,
+ 'elements' => $elements,
+ 'attrs' => $attrs,
+ 'arrayType' => $arrayType
+ );
+
+ $this->xdebug("addComplexType $name:");
+ $this->appendDebug($this->varDump($this->complexTypes[$name]));
+ }
+
+ /**
+ * adds a simple type to the schema
+ *
+ * @param string $name
+ * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
+ * @param string $typeClass (should always be simpleType)
+ * @param string $phpType (should always be scalar)
+ * @param array $enumeration array of values
+ * @access public
+ * @see nusoap_xmlschema
+ * @see getTypeDef
+ */
+ function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) {
+ $this->simpleTypes[$name] = array(
+ 'name' => $name,
+ 'typeClass' => $typeClass,
+ 'phpType' => $phpType,
+ 'type' => $restrictionBase,
+ 'enumeration' => $enumeration
+ );
+
+ $this->xdebug("addSimpleType $name:");
+ $this->appendDebug($this->varDump($this->simpleTypes[$name]));
+ }
+
+ /**
+ * adds an element to the schema
+ *
+ * @param array $attrs attributes that must include name and type
+ * @see nusoap_xmlschema
+ * @access public
+ */
+ function addElement($attrs) {
+ if (! $this->getPrefix($attrs['type'])) {
+ $attrs['type'] = $this->schemaTargetNamespace . ':' . $attrs['type'];
+ }
+ $this->elements[ $attrs['name'] ] = $attrs;
+ $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
+
+ $this->xdebug("addElement " . $attrs['name']);
+ $this->appendDebug($this->varDump($this->elements[ $attrs['name'] ]));
+ }
+}
+
+/**
+ * Backward compatibility
+ */
+class XMLSchema extends nusoap_xmlschema {
+}
+
+?><?php
+
+
+
+/**
+* For creating serializable abstractions of native PHP types. This class
+* allows element name/namespace, XSD type, and XML attributes to be
+* associated with a value. This is extremely useful when WSDL is not
+* used, but is also useful when WSDL is used with polymorphic types, including
+* xsd:anyType and user-defined types.
+*
+* @author Dietrich Ayala <dietrich@ganx4.com>
+* @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
+* @access public
+*/
+class soapval extends nusoap_base {
+ /**
+ * The XML element name
+ *
+ * @var string
+ * @access private
+ */
+ var $name;
+ /**
+ * The XML type name (string or false)
+ *
+ * @var mixed
+ * @access private
+ */
+ var $type;
+ /**
+ * The PHP value
+ *
+ * @var mixed
+ * @access private
+ */
+ var $value;
+ /**
+ * The XML element namespace (string or false)
+ *
+ * @var mixed
+ * @access private
+ */
+ var $element_ns;
+ /**
+ * The XML type namespace (string or false)
+ *
+ * @var mixed
+ * @access private
+ */
+ var $type_ns;
+ /**
+ * The XML element attributes (array or false)
+ *
+ * @var mixed
+ * @access private
+ */
+ var $attributes;
+
+ /**
+ * constructor
+ *
+ * @param string $name optional name
+ * @param mixed $type optional type name
+ * @param mixed $value optional value
+ * @param mixed $element_ns optional namespace of value
+ * @param mixed $type_ns optional namespace of type
+ * @param mixed $attributes associative array of attributes to add to element serialization
+ * @access public
+ */
+ function __construct($name='soapval',$type=false,$value=-1,$element_ns=false,$type_ns=false,$attributes=false) {
+ parent::__construct();
+ $this->name = $name;
+ $this->type = $type;
+ $this->value = $value;
+ $this->element_ns = $element_ns;
+ $this->type_ns = $type_ns;
+ $this->attributes = $attributes;
+ }
+
+ /**
+ * return serialized value
+ *
+ * @param string $use The WSDL use value (encoded|literal)
+ * @return string XML data
+ * @access public
+ */
+ function serialize($use='encoded') {
+ return $this->serialize_val($this->value, $this->name, $this->type, $this->element_ns, $this->type_ns, $this->attributes, $use, true);
+ }
+
+ /**
+ * decodes a soapval object into a PHP native type
+ *
+ * @return mixed
+ * @access public
+ */
+ function decode(){
+ return $this->value;
+ }
+}
+
+
+
+?><?php
+
+
+
+/**
+* transport class for sending/receiving data via HTTP and HTTPS
+* NOTE: PHP must be compiled with the CURL extension for HTTPS support
+*
+* @author Dietrich Ayala <dietrich@ganx4.com>
+* @author Scott Nichol <snichol@users.sourceforge.net>
+* @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
+* @access public
+*/
+class soap_transport_http extends nusoap_base {
+
+ var $url = '';
+ var $uri = '';
+ var $digest_uri = '';
+ var $scheme = '';
+ var $host = '';
+ var $port = '';
+ var $path = '';
+ var $request_method = 'POST';
+ var $protocol_version = '1.0';
+ var $encoding = '';
+ var $outgoing_headers = array();
+ var $incoming_headers = array();
+ var $incoming_cookies = array();
+ var $outgoing_payload = '';
+ var $incoming_payload = '';
+ var $response_status_line; // HTTP response status line
+ var $useSOAPAction = true;
+ var $persistentConnection = false;
+ var $ch = false; // cURL handle
+ var $ch_options = array(); // cURL custom options
+ var $use_curl = false; // force cURL use
+ var $proxy = null; // proxy information (associative array)
+ var $username = '';
+ var $password = '';
+ var $authtype = '';
+ var $digestRequest = array();
+ var $certRequest = array(); // keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional)
+ // cainfofile: certificate authority file, e.g. '$pathToPemFiles/rootca.pem'
+ // sslcertfile: SSL certificate file, e.g. '$pathToPemFiles/mycert.pem'
+ // sslkeyfile: SSL key file, e.g. '$pathToPemFiles/mykey.pem'
+ // passphrase: SSL key password/passphrase
+ // certpassword: SSL certificate password
+ // verifypeer: default is 1
+ // verifyhost: default is 1
+
+ /**
+ * constructor
+ *
+ * @param string $url The URL to which to connect
+ * @param array $curl_options User-specified cURL options
+ * @param boolean $use_curl Whether to try to force cURL use
+ * @access public
+ */
+ function __construct($url, $curl_options = NULL, $use_curl = false){
+ parent::__construct();
+ $this->debug("ctor url=$url use_curl=$use_curl curl_options:");
+ $this->appendDebug($this->varDump($curl_options));
+ $this->setURL($url);
+ if (is_array($curl_options)) {
+ $this->ch_options = $curl_options;
+ }
+ $this->use_curl = $use_curl;
+ preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev);
+ $this->setHeader('User-Agent', $this->title.'/'.$this->version.' ('.$rev[1].')');
+ }
+
+ /**
+ * sets a cURL option
+ *
+ * @param mixed $option The cURL option (always integer?)
+ * @param mixed $value The cURL option value
+ * @access private
+ */
+ function setCurlOption($option, $value) {
+ $this->debug("setCurlOption option=$option, value=");
+ $this->appendDebug($this->varDump($value));
+ curl_setopt($this->ch, $option, $value);
+ }
+
+ /**
+ * sets an HTTP header
+ *
+ * @param string $name The name of the header
+ * @param string $value The value of the header
+ * @access private
+ */
+ function setHeader($name, $value) {
+ $this->outgoing_headers[$name] = $value;
+ $this->debug("set header $name: $value");
+ }
+
+ /**
+ * unsets an HTTP header
+ *
+ * @param string $name The name of the header
+ * @access private
+ */
+ function unsetHeader($name) {
+ if (isset($this->outgoing_headers[$name])) {
+ $this->debug("unset header $name");
+ unset($this->outgoing_headers[$name]);
+ }
+ }
+
+ /**
+ * sets the URL to which to connect
+ *
+ * @param string $url The URL to which to connect
+ * @access private
+ */
+ function setURL($url) {
+ $this->url = $url;
+
+ $u = parse_url($url);
+ foreach($u as $k => $v){
+ $this->debug("parsed URL $k = $v");
+ $this->$k = $v;
+ }
+
+ // add any GET params to path
+ if(isset($u['query']) && $u['query'] != ''){
+ $this->path .= '?' . $u['query'];
+ }
+
+ // set default port
+ if(!isset($u['port'])){
+ if($u['scheme'] == 'https'){
+ $this->port = 443;
+ } else {
+ $this->port = 80;
+ }
+ }
+
+ $this->uri = $this->path;
+ $this->digest_uri = $this->uri;
+
+ // build headers
+ if (!isset($u['port'])) {
+ $this->setHeader('Host', $this->host);
+ } else {
+ $this->setHeader('Host', $this->host.':'.$this->port);
+ }
+
+ if (isset($u['user']) && $u['user'] != '') {
+ $this->setCredentials(urldecode($u['user']), isset($u['pass']) ? urldecode($u['pass']) : '');
+ }
+ }
+
+ /**
+ * gets the I/O method to use
+ *
+ * @return string I/O method to use (socket|curl|unknown)
+ * @access private
+ */
+ function io_method() {
+ if ($this->use_curl || ($this->scheme == 'https') || ($this->scheme == 'http' && $this->authtype == 'ntlm') || ($this->scheme == 'http' && is_array($this->proxy) && $this->proxy['authtype'] == 'ntlm'))
+ return 'curl';
+ if (($this->scheme == 'http' || $this->scheme == 'ssl') && $this->authtype != 'ntlm' && (!is_array($this->proxy) || $this->proxy['authtype'] != 'ntlm'))
+ return 'socket';
+ return 'unknown';
+ }
+
+ /**
+ * establish an HTTP connection
+ *
+ * @param integer $timeout set connection timeout in seconds
+ * @param integer $response_timeout set response timeout in seconds
+ * @return boolean true if connected, false if not
+ * @access private
+ */
+ function connect($connection_timeout=0,$response_timeout=30){
+ // For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like
+ // "regular" socket.
+ // TODO: disabled for now because OpenSSL must be *compiled* in (not just
+ // loaded), and until PHP5 stream_get_wrappers is not available.
+// if ($this->scheme == 'https') {
+// if (version_compare(phpversion(), '4.3.0') >= 0) {
+// if (extension_loaded('openssl')) {
+// $this->scheme = 'ssl';
+// $this->debug('Using SSL over OpenSSL');
+// }
+// }
+// }
+ $this->debug("connect connection_timeout $connection_timeout, response_timeout $response_timeout, scheme $this->scheme, host $this->host, port $this->port");
+ if ($this->io_method() == 'socket') {
+ if (!is_array($this->proxy)) {
+ $host = $this->host;
+ $port = $this->port;
+ } else {
+ $host = $this->proxy['host'];
+ $port = $this->proxy['port'];
+ }
+
+ // use persistent connection
+ if($this->persistentConnection && isset($this->fp) && is_resource($this->fp)){
+ if (!feof($this->fp)) {
+ $this->debug('Re-use persistent connection');
+ return true;
+ }
+ fclose($this->fp);
+ $this->debug('Closed persistent connection at EOF');
+ }
+
+ // munge host if using OpenSSL
+ if ($this->scheme == 'ssl') {
+ $host = 'ssl://' . $host;
+ }
+ $this->debug('calling fsockopen with host ' . $host . ' connection_timeout ' . $connection_timeout);
+
+ // open socket
+ if($connection_timeout > 0){
+ $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str, $connection_timeout);
+ } else {
+ $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str);
+ }
+
+ // test pointer
+ if(!$this->fp) {
+ $msg = 'Couldn\'t open socket connection to server ' . $this->url;
+ if ($this->errno) {
+ $msg .= ', Error ('.$this->errno.'): '.$this->error_str;
+ } else {
+ $msg .= ' prior to connect(). This is often a problem looking up the host name.';
+ }
+ $this->debug($msg);
+ $this->setError($msg);
+ return false;
+ }
+
+ // set response timeout
+ $this->debug('set response timeout to ' . $response_timeout);
+ socket_set_timeout( $this->fp, $response_timeout);
+
+ $this->debug('socket connected');
+ return true;
+ } else if ($this->io_method() == 'curl') {
+ if (!extension_loaded('curl')) {
+// $this->setError('cURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
+ $this->setError('The PHP cURL Extension is required for HTTPS or NLTM. You will need to re-build or update your PHP to include cURL or change php.ini to load the PHP cURL extension.');
+ return false;
+ }
+ // Avoid warnings when PHP does not have these options
+ if (defined('CURLOPT_CONNECTIONTIMEOUT'))
+ $CURLOPT_CONNECTIONTIMEOUT = CURLOPT_CONNECTIONTIMEOUT;
+ else
+ $CURLOPT_CONNECTIONTIMEOUT = 78;
+ if (defined('CURLOPT_HTTPAUTH'))
+ $CURLOPT_HTTPAUTH = CURLOPT_HTTPAUTH;
+ else
+ $CURLOPT_HTTPAUTH = 107;
+ if (defined('CURLOPT_PROXYAUTH'))
+ $CURLOPT_PROXYAUTH = CURLOPT_PROXYAUTH;
+ else
+ $CURLOPT_PROXYAUTH = 111;
+ if (defined('CURLAUTH_BASIC'))
+ $CURLAUTH_BASIC = CURLAUTH_BASIC;
+ else
+ $CURLAUTH_BASIC = 1;
+ if (defined('CURLAUTH_DIGEST'))
+ $CURLAUTH_DIGEST = CURLAUTH_DIGEST;
+ else
+ $CURLAUTH_DIGEST = 2;
+ if (defined('CURLAUTH_NTLM'))
+ $CURLAUTH_NTLM = CURLAUTH_NTLM;
+ else
+ $CURLAUTH_NTLM = 8;
+
+ $this->debug('connect using cURL');
+ // init CURL
+ $this->ch = curl_init();
+ // set url
+ $hostURL = ($this->port != '') ? "$this->scheme://$this->host:$this->port" : "$this->scheme://$this->host";
+ // add path
+ $hostURL .= $this->path;
+ $this->setCurlOption(CURLOPT_URL, $hostURL);
+ // follow location headers (re-directs)
+ $ini_safemode_chk = false;
+ if (version_compare(PHP_VERSION, '5.3.0', '<')) {
+ $ini_safemode_chk = ini_get('safe_mode');
+ }
+ if ( $ini_safemode_chk || ini_get('open_basedir')) {
+ $this->debug('safe_mode or open_basedir set, so do not set CURLOPT_FOLLOWLOCATION');
+ $this->debug('safe_mode = ');
+ $this->appendDebug($this->varDump($ini_safemode_chk));
+ $this->debug('open_basedir = ');
+ $this->appendDebug($this->varDump(ini_get('open_basedir')));
+ } else {
+ $this->setCurlOption(CURLOPT_FOLLOWLOCATION, 1);
+ }
+ // ask for headers in the response output
+ $this->setCurlOption(CURLOPT_HEADER, 1);
+ // ask for the response output as the return value
+ $this->setCurlOption(CURLOPT_RETURNTRANSFER, 1);
+ // encode
+ // We manage this ourselves through headers and encoding
+// if(function_exists('gzuncompress')){
+// $this->setCurlOption(CURLOPT_ENCODING, 'deflate');
+// }
+ // persistent connection
+ if ($this->persistentConnection) {
+ // I believe the following comment is now bogus, having applied to
+ // the code when it used CURLOPT_CUSTOMREQUEST to send the request.
+ // The way we send data, we cannot use persistent connections, since
+ // there will be some "junk" at the end of our request.
+ //$this->setCurlOption(CURL_HTTP_VERSION_1_1, true);
+ $this->persistentConnection = false;
+ $this->setHeader('Connection', 'close');
+ }
+ // set timeouts
+ if ($connection_timeout != 0) {
+ $this->setCurlOption($CURLOPT_CONNECTIONTIMEOUT, $connection_timeout);
+ }
+ if ($response_timeout != 0) {
+ $this->setCurlOption(CURLOPT_TIMEOUT, $response_timeout);
+ }
+
+ if ($this->scheme == 'https') {
+ $this->debug('set cURL SSL verify options');
+ // recent versions of cURL turn on peer/host checking by default,
+ // while PHP binaries are not compiled with a default location for the
+ // CA cert bundle, so disable peer/host checking.
+ //$this->setCurlOption(CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt');
+ $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 0);
+ $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 0);
+
+ // support client certificates (thanks Tobias Boes, Doug Anarino, Eryan Ariobowo)
+ if ($this->authtype == 'certificate') {
+ $this->debug('set cURL certificate options');
+ if (isset($this->certRequest['cainfofile'])) {
+ $this->setCurlOption(CURLOPT_CAINFO, $this->certRequest['cainfofile']);
+ }
+ if (isset($this->certRequest['verifypeer'])) {
+ $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, $this->certRequest['verifypeer']);
+ } else {
+ $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 1);
+ }
+ if (isset($this->certRequest['verifyhost'])) {
+ $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, $this->certRequest['verifyhost']);
+ } else {
+ $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 1);
+ }
+ if (isset($this->certRequest['sslcertfile'])) {
+ $this->setCurlOption(CURLOPT_SSLCERT, $this->certRequest['sslcertfile']);
+ }
+ if (isset($this->certRequest['sslkeyfile'])) {
+ $this->setCurlOption(CURLOPT_SSLKEY, $this->certRequest['sslkeyfile']);
+ }
+ if (isset($this->certRequest['passphrase'])) {
+ $this->setCurlOption(CURLOPT_SSLKEYPASSWD, $this->certRequest['passphrase']);
+ }
+ if (isset($this->certRequest['certpassword'])) {
+ $this->setCurlOption(CURLOPT_SSLCERTPASSWD, $this->certRequest['certpassword']);
+ }
+ }
+ }
+ if ($this->authtype && ($this->authtype != 'certificate')) {
+ if ($this->username) {
+ $this->debug('set cURL username/password');
+ $this->setCurlOption(CURLOPT_USERPWD, "$this->username:$this->password");
+ }
+ if ($this->authtype == 'basic') {
+ $this->debug('set cURL for Basic authentication');
+ $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_BASIC);
+ }
+ if ($this->authtype == 'digest') {
+ $this->debug('set cURL for digest authentication');
+ $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_DIGEST);
+ }
+ if ($this->authtype == 'ntlm') {
+ $this->debug('set cURL for NTLM authentication');
+ $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_NTLM);
+ }
+ }
+ if (is_array($this->proxy)) {
+ $this->debug('set cURL proxy options');
+ if ($this->proxy['port'] != '') {
+ $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host'].':'.$this->proxy['port']);
+ } else {
+ $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host']);
+ }
+ if ($this->proxy['username'] || $this->proxy['password']) {
+ $this->debug('set cURL proxy authentication options');
+ $this->setCurlOption(CURLOPT_PROXYUSERPWD, $this->proxy['username'].':'.$this->proxy['password']);
+ if ($this->proxy['authtype'] == 'basic') {
+ $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_BASIC);
+ }
+ if ($this->proxy['authtype'] == 'ntlm') {
+ $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_NTLM);
+ }
+ }
+ }
+ $this->debug('cURL connection set up');
+ return true;
+ } else {
+ $this->setError('Unknown scheme ' . $this->scheme);
+ $this->debug('Unknown scheme ' . $this->scheme);
+ return false;
+ }
+ }
+
+ /**
+ * sends the SOAP request and gets the SOAP response via HTTP[S]
+ *
+ * @param string $data message data
+ * @param integer $timeout set connection timeout in seconds
+ * @param integer $response_timeout set response timeout in seconds
+ * @param array $cookies cookies to send
+ * @return string data
+ * @access public
+ */
+ function send($data, $timeout=0, $response_timeout=30, $cookies=NULL) {
+
+ $this->debug('entered send() with data of length: '.strlen($data));
+
+ $this->tryagain = true;
+ $tries = 0;
+ while ($this->tryagain) {
+ $this->tryagain = false;
+ if ($tries++ < 2) {
+ // make connnection
+ if (!$this->connect($timeout, $response_timeout)){
+ return false;
+ }
+
+ // send request
+ if (!$this->sendRequest($data, $cookies)){
+ return false;
+ }
+
+ // get response
+ $respdata = $this->getResponse();
+ } else {
+ $this->setError("Too many tries to get an OK response ($this->response_status_line)");
+ }
+ }
+ $this->debug('end of send()');
+ return $respdata;
+ }
+
+
+ /**
+ * sends the SOAP request and gets the SOAP response via HTTPS using CURL
+ *
+ * @param string $data message data
+ * @param integer $timeout set connection timeout in seconds
+ * @param integer $response_timeout set response timeout in seconds
+ * @param array $cookies cookies to send
+ * @return string data
+ * @access public
+ * @deprecated
+ */
+ function sendHTTPS($data, $timeout=0, $response_timeout=30, $cookies) {
+ return $this->send($data, $timeout, $response_timeout, $cookies);
+ }
+
+ /**
+ * if authenticating, set user credentials here
+ *
+ * @param string $username
+ * @param string $password
+ * @param string $authtype (basic|digest|certificate|ntlm)
+ * @param array $digestRequest (keys must be nonce, nc, realm, qop)
+ * @param array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
+ * @access public
+ */
+ function setCredentials($username, $password, $authtype = 'basic', $digestRequest = array(), $certRequest = array()) {
+ $this->debug("setCredentials username=$username authtype=$authtype digestRequest=");
+ $this->appendDebug($this->varDump($digestRequest));
+ $this->debug("certRequest=");
+ $this->appendDebug($this->varDump($certRequest));
+ // cf. RFC 2617
+ if ($authtype == 'basic') {
+ $this->setHeader('Authorization', 'Basic '.base64_encode(str_replace(':','',$username).':'.$password));
+ } elseif ($authtype == 'digest') {
+ if (isset($digestRequest['nonce'])) {
+ $digestRequest['nc'] = isset($digestRequest['nc']) ? $digestRequest['nc']++ : 1;
+
+ // calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html)
+
+ // A1 = unq(username-value) ":" unq(realm-value) ":" passwd
+ $A1 = $username. ':' . (isset($digestRequest['realm']) ? $digestRequest['realm'] : '') . ':' . $password;
+
+ // H(A1) = MD5(A1)
+ $HA1 = md5($A1);
+
+ // A2 = Method ":" digest-uri-value
+ $A2 = $this->request_method . ':' . $this->digest_uri;
+
+ // H(A2)
+ $HA2 = md5($A2);
+
+ // KD(secret, data) = H(concat(secret, ":", data))
+ // if qop == auth:
+ // request-digest = <"> < KD ( H(A1), unq(nonce-value)
+ // ":" nc-value
+ // ":" unq(cnonce-value)
+ // ":" unq(qop-value)
+ // ":" H(A2)
+ // ) <">
+ // if qop is missing,
+ // request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">
+
+ $unhashedDigest = '';
+ $nonce = isset($digestRequest['nonce']) ? $digestRequest['nonce'] : '';
+ $cnonce = $nonce;
+ if ($digestRequest['qop'] != '') {
+ $unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf("%08d", $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2;
+ } else {
+ $unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2;
+ }
+
+ $hashedDigest = md5($unhashedDigest);
+
+ $opaque = '';
+ if (isset($digestRequest['opaque'])) {
+ $opaque = ', opaque="' . $digestRequest['opaque'] . '"';
+ }
+
+ $this->setHeader('Authorization', 'Digest username="' . $username . '", realm="' . $digestRequest['realm'] . '", nonce="' . $nonce . '", uri="' . $this->digest_uri . $opaque . '", cnonce="' . $cnonce . '", nc=' . sprintf("%08x", $digestRequest['nc']) . ', qop="' . $digestRequest['qop'] . '", response="' . $hashedDigest . '"');
+ }
+ } elseif ($authtype == 'certificate') {
+ $this->certRequest = $certRequest;
+ $this->debug('Authorization header not set for certificate');
+ } elseif ($authtype == 'ntlm') {
+ // do nothing
+ $this->debug('Authorization header not set for ntlm');
+ }
+ $this->username = $username;
+ $this->password = $password;
+ $this->authtype = $authtype;
+ $this->digestRequest = $digestRequest;
+ }
+
+ /**
+ * set the soapaction value
+ *
+ * @param string $soapaction
+ * @access public
+ */
+ function setSOAPAction($soapaction) {
+ $this->setHeader('SOAPAction', '"' . $soapaction . '"');
+ }
+
+ /**
+ * use http encoding
+ *
+ * @param string $enc encoding style. supported values: gzip, deflate, or both
+ * @access public
+ */
+ function setEncoding($enc='gzip, deflate') {
+ if (function_exists('gzdeflate')) {
+ $this->protocol_version = '1.1';
+ $this->setHeader('Accept-Encoding', $enc);
+ if (!isset($this->outgoing_headers['Connection'])) {
+ $this->setHeader('Connection', 'close');
+ $this->persistentConnection = false;
+ }
+ // deprecated as of PHP 5.3.0
+ //set_magic_quotes_runtime(0);
+ $this->encoding = $enc;
+ }
+ }
+
+ /**
+ * set proxy info here
+ *
+ * @param string $proxyhost use an empty string to remove proxy
+ * @param string $proxyport
+ * @param string $proxyusername
+ * @param string $proxypassword
+ * @param string $proxyauthtype (basic|ntlm)
+ * @access public
+ */
+ function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 'basic') {
+ if ($proxyhost) {
+ $this->proxy = array(
+ 'host' => $proxyhost,
+ 'port' => $proxyport,
+ 'username' => $proxyusername,
+ 'password' => $proxypassword,
+ 'authtype' => $proxyauthtype
+ );
+ if ($proxyusername != '' && $proxypassword != '' && $proxyauthtype = 'basic') {
+ $this->setHeader('Proxy-Authorization', ' Basic '.base64_encode($proxyusername.':'.$proxypassword));
+ }
+ } else {
+ $this->debug('remove proxy');
+ $proxy = null;
+ unsetHeader('Proxy-Authorization');
+ }
+ }
+
+
+ /**
+ * Test if the given string starts with a header that is to be skipped.
+ * Skippable headers result from chunked transfer and proxy requests.
+ *
+ * @param string $data The string to check.
+ * @returns boolean Whether a skippable header was found.
+ * @access private
+ */
+ function isSkippableCurlHeader(&$data) {
+ $skipHeaders = array( 'HTTP/1.1 100',
+ 'HTTP/1.0 301',
+ 'HTTP/1.1 301',
+ 'HTTP/1.0 302',
+ 'HTTP/1.1 302',
+ 'HTTP/1.0 401',
+ 'HTTP/1.1 401',
+ 'HTTP/1.0 200 Connection established');
+ foreach ($skipHeaders as $hd) {
+ $prefix = substr($data, 0, strlen($hd));
+ if ($prefix == $hd) return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * decode a string that is encoded w/ "chunked' transfer encoding
+ * as defined in RFC2068 19.4.6
+ *
+ * @param string $buffer
+ * @param string $lb
+ * @returns string
+ * @access public
+ * @deprecated
+ */
+ function decodeChunked($buffer, $lb){
+ // length := 0
+ $length = 0;
+ $new = '';
+
+ // read chunk-size, chunk-extension (if any) and CRLF
+ // get the position of the linebreak
+ $chunkend = strpos($buffer, $lb);
+ if ($chunkend == FALSE) {
+ $this->debug('no linebreak found in decodeChunked');
+ return $new;
+ }
+ $temp = substr($buffer,0,$chunkend);
+ $chunk_size = hexdec( trim($temp) );
+ $chunkstart = $chunkend + strlen($lb);
+ // while (chunk-size > 0) {
+ while ($chunk_size > 0) {
+ $this->debug("chunkstart: $chunkstart chunk_size: $chunk_size");
+ $chunkend = strpos( $buffer, $lb, $chunkstart + $chunk_size);
+
+ // Just in case we got a broken connection
+ if ($chunkend == FALSE) {
+ $chunk = substr($buffer,$chunkstart);
+ // append chunk-data to entity-body
+ $new .= $chunk;
+ $length += strlen($chunk);
+ break;
+ }
+
+ // read chunk-data and CRLF
+ $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
+ // append chunk-data to entity-body
+ $new .= $chunk;
+ // length := length + chunk-size
+ $length += strlen($chunk);
+ // read chunk-size and CRLF
+ $chunkstart = $chunkend + strlen($lb);
+
+ $chunkend = strpos($buffer, $lb, $chunkstart) + strlen($lb);
+ if ($chunkend == FALSE) {
+ break; //Just in case we got a broken connection
+ }
+ $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
+ $chunk_size = hexdec( trim($temp) );
+ $chunkstart = $chunkend;
+ }
+ return $new;
+ }
+
+ /**
+ * Writes the payload, including HTTP headers, to $this->outgoing_payload.
+ *
+ * @param string $data HTTP body
+ * @param string $cookie_str data for HTTP Cookie header
+ * @return void
+ * @access private
+ */
+ function buildPayload($data, $cookie_str = '') {
+ // Note: for cURL connections, $this->outgoing_payload is ignored,
+ // as is the Content-Length header, but these are still created as
+ // debugging guides.
+
+ // add content-length header
+ if ($this->request_method != 'GET') {
+ $this->setHeader('Content-Length', strlen($data));
+ }
+
+ // start building outgoing payload:
+ if ($this->proxy) {
+ $uri = $this->url;
+ } else {
+ $uri = $this->uri;
+ }
+ $req = "$this->request_method $uri HTTP/$this->protocol_version";
+ $this->debug("HTTP request: $req");
+ $this->outgoing_payload = "$req\r\n";
+
+ // loop thru headers, serializing
+ foreach($this->outgoing_headers as $k => $v){
+ $hdr = $k.': '.$v;
+ $this->debug("HTTP header: $hdr");
+ $this->outgoing_payload .= "$hdr\r\n";
+ }
+
+ // add any cookies
+ if ($cookie_str != '') {
+ $hdr = 'Cookie: '.$cookie_str;
+ $this->debug("HTTP header: $hdr");
+ $this->outgoing_payload .= "$hdr\r\n";
+ }
+
+ // header/body separator
+ $this->outgoing_payload .= "\r\n";
+
+ // add data
+ $this->outgoing_payload .= $data;
+ }
+
+ /**
+ * sends the SOAP request via HTTP[S]
+ *
+ * @param string $data message data
+ * @param array $cookies cookies to send
+ * @return boolean true if OK, false if problem
+ * @access private
+ */
+ function sendRequest($data, $cookies = NULL) {
+ // build cookie string
+ $cookie_str = $this->getCookiesForRequest($cookies, (($this->scheme == 'ssl') || ($this->scheme == 'https')));
+
+ // build payload
+ $this->buildPayload($data, $cookie_str);
+
+ if ($this->io_method() == 'socket') {
+ // send payload
+ if(!fputs($this->fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
+ $this->setError('couldn\'t write message data to socket');
+ $this->debug('couldn\'t write message data to socket');
+ return false;
+ }
+ $this->debug('wrote data to socket, length = ' . strlen($this->outgoing_payload));
+ return true;
+ } else if ($this->io_method() == 'curl') {
+ // set payload
+ // cURL does say this should only be the verb, and in fact it
+ // turns out that the URI and HTTP version are appended to this, which
+ // some servers refuse to work with (so we no longer use this method!)
+ //$this->setCurlOption(CURLOPT_CUSTOMREQUEST, $this->outgoing_payload);
+ $curl_headers = array();
+ foreach($this->outgoing_headers as $k => $v){
+ if ($k == 'Connection' || $k == 'Content-Length' || $k == 'Host' || $k == 'Authorization' || $k == 'Proxy-Authorization') {
+ $this->debug("Skip cURL header $k: $v");
+ } else {
+ $curl_headers[] = "$k: $v";
+ }
+ }
+ if ($cookie_str != '') {
+ $curl_headers[] = 'Cookie: ' . $cookie_str;
+ }
+ $this->setCurlOption(CURLOPT_HTTPHEADER, $curl_headers);
+ $this->debug('set cURL HTTP headers');
+ if ($this->request_method == "POST") {
+ $this->setCurlOption(CURLOPT_POST, 1);
+ $this->setCurlOption(CURLOPT_POSTFIELDS, $data);
+ $this->debug('set cURL POST data');
+ } else {
+ }
+ // insert custom user-set cURL options
+ foreach ($this->ch_options as $key => $val) {
+ $this->setCurlOption($key, $val);
+ }
+
+ $this->debug('set cURL payload');
+ return true;
+ }
+ }
+
+ /**
+ * gets the SOAP response via HTTP[S]
+ *
+ * @return string the response (also sets member variables like incoming_payload)
+ * @access private
+ */
+ function getResponse(){
+ $this->incoming_payload = '';
+
+ if ($this->io_method() == 'socket') {
+ // loop until headers have been retrieved
+ $data = '';
+ while (!isset($lb)){
+
+ // We might EOF during header read.
+ if(feof($this->fp)) {
+ $this->incoming_payload = $data;
+ $this->debug('found no headers before EOF after length ' . strlen($data));
+ $this->debug("received before EOF:\n" . $data);
+ $this->setError('server failed to send headers');
+ return false;
+ }
+
+ $tmp = fgets($this->fp, 256);
+ $tmplen = strlen($tmp);
+ $this->debug("read line of $tmplen bytes: " . trim($tmp));
+
+ if ($tmplen == 0) {
+ $this->incoming_payload = $data;
+ $this->debug('socket read of headers timed out after length ' . strlen($data));
+ $this->debug("read before timeout: " . $data);
+ $this->setError('socket read of headers timed out');
+ return false;
+ }
+
+ $data .= $tmp;
+ $pos = strpos($data,"\r\n\r\n");
+ if($pos > 1){
+ $lb = "\r\n";
+ } else {
+ $pos = strpos($data,"\n\n");
+ if($pos > 1){
+ $lb = "\n";
+ }
+ }
+ // remove 100 headers
+ if (isset($lb) && preg_match('/^HTTP\/1.1 100/',$data)) {
+ unset($lb);
+ $data = '';
+ }//
+ }
+ // store header data
+ $this->incoming_payload .= $data;
+ $this->debug('found end of headers after length ' . strlen($data));
+ // process headers
+ $header_data = trim(substr($data,0,$pos));
+ $header_array = explode($lb,$header_data);
+ $this->incoming_headers = array();
+ $this->incoming_cookies = array();
+ foreach($header_array as $header_line){
+ $arr = explode(':',$header_line, 2);
+ if(count($arr) > 1){
+ $header_name = strtolower(trim($arr[0]));
+ $this->incoming_headers[$header_name] = trim($arr[1]);
+ if ($header_name == 'set-cookie') {
+ // TODO: allow multiple cookies from parseCookie
+ $cookie = $this->parseCookie(trim($arr[1]));
+ if ($cookie) {
+ $this->incoming_cookies[] = $cookie;
+ $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
+ } else {
+ $this->debug('did not find cookie in ' . trim($arr[1]));
+ }
+ }
+ } else if (isset($header_name)) {
+ // append continuation line to previous header
+ $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
+ }
+ }
+
+ // loop until msg has been received
+ if (isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked') {
+ $content_length = 2147483647; // ignore any content-length header
+ $chunked = true;
+ $this->debug("want to read chunked content");
+ } elseif (isset($this->incoming_headers['content-length'])) {
+ $content_length = $this->incoming_headers['content-length'];
+ $chunked = false;
+ $this->debug("want to read content of length $content_length");
+ } else {
+ $content_length = 2147483647;
+ $chunked = false;
+ $this->debug("want to read content to EOF");
+ }
+ $data = '';
+ do {
+ if ($chunked) {
+ $tmp = fgets($this->fp, 256);
+ $tmplen = strlen($tmp);
+ $this->debug("read chunk line of $tmplen bytes");
+ if ($tmplen == 0) {
+ $this->incoming_payload = $data;
+ $this->debug('socket read of chunk length timed out after length ' . strlen($data));
+ $this->debug("read before timeout:\n" . $data);
+ $this->setError('socket read of chunk length timed out');
+ return false;
+ }
+ $content_length = hexdec(trim($tmp));
+ $this->debug("chunk length $content_length");
+ }
+ $strlen = 0;
+ while (($strlen < $content_length) && (!feof($this->fp))) {
+ $readlen = min(8192, $content_length - $strlen);
+ $tmp = fread($this->fp, $readlen);
+ $tmplen = strlen($tmp);
+ $this->debug("read buffer of $tmplen bytes");
+ if (($tmplen == 0) && (!feof($this->fp))) {
+ $this->incoming_payload = $data;
+ $this->debug('socket read of body timed out after length ' . strlen($data));
+ $this->debug("read before timeout:\n" . $data);
+ $this->setError('socket read of body timed out');
+ return false;
+ }
+ $strlen += $tmplen;
+ $data .= $tmp;
+ }
+ if ($chunked && ($content_length > 0)) {
+ $tmp = fgets($this->fp, 256);
+ $tmplen = strlen($tmp);
+ $this->debug("read chunk terminator of $tmplen bytes");
+ if ($tmplen == 0) {
+ $this->incoming_payload = $data;
+ $this->debug('socket read of chunk terminator timed out after length ' . strlen($data));
+ $this->debug("read before timeout:\n" . $data);
+ $this->setError('socket read of chunk terminator timed out');
+ return false;
+ }
+ }
+ } while ($chunked && ($content_length > 0) && (!feof($this->fp)));
+ if (feof($this->fp)) {
+ $this->debug('read to EOF');
+ }
+ $this->debug('read body of length ' . strlen($data));
+ $this->incoming_payload .= $data;
+ $this->debug('received a total of '.strlen($this->incoming_payload).' bytes of data from server');
+
+ // close filepointer
+ if(
+ (isset($this->incoming_headers['connection']) && strtolower($this->incoming_headers['connection']) == 'close') ||
+ (! $this->persistentConnection) || feof($this->fp)){
+ fclose($this->fp);
+ $this->fp = false;
+ $this->debug('closed socket');
+ }
+
+ // connection was closed unexpectedly
+ if($this->incoming_payload == ''){
+ $this->setError('no response from server');
+ return false;
+ }
+
+ // decode transfer-encoding
+// if(isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked'){
+// if(!$data = $this->decodeChunked($data, $lb)){
+// $this->setError('Decoding of chunked data failed');
+// return false;
+// }
+ //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";
+ // set decoded payload
+// $this->incoming_payload = $header_data.$lb.$lb.$data;
+// }
+
+ } else if ($this->io_method() == 'curl') {
+ // send and receive
+ $this->debug('send and receive with cURL');
+ $this->incoming_payload = curl_exec($this->ch);
+ $data = $this->incoming_payload;
+
+ $cErr = curl_error($this->ch);
+ if ($cErr != '') {
+ $err = 'cURL ERROR: '.curl_errno($this->ch).': '.$cErr.'<br>';
+ // TODO: there is a PHP bug that can cause this to SEGV for CURLINFO_CONTENT_TYPE
+ foreach(curl_getinfo($this->ch) as $k => $v){
+ $err .= "$k: $v<br>";
+ }
+ $this->debug($err);
+ $this->setError($err);
+ curl_close($this->ch);
+ return false;
+ } else {
+ //echo '<pre>';
+ //var_dump(curl_getinfo($this->ch));
+ //echo '</pre>';
+ }
+ // close curl
+ $this->debug('No cURL error, closing cURL');
+ curl_close($this->ch);
+
+ // try removing skippable headers
+ $savedata = $data;
+ while ($this->isSkippableCurlHeader($data)) {
+ $this->debug("Found HTTP header to skip");
+ if ($pos = strpos($data,"\r\n\r\n")) {
+ $data = ltrim(substr($data,$pos));
+ } elseif($pos = strpos($data,"\n\n") ) {
+ $data = ltrim(substr($data,$pos));
+ }
+ }
+
+ if ($data == '') {
+ // have nothing left; just remove 100 header(s)
+ $data = $savedata;
+ while (preg_match('/^HTTP\/1.1 100/',$data)) {
+ if ($pos = strpos($data,"\r\n\r\n")) {
+ $data = ltrim(substr($data,$pos));
+ } elseif($pos = strpos($data,"\n\n") ) {
+ $data = ltrim(substr($data,$pos));
+ }
+ }
+ }
+
+ // separate content from HTTP headers
+ if ($pos = strpos($data,"\r\n\r\n")) {
+ $lb = "\r\n";
+ } elseif( $pos = strpos($data,"\n\n")) {
+ $lb = "\n";
+ } else {
+ $this->debug('no proper separation of headers and document');
+ $this->setError('no proper separation of headers and document');
+ return false;
+ }
+ $header_data = trim(substr($data,0,$pos));
+ $header_array = explode($lb,$header_data);
+ $data = ltrim(substr($data,$pos));
+ $this->debug('found proper separation of headers and document');
+ $this->debug('cleaned data, stringlen: '.strlen($data));
+ // clean headers
+ foreach ($header_array as $header_line) {
+ $arr = explode(':',$header_line,2);
+ if(count($arr) > 1){
+ $header_name = strtolower(trim($arr[0]));
+ $this->incoming_headers[$header_name] = trim($arr[1]);
+ if ($header_name == 'set-cookie') {
+ // TODO: allow multiple cookies from parseCookie
+ $cookie = $this->parseCookie(trim($arr[1]));
+ if ($cookie) {
+ $this->incoming_cookies[] = $cookie;
+ $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
+ } else {
+ $this->debug('did not find cookie in ' . trim($arr[1]));
+ }
+ }
+ } else if (isset($header_name)) {
+ // append continuation line to previous header
+ $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
+ }
+ }
+ }
+
+ $this->response_status_line = $header_array[0];
+ $arr = explode(' ', $this->response_status_line, 3);
+ $http_version = $arr[0];
+ $http_status = intval($arr[1]);
+ $http_reason = count($arr) > 2 ? $arr[2] : '';
+
+ // see if we need to resend the request with http digest authentication
+ if (isset($this->incoming_headers['location']) && ($http_status == 301 || $http_status == 302)) {
+ $this->debug("Got $http_status $http_reason with Location: " . $this->incoming_headers['location']);
+ $this->setURL($this->incoming_headers['location']);
+ $this->tryagain = true;
+ return false;
+ }
+
+ // see if we need to resend the request with http digest authentication
+ if (isset($this->incoming_headers['www-authenticate']) && $http_status == 401) {
+ $this->debug("Got 401 $http_reason with WWW-Authenticate: " . $this->incoming_headers['www-authenticate']);
+ if (strstr($this->incoming_headers['www-authenticate'], "Digest ")) {
+ $this->debug('Server wants digest authentication');
+ // remove "Digest " from our elements
+ $digestString = str_replace('Digest ', '', $this->incoming_headers['www-authenticate']);
+
+ // parse elements into array
+ $digestElements = explode(',', $digestString);
+ foreach ($digestElements as $val) {
+ $tempElement = explode('=', trim($val), 2);
+ $digestRequest[$tempElement[0]] = str_replace("\"", '', $tempElement[1]);
+ }
+
+ // should have (at least) qop, realm, nonce
+ if (isset($digestRequest['nonce'])) {
+ $this->setCredentials($this->username, $this->password, 'digest', $digestRequest);
+ $this->tryagain = true;
+ return false;
+ }
+ }
+ $this->debug('HTTP authentication failed');
+ $this->setError('HTTP authentication failed');
+ return false;
+ }
+
+ if (
+ ($http_status >= 300 && $http_status <= 307) ||
+ ($http_status >= 400 && $http_status <= 417) ||
+ ($http_status >= 501 && $http_status <= 505)
+ ) {
+ $this->setError("Unsupported HTTP response status $http_status $http_reason (soapclient->response has contents of the response)");
+ return false;
+ }
+
+ // decode content-encoding
+ if(isset($this->incoming_headers['content-encoding']) && $this->incoming_headers['content-encoding'] != ''){
+ if(strtolower($this->incoming_headers['content-encoding']) == 'deflate' || strtolower($this->incoming_headers['content-encoding']) == 'gzip'){
+ // if decoding works, use it. else assume data wasn't gzencoded
+ if(function_exists('gzinflate')){
+ //$timer->setMarker('starting decoding of gzip/deflated content');
+ // IIS 5 requires gzinflate instead of gzuncompress (similar to IE 5 and gzdeflate v. gzcompress)
+ // this means there are no Zlib headers, although there should be
+ $this->debug('The gzinflate function exists');
+ $datalen = strlen($data);
+ if ($this->incoming_headers['content-encoding'] == 'deflate') {
+ if ($degzdata = @gzinflate($data)) {
+ $data = $degzdata;
+ $this->debug('The payload has been inflated to ' . strlen($data) . ' bytes');
+ if (strlen($data) < $datalen) {
+ // test for the case that the payload has been compressed twice
+ $this->debug('The inflated payload is smaller than the gzipped one; try again');
+ if ($degzdata = @gzinflate($data)) {
+ $data = $degzdata;
+ $this->debug('The payload has been inflated again to ' . strlen($data) . ' bytes');
+ }
+ }
+ } else {
+ $this->debug('Error using gzinflate to inflate the payload');
+ $this->setError('Error using gzinflate to inflate the payload');
+ }
+ } elseif ($this->incoming_headers['content-encoding'] == 'gzip') {
+ if ($degzdata = @gzinflate(substr($data, 10))) { // do our best
+ $data = $degzdata;
+ $this->debug('The payload has been un-gzipped to ' . strlen($data) . ' bytes');
+ if (strlen($data) < $datalen) {
+ // test for the case that the payload has been compressed twice
+ $this->debug('The un-gzipped payload is smaller than the gzipped one; try again');
+ if ($degzdata = @gzinflate(substr($data, 10))) {
+ $data = $degzdata;
+ $this->debug('The payload has been un-gzipped again to ' . strlen($data) . ' bytes');
+ }
+ }
+ } else {
+ $this->debug('Error using gzinflate to un-gzip the payload');
+ $this->setError('Error using gzinflate to un-gzip the payload');
+ }
+ }
+ //$timer->setMarker('finished decoding of gzip/deflated content');
+ //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";
+ // set decoded payload
+ $this->incoming_payload = $header_data.$lb.$lb.$data;
+ } else {
+ $this->debug('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
+ $this->setError('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
+ }
+ } else {
+ $this->debug('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
+ $this->setError('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
+ }
+ } else {
+ $this->debug('No Content-Encoding header');
+ }
+
+ if(strlen($data) == 0){
+ $this->debug('no data after headers!');
+ $this->setError('no data present after HTTP headers');
+ return false;
+ }
+
+ return $data;
+ }
+
+ /**
+ * sets the content-type for the SOAP message to be sent
+ *
+ * @param string $type the content type, MIME style
+ * @param mixed $charset character set used for encoding (or false)
+ * @access public
+ */
+ function setContentType($type, $charset = false) {
+ $this->setHeader('Content-Type', $type . ($charset ? '; charset=' . $charset : ''));
+ }
+
+ /**
+ * specifies that an HTTP persistent connection should be used
+ *
+ * @return boolean whether the request was honored by this method.
+ * @access public
+ */
+ function usePersistentConnection(){
+ if (isset($this->outgoing_headers['Accept-Encoding'])) {
+ return false;
+ }
+ $this->protocol_version = '1.1';
+ $this->persistentConnection = true;
+ $this->setHeader('Connection', 'Keep-Alive');
+ return true;
+ }
+
+ /**
+ * parse an incoming Cookie into it's parts
+ *
+ * @param string $cookie_str content of cookie
+ * @return array with data of that cookie
+ * @access private
+ */
+ /*
+ * TODO: allow a Set-Cookie string to be parsed into multiple cookies
+ */
+ function parseCookie($cookie_str) {
+ $cookie_str = str_replace('; ', ';', $cookie_str) . ';';
+ $data = preg_split('/;/', $cookie_str);
+ $value_str = $data[0];
+
+ $cookie_param = 'domain=';
+ $start = strpos($cookie_str, $cookie_param);
+ if ($start > 0) {
+ $domain = substr($cookie_str, $start + strlen($cookie_param));
+ $domain = substr($domain, 0, strpos($domain, ';'));
+ } else {
+ $domain = '';
+ }
+
+ $cookie_param = 'expires=';
+ $start = strpos($cookie_str, $cookie_param);
+ if ($start > 0) {
+ $expires = substr($cookie_str, $start + strlen($cookie_param));
+ $expires = substr($expires, 0, strpos($expires, ';'));
+ } else {
+ $expires = '';
+ }
+
+ $cookie_param = 'path=';
+ $start = strpos($cookie_str, $cookie_param);
+ if ( $start > 0 ) {
+ $path = substr($cookie_str, $start + strlen($cookie_param));
+ $path = substr($path, 0, strpos($path, ';'));
+ } else {
+ $path = '/';
+ }
+
+ $cookie_param = ';secure;';
+ if (strpos($cookie_str, $cookie_param) !== FALSE) {
+ $secure = true;
+ } else {
+ $secure = false;
+ }
+
+ $sep_pos = strpos($value_str, '=');
+
+ if ($sep_pos) {
+ $name = substr($value_str, 0, $sep_pos);
+ $value = substr($value_str, $sep_pos + 1);
+ $cookie= array( 'name' => $name,
+ 'value' => $value,
+ 'domain' => $domain,
+ 'path' => $path,
+ 'expires' => $expires,
+ 'secure' => $secure
+ );
+ return $cookie;
+ }
+ return false;
+ }
+
+ /**
+ * sort out cookies for the current request
+ *
+ * @param array $cookies array with all cookies
+ * @param boolean $secure is the send-content secure or not?
+ * @return string for Cookie-HTTP-Header
+ * @access private
+ */
+ function getCookiesForRequest($cookies, $secure=false) {
+ $cookie_str = '';
+ if ((! is_null($cookies)) && (is_array($cookies))) {
+ foreach ($cookies as $cookie) {
+ if (! is_array($cookie)) {
+ continue;
+ }
+ $this->debug("check cookie for validity: ".$cookie['name'].'='.$cookie['value']);
+ if ((isset($cookie['expires'])) && (! empty($cookie['expires']))) {
+ if (strtotime($cookie['expires']) <= time()) {
+ $this->debug('cookie has expired');
+ continue;
+ }
+ }
+ if ((isset($cookie['domain'])) && (! empty($cookie['domain']))) {
+ $domain = preg_quote($cookie['domain']);
+ if (! preg_match("'.*$domain$'i", $this->host)) {
+ $this->debug('cookie has different domain');
+ continue;
+ }
+ }
+ if ((isset($cookie['path'])) && (! empty($cookie['path']))) {
+ $path = preg_quote($cookie['path']);
+ if (! preg_match("'^$path.*'i", $this->path)) {
+ $this->debug('cookie is for a different path');
+ continue;
+ }
+ }
+ if ((! $secure) && (isset($cookie['secure'])) && ($cookie['secure'])) {
+ $this->debug('cookie is secure, transport is not');
+ continue;
+ }
+ $cookie_str .= $cookie['name'] . '=' . $cookie['value'] . '; ';
+ $this->debug('add cookie to Cookie-String: ' . $cookie['name'] . '=' . $cookie['value']);
+ }
+ }
+ return $cookie_str;
+ }
+}
+
+?><?php
+
+
+
+/**
+*
+* nusoap_server allows the user to create a SOAP server
+* that is capable of receiving messages and returning responses
+*
+* @author Dietrich Ayala <dietrich@ganx4.com>
+* @author Scott Nichol <snichol@users.sourceforge.net>
+* @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
+* @access public
+*/
+class nusoap_server extends nusoap_base {
+ /**
+ * HTTP headers of request
+ * @var array
+ * @access private
+ */
+ var $headers = array();
+ /**
+ * HTTP request
+ * @var string
+ * @access private
+ */
+ var $request = '';
+ /**
+ * SOAP headers from request (incomplete namespace resolution; special characters not escaped) (text)
+ * @var string
+ * @access public
+ */
+ var $requestHeaders = '';
+ /**
+ * SOAP Headers from request (parsed)
+ * @var mixed
+ * @access public
+ */
+ var $requestHeader = NULL;
+ /**
+ * SOAP body request portion (incomplete namespace resolution; special characters not escaped) (text)
+ * @var string
+ * @access public
+ */
+ var $document = '';
+ /**
+ * SOAP payload for request (text)
+ * @var string
+ * @access public
+ */
+ var $requestSOAP = '';
+ /**
+ * requested method namespace URI
+ * @var string
+ * @access private
+ */
+ var $methodURI = '';
+ /**
+ * name of method requested
+ * @var string
+ * @access private
+ */
+ var $methodname = '';
+ /**
+ * method parameters from request
+ * @var array
+ * @access private
+ */
+ var $methodparams = array();
+ /**
+ * SOAP Action from request
+ * @var string
+ * @access private
+ */
+ var $SOAPAction = '';
+ /**
+ * character set encoding of incoming (request) messages
+ * @var string
+ * @access public
+ */
+ var $xml_encoding = '';
+ /**
+ * toggles whether the parser decodes element content w/ utf8_decode()
+ * @var boolean
+ * @access public
+ */
+ var $decode_utf8 = true;
+
+ /**
+ * HTTP headers of response
+ * @var array
+ * @access public
+ */
+ var $outgoing_headers = array();
+ /**
+ * HTTP response
+ * @var string
+ * @access private
+ */
+ var $response = '';
+ /**
+ * SOAP headers for response (text or array of soapval or associative array)
+ * @var mixed
+ * @access public
+ */
+ var $responseHeaders = '';
+ /**
+ * SOAP payload for response (text)
+ * @var string
+ * @access private
+ */
+ var $responseSOAP = '';
+ /**
+ * method return value to place in response
+ * @var mixed
+ * @access private
+ */
+ var $methodreturn = false;
+ /**
+ * whether $methodreturn is a string of literal XML
+ * @var boolean
+ * @access public
+ */
+ var $methodreturnisliteralxml = false;
+ /**
+ * SOAP fault for response (or false)
+ * @var mixed
+ * @access private
+ */
+ var $fault = false;
+ /**
+ * text indication of result (for debugging)
+ * @var string
+ * @access private
+ */
+ var $result = 'successful';
+
+ /**
+ * assoc array of operations => opData; operations are added by the register()
+ * method or by parsing an external WSDL definition
+ * @var array
+ * @access private
+ */
+ var $operations = array();
+ /**
+ * wsdl instance (if one)
+ * @var mixed
+ * @access private
+ */
+ var $wsdl = false;
+ /**
+ * URL for WSDL (if one)
+ * @var mixed
+ * @access private
+ */
+ var $externalWSDLURL = false;
+ /**
+ * whether to append debug to response as XML comment
+ * @var boolean
+ * @access public
+ */
+ var $debug_flag = false;
+
+
+ /**
+ * constructor
+ * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
+ *
+ * @param mixed $wsdl file path or URL (string), or wsdl instance (object)
+ * @access public
+ */
+ function __construct($wsdl=false){
+ parent::__construct();
+ // turn on debugging?
+ global $debug;
+ if (version_compare(PHP_VERSION, '5.3.0', '<')) {
+ global $HTTP_SERVER_VARS;
+ $http_server_vars_local = $HTTP_SERVER_VARS;
+ }
+ if (isset($_SERVER)) {
+ $this->debug("_SERVER is defined:");
+ $this->appendDebug($this->varDump($_SERVER));
+ } elseif (isset($http_server_vars_local)) {
+ $this->debug("HTTP_SERVER_VARS is defined:");
+ $this->appendDebug($this->varDump($http_server_vars_local));
+ } else {
+ $this->debug("Neither _SERVER nor HTTP_SERVER_VARS is defined.");
+ }
+
+ if (isset($debug)) {
+ $this->debug("In nusoap_server, set debug_flag=$debug based on global flag");
+ $this->debug_flag = $debug;
+ } elseif (isset($_SERVER['QUERY_STRING'])) {
+ $qs = explode('&', $_SERVER['QUERY_STRING']);
+ foreach ($qs as $v) {
+ if (substr($v, 0, 6) == 'debug=') {
+ $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #1");
+ $this->debug_flag = substr($v, 6);
+ }
+ }
+ } elseif (isset($http_server_vars_local['QUERY_STRING'])) {
+ $qs = explode('&', $http_server_vars_local['QUERY_STRING']);
+ foreach ($qs as $v) {
+ if (substr($v, 0, 6) == 'debug=') {
+ $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #2");
+ $this->debug_flag = substr($v, 6);
+ }
+ }
+ }
+
+ // wsdl
+ if($wsdl){
+ $this->debug("In nusoap_server, WSDL is specified");
+ if (is_object($wsdl) && (get_class($wsdl) == 'wsdl')) {
+ $this->wsdl = $wsdl;
+ $this->externalWSDLURL = $this->wsdl->wsdl;
+ $this->debug('Use existing wsdl instance from ' . $this->externalWSDLURL);
+ } else {
+ $this->debug('Create wsdl from ' . $wsdl);
+ $this->wsdl = new wsdl($wsdl);
+ $this->externalWSDLURL = $wsdl;
+ }
+ $this->appendDebug($this->wsdl->getDebug());
+ $this->wsdl->clearDebug();
+ if($err = $this->wsdl->getError()){
+ die('WSDL ERROR: '.$err);
+ }
+ }
+ }
+
+ /**
+ * processes request and returns response
+ *
+ * @param string $data usually is the value of $HTTP_RAW_POST_DATA
+ * @access public
+ */
+ function service($data){
+ if (version_compare(PHP_VERSION, '5.3.0', '<')) {
+ global $HTTP_SERVER_VARS;
+ $http_server_vars_local = $HTTP_SERVER_VARS;
+ }
+
+ if (isset($_SERVER['REQUEST_METHOD'])) {
+ $rm = $_SERVER['REQUEST_METHOD'];
+ } elseif (isset($http_server_vars_local['REQUEST_METHOD'])) {
+ $rm = $http_server_vars_local['REQUEST_METHOD'];
+ } else {
+ $rm = '';
+ }
+
+ if (isset($_SERVER['QUERY_STRING'])) {
+ $qs = $_SERVER['QUERY_STRING'];
+ } elseif (isset($http_server_vars_local['QUERY_STRING'])) {
+ $qs = $http_server_vars_local['QUERY_STRING'];
+ } else {
+ $qs = '';
+ }
+ $this->debug("In service, request method=$rm query string=$qs strlen(\$data)=" . strlen($data));
+
+ if ($rm == 'POST') {
+ $this->debug("In service, invoke the request");
+ $this->parse_request($data);
+ if (! $this->fault) {
+ $this->invoke_method();
+ }
+ if (! $this->fault) {
+ $this->serialize_return();
+ }
+ $this->send_response();
+ } elseif (preg_match('/wsdl/', $qs) ){
+ $this->debug("In service, this is a request for WSDL");
+ if ($this->externalWSDLURL){
+ if (strpos($this->externalWSDLURL, "http://") !== false) { // assume URL
+ $this->debug("In service, re-direct for WSDL");
+ header('Location: '.$this->externalWSDLURL);
+ } else { // assume file
+ $this->debug("In service, use file passthru for WSDL");
+ header("Content-Type: text/xml\r\n");
+ $pos = strpos($this->externalWSDLURL, "file://");
+ if ($pos === false) {
+ $filename = $this->externalWSDLURL;
+ } else {
+ $filename = substr($this->externalWSDLURL, $pos + 7);
+ }
+ $fp = fopen($this->externalWSDLURL, 'r');
+ fpassthru($fp);
+ }
+ } elseif ($this->wsdl) {
+ $this->debug("In service, serialize WSDL");
+ header("Content-Type: text/xml; charset=ISO-8859-1\r\n");
+ print $this->wsdl->serialize($this->debug_flag);
+ if ($this->debug_flag) {
+ $this->debug('wsdl:');
+ $this->appendDebug($this->varDump($this->wsdl));
+ print $this->getDebugAsXMLComment();
+ }
+ } else {
+ $this->debug("In service, there is no WSDL");
+ header("Content-Type: text/html; charset=ISO-8859-1\r\n");
+ print "This service does not provide WSDL";
+ }
+ } elseif ($this->wsdl) {
+ $this->debug("In service, return Web description");
+ print $this->wsdl->webDescription();
+ } else {
+ $this->debug("In service, no Web description");
+ header("Content-Type: text/html; charset=ISO-8859-1\r\n");
+ print "This service does not provide a Web description";
+ }
+ }
+
+ /**
+ * parses HTTP request headers.
+ *
+ * The following fields are set by this function (when successful)
+ *
+ * headers
+ * request
+ * xml_encoding
+ * SOAPAction
+ *
+ * @access private
+ */
+ function parse_http_headers() {
+ if (version_compare(PHP_VERSION, '5.3.0', '<')) {
+ global $HTTP_SERVER_VARS;
+ $http_server_vars_local = $HTTP_SERVER_VARS;
+ }
+
+ $this->request = '';
+ $this->SOAPAction = '';
+ if(function_exists('getallheaders')){
+ $this->debug("In parse_http_headers, use getallheaders");
+ $headers = getallheaders();
+ foreach($headers as $k=>$v){
+ $k = strtolower($k);
+ $this->headers[$k] = $v;
+ $this->request .= "$k: $v\r\n";
+ $this->debug("$k: $v");
+ }
+ // get SOAPAction header
+ if(isset($this->headers['soapaction'])){
+ $this->SOAPAction = str_replace('"','',$this->headers['soapaction']);
+ }
+ // get the character encoding of the incoming request
+ if(isset($this->headers['content-type']) && strpos($this->headers['content-type'],'=')){
+ $enc = str_replace('"','',substr(strstr($this->headers["content-type"],'='),1));
+ if(preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)){
+ $this->xml_encoding = strtoupper($enc);
+ } else {
+ $this->xml_encoding = 'US-ASCII';
+ }
+ } else {
+ // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
+ $this->xml_encoding = 'ISO-8859-1';
+ }
+ } elseif(isset($_SERVER) && is_array($_SERVER)){
+ $this->debug("In parse_http_headers, use _SERVER");
+ foreach ($_SERVER as $k => $v) {
+ if (substr($k, 0, 5) == 'HTTP_') {
+ $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5))));
+ } else {
+ $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k)));
+ }
+ if ($k == 'soapaction') {
+ // get SOAPAction header
+ $k = 'SOAPAction';
+ $v = str_replace('"', '', $v);
+ $v = str_replace('\\', '', $v);
+ $this->SOAPAction = $v;
+ } else if ($k == 'content-type') {
+ // get the character encoding of the incoming request
+ if (strpos($v, '=')) {
+ $enc = substr(strstr($v, '='), 1);
+ $enc = str_replace('"', '', $enc);
+ $enc = str_replace('\\', '', $enc);
+ if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)) {
+ $this->xml_encoding = strtoupper($enc);
+ } else {
+ $this->xml_encoding = 'US-ASCII';
+ }
+ } else {
+ // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
+ $this->xml_encoding = 'ISO-8859-1';
+ }
+ }
+ $this->headers[$k] = $v;
+ $this->request .= "$k: $v\r\n";
+ $this->debug("$k: $v");
+ }
+ } elseif (is_array($http_server_vars_local)) {
+ $this->debug("In parse_http_headers, use HTTP_SERVER_VARS");
+ foreach ($http_server_vars_local as $k => $v) {
+ if (substr($k, 0, 5) == 'HTTP_') {
+ $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5)))); $k = strtolower(substr($k, 5));
+ } else {
+ $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k))); $k = strtolower($k);
+ }
+ if ($k == 'soapaction') {
+ // get SOAPAction header
+ $k = 'SOAPAction';
+ $v = str_replace('"', '', $v);
+ $v = str_replace('\\', '', $v);
+ $this->SOAPAction = $v;
+ } else if ($k == 'content-type') {
+ // get the character encoding of the incoming request
+ if (strpos($v, '=')) {
+ $enc = substr(strstr($v, '='), 1);
+ $enc = str_replace('"', '', $enc);
+ $enc = str_replace('\\', '', $enc);
+ if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)) {
+ $this->xml_encoding = strtoupper($enc);
+ } else {
+ $this->xml_encoding = 'US-ASCII';
+ }
+ } else {
+ // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
+ $this->xml_encoding = 'ISO-8859-1';
+ }
+ }
+ $this->headers[$k] = $v;
+ $this->request .= "$k: $v\r\n";
+ $this->debug("$k: $v");
+ }
+ } else {
+ $this->debug("In parse_http_headers, HTTP headers not accessible");
+ $this->setError("HTTP headers not accessible");
+ }
+ }
+
+ /**
+ * parses a request
+ *
+ * The following fields are set by this function (when successful)
+ *
+ * headers
+ * request
+ * xml_encoding
+ * SOAPAction
+ * request
+ * requestSOAP
+ * methodURI
+ * methodname
+ * methodparams
+ * requestHeaders
+ * document
+ *
+ * This sets the fault field on error
+ *
+ * @param string $data XML string
+ * @access private
+ */
+ function parse_request($data='') {
+ $this->debug('entering parse_request()');
+ $this->parse_http_headers();
+ $this->debug('got character encoding: '.$this->xml_encoding);
+ // uncompress if necessary
+ if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] != '') {
+ $this->debug('got content encoding: ' . $this->headers['content-encoding']);
+ if ($this->headers['content-encoding'] == 'deflate' || $this->headers['content-encoding'] == 'gzip') {
+ // if decoding works, use it. else assume data wasn't gzencoded
+ if (function_exists('gzuncompress')) {
+ if ($this->headers['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data)) {
+ $data = $degzdata;
+ } elseif ($this->headers['content-encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))) {
+ $data = $degzdata;
+ } else {
+ $this->fault('SOAP-ENV:Client', 'Errors occurred when trying to decode the data');
+ return;
+ }
+ } else {
+ $this->fault('SOAP-ENV:Client', 'This Server does not support compressed data');
+ return;
+ }
+ }
+ }
+ $this->request .= "\r\n".$data;
+ $data = $this->parseRequest($this->headers, $data);
+ $this->requestSOAP = $data;
+ $this->debug('leaving parse_request');
+ }
+
+ /**
+ * invokes a PHP function for the requested SOAP method
+ *
+ * The following fields are set by this function (when successful)
+ *
+ * methodreturn
+ *
+ * Note that the PHP function that is called may also set the following
+ * fields to affect the response sent to the client
+ *
+ * responseHeaders
+ * outgoing_headers
+ *
+ * This sets the fault field on error
+ *
+ * @access private
+ */
+ function invoke_method() {
+ $this->debug('in invoke_method, methodname=' . $this->methodname . ' methodURI=' . $this->methodURI . ' SOAPAction=' . $this->SOAPAction);
+
+ //
+ // if you are debugging in this area of the code, your service uses a class to implement methods,
+ // you use SOAP RPC, and the client is .NET, please be aware of the following...
+ // when the .NET wsdl.exe utility generates a proxy, it will remove the '.' or '..' from the
+ // method name. that is fine for naming the .NET methods. it is not fine for properly constructing
+ // the XML request and reading the XML response. you need to add the RequestElementName and
+ // ResponseElementName to the System.Web.Services.Protocols.SoapRpcMethodAttribute that wsdl.exe
+ // generates for the method. these parameters are used to specify the correct XML element names
+ // for .NET to use, i.e. the names with the '.' in them.
+ //
+ $orig_methodname = $this->methodname;
+ if ($this->wsdl) {
+ if ($this->opData = $this->wsdl->getOperationData($this->methodname)) {
+ $this->debug('in invoke_method, found WSDL operation=' . $this->methodname);
+ $this->appendDebug('opData=' . $this->varDump($this->opData));
+ } elseif ($this->opData = $this->wsdl->getOperationDataForSoapAction($this->SOAPAction)) {
+ // Note: hopefully this case will only be used for doc/lit, since rpc services should have wrapper element
+ $this->debug('in invoke_method, found WSDL soapAction=' . $this->SOAPAction . ' for operation=' . $this->opData['name']);
+ $this->appendDebug('opData=' . $this->varDump($this->opData));
+ $this->methodname = $this->opData['name'];
+ } else {
+ $this->debug('in invoke_method, no WSDL for operation=' . $this->methodname);
+ $this->fault('SOAP-ENV:Client', "Operation '" . $this->methodname . "' is not defined in the WSDL for this service");
+ return;
+ }
+ } else {
+ $this->debug('in invoke_method, no WSDL to validate method');
+ }
+
+ // if a . is present in $this->methodname, we see if there is a class in scope,
+ // which could be referred to. We will also distinguish between two deliminators,
+ // to allow methods to be called a the class or an instance
+ if (strpos($this->methodname, '..') > 0) {
+ $delim = '..';
+ } else if (strpos($this->methodname, '.') > 0) {
+ $delim = '.';
+ } else {
+ $delim = '';
+ }
+ $this->debug("in invoke_method, delim=$delim");
+
+ $class = '';
+ $method = '';
+ if (strlen($delim) > 0 && substr_count($this->methodname, $delim) == 1) {
+ $try_class = substr($this->methodname, 0, strpos($this->methodname, $delim));
+ if (class_exists($try_class)) {
+ // get the class and method name
+ $class = $try_class;
+ $method = substr($this->methodname, strpos($this->methodname, $delim) + strlen($delim));
+ $this->debug("in invoke_method, class=$class method=$method delim=$delim");
+ } else {
+ $this->debug("in invoke_method, class=$try_class not found");
+ }
+ } else {
+ $try_class = '';
+ $this->debug("in invoke_method, no class to try");
+ }
+
+ // does method exist?
+ if ($class == '') {
+ if (!function_exists($this->methodname)) {
+ $this->debug("in invoke_method, function '$this->methodname' not found!");
+ $this->result = 'fault: method not found';
+ $this->fault('SOAP-ENV:Client',"method '$this->methodname'('$orig_methodname') not defined in service('$try_class' '$delim')");
+ return;
+ }
+ } else {
+ $method_to_compare = (substr(phpversion(), 0, 2) == '4.') ? strtolower($method) : $method;
+ if (!in_array($method_to_compare, get_class_methods($class))) {
+ $this->debug("in invoke_method, method '$this->methodname' not found in class '$class'!");
+ $this->result = 'fault: method not found';
+ $this->fault('SOAP-ENV:Client',"method '$this->methodname'/'$method_to_compare'('$orig_methodname') not defined in service/'$class'('$try_class' '$delim')");
+ return;
+ }
+ }
+
+ // evaluate message, getting back parameters
+ // verify that request parameters match the method's signature
+ if(! $this->verify_method($this->methodname,$this->methodparams)){
+ // debug
+ $this->debug('ERROR: request not verified against method signature');
+ $this->result = 'fault: request failed validation against method signature';
+ // return fault
+ $this->fault('SOAP-ENV:Client',"Operation '$this->methodname' not defined in service.");
+ return;
+ }
+
+ // if there are parameters to pass
+ $this->debug('in invoke_method, params:');
+ $this->appendDebug($this->varDump($this->methodparams));
+ $this->debug("in invoke_method, calling '$this->methodname'");
+ if (!function_exists('call_user_func_array')) {
+ if ($class == '') {
+ $this->debug('in invoke_method, calling function using eval()');
+ $funcCall = "\$this->methodreturn = $this->methodname(";
+ } else {
+ if ($delim == '..') {
+ $this->debug('in invoke_method, calling class method using eval()');
+ $funcCall = "\$this->methodreturn = ".$class."::".$method."(";
+ } else {
+ $this->debug('in invoke_method, calling instance method using eval()');
+ // generate unique instance name
+ $instname = "\$inst_".time();
+ $funcCall = $instname." = new ".$class."(); ";
+ $funcCall .= "\$this->methodreturn = ".$instname."->".$method."(";
+ }
+ }
+ if ($this->methodparams) {
+ foreach ($this->methodparams as $param) {
+ if (is_array($param) || is_object($param)) {
+ $this->fault('SOAP-ENV:Client', 'NuSOAP does not handle complexType parameters correctly when using eval; call_user_func_array must be available');
+ return;
+ }
+ $funcCall .= "\"$param\",";
+ }
+ $funcCall = substr($funcCall, 0, -1);
+ }
+ $funcCall .= ');';
+ $this->debug('in invoke_method, function call: '.$funcCall);
+ @eval($funcCall);
+ } else {
+ if ($class == '') {
+ $this->debug('in invoke_method, calling function using call_user_func_array()');
+ $call_arg = "$this->methodname"; // straight assignment changes $this->methodname to lower case after call_user_func_array()
+ } elseif ($delim == '..') {
+ $this->debug('in invoke_method, calling class method using call_user_func_array()');
+ $call_arg = array ($class, $method);
+ } else {
+ $this->debug('in invoke_method, calling instance method using call_user_func_array()');
+ $instance = new $class ();
+ $call_arg = array(&$instance, $method);
+ }
+ if (is_array($this->methodparams)) {
+ $this->methodreturn = call_user_func_array($call_arg, array_values($this->methodparams));
+ } else {
+ $this->methodreturn = call_user_func_array($call_arg, array());
+ }
+ }
+ $this->debug('in invoke_method, methodreturn:');
+ $this->appendDebug($this->varDump($this->methodreturn));
+ $this->debug("in invoke_method, called method $this->methodname, received data of type ".gettype($this->methodreturn));
+ }
+
+ /**
+ * serializes the return value from a PHP function into a full SOAP Envelope
+ *
+ * The following fields are set by this function (when successful)
+ *
+ * responseSOAP
+ *
+ * This sets the fault field on error
+ *
+ * @access private
+ */
+ function serialize_return() {
+ $this->debug('Entering serialize_return methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);
+ // if fault
+ if (isset($this->methodreturn) && is_object($this->methodreturn) && ((get_class($this->methodreturn) == 'soap_fault') || (get_class($this->methodreturn) == 'nusoap_fault'))) {
+ $this->debug('got a fault object from method');
+ $this->fault = $this->methodreturn;
+ return;
+ } elseif ($this->methodreturnisliteralxml) {
+ $return_val = $this->methodreturn;
+ // returned value(s)
+ } else {
+ $this->debug('got a(n) '.gettype($this->methodreturn).' from method');
+ $this->debug('serializing return value');
+ if($this->wsdl){
+ if (sizeof($this->opData['output']['parts']) > 1) {
+ $this->debug('more than one output part, so use the method return unchanged');
+ $opParams = $this->methodreturn;
+ } elseif (sizeof($this->opData['output']['parts']) == 1) {
+ $this->debug('exactly one output part, so wrap the method return in a simple array');
+ // TODO: verify that it is not already wrapped!
+ //foreach ($this->opData['output']['parts'] as $name => $type) {
+ // $this->debug('wrap in element named ' . $name);
+ //}
+ $opParams = array($this->methodreturn);
+ }
+ $return_val = $this->wsdl->serializeRPCParameters($this->methodname,'output',$opParams);
+ $this->appendDebug($this->wsdl->getDebug());
+ $this->wsdl->clearDebug();
+ if($errstr = $this->wsdl->getError()){
+ $this->debug('got wsdl error: '.$errstr);
+ $this->fault('SOAP-ENV:Server', 'unable to serialize result');
+ return;
+ }
+ } else {
+ if (isset($this->methodreturn)) {
+ $return_val = $this->serialize_val($this->methodreturn, 'return');
+ } else {
+ $return_val = '';
+ $this->debug('in absence of WSDL, assume void return for backward compatibility');
+ }
+ }
+ }
+ $this->debug('return value:');
+ $this->appendDebug($this->varDump($return_val));
+
+ $this->debug('serializing response');
+ if ($this->wsdl) {
+ $this->debug('have WSDL for serialization: style is ' . $this->opData['style']);
+ if ($this->opData['style'] == 'rpc') {
+ $this->debug('style is rpc for serialization: use is ' . $this->opData['output']['use']);
+ if ($this->opData['output']['use'] == 'literal') {
+ // http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html R2735 says rpc/literal accessor elements should not be in a namespace
+ if ($this->methodURI) {
+ $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
+ } else {
+ $payload = '<'.$this->methodname.'Response>'.$return_val.'</'.$this->methodname.'Response>';
+ }
+ } else {
+ if ($this->methodURI) {
+ $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
+ } else {
+ $payload = '<'.$this->methodname.'Response>'.$return_val.'</'.$this->methodname.'Response>';
+ }
+ }
+ } else {
+ $this->debug('style is not rpc for serialization: assume document');
+ $payload = $return_val;
+ }
+ } else {
+ $this->debug('do not have WSDL for serialization: assume rpc/encoded');
+ $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
+ }
+ $this->result = 'successful';
+ if($this->wsdl){
+ //if($this->debug_flag){
+ $this->appendDebug($this->wsdl->getDebug());
+ // }
+ if (isset($this->opData['output']['encodingStyle'])) {
+ $encodingStyle = $this->opData['output']['encodingStyle'];
+ } else {
+ $encodingStyle = '';
+ }
+ // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
+ $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders,$this->wsdl->usedNamespaces,$this->opData['style'],$this->opData['output']['use'],$encodingStyle);
+ } else {
+ $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders);
+ }
+ $this->debug("Leaving serialize_return");
+ }
+
+ /**
+ * sends an HTTP response
+ *
+ * The following fields are set by this function (when successful)
+ *
+ * outgoing_headers
+ * response
+ *
+ * @access private
+ */
+ function send_response() {
+ $this->debug('Enter send_response');
+ if ($this->fault) {
+ $payload = $this->fault->serialize();
+ $this->outgoing_headers[] = "HTTP/1.0 500 Internal Server Error";
+ $this->outgoing_headers[] = "Status: 500 Internal Server Error";
+ } else {
+ $payload = $this->responseSOAP;
+ // Some combinations of PHP+Web server allow the Status
+ // to come through as a header. Since OK is the default
+ // just do nothing.
+ // $this->outgoing_headers[] = "HTTP/1.0 200 OK";
+ // $this->outgoing_headers[] = "Status: 200 OK";
+ }
+ // add debug data if in debug mode
+ if(isset($this->debug_flag) && $this->debug_flag){
+ $payload .= $this->getDebugAsXMLComment();
+ }
+ $this->outgoing_headers[] = "Server: $this->title Server v$this->version";
+ preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev);
+ $this->outgoing_headers[] = "X-SOAP-Server: $this->title/$this->version (".$rev[1].")";
+ // Let the Web server decide about this
+ //$this->outgoing_headers[] = "Connection: Close\r\n";
+ $payload = $this->getHTTPBody($payload);
+ $type = $this->getHTTPContentType();
+ $charset = $this->getHTTPContentTypeCharset();
+ $this->outgoing_headers[] = "Content-Type: $type" . ($charset ? '; charset=' . $charset : '');
+ //begin code to compress payload - by John
+ // NOTE: there is no way to know whether the Web server will also compress
+ // this data.
+ if (strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['accept-encoding'])) {
+ if (strstr($this->headers['accept-encoding'], 'gzip')) {
+ if (function_exists('gzencode')) {
+ if (isset($this->debug_flag) && $this->debug_flag) {
+ $payload .= "<!-- Content being gzipped -->";
+ }
+ $this->outgoing_headers[] = "Content-Encoding: gzip";
+ $payload = gzencode($payload);
+ } else {
+ if (isset($this->debug_flag) && $this->debug_flag) {
+ $payload .= "<!-- Content will not be gzipped: no gzencode -->";
+ }
+ }
+ } elseif (strstr($this->headers['accept-encoding'], 'deflate')) {
+ // Note: MSIE requires gzdeflate output (no Zlib header and checksum),
+ // instead of gzcompress output,
+ // which conflicts with HTTP 1.1 spec (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5)
+ if (function_exists('gzdeflate')) {
+ if (isset($this->debug_flag) && $this->debug_flag) {
+ $payload .= "<!-- Content being deflated -->";
+ }
+ $this->outgoing_headers[] = "Content-Encoding: deflate";
+ $payload = gzdeflate($payload);
+ } else {
+ if (isset($this->debug_flag) && $this->debug_flag) {
+ $payload .= "<!-- Content will not be deflated: no gzcompress -->";
+ }
+ }
+ }
+ }
+ //end code
+ $this->outgoing_headers[] = "Content-Length: ".strlen($payload);
+ reset($this->outgoing_headers);
+ foreach($this->outgoing_headers as $hdr){
+ header($hdr, false);
+ }
+ print $payload;
+ $this->response = join("\r\n",$this->outgoing_headers)."\r\n\r\n".$payload;
+ }
+
+ /**
+ * takes the value that was created by parsing the request
+ * and compares to the method's signature, if available.
+ *
+ * @param string $operation The operation to be invoked
+ * @param array $request The array of parameter values
+ * @return boolean Whether the operation was found
+ * @access private
+ */
+ function verify_method($operation,$request){
+ if(isset($this->wsdl) && is_object($this->wsdl)){
+ if($this->wsdl->getOperationData($operation)){
+ return true;
+ }
+ } elseif(isset($this->operations[$operation])){
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * processes SOAP message received from client
+ *
+ * @param array $headers The HTTP headers
+ * @param string $data unprocessed request data from client
+ * @return mixed value of the message, decoded into a PHP type
+ * @access private
+ */
+ function parseRequest($headers, $data) {
+ $this->debug('Entering parseRequest() for data of length ' . strlen($data) . ' headers:');
+ $this->appendDebug($this->varDump($headers));
+ if (!isset($headers['content-type'])) {
+ $this->setError('Request not of type text/xml (no content-type header)');
+ return false;
+ }
+ if (!strstr($headers['content-type'], 'text/xml')) {
+ $this->setError('Request not of type text/xml');
+ return false;
+ }
+ if (strpos($headers['content-type'], '=')) {
+ $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
+ $this->debug('Got response encoding: ' . $enc);
+ if(preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)){
+ $this->xml_encoding = strtoupper($enc);
+ } else {
+ $this->xml_encoding = 'US-ASCII';
+ }
+ } else {
+ // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
+ $this->xml_encoding = 'ISO-8859-1';
+ }
+ $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser');
+ // parse response, get soap parser obj
+ $parser = new nusoap_parser($data,$this->xml_encoding,'',$this->decode_utf8);
+ // parser debug
+ $this->debug("parser debug: \n".$parser->getDebug());
+ // if fault occurred during message parsing
+ if($err = $parser->getError()){
+ $this->result = 'fault: error in msg parsing: '.$err;
+ $this->fault('SOAP-ENV:Client',"error in msg parsing:\n".$err);
+ // else successfully parsed request into soapval object
+ } else {
+ // get/set methodname
+ $this->methodURI = $parser->root_struct_namespace;
+ $this->methodname = $parser->root_struct_name;
+ $this->debug('methodname: '.$this->methodname.' methodURI: '.$this->methodURI);
+ $this->debug('calling parser->get_soapbody()');
+ $this->methodparams = $parser->get_soapbody();
+ // get SOAP headers
+ $this->requestHeaders = $parser->getHeaders();
+ // get SOAP Header
+ $this->requestHeader = $parser->get_soapheader();
+ // add document for doclit support
+ $this->document = $parser->document;
+ }
+ }
+
+ /**
+ * gets the HTTP body for the current response.
+ *
+ * @param string $soapmsg The SOAP payload
+ * @return string The HTTP body, which includes the SOAP payload
+ * @access private
+ */
+ function getHTTPBody($soapmsg) {
+ return $soapmsg;
+ }
+
+ /**
+ * gets the HTTP content type for the current response.
+ *
+ * Note: getHTTPBody must be called before this.
+ *
+ * @return string the HTTP content type for the current response.
+ * @access private
+ */
+ function getHTTPContentType() {
+ return 'text/xml';
+ }
+
+ /**
+ * gets the HTTP content type charset for the current response.
+ * returns false for non-text content types.
+ *
+ * Note: getHTTPBody must be called before this.
+ *
+ * @return string the HTTP content type charset for the current response.
+ * @access private
+ */
+ function getHTTPContentTypeCharset() {
+ return $this->soap_defencoding;
+ }
+
+ /**
+ * add a method to the dispatch map (this has been replaced by the register method)
+ *
+ * @param string $methodname
+ * @param string $in array of input values
+ * @param string $out array of output values
+ * @access public
+ * @deprecated
+ */
+ function add_to_map($methodname,$in,$out){
+ $this->operations[$methodname] = array('name' => $methodname,'in' => $in,'out' => $out);
+ }
+
+ /**
+ * register a service function with the server
+ *
+ * @param string $name the name of the PHP function, class.method or class..method
+ * @param array $in assoc array of input values: key = param name, value = param type
+ * @param array $out assoc array of output values: key = param name, value = param type
+ * @param mixed $namespace the element namespace for the method or false
+ * @param mixed $soapaction the soapaction for the method or false
+ * @param mixed $style optional (rpc|document) or false Note: when 'document' is specified, parameter and return wrappers are created for you automatically
+ * @param mixed $use optional (encoded|literal) or false
+ * @param string $documentation optional Description to include in WSDL
+ * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
+ * @access public
+ */
+ function register($name,$in=array(),$out=array(),$namespace=false,$soapaction=false,$style=false,$use=false,$documentation='',$encodingStyle=''){
+ if (version_compare(PHP_VERSION, '5.3.0', '<')) {
+ global $HTTP_SERVER_VARS;
+ $http_server_vars_local = $HTTP_SERVER_VARS;
+ }
+
+ if($this->externalWSDLURL){
+ die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.');
+ }
+ if (! $name) {
+ die('You must specify a name when you register an operation');
+ }
+ if (!is_array($in)) {
+ die('You must provide an array for operation inputs');
+ }
+ if (!is_array($out)) {
+ die('You must provide an array for operation outputs');
+ }
+ if(false == $namespace) {
+ }
+ if(false == $soapaction) {
+ if (isset($_SERVER)) {
+ $SERVER_NAME = $_SERVER['SERVER_NAME'];
+ $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
+ $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($http_server_vars_local['HTTPS']) ? $http_server_vars_local['HTTPS'] : 'off');
+ } elseif (isset($http_server_vars_local)) {
+ $SERVER_NAME = $http_server_vars_local['SERVER_NAME'];
+ $SCRIPT_NAME = isset($http_server_vars_local['PHP_SELF']) ? $http_server_vars_local['PHP_SELF'] : $http_server_vars_local['SCRIPT_NAME'];
+ $HTTPS = isset($http_server_vars_local['HTTPS']) ? $http_server_vars_local['HTTPS'] : 'off';
+ } else {
+ $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
+ }
+ if ($HTTPS == '1' || $HTTPS == 'on') {
+ $SCHEME = 'https';
+ } else {
+ $SCHEME = 'http';
+ }
+ $soapaction = "$SCHEME://$SERVER_NAME$SCRIPT_NAME/$name";
+ }
+ if(false == $style) {
+ $style = "rpc";
+ }
+ if(false == $use) {
+ $use = "encoded";
+ }
+ if ($use == 'encoded' && $encodingStyle == '') {
+ $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
+ }
+
+ $this->operations[$name] = array(
+ 'name' => $name,
+ 'in' => $in,
+ 'out' => $out,
+ 'namespace' => $namespace,
+ 'soapaction' => $soapaction,
+ 'style' => $style);
+ if($this->wsdl){
+ $this->wsdl->addOperation($name,$in,$out,$namespace,$soapaction,$style,$use,$documentation,$encodingStyle);
+ }
+ return true;
+ }
+
+ /**
+ * Specify a fault to be returned to the client.
+ * This also acts as a flag to the server that a fault has occured.
+ *
+ * @param string $faultcode
+ * @param string $faultstring
+ * @param string $faultactor
+ * @param string $faultdetail
+ * @access public
+ */
+ function fault($faultcode,$faultstring,$faultactor='',$faultdetail=''){
+ if ($faultdetail == '' && $this->debug_flag) {
+ $faultdetail = $this->getDebug();
+ }
+ $this->fault = new nusoap_fault($faultcode,$faultactor,$faultstring,$faultdetail);
+ $this->fault->soap_defencoding = $this->soap_defencoding;
+ }
+
+ /**
+ * Sets up wsdl object.
+ * Acts as a flag to enable internal WSDL generation
+ *
+ * @param string $serviceName, name of the service
+ * @param mixed $namespace optional 'tns' service namespace or false
+ * @param mixed $endpoint optional URL of service endpoint or false
+ * @param string $style optional (rpc|document) WSDL style (also specified by operation)
+ * @param string $transport optional SOAP transport
+ * @param mixed $schemaTargetNamespace optional 'types' targetNamespace for service schema or false
+ */
+ function configureWSDL($serviceName,$namespace = false,$endpoint = false,$style='rpc', $transport = 'http://schemas.xmlsoap.org/soap/http', $schemaTargetNamespace = false)
+ {
+ if (version_compare(PHP_VERSION, '5.3.0', '<')) {
+ global $HTTP_SERVER_VARS;
+ $http_server_vars_local = $HTTP_SERVER_VARS;
+ }
+
+ if (isset($_SERVER)) {
+ $SERVER_NAME = $_SERVER['SERVER_NAME'];
+ $SERVER_PORT = $_SERVER['SERVER_PORT'];
+ $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
+ $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($http_server_vars_local['HTTPS']) ? $http_server_vars_local['HTTPS'] : 'off');
+ } elseif (isset($http_server_vars_local)) {
+ $SERVER_NAME = $http_server_vars_local['SERVER_NAME'];
+ $SERVER_PORT = $http_server_vars_local['SERVER_PORT'];
+ $SCRIPT_NAME = isset($http_server_vars_local['PHP_SELF']) ? $http_server_vars_local['PHP_SELF'] : $http_server_vars_local['SCRIPT_NAME'];
+ $HTTPS = isset($http_server_vars_local['HTTPS']) ? $http_server_vars_local['HTTPS'] : 'off';
+ } else {
+ $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
+ }
+ // If server name has port number attached then strip it (else port number gets duplicated in WSDL output) (occurred using lighttpd and FastCGI)
+ $colon = strpos($SERVER_NAME,":");
+ if ($colon) {
+ $SERVER_NAME = substr($SERVER_NAME, 0, $colon);
+ }
+ if ($SERVER_PORT == 80) {
+ $SERVER_PORT = '';
+ } else {
+ $SERVER_PORT = ':' . $SERVER_PORT;
+ }
+ if(false == $namespace) {
+ $namespace = "http://$SERVER_NAME/soap/$serviceName";
+ }
+
+ if(false == $endpoint) {
+ if ($HTTPS == '1' || $HTTPS == 'on') {
+ $SCHEME = 'https';
+ } else {
+ $SCHEME = 'http';
+ }
+ $endpoint = "$SCHEME://$SERVER_NAME$SERVER_PORT$SCRIPT_NAME";
+ }
+
+ if(false == $schemaTargetNamespace) {
+ $schemaTargetNamespace = $namespace;
+ }
+
+ $this->wsdl = new wsdl;
+ $this->wsdl->serviceName = $serviceName;
+ $this->wsdl->endpoint = $endpoint;
+ $this->wsdl->namespaces['tns'] = $namespace;
+ $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
+ $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
+ if ($schemaTargetNamespace != $namespace) {
+ $this->wsdl->namespaces['types'] = $schemaTargetNamespace;
+ }
+ $this->wsdl->schemas[$schemaTargetNamespace][0] = new nusoap_xmlschema('', '', $this->wsdl->namespaces);
+ if ($style == 'document') {
+ $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaInfo['elementFormDefault'] = 'qualified';
+ }
+ $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaTargetNamespace = $schemaTargetNamespace;
+ $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/soap/encoding/'][0] = array('location' => '', 'loaded' => true);
+ $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/wsdl/'][0] = array('location' => '', 'loaded' => true);
+ $this->wsdl->bindings[$serviceName.'Binding'] = array(
+ 'name'=>$serviceName.'Binding',
+ 'style'=>$style,
+ 'transport'=>$transport,
+ 'portType'=>$serviceName.'PortType');
+ $this->wsdl->ports[$serviceName.'Port'] = array(
+ 'binding'=>$serviceName.'Binding',
+ 'location'=>$endpoint,
+ 'bindingType'=>'http://schemas.xmlsoap.org/wsdl/soap/');
+ }
+}
+
+/**
+ * Backward compatibility
+ */
+class soap_server extends nusoap_server {
+}
+
+?><?php
+
+
+
+/**
+* parses a WSDL file, allows access to it's data, other utility methods.
+* also builds WSDL structures programmatically.
+*
+* @author Dietrich Ayala <dietrich@ganx4.com>
+* @author Scott Nichol <snichol@users.sourceforge.net>
+* @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
+* @access public
+*/
+class wsdl extends nusoap_base {
+ // URL or filename of the root of this WSDL
+ var $wsdl;
+ // define internal arrays of bindings, ports, operations, messages, etc.
+ var $schemas = array();
+ var $currentSchema;
+ var $message = array();
+ var $complexTypes = array();
+ var $messages = array();
+ var $currentMessage;
+ var $currentOperation;
+ var $portTypes = array();
+ var $currentPortType;
+ var $bindings = array();
+ var $currentBinding;
+ var $ports = array();
+ var $currentPort;
+ var $opData = array();
+ var $status = '';
+ var $documentation = false;
+ var $endpoint = '';
+ // array of wsdl docs to import
+ var $import = array();
+ // parser vars
+ var $parser;
+ var $position = 0;
+ var $depth = 0;
+ var $depth_array = array();
+ // for getting wsdl
+ var $proxyhost = '';
+ var $proxyport = '';
+ var $proxyusername = '';
+ var $proxypassword = '';
+ var $timeout = 0;
+ var $response_timeout = 30;
+ var $curl_options = array(); // User-specified cURL options
+ var $use_curl = false; // whether to always try to use cURL
+ // for HTTP authentication
+ var $username = ''; // Username for HTTP authentication
+ var $password = ''; // Password for HTTP authentication
+ var $authtype = ''; // Type of HTTP authentication
+ var $certRequest = array(); // Certificate for HTTP SSL authentication
+
+ /**
+ * constructor
+ *
+ * @param string $wsdl WSDL document URL
+ * @param string $proxyhost
+ * @param string $proxyport
+ * @param string $proxyusername
+ * @param string $proxypassword
+ * @param integer $timeout set the connection timeout
+ * @param integer $response_timeout set the response timeout
+ * @param array $curl_options user-specified cURL options
+ * @param boolean $use_curl try to use cURL
+ * @access public
+ */
+ function __construct($wsdl = '',$proxyhost=false,$proxyport=false,$proxyusername=false,$proxypassword=false,$timeout=0,$response_timeout=30,$curl_options=null,$use_curl=false){
+ parent::__construct();
+ $this->debug("ctor wsdl=$wsdl timeout=$timeout response_timeout=$response_timeout");
+ $this->proxyhost = $proxyhost;
+ $this->proxyport = $proxyport;
+ $this->proxyusername = $proxyusername;
+ $this->proxypassword = $proxypassword;
+ $this->timeout = $timeout;
+ $this->response_timeout = $response_timeout;
+ if (is_array($curl_options))
+ $this->curl_options = $curl_options;
+ $this->use_curl = $use_curl;
+ $this->fetchWSDL($wsdl);
+ }
+
+ /**
+ * fetches the WSDL document and parses it
+ *
+ * @access public
+ */
+ function fetchWSDL($wsdl) {
+ $this->debug("parse and process WSDL path=$wsdl");
+ $this->wsdl = $wsdl;
+ // parse wsdl file
+ if ($this->wsdl != "") {
+ $this->parseWSDL($this->wsdl);
+ }
+ // imports
+ // TODO: handle imports more properly, grabbing them in-line and nesting them
+ $imported_urls = array();
+ $imported = 1;
+ while ($imported > 0) {
+ $imported = 0;
+ // Schema imports
+ foreach ($this->schemas as $ns => $list) {
+ foreach ($list as $xs) {
+ $wsdlparts = parse_url($this->wsdl); // this is bogusly simple!
+ foreach ($xs->imports as $ns2 => $list2) {
+ for ($ii = 0; $ii < count($list2); $ii++) {
+ if (! $list2[$ii]['loaded']) {
+ $this->schemas[$ns]->imports[$ns2][$ii]['loaded'] = true;
+ $url = $list2[$ii]['location'];
+ if ($url != '') {
+ $urlparts = parse_url($url);
+ if (!isset($urlparts['host'])) {
+ $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' .$wsdlparts['port'] : '') .
+ substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path'];
+ }
+ if (! in_array($url, $imported_urls)) {
+ $this->parseWSDL($url);
+ $imported++;
+ $imported_urls[] = $url;
+ }
+ } else {
+ $this->debug("Unexpected scenario: empty URL for unloaded import");
+ }
+ }
+ }
+ }
+ }
+ }
+ // WSDL imports
+ $wsdlparts = parse_url($this->wsdl); // this is bogusly simple!
+ foreach ($this->import as $ns => $list) {
+ for ($ii = 0; $ii < count($list); $ii++) {
+ if (! $list[$ii]['loaded']) {
+ $this->import[$ns][$ii]['loaded'] = true;
+ $url = $list[$ii]['location'];
+ if ($url != '') {
+ $urlparts = parse_url($url);
+ if (!isset($urlparts['host'])) {
+ $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' . $wsdlparts['port'] : '') .
+ substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path'];
+ }
+ if (! in_array($url, $imported_urls)) {
+ $this->parseWSDL($url);
+ $imported++;
+ $imported_urls[] = $url;
+ }
+ } else {
+ $this->debug("Unexpected scenario: empty URL for unloaded import");
+ }
+ }
+ }
+ }
+ }
+ // add new data to operation data
+ foreach($this->bindings as $binding => $bindingData) {
+ if (isset($bindingData['operations']) && is_array($bindingData['operations'])) {
+ foreach($bindingData['operations'] as $operation => $data) {
+ $this->debug('post-parse data gathering for ' . $operation);
+ $this->bindings[$binding]['operations'][$operation]['input'] =
+ isset($this->bindings[$binding]['operations'][$operation]['input']) ?
+ array_merge($this->bindings[$binding]['operations'][$operation]['input'], $this->portTypes[ $bindingData['portType'] ][$operation]['input']) :
+ $this->portTypes[ $bindingData['portType'] ][$operation]['input'];
+ $this->bindings[$binding]['operations'][$operation]['output'] =
+ isset($this->bindings[$binding]['operations'][$operation]['output']) ?
+ array_merge($this->bindings[$binding]['operations'][$operation]['output'], $this->portTypes[ $bindingData['portType'] ][$operation]['output']) :
+ $this->portTypes[ $bindingData['portType'] ][$operation]['output'];
+ if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ])){
+ $this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ];
+ }
+ if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ])){
+ $this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ];
+ }
+ // Set operation style if necessary, but do not override one already provided
+ if (isset($bindingData['style']) && !isset($this->bindings[$binding]['operations'][$operation]['style'])) {
+ $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style'];
+ }
+ $this->bindings[$binding]['operations'][$operation]['transport'] = isset($bindingData['transport']) ? $bindingData['transport'] : '';
+ $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[ $bindingData['portType'] ][$operation]['documentation']) ? $this->portTypes[ $bindingData['portType'] ][$operation]['documentation'] : '';
+ $this->bindings[$binding]['operations'][$operation]['endpoint'] = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : '';
+ }
+ }
+ }
+ }
+
+ /**
+ * parses the wsdl document
+ *
+ * @param string $wsdl path or URL
+ * @access private
+ */
+ function parseWSDL($wsdl = '') {
+ $this->debug("parse WSDL at path=$wsdl");
+
+ if ($wsdl == '') {
+ $this->debug('no wsdl passed to parseWSDL()!!');
+ $this->setError('no wsdl passed to parseWSDL()!!');
+ return false;
+ }
+
+ // parse $wsdl for url format
+ $wsdl_props = parse_url($wsdl);
+
+ if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'http' || $wsdl_props['scheme'] == 'https')) {
+ $this->debug('getting WSDL http(s) URL ' . $wsdl);
+ // get wsdl
+ $tr = new soap_transport_http($wsdl, $this->curl_options, $this->use_curl);
+ $tr->request_method = 'GET';
+ $tr->useSOAPAction = false;
+ if($this->proxyhost && $this->proxyport){
+ $tr->setProxy($this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword);
+ }
+ if ($this->authtype != '') {
+ $tr->setCredentials($this->username, $this->password, $this->authtype, array(), $this->certRequest);
+ }
+ $tr->setEncoding('gzip, deflate');
+ $wsdl_string = $tr->send('', $this->timeout, $this->response_timeout);
+ //$this->debug("WSDL request\n" . $tr->outgoing_payload);
+ //$this->debug("WSDL response\n" . $tr->incoming_payload);
+ $this->appendDebug($tr->getDebug());
+ // catch errors
+ if($err = $tr->getError() ){
+ $errstr = 'Getting ' . $wsdl . ' - HTTP ERROR: '.$err;
+ $this->debug($errstr);
+ $this->setError($errstr);
+ unset($tr);
+ return false;
+ }
+ unset($tr);
+ $this->debug("got WSDL URL");
+ } else {
+ // $wsdl is not http(s), so treat it as a file URL or plain file path
+ if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'file') && isset($wsdl_props['path'])) {
+ $path = isset($wsdl_props['host']) ? ($wsdl_props['host'] . ':' . $wsdl_props['path']) : $wsdl_props['path'];
+ } else {
+ $path = $wsdl;
+ }
+ $this->debug('getting WSDL file ' . $path);
+ if ($fp = @fopen($path, 'r')) {
+ $wsdl_string = '';
+ while ($data = fread($fp, 32768)) {
+ $wsdl_string .= $data;
+ }
+ fclose($fp);
+ } else {
+ $errstr = "Bad path to WSDL file $path";
+ $this->debug($errstr);
+ $this->setError($errstr);
+ return false;
+ }
+ }
+ $this->debug('Parse WSDL');
+ // end new code added
+ // Create an XML parser.
+ $this->parser = xml_parser_create();
+ // Set the options for parsing the XML data.
+ // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
+ xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
+ // Set the object for the parser.
+ xml_set_object($this->parser, $this);
+ // Set the element handlers for the parser.
+ xml_set_element_handler($this->parser, 'start_element', 'end_element');
+ xml_set_character_data_handler($this->parser, 'character_data');
+ // Parse the XML file.
+ if (!xml_parse($this->parser, $wsdl_string, true)) {
+ // Display an error message.
+ $errstr = sprintf(
+ 'XML error parsing WSDL from %s on line %d: %s',
+ $wsdl,
+ xml_get_current_line_number($this->parser),
+ xml_error_string(xml_get_error_code($this->parser))
+ );
+ $this->debug($errstr);
+ $this->debug("XML payload:\n" . $wsdl_string);
+ $this->setError($errstr);
+ return false;
+ }
+ // free the parser
+ xml_parser_free($this->parser);
+ $this->debug('Parsing WSDL done');
+ // catch wsdl parse errors
+ if($this->getError()){
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * start-element handler
+ *
+ * @param string $parser XML parser object
+ * @param string $name element name
+ * @param string $attrs associative array of attributes
+ * @access private
+ */
+ function start_element($parser, $name, $attrs)
+ {
+ if ($this->status == 'schema') {
+ $this->currentSchema->schemaStartElement($parser, $name, $attrs);
+ $this->appendDebug($this->currentSchema->getDebug());
+ $this->currentSchema->clearDebug();
+ } elseif (preg_match('/schema$/', $name)) {
+ $this->debug('Parsing WSDL schema');
+ // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")");
+ $this->status = 'schema';
+ $this->currentSchema = new nusoap_xmlschema('', '', $this->namespaces);
+ $this->currentSchema->schemaStartElement($parser, $name, $attrs);
+ $this->appendDebug($this->currentSchema->getDebug());
+ $this->currentSchema->clearDebug();
+ } else {
+ // position in the total number of elements, starting from 0
+ $pos = $this->position++;
+ $depth = $this->depth++;
+ // set self as current value for this depth
+ $this->depth_array[$depth] = $pos;
+ $this->message[$pos] = array('cdata' => '');
+ // process attributes
+ if (count($attrs) > 0) {
+ // register namespace declarations
+ foreach($attrs as $k => $v) {
+ if (preg_match('/^xmlns/',$k)) {
+ if ($ns_prefix = substr(strrchr($k, ':'), 1)) {
+ $this->namespaces[$ns_prefix] = $v;
+ } else {
+ $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
+ }
+ if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema') {
+ $this->XMLSchemaVersion = $v;
+ $this->namespaces['xsi'] = $v . '-instance';
+ }
+ }
+ }
+ // expand each attribute prefix to its namespace
+ foreach($attrs as $k => $v) {
+ $k = strpos($k, ':') ? $this->expandQname($k) : $k;
+ if ($k != 'location' && $k != 'soapAction' && $k != 'namespace') {
+ $v = strpos($v, ':') ? $this->expandQname($v) : $v;
+ }
+ $eAttrs[$k] = $v;
+ }
+ $attrs = $eAttrs;
+ } else {
+ $attrs = array();
+ }
+ // get element prefix, namespace and name
+ if (preg_match('/:/', $name)) {
+ // get ns prefix
+ $prefix = substr($name, 0, strpos($name, ':'));
+ // get ns
+ $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : '';
+ // get unqualified name
+ $name = substr(strstr($name, ':'), 1);
+ }
+ // process attributes, expanding any prefixes to namespaces
+ // find status, register data
+ switch ($this->status) {
+ case 'message':
+ if ($name == 'part') {
+ if (isset($attrs['type'])) {
+ $this->debug("msg " . $this->currentMessage . ": found part (with type) $attrs[name]: " . implode(',', $attrs));
+ $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type'];
+ }
+ if (isset($attrs['element'])) {
+ $this->debug("msg " . $this->currentMessage . ": found part (with element) $attrs[name]: " . implode(',', $attrs));
+ $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element'] . '^';
+ }
+ }
+ break;
+ case 'portType':
+ switch ($name) {
+ case 'operation':
+ $this->currentPortOperation = $attrs['name'];
+ $this->debug("portType $this->currentPortType operation: $this->currentPortOperation");
+ if (isset($attrs['parameterOrder'])) {
+ $this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder'];
+ }
+ break;
+ case 'documentation':
+ $this->documentation = true;
+ break;
+ // merge input/output data
+ default:
+ $m = isset($attrs['message']) ? $this->getLocalPart($attrs['message']) : '';
+ $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m;
+ break;
+ }
+ break;
+ case 'binding':
+ switch ($name) {
+ case 'binding':
+ // get ns prefix
+ if (isset($attrs['style'])) {
+ $this->bindings[$this->currentBinding]['prefix'] = $prefix;
+ }
+ $this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs);
+ break;
+ case 'header':
+ $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
+ break;
+ case 'operation':
+ if (isset($attrs['soapAction'])) {
+ $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction'];
+ }
+ if (isset($attrs['style'])) {
+ $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style'];
+ }
+ if (isset($attrs['name'])) {
+ $this->currentOperation = $attrs['name'];
+ $this->debug("current binding operation: $this->currentOperation");
+ $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name'] = $attrs['name'];
+ $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding'] = $this->currentBinding;
+ $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : '';
+ }
+ break;
+ case 'input':
+ $this->opStatus = 'input';
+ break;
+ case 'output':
+ $this->opStatus = 'output';
+ break;
+ case 'body':
+ if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) {
+ $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs);
+ } else {
+ $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs;
+ }
+ break;
+ }
+ break;
+ case 'service':
+ switch ($name) {
+ case 'port':
+ $this->currentPort = $attrs['name'];
+ $this->debug('current port: ' . $this->currentPort);
+ $this->ports[$this->currentPort]['binding'] = $this->getLocalPart($attrs['binding']);
+
+ break;
+ case 'address':
+ $this->ports[$this->currentPort]['location'] = $attrs['location'];
+ $this->ports[$this->currentPort]['bindingType'] = $namespace;
+ $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['bindingType'] = $namespace;
+ $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['endpoint'] = $attrs['location'];
+ break;
+ }
+ break;
+ }
+ // set status
+ switch ($name) {
+ case 'import':
+ if (isset($attrs['location'])) {
+ $this->import[$attrs['namespace']][] = array('location' => $attrs['location'], 'loaded' => false);
+ $this->debug('parsing import ' . $attrs['namespace']. ' - ' . $attrs['location'] . ' (' . count($this->import[$attrs['namespace']]).')');
+ } else {
+ $this->import[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
+ if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
+ $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace'];
+ }
+ $this->debug('parsing import ' . $attrs['namespace']. ' - [no location] (' . count($this->import[$attrs['namespace']]).')');
+ }
+ break;
+ //wait for schema
+ //case 'types':
+ // $this->status = 'schema';
+ // break;
+ case 'message':
+ $this->status = 'message';
+ $this->messages[$attrs['name']] = array();
+ $this->currentMessage = $attrs['name'];
+ break;
+ case 'portType':
+ $this->status = 'portType';
+ $this->portTypes[$attrs['name']] = array();
+ $this->currentPortType = $attrs['name'];
+ break;
+ case "binding":
+ if (isset($attrs['name'])) {
+ // get binding name
+ if (strpos($attrs['name'], ':')) {
+ $this->currentBinding = $this->getLocalPart($attrs['name']);
+ } else {
+ $this->currentBinding = $attrs['name'];
+ }
+ $this->status = 'binding';
+ $this->bindings[$this->currentBinding]['portType'] = $this->getLocalPart($attrs['type']);
+ $this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']);
+ }
+ break;
+ case 'service':
+ $this->serviceName = $attrs['name'];
+ $this->status = 'service';
+ $this->debug('current service: ' . $this->serviceName);
+ break;
+ case 'definitions':
+ foreach ($attrs as $name => $value) {
+ $this->wsdl_info[$name] = $value;
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * end-element handler
+ *
+ * @param string $parser XML parser object
+ * @param string $name element name
+ * @access private
+ */
+ function end_element($parser, $name){
+ // unset schema status
+ if (/*preg_match('/types$/', $name) ||*/ preg_match('/schema$/', $name)) {
+ $this->status = "";
+ $this->appendDebug($this->currentSchema->getDebug());
+ $this->currentSchema->clearDebug();
+ $this->schemas[$this->currentSchema->schemaTargetNamespace][] = $this->currentSchema;
+ $this->debug('Parsing WSDL schema done');
+ }
+ if ($this->status == 'schema') {
+ $this->currentSchema->schemaEndElement($parser, $name);
+ } else {
+ // bring depth down a notch
+ $this->depth--;
+ }
+ // end documentation
+ if ($this->documentation) {
+ //TODO: track the node to which documentation should be assigned; it can be a part, message, etc.
+ //$this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation;
+ $this->documentation = false;
+ }
+ }
+
+ /**
+ * element content handler
+ *
+ * @param string $parser XML parser object
+ * @param string $data element content
+ * @access private
+ */
+ function character_data($parser, $data)
+ {
+ $pos = isset($this->depth_array[$this->depth]) ? $this->depth_array[$this->depth] : 0;
+ if (isset($this->message[$pos]['cdata'])) {
+ $this->message[$pos]['cdata'] .= $data;
+ }
+ if ($this->documentation) {
+ $this->documentation .= $data;
+ }
+ }
+
+ /**
+ * if authenticating, set user credentials here
+ *
+ * @param string $username
+ * @param string $password
+ * @param string $authtype (basic|digest|certificate|ntlm)
+ * @param array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
+ * @access public
+ */
+ function setCredentials($username, $password, $authtype = 'basic', $certRequest = array()) {
+ $this->debug("setCredentials username=$username authtype=$authtype certRequest=");
+ $this->appendDebug($this->varDump($certRequest));
+ $this->username = $username;
+ $this->password = $password;
+ $this->authtype = $authtype;
+ $this->certRequest = $certRequest;
+ }
+
+ function getBindingData($binding)
+ {
+ if (is_array($this->bindings[$binding])) {
+ return $this->bindings[$binding];
+ }
+ }
+
+ /**
+ * returns an assoc array of operation names => operation data
+ *
+ * @param string $portName WSDL port name
+ * @param string $bindingType eg: soap, smtp, dime (only soap and soap12 are currently supported)
+ * @return array
+ * @access public
+ */
+ function getOperations($portName = '', $bindingType = 'soap') {
+ $ops = array();
+ if ($bindingType == 'soap') {
+ $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
+ } elseif ($bindingType == 'soap12') {
+ $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
+ } else {
+ $this->debug("getOperations bindingType $bindingType may not be supported");
+ }
+ $this->debug("getOperations for port '$portName' bindingType $bindingType");
+ // loop thru ports
+ foreach($this->ports as $port => $portData) {
+ $this->debug("getOperations checking port $port bindingType " . $portData['bindingType']);
+ if ($portName == '' || $port == $portName) {
+ // binding type of port matches parameter
+ if ($portData['bindingType'] == $bindingType) {
+ $this->debug("getOperations found port $port bindingType $bindingType");
+ //$this->debug("port data: " . $this->varDump($portData));
+ //$this->debug("bindings: " . $this->varDump($this->bindings[ $portData['binding'] ]));
+ // merge bindings
+ if (isset($this->bindings[ $portData['binding'] ]['operations'])) {
+ $ops = array_merge ($ops, $this->bindings[ $portData['binding'] ]['operations']);
+ }
+ }
+ }
+ }
+ if (count($ops) == 0) {
+ $this->debug("getOperations found no operations for port '$portName' bindingType $bindingType");
+ }
+ return $ops;
+ }
+
+ /**
+ * returns an associative array of data necessary for calling an operation
+ *
+ * @param string $operation name of operation
+ * @param string $bindingType type of binding eg: soap, soap12
+ * @return array
+ * @access public
+ */
+ function getOperationData($operation, $bindingType = 'soap')
+ {
+ if ($bindingType == 'soap') {
+ $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
+ } elseif ($bindingType == 'soap12') {
+ $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
+ }
+ // loop thru ports
+ foreach($this->ports as $port => $portData) {
+ // binding type of port matches parameter
+ if ($portData['bindingType'] == $bindingType) {
+ // get binding
+ //foreach($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
+ foreach(array_keys($this->bindings[ $portData['binding'] ]['operations']) as $bOperation) {
+ // note that we could/should also check the namespace here
+ if ($operation == $bOperation) {
+ $opData = $this->bindings[ $portData['binding'] ]['operations'][$operation];
+ return $opData;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * returns an associative array of data necessary for calling an operation
+ *
+ * @param string $soapAction soapAction for operation
+ * @param string $bindingType type of binding eg: soap, soap12
+ * @return array
+ * @access public
+ */
+ function getOperationDataForSoapAction($soapAction, $bindingType = 'soap') {
+ if ($bindingType == 'soap') {
+ $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
+ } elseif ($bindingType == 'soap12') {
+ $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
+ }
+ // loop thru ports
+ foreach($this->ports as $port => $portData) {
+ // binding type of port matches parameter
+ if ($portData['bindingType'] == $bindingType) {
+ // loop through operations for the binding
+ foreach ($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
+ if ($opData['soapAction'] == $soapAction) {
+ return $opData;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * returns an array of information about a given type
+ * returns false if no type exists by the given name
+ *
+ * typeDef = array(
+ * 'elements' => array(), // refs to elements array
+ * 'restrictionBase' => '',
+ * 'phpType' => '',
+ * 'order' => '(sequence|all)',
+ * 'attrs' => array() // refs to attributes array
+ * )
+ *
+ * @param string $type the type
+ * @param string $ns namespace (not prefix) of the type
+ * @return mixed
+ * @access public
+ * @see nusoap_xmlschema
+ */
+ function getTypeDef($type, $ns) {
+ $this->debug("in getTypeDef: type=$type, ns=$ns");
+ if ((! $ns) && isset($this->namespaces['tns'])) {
+ $ns = $this->namespaces['tns'];
+ $this->debug("in getTypeDef: type namespace forced to $ns");
+ }
+ if (!isset($this->schemas[$ns])) {
+ foreach ($this->schemas as $ns0 => $schema0) {
+ if (strcasecmp($ns, $ns0) == 0) {
+ $this->debug("in getTypeDef: replacing schema namespace $ns with $ns0");
+ $ns = $ns0;
+ break;
+ }
+ }
+ }
+ if (isset($this->schemas[$ns])) {
+ $this->debug("in getTypeDef: have schema for namespace $ns");
+ for ($i = 0; $i < count($this->schemas[$ns]); $i++) {
+ $xs = &$this->schemas[$ns][$i];
+ $t = $xs->getTypeDef($type);
+ $this->appendDebug($xs->getDebug());
+ $xs->clearDebug();
+ if ($t) {
+ $this->debug("in getTypeDef: found type $type");
+ if (!isset($t['phpType'])) {
+ // get info for type to tack onto the element
+ $uqType = substr($t['type'], strrpos($t['type'], ':') + 1);
+ $ns = substr($t['type'], 0, strrpos($t['type'], ':'));
+ $etype = $this->getTypeDef($uqType, $ns);
+ if ($etype) {
+ $this->debug("found type for [element] $type:");
+ $this->debug($this->varDump($etype));
+ if (isset($etype['phpType'])) {
+ $t['phpType'] = $etype['phpType'];
+ }
+ if (isset($etype['elements'])) {
+ $t['elements'] = $etype['elements'];
+ }
+ if (isset($etype['attrs'])) {
+ $t['attrs'] = $etype['attrs'];
+ }
+ } else {
+ $this->debug("did not find type for [element] $type");
+ }
+ }
+ return $t;
+ }
+ }
+ $this->debug("in getTypeDef: did not find type $type");
+ } else {
+ $this->debug("in getTypeDef: do not have schema for namespace $ns");
+ }
+ return false;
+ }
+
+ /**
+ * prints html description of services
+ *
+ * @access private
+ */
+ function webDescription(){
+ if (version_compare(PHP_VERSION, '5.3.0', '<')) {
+ global $HTTP_SERVER_VARS;
+ $http_server_vars_local = $HTTP_SERVER_VARS;
+ }
+ if (isset($_SERVER)) {
+ $PHP_SELF = $_SERVER['PHP_SELF'];
+ } elseif (isset($http_server_vars_local)) {
+ $PHP_SELF = $http_server_vars_local['PHP_SELF'];
+ } else {
+ $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
+ }
+
+ $b = '
+ <html><head><title>NuSOAP: '.$this->serviceName.'</title>
+ <style type="text/css">
+ body { font-family: arial; color: #000000; background-color: #ffffff; margin: 0px 0px 0px 0px; }
+ p { font-family: arial; color: #000000; margin-top: 0px; margin-bottom: 12px; }
+ pre { background-color: silver; padding: 5px; font-family: Courier New; font-size: x-small; color: #000000;}
+ ul { margin-top: 10px; margin-left: 20px; }
+ li { list-style-type: none; margin-top: 10px; color: #000000; }
+ .content{
+ margin-left: 0px; padding-bottom: 2em; }
+ .nav {
+ padding-top: 10px; padding-bottom: 10px; padding-left: 15px; font-size: .70em;
+ margin-top: 10px; margin-left: 0px; color: #000000;
+ background-color: #ccccff; width: 20%; margin-left: 20px; margin-top: 20px; }
+ .title {
+ font-family: arial; font-size: 26px; color: #ffffff;
+ background-color: #999999; width: 100%;
+ margin-left: 0px; margin-right: 0px;
+ padding-top: 10px; padding-bottom: 10px;}
+ .hidden {
+ position: absolute; visibility: hidden; z-index: 200; left: 250px; top: 100px;
+ font-family: arial; overflow: hidden; width: 600;
+ padding: 20px; font-size: 10px; background-color: #999999;
+ layer-background-color:#FFFFFF; }
+ a,a:active { color: charcoal; font-weight: bold; }
+ a:visited { color: #666666; font-weight: bold; }
+ a:hover { color: cc3300; font-weight: bold; }
+ </style>
+ <script language="JavaScript" type="text/javascript">
+ <!--
+ // POP-UP CAPTIONS...
+ function lib_bwcheck(){ //Browsercheck (needed)
+ this.ver=navigator.appVersion
+ this.agent=navigator.userAgent
+ this.dom=document.getElementById?1:0
+ this.opera5=this.agent.indexOf("Opera 5")>-1
+ this.ie5=(this.ver.indexOf("MSIE 5")>-1 && this.dom && !this.opera5)?1:0;
+ this.ie6=(this.ver.indexOf("MSIE 6")>-1 && this.dom && !this.opera5)?1:0;
+ this.ie4=(document.all && !this.dom && !this.opera5)?1:0;
+ this.ie=this.ie4||this.ie5||this.ie6
+ this.mac=this.agent.indexOf("Mac")>-1
+ this.ns6=(this.dom && parseInt(this.ver) >= 5) ?1:0;
+ this.ns4=(document.layers && !this.dom)?1:0;
+ this.bw=(this.ie6 || this.ie5 || this.ie4 || this.ns4 || this.ns6 || this.opera5)
+ return this
+ }
+ var bw = new lib_bwcheck()
+ //Makes crossbrowser object.
+ function makeObj(obj){
+ this.evnt=bw.dom? document.getElementById(obj):bw.ie4?document.all[obj]:bw.ns4?document.layers[obj]:0;
+ if(!this.evnt) return false
+ this.css=bw.dom||bw.ie4?this.evnt.style:bw.ns4?this.evnt:0;
+ this.wref=bw.dom||bw.ie4?this.evnt:bw.ns4?this.css.document:0;
+ this.writeIt=b_writeIt;
+ return this
+ }
+ // A unit of measure that will be added when setting the position of a layer.
+ //var px = bw.ns4||window.opera?"":"px";
+ function b_writeIt(text){
+ if (bw.ns4){this.wref.write(text);this.wref.close()}
+ else this.wref.innerHTML = text
+ }
+ //Shows the messages
+ var oDesc;
+ function popup(divid){
+ if(oDesc = new makeObj(divid)){
+ oDesc.css.visibility = "visible"
+ }
+ }
+ function popout(){ // Hides message
+ if(oDesc) oDesc.css.visibility = "hidden"
+ }
+ //-->
+ </script>
+ </head>
+ <body>
+ <div class=content>
+ <br><br>
+ <div class=title>'.$this->serviceName.'</div>
+ <div class=nav>
+ <p>View the <a href="'.$PHP_SELF.'?wsdl">WSDL</a> for the service.
+ Click on an operation name to view it's details.</p>
+ <ul>';
+ foreach($this->getOperations() as $op => $data){
+ $b .= "<li><a href='#' onclick=\"popout();popup('$op')\">$op</a></li>";
+ // create hidden div
+ $b .= "<div id='$op' class='hidden'>
+ <a href='#' onclick='popout()'><font color='#ffffff'>Close</font></a><br><br>";
+ foreach($data as $donnie => $marie){ // loop through opdata
+ if($donnie == 'input' || $donnie == 'output'){ // show input/output data
+ $b .= "<font color='white'>".ucfirst($donnie).':</font><br>';
+ foreach($marie as $captain => $tenille){ // loop through data
+ if($captain == 'parts'){ // loop thru parts
+ $b .= " $captain:<br>";
+ //if(is_array($tenille)){
+ foreach($tenille as $joanie => $chachi){
+ $b .= " $joanie: $chachi<br>";
+ }
+ //}
+ } else {
+ $b .= " $captain: $tenille<br>";
+ }
+ }
+ } else {
+ $b .= "<font color='white'>".ucfirst($donnie).":</font> $marie<br>";
+ }
+ }
+ $b .= '</div>';
+ }
+ $b .= '
+ <ul>
+ </div>
+ </div></body></html>';
+ return $b;
+ }
+
+ /**
+ * serialize the parsed wsdl
+ *
+ * @param mixed $debug whether to put debug=1 in endpoint URL
+ * @return string serialization of WSDL
+ * @access public
+ */
+ function serialize($debug = 0)
+ {
+ $xml = '<?xml version="1.0" encoding="ISO-8859-1"?>';
+ $xml .= "\n<definitions";
+ foreach($this->namespaces as $k => $v) {
+ $xml .= " xmlns:$k=\"$v\"";
+ }
+ // 10.9.02 - add poulter fix for wsdl and tns declarations
+ if (isset($this->namespaces['wsdl'])) {
+ $xml .= " xmlns=\"" . $this->namespaces['wsdl'] . "\"";
+ }
+ if (isset($this->namespaces['tns'])) {
+ $xml .= " targetNamespace=\"" . $this->namespaces['tns'] . "\"";
+ }
+ $xml .= '>';
+ // imports
+ if (sizeof($this->import) > 0) {
+ foreach($this->import as $ns => $list) {
+ foreach ($list as $ii) {
+ if ($ii['location'] != '') {
+ $xml .= '<import location="' . $ii['location'] . '" namespace="' . $ns . '" />';
+ } else {
+ $xml .= '<import namespace="' . $ns . '" />';
+ }
+ }
+ }
+ }
+ // types
+ if (count($this->schemas)>=1) {
+ $xml .= "\n<types>\n";
+ foreach ($this->schemas as $ns => $list) {
+ foreach ($list as $xs) {
+ $xml .= $xs->serializeSchema();
+ }
+ }
+ $xml .= '</types>';
+ }
+ // messages
+ if (count($this->messages) >= 1) {
+ foreach($this->messages as $msgName => $msgParts) {
+ $xml .= "\n<message name=\"" . $msgName . '">';
+ if(is_array($msgParts)){
+ foreach($msgParts as $partName => $partType) {
+ // print 'serializing '.$partType.', sv: '.$this->XMLSchemaVersion.'<br>';
+ if (strpos($partType, ':')) {
+ $typePrefix = $this->getPrefixFromNamespace($this->getPrefix($partType));
+ } elseif (isset($this->typemap[$this->namespaces['xsd']][$partType])) {
+ // print 'checking typemap: '.$this->XMLSchemaVersion.'<br>';
+ $typePrefix = 'xsd';
+ } else {
+ foreach($this->typemap as $ns => $types) {
+ if (isset($types[$partType])) {
+ $typePrefix = $this->getPrefixFromNamespace($ns);
+ }
+ }
+ if (!isset($typePrefix)) {
+ die("$partType has no namespace!");
+ }
+ }
+ $ns = $this->getNamespaceFromPrefix($typePrefix);
+ $localPart = $this->getLocalPart($partType);
+ $typeDef = $this->getTypeDef($localPart, $ns);
+ if ($typeDef['typeClass'] == 'element') {
+ $elementortype = 'element';
+ if (substr($localPart, -1) == '^') {
+ $localPart = substr($localPart, 0, -1);
+ }
+ } else {
+ $elementortype = 'type';
+ }
+ $xml .= "\n" . ' <part name="' . $partName . '" ' . $elementortype . '="' . $typePrefix . ':' . $localPart . '" />';
+ }
+ }
+ $xml .= '</message>';
+ }
+ }
+ // bindings & porttypes
+ if (count($this->bindings) >= 1) {
+ $binding_xml = '';
+ $portType_xml = '';
+ foreach($this->bindings as $bindingName => $attrs) {
+ $binding_xml .= "\n<binding name=\"" . $bindingName . '" type="tns:' . $attrs['portType'] . '">';
+ $binding_xml .= "\n" . ' <soap:binding style="' . $attrs['style'] . '" transport="' . $attrs['transport'] . '"/>';
+ $portType_xml .= "\n<portType name=\"" . $attrs['portType'] . '">';
+ foreach($attrs['operations'] as $opName => $opParts) {
+ $binding_xml .= "\n" . ' <operation name="' . $opName . '">';
+ $binding_xml .= "\n" . ' <soap:operation soapAction="' . $opParts['soapAction'] . '" style="'. $opParts['style'] . '"/>';
+ if (isset($opParts['input']['encodingStyle']) && $opParts['input']['encodingStyle'] != '') {
+ $enc_style = ' encodingStyle="' . $opParts['input']['encodingStyle'] . '"';
+ } else {
+ $enc_style = '';
+ }
+ $binding_xml .= "\n" . ' <input><soap:body use="' . $opParts['input']['use'] . '" namespace="' . $opParts['input']['namespace'] . '"' . $enc_style . '/></input>';
+ if (isset($opParts['output']['encodingStyle']) && $opParts['output']['encodingStyle'] != '') {
+ $enc_style = ' encodingStyle="' . $opParts['output']['encodingStyle'] . '"';
+ } else {
+ $enc_style = '';
+ }
+ $binding_xml .= "\n" . ' <output><soap:body use="' . $opParts['output']['use'] . '" namespace="' . $opParts['output']['namespace'] . '"' . $enc_style . '/></output>';
+ $binding_xml .= "\n" . ' </operation>';
+ $portType_xml .= "\n" . ' <operation name="' . $opParts['name'] . '"';
+ if (isset($opParts['parameterOrder'])) {
+ $portType_xml .= ' parameterOrder="' . $opParts['parameterOrder'] . '"';
+ }
+ $portType_xml .= '>';
+ if(isset($opParts['documentation']) && $opParts['documentation'] != '') {
+ $portType_xml .= "\n" . ' <documentation>' . htmlspecialchars($opParts['documentation']) . '</documentation>';
+ }
+ $portType_xml .= "\n" . ' <input message="tns:' . $opParts['input']['message'] . '"/>';
+ $portType_xml .= "\n" . ' <output message="tns:' . $opParts['output']['message'] . '"/>';
+ $portType_xml .= "\n" . ' </operation>';
+ }
+ $portType_xml .= "\n" . '</portType>';
+ $binding_xml .= "\n" . '</binding>';
+ }
+ $xml .= $portType_xml . $binding_xml;
+ }
+ // services
+ $xml .= "\n<service name=\"" . $this->serviceName . '">';
+ if (count($this->ports) >= 1) {
+ foreach($this->ports as $pName => $attrs) {
+ $xml .= "\n" . ' <port name="' . $pName . '" binding="tns:' . $attrs['binding'] . '">';
+ $xml .= "\n" . ' <soap:address location="' . $attrs['location'] . ($debug ? '?debug=1' : '') . '"/>';
+ $xml .= "\n" . ' </port>';
+ }
+ }
+ $xml .= "\n" . '</service>';
+ return $xml . "\n</definitions>";
+ }
+
+ /**
+ * determine whether a set of parameters are unwrapped
+ * when they are expect to be wrapped, Microsoft-style.
+ *
+ * @param string $type the type (element name) of the wrapper
+ * @param array $parameters the parameter values for the SOAP call
+ * @return boolean whether they parameters are unwrapped (and should be wrapped)
+ * @access private
+ */
+ function parametersMatchWrapped($type, &$parameters) {
+ $this->debug("in parametersMatchWrapped type=$type, parameters=");
+ $this->appendDebug($this->varDump($parameters));
+
+ // split type into namespace:unqualified-type
+ if (strpos($type, ':')) {
+ $uqType = substr($type, strrpos($type, ':') + 1);
+ $ns = substr($type, 0, strrpos($type, ':'));
+ $this->debug("in parametersMatchWrapped: got a prefixed type: $uqType, $ns");
+ if ($this->getNamespaceFromPrefix($ns)) {
+ $ns = $this->getNamespaceFromPrefix($ns);
+ $this->debug("in parametersMatchWrapped: expanded prefixed type: $uqType, $ns");
+ }
+ } else {
+ // TODO: should the type be compared to types in XSD, and the namespace
+ // set to XSD if the type matches?
+ $this->debug("in parametersMatchWrapped: No namespace for type $type");
+ $ns = '';
+ $uqType = $type;
+ }
+
+ // get the type information
+ if (!$typeDef = $this->getTypeDef($uqType, $ns)) {
+ $this->debug("in parametersMatchWrapped: $type ($uqType) is not a supported type.");
+ return false;
+ }
+ $this->debug("in parametersMatchWrapped: found typeDef=");
+ $this->appendDebug($this->varDump($typeDef));
+ if (substr($uqType, -1) == '^') {
+ $uqType = substr($uqType, 0, -1);
+ }
+ $phpType = $typeDef['phpType'];
+ $arrayType = (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '');
+ $this->debug("in parametersMatchWrapped: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: $arrayType");
+
+ // we expect a complexType or element of complexType
+ if ($phpType != 'struct') {
+ $this->debug("in parametersMatchWrapped: not a struct");
+ return false;
+ }
+
+ // see whether the parameter names match the elements
+ if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
+ $elements = 0;
+ $matches = 0;
+ foreach ($typeDef['elements'] as $name => $attrs) {
+ if (isset($parameters[$name])) {
+ $this->debug("in parametersMatchWrapped: have parameter named $name");
+ $matches++;
+ } else {
+ $this->debug("in parametersMatchWrapped: do not have parameter named $name");
+ }
+ $elements++;
+ }
+
+ $this->debug("in parametersMatchWrapped: $matches parameter names match $elements wrapped parameter names");
+ if ($matches == 0) {
+ return false;
+ }
+ return true;
+ }
+
+ // since there are no elements for the type, if the user passed no
+ // parameters, the parameters match wrapped.
+ $this->debug("in parametersMatchWrapped: no elements type $ns:$uqType");
+ return count($parameters) == 0;
+ }
+
+ /**
+ * serialize PHP values according to a WSDL message definition
+ * contrary to the method name, this is not limited to RPC
+ *
+ * TODO
+ * - multi-ref serialization
+ * - validate PHP values against type definitions, return errors if invalid
+ *
+ * @param string $operation operation name
+ * @param string $direction (input|output)
+ * @param mixed $parameters parameter value(s)
+ * @param string $bindingType (soap|soap12)
+ * @return mixed parameters serialized as XML or false on error (e.g. operation not found)
+ * @access public
+ */
+ function serializeRPCParameters($operation, $direction, $parameters, $bindingType = 'soap') {
+ $this->debug("in serializeRPCParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion, bindingType=$bindingType");
+ $this->appendDebug('parameters=' . $this->varDump($parameters));
+
+ if ($direction != 'input' && $direction != 'output') {
+ $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
+ $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
+ return false;
+ }
+ if (!$opData = $this->getOperationData($operation, $bindingType)) {
+ $this->debug('Unable to retrieve WSDL data for operation: ' . $operation . ' bindingType: ' . $bindingType);
+ $this->setError('Unable to retrieve WSDL data for operation: ' . $operation . ' bindingType: ' . $bindingType);
+ return false;
+ }
+ $this->debug('in serializeRPCParameters: opData:');
+ $this->appendDebug($this->varDump($opData));
+
+ // Get encoding style for output and set to current
+ $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
+ if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
+ $encodingStyle = $opData['output']['encodingStyle'];
+ $enc_style = $encodingStyle;
+ }
+
+ // set input params
+ $xml = '';
+ if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
+ $parts = &$opData[$direction]['parts'];
+ $part_count = sizeof($parts);
+ $style = $opData['style'];
+ $use = $opData[$direction]['use'];
+ $this->debug("have $part_count part(s) to serialize using $style/$use");
+ if (is_array($parameters)) {
+ $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
+ $parameter_count = count($parameters);
+ $this->debug("have $parameter_count parameter(s) provided as $parametersArrayType to serialize");
+ // check for Microsoft-style wrapped parameters
+ if ($style == 'document' && $use == 'literal' && $part_count == 1 && isset($parts['parameters'])) {
+ $this->debug('check whether the caller has wrapped the parameters');
+ if ($direction == 'output' && $parametersArrayType == 'arraySimple' && $parameter_count == 1) {
+ // TODO: consider checking here for double-wrapping, when
+ // service function wraps, then NuSOAP wraps again
+ $this->debug("change simple array to associative with 'parameters' element");
+ $parameters['parameters'] = $parameters[0];
+ unset($parameters[0]);
+ }
+ if (($parametersArrayType == 'arrayStruct' || $parameter_count == 0) && !isset($parameters['parameters'])) {
+ $this->debug('check whether caller\'s parameters match the wrapped ones');
+ if ($this->parametersMatchWrapped($parts['parameters'], $parameters)) {
+ $this->debug('wrap the parameters for the caller');
+ $parameters = array('parameters' => $parameters);
+ $parameter_count = 1;
+ }
+ }
+ }
+ foreach ($parts as $name => $type) {
+ $this->debug("serializing part $name of type $type");
+ // Track encoding style
+ if (isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
+ $encodingStyle = $opData[$direction]['encodingStyle'];
+ $enc_style = $encodingStyle;
+ } else {
+ $enc_style = false;
+ }
+ // NOTE: add error handling here
+ // if serializeType returns false, then catch global error and fault
+ if ($parametersArrayType == 'arraySimple') {
+ $p = array_shift($parameters);
+ $this->debug('calling serializeType w/indexed param');
+ $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
+ } elseif (isset($parameters[$name])) {
+ $this->debug('calling serializeType w/named param');
+ $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
+ } else {
+ // TODO: only send nillable
+ $this->debug('calling serializeType w/null param');
+ $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
+ }
+ }
+ } else {
+ $this->debug('no parameters passed.');
+ }
+ }
+ $this->debug("serializeRPCParameters returning: $xml");
+ return $xml;
+ }
+
+ /**
+ * serialize a PHP value according to a WSDL message definition
+ *
+ * TODO
+ * - multi-ref serialization
+ * - validate PHP values against type definitions, return errors if invalid
+ *
+ * @param string $operation operation name
+ * @param string $direction (input|output)
+ * @param mixed $parameters parameter value(s)
+ * @return mixed parameters serialized as XML or false on error (e.g. operation not found)
+ * @access public
+ * @deprecated
+ */
+ function serializeParameters($operation, $direction, $parameters)
+ {
+ $this->debug("in serializeParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion");
+ $this->appendDebug('parameters=' . $this->varDump($parameters));
+
+ if ($direction != 'input' && $direction != 'output') {
+ $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
+ $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
+ return false;
+ }
+ if (!$opData = $this->getOperationData($operation)) {
+ $this->debug('Unable to retrieve WSDL data for operation: ' . $operation);
+ $this->setError('Unable to retrieve WSDL data for operation: ' . $operation);
+ return false;
+ }
+ $this->debug('opData:');
+ $this->appendDebug($this->varDump($opData));
+
+ // Get encoding style for output and set to current
+ $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
+ if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
+ $encodingStyle = $opData['output']['encodingStyle'];
+ $enc_style = $encodingStyle;
+ }
+
+ // set input params
+ $xml = '';
+ if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
+
+ $use = $opData[$direction]['use'];
+ $this->debug("use=$use");
+ $this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)');
+ if (is_array($parameters)) {
+ $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
+ $this->debug('have ' . $parametersArrayType . ' parameters');
+ foreach($opData[$direction]['parts'] as $name => $type) {
+ $this->debug('serializing part "'.$name.'" of type "'.$type.'"');
+ // Track encoding style
+ if(isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
+ $encodingStyle = $opData[$direction]['encodingStyle'];
+ $enc_style = $encodingStyle;
+ } else {
+ $enc_style = false;
+ }
+ // NOTE: add error handling here
+ // if serializeType returns false, then catch global error and fault
+ if ($parametersArrayType == 'arraySimple') {
+ $p = array_shift($parameters);
+ $this->debug('calling serializeType w/indexed param');
+ $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
+ } elseif (isset($parameters[$name])) {
+ $this->debug('calling serializeType w/named param');
+ $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
+ } else {
+ // TODO: only send nillable
+ $this->debug('calling serializeType w/null param');
+ $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
+ }
+ }
+ } else {
+ $this->debug('no parameters passed.');
+ }
+ }
+ $this->debug("serializeParameters returning: $xml");
+ return $xml;
+ }
+
+ /**
+ * serializes a PHP value according a given type definition
+ *
+ * @param string $name name of value (part or element)
+ * @param string $type XML schema type of value (type or element)
+ * @param mixed $value a native PHP value (parameter value)
+ * @param string $use use for part (encoded|literal)
+ * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
+ * @param boolean $unqualified a kludge for what should be XML namespace form handling
+ * @return string value serialized as an XML string
+ * @access private
+ */
+ function serializeType($name, $type, $value, $use='encoded', $encodingStyle=false, $unqualified=false)
+ {
+ $this->debug("in serializeType: name=$name, type=$type, use=$use, encodingStyle=$encodingStyle, unqualified=" . ($unqualified ? "unqualified" : "qualified"));
+ $this->appendDebug("value=" . $this->varDump($value));
+ if($use == 'encoded' && $encodingStyle) {
+ $encodingStyle = ' SOAP-ENV:encodingStyle="' . $encodingStyle . '"';
+ }
+
+ // if a soapval has been supplied, let its type override the WSDL
+ if (is_object($value) && get_class($value) == 'soapval') {
+ if ($value->type_ns) {
+ $type = $value->type_ns . ':' . $value->type;
+ $forceType = true;
+ $this->debug("in serializeType: soapval overrides type to $type");
+ } elseif ($value->type) {
+ $type = $value->type;
+ $forceType = true;
+ $this->debug("in serializeType: soapval overrides type to $type");
+ } else {
+ $forceType = false;
+ $this->debug("in serializeType: soapval does not override type");
+ }
+ $attrs = $value->attributes;
+ $value = $value->value;
+ $this->debug("in serializeType: soapval overrides value to $value");
+ if ($attrs) {
+ if (!is_array($value)) {
+ $value['!'] = $value;
+ }
+ foreach ($attrs as $n => $v) {
+ $value['!' . $n] = $v;
+ }
+ $this->debug("in serializeType: soapval provides attributes");
+ }
+ } else {
+ $forceType = false;
+ }
+
+ $xml = '';
+ if (strpos($type, ':')) {
+ $uqType = substr($type, strrpos($type, ':') + 1);
+ $ns = substr($type, 0, strrpos($type, ':'));
+ $this->debug("in serializeType: got a prefixed type: $uqType, $ns");
+ if ($this->getNamespaceFromPrefix($ns)) {
+ $ns = $this->getNamespaceFromPrefix($ns);
+ $this->debug("in serializeType: expanded prefixed type: $uqType, $ns");
+ }
+
+ if($ns == $this->XMLSchemaVersion || $ns == 'http://schemas.xmlsoap.org/soap/encoding/'){
+ $this->debug('in serializeType: type namespace indicates XML Schema or SOAP Encoding type');
+ if ($unqualified && $use == 'literal') {
+ $elementNS = " xmlns=\"\"";
+ } else {
+ $elementNS = '';
+ }
+ if (is_null($value)) {
+ if ($use == 'literal') {
+ // TODO: depends on minOccurs
+ $xml = "<$name$elementNS/>";
+ } else {
+ // TODO: depends on nillable, which should be checked before calling this method
+ $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>";
+ }
+ $this->debug("in serializeType: returning: $xml");
+ return $xml;
+ }
+ if ($uqType == 'Array') {
+ // JBoss/Axis does this sometimes
+ return $this->serialize_val($value, $name, false, false, false, false, $use);
+ }
+ if ($uqType == 'boolean') {
+ if ((is_string($value) && $value == 'false') || (! $value)) {
+ $value = 'false';
+ } else {
+ $value = 'true';
+ }
+ }
+ if ($uqType == 'string' && gettype($value) == 'string') {
+ $value = $this->expandEntities($value);
+ }
+ if (($uqType == 'long' || $uqType == 'unsignedLong') && gettype($value) == 'double') {
+ $value = sprintf("%.0lf", $value);
+ }
+ // it's a scalar
+ // TODO: what about null/nil values?
+ // check type isn't a custom type extending xmlschema namespace
+ if (!$this->getTypeDef($uqType, $ns)) {
+ if ($use == 'literal') {
+ if ($forceType) {
+ $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
+ } else {
+ $xml = "<$name$elementNS>$value</$name>";
+ }
+ } else {
+ $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
+ }
+ $this->debug("in serializeType: returning: $xml");
+ return $xml;
+ }
+ $this->debug('custom type extends XML Schema or SOAP Encoding namespace (yuck)');
+ } else if ($ns == 'http://xml.apache.org/xml-soap') {
+ $this->debug('in serializeType: appears to be Apache SOAP type');
+ if ($uqType == 'Map') {
+ $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
+ if (! $tt_prefix) {
+ $this->debug('in serializeType: Add namespace for Apache SOAP type');
+ $tt_prefix = 'ns' . rand(1000, 9999);
+ $this->namespaces[$tt_prefix] = 'http://xml.apache.org/xml-soap';
+ // force this to be added to usedNamespaces
+ $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
+ }
+ $contents = '';
+ foreach($value as $k => $v) {
+ $this->debug("serializing map element: key $k, value $v");
+ $contents .= '<item>';
+ $contents .= $this->serialize_val($k,'key',false,false,false,false,$use);
+ $contents .= $this->serialize_val($v,'value',false,false,false,false,$use);
+ $contents .= '</item>';
+ }
+ if ($use == 'literal') {
+ if ($forceType) {
+ $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\">$contents</$name>";
+ } else {
+ $xml = "<$name>$contents</$name>";
+ }
+ } else {
+ $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\"$encodingStyle>$contents</$name>";
+ }
+ $this->debug("in serializeType: returning: $xml");
+ return $xml;
+ }
+ $this->debug('in serializeType: Apache SOAP type, but only support Map');
+ }
+ } else {
+ // TODO: should the type be compared to types in XSD, and the namespace
+ // set to XSD if the type matches?
+ $this->debug("in serializeType: No namespace for type $type");
+ $ns = '';
+ $uqType = $type;
+ }
+ if(!$typeDef = $this->getTypeDef($uqType, $ns)){
+ $this->setError("$type ($uqType) is not a supported type.");
+ $this->debug("in serializeType: $type ($uqType) is not a supported type.");
+ return false;
+ } else {
+ $this->debug("in serializeType: found typeDef");
+ $this->appendDebug('typeDef=' . $this->varDump($typeDef));
+ if (substr($uqType, -1) == '^') {
+ $uqType = substr($uqType, 0, -1);
+ }
+ }
+ if (!isset($typeDef['phpType'])) {
+ $this->setError("$type ($uqType) has no phpType.");
+ $this->debug("in serializeType: $type ($uqType) has no phpType.");
+ return false;
+ }
+ $phpType = $typeDef['phpType'];
+ $this->debug("in serializeType: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: " . (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '') );
+ // if php type == struct, map value to the <all> element names
+ if ($phpType == 'struct') {
+ if (isset($typeDef['typeClass']) && $typeDef['typeClass'] == 'element') {
+ $elementName = $uqType;
+ if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
+ $elementNS = " xmlns=\"$ns\"";
+ } else {
+ $elementNS = " xmlns=\"\"";
+ }
+ } else {
+ $elementName = $name;
+ if ($unqualified) {
+ $elementNS = " xmlns=\"\"";
+ } else {
+ $elementNS = '';
+ }
+ }
+ if (is_null($value)) {
+ if ($use == 'literal') {
+ // TODO: depends on minOccurs and nillable
+ $xml = "<$elementName$elementNS/>";
+ } else {
+ $xml = "<$elementName$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>";
+ }
+ $this->debug("in serializeType: returning: $xml");
+ return $xml;
+ }
+ if (is_object($value)) {
+ $value = get_object_vars($value);
+ }
+ if (is_array($value)) {
+ $elementAttrs = $this->serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType);
+ if ($use == 'literal') {
+ if ($forceType) {
+ $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">";
+ } else {
+ $xml = "<$elementName$elementNS$elementAttrs>";
+ }
+ } else {
+ $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>";
+ }
+
+ if (isset($typeDef['simpleContent']) && $typeDef['simpleContent'] == 'true') {
+ if (isset($value['!'])) {
+ $xml .= $value['!'];
+ $this->debug("in serializeType: serialized simpleContent for type $type");
+ } else {
+ $this->debug("in serializeType: no simpleContent to serialize for type $type");
+ }
+ } else {
+ // complexContent
+ $xml .= $this->serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use, $encodingStyle);
+ }
+ $xml .= "</$elementName>";
+ } else {
+ $this->debug("in serializeType: phpType is struct, but value is not an array");
+ $this->setError("phpType is struct, but value is not an array: see debug output for details");
+ $xml = '';
+ }
+ } elseif ($phpType == 'array') {
+ if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
+ $elementNS = " xmlns=\"$ns\"";
+ } else {
+ if ($unqualified) {
+ $elementNS = " xmlns=\"\"";
+ } else {
+ $elementNS = '';
+ }
+ }
+ if (is_null($value)) {
+ if ($use == 'literal') {
+ // TODO: depends on minOccurs
+ $xml = "<$name$elementNS/>";
+ } else {
+ $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" .
+ $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') .
+ ":Array\" " .
+ $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') .
+ ':arrayType="' .
+ $this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType'])) .
+ ':' .
+ $this->getLocalPart($typeDef['arrayType'])."[0]\"/>";
+ }
+ $this->debug("in serializeType: returning: $xml");
+ return $xml;
+ }
+ if (isset($typeDef['multidimensional'])) {
+ $nv = array();
+ foreach($value as $v) {
+ $cols = ',' . sizeof($v);
+ $nv = array_merge($nv, $v);
+ }
+ $value = $nv;
+ } else {
+ $cols = '';
+ }
+ if (is_array($value) && sizeof($value) >= 1) {
+ $rows = sizeof($value);
+ $contents = '';
+ foreach($value as $k => $v) {
+ $this->debug("serializing array element: $k, ".$this->varDump($v)." of type: ".$typeDef['arrayType']);
+ //if (strpos($typeDef['arrayType'], ':') ) {
+ if (!in_array($typeDef['arrayType'],$this->typemap['http://www.w3.org/2001/XMLSchema'])) {
+ $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use);
+ } else {
+ $contents .= $this->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion, false, $use);
+ }
+ }
+ } else {
+ $rows = 0;
+ $contents = null;
+ }
+ // TODO: for now, an empty value will be serialized as a zero element
+ // array. Revisit this when coding the handling of null/nil values.
+ if ($use == 'literal') {
+ $xml = "<$name$elementNS>"
+ .$contents
+ ."</$name>";
+ } else {
+ $xml = "<$name$elementNS xsi:type=\"".$this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/').':Array" '.
+ $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
+ .':arrayType="'
+ .$this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType']))
+ .":".$this->getLocalPart($typeDef['arrayType'])."[$rows$cols]\">"
+ .$contents
+ ."</$name>";
+ }
+ } elseif ($phpType == 'scalar') {
+ if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
+ $elementNS = " xmlns=\"$ns\"";
+ } else {
+ if ($unqualified) {
+ $elementNS = " xmlns=\"\"";
+ } else {
+ $elementNS = '';
+ }
+ }
+ if ($use == 'literal') {
+ if ($forceType) {
+ $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
+ } else {
+ $xml = "<$name$elementNS>$value</$name>";
+ }
+ } else {
+ $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
+ }
+ }
+ $this->debug("in serializeType: returning: $xml");
+ return $xml;
+ }
+
+ /**
+ * serializes the attributes for a complexType
+ *
+ * @param array $typeDef our internal representation of an XML schema type (or element)
+ * @param mixed $value a native PHP value (parameter value)
+ * @param string $ns the namespace of the type
+ * @param string $uqType the local part of the type
+ * @return string value serialized as an XML string
+ * @access private
+ */
+ function serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType) {
+ $this->debug("serializeComplexTypeAttributes for XML Schema type $ns:$uqType");
+ $xml = '';
+ if (isset($typeDef['extensionBase'])) {
+ $nsx = $this->getPrefix($typeDef['extensionBase']);
+ $uqTypex = $this->getLocalPart($typeDef['extensionBase']);
+ if ($this->getNamespaceFromPrefix($nsx)) {
+ $nsx = $this->getNamespaceFromPrefix($nsx);
+ }
+ if ($typeDefx = $this->getTypeDef($uqTypex, $nsx)) {
+ $this->debug("serialize attributes for extension base $nsx:$uqTypex");
+ $xml .= $this->serializeComplexTypeAttributes($typeDefx, $value, $nsx, $uqTypex);
+ } else {
+ $this->debug("extension base $nsx:$uqTypex is not a supported type");
+ }
+ }
+ if (isset($typeDef['attrs']) && is_array($typeDef['attrs'])) {
+ $this->debug("serialize attributes for XML Schema type $ns:$uqType");
+ if (is_array($value)) {
+ $xvalue = $value;
+ } elseif (is_object($value)) {
+ $xvalue = get_object_vars($value);
+ } else {
+ $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
+ $xvalue = array();
+ }
+ foreach ($typeDef['attrs'] as $aName => $attrs) {
+ if (isset($xvalue['!' . $aName])) {
+ $xname = '!' . $aName;
+ $this->debug("value provided for attribute $aName with key $xname");
+ } elseif (isset($xvalue[$aName])) {
+ $xname = $aName;
+ $this->debug("value provided for attribute $aName with key $xname");
+ } elseif (isset($attrs['default'])) {
+ $xname = '!' . $aName;
+ $xvalue[$xname] = $attrs['default'];
+ $this->debug('use default value of ' . $xvalue[$aName] . ' for attribute ' . $aName);
+ } else {
+ $xname = '';
+ $this->debug("no value provided for attribute $aName");
+ }
+ if ($xname) {
+ $xml .= " $aName=\"" . $this->expandEntities($xvalue[$xname]) . "\"";
+ }
+ }
+ } else {
+ $this->debug("no attributes to serialize for XML Schema type $ns:$uqType");
+ }
+ return $xml;
+ }
+
+ /**
+ * serializes the elements for a complexType
+ *
+ * @param array $typeDef our internal representation of an XML schema type (or element)
+ * @param mixed $value a native PHP value (parameter value)
+ * @param string $ns the namespace of the type
+ * @param string $uqType the local part of the type
+ * @param string $use use for part (encoded|literal)
+ * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
+ * @return string value serialized as an XML string
+ * @access private
+ */
+ function serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use='encoded', $encodingStyle=false) {
+ $this->debug("in serializeComplexTypeElements for XML Schema type $ns:$uqType");
+ $xml = '';
+ if (isset($typeDef['extensionBase'])) {
+ $nsx = $this->getPrefix($typeDef['extensionBase']);
+ $uqTypex = $this->getLocalPart($typeDef['extensionBase']);
+ if ($this->getNamespaceFromPrefix($nsx)) {
+ $nsx = $this->getNamespaceFromPrefix($nsx);
+ }
+ if ($typeDefx = $this->getTypeDef($uqTypex, $nsx)) {
+ $this->debug("serialize elements for extension base $nsx:$uqTypex");
+ $xml .= $this->serializeComplexTypeElements($typeDefx, $value, $nsx, $uqTypex, $use, $encodingStyle);
+ } else {
+ $this->debug("extension base $nsx:$uqTypex is not a supported type");
+ }
+ }
+ if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
+ $this->debug("in serializeComplexTypeElements, serialize elements for XML Schema type $ns:$uqType");
+ if (is_array($value)) {
+ $xvalue = $value;
+ } elseif (is_object($value)) {
+ $xvalue = get_object_vars($value);
+ } else {
+ $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
+ $xvalue = array();
+ }
+ // toggle whether all elements are present - ideally should validate against schema
+ if (count($typeDef['elements']) != count($xvalue)){
+ $optionals = true;
+ }
+ foreach ($typeDef['elements'] as $eName => $attrs) {
+ if (!isset($xvalue[$eName])) {
+ if (isset($attrs['default'])) {
+ $xvalue[$eName] = $attrs['default'];
+ $this->debug('use default value of ' . $xvalue[$eName] . ' for element ' . $eName);
+ }
+ }
+ // if user took advantage of a minOccurs=0, then only serialize named parameters
+ if (isset($optionals)
+ && (!isset($xvalue[$eName]))
+ && ( (!isset($attrs['nillable'])) || $attrs['nillable'] != 'true')
+ ){
+ if (isset($attrs['minOccurs']) && $attrs['minOccurs'] <> '0') {
+ $this->debug("apparent error: no value provided for element $eName with minOccurs=" . $attrs['minOccurs']);
+ }
+ // do nothing
+ $this->debug("no value provided for complexType element $eName and element is not nillable, so serialize nothing");
+ } else {
+ // get value
+ if (isset($xvalue[$eName])) {
+ $v = $xvalue[$eName];
+ } else {
+ $v = null;
+ }
+ if (isset($attrs['form'])) {
+ $unqualified = ($attrs['form'] == 'unqualified');
+ } else {
+ $unqualified = false;
+ }
+ if (isset($attrs['maxOccurs']) && ($attrs['maxOccurs'] == 'unbounded' || $attrs['maxOccurs'] > 1) && isset($v) && is_array($v) && $this->isArraySimpleOrStruct($v) == 'arraySimple') {
+ $vv = $v;
+ foreach ($vv as $k => $v) {
+ if (isset($attrs['type']) || isset($attrs['ref'])) {
+ // serialize schema-defined type
+ $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
+ } else {
+ // serialize generic type (can this ever really happen?)
+ $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
+ $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
+ }
+ }
+ } else {
+ if (is_null($v) && isset($attrs['minOccurs']) && $attrs['minOccurs'] == '0') {
+ // do nothing
+ } elseif (is_null($v) && isset($attrs['nillable']) && $attrs['nillable'] == 'true') {
+ // TODO: serialize a nil correctly, but for now serialize schema-defined type
+ $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
+ } elseif (isset($attrs['type']) || isset($attrs['ref'])) {
+ // serialize schema-defined type
+ $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
+ } else {
+ // serialize generic type (can this ever really happen?)
+ $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
+ $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
+ }
+ }
+ }
+ }
+ } else {
+ $this->debug("no elements to serialize for XML Schema type $ns:$uqType");
+ }
+ return $xml;
+ }
+
+ /**
+ * adds an XML Schema complex type to the WSDL types
+ *
+ * @param string $name
+ * @param string $typeClass (complexType|simpleType|attribute)
+ * @param string $phpType currently supported are array and struct (php assoc array)
+ * @param string $compositor (all|sequence|choice)
+ * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
+ * @param array $elements e.g. array ( name => array(name=>'',type=>'') )
+ * @param array $attrs e.g. array(array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'xsd:string[]'))
+ * @param string $arrayType as namespace:name (xsd:string)
+ * @see nusoap_xmlschema
+ * @access public
+ */
+ function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType='') {
+ if (count($elements) > 0) {
+ $eElements = array();
+ foreach($elements as $n => $e){
+ // expand each element
+ $ee = array();
+ foreach ($e as $k => $v) {
+ $k = strpos($k,':') ? $this->expandQname($k) : $k;
+ $v = strpos($v,':') ? $this->expandQname($v) : $v;
+ $ee[$k] = $v;
+ }
+ $eElements[$n] = $ee;
+ }
+ $elements = $eElements;
+ }
+
+ if (count($attrs) > 0) {
+ foreach($attrs as $n => $a){
+ // expand each attribute
+ foreach ($a as $k => $v) {
+ $k = strpos($k,':') ? $this->expandQname($k) : $k;
+ $v = strpos($v,':') ? $this->expandQname($v) : $v;
+ $aa[$k] = $v;
+ }
+ $eAttrs[$n] = $aa;
+ }
+ $attrs = $eAttrs;
+ }
+
+ $restrictionBase = strpos($restrictionBase,':') ? $this->expandQname($restrictionBase) : $restrictionBase;
+ $arrayType = strpos($arrayType,':') ? $this->expandQname($arrayType) : $arrayType;
+
+ $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
+ $this->schemas[$typens][0]->addComplexType($name,$typeClass,$phpType,$compositor,$restrictionBase,$elements,$attrs,$arrayType);
+ }
+
+ /**
+ * adds an XML Schema simple type to the WSDL types
+ *
+ * @param string $name
+ * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
+ * @param string $typeClass (should always be simpleType)
+ * @param string $phpType (should always be scalar)
+ * @param array $enumeration array of values
+ * @see nusoap_xmlschema
+ * @access public
+ */
+ function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) {
+ $restrictionBase = strpos($restrictionBase,':') ? $this->expandQname($restrictionBase) : $restrictionBase;
+
+ $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
+ $this->schemas[$typens][0]->addSimpleType($name, $restrictionBase, $typeClass, $phpType, $enumeration);
+ }
+
+ /**
+ * adds an element to the WSDL types
+ *
+ * @param array $attrs attributes that must include name and type
+ * @see nusoap_xmlschema
+ * @access public
+ */
+ function addElement($attrs) {
+ $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
+ $this->schemas[$typens][0]->addElement($attrs);
+ }
+
+ /**
+ * register an operation with the server
+ *
+ * @param string $name operation (method) name
+ * @param array $in assoc array of input values: key = param name, value = param type
+ * @param array $out assoc array of output values: key = param name, value = param type
+ * @param string $namespace optional The namespace for the operation
+ * @param string $soapaction optional The soapaction for the operation
+ * @param string $style (rpc|document) optional The style for the operation Note: when 'document' is specified, parameter and return wrappers are created for you automatically
+ * @param string $use (encoded|literal) optional The use for the parameters (cannot mix right now)
+ * @param string $documentation optional The description to include in the WSDL
+ * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
+ * @access public
+ */
+ function addOperation($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = 'rpc', $use = 'encoded', $documentation = '', $encodingStyle = ''){
+ if ($use == 'encoded' && $encodingStyle == '') {
+ $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
+ }
+
+ if ($style == 'document') {
+ $elements = array();
+ foreach ($in as $n => $t) {
+ $elements[$n] = array('name' => $n, 'type' => $t, 'form' => 'unqualified');
+ }
+ $this->addComplexType($name . 'RequestType', 'complexType', 'struct', 'all', '', $elements);
+ $this->addElement(array('name' => $name, 'type' => $name . 'RequestType'));
+ $in = array('parameters' => 'tns:' . $name . '^');
+
+ $elements = array();
+ foreach ($out as $n => $t) {
+ $elements[$n] = array('name' => $n, 'type' => $t, 'form' => 'unqualified');
+ }
+ $this->addComplexType($name . 'ResponseType', 'complexType', 'struct', 'all', '', $elements);
+ $this->addElement(array('name' => $name . 'Response', 'type' => $name . 'ResponseType', 'form' => 'qualified'));
+ $out = array('parameters' => 'tns:' . $name . 'Response' . '^');
+ }
+
+ // get binding
+ $this->bindings[ $this->serviceName . 'Binding' ]['operations'][$name] =
+ array(
+ 'name' => $name,
+ 'binding' => $this->serviceName . 'Binding',
+ 'endpoint' => $this->endpoint,
+ 'soapAction' => $soapaction,
+ 'style' => $style,
+ 'input' => array(
+ 'use' => $use,
+ 'namespace' => $namespace,
+ 'encodingStyle' => $encodingStyle,
+ 'message' => $name . 'Request',
+ 'parts' => $in),
+ 'output' => array(
+ 'use' => $use,
+ 'namespace' => $namespace,
+ 'encodingStyle' => $encodingStyle,
+ 'message' => $name . 'Response',
+ 'parts' => $out),
+ 'namespace' => $namespace,
+ 'transport' => 'http://schemas.xmlsoap.org/soap/http',
+ 'documentation' => $documentation);
+ // add portTypes
+ // add messages
+ if($in)
+ {
+ foreach($in as $pName => $pType)
+ {
+ if(strpos($pType,':')) {
+ $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
+ }
+ $this->messages[$name.'Request'][$pName] = $pType;
+ }
+ } else {
+ $this->messages[$name.'Request']= '0';
+ }
+ if($out)
+ {
+ foreach($out as $pName => $pType)
+ {
+ if(strpos($pType,':')) {
+ $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
+ }
+ $this->messages[$name.'Response'][$pName] = $pType;
+ }
+ } else {
+ $this->messages[$name.'Response']= '0';
+ }
+ return true;
+ }
+}
+?><?php
+
+
+
+/**
+*
+* nusoap_parser class parses SOAP XML messages into native PHP values
+*
+* @author Dietrich Ayala <dietrich@ganx4.com>
+* @author Scott Nichol <snichol@users.sourceforge.net>
+* @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
+* @access public
+*/
+class nusoap_parser extends nusoap_base {
+
+ var $xml = '';
+ var $xml_encoding = '';
+ var $method = '';
+ var $root_struct = '';
+ var $root_struct_name = '';
+ var $root_struct_namespace = '';
+ var $root_header = '';
+ var $document = ''; // incoming SOAP body (text)
+ // determines where in the message we are (envelope,header,body,method)
+ var $status = '';
+ var $position = 0;
+ var $depth = 0;
+ var $default_namespace = '';
+ var $namespaces = array();
+ var $message = array();
+ var $parent = '';
+ var $fault = false;
+ var $fault_code = '';
+ var $fault_str = '';
+ var $fault_detail = '';
+ var $depth_array = array();
+ var $debug_flag = true;
+ var $soapresponse = NULL; // parsed SOAP Body
+ var $soapheader = NULL; // parsed SOAP Header
+ var $responseHeaders = ''; // incoming SOAP headers (text)
+ var $body_position = 0;
+ // for multiref parsing:
+ // array of id => pos
+ var $ids = array();
+ // array of id => hrefs => pos
+ var $multirefs = array();
+ // toggle for auto-decoding element content
+ var $decode_utf8 = true;
+
+ /**
+ * constructor that actually does the parsing
+ *
+ * @param string $xml SOAP message
+ * @param string $encoding character encoding scheme of message
+ * @param string $method method for which XML is parsed (unused?)
+ * @param string $decode_utf8 whether to decode UTF-8 to ISO-8859-1
+ * @access public
+ */
+ function __construct($xml,$encoding='UTF-8',$method='',$decode_utf8=true){
+ parent::__construct();
+ $this->xml = $xml;
+ $this->xml_encoding = $encoding;
+ $this->method = $method;
+ $this->decode_utf8 = $decode_utf8;
+
+ // Check whether content has been read.
+ if(!empty($xml)){
+ // Check XML encoding
+ $pos_xml = strpos($xml, '<?xml');
+ if ($pos_xml !== FALSE) {
+ $xml_decl = substr($xml, $pos_xml, strpos($xml, '?>', $pos_xml + 2) - $pos_xml + 1);
+ if (preg_match("/encoding=[\"']([^\"']*)[\"']/", $xml_decl, $res)) {
+ $xml_encoding = $res[1];
+ if (strtoupper($xml_encoding) != $encoding) {
+ $err = "Charset from HTTP Content-Type '" . $encoding . "' does not match encoding from XML declaration '" . $xml_encoding . "'";
+ $this->debug($err);
+ if ($encoding != 'ISO-8859-1' || strtoupper($xml_encoding) != 'UTF-8') {
+ $this->setError($err);
+ return;
+ }
+ // when HTTP says ISO-8859-1 (the default) and XML says UTF-8 (the typical), assume the other endpoint is just sloppy and proceed
+ } else {
+ $this->debug('Charset from HTTP Content-Type matches encoding from XML declaration');
+ }
+ } else {
+ $this->debug('No encoding specified in XML declaration');
+ }
+ } else {
+ $this->debug('No XML declaration');
+ }
+ $this->debug('Entering nusoap_parser(), length='.strlen($xml).', encoding='.$encoding);
+ // Create an XML parser - why not xml_parser_create_ns?
+ $this->parser = xml_parser_create($this->xml_encoding);
+ // Set the options for parsing the XML data.
+ //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
+ xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
+ xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $this->xml_encoding);
+ // Set the object for the parser.
+ xml_set_object($this->parser, $this);
+ // Set the element handlers for the parser.
+ xml_set_element_handler($this->parser, 'start_element','end_element');
+ xml_set_character_data_handler($this->parser,'character_data');
+
+ // Parse the XML file.
+ if(!xml_parse($this->parser,$xml,true)){
+ // Display an error message.
+ $err = sprintf('XML error parsing SOAP payload on line %d: %s',
+ xml_get_current_line_number($this->parser),
+ xml_error_string(xml_get_error_code($this->parser)));
+ $this->debug($err);
+ $this->debug("XML payload:\n" . $xml);
+ $this->setError($err);
+ } else {
+ $this->debug('in nusoap_parser ctor, message:');
+ $this->appendDebug($this->varDump($this->message));
+ $this->debug('parsed successfully, found root struct: '.$this->root_struct.' of name '.$this->root_struct_name);
+ // get final value
+ $this->soapresponse = $this->message[$this->root_struct]['result'];
+ // get header value
+ if($this->root_header != '' && isset($this->message[$this->root_header]['result'])){
+ $this->soapheader = $this->message[$this->root_header]['result'];
+ }
+ // resolve hrefs/ids
+ if(sizeof($this->multirefs) > 0){
+ foreach($this->multirefs as $id => $hrefs){
+ $this->debug('resolving multirefs for id: '.$id);
+ $idVal = $this->buildVal($this->ids[$id]);
+ if (is_array($idVal) && isset($idVal['!id'])) {
+ unset($idVal['!id']);
+ }
+ foreach($hrefs as $refPos => $ref){
+ $this->debug('resolving href at pos '.$refPos);
+ $this->multirefs[$id][$refPos] = $idVal;
+ }
+ }
+ }
+ }
+ xml_parser_free($this->parser);
+ } else {
+ $this->debug('xml was empty, didn\'t parse!');
+ $this->setError('xml was empty, didn\'t parse!');
+ }
+ }
+
+ /**
+ * start-element handler
+ *
+ * @param resource $parser XML parser object
+ * @param string $name element name
+ * @param array $attrs associative array of attributes
+ * @access private
+ */
+ function start_element($parser, $name, $attrs) {
+ // position in a total number of elements, starting from 0
+ // update class level pos
+ $pos = $this->position++;
+ // and set mine
+ $this->message[$pos] = array('pos' => $pos,'children'=>'','cdata'=>'');
+ // depth = how many levels removed from root?
+ // set mine as current global depth and increment global depth value
+ $this->message[$pos]['depth'] = $this->depth++;
+
+ // else add self as child to whoever the current parent is
+ if($pos != 0){
+ $this->message[$this->parent]['children'] .= '|'.$pos;
+ }
+ // set my parent
+ $this->message[$pos]['parent'] = $this->parent;
+ // set self as current parent
+ $this->parent = $pos;
+ // set self as current value for this depth
+ $this->depth_array[$this->depth] = $pos;
+ // get element prefix
+ if(strpos($name,':')){
+ // get ns prefix
+ $prefix = substr($name,0,strpos($name,':'));
+ // get unqualified name
+ $name = substr(strstr($name,':'),1);
+ }
+ // set status
+ if ($name == 'Envelope' && $this->status == '') {
+ $this->status = 'envelope';
+ } elseif ($name == 'Header' && $this->status == 'envelope') {
+ $this->root_header = $pos;
+ $this->status = 'header';
+ } elseif ($name == 'Body' && $this->status == 'envelope'){
+ $this->status = 'body';
+ $this->body_position = $pos;
+ // set method
+ } elseif($this->status == 'body' && $pos == ($this->body_position+1)) {
+ $this->status = 'method';
+ $this->root_struct_name = $name;
+ $this->root_struct = $pos;
+ $this->message[$pos]['type'] = 'struct';
+ $this->debug("found root struct $this->root_struct_name, pos $this->root_struct");
+ }
+ // set my status
+ $this->message[$pos]['status'] = $this->status;
+ // set name
+ $this->message[$pos]['name'] = htmlspecialchars($name);
+ // set attrs
+ $this->message[$pos]['attrs'] = $attrs;
+
+ // loop through atts, logging ns and type declarations
+ $attstr = '';
+ foreach($attrs as $key => $value){
+ $key_prefix = $this->getPrefix($key);
+ $key_localpart = $this->getLocalPart($key);
+ // if ns declarations, add to class level array of valid namespaces
+ if($key_prefix == 'xmlns'){
+ if(preg_match('/^http:\/\/www.w3.org\/[0-9]{4}\/XMLSchema$/',$value)){
+ $this->XMLSchemaVersion = $value;
+ $this->namespaces['xsd'] = $this->XMLSchemaVersion;
+ $this->namespaces['xsi'] = $this->XMLSchemaVersion.'-instance';
+ }
+ $this->namespaces[$key_localpart] = $value;
+ // set method namespace
+ if($name == $this->root_struct_name){
+ $this->methodNamespace = $value;
+ }
+ // if it's a type declaration, set type
+ } elseif($key_localpart == 'type'){
+ if (isset($this->message[$pos]['type']) && $this->message[$pos]['type'] == 'array') {
+ // do nothing: already processed arrayType
+ } else {
+ $value_prefix = $this->getPrefix($value);
+ $value_localpart = $this->getLocalPart($value);
+ $this->message[$pos]['type'] = $value_localpart;
+ $this->message[$pos]['typePrefix'] = $value_prefix;
+ if(isset($this->namespaces[$value_prefix])){
+ $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix];
+ } else if(isset($attrs['xmlns:'.$value_prefix])) {
+ $this->message[$pos]['type_namespace'] = $attrs['xmlns:'.$value_prefix];
+ }
+ // should do something here with the namespace of specified type?
+ }
+ } elseif($key_localpart == 'arrayType'){
+ $this->message[$pos]['type'] = 'array';
+ /* do arrayType ereg here
+ [1] arrayTypeValue ::= atype asize
+ [2] atype ::= QName rank*
+ [3] rank ::= '[' (',')* ']'
+ [4] asize ::= '[' length~ ']'
+ [5] length ::= nextDimension* Digit+
+ [6] nextDimension ::= Digit+ ','
+ */
+ $expr = '/([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+),?([0-9]*)\]/';
+ if(preg_match($expr,$value,$regs)){
+ $this->message[$pos]['typePrefix'] = $regs[1];
+ $this->message[$pos]['arrayTypePrefix'] = $regs[1];
+ if (isset($this->namespaces[$regs[1]])) {
+ $this->message[$pos]['arrayTypeNamespace'] = $this->namespaces[$regs[1]];
+ } else if (isset($attrs['xmlns:'.$regs[1]])) {
+ $this->message[$pos]['arrayTypeNamespace'] = $attrs['xmlns:'.$regs[1]];
+ }
+ $this->message[$pos]['arrayType'] = $regs[2];
+ $this->message[$pos]['arraySize'] = $regs[3];
+ $this->message[$pos]['arrayCols'] = $regs[4];
+ }
+ // specifies nil value (or not)
+ } elseif ($key_localpart == 'nil'){
+ $this->message[$pos]['nil'] = ($value == 'true' || $value == '1');
+ // some other attribute
+ } elseif ($key != 'href' && $key != 'xmlns' && $key_localpart != 'encodingStyle' && $key_localpart != 'root') {
+ $this->message[$pos]['xattrs']['!' . $key] = $value;
+ }
+
+ if ($key == 'xmlns') {
+ $this->default_namespace = $value;
+ }
+ // log id
+ if($key == 'id'){
+ $this->ids[$value] = $pos;
+ }
+ // root
+ if($key_localpart == 'root' && $value == 1){
+ $this->status = 'method';
+ $this->root_struct_name = $name;
+ $this->root_struct = $pos;
+ $this->debug("found root struct $this->root_struct_name, pos $pos");
+ }
+ // for doclit
+ $attstr .= " $key=\"$value\"";
+ }
+ // get namespace - must be done after namespace atts are processed
+ if(isset($prefix)){
+ $this->message[$pos]['namespace'] = $this->namespaces[$prefix];
+ $this->default_namespace = $this->namespaces[$prefix];
+ } else {
+ $this->message[$pos]['namespace'] = $this->default_namespace;
+ }
+ if($this->status == 'header'){
+ if ($this->root_header != $pos) {
+ $this->responseHeaders .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
+ }
+ } elseif($this->root_struct_name != ''){
+ $this->document .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
+ }
+ }
+
+ /**
+ * end-element handler
+ *
+ * @param resource $parser XML parser object
+ * @param string $name element name
+ * @access private
+ */
+ function end_element($parser, $name) {
+ // position of current element is equal to the last value left in depth_array for my depth
+ $pos = $this->depth_array[$this->depth--];
+
+ // get element prefix
+ if(strpos($name,':')){
+ // get ns prefix
+ $prefix = substr($name,0,strpos($name,':'));
+ // get unqualified name
+ $name = substr(strstr($name,':'),1);
+ }
+
+ // build to native type
+ if(isset($this->body_position) && $pos > $this->body_position){
+ // deal w/ multirefs
+ if(isset($this->message[$pos]['attrs']['href'])){
+ // get id
+ $id = substr($this->message[$pos]['attrs']['href'],1);
+ // add placeholder to href array
+ $this->multirefs[$id][$pos] = 'placeholder';
+ // add set a reference to it as the result value
+ $this->message[$pos]['result'] =& $this->multirefs[$id][$pos];
+ // build complexType values
+ } elseif($this->message[$pos]['children'] != ''){
+ // if result has already been generated (struct/array)
+ if(!isset($this->message[$pos]['result'])){
+ $this->message[$pos]['result'] = $this->buildVal($pos);
+ }
+ // build complexType values of attributes and possibly simpleContent
+ } elseif (isset($this->message[$pos]['xattrs'])) {
+ if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) {
+ $this->message[$pos]['xattrs']['!'] = null;
+ } elseif (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') {
+ if (isset($this->message[$pos]['type'])) {
+ $this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
+ } else {
+ $parent = $this->message[$pos]['parent'];
+ if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
+ $this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
+ } else {
+ $this->message[$pos]['xattrs']['!'] = $this->message[$pos]['cdata'];
+ }
+ }
+ }
+ $this->message[$pos]['result'] = $this->message[$pos]['xattrs'];
+ // set value of simpleType (or nil complexType)
+ } else {
+ //$this->debug('adding data for scalar value '.$this->message[$pos]['name'].' of value '.$this->message[$pos]['cdata']);
+ if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) {
+ $this->message[$pos]['xattrs']['!'] = null;
+ } elseif (isset($this->message[$pos]['type'])) {
+ $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
+ } else {
+ $parent = $this->message[$pos]['parent'];
+ if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
+ $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
+ } else {
+ $this->message[$pos]['result'] = $this->message[$pos]['cdata'];
+ }
+ }
+
+ /* add value to parent's result, if parent is struct/array
+ $parent = $this->message[$pos]['parent'];
+ if($this->message[$parent]['type'] != 'map'){
+ if(strtolower($this->message[$parent]['type']) == 'array'){
+ $this->message[$parent]['result'][] = $this->message[$pos]['result'];
+ } else {
+ $this->message[$parent]['result'][$this->message[$pos]['name']] = $this->message[$pos]['result'];
+ }
+ }
+ */
+ }
+ }
+
+ // for doclit
+ if($this->status == 'header'){
+ if ($this->root_header != $pos) {
+ $this->responseHeaders .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>";
+ }
+ } elseif($pos >= $this->root_struct){
+ $this->document .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>";
+ }
+ // switch status
+ if ($pos == $this->root_struct){
+ $this->status = 'body';
+ $this->root_struct_namespace = $this->message[$pos]['namespace'];
+ } elseif ($pos == $this->root_header) {
+ $this->status = 'envelope';
+ } elseif ($name == 'Body' && $this->status == 'body') {
+ $this->status = 'envelope';
+ } elseif ($name == 'Header' && $this->status == 'header') { // will never happen
+ $this->status = 'envelope';
+ } elseif ($name == 'Envelope' && $this->status == 'envelope') {
+ $this->status = '';
+ }
+ // set parent back to my parent
+ $this->parent = $this->message[$pos]['parent'];
+ }
+
+ /**
+ * element content handler
+ *
+ * @param resource $parser XML parser object
+ * @param string $data element content
+ * @access private
+ */
+ function character_data($parser, $data){
+ $pos = $this->depth_array[$this->depth];
+ if ($this->xml_encoding=='UTF-8'){
+ // TODO: add an option to disable this for folks who want
+ // raw UTF-8 that, e.g., might not map to iso-8859-1
+ // TODO: this can also be handled with xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1");
+ if($this->decode_utf8){
+ $data = utf8_decode($data);
+ }
+ }
+ $this->message[$pos]['cdata'] .= $data;
+ // for doclit
+ if($this->status == 'header'){
+ $this->responseHeaders .= $data;
+ } else {
+ $this->document .= $data;
+ }
+ }
+
+ /**
+ * get the parsed message (SOAP Body)
+ *
+ * @return mixed
+ * @access public
+ * @deprecated use get_soapbody instead
+ */
+ function get_response(){
+ return $this->soapresponse;
+ }
+
+ /**
+ * get the parsed SOAP Body (NULL if there was none)
+ *
+ * @return mixed
+ * @access public
+ */
+ function get_soapbody(){
+ return $this->soapresponse;
+ }
+
+ /**
+ * get the parsed SOAP Header (NULL if there was none)
+ *
+ * @return mixed
+ * @access public
+ */
+ function get_soapheader(){
+ return $this->soapheader;
+ }
+
+ /**
+ * get the unparsed SOAP Header
+ *
+ * @return string XML or empty if no Header
+ * @access public
+ */
+ function getHeaders(){
+ return $this->responseHeaders;
+ }
+
+ /**
+ * decodes simple types into PHP variables
+ *
+ * @param string $value value to decode
+ * @param string $type XML type to decode
+ * @param string $typens XML type namespace to decode
+ * @return mixed PHP value
+ * @access private
+ */
+ function decodeSimple($value, $type, $typens) {
+ // TODO: use the namespace!
+ if ((!isset($type)) || $type == 'string' || $type == 'long' || $type == 'unsignedLong') {
+ return (string) $value;
+ }
+ if ($type == 'int' || $type == 'integer' || $type == 'short' || $type == 'byte') {
+ return (int) $value;
+ }
+ if ($type == 'float' || $type == 'double' || $type == 'decimal') {
+ return (double) $value;
+ }
+ if ($type == 'boolean') {
+ if (strtolower($value) == 'false' || strtolower($value) == 'f') {
+ return false;
+ }
+ return (boolean) $value;
+ }
+ if ($type == 'base64' || $type == 'base64Binary') {
+ $this->debug('Decode base64 value');
+ return base64_decode($value);
+ }
+ // obscure numeric types
+ if ($type == 'nonPositiveInteger' || $type == 'negativeInteger'
+ || $type == 'nonNegativeInteger' || $type == 'positiveInteger'
+ || $type == 'unsignedInt'
+ || $type == 'unsignedShort' || $type == 'unsignedByte') {
+ return (int) $value;
+ }
+ // bogus: parser treats array with no elements as a simple type
+ if ($type == 'array') {
+ return array();
+ }
+ // everything else
+ return (string) $value;
+ }
+
+ /**
+ * builds response structures for compound values (arrays/structs)
+ * and scalars
+ *
+ * @param integer $pos position in node tree
+ * @return mixed PHP value
+ * @access private
+ */
+ function buildVal($pos){
+ if(!isset($this->message[$pos]['type'])){
+ $this->message[$pos]['type'] = '';
+ }
+ $this->debug('in buildVal() for '.$this->message[$pos]['name']."(pos $pos) of type ".$this->message[$pos]['type']);
+ // if there are children...
+ if($this->message[$pos]['children'] != ''){
+ $this->debug('in buildVal, there are children');
+ $children = explode('|',$this->message[$pos]['children']);
+ array_shift($children); // knock off empty
+ // md array
+ if(isset($this->message[$pos]['arrayCols']) && $this->message[$pos]['arrayCols'] != ''){
+ $r=0; // rowcount
+ $c=0; // colcount
+ foreach($children as $child_pos){
+ $this->debug("in buildVal, got an MD array element: $r, $c");
+ $params[$r][] = $this->message[$child_pos]['result'];
+ $c++;
+ if($c == $this->message[$pos]['arrayCols']){
+ $c = 0;
+ $r++;
+ }
+ }
+ // array
+ } elseif($this->message[$pos]['type'] == 'array' || $this->message[$pos]['type'] == 'Array'){
+ $this->debug('in buildVal, adding array '.$this->message[$pos]['name']);
+ foreach($children as $child_pos){
+ $params[] = &$this->message[$child_pos]['result'];
+ }
+ // apache Map type: java hashtable
+ } elseif($this->message[$pos]['type'] == 'Map' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap'){
+ $this->debug('in buildVal, Java Map '.$this->message[$pos]['name']);
+ foreach($children as $child_pos){
+ $kv = explode("|",$this->message[$child_pos]['children']);
+ $params[$this->message[$kv[1]]['result']] = &$this->message[$kv[2]]['result'];
+ }
+ // generic compound type
+ //} elseif($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') {
+ } else {
+ // Apache Vector type: treat as an array
+ $this->debug('in buildVal, adding Java Vector or generic compound type '.$this->message[$pos]['name']);
+ if ($this->message[$pos]['type'] == 'Vector' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap') {
+ $notstruct = 1;
+ } else {
+ $notstruct = 0;
+ }
+ //
+ foreach($children as $child_pos){
+ if($notstruct){
+ $params[] = &$this->message[$child_pos]['result'];
+ } else {
+ if (isset($params[$this->message[$child_pos]['name']])) {
+ // de-serialize repeated element name into an array
+ if ((!is_array($params[$this->message[$child_pos]['name']])) || (!isset($params[$this->message[$child_pos]['name']][0]))) {
+ $params[$this->message[$child_pos]['name']] = array($params[$this->message[$child_pos]['name']]);
+ }
+ $params[$this->message[$child_pos]['name']][] = &$this->message[$child_pos]['result'];
+ } else {
+ $params[$this->message[$child_pos]['name']] = &$this->message[$child_pos]['result'];
+ }
+ }
+ }
+ }
+ if (isset($this->message[$pos]['xattrs'])) {
+ $this->debug('in buildVal, handling attributes');
+ foreach ($this->message[$pos]['xattrs'] as $n => $v) {
+ $params[$n] = $v;
+ }
+ }
+ // handle simpleContent
+ if (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') {
+ $this->debug('in buildVal, handling simpleContent');
+ if (isset($this->message[$pos]['type'])) {
+ $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
+ } else {
+ $parent = $this->message[$pos]['parent'];
+ if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
+ $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
+ } else {
+ $params['!'] = $this->message[$pos]['cdata'];
+ }
+ }
+ }
+ $ret = is_array($params) ? $params : array();
+ $this->debug('in buildVal, return:');
+ $this->appendDebug($this->varDump($ret));
+ return $ret;
+ } else {
+ $this->debug('in buildVal, no children, building scalar');
+ $cdata = isset($this->message[$pos]['cdata']) ? $this->message[$pos]['cdata'] : '';
+ if (isset($this->message[$pos]['type'])) {
+ $ret = $this->decodeSimple($cdata, $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
+ $this->debug("in buildVal, return: $ret");
+ return $ret;
+ }
+ $parent = $this->message[$pos]['parent'];
+ if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
+ $ret = $this->decodeSimple($cdata, $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
+ $this->debug("in buildVal, return: $ret");
+ return $ret;
+ }
+ $ret = $this->message[$pos]['cdata'];
+ $this->debug("in buildVal, return: $ret");
+ return $ret;
+ }
+ }
+}
+
+/**
+ * Backward compatibility
+ */
+class soap_parser extends nusoap_parser {
+}
+
+?><?php
+
+
+
+/**
+*
+* [nu]soapclient higher level class for easy usage.
+*
+* usage:
+*
+* // instantiate client with server info
+* $soapclient = new nusoap_client( string path [ ,mixed wsdl] );
+*
+* // call method, get results
+* echo $soapclient->call( string methodname [ ,array parameters] );
+*
+* // bye bye client
+* unset($soapclient);
+*
+* @author Dietrich Ayala <dietrich@ganx4.com>
+* @author Scott Nichol <snichol@users.sourceforge.net>
+* @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
+* @access public
+*/
+class nusoap_client extends nusoap_base {
+
+ var $username = ''; // Username for HTTP authentication
+ var $password = ''; // Password for HTTP authentication
+ var $authtype = ''; // Type of HTTP authentication
+ var $certRequest = array(); // Certificate for HTTP SSL authentication
+ var $requestHeaders = false; // SOAP headers in request (text)
+ var $responseHeaders = ''; // SOAP headers from response (incomplete namespace resolution) (text)
+ var $responseHeader = NULL; // SOAP Header from response (parsed)
+ var $document = ''; // SOAP body response portion (incomplete namespace resolution) (text)
+ var $endpoint;
+ var $forceEndpoint = ''; // overrides WSDL endpoint
+ var $proxyhost = '';
+ var $proxyport = '';
+ var $proxyusername = '';
+ var $proxypassword = '';
+ var $portName = ''; // port name to use in WSDL
+ var $xml_encoding = ''; // character set encoding of incoming (response) messages
+ var $http_encoding = false;
+ var $timeout = 0; // HTTP connection timeout
+ var $response_timeout = 30; // HTTP response timeout
+ var $endpointType = ''; // soap|wsdl, empty for WSDL initialization error
+ var $persistentConnection = false;
+ var $defaultRpcParams = false; // This is no longer used
+ var $request = ''; // HTTP request
+ var $response = ''; // HTTP response
+ var $responseData = ''; // SOAP payload of response
+ var $cookies = array(); // Cookies from response or for request
+ var $decode_utf8 = true; // toggles whether the parser decodes element content w/ utf8_decode()
+ var $operations = array(); // WSDL operations, empty for WSDL initialization error
+ var $curl_options = array(); // User-specified cURL options
+ var $bindingType = ''; // WSDL operation binding type
+ var $use_curl = false; // whether to always try to use cURL
+
+ /*
+ * fault related variables
+ */
+ /**
+ * @var fault
+ * @access public
+ */
+ var $fault;
+ /**
+ * @var faultcode
+ * @access public
+ */
+ var $faultcode;
+ /**
+ * @var faultstring
+ * @access public
+ */
+ var $faultstring;
+ /**
+ * @var faultdetail
+ * @access public
+ */
+ var $faultdetail;
+
+ /**
+ * constructor
+ *
+ * @param mixed $endpoint SOAP server or WSDL URL (string), or wsdl instance (object)
+ * @param mixed $wsdl optional, set to 'wsdl' or true if using WSDL
+ * @param string $proxyhost optional
+ * @param string $proxyport optional
+ * @param string $proxyusername optional
+ * @param string $proxypassword optional
+ * @param integer $timeout set the connection timeout
+ * @param integer $response_timeout set the response timeout
+ * @param string $portName optional portName in WSDL document
+ * @access public
+ */
+ function __construct($endpoint,$wsdl = false,$proxyhost = false,$proxyport = false,$proxyusername = false, $proxypassword = false, $timeout = 0, $response_timeout = 30, $portName = ''){
+ parent::__construct();
+ $this->endpoint = $endpoint;
+ $this->proxyhost = $proxyhost;
+ $this->proxyport = $proxyport;
+ $this->proxyusername = $proxyusername;
+ $this->proxypassword = $proxypassword;
+ $this->timeout = $timeout;
+ $this->response_timeout = $response_timeout;
+ $this->portName = $portName;
+
+ $this->debug("ctor wsdl=$wsdl timeout=$timeout response_timeout=$response_timeout");
+ $this->appendDebug('endpoint=' . $this->varDump($endpoint));
+
+ // make values
+ if($wsdl){
+ if (is_object($endpoint) && (get_class($endpoint) == 'wsdl')) {
+ $this->wsdl = $endpoint;
+ $this->endpoint = $this->wsdl->wsdl;
+ $this->wsdlFile = $this->endpoint;
+ $this->debug('existing wsdl instance created from ' . $this->endpoint);
+ $this->checkWSDL();
+ } else {
+ $this->wsdlFile = $this->endpoint;
+ $this->wsdl = null;
+ $this->debug('will use lazy evaluation of wsdl from ' . $this->endpoint);
+ }
+ $this->endpointType = 'wsdl';
+ } else {
+ $this->debug("instantiate SOAP with endpoint at $endpoint");
+ $this->endpointType = 'soap';
+ }
+ }
+
+ /**
+ * calls method, returns PHP native type
+ *
+ * @param string $operation SOAP server URL or path
+ * @param mixed $params An array, associative or simple, of the parameters
+ * for the method call, or a string that is the XML
+ * for the call. For rpc style, this call will
+ * wrap the XML in a tag named after the method, as
+ * well as the SOAP Envelope and Body. For document
+ * style, this will only wrap with the Envelope and Body.
+ * IMPORTANT: when using an array with document style,
+ * in which case there
+ * is really one parameter, the root of the fragment
+ * used in the call, which encloses what programmers
+ * normally think of parameters. A parameter array
+ * *must* include the wrapper.
+ * @param string $namespace optional method namespace (WSDL can override)
+ * @param string $soapAction optional SOAPAction value (WSDL can override)
+ * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers, or associative array
+ * @param boolean $rpcParams optional (no longer used)
+ * @param string $style optional (rpc|document) the style to use when serializing parameters (WSDL can override)
+ * @param string $use optional (encoded|literal) the use when serializing parameters (WSDL can override)
+ * @return mixed response from SOAP call, normally an associative array mirroring the structure of the XML response, false for certain fatal errors
+ * @access public
+ */
+ function call($operation,$params=array(),$namespace='http://tempuri.org',$soapAction='',$headers=false,$rpcParams=null,$style='rpc',$use='encoded'){
+ $this->operation = $operation;
+ $this->fault = false;
+ $this->setError('');
+ $this->request = '';
+ $this->response = '';
+ $this->responseData = '';
+ $this->faultstring = '';
+ $this->faultcode = '';
+ $this->opData = array();
+
+ $this->debug("call: operation=$operation, namespace=$namespace, soapAction=$soapAction, rpcParams=$rpcParams, style=$style, use=$use, endpointType=$this->endpointType");
+ $this->appendDebug('params=' . $this->varDump($params));
+ $this->appendDebug('headers=' . $this->varDump($headers));
+ if ($headers) {
+ $this->requestHeaders = $headers;
+ }
+ if ($this->endpointType == 'wsdl' && is_null($this->wsdl)) {
+ $this->loadWSDL();
+ if ($this->getError())
+ return false;
+ }
+ // serialize parameters
+ if($this->endpointType == 'wsdl' && $opData = $this->getOperationData($operation)){
+ // use WSDL for operation
+ $this->opData = $opData;
+ $this->debug("found operation");
+ $this->appendDebug('opData=' . $this->varDump($opData));
+ if (isset($opData['soapAction'])) {
+ $soapAction = $opData['soapAction'];
+ }
+ if (! $this->forceEndpoint) {
+ $this->endpoint = $opData['endpoint'];
+ } else {
+ $this->endpoint = $this->forceEndpoint;
+ }
+ $namespace = isset($opData['input']['namespace']) ? $opData['input']['namespace'] : $namespace;
+ $style = $opData['style'];
+ $use = $opData['input']['use'];
+ // add ns to ns array
+ if($namespace != '' && !isset($this->wsdl->namespaces[$namespace])){
+ $nsPrefix = 'ns' . rand(1000, 9999);
+ $this->wsdl->namespaces[$nsPrefix] = $namespace;
+ }
+ $nsPrefix = $this->wsdl->getPrefixFromNamespace($namespace);
+ // serialize payload
+ if (is_string($params)) {
+ $this->debug("serializing param string for WSDL operation $operation");
+ $payload = $params;
+ } elseif (is_array($params)) {
+ $this->debug("serializing param array for WSDL operation $operation");
+ $payload = $this->wsdl->serializeRPCParameters($operation,'input',$params,$this->bindingType);
+ } else {
+ $this->debug('params must be array or string');
+ $this->setError('params must be array or string');
+ return false;
+ }
+ $usedNamespaces = $this->wsdl->usedNamespaces;
+ if (isset($opData['input']['encodingStyle'])) {
+ $encodingStyle = $opData['input']['encodingStyle'];
+ } else {
+ $encodingStyle = '';
+ }
+ $this->appendDebug($this->wsdl->getDebug());
+ $this->wsdl->clearDebug();
+ if ($errstr = $this->wsdl->getError()) {
+ $this->debug('got wsdl error: '.$errstr);
+ $this->setError('wsdl error: '.$errstr);
+ return false;
+ }
+ } elseif($this->endpointType == 'wsdl') {
+ // operation not in WSDL
+ $this->appendDebug($this->wsdl->getDebug());
+ $this->wsdl->clearDebug();
+ $this->setError('operation '.$operation.' not present in WSDL.');
+ $this->debug("operation '$operation' not present in WSDL.");
+ return false;
+ } else {
+ // no WSDL
+ //$this->namespaces['ns1'] = $namespace;
+ $nsPrefix = 'ns' . rand(1000, 9999);
+ // serialize
+ $payload = '';
+ if (is_string($params)) {
+ $this->debug("serializing param string for operation $operation");
+ $payload = $params;
+ } elseif (is_array($params)) {
+ $this->debug("serializing param array for operation $operation");
+ foreach($params as $k => $v){
+ $payload .= $this->serialize_val($v,$k,false,false,false,false,$use);
+ }
+ } else {
+ $this->debug('params must be array or string');
+ $this->setError('params must be array or string');
+ return false;
+ }
+ $usedNamespaces = array();
+ if ($use == 'encoded') {
+ $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
+ } else {
+ $encodingStyle = '';
+ }
+ }
+ // wrap RPC calls with method element
+ if ($style == 'rpc') {
+ if ($use == 'literal') {
+ $this->debug("wrapping RPC request with literal method element");
+ if ($namespace) {
+ // http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html R2735 says rpc/literal accessor elements should not be in a namespace
+ $payload = "<$nsPrefix:$operation xmlns:$nsPrefix=\"$namespace\">" .
+ $payload .
+ "</$nsPrefix:$operation>";
+ } else {
+ $payload = "<$operation>" . $payload . "</$operation>";
+ }
+ } else {
+ $this->debug("wrapping RPC request with encoded method element");
+ if ($namespace) {
+ $payload = "<$nsPrefix:$operation xmlns:$nsPrefix=\"$namespace\">" .
+ $payload .
+ "</$nsPrefix:$operation>";
+ } else {
+ $payload = "<$operation>" .
+ $payload .
+ "</$operation>";
+ }
+ }
+ }
+ // serialize envelope
+ $soapmsg = $this->serializeEnvelope($payload,$this->requestHeaders,$usedNamespaces,$style,$use,$encodingStyle);
+ $this->debug("endpoint=$this->endpoint, soapAction=$soapAction, namespace=$namespace, style=$style, use=$use, encodingStyle=$encodingStyle");
+ $this->debug('SOAP message length=' . strlen($soapmsg) . ' contents (max 1000 bytes)=' . substr($soapmsg, 0, 1000));
+ // send
+ $return = $this->send($this->getHTTPBody($soapmsg),$soapAction,$this->timeout,$this->response_timeout);
+ if($errstr = $this->getError()){
+ $this->debug('Error: '.$errstr);
+ return false;
+ } else {
+ $this->return = $return;
+ $this->debug('sent message successfully and got a(n) '.gettype($return));
+ $this->appendDebug('return=' . $this->varDump($return));
+
+ // fault?
+ if(is_array($return) && isset($return['faultcode'])){
+ $this->debug('got fault');
+ $this->setError($return['faultcode'].': '.$return['faultstring']);
+ $this->fault = true;
+ foreach($return as $k => $v){
+ $this->$k = $v;
+ $this->debug("$k = $v<br>");
+ }
+ return $return;
+ } elseif ($style == 'document') {
+ // NOTE: if the response is defined to have multiple parts (i.e. unwrapped),
+ // we are only going to return the first part here...sorry about that
+ return $return;
+ } else {
+ // array of return values
+ if(is_array($return)){
+ // multiple 'out' parameters, which we return wrapped up
+ // in the array
+ if(sizeof($return) > 1){
+ return $return;
+ }
+ // single 'out' parameter (normally the return value)
+ $return = array_shift($return);
+ $this->debug('return shifted value: ');
+ $this->appendDebug($this->varDump($return));
+ return $return;
+ // nothing returned (ie, echoVoid)
+ } else {
+ return "";
+ }
+ }
+ }
+ }
+
+ /**
+ * check WSDL passed as an instance or pulled from an endpoint
+ *
+ * @access private
+ */
+ function checkWSDL() {
+ $this->appendDebug($this->wsdl->getDebug());
+ $this->wsdl->clearDebug();
+ $this->debug('checkWSDL');
+ // catch errors
+ if ($errstr = $this->wsdl->getError()) {
+ $this->appendDebug($this->wsdl->getDebug());
+ $this->wsdl->clearDebug();
+ $this->debug('got wsdl error: '.$errstr);
+ $this->setError('wsdl error: '.$errstr);
+ } elseif ($this->operations = $this->wsdl->getOperations($this->portName, 'soap')) {
+ $this->appendDebug($this->wsdl->getDebug());
+ $this->wsdl->clearDebug();
+ $this->bindingType = 'soap';
+ $this->debug('got '.count($this->operations).' operations from wsdl '.$this->wsdlFile.' for binding type '.$this->bindingType);
+ } elseif ($this->operations = $this->wsdl->getOperations($this->portName, 'soap12')) {
+ $this->appendDebug($this->wsdl->getDebug());
+ $this->wsdl->clearDebug();
+ $this->bindingType = 'soap12';
+ $this->debug('got '.count($this->operations).' operations from wsdl '.$this->wsdlFile.' for binding type '.$this->bindingType);
+ $this->debug('**************** WARNING: SOAP 1.2 BINDING *****************');
+ } else {
+ $this->appendDebug($this->wsdl->getDebug());
+ $this->wsdl->clearDebug();
+ $this->debug('getOperations returned false');
+ $this->setError('no operations defined in the WSDL document!');
+ }
+ }
+
+ /**
+ * instantiate wsdl object and parse wsdl file
+ *
+ * @access public
+ */
+ function loadWSDL() {
+ $this->debug('instantiating wsdl class with doc: '.$this->wsdlFile);
+ $this->wsdl = new wsdl('',$this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword,$this->timeout,$this->response_timeout,$this->curl_options,$this->use_curl);
+ $this->wsdl->setCredentials($this->username, $this->password, $this->authtype, $this->certRequest);
+ $this->wsdl->fetchWSDL($this->wsdlFile);
+ $this->checkWSDL();
+ }
+
+ /**
+ * get available data pertaining to an operation
+ *
+ * @param string $operation operation name
+ * @return array array of data pertaining to the operation
+ * @access public
+ */
+ function getOperationData($operation){
+ if ($this->endpointType == 'wsdl' && is_null($this->wsdl)) {
+ $this->loadWSDL();
+ if ($this->getError())
+ return false;
+ }
+ if(isset($this->operations[$operation])){
+ return $this->operations[$operation];
+ }
+ $this->debug("No data for operation: $operation");
+ }
+
+ /**
+ * send the SOAP message
+ *
+ * Note: if the operation has multiple return values
+ * the return value of this method will be an array
+ * of those values.
+ *
+ * @param string $msg a SOAPx4 soapmsg object
+ * @param string $soapaction SOAPAction value
+ * @param integer $timeout set connection timeout in seconds
+ * @param integer $response_timeout set response timeout in seconds
+ * @return mixed native PHP types.
+ * @access private
+ */
+ function send($msg, $soapaction = '', $timeout=0, $response_timeout=30) {
+ $this->checkCookies();
+ // detect transport
+ switch(true){
+ // http(s)
+ case preg_match('/^http/',$this->endpoint):
+ $this->debug('transporting via HTTP');
+ if($this->persistentConnection == true && is_object($this->persistentConnection)){
+ $http =& $this->persistentConnection;
+ } else {
+ $http = new soap_transport_http($this->endpoint, $this->curl_options, $this->use_curl);
+ if ($this->persistentConnection) {
+ $http->usePersistentConnection();
+ }
+ }
+ $http->setContentType($this->getHTTPContentType(), $this->getHTTPContentTypeCharset());
+ $http->setSOAPAction($soapaction);
+ if($this->proxyhost && $this->proxyport){
+ $http->setProxy($this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword);
+ }
+ if($this->authtype != '') {
+ $http->setCredentials($this->username, $this->password, $this->authtype, array(), $this->certRequest);
+ }
+ if($this->http_encoding != ''){
+ $http->setEncoding($this->http_encoding);
+ }
+ $this->debug('sending message, length='.strlen($msg));
+ if(preg_match('/^http:/',$this->endpoint)){
+ //if(strpos($this->endpoint,'http:')){
+ $this->responseData = $http->send($msg,$timeout,$response_timeout,$this->cookies);
+ } elseif(preg_match('/^https/',$this->endpoint)){
+ //} elseif(strpos($this->endpoint,'https:')){
+ //if(phpversion() == '4.3.0-dev'){
+ //$response = $http->send($msg,$timeout,$response_timeout);
+ //$this->request = $http->outgoing_payload;
+ //$this->response = $http->incoming_payload;
+ //} else
+ $this->responseData = $http->sendHTTPS($msg,$timeout,$response_timeout,$this->cookies);
+ } else {
+ $this->setError('no http/s in endpoint url');
+ }
+ $this->request = $http->outgoing_payload;
+ $this->response = $http->incoming_payload;
+ $this->appendDebug($http->getDebug());
+ $this->UpdateCookies($http->incoming_cookies);
+
+ // save transport object if using persistent connections
+ if ($this->persistentConnection) {
+ $http->clearDebug();
+ if (!is_object($this->persistentConnection)) {
+ $this->persistentConnection = $http;
+ }
+ }
+
+ if($err = $http->getError()){
+ $this->setError('HTTP Error: '.$err);
+ return false;
+ } elseif($this->getError()){
+ return false;
+ } else {
+ $this->debug('got response, length='. strlen($this->responseData).' type='.$http->incoming_headers['content-type']);
+ return $this->parseResponse($http->incoming_headers, $this->responseData);
+ }
+ break;
+ default:
+ $this->setError('no transport found, or selected transport is not yet supported!');
+ return false;
+ break;
+ }
+ }
+
+ /**
+ * processes SOAP message returned from server
+ *
+ * @param array $headers The HTTP headers
+ * @param string $data unprocessed response data from server
+ * @return mixed value of the message, decoded into a PHP type
+ * @access private
+ */
+ function parseResponse($headers, $data) {
+ $this->debug('Entering parseResponse() for data of length ' . strlen($data) . ' headers:');
+ $this->appendDebug($this->varDump($headers));
+ if (!isset($headers['content-type'])) {
+ $this->setError('Response not of type text/xml (no content-type header)');
+ return false;
+ }
+ if (!strstr($headers['content-type'], 'text/xml')) {
+ $this->setError('Response not of type text/xml: ' . $headers['content-type']);
+ return false;
+ }
+ if (strpos($headers['content-type'], '=')) {
+ $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
+ $this->debug('Got response encoding: ' . $enc);
+ if(preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)){
+ $this->xml_encoding = strtoupper($enc);
+ } else {
+ $this->xml_encoding = 'US-ASCII';
+ }
+ } else {
+ // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
+ $this->xml_encoding = 'ISO-8859-1';
+ }
+ $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser');
+ $parser = new nusoap_parser($data,$this->xml_encoding,$this->operation,$this->decode_utf8);
+ // add parser debug data to our debug
+ $this->appendDebug($parser->getDebug());
+ // if parse errors
+ if($errstr = $parser->getError()){
+ $this->setError( $errstr);
+ // destroy the parser object
+ unset($parser);
+ return false;
+ } else {
+ // get SOAP headers
+ $this->responseHeaders = $parser->getHeaders();
+ // get SOAP headers
+ $this->responseHeader = $parser->get_soapheader();
+ // get decoded message
+ $return = $parser->get_soapbody();
+ // add document for doclit support
+ $this->document = $parser->document;
+ // destroy the parser object
+ unset($parser);
+ // return decode message
+ return $return;
+ }
+ }
+
+ /**
+ * sets user-specified cURL options
+ *
+ * @param mixed $option The cURL option (always integer?)
+ * @param mixed $value The cURL option value
+ * @access public
+ */
+ function setCurlOption($option, $value) {
+ $this->debug("setCurlOption option=$option, value=");
+ $this->appendDebug($this->varDump($value));
+ $this->curl_options[$option] = $value;
+ }
+
+ /**
+ * sets the SOAP endpoint, which can override WSDL
+ *
+ * @param string $endpoint The endpoint URL to use, or empty string or false to prevent override
+ * @access public
+ */
+ function setEndpoint($endpoint) {
+ $this->debug("setEndpoint(\"$endpoint\")");
+ $this->forceEndpoint = $endpoint;
+ }
+
+ /**
+ * set the SOAP headers
+ *
+ * @param mixed $headers String of XML with SOAP header content, or array of soapval objects for SOAP headers
+ * @access public
+ */
+ function setHeaders($headers){
+ $this->debug("setHeaders headers=");
+ $this->appendDebug($this->varDump($headers));
+ $this->requestHeaders = $headers;
+ }
+
+ /**
+ * get the SOAP response headers (namespace resolution incomplete)
+ *
+ * @return string
+ * @access public
+ */
+ function getHeaders(){
+ return $this->responseHeaders;
+ }
+
+ /**
+ * get the SOAP response Header (parsed)
+ *
+ * @return mixed
+ * @access public
+ */
+ function getHeader(){
+ return $this->responseHeader;
+ }
+
+ /**
+ * set proxy info here
+ *
+ * @param string $proxyhost
+ * @param string $proxyport
+ * @param string $proxyusername
+ * @param string $proxypassword
+ * @access public
+ */
+ function setHTTPProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '') {
+ $this->proxyhost = $proxyhost;
+ $this->proxyport = $proxyport;
+ $this->proxyusername = $proxyusername;
+ $this->proxypassword = $proxypassword;
+ }
+
+ /**
+ * if authenticating, set user credentials here
+ *
+ * @param string $username
+ * @param string $password
+ * @param string $authtype (basic|digest|certificate|ntlm)
+ * @param array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
+ * @access public
+ */
+ function setCredentials($username, $password, $authtype = 'basic', $certRequest = array()) {
+ $this->debug("setCredentials username=$username authtype=$authtype certRequest=");
+ $this->appendDebug($this->varDump($certRequest));
+ $this->username = $username;
+ $this->password = $password;
+ $this->authtype = $authtype;
+ $this->certRequest = $certRequest;
+ }
+
+ /**
+ * use HTTP encoding
+ *
+ * @param string $enc HTTP encoding
+ * @access public
+ */
+ function setHTTPEncoding($enc='gzip, deflate'){
+ $this->debug("setHTTPEncoding(\"$enc\")");
+ $this->http_encoding = $enc;
+ }
+
+ /**
+ * Set whether to try to use cURL connections if possible
+ *
+ * @param boolean $use Whether to try to use cURL
+ * @access public
+ */
+ function setUseCURL($use) {
+ $this->debug("setUseCURL($use)");
+ $this->use_curl = $use;
+ }
+
+ /**
+ * use HTTP persistent connections if possible
+ *
+ * @access public
+ */
+ function useHTTPPersistentConnection(){
+ $this->debug("useHTTPPersistentConnection");
+ $this->persistentConnection = true;
+ }
+
+ /**
+ * gets the default RPC parameter setting.
+ * If true, default is that call params are like RPC even for document style.
+ * Each call() can override this value.
+ *
+ * This is no longer used.
+ *
+ * @return boolean
+ * @access public
+ * @deprecated
+ */
+ function getDefaultRpcParams() {
+ return $this->defaultRpcParams;
+ }
+
+ /**
+ * sets the default RPC parameter setting.
+ * If true, default is that call params are like RPC even for document style
+ * Each call() can override this value.
+ *
+ * This is no longer used.
+ *
+ * @param boolean $rpcParams
+ * @access public
+ * @deprecated
+ */
+ function setDefaultRpcParams($rpcParams) {
+ $this->defaultRpcParams = $rpcParams;
+ }
+
+ /**
+ * dynamically creates an instance of a proxy class,
+ * allowing user to directly call methods from wsdl
+ *
+ * @return object soap_proxy object
+ * @access public
+ */
+ function getProxy() {
+ $r = rand();
+ $evalStr = $this->_getProxyClassCode($r);
+ //$this->debug("proxy class: $evalStr");
+ if ($this->getError()) {
+ $this->debug("Error from _getProxyClassCode, so return NULL");
+ return null;
+ }
+ // eval the class
+ eval($evalStr);
+ // instantiate proxy object
+ eval("\$proxy = new nusoap_proxy_$r('');");
+ // transfer current wsdl data to the proxy thereby avoiding parsing the wsdl twice
+ $proxy->endpointType = 'wsdl';
+ $proxy->wsdlFile = $this->wsdlFile;
+ $proxy->wsdl = $this->wsdl;
+ $proxy->operations = $this->operations;
+ $proxy->defaultRpcParams = $this->defaultRpcParams;
+ // transfer other state
+ $proxy->soap_defencoding = $this->soap_defencoding;
+ $proxy->username = $this->username;
+ $proxy->password = $this->password;
+ $proxy->authtype = $this->authtype;
+ $proxy->certRequest = $this->certRequest;
+ $proxy->requestHeaders = $this->requestHeaders;
+ $proxy->endpoint = $this->endpoint;
+ $proxy->forceEndpoint = $this->forceEndpoint;
+ $proxy->proxyhost = $this->proxyhost;
+ $proxy->proxyport = $this->proxyport;
+ $proxy->proxyusername = $this->proxyusername;
+ $proxy->proxypassword = $this->proxypassword;
+ $proxy->http_encoding = $this->http_encoding;
+ $proxy->timeout = $this->timeout;
+ $proxy->response_timeout = $this->response_timeout;
+ $proxy->persistentConnection = &$this->persistentConnection;
+ $proxy->decode_utf8 = $this->decode_utf8;
+ $proxy->curl_options = $this->curl_options;
+ $proxy->bindingType = $this->bindingType;
+ $proxy->use_curl = $this->use_curl;
+ return $proxy;
+ }
+
+ /**
+ * dynamically creates proxy class code
+ *
+ * @return string PHP/NuSOAP code for the proxy class
+ * @access private
+ */
+ function _getProxyClassCode($r) {
+ $this->debug("in getProxy endpointType=$this->endpointType");
+ $this->appendDebug("wsdl=" . $this->varDump($this->wsdl));
+ if ($this->endpointType != 'wsdl') {
+ $evalStr = 'A proxy can only be created for a WSDL client';
+ $this->setError($evalStr);
+ $evalStr = "echo \"$evalStr\";";
+ return $evalStr;
+ }
+ if ($this->endpointType == 'wsdl' && is_null($this->wsdl)) {
+ $this->loadWSDL();
+ if ($this->getError()) {
+ return "echo \"" . $this->getError() . "\";";
+ }
+ }
+ $evalStr = '';
+ foreach ($this->operations as $operation => $opData) {
+ if ($operation != '') {
+ // create param string and param comment string
+ if (sizeof($opData['input']['parts']) > 0) {
+ $paramStr = '';
+ $paramArrayStr = '';
+ $paramCommentStr = '';
+ foreach ($opData['input']['parts'] as $name => $type) {
+ $paramStr .= "\$$name, ";
+ $paramArrayStr .= "'$name' => \$$name, ";
+ $paramCommentStr .= "$type \$$name, ";
+ }
+ $paramStr = substr($paramStr, 0, strlen($paramStr)-2);
+ $paramArrayStr = substr($paramArrayStr, 0, strlen($paramArrayStr)-2);
+ $paramCommentStr = substr($paramCommentStr, 0, strlen($paramCommentStr)-2);
+ } else {
+ $paramStr = '';
+ $paramArrayStr = '';
+ $paramCommentStr = 'void';
+ }
+ $opData['namespace'] = !isset($opData['namespace']) ? 'http://testuri.com' : $opData['namespace'];
+ $evalStr .= "// $paramCommentStr
+ function " . str_replace('.', '__', $operation) . "($paramStr) {
+ \$params = array($paramArrayStr);
+ return \$this->call('$operation', \$params, '".$opData['namespace']."', '".(isset($opData['soapAction']) ? $opData['soapAction'] : '')."');
+ }
+ ";
+ unset($paramStr);
+ unset($paramCommentStr);
+ }
+ }
+ $evalStr = 'class nusoap_proxy_'.$r.' extends nusoap_client {
+ '.$evalStr.'
+}';
+ return $evalStr;
+ }
+
+ /**
+ * dynamically creates proxy class code
+ *
+ * @return string PHP/NuSOAP code for the proxy class
+ * @access public
+ */
+ function getProxyClassCode() {
+ $r = rand();
+ return $this->_getProxyClassCode($r);
+ }
+
+ /**
+ * gets the HTTP body for the current request.
+ *
+ * @param string $soapmsg The SOAP payload
+ * @return string The HTTP body, which includes the SOAP payload
+ * @access private
+ */
+ function getHTTPBody($soapmsg) {
+ return $soapmsg;
+ }
+
+ /**
+ * gets the HTTP content type for the current request.
+ *
+ * Note: getHTTPBody must be called before this.
+ *
+ * @return string the HTTP content type for the current request.
+ * @access private
+ */
+ function getHTTPContentType() {
+ return 'text/xml';
+ }
+
+ /**
+ * gets the HTTP content type charset for the current request.
+ * returns false for non-text content types.
+ *
+ * Note: getHTTPBody must be called before this.
+ *
+ * @return string the HTTP content type charset for the current request.
+ * @access private
+ */
+ function getHTTPContentTypeCharset() {
+ return $this->soap_defencoding;
+ }
+
+ /*
+ * whether or not parser should decode utf8 element content
+ *
+ * @return always returns true
+ * @access public
+ */
+ function decodeUTF8($bool){
+ $this->decode_utf8 = $bool;
+ return true;
+ }
+
+ /**
+ * adds a new Cookie into $this->cookies array
+ *
+ * @param string $name Cookie Name
+ * @param string $value Cookie Value
+ * @return boolean if cookie-set was successful returns true, else false
+ * @access public
+ */
+ function setCookie($name, $value) {
+ if (strlen($name) == 0) {
+ return false;
+ }
+ $this->cookies[] = array('name' => $name, 'value' => $value);
+ return true;
+ }
+
+ /**
+ * gets all Cookies
+ *
+ * @return array with all internal cookies
+ * @access public
+ */
+ function getCookies() {
+ return $this->cookies;
+ }
+
+ /**
+ * checks all Cookies and delete those which are expired
+ *
+ * @return boolean always return true
+ * @access private
+ */
+ function checkCookies() {
+ if (sizeof($this->cookies) == 0) {
+ return true;
+ }
+ $this->debug('checkCookie: check ' . sizeof($this->cookies) . ' cookies');
+ $curr_cookies = $this->cookies;
+ $this->cookies = array();
+ foreach ($curr_cookies as $cookie) {
+ if (! is_array($cookie)) {
+ $this->debug('Remove cookie that is not an array');
+ continue;
+ }
+ if ((isset($cookie['expires'])) && (! empty($cookie['expires']))) {
+ if (strtotime($cookie['expires']) > time()) {
+ $this->cookies[] = $cookie;
+ } else {
+ $this->debug('Remove expired cookie ' . $cookie['name']);
+ }
+ } else {
+ $this->cookies[] = $cookie;
+ }
+ }
+ $this->debug('checkCookie: '.sizeof($this->cookies).' cookies left in array');
+ return true;
+ }
+
+ /**
+ * updates the current cookies with a new set
+ *
+ * @param array $cookies new cookies with which to update current ones
+ * @return boolean always return true
+ * @access private
+ */
+ function UpdateCookies($cookies) {
+ if (sizeof($this->cookies) == 0) {
+ // no existing cookies: take whatever is new
+ if (sizeof($cookies) > 0) {
+ $this->debug('Setting new cookie(s)');
+ $this->cookies = $cookies;
+ }
+ return true;
+ }
+ if (sizeof($cookies) == 0) {
+ // no new cookies: keep what we've got
+ return true;
+ }
+ // merge
+ foreach ($cookies as $newCookie) {
+ if (!is_array($newCookie)) {
+ continue;
+ }
+ if ((!isset($newCookie['name'])) || (!isset($newCookie['value']))) {
+ continue;
+ }
+ $newName = $newCookie['name'];
+
+ $found = false;
+ for ($i = 0; $i < count($this->cookies); $i++) {
+ $cookie = $this->cookies[$i];
+ if (!is_array($cookie)) {
+ continue;
+ }
+ if (!isset($cookie['name'])) {
+ continue;
+ }
+ if ($newName != $cookie['name']) {
+ continue;
+ }
+ $newDomain = isset($newCookie['domain']) ? $newCookie['domain'] : 'NODOMAIN';
+ $domain = isset($cookie['domain']) ? $cookie['domain'] : 'NODOMAIN';
+ if ($newDomain != $domain) {
+ continue;
+ }
+ $newPath = isset($newCookie['path']) ? $newCookie['path'] : 'NOPATH';
+ $path = isset($cookie['path']) ? $cookie['path'] : 'NOPATH';
+ if ($newPath != $path) {
+ continue;
+ }
+ $this->cookies[$i] = $newCookie;
+ $found = true;
+ $this->debug('Update cookie ' . $newName . '=' . $newCookie['value']);
+ break;
+ }
+ if (! $found) {
+ $this->debug('Add cookie ' . $newName . '=' . $newCookie['value']);
+ $this->cookies[] = $newCookie;
+ }
+ }
+ return true;
+ }
+}
+
+/**
+ * For backwards compatiblity, define soap_client as in version 0.7.2.
+ */
+class soap_client extends nusoap_client {
+}
+?>
\ No newline at end of file diff --git a/vendor/nusoap/nusoap.php.studip.patch b/vendor/nusoap/nusoap.php.studip.patch new file mode 100644 index 0000000..7669f41 --- /dev/null +++ b/vendor/nusoap/nusoap.php.studip.patch @@ -0,0 +1,26 @@ +--- nusoap.php 2010-04-26 22:38:08.000000000 +0200
++++ nusoap.php 2011-07-27 12:30:48.543869991 +0200
+@@ -6129,7 +6129,7 @@
+ $rows = sizeof($value);
+ $contents = '';
+ foreach($value as $k => $v) {
+- $this->debug("serializing array element: $k, $v of type: $typeDef[arrayType]");
++ $this->debug("serializing array element: $k, ".$this->varDump($v)." of type: ".$typeDef['arrayType']);
+ //if (strpos($typeDef['arrayType'], ':') ) {
+ if (!in_array($typeDef['arrayType'],$this->typemap['http://www.w3.org/2001/XMLSchema'])) {
+ $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use);
+@@ -8138,11 +8138,9 @@
+ }
+ }
+
+-if (!extension_loaded('soap')) {
+- /**
+- * For backwards compatiblity, define soapclient unless the PHP SOAP extension is loaded.
++/**
++ * For backwards compatiblity, define soap_client as in version 0.7.2.
+ */
+- class soapclient extends nusoap_client {
+- }
++class soap_client extends nusoap_client {
+ }
+ ?>
diff --git a/vendor/oauth-php/LICENSE b/vendor/oauth-php/LICENSE new file mode 100644 index 0000000..fbdcc37 --- /dev/null +++ b/vendor/oauth-php/LICENSE @@ -0,0 +1,22 @@ +The MIT License + +Copyright (c) 2007-2009 Mediamatic Lab +Copyright (c) 2010 Corollarium Technologies + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE.
\ No newline at end of file diff --git a/vendor/oauth-php/README b/vendor/oauth-php/README new file mode 100644 index 0000000..ecd6815 --- /dev/null +++ b/vendor/oauth-php/README @@ -0,0 +1 @@ +Please see http://code.google.com/p/oauth-php/ for documentation and help. diff --git a/vendor/oauth-php/example/client/googledocs.php b/vendor/oauth-php/example/client/googledocs.php new file mode 100644 index 0000000..45d66cc --- /dev/null +++ b/vendor/oauth-php/example/client/googledocs.php @@ -0,0 +1,109 @@ +<?php + +/** + * oauth-php: Example OAuth client for accessing Google Docs + * + * @author BBG + * + * + * The MIT License + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +include_once "../../library/OAuthStore.php"; +include_once "../../library/OAuthRequester.php"; + +define("GOOGLE_CONSUMER_KEY", "FILL THIS"); // +define("GOOGLE_CONSUMER_SECRET", "FILL THIS"); // + +define("GOOGLE_OAUTH_HOST", "https://www.google.com"); +define("GOOGLE_REQUEST_TOKEN_URL", GOOGLE_OAUTH_HOST . "/accounts/OAuthGetRequestToken"); +define("GOOGLE_AUTHORIZE_URL", GOOGLE_OAUTH_HOST . "/accounts/OAuthAuthorizeToken"); +define("GOOGLE_ACCESS_TOKEN_URL", GOOGLE_OAUTH_HOST . "/accounts/OAuthGetAccessToken"); + +define('OAUTH_TMP_DIR', function_exists('sys_get_temp_dir') ? sys_get_temp_dir() : realpath($_ENV["TMP"])); + +// Init the OAuthStore +$options = array( + 'consumer_key' => GOOGLE_CONSUMER_KEY, + 'consumer_secret' => GOOGLE_CONSUMER_SECRET, + 'server_uri' => GOOGLE_OAUTH_HOST, + 'request_token_uri' => GOOGLE_REQUEST_TOKEN_URL, + 'authorize_uri' => GOOGLE_AUTHORIZE_URL, + 'access_token_uri' => GOOGLE_ACCESS_TOKEN_URL +); +// Note: do not use "Session" storage in production. Prefer a database +// storage, such as MySQL. +OAuthStore::instance("Session", $options); + +try +{ + // STEP 1: If we do not have an OAuth token yet, go get one + if (empty($_GET["oauth_token"])) + { + $getAuthTokenParams = array('scope' => + 'http://docs.google.com/feeds/', + 'xoauth_displayname' => 'Oauth test', + 'oauth_callback' => 'XXXXXXXXXXX'); + + // get a request token + $tokenResultParams = OAuthRequester::requestRequestToken(GOOGLE_CONSUMER_KEY, 0, $getAuthTokenParams); + + // redirect to the google authorization page, they will redirect back + header("Location: " . GOOGLE_AUTHORIZE_URL . "?btmpl=mobile&oauth_token=" . $tokenResultParams['token']); + } + else { + // STEP 2: Get an access token + $oauthToken = $_GET["oauth_token"]; + + // echo "oauth_verifier = '" . $oauthVerifier . "'<br/>"; + $tokenResultParams = $_GET; + + try { + OAuthRequester::requestAccessToken(GOOGLE_CONSUMER_KEY, $oauthToken, 0, 'POST', $_GET); + } + catch (OAuthException2 $e) + { + var_dump($e); + // Something wrong with the oauth_token. + // Could be: + // 1. Was already ok + // 2. We were not authorized + return; + } + + // make the docs requestrequest. + $request = new OAuthRequester("http://docs.google.com/feeds/documents/private/full", 'GET', $tokenResultParams); + $result = $request->doRequest(0); + if ($result['code'] == 200) { + var_dump($result['body']); + } + else { + echo 'Error'; + } + } +} +catch(OAuthException2 $e) { + echo "OAuthException: " . $e->getMessage(); + var_dump($e); +} +?>
\ No newline at end of file diff --git a/vendor/oauth-php/example/client/opera.php b/vendor/oauth-php/example/client/opera.php new file mode 100644 index 0000000..d881c98 --- /dev/null +++ b/vendor/oauth-php/example/client/opera.php @@ -0,0 +1,125 @@ +<?php + +/** + * oauth-php: Example OAuth client for accessing my opera + * + * @author Ryan + * + * + * The MIT License + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Request your consumer key/secret here: + * http://auth.opera.com/service/oauth/applications/ + * Make sure to set the Application callback URL + * + * To make this example work change the following files + * + * OAuthRequestSigner.php // Opera oAuth doesn't accept twice encoded signature + * $this->setParam('oauth_signature', $signature, true); + * to: + * $this->setParam('oauth_signature', urldecode($signature), true); + */ + +include_once "../../library/OAuthStore.php"; +include_once "../../library/OAuthRequester.php"; + +define("OPERA_CONSUMER_KEY", "---"); +define("OPERA_CONSUMER_SECRET", "---"); + +define("OPERA_REQUEST_TOKEN_URL", "https://auth.opera.com/service/oauth/request_token"); +define("OPERA_AUTHORIZE_URL", "https://auth.opera.com/service/oauth/authorize"); +define("OPERA_ACCESS_TOKEN_URL", "https://auth.opera.com/service/oauth/access_token"); + +define('OAUTH_TMP_DIR', function_exists('sys_get_temp_dir') ? sys_get_temp_dir() : realpath($_ENV["TMP"])); + +// Start the session +session_start(); + +// Init the OAuthStore +$options = array( + 'consumer_key' => OPERA_CONSUMER_KEY, + 'consumer_secret' => OPERA_CONSUMER_SECRET, + 'server_uri' => 'http://my.opera.com/community/api/', + 'request_token_uri' => OPERA_REQUEST_TOKEN_URL, + 'authorize_uri' => OPERA_AUTHORIZE_URL, + 'access_token_uri' => OPERA_ACCESS_TOKEN_URL +); +// Note: do not use "Session" storage in production. Prefer a database +// storage, such as MySQL. +OAuthStore::instance("Session", $options); + +try +{ + // STEP 1: If we do not have an OAuth token yet, go get one + if (empty($_GET["oauth_verifier"])) + { + $getAuthTokenParams = array( + 'oauth_callback'=>'oob' + ); + $options = array ( + 'oauth_as_header' => false + ); + + // get a request token + $tokenResultParams = OAuthRequester::requestRequestToken(OPERA_CONSUMER_KEY, 0, $getAuthTokenParams, 'POST', $options); + $_SESSION['oauth_token'] = $tokenResultParams['token']; + + // redirect to the opera authorization page, they will redirect back + header("Location: " . OPERA_AUTHORIZE_URL . "?oauth_token=" . $tokenResultParams['token']); + } + else { + // STEP 2: Get an access token + try { + OAuthRequester::requestAccessToken(OPERA_CONSUMER_KEY, $_SESSION['oauth_token'], 0, 'POST', $options=array( + 'oauth_verifier'=>$_GET['oauth_verifier'] + )); + } + catch (OAuthException2 $e) + { + var_dump($e); + // Something wrong with the oauth_token. + // Could be: + // 1. Was already ok + // 2. We were not authorized + return; + } + + // make the docs requestrequest. + $request = new OAuthRequester("http://my.opera.com/community/api/users/status.pl", 'GET'); + $result = $request->doRequest(0,array( + CURLOPT_HTTPHEADER=>array( + 'Accept: application/json', + ), + )); + if ($result['code'] == 200) { + var_dump($result['body']); + } + else { + echo 'Error'; + } + } +} +catch(OAuthException2 $e) { + echo "OAuthException: " . $e->getMessage(); + var_dump($e); +} +?> diff --git a/vendor/oauth-php/example/client/twolegged.php b/vendor/oauth-php/example/client/twolegged.php new file mode 100644 index 0000000..a22c4fd --- /dev/null +++ b/vendor/oauth-php/example/client/twolegged.php @@ -0,0 +1,67 @@ +<?php + +/** + * oauth-php: Example OAuth client + * + * Performs simple 2-legged authentication + * + * @author Ben Hesketh + * + * + * The MIT License + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +include_once "../../library/OAuthStore.php"; +include_once "../../library/OAuthRequester.php"; + +// Test of the OAuthStore2Leg + +$key = '???????'; // fill with your public key +$secret = '????????'; // fill with your secret key +$url = "?????????"; // fill with the url for the oauth service + +$options = array('consumer_key' => $key, 'consumer_secret' => $secret); +OAuthStore::instance("2Leg", $options); + +$method = "GET"; +$params = null; + +try +{ + // Obtain a request object for the request we want to make + $request = new OAuthRequester($url, $method, $params); + + // Sign the request, perform a curl request and return the results, + // throws OAuthException2 exception on an error + // $result is an array of the form: array ('code'=>int, 'headers'=>array(), 'body'=>string) + $result = $request->doRequest(); + + $response = $result['body']; + var_dump($response); +} +catch(OAuthException2 $e) +{ + echo "Exception"; +} + +?> diff --git a/vendor/oauth-php/example/client/twoleggedtest.php b/vendor/oauth-php/example/client/twoleggedtest.php new file mode 100644 index 0000000..0fc866b --- /dev/null +++ b/vendor/oauth-php/example/client/twoleggedtest.php @@ -0,0 +1,78 @@ +<?php + +/** + * oauth-php: Example OAuth client + * + * Performs simple 2-legged authentication + * + * @author Ben Hesketh + * + * + * The MIT License + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +include_once "../../library/OAuthStore.php"; +include_once "../../library/OAuthRequester.php"; + +// Test of the OAuthStore2Leg +// uses http://term.ie/oauth/example/ + +$key = 'key'; // fill with your public key +$secret = 'secret'; // fill with your secret key +$url = "http://term.ie/oauth/example/request_token.php"; // fill with the url for the oauth service + +$options = array('consumer_key' => $key, 'consumer_secret' => $secret); +OAuthStore::instance("2Leg", $options); + +$method = "GET"; +$params = null; + +try +{ + // Obtain a request object for the request we want to make + $request = new OAuthRequester($url, $method, $params); + + // Sign the request, perform a curl request and return the results, + // throws OAuthException2 exception on an error + // $result is an array of the form: array ('code'=>int, 'headers'=>array(), 'body'=>string) + $result = $request->doRequest(); + + $response = $result['body']; + + if ($response != 'oauth_token=requestkey&oauth_token_secret=requestsecret') + { + echo 'Error! $response ' . $response; + } + else + { + } + + + var_dump($response); +} +catch(OAuthException2 $e) +{ + echo "Exception" . $e->getMessage(); +} + +?> diff --git a/vendor/oauth-php/example/client/twoleggedtwitter.php b/vendor/oauth-php/example/client/twoleggedtwitter.php new file mode 100644 index 0000000..871c2a4 --- /dev/null +++ b/vendor/oauth-php/example/client/twoleggedtwitter.php @@ -0,0 +1,67 @@ +<?php + +/** + * oauth-php: Example OAuth client + * + * Performs simple 2-legged authentication + * + * The MIT License + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +include_once "../../library/OAuthStore.php"; +include_once "../../library/OAuthRequester.php"; + +// register at http://twitter.com/oauth_clients and fill these two +define("TWITTER_CONSUMER_KEY", "FILL THIS"); +define("TWITTER_CONSUMER_SECRET", "FILL THIS"); + +define("TWITTER_OAUTH_HOST","https://twitter.com"); +define("TWITTER_REQUEST_TOKEN_URL", TWITTER_OAUTH_HOST . "/oauth/request_token"); +define("TWITTER_AUTHORIZE_URL", TWITTER_OAUTH_HOST . "/oauth/authorize"); +define("TWITTER_ACCESS_TOKEN_URL", TWITTER_OAUTH_HOST . "/oauth/access_token"); +define("TWITTER_PUBLIC_TIMELINE_API", TWITTER_OAUTH_HOST . "/statuses/public_timeline.json"); +define("TWITTER_UPDATE_STATUS_API", TWITTER_OAUTH_HOST . "/statuses/update.json"); + +define('OAUTH_TMP_DIR', function_exists('sys_get_temp_dir') ? sys_get_temp_dir() : realpath($_ENV["TMP"])); + +// Twitter test +$options = array('consumer_key' => TWITTER_CONSUMER_KEY, 'consumer_secret' => TWITTER_CONSUMER_SECRET); +OAuthStore::instance("2Leg", $options); + +try +{ + // Obtain a request object for the request we want to make + $request = new OAuthRequester(TWITTER_REQUEST_TOKEN_URL, "POST"); + $result = $request->doRequest(0); + parse_str($result['body'], $params); + + // now make the request. + $request = new OAuthRequester(TWITTER_PUBLIC_TIMELINE_API, 'GET', $params); + $result = $request->doRequest(); +} +catch(OAuthException2 $e) +{ + echo "Exception" . $e->getMessage(); +} + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/example/server/INSTALL b/vendor/oauth-php/example/server/INSTALL new file mode 100644 index 0000000..249c85e --- /dev/null +++ b/vendor/oauth-php/example/server/INSTALL @@ -0,0 +1,53 @@ +In this example I assume that oauth-php lives in /home/john/src/oauth-php + + +1) Create a virtual host and set the DB_DSN VARIABLE to the DSN of your (mysql) database. + +Example +<VirtualHost *> + ServerAdmin admin@localhost + ServerName hello.local + DocumentRoot /home/john/src/oauth-php/example/server/www + + UseCanonicalName Off + ServerSignature On + + SetEnv DB_DSN mysql://foo:bar@localhost/oauth_example_server_db + + <Directory "home/john/src/oauth-php/example/server/www"> + Options Indexes FollowSymLinks MultiViews + AllowOverride None + Order allow,deny + Allow from all + + <IfModule mod_php5.c> + php_value magic_quotes_gpc 0 + php_value register_globals 0 + php_value session.auto_start 0 + </IfModule> + + </Directory> +</VirtualHost> + + +2) Create the database structure for the server: + +# mysql -u foo -p bar -h localhost < /home/john/src/oauth-php/library/store/mysql/mysql.sql + + + +3) Download and install smarty into the smarty/core/smarty directory: + +# cd /home/john/src/oauth-php/example/server/core +# wget 'http://www.smarty.net/do_download.php?download_file=Smarty-2.6.19.tar.gz' +# tar zxf Smarty-2.6.19.tar.gz +# mv Smarty-2.6.19 smarty + + +4) That's it! Point your browser to + + http://hello.local/ + +To get started. + +Arjan Scherpenisse <arjan@mediamatic.nl>, July 2008 diff --git a/vendor/oauth-php/example/server/core/init.php b/vendor/oauth-php/example/server/core/init.php new file mode 100644 index 0000000..82c65db --- /dev/null +++ b/vendor/oauth-php/example/server/core/init.php @@ -0,0 +1,128 @@ +<?php + +/** + * oauth-php: Example OAuth server + * + * Global initialization file for the server, defines some helper + * functions, required includes, and starts the session. + * + * @author Arjan Scherpenisse <arjan@scherpenisse.net> + * + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +/* + * Simple 'user management' + */ +define ('USERNAME', 'sysadmin'); +define ('PASSWORD', 'sysadmin'); + + +/* + * Always announce XRDS OAuth discovery + */ +header('X-XRDS-Location: http://' . $_SERVER['SERVER_NAME'] . '/services.xrds'); + + +/* + * Initialize the database connection + */ +$info = parse_url(getenv('DB_DSN')); +($GLOBALS['db_conn'] = mysql_connect($info['host'], $info['user'], $info['pass'])) || die(mysql_error()); +mysql_select_db(basename($info['path']), $GLOBALS['db_conn']) || die(mysql_error()); +unset($info); + + +require_once '../../../library/OAuthServer.php'; + +/* + * Initialize OAuth store + */ +require_once '../../../library/OAuthStore.php'; +OAuthStore::instance('MySQL', array('conn' => $GLOBALS['db_conn'])); + + +/* + * Session + */ +session_start(); + + +/* + * Template handling + */ +require_once 'smarty/libs/Smarty.class.php'; +function session_smarty() +{ + if (!isset($GLOBALS['smarty'])) + { + $GLOBALS['smarty'] = new Smarty; + $GLOBALS['smarty']->template_dir = dirname(__FILE__) . '/templates/'; + $GLOBALS['smarty']->compile_dir = dirname(__FILE__) . '/../cache/templates_c'; + } + + return $GLOBALS['smarty']; +} + +function assert_logged_in() +{ + if (empty($_SESSION['authorized'])) + { + $uri = $_SERVER['REQUEST_URI']; + header('Location: /logon?goto=' . urlencode($uri)); + exit(); + } +} + +function assert_request_vars() +{ + foreach(func_get_args() as $a) + { + if (!isset($_REQUEST[$a])) + { + header('HTTP/1.1 400 Bad Request'); + echo 'Bad request.'; + exit; + } + } +} + +function assert_request_vars_all() +{ + foreach($_REQUEST as $row) + { + foreach(func_get_args() as $a) + { + if (!isset($row[$a])) + { + header('HTTP/1.1 400 Bad Request'); + echo 'Bad request.'; + exit; + } + } + } +} + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/example/server/core/templates/inc/footer.tpl b/vendor/oauth-php/example/server/core/templates/inc/footer.tpl new file mode 100644 index 0000000..308b1d0 --- /dev/null +++ b/vendor/oauth-php/example/server/core/templates/inc/footer.tpl @@ -0,0 +1,2 @@ +</body> +</html> diff --git a/vendor/oauth-php/example/server/core/templates/inc/header.tpl b/vendor/oauth-php/example/server/core/templates/inc/header.tpl new file mode 100644 index 0000000..5046f54 --- /dev/null +++ b/vendor/oauth-php/example/server/core/templates/inc/header.tpl @@ -0,0 +1,2 @@ +<html> + <body> diff --git a/vendor/oauth-php/example/server/core/templates/index.tpl b/vendor/oauth-php/example/server/core/templates/index.tpl new file mode 100644 index 0000000..7b06553 --- /dev/null +++ b/vendor/oauth-php/example/server/core/templates/index.tpl @@ -0,0 +1,13 @@ +{include file='inc/header.tpl'} + +<h1>OAuth server</h1> +Go to: + +<ul> + <li><a href="/logon">Logon</a></li> + <li><a href="/register">Register your consumer</a></li> +</ul> + +Afterwards, make an OAuth test request to <strong>http://{$smarty.server.name}/hello</strong> to test your connection.</p> + +{include file='inc/footer.tpl'} diff --git a/vendor/oauth-php/example/server/core/templates/logon.tpl b/vendor/oauth-php/example/server/core/templates/logon.tpl new file mode 100644 index 0000000..5ccd432 --- /dev/null +++ b/vendor/oauth-php/example/server/core/templates/logon.tpl @@ -0,0 +1,21 @@ +{include file='inc/header.tpl'} + +<h1>Login</h1> + +<form method="post"> + <input type="hidden" name="goto" value="{$smarty.request.goto}" /> + + <label for="username">User name</label><br /> + <input type="text" name="username" id="username" /> + + <br /><br /> + + <label for="password">Password</label><br /> + <input type="text" name="password" id="password" /> + + <br /><br /> + + <input type="submit" value="Login" /> +</form> + +{include file='inc/footer.tpl'} diff --git a/vendor/oauth-php/example/server/core/templates/register.tpl b/vendor/oauth-php/example/server/core/templates/register.tpl new file mode 100644 index 0000000..0e28c15 --- /dev/null +++ b/vendor/oauth-php/example/server/core/templates/register.tpl @@ -0,0 +1,41 @@ +{include file='inc/header.tpl'} + +<h1>Register server</h1> + +<p>Register a server which is gonna act as an identity client.</p> + +<form method="post"> + + <fieldset> + <legend>About You</legend> + + <p> + <label for="requester_name">Your name</label><br/> + <input class="text" id="requester_name" name="requester_name" type="text" value="{$consumer.requester_name|default:$smarty.request.requester_name|escape}" /> + </p> + + <p> + <label for="requester_email">Your email address</label><br/> + <input class="text" id="requester_email" name="requester_email" type="text" value="{$consumer.requester_email|default:$smarty.request.requester_email|escape}" /> + </p> + </fieldset> + + <fieldset> + <legend>Location Of Your Application Or Site</legend> + + <p> + <label for="application_uri">URL of your application or site</label><br/> + <input id="application_uri" class="text" name="application_uri" type="text" value="{$consumer.application_uri|default:$smarty.request.application_uri|escape}" /> + </p> + + <p> + <label for="callback_uri">Callback URL</label><br/> + <input id="callback_uri" class="text" name="callback_uri" type="text" value="{$consumer.callback_uri|default:$smarty.request.callback_uri|escape}" /> + </p> + </fieldset> + + <br /> + <input type="submit" value="Register server" /> +</form> + +{include file='inc/footer.tpl'} diff --git a/vendor/oauth-php/example/server/www/hello.php b/vendor/oauth-php/example/server/www/hello.php new file mode 100644 index 0000000..12526a9 --- /dev/null +++ b/vendor/oauth-php/example/server/www/hello.php @@ -0,0 +1,65 @@ +<?php + +/** + * oauth-php: Example OAuth server + * + * An example service, http://hostname/hello. You will only get the + * 'Hello, world!' string back if you have signed your request with + * oauth. + * + * @author Arjan Scherpenisse <arjan@scherpenisse.net> + * + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +require_once '../core/init.php'; + +$authorized = false; +$server = new OAuthServer(); +try +{ + if ($server->verifyIfSigned()) + { + $authorized = true; + } +} +catch (OAuthException2 $e) +{ +} + +if (!$authorized) +{ + header('HTTP/1.1 401 Unauthorized'); + header('Content-Type: text/plain'); + + echo "OAuth Verification Failed: " . $e->getMessage(); + die; +} + +// From here on we are authenticated with OAuth. + +header('Content-type: text/plain'); +echo 'Hello, world!'; + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/example/server/www/index.php b/vendor/oauth-php/example/server/www/index.php new file mode 100644 index 0000000..f5cadbe --- /dev/null +++ b/vendor/oauth-php/example/server/www/index.php @@ -0,0 +1,37 @@ +<?php + +/** + * oauth-php: Example OAuth server + * + * @author Arjan Scherpenisse <arjan@scherpenisse.net> + * + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +require '../core/init.php'; + +$smarty = session_smarty(); +$smarty->display('index.tpl'); + +?> diff --git a/vendor/oauth-php/example/server/www/logon.php b/vendor/oauth-php/example/server/www/logon.php new file mode 100644 index 0000000..5c937b7 --- /dev/null +++ b/vendor/oauth-php/example/server/www/logon.php @@ -0,0 +1,55 @@ +<?php + +/** + * oauth-php: Example OAuth server + * + * Simple logon for consumer registration at this server. + * + * @author Arjan Scherpenisse <arjan@scherpenisse.net> + * + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +require_once '../core/init.php'; + +if (isset($_POST['username']) && isset($_POST['password'])) +{ + if ($_POST['username'] == USERNAME && $_POST['password'] == PASSWORD) + { + $_SESSION['authorized'] = true; + if (!empty($_REQUEST['goto'])) + { + header('Location: ' . $_REQUEST['goto']); + die; + } + + echo "Logon succesfull."; + die; + } +} + +$smarty = session_smarty(); +$smarty->display('logon.tpl'); + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/example/server/www/oauth.php b/vendor/oauth-php/example/server/www/oauth.php new file mode 100644 index 0000000..6dafd61 --- /dev/null +++ b/vendor/oauth-php/example/server/www/oauth.php @@ -0,0 +1,77 @@ +<?php + +/** + * oauth-php: Example OAuth server + * + * This file implements the OAuth server endpoints. The most basic + * implementation of an OAuth server. + * + * Call with: /oauth/request_token, /oauth/authorize, /oauth/access_token + * + * @author Arjan Scherpenisse <arjan@scherpenisse.net> + * + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +require_once '../core/init.php'; + +$server = new OAuthServer(); + +switch($_SERVER['PATH_INFO']) +{ +case '/request_token': + $server->requestToken(); + exit; + +case '/access_token': + $server->accessToken(); + exit; + +case '/authorize': + # logon + + assert_logged_in(); + + try + { + $server->authorizeVerify(); + $server->authorizeFinish(true, 1); + } + catch (OAuthException2 $e) + { + header('HTTP/1.1 400 Bad Request'); + header('Content-Type: text/plain'); + + echo "Failed OAuth Request: " . $e->getMessage(); + } + exit; + + +default: + header('HTTP/1.1 500 Internal Server Error'); + header('Content-Type: text/plain'); + echo "Unknown request"; +} + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/example/server/www/register.php b/vendor/oauth-php/example/server/www/register.php new file mode 100644 index 0000000..0a74297 --- /dev/null +++ b/vendor/oauth-php/example/server/www/register.php @@ -0,0 +1,29 @@ +<?php + +require_once '../core/init.php'; + +assert_logged_in(); + +if ($_SERVER['REQUEST_METHOD'] == 'POST') +{ + try + { + $store = OAuthStore::instance(); + $user_id = 1; // this should not be hardcoded, of course + $key = $store->updateConsumer($_POST, $user_id, true); + + $c = $store->getConsumer($key, $user_id); + echo 'Your consumer key is: <strong>' . $c['consumer_key'] . '</strong><br />'; + echo 'Your consumer secret is: <strong>' . $c['consumer_secret'] . '</strong><br />'; + } + catch (OAuthException2 $e) + { + echo '<strong>Error: ' . $e->getMessage() . '</strong><br />'; + } +} + + +$smarty = session_smarty(); +$smarty->display('register.tpl'); + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/example/server/www/services.xrds.php b/vendor/oauth-php/example/server/www/services.xrds.php new file mode 100644 index 0000000..0f4bbac --- /dev/null +++ b/vendor/oauth-php/example/server/www/services.xrds.php @@ -0,0 +1,71 @@ +<?php + +/** + * oauth-php: Example OAuth server + * + * XRDS discovery for OAuth. This file helps the consumer program to + * discover where the OAuth endpoints for this server are. + * + * @author Arjan Scherpenisse <arjan@scherpenisse.net> + * + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +header('Content-Type: application/xrds+xml'); + +$server = $_SERVER['SERVER_NAME']; + +echo '<?xml version="1.0" encoding="utf-8"?>' . "\n"; + +?> +<XRDS xmlns="xri://$xrds"> + <XRD xmlns:simple="http://xrds-simple.net/core/1.0" xmlns="xri://$XRD*($v*2.0)" xmlns:openid="http://openid.net/xmlns/1.0" version="2.0" xml:id="main"> + <Type>xri://$xrds*simple</Type> + <Service> + <Type>http://oauth.net/discovery/1.0</Type> + <URI>#main</URI> + </Service> + <Service> + <Type>http://oauth.net/core/1.0/endpoint/request</Type> + <Type>http://oauth.net/core/1.0/parameters/auth-header</Type> + <Type>http://oauth.net/core/1.0/parameters/uri-query</Type> + <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type> + <Type>http://oauth.net/core/1.0/signature/PLAINTEXT</Type> + <URI>http://<?php echo $server; ?>/oauth/request_token</URI> + </Service> + <Service> + <Type>http://oauth.net/core/1.0/endpoint/authorize</Type> + <Type>http://oauth.net/core/1.0/parameters/uri-query</Type> + <URI>http://<?php echo $server; ?>/oauth/authorize</URI> + </Service> + <Service> + <Type>http://oauth.net/core/1.0/endpoint/access</Type> + <Type>http://oauth.net/core/1.0/parameters/auth-header</Type> + <Type>http://oauth.net/core/1.0/parameters/uri-query</Type> + <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type> + <Type>http://oauth.net/core/1.0/signature/PLAINTEXT</Type> + <URI>http://<?php echo $server; ?>/oauth/access_token</URI> + </Service> + </XRD> +</XRDS> diff --git a/vendor/oauth-php/library/OAuthDiscovery.php b/vendor/oauth-php/library/OAuthDiscovery.php new file mode 100644 index 0000000..8eee118 --- /dev/null +++ b/vendor/oauth-php/library/OAuthDiscovery.php @@ -0,0 +1,227 @@ +<?php + +/** + * Handle the discovery of OAuth service provider endpoints and static consumer identity. + * + * @version $Id$ + * @author Marc Worrell <marcw@pobox.com> + * @date Sep 4, 2008 5:05:19 PM + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +require_once dirname(__FILE__).'/discovery/xrds_parse.php'; + +require_once dirname(__FILE__).'/OAuthException2.php'; +require_once dirname(__FILE__).'/OAuthRequestLogger.php'; + + +class OAuthDiscovery +{ + /** + * Return a description how we can do a consumer allocation. Prefers static allocation if + * possible. If static allocation is possible + * + * See also: http://oauth.net/discovery/#consumer_identity_types + * + * @param string uri + * @return array provider description + */ + static function discover ( $uri ) + { + // See what kind of consumer allocations are available + $xrds_file = self::discoverXRDS($uri); + if (!empty($xrds_file)) + { + $xrds = xrds_parse($xrds_file); + if (empty($xrds)) + { + throw new OAuthException2('Could not discover OAuth information for '.$uri); + } + } + else + { + throw new OAuthException2('Could not discover XRDS file at '.$uri); + } + + // Fill an OAuthServer record for the uri found + $ps = parse_url($uri); + $host = isset($ps['host']) ? $ps['host'] : 'localhost'; + $server_uri = $ps['scheme'].'://'.$host.'/'; + + $p = array( + 'user_id' => null, + 'consumer_key' => '', + 'consumer_secret' => '', + 'signature_methods' => '', + 'server_uri' => $server_uri, + 'request_token_uri' => '', + 'authorize_uri' => '', + 'access_token_uri' => '' + ); + + + // Consumer identity (out of bounds or static) + if (isset($xrds['consumer_identity'])) + { + // Try to find a static consumer allocation, we like those :) + foreach ($xrds['consumer_identity'] as $ci) + { + if ($ci['method'] == 'static' && !empty($ci['consumer_key'])) + { + $p['consumer_key'] = $ci['consumer_key']; + $p['consumer_secret'] = ''; + } + else if ($ci['method'] == 'oob' && !empty($ci['uri'])) + { + // TODO: Keep this uri somewhere for the user? + $p['consumer_oob_uri'] = $ci['uri']; + } + } + } + + // The token uris + if (isset($xrds['request'][0]['uri'])) + { + $p['request_token_uri'] = $xrds['request'][0]['uri']; + if (!empty($xrds['request'][0]['signature_method'])) + { + $p['signature_methods'] = $xrds['request'][0]['signature_method']; + } + } + if (isset($xrds['authorize'][0]['uri'])) + { + $p['authorize_uri'] = $xrds['authorize'][0]['uri']; + if (!empty($xrds['authorize'][0]['signature_method'])) + { + $p['signature_methods'] = $xrds['authorize'][0]['signature_method']; + } + } + if (isset($xrds['access'][0]['uri'])) + { + $p['access_token_uri'] = $xrds['access'][0]['uri']; + if (!empty($xrds['access'][0]['signature_method'])) + { + $p['signature_methods'] = $xrds['access'][0]['signature_method']; + } + } + return $p; + } + + + /** + * Discover the XRDS file at the uri. This is a bit primitive, you should overrule + * this function so that the XRDS file can be cached for later referral. + * + * @param string uri + * @return string false when no XRDS file found + */ + static protected function discoverXRDS ( $uri, $recur = 0 ) + { + // Bail out when we are following redirects + if ($recur > 10) + { + return false; + } + + $data = self::curl($uri); + + // Check what we got back, could be: + // 1. The XRDS discovery file itself (check content-type) + // 2. The X-XRDS-Location header + + if (is_string($data) && !empty($data)) + { + list($head,$body) = explode("\r\n\r\n", $data); + $body = trim($body); + $m = false; + + // See if we got the XRDS file itself or we have to follow a location header + if ( preg_match('/^Content-Type:\s*application\/xrds+xml/im', $head) + || preg_match('/^<\?xml[^>]*\?>\s*<xrds\s/i', $body) + || preg_match('/^<xrds\s/i', $body) + ) + { + $xrds = $body; + } + else if ( preg_match('/^X-XRDS-Location:\s*([^\r\n]*)/im', $head, $m) + || preg_match('/^Location:\s*([^\r\n]*)/im', $head, $m)) + { + // Recurse to the given location + if ($uri != $m[1]) + { + $xrds = self::discoverXRDS($m[1], $recur+1); + } + else + { + // Referring to the same uri, bail out + $xrds = false; + } + } + else + { + // Not an XRDS file an nowhere else to check + $xrds = false; + } + } + else + { + $xrds = false; + } + return $xrds; + } + + + /** + * Try to fetch an XRDS file at the given location. Sends an accept header preferring the xrds file. + * + * @param string uri + * @return array (head,body), false on an error + */ + static protected function curl ( $uri ) + { + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept: application/xrds+xml, */*;q=0.1')); + curl_setopt($ch, CURLOPT_USERAGENT, 'anyMeta/OAuth 1.0 - (OAuth Discovery $LastChangedRevision: 45 $)'); + curl_setopt($ch, CURLOPT_URL, $uri); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HEADER, true); + curl_setopt($ch, CURLOPT_TIMEOUT, 30); + + $txt = curl_exec($ch); + curl_close($ch); + + // Tell the logger what we requested and what we received back + $data = "GET $uri"; + OAuthRequestLogger::setSent($data, ""); + OAuthRequestLogger::setReceived($txt); + + return $txt; + } +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/library/OAuthException2.php b/vendor/oauth-php/library/OAuthException2.php new file mode 100644 index 0000000..30fc80e --- /dev/null +++ b/vendor/oauth-php/library/OAuthException2.php @@ -0,0 +1,50 @@ +<?php + +/** + * Simple exception wrapper for OAuth + * + * @version $Id: OAuthException2.php 67 2010-01-12 18:42:04Z brunobg@corollarium.com $ + * @author Marc Worrell <marcw@pobox.com> + * @date Nov 29, 2007 5:33:54 PM + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// TODO: something with the HTTP return code matching to the problem + +require_once dirname(__FILE__) . '/OAuthRequestLogger.php'; + +class OAuthException2 extends Exception +{ + function __construct ( $message ) + { + Exception::__construct($message); + OAuthRequestLogger::addNote('OAuthException2: '.$message); + } + +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/library/OAuthRequest.php b/vendor/oauth-php/library/OAuthRequest.php new file mode 100644 index 0000000..5bde0f3 --- /dev/null +++ b/vendor/oauth-php/library/OAuthRequest.php @@ -0,0 +1,854 @@ +<?php + +/** + * Request wrapper class. Prepares a request for consumption by the OAuth routines + * + * @version $Id: OAuthRequest.php 186 2011-02-18 15:46:18Z scherpenisse $ + * @author Marc Worrell <marcw@pobox.com> + * @date Nov 16, 2007 12:20:31 PM + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +require_once dirname(__FILE__) . '/OAuthException2.php'; + +/** + * Object to parse an incoming OAuth request or prepare an outgoing OAuth request + */ +class OAuthRequest +{ + /* the realm for this request */ + protected $realm; + + /* all the parameters, RFC3986 encoded name/value pairs */ + protected $param = array(); + + /* the parsed request uri */ + protected $uri_parts; + + /* the raw request uri */ + protected $uri; + + /* the request headers */ + protected $headers; + + /* the request method */ + protected $method; + + /* the body of the OAuth request */ + protected $body; + + + /** + * Construct from the current request. Useful for checking the signature of a request. + * When not supplied with any parameters this will use the current request. + * + * @param string uri might include parameters + * @param string method GET, PUT, POST etc. + * @param string parameters additional post parameters, urlencoded (RFC1738) + * @param array headers headers for request + * @param string body optional body of the OAuth request (POST or PUT) + */ + function __construct ( $uri = null, $method = null, $parameters = '', $headers = array(), $body = null ) + { + if (is_object($_SERVER)) + { + // Tainted arrays - the normal stuff in anyMeta + if (!$method) { + $method = $_SERVER->REQUEST_METHOD->getRawUnsafe(); + } + if (empty($uri)) { + $uri = $_SERVER->REQUEST_URI->getRawUnsafe(); + $proto = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http'; + if (strpos($uri, "://") === false) { + $uri = sprintf('%s://%s%s', $proto, $_SERVER->HTTP_HOST->getRawUnsafe(), $uri); + } + } + } + else + { + // non anyMeta systems + if (!$method) { + if (isset($_SERVER['REQUEST_METHOD'])) { + $method = $_SERVER['REQUEST_METHOD']; + } + else { + $method = 'GET'; + } + } + $proto = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http'; + if (empty($uri)) { + if (strpos($_SERVER['REQUEST_URI'], "://") !== false) { + $uri = $_SERVER['REQUEST_URI']; + } + else { + $uri = sprintf('%s://%s%s', $proto, $_SERVER['HTTP_HOST'], $_SERVER['REQUEST_URI']); + } + } + } + $headers = OAuthRequestLogger::getAllHeaders(); + $this->method = strtoupper($method); + + // If this is a post then also check the posted variables + if (strcasecmp($method, 'POST') == 0) + { + // TODO: what to do with 'multipart/form-data'? + if ($this->getRequestContentType() == 'multipart/form-data') + { + // Get the posted body (when available) + if (!isset($headers['X-OAuth-Test'])) + { + $parameters .= $this->getRequestBodyOfMultipart(); + } + } + if ($this->getRequestContentType() == 'application/x-www-form-urlencoded') + { + // Get the posted body (when available) + if (!isset($headers['X-OAuth-Test'])) + { + $parameters .= $this->getRequestBody(); + } + } + else + { + $body = $this->getRequestBody(); + } + } + else if (strcasecmp($method, 'PUT') == 0) + { + $body = $this->getRequestBody(); + } + + $this->method = strtoupper($method); + $this->headers = $headers; + // Store the values, prepare for oauth + $this->uri = $uri; + $this->body = $body; + $this->parseUri($parameters); + $this->parseHeaders(); + $this->transcodeParams(); + } + + + /** + * Return the signature base string. + * Note that we can't use rawurlencode due to specified use of RFC3986. + * + * @return string + */ + function signatureBaseString () + { + $sig = array(); + $sig[] = $this->method; + $sig[] = $this->getRequestUrl(); + $sig[] = $this->getNormalizedParams(); + + return implode('&', array_map(array($this, 'urlencode'), $sig)); + } + + + /** + * Calculate the signature of the request, using the method in oauth_signature_method. + * The signature is returned encoded in the form as used in the url. So the base64 and + * urlencoding has been done. + * + * @param string consumer_secret + * @param string token_secret + * @param string token_type + * @exception when not all parts available + * @return string + */ + function calculateSignature ( $consumer_secret, $token_secret, $token_type = 'access' ) + { + $required = array( + 'oauth_consumer_key', + 'oauth_signature_method', + 'oauth_timestamp', + 'oauth_nonce' + ); + + if ($token_type != 'requestToken') + { + $required[] = 'oauth_token'; + } + + foreach ($required as $req) + { + if (!isset($this->param[$req])) + { + throw new OAuthException2('Can\'t sign request, missing parameter "'.$req.'"'); + } + } + + $this->checks(); + + $base = $this->signatureBaseString(); + $signature = $this->calculateDataSignature($base, $consumer_secret, $token_secret, $this->param['oauth_signature_method']); + return $signature; + } + + + /** + * Calculate the signature of a string. + * Uses the signature method from the current parameters. + * + * @param string data + * @param string consumer_secret + * @param string token_secret + * @param string signature_method + * @exception OAuthException2 thrown when the signature method is unknown + * @return string signature + */ + function calculateDataSignature ( $data, $consumer_secret, $token_secret, $signature_method ) + { + if (is_null($data)) + { + $data = ''; + } + + $sig = $this->getSignatureMethod($signature_method); + return $sig->signature($this, $data, $consumer_secret, $token_secret); + } + + + /** + * Select a signature method from the list of available methods. + * We try to check the most secure methods first. + * + * @todo Let the signature method tell us how secure it is + * @param array methods + * @exception OAuthException2 when we don't support any method in the list + * @return string + */ + public function selectSignatureMethod ( $methods ) + { + if (in_array('HMAC-SHA1', $methods)) + { + $method = 'HMAC-SHA1'; + } + else if (in_array('MD5', $methods)) + { + $method = 'MD5'; + } + else + { + $method = false; + foreach ($methods as $m) + { + $m = strtoupper($m); + $m2 = preg_replace('/[^A-Z0-9]/', '_', $m); + if (file_exists(dirname(__FILE__).'/signature_method/OAuthSignatureMethod_'.$m2.'.php')) + { + $method = $m; + break; + } + } + + if (empty($method)) + { + throw new OAuthException2('None of the signing methods is supported.'); + } + } + return $method; + } + + + /** + * Fetch the signature object used for calculating and checking the signature base string + * + * @param string method + * @return OAuthSignatureMethod object + */ + function getSignatureMethod ( $method ) + { + $m = strtoupper($method); + $m = preg_replace('/[^A-Z0-9]/', '_', $m); + $class = 'OAuthSignatureMethod_'.$m; + + if (file_exists(dirname(__FILE__).'/signature_method/'.$class.'.php')) + { + require_once dirname(__FILE__).'/signature_method/'.$class.'.php'; + $sig = new $class(); + } + else + { + throw new OAuthException2('Unsupported signature method "'.$m.'".'); + } + return $sig; + } + + + /** + * Perform some sanity checks. + * + * @exception OAuthException2 thrown when sanity checks failed + */ + function checks () + { + if (isset($this->param['oauth_version'])) + { + $version = $this->urldecode($this->param['oauth_version']); + if ($version != '1.0') + { + throw new OAuthException2('Expected OAuth version 1.0, got "'.$this->param['oauth_version'].'"'); + } + } + } + + + /** + * Return the request method + * + * @return string + */ + function getMethod () + { + return $this->method; + } + + /** + * Return the complete parameter string for the signature check. + * All parameters are correctly urlencoded and sorted on name and value + * + * @return string + */ + function getNormalizedParams () + { + /* + // sort by name, then by value + // (needed when we start allowing multiple values with the same name) + $keys = array_keys($this->param); + $values = array_values($this->param); + array_multisort($keys, SORT_ASC, $values, SORT_ASC); + */ + $params = $this->param; + $normalized = array(); + + ksort($params); + foreach ($params as $key => $value) + { + // all names and values are already urlencoded, exclude the oauth signature + if ($key != 'oauth_signature') + { + if (is_array($value)) + { + $value_sort = $value; + sort($value_sort); + foreach ($value_sort as $v) + { + $normalized[] = $key.'='.$v; + } + } + else + { + $normalized[] = $key.'='.$value; + } + } + } + return implode('&', $normalized); + } + + + /** + * Return the normalised url for signature checks + */ + function getRequestUrl () + { + $url = $this->uri_parts['scheme'] . '://' + . $this->uri_parts['user'] . (!empty($this->uri_parts['pass']) ? ':' : '') + . $this->uri_parts['pass'] . (!empty($this->uri_parts['user']) ? '@' : '') + . $this->uri_parts['host']; + + if ( $this->uri_parts['port'] + && $this->uri_parts['port'] != $this->defaultPortForScheme($this->uri_parts['scheme'])) + { + $url .= ':'.$this->uri_parts['port']; + } + if (!empty($this->uri_parts['path'])) + { + $url .= $this->uri_parts['path']; + } + return $url; + } + + + /** + * Get a parameter, value is always urlencoded + * + * @param string name + * @param boolean urldecode set to true to decode the value upon return + * @return string value false when not found + */ + function getParam ( $name, $urldecode = false ) + { + if (isset($this->param[$name])) + { + $s = $this->param[$name]; + } + else if (isset($this->param[$this->urlencode($name)])) + { + $s = $this->param[$this->urlencode($name)]; + } + else + { + $s = false; + } + if (!empty($s) && $urldecode) + { + if (is_array($s)) + { + $s = array_map(array($this,'urldecode'), $s); + } + else + { + $s = $this->urldecode($s); + } + } + return $s; + } + + /** + * Set a parameter + * + * @param string name + * @param string value + * @param boolean encoded set to true when the values are already encoded + */ + function setParam ( $name, $value, $encoded = false ) + { + if (!$encoded) + { + $name_encoded = $this->urlencode($name); + if (is_array($value)) + { + foreach ($value as $v) + { + $this->param[$name_encoded][] = $this->urlencode($v); + } + } + else + { + $this->param[$name_encoded] = $this->urlencode($value); + } + } + else + { + $this->param[$name] = $value; + } + } + + + /** + * Re-encode all parameters so that they are encoded using RFC3986. + * Updates the $this->param attribute. + */ + protected function transcodeParams () + { + $params = $this->param; + $this->param = array(); + + foreach ($params as $name=>$value) + { + if (is_array($value)) + { + $this->param[$this->urltranscode($name)] = array_map(array($this,'urltranscode'), $value); + } + else + { + $this->param[$this->urltranscode($name)] = $this->urltranscode($value); + } + } + } + + + + /** + * Return the body of the OAuth request. + * + * @return string null when no body + */ + function getBody () + { + return $this->body; + } + + + /** + * Return the body of the OAuth request. + * + * @return string null when no body + */ + function setBody ( $body ) + { + $this->body = $body; + } + + + /** + * Parse the uri into its parts. Fill in the missing parts. + * + * @param string $parameters optional extra parameters (from eg the http post) + */ + protected function parseUri ( $parameters ) + { + $ps = @parse_url($this->uri); + + // Get the current/requested method + $ps['scheme'] = strtolower($ps['scheme']); + + // Get the current/requested host + if (function_exists('mb_strtolower')) + $ps['host'] = mb_strtolower($ps['host']); + else + $ps['host'] = strtolower($ps['host']); + + if (!preg_match('/^[a-z0-9\.\-]+$/', $ps['host'])) + { + throw new OAuthException2('Unsupported characters in host name'); + } + + // Get the port we are talking on + if (empty($ps['port'])) + { + $ps['port'] = $this->defaultPortForScheme($ps['scheme']); + } + + if (empty($ps['user'])) + { + $ps['user'] = ''; + } + if (empty($ps['pass'])) + { + $ps['pass'] = ''; + } + if (empty($ps['path'])) + { + $ps['path'] = '/'; + } + if (empty($ps['query'])) + { + $ps['query'] = ''; + } + if (empty($ps['fragment'])) + { + $ps['fragment'] = ''; + } + + // Now all is complete - parse all parameters + foreach (array($ps['query'], $parameters) as $params) + { + if (strlen($params) > 0) + { + $params = explode('&', $params); + foreach ($params as $p) + { + @list($name, $value) = explode('=', $p, 2); + if (!strlen($name)) + { + continue; + } + + if (array_key_exists($name, $this->param)) + { + if (is_array($this->param[$name])) + $this->param[$name][] = $value; + else + $this->param[$name] = array($this->param[$name], $value); + } + else + { + $this->param[$name] = $value; + } + } + } + } + $this->uri_parts = $ps; + } + + + /** + * Return the default port for a scheme + * + * @param string scheme + * @return int + */ + protected function defaultPortForScheme ( $scheme ) + { + switch ($scheme) + { + case 'http': return 80; + case 'https': return 443; + default: + throw new OAuthException2('Unsupported scheme type, expected http or https, got "'.$scheme.'"'); + break; + } + } + + + /** + * Encode a string according to the RFC3986 + * + * @param string s + * @return string + */ + function urlencode ( $s ) + { + if ($s === false) + { + return $s; + } + else + { + return str_replace('%7E', '~', rawurlencode($s)); + } + } + + /** + * Decode a string according to RFC3986. + * Also correctly decodes RFC1738 urls. + * + * @param string s + * @return string + */ + function urldecode ( $s ) + { + if ($s === false) + { + return $s; + } + else + { + return rawurldecode($s); + } + } + + /** + * urltranscode - make sure that a value is encoded using RFC3986. + * We use a basic urldecode() function so that any use of '+' as the + * encoding of the space character is correctly handled. + * + * @param string s + * @return string + */ + function urltranscode ( $s ) + { + if ($s === false) + { + return $s; + } + else + { + //return $this->urlencode(rawurldecode($s)); + return $this->urlencode(urldecode($s)); + } + } + + + /** + * Parse the oauth parameters from the request headers + * Looks for something like: + * + * Authorization: OAuth realm="http://photos.example.net/authorize", + * oauth_consumer_key="dpf43f3p2l4k3l03", + * oauth_token="nnch734d00sl2jdk", + * oauth_signature_method="HMAC-SHA1", + * oauth_signature="tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D", + * oauth_timestamp="1191242096", + * oauth_nonce="kllo9940pd9333jh", + * oauth_version="1.0" + */ + private function parseHeaders () + { +/* + $this->headers['Authorization'] = 'OAuth realm="http://photos.example.net/authorize", + oauth_consumer_key="dpf43f3p2l4k3l03", + oauth_token="nnch734d00sl2jdk", + oauth_signature_method="HMAC-SHA1", + oauth_signature="tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D", + oauth_timestamp="1191242096", + oauth_nonce="kllo9940pd9333jh", + oauth_version="1.0"'; +*/ + if (isset($this->headers['Authorization'])) + { + $auth = trim($this->headers['Authorization']); + if (strncasecmp($auth, 'OAuth', 4) == 0) + { + $vs = explode(',', substr($auth, 6)); + foreach ($vs as $v) + { + if (strpos($v, '=')) + { + $v = trim($v); + list($name,$value) = explode('=', $v, 2); + if (!empty($value) && $value{0} == '"' && substr($value, -1) == '"') + { + $value = substr(substr($value, 1), 0, -1); + } + + if (strcasecmp($name, 'realm') == 0) + { + $this->realm = $value; + } + else + { + $this->param[$name] = $value; + } + } + } + } + } + } + + + /** + * Fetch the content type of the current request + * + * @return string + */ + private function getRequestContentType () + { + $content_type = 'application/octet-stream'; + if (!empty($_SERVER) && array_key_exists('CONTENT_TYPE', $_SERVER)) + { + list($content_type) = explode(';', $_SERVER['CONTENT_TYPE']); + } + return trim($content_type); + } + + + /** + * Get the body of a POST or PUT. + * + * Used for fetching the post parameters and to calculate the body signature. + * + * @return string null when no body present (or wrong content type for body) + */ + private function getRequestBody () + { + $body = null; + if ($this->method == 'POST' || $this->method == 'PUT') + { + $body = ''; + $fh = @fopen('php://input', 'r'); + if ($fh) + { + while (!feof($fh)) + { + $s = fread($fh, 1024); + if (is_string($s)) + { + $body .= $s; + } + } + fclose($fh); + } + } + return $body; + } + + /** + * Get the body of a POST with multipart/form-data by Edison tsai on 16:52 2010/09/16 + * + * Used for fetching the post parameters and to calculate the body signature. + * + * @return string null when no body present (or wrong content type for body) + */ + private function getRequestBodyOfMultipart() + { + $body = null; + if ($this->method == 'POST') + { + $body = ''; + if (is_array($_POST) && count($_POST) > 1) + { + foreach ($_POST AS $k => $v) { + $body .= $k . '=' . $this->urlencode($v) . '&'; + } #end foreach + if(substr($body,-1) == '&') + { + $body = substr($body, 0, strlen($body)-1); + } #end if + } #end if + } #end if + + return $body; + } + + + /** + * Simple function to perform a redirect (GET). + * Redirects the User-Agent, does not return. + * + * @param string uri + * @param array params parameters, urlencoded + * @param bool skip protocol check + * @exception OAuthException2 when redirect uri is illegal + */ + public function redirect ( $uri, $params, $skip_protocol_check = false ) + { + if (!empty($params)) + { + $q = array(); + foreach ($params as $name=>$value) + { + $q[] = $name.'='.$value; + } + $q_s = implode('&', $q); + + if (strpos($uri, '?')) + { + $uri .= '&'.$q_s; + } + else + { + $uri .= '?'.$q_s; + } + } + + // simple security - multiline location headers can inject all kinds of extras + $uri = preg_replace('/\s/', '%20', $uri); + + if (!$skip_protocol_check) { + if (strncasecmp($uri, 'http://', 7) && strncasecmp($uri, 'https://', 8)) + { + if (strpos($uri, '://')) + { + throw new OAuthException2('Illegal protocol in redirect uri '.$uri); + } + $uri = 'http://'.$uri; + } + } + + header('HTTP/1.1 302 Found'); + header('Location: '.$uri); + echo ''; + exit(); + } +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?> diff --git a/vendor/oauth-php/library/OAuthRequestLogger.php b/vendor/oauth-php/library/OAuthRequestLogger.php new file mode 100644 index 0000000..24cd8ba --- /dev/null +++ b/vendor/oauth-php/library/OAuthRequestLogger.php @@ -0,0 +1,314 @@ +<?php + +/** + * Log OAuth requests + * + * @version $Id: OAuthRequestLogger.php 185 2011-02-08 16:11:20Z brunobg@corollarium.com $ + * @author Marc Worrell <marcw@pobox.com> + * @date Dec 7, 2007 12:22:43 PM + * + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +class OAuthRequestLogger +{ + static private $logging = 0; + static private $enable_logging = null; + static private $store_log = null; + static private $note = ''; + static private $user_id = null; + static private $request_object = null; + static private $sent = null; + static private $received = null; + static private $log = array(); + + /** + * Start any logging, checks the system configuration if logging is needed. + * + * @param OAuthRequest $request_object + */ + static function start ( $request_object = null ) + { + if (defined('OAUTH_LOG_REQUEST')) + { + if (is_null(OAuthRequestLogger::$enable_logging)) + { + OAuthRequestLogger::$enable_logging = true; + } + if (is_null(OAuthRequestLogger::$store_log)) + { + OAuthRequestLogger::$store_log = true; + } + } + + if (OAuthRequestLogger::$enable_logging && !OAuthRequestLogger::$logging) + { + OAuthRequestLogger::$logging = true; + OAuthRequestLogger::$request_object = $request_object; + ob_start(); + + // Make sure we flush our log entry when we stop the request (eg on an exception) + register_shutdown_function(array('OAuthRequestLogger','flush')); + } + } + + + /** + * Force logging, needed for performing test connects independent from the debugging setting. + * + * @param boolean store_log (optional) true to store the log in the db + */ + static function enableLogging ( $store_log = null ) + { + OAuthRequestLogger::$enable_logging = true; + if (!is_null($store_log)) + { + OAuthRequestLogger::$store_log = $store_log; + } + } + + + /** + * Logs the request to the database, sends any cached output. + * Also called on shutdown, to make sure we always log the request being handled. + */ + static function flush () + { + if (OAuthRequestLogger::$logging) + { + OAuthRequestLogger::$logging = false; + + if (is_null(OAuthRequestLogger::$sent)) + { + // What has been sent to the user-agent? + $data = ob_get_contents(); + if (strlen($data) > 0) + { + ob_end_flush(); + } + elseif (ob_get_level()) + { + ob_end_clean(); + } + $hs = headers_list(); + $sent = implode("\n", $hs) . "\n\n" . $data; + } + else + { + // The request we sent + $sent = OAuthRequestLogger::$sent; + } + + if (is_null(OAuthRequestLogger::$received)) + { + // Build the request we received + $hs0 = self::getAllHeaders(); + $hs = array(); + foreach ($hs0 as $h => $v) + { + $hs[] = "$h: $v"; + } + + $data = ''; + $fh = @fopen('php://input', 'r'); + if ($fh) + { + while (!feof($fh)) + { + $s = fread($fh, 1024); + if (is_string($s)) + { + $data .= $s; + } + } + fclose($fh); + } + $received = implode("\n", $hs) . "\n\n" . $data; + } + else + { + // The answer we received + $received = OAuthRequestLogger::$received; + } + + // The request base string + if (OAuthRequestLogger::$request_object) + { + $base_string = OAuthRequestLogger::$request_object->signatureBaseString(); + } + else + { + $base_string = ''; + } + + // Figure out to what keys we want to log this request + $keys = array(); + if (OAuthRequestLogger::$request_object) + { + $consumer_key = OAuthRequestLogger::$request_object->getParam('oauth_consumer_key', true); + $token = OAuthRequestLogger::$request_object->getParam('oauth_token', true); + + switch (get_class(OAuthRequestLogger::$request_object)) + { + // tokens are access/request tokens by a consumer + case 'OAuthServer': + case 'OAuthRequestVerifier': + $keys['ocr_consumer_key'] = $consumer_key; + $keys['oct_token'] = $token; + break; + + // tokens are access/request tokens to a server + case 'OAuthRequester': + case 'OAuthRequestSigner': + $keys['osr_consumer_key'] = $consumer_key; + $keys['ost_token'] = $token; + break; + } + } + + // Log the request + if (OAuthRequestLogger::$store_log) + { + $store = OAuthStore::instance(); + $store->addLog($keys, $received, $sent, $base_string, OAuthRequestLogger::$note, OAuthRequestLogger::$user_id); + } + + OAuthRequestLogger::$log[] = array( + 'keys' => $keys, + 'received' => $received, + 'sent' => $sent, + 'base_string' => $base_string, + 'note' => OAuthRequestLogger::$note + ); + } + } + + + /** + * Add a note, used by the OAuthException2 to log all exceptions. + * + * @param string note + */ + static function addNote ( $note ) + { + OAuthRequestLogger::$note .= $note . "\n\n"; + } + + /** + * Set the OAuth request object being used + * + * @param OAuthRequest request_object + */ + static function setRequestObject ( $request_object ) + { + OAuthRequestLogger::$request_object = $request_object; + } + + + /** + * Set the relevant user (defaults to the current user) + * + * @param int user_id + */ + static function setUser ( $user_id ) + { + OAuthRequestLogger::$user_id = $user_id; + } + + + /** + * Set the request we sent + * + * @param string request + */ + static function setSent ( $request ) + { + OAuthRequestLogger::$sent = $request; + } + + /** + * Set the reply we received + * + * @param string request + */ + static function setReceived ( $reply ) + { + OAuthRequestLogger::$received = $reply; + } + + + /** + * Get the the log till now + * + * @return array + */ + static function getLog () + { + return OAuthRequestLogger::$log; + } + + + /** + * helper to try to sort out headers for people who aren't running apache, + * or people who are running PHP as FastCGI. + * + * @return array of request headers as associative array. + */ + public static function getAllHeaders() { + $retarr = array(); + $headers = array(); + + if (function_exists('apache_request_headers')) { + $headers = apache_request_headers(); + } else { + $headers = array_merge($_ENV, $_SERVER); + + foreach ($headers as $key => $val) { + //we need this header + if (strpos(strtolower($key), 'content-type') !== FALSE) + continue; + if (strtoupper(substr($key, 0, 5)) != "HTTP_") + unset($headers[$key]); + } + } + + //Normalize this array to Cased-Like-This structure. + foreach ($headers AS $key => $value) { + $key = preg_replace('/^HTTP_/i', '', $key); + $key = str_replace( + " ", + "-", + ucwords(strtolower(str_replace(array("-", "_"), " ", $key))) + ); + $retarr[$key] = $value; + } + ksort($retarr); + + return $retarr; + } +} + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/library/OAuthRequestSigner.php b/vendor/oauth-php/library/OAuthRequestSigner.php new file mode 100644 index 0000000..15c0fd8 --- /dev/null +++ b/vendor/oauth-php/library/OAuthRequestSigner.php @@ -0,0 +1,215 @@ +<?php + +/** + * Sign requests before performing the request. + * + * @version $Id: OAuthRequestSigner.php 174 2010-11-24 15:15:41Z brunobg@corollarium.com $ + * @author Marc Worrell <marcw@pobox.com> + * @date Nov 16, 2007 4:02:49 PM + * + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +require_once dirname(__FILE__) . '/OAuthStore.php'; +require_once dirname(__FILE__) . '/OAuthRequest.php'; + + +class OAuthRequestSigner extends OAuthRequest +{ + protected $request; + protected $store; + protected $usr_id = 0; + private $signed = false; + + + /** + * Construct the request to be signed. Parses or appends the parameters in the params url. + * When you supply an params array, then the params should not be urlencoded. + * When you supply a string, then it is assumed it is of the type application/x-www-form-urlencoded + * + * @param string request url + * @param string method PUT, GET, POST etc. + * @param mixed params string (for urlencoded data, or array with name/value pairs) + * @param string body optional body for PUT and/or POST requests + */ + function __construct ( $request, $method = null, $params = null, $body = null ) + { + $this->store = OAuthStore::instance(); + + if (is_string($params)) + { + parent::__construct($request, $method, $params); + } + else + { + parent::__construct($request, $method); + if (is_array($params)) + { + foreach ($params as $name => $value) + { + $this->setParam($name, $value); + } + } + } + + // With put/ post we might have a body (not for application/x-www-form-urlencoded requests) + if (strcasecmp($method, 'PUT') == 0 || strcasecmp($method, 'POST') == 0) + { + $this->setBody($body); + } + } + + + /** + * Reset the 'signed' flag, so that any changes in the parameters force a recalculation + * of the signature. + */ + function setUnsigned () + { + $this->signed = false; + } + + + /** + * Sign our message in the way the server understands. + * Set the needed oauth_xxxx parameters. + * + * @param int usr_id (optional) user that wants to sign this request + * @param array secrets secrets used for signing, when empty then secrets will be fetched from the token registry + * @param string name name of the token to be used for signing + * @exception OAuthException2 when there is no oauth relation with the server + * @exception OAuthException2 when we don't support the signing methods of the server + */ + function sign ( $usr_id = 0, $secrets = null, $name = '', $token_type = null) + { + $url = $this->getRequestUrl(); + if (empty($secrets)) + { + // get the access tokens for the site (on an user by user basis) + $secrets = $this->store->getSecretsForSignature($url, $usr_id, $name); + } + if (empty($secrets)) + { + throw new OAuthException2('No OAuth relation with the server for at "'.$url.'"'); + } + + $signature_method = $this->selectSignatureMethod($secrets['signature_methods']); + + $token = isset($secrets['token']) ? $secrets['token'] : ''; + $token_secret = isset($secrets['token_secret']) ? $secrets['token_secret'] : ''; + + if (!$token) { + $token = $this->getParam('oauth_token'); + } + + $this->setParam('oauth_signature_method',$signature_method); + $this->setParam('oauth_signature', ''); + $this->setParam('oauth_nonce', !empty($secrets['nonce']) ? $secrets['nonce'] : uniqid('')); + $this->setParam('oauth_timestamp', !empty($secrets['timestamp']) ? $secrets['timestamp'] : time()); + if ($token_type != 'requestToken') + $this->setParam('oauth_token', $token); + $this->setParam('oauth_consumer_key', $secrets['consumer_key']); + $this->setParam('oauth_version', '1.0'); + + $body = $this->getBody(); + if (!is_null($body)) + { + // We also need to sign the body, use the default signature method + $body_signature = $this->calculateDataSignature($body, $secrets['consumer_secret'], $token_secret, $signature_method); + $this->setParam('xoauth_body_signature', $body_signature, true); + } + + $signature = $this->calculateSignature($secrets['consumer_secret'], $token_secret, $token_type); + $this->setParam('oauth_signature', $signature, true); + // $this->setParam('oauth_signature', urldecode($signature), true); + + $this->signed = true; + $this->usr_id = $usr_id; + } + + + /** + * Builds the Authorization header for the request. + * Adds all oauth_ and xoauth_ parameters to the Authorization header. + * + * @return string + */ + function getAuthorizationHeader () + { + if (!$this->signed) + { + $this->sign($this->usr_id); + } + $h = array(); + $h[] = 'Authorization: OAuth realm=""'; + foreach ($this->param as $name => $value) + { + if (strncmp($name, 'oauth_', 6) == 0 || strncmp($name, 'xoauth_', 7) == 0) + { + $h[] = $name.'="'.$value.'"'; + } + } + $hs = implode(', ', $h); + return $hs; + } + + + /** + * Builds the application/x-www-form-urlencoded parameter string. Can be appended as + * the query part to a GET or inside the request body for a POST. + * + * @param boolean oauth_as_header (optional) set to false to include oauth parameters + * @return string + */ + function getQueryString ( $oauth_as_header = true ) + { + $parms = array(); + foreach ($this->param as $name => $value) + { + if ( !$oauth_as_header + || (strncmp($name, 'oauth_', 6) != 0 && strncmp($name, 'xoauth_', 7) != 0)) + { + if (is_array($value)) + { + foreach ($value as $v) + { + $parms[] = $name.'='.$v; + } + } + else + { + $parms[] = $name.'='.$value; + } + } + } + return implode('&', $parms); + } + +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/library/OAuthRequestVerifier.php b/vendor/oauth-php/library/OAuthRequestVerifier.php new file mode 100644 index 0000000..a5def75 --- /dev/null +++ b/vendor/oauth-php/library/OAuthRequestVerifier.php @@ -0,0 +1,306 @@ +<?php + +/** + * Verify the current request. Checks if signed and if the signature is correct. + * When correct then also figures out on behalf of which user this request is being made. + * + * @version $Id: OAuthRequestVerifier.php 155 2010-09-10 18:38:33Z brunobg@corollarium.com $ + * @author Marc Worrell <marcw@pobox.com> + * @date Nov 16, 2007 4:35:03 PM + * + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +require_once dirname(__FILE__) . '/OAuthStore.php'; +require_once dirname(__FILE__) . '/OAuthRequest.php'; + + +class OAuthRequestVerifier extends OAuthRequest +{ + private $request; + private $store; + private $accepted_signatures = null; + + /** + * Construct the request to be verified + * + * @param string request + * @param string method + * @param array params The request parameters + */ + function __construct ( $uri = null, $method = null, $params = null ) + { + if ($params) { + $encodedParams = array(); + foreach ($params as $key => $value) { + if (preg_match("/^oauth_/", $key)) { + continue; + } + $encodedParams[rawurlencode($key)] = rawurlencode($value); + } + $this->param = array_merge($this->param, $encodedParams); + } + + $this->store = OAuthStore::instance(); + parent::__construct($uri, $method); + + OAuthRequestLogger::start($this); + } + + + /** + * See if the current request is signed with OAuth + * + * @return boolean + */ + static public function requestIsSigned () + { + if (isset($_REQUEST['oauth_signature'])) + { + $signed = true; + } + else + { + $hs = OAuthRequestLogger::getAllHeaders(); + if (isset($hs['Authorization']) && strpos($hs['Authorization'], 'oauth_signature') !== false) + { + $signed = true; + } + else + { + $signed = false; + } + } + return $signed; + } + + + /** + * Verify the request if it seemed to be signed. + * + * @param string token_type the kind of token needed, defaults to 'access' + * @exception OAuthException2 thrown when the request did not verify + * @return boolean true when signed, false when not signed + */ + public function verifyIfSigned ( $token_type = 'access' ) + { + if ($this->getParam('oauth_consumer_key')) + { + OAuthRequestLogger::start($this); + $this->verify($token_type); + $signed = true; + OAuthRequestLogger::flush(); + } + else + { + $signed = false; + } + return $signed; + } + + + + /** + * Verify the request + * + * @param string token_type the kind of token needed, defaults to 'access' (false, 'access', 'request') + * @exception OAuthException2 thrown when the request did not verify + * @return int user_id associated with token (false when no user associated) + */ + public function verify ( $token_type = 'access' ) + { + $retval = $this->verifyExtended($token_type); + return $retval['user_id']; + } + + + /** + * Verify the request + * + * @param string token_type the kind of token needed, defaults to 'access' (false, 'access', 'request') + * @exception OAuthException2 thrown when the request did not verify + * @return array ('user_id' => associated with token (false when no user associated), + * 'consumer_key' => the associated consumer_key) + * + */ + public function verifyExtended ( $token_type = 'access' ) + { + $consumer_key = $this->getParam('oauth_consumer_key'); + $token = $this->getParam('oauth_token'); + $user_id = false; + $secrets = array(); + + if ($consumer_key && ($token_type === false || $token)) + { + $secrets = $this->store->getSecretsForVerify( $this->urldecode($consumer_key), + $this->urldecode($token), + $token_type); + + $this->store->checkServerNonce( $this->urldecode($consumer_key), + $this->urldecode($token), + $this->getParam('oauth_timestamp', true), + $this->getParam('oauth_nonce', true)); + + $oauth_sig = $this->getParam('oauth_signature'); + if (empty($oauth_sig)) + { + throw new OAuthException2('Verification of signature failed (no oauth_signature in request).'); + } + + try + { + $this->verifySignature($secrets['consumer_secret'], $secrets['token_secret'], $token_type); + } + catch (OAuthException2 $e) + { + throw new OAuthException2('Verification of signature failed (signature base string was "'.$this->signatureBaseString().'").' + . " with " . print_r(array($secrets['consumer_secret'], $secrets['token_secret'], $token_type), true)); + } + + // Check the optional body signature + if ($this->getParam('xoauth_body_signature')) + { + $method = $this->getParam('xoauth_body_signature_method'); + if (empty($method)) + { + $method = $this->getParam('oauth_signature_method'); + } + + try + { + $this->verifyDataSignature($this->getBody(), $secrets['consumer_secret'], $secrets['token_secret'], $method, $this->getParam('xoauth_body_signature')); + } + catch (OAuthException2 $e) + { + throw new OAuthException2('Verification of body signature failed.'); + } + } + + // All ok - fetch the user associated with this request + if (isset($secrets['user_id'])) + { + $user_id = $secrets['user_id']; + } + + // Check if the consumer wants us to reset the ttl of this token + $ttl = $this->getParam('xoauth_token_ttl', true); + if (is_numeric($ttl)) + { + $this->store->setConsumerAccessTokenTtl($this->urldecode($token), $ttl); + } + } + else + { + throw new OAuthException2('Can\'t verify request, missing oauth_consumer_key or oauth_token'); + } + return array('user_id' => $user_id, 'consumer_key' => $consumer_key, 'osr_id' => $secrets['osr_id']); + } + + + + /** + * Verify the signature of the request, using the method in oauth_signature_method. + * The signature is returned encoded in the form as used in the url. So the base64 and + * urlencoding has been done. + * + * @param string consumer_secret + * @param string token_secret + * @exception OAuthException2 thrown when the signature method is unknown + * @exception OAuthException2 when not all parts available + * @exception OAuthException2 when signature does not match + */ + public function verifySignature ( $consumer_secret, $token_secret, $token_type = 'access' ) + { + $required = array( + 'oauth_consumer_key', + 'oauth_signature_method', + 'oauth_timestamp', + 'oauth_nonce', + 'oauth_signature' + ); + + if ($token_type !== false) + { + $required[] = 'oauth_token'; + } + + foreach ($required as $req) + { + if (!isset($this->param[$req])) + { + throw new OAuthException2('Can\'t verify request signature, missing parameter "'.$req.'"'); + } + } + + $this->checks(); + + $base = $this->signatureBaseString(); + $this->verifyDataSignature($base, $consumer_secret, $token_secret, $this->param['oauth_signature_method'], $this->param['oauth_signature']); + } + + + + /** + * Verify the signature of a string. + * + * @param string data + * @param string consumer_secret + * @param string token_secret + * @param string signature_method + * @param string signature + * @exception OAuthException2 thrown when the signature method is unknown + * @exception OAuthException2 when signature does not match + */ + public function verifyDataSignature ( $data, $consumer_secret, $token_secret, $signature_method, $signature ) + { + if (is_null($data)) + { + $data = ''; + } + + $sig = $this->getSignatureMethod($signature_method); + if (!$sig->verify($this, $data, $consumer_secret, $token_secret, $signature)) + { + throw new OAuthException2('Signature verification failed ('.$signature_method.')'); + } + } + + /** + * + * @param array $accepted The array of accepted signature methods, or if null is passed + * all supported methods are accepted and there is no filtering. + * + */ + public function setAcceptedSignatureMethods($accepted = null) { + if (is_array($accepted)) + $this->accepted_signatures = $accepted; + else if ($accepted == null) + $this->accepted_signatures = null; + } +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/library/OAuthRequester.php b/vendor/oauth-php/library/OAuthRequester.php new file mode 100644 index 0000000..dde9a99 --- /dev/null +++ b/vendor/oauth-php/library/OAuthRequester.php @@ -0,0 +1,543 @@ +<?php + +/** + * Perform a signed OAuth request with a GET, POST, PUT or DELETE operation. + * + * @version $Id: OAuthRequester.php 191 2011-03-23 17:50:55Z scherpenisse $ + * @author Marc Worrell <marcw@pobox.com> + * @date Nov 20, 2007 1:41:38 PM + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +require_once dirname(__FILE__) . '/OAuthRequestSigner.php'; +require_once dirname(__FILE__) . '/body/OAuthBodyContentDisposition.php'; + + +class OAuthRequester extends OAuthRequestSigner +{ + protected $files; + + /** + * Construct a new request signer. Perform the request with the doRequest() method below. + * + * A request can have either one file or a body, not both. + * + * The files array consists of arrays: + * - file the filename/path containing the data for the POST/PUT + * - data data for the file, omit when you have a file + * - mime content-type of the file + * - filename filename for content disposition header + * + * When OAuth (and PHP) can support multipart/form-data then we can handle more than one file. + * For now max one file, with all the params encoded in the query string. + * + * @param string request + * @param string method http method. GET, PUT, POST etc. Defaults to 'GET'. + * @param array params name=>value array with request parameters + * @param string body optional body to send + * @param array files optional files to send (max 1 till OAuth support multipart/form-data posts) + */ + function __construct ( $request, $method = 'GET', $params = null, $body = null, $files = null ) + { + parent::__construct($request, $method, $params, $body); + + // When there are files, then we can construct a POST with a single file + if (!empty($files)) + { + $empty = true; + foreach ($files as $f) + { + $empty = $empty && empty($f['file']) && !isset($f['data']); + } + + if (!$empty) + { + if (!is_null($body)) + { + throw new OAuthException2('When sending files, you can\'t send a body as well.'); + } + $this->files = $files; + } + } + } + + + /** + * Perform the request, returns the response code, headers and body. + * + * @param int usr_id optional user id for which we make the request + * @param array curl_options optional extra options for curl request + * @param array options options like + * - name Named tokens, unique per user/consumer key + * - token_ttl Time to live + * - server_uri The server uri + * - boolean oauth_as_header set to false to include oauth parameters in query string. Default true (includes on headers) + * @exception OAuthException2 when authentication not accepted + * @exception OAuthException2 when signing was not possible + * @return array (code=>int, headers=>array(), body=>string) + */ + function doRequest ( $usr_id = 0, $curl_options = array(), $options = array() ) + { + $name = isset($options['name']) ? $options['name'] : ''; + if (isset($options['token_ttl'])) + { + $this->setParam('xoauth_token_ttl', intval($options['token_ttl'])); + } + + if (!empty($this->files)) + { + // At the moment OAuth does not support multipart/form-data, so try to encode + // the supplied file (or data) as the request body and add a content-disposition header. + list($extra_headers, $body) = OAuthBodyContentDisposition::encodeBody($this->files); + $this->setBody($body); + $curl_options = $this->prepareCurlOptions($curl_options, $extra_headers); + } + $this->sign($usr_id, null, $name); + $text = $this->curl_raw($curl_options, (isset($options['oauth_as_header']) ? $options['oauth_as_header'] : true)); + $result = $this->curl_parse($text); + if ($result['code'] >= 400) + { + throw new OAuthException2('Request failed with code ' . $result['code'] . ': ' . $result['body']); + } + + // Record the token time to live for this server access token, immediate delete iff ttl <= 0 + // Only done on a succesful request. + $token_ttl = $this->getParam('xoauth_token_ttl', false); + if (is_numeric($token_ttl)) + { + $this->store->setServerTokenTtl($this->getParam('oauth_consumer_key',true), $this->getParam('oauth_token',true), + $token_ttl, (isset($options['server_uri']) ? $options['server_uri'] : NULL)); + } + + return $result; + } + + + /** + * Request a request token from the site belonging to consumer_key + * + * @param string consumer_key + * @param int usr_id + * @param array params (optional) extra arguments for when requesting the request token + * @param string method (optional) change the method of the request, defaults to POST (as it should be) + * @param array options (optional) options: + * - name Named tokens, unique per user/consumer key + * - token_ttl Time to live + * - server_uri The server uri + * - boolean oauth_as_header set to false to include oauth parameters in query string. Default true (includes on headers) + * @param array curl_options optional extra options for curl request + * @exception OAuthException2 when no key could be fetched + * @exception OAuthException2 when no server with consumer_key registered + * @return array (authorize_uri, token) + */ + static function requestRequestToken ( $consumer_key, $usr_id, $params = null, $method = 'POST', $options = array(), $curl_options = array() ) + { + OAuthRequestLogger::start(); + + if (isset($options['token_ttl']) && is_numeric($options['token_ttl'])) + { + $params['xoauth_token_ttl'] = intval($options['token_ttl']); + } + + $store = OAuthStore::instance(); + $r = $store->getServer($consumer_key, $usr_id); + $uri = $r['request_token_uri']; + + $oauth = new OAuthRequester($uri, $method, $params); + $oauth->sign($usr_id, $r, '', 'requestToken'); + $text = $oauth->curl_raw($curl_options, (isset($options['oauth_as_header']) ? $options['oauth_as_header'] : true)); + + if (empty($text)) + { + throw new OAuthException2('No answer from the server "'.$uri.'" while requesting a request token'); + } + $data = $oauth->curl_parse($text); + if ($data['code'] != 200) + { + throw new OAuthException2('Unexpected result from the server "'.$uri.'" ('.$data['code'].') while requesting a request token:' . $data['body']); + } + $token = array(); + $params = explode('&', $data['body']); + foreach ($params as $p) + { + @list($name, $value) = explode('=', $p, 2); + $token[$name] = $oauth->urldecode($value); + } + + if (!empty($token['oauth_token']) && !empty($token['oauth_token_secret'])) + { + $opts = array(); + if (isset($options['name'])) + { + $opts['name'] = $options['name']; + } + if (isset($token['xoauth_token_ttl'])) + { + $opts['token_ttl'] = $token['xoauth_token_ttl']; + } + if (isset($options['server_uri'])) + { + $opts['server_uri'] = $options['server_uri']; + } + $store->addServerToken($consumer_key, 'request', $token['oauth_token'], $token['oauth_token_secret'], $usr_id, $opts); + } + else + { + throw new OAuthException2('The server "'.$uri.'" did not return the oauth_token or the oauth_token_secret'); + } + + OAuthRequestLogger::flush(); + + // Now we can direct a browser to the authorize_uri + return array( + 'authorize_uri' => $r['authorize_uri'], + 'token' => $token['oauth_token'] + ); + } + + + /** + * Request an access token from the site belonging to consumer_key. + * Before this we got an request token, now we want to exchange it for + * an access token. + * + * @param string consumer_key + * @param string token + * @param int usr_id user requesting the access token + * @param string method (optional) change the method of the request, defaults to POST (as it should be) + * @param array options (optional) options: + * - name Named tokens, unique per user/consumer key + * - token_ttl Time to live + * - server_uri The server uri + * - boolean oauth_as_header set to false to include oauth parameters in query string. Default true (includes on headers) + * @param array curl_options optional extra options for curl request + * + * @exception OAuthException2 when no key could be fetched + * @exception OAuthException2 when no server with consumer_key registered + */ + static function requestAccessToken ( $consumer_key, $token, $usr_id, $method = 'POST', $options = array(), $curl_options = array()) + { + OAuthRequestLogger::start(); + + $store = OAuthStore::instance(); + $r = $store->getServerTokenSecrets($consumer_key, $token, 'request', $usr_id); + $uri = $r['access_token_uri']; + $token_name = $r['token_name']; + + // Delete the server request token, this one was for one use only + $store->deleteServerToken($consumer_key, $r['token'], 0, true); + + // Try to exchange our request token for an access token + $oauth = new OAuthRequester($uri, $method); + + if (isset($options['oauth_verifier'])) + { + $oauth->setParam('oauth_verifier', $options['oauth_verifier']); + } + if (isset($options['token_ttl']) && is_numeric($options['token_ttl'])) + { + $oauth->setParam('xoauth_token_ttl', intval($options['token_ttl'])); + } + + OAuthRequestLogger::setRequestObject($oauth); + + $oauth->sign($usr_id, $r, '', 'accessToken'); + $text = $oauth->curl_raw($curl_options, (isset($options['oauth_as_header']) ? $options['oauth_as_header'] : true)); + if (empty($text)) + { + throw new OAuthException2('No answer from the server "'.$uri.'" while requesting an access token'); + } + $data = $oauth->curl_parse($text); + + if ($data['code'] != 200) + { + throw new OAuthException2('Unexpected result from the server "'.$uri.'" ('.$data['code'].') while requesting an access token'); + } + + $token = array(); + $params = explode('&', $data['body']); + foreach ($params as $p) + { + @list($name, $value) = explode('=', $p, 2); + $token[$oauth->urldecode($name)] = $oauth->urldecode($value); + } + + if (!empty($token['oauth_token']) && !empty($token['oauth_token_secret'])) + { + $opts = array(); + $opts['name'] = $token_name; + if (isset($token['xoauth_token_ttl'])) + { + $opts['token_ttl'] = $token['xoauth_token_ttl']; + } + if (isset($options['server_uri'])) + { + $opts['server_uri'] = $options['server_uri']; + } + $store->addServerToken($consumer_key, 'access', $token['oauth_token'], $token['oauth_token_secret'], $usr_id, $opts); + } + else + { + throw new OAuthException2('The server "'.$uri.'" did not return the oauth_token or the oauth_token_secret'); + } + + OAuthRequestLogger::flush(); + } + + + + /** + * Open and close a curl session passing all the options to the curl libs + * + * @param array opts the curl options. + * @param boolean oauth_as_header (optional) set to false to include oauth parameters in query string + * @exception OAuthException2 when temporary file for PUT operation could not be created + * @return string the result of the curl action + */ + protected function curl_raw ( $opts = array(), $oauth_as_header = true ) + { + if (isset($opts[CURLOPT_HTTPHEADER])) + { + $header = $opts[CURLOPT_HTTPHEADER]; + } + else + { + $header = array(); + } + + $ch = curl_init(); + $method = $this->getMethod(); + $url = $this->getRequestUrl(); + $header[] = $this->getAuthorizationHeader(); + $query = $this->getQueryString($oauth_as_header); + $body = $this->getBody(); + + $has_content_type = false; + foreach ($header as $h) + { + if (strncasecmp($h, 'Content-Type:', 13) == 0) + { + $has_content_type = true; + } + } + + if (!is_null($body)) + { + if ($method == 'TRACE') + { + throw new OAuthException2('A body can not be sent with a TRACE operation'); + } + + // PUT and POST allow a request body + if (!empty($query)) + { + $url .= '?'.$query; + } + + // Make sure that the content type of the request is ok + if (!$has_content_type) + { + $header[] = 'Content-Type: application/octet-stream'; + $has_content_type = true; + } + + // When PUTting, we need to use an intermediate file (because of the curl implementation) + if ($method == 'PUT') + { + /* + if (version_compare(phpversion(), '5.2.0') >= 0) + { + // Use the data wrapper to create the file expected by the put method + $put_file = fopen('data://application/octet-stream;base64,'.base64_encode($body)); + } + */ + + $put_file = @tmpfile(); + if (!$put_file) + { + throw new OAuthException2('Could not create tmpfile for PUT operation'); + } + fwrite($put_file, $body); + fseek($put_file, 0); + + curl_setopt($ch, CURLOPT_PUT, true); + curl_setopt($ch, CURLOPT_INFILE, $put_file); + curl_setopt($ch, CURLOPT_INFILESIZE, strlen($body)); + } + else + { + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $body); + } + } + else + { + // a 'normal' request, no body to be send + if ($method == 'POST') + { + if (!$has_content_type) + { + $header[] = 'Content-Type: application/x-www-form-urlencoded'; + $has_content_type = true; + } + + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $query); + } + else + { + if (!empty($query)) + { + $url .= '?'.$query; + } + if ($method != 'GET') + { + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); + } + } + } + + curl_setopt($ch, CURLOPT_HTTPHEADER, $header); + curl_setopt($ch, CURLOPT_USERAGENT, 'anyMeta/OAuth 1.0 - ($LastChangedRevision: 191 $)'); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HEADER, true); + curl_setopt($ch, CURLOPT_TIMEOUT, 30); + + foreach ($opts as $k => $v) + { + if ($k != CURLOPT_HTTPHEADER) + { + curl_setopt($ch, $k, $v); + } + } + + $txt = curl_exec($ch); + if ($txt === false) { + $error = curl_error($ch); + curl_close($ch); + throw new OAuthException2('CURL error: ' . $error); + } + curl_close($ch); + + if (!empty($put_file)) + { + fclose($put_file); + } + + // Tell the logger what we requested and what we received back + $data = $method . " $url\n".implode("\n",$header); + if (is_string($body)) + { + $data .= "\n\n".$body; + } + else if ($method == 'POST') + { + $data .= "\n\n".$query; + } + + OAuthRequestLogger::setSent($data, $body); + OAuthRequestLogger::setReceived($txt); + + return $txt; + } + + + /** + * Parse an http response + * + * @param string response the http text to parse + * @return array (code=>http-code, headers=>http-headers, body=>body) + */ + protected function curl_parse ( $response ) + { + if (empty($response)) + { + return array(); + } + + @list($headers,$body) = explode("\r\n\r\n",$response,2); + $lines = explode("\r\n",$headers); + + if (preg_match('@^HTTP/[0-9]\.[0-9] +100@', $lines[0])) + { + /* HTTP/1.x 100 Continue + * the real data is on the next line + */ + @list($headers,$body) = explode("\r\n\r\n",$body,2); + $lines = explode("\r\n",$headers); + } + + // first line of headers is the HTTP response code + $http_line = array_shift($lines); + if (preg_match('@^HTTP/[0-9]\.[0-9] +([0-9]{3})@', $http_line, $matches)) + { + $code = $matches[1]; + } + + // put the rest of the headers in an array + $headers = array(); + foreach ($lines as $l) + { + list($k, $v) = explode(': ', $l, 2); + $headers[strtolower($k)] = $v; + } + + return array( 'code' => $code, 'headers' => $headers, 'body' => $body); + } + + + /** + * Mix the given headers into the headers that were given to curl + * + * @param array curl_options + * @param array extra_headers + * @return array new curl options + */ + protected function prepareCurlOptions ( $curl_options, $extra_headers ) + { + $hs = array(); + if (!empty($curl_options[CURLOPT_HTTPHEADER]) && is_array($curl_options[CURLOPT_HTTPHEADER])) + { + foreach ($curl_options[CURLOPT_HTTPHEADER] as $h) + { + list($opt, $val) = explode(':', $h, 2); + $opt = str_replace(' ', '-', ucwords(str_replace('-', ' ', $opt))); + $hs[$opt] = $val; + } + } + + $curl_options[CURLOPT_HTTPHEADER] = array(); + $hs = array_merge($hs, $extra_headers); + foreach ($hs as $h => $v) + { + $curl_options[CURLOPT_HTTPHEADER][] = "$h: $v"; + } + return $curl_options; + } +} + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/library/OAuthServer.php b/vendor/oauth-php/library/OAuthServer.php new file mode 100644 index 0000000..878796a --- /dev/null +++ b/vendor/oauth-php/library/OAuthServer.php @@ -0,0 +1,333 @@ +<?php + +/** + * Server layer over the OAuthRequest handler + * + * @version $Id: OAuthServer.php 154 2010-08-31 18:04:41Z brunobg@corollarium.com $ + * @author Marc Worrell <marcw@pobox.com> + * @date Nov 27, 2007 12:36:38 PM + * + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +require_once 'OAuthRequestVerifier.php'; +require_once 'OAuthSession.php'; + +class OAuthServer extends OAuthRequestVerifier +{ + protected $session; + + protected $allowed_uri_schemes = array( + 'http', + 'https' + ); + + protected $disallowed_uri_schemes = array( + 'file', + 'callto', + 'mailto' + ); + + /** + * Construct the request to be verified + * + * @param string request + * @param string method + * @param array params The request parameters + * @param string store The session storage class. + * @param array store_options The session storage class parameters. + * @param array options Extra options: + * - allowed_uri_schemes: list of allowed uri schemes. + * - disallowed_uri_schemes: list of unallowed uri schemes. + * + * e.g. Allow only http and https + * $options = array( + * 'allowed_uri_schemes' => array('http', 'https'), + * 'disallowed_uri_schemes' => array() + * ); + * + * e.g. Disallow callto, mailto and file, allow everything else + * $options = array( + * 'allowed_uri_schemes' => array(), + * 'disallowed_uri_schemes' => array('callto', 'mailto', 'file') + * ); + * + * e.g. Allow everything + * $options = array( + * 'allowed_uri_schemes' => array(), + * 'disallowed_uri_schemes' => array() + * ); + * + */ + function __construct ( $uri = null, $method = null, $params = null, $store = 'SESSION', + $store_options = array(), $options = array() ) + { + parent::__construct($uri, $method, $params); + $this->session = OAuthSession::instance($store, $store_options); + + if (array_key_exists('allowed_uri_schemes', $options) && is_array($options['allowed_uri_schemes'])) { + $this->allowed_uri_schemes = $options['allowed_uri_schemes']; + } + if (array_key_exists('disallowed_uri_schemes', $options) && is_array($options['disallowed_uri_schemes'])) { + $this->disallowed_uri_schemes = $options['disallowed_uri_schemes']; + } + } + + /** + * Handle the request_token request. + * Returns the new request token and request token secret. + * + * TODO: add correct result code to exception + * + * @return string returned request token, false on an error + */ + public function requestToken () + { + OAuthRequestLogger::start($this); + try + { + $this->verify(false); + + $options = array(); + $ttl = $this->getParam('xoauth_token_ttl', false); + if ($ttl) + { + $options['token_ttl'] = $ttl; + } + + // 1.0a Compatibility : associate callback url to the request token + $cbUrl = $this->getParam('oauth_callback', true); + if ($cbUrl) { + $options['oauth_callback'] = $cbUrl; + } + + // Create a request token + $store = OAuthStore::instance(); + $token = $store->addConsumerRequestToken($this->getParam('oauth_consumer_key', true), $options); + $result = 'oauth_callback_confirmed=1&oauth_token='.$this->urlencode($token['token']) + .'&oauth_token_secret='.$this->urlencode($token['token_secret']); + + if (!empty($token['token_ttl'])) + { + $result .= '&xoauth_token_ttl='.$this->urlencode($token['token_ttl']); + } + + $request_token = $token['token']; + + header('HTTP/1.1 200 OK'); + header('Content-Length: '.strlen($result)); + header('Content-Type: application/x-www-form-urlencoded'); + + echo $result; + } + catch (OAuthException2 $e) + { + $request_token = false; + + header('HTTP/1.1 401 Unauthorized'); + header('Content-Type: text/plain'); + + echo "OAuth Verification Failed: " . $e->getMessage(); + } + + OAuthRequestLogger::flush(); + return $request_token; + } + + + /** + * Verify the start of an authorization request. Verifies if the request token is valid. + * Next step is the method authorizeFinish() + * + * Nota bene: this stores the current token, consumer key and callback in the _SESSION + * + * @exception OAuthException2 thrown when not a valid request + * @return array token description + */ + public function authorizeVerify () + { + OAuthRequestLogger::start($this); + + $store = OAuthStore::instance(); + $token = $this->getParam('oauth_token', true); + $rs = $store->getConsumerRequestToken($token); + if (empty($rs)) + { + throw new OAuthException2('Unknown request token "'.$token.'"'); + } + + // We need to remember the callback + $verify_oauth_token = $this->session->get('verify_oauth_token'); + if ( empty($verify_oauth_token) + || strcmp($verify_oauth_token, $rs['token'])) + { + $this->session->set('verify_oauth_token', $rs['token']); + $this->session->set('verify_oauth_consumer_key', $rs['consumer_key']); + $cb = $this->getParam('oauth_callback', true); + if ($cb) + $this->session->set('verify_oauth_callback', $cb); + else + $this->session->set('verify_oauth_callback', $rs['callback_url']); + } + OAuthRequestLogger::flush(); + return $rs; + } + + + /** + * Overrule this method when you want to display a nice page when + * the authorization is finished. This function does not know if the authorization was + * succesfull, you need to check the token in the database. + * + * @param boolean authorized if the current token (oauth_token param) is authorized or not + * @param int user_id user for which the token was authorized (or denied) + * @return string verifier For 1.0a Compatibility + */ + public function authorizeFinish ( $authorized, $user_id ) + { + OAuthRequestLogger::start($this); + + $token = $this->getParam('oauth_token', true); + $verifier = null; + if ($this->session->get('verify_oauth_token') == $token) + { + // Flag the token as authorized, or remove the token when not authorized + $store = OAuthStore::instance(); + + // Fetch the referrer host from the oauth callback parameter + $referrer_host = ''; + $oauth_callback = false; + $verify_oauth_callback = $this->session->get('verify_oauth_callback'); + if (!empty($verify_oauth_callback) && $verify_oauth_callback != 'oob') // OUT OF BAND + { + $oauth_callback = $this->session->get('verify_oauth_callback'); + $ps = parse_url($oauth_callback); + if (isset($ps['host'])) + { + $referrer_host = $ps['host']; + } + } + + if ($authorized) + { + OAuthRequestLogger::addNote('Authorized token "'.$token.'" for user '.$user_id.' with referrer "'.$referrer_host.'"'); + // 1.0a Compatibility : create a verifier code + $verifier = $store->authorizeConsumerRequestToken($token, $user_id, $referrer_host); + } + else + { + OAuthRequestLogger::addNote('Authorization rejected for token "'.$token.'" for user '.$user_id."\nToken has been deleted"); + $store->deleteConsumerRequestToken($token); + } + + if (!empty($oauth_callback)) + { + $params = array('oauth_token' => rawurlencode($token)); + // 1.0a Compatibility : if verifier code has been generated, add it to the URL + if ($verifier) { + $params['oauth_verifier'] = $verifier; + } + + $uri = preg_replace('/\s/', '%20', $oauth_callback); + if (!empty($this->allowed_uri_schemes)) + { + if (!in_array(substr($uri, 0, strpos($uri, '://')), $this->allowed_uri_schemes)) + { + throw new OAuthException2('Illegal protocol in redirect uri '.$uri); + } + } + else if (!empty($this->disallowed_uri_schemes)) + { + if (in_array(substr($uri, 0, strpos($uri, '://')), $this->disallowed_uri_schemes)) + { + throw new OAuthException2('Illegal protocol in redirect uri '.$uri); + } + } + + $this->redirect($oauth_callback, $params, true); + } + } + OAuthRequestLogger::flush(); + return $verifier; + } + + + /** + * Exchange a request token for an access token. + * The exchange is only succesful iff the request token has been authorized. + * + * Never returns, calls exit() when token is exchanged or when error is returned. + */ + public function accessToken () + { + OAuthRequestLogger::start($this); + + try + { + $this->verify('request'); + + $options = array(); + $ttl = $this->getParam('xoauth_token_ttl', false); + if ($ttl) + { + $options['token_ttl'] = $ttl; + } + + $verifier = $this->getParam('oauth_verifier', false); + if ($verifier) { + $options['verifier'] = $verifier; + } + + $store = OAuthStore::instance(); + $token = $store->exchangeConsumerRequestForAccessToken($this->getParam('oauth_token', true), $options); + $result = 'oauth_token='.$this->urlencode($token['token']) + .'&oauth_token_secret='.$this->urlencode($token['token_secret']); + + if (!empty($token['token_ttl'])) + { + $result .= '&xoauth_token_ttl='.$this->urlencode($token['token_ttl']); + } + + header('HTTP/1.1 200 OK'); + header('Content-Length: '.strlen($result)); + header('Content-Type: application/x-www-form-urlencoded'); + + echo $result; + } + catch (OAuthException2 $e) + { + header('HTTP/1.1 401 Access Denied'); + header('Content-Type: text/plain'); + + echo "OAuth Verification Failed: " . $e->getMessage(); + } + + OAuthRequestLogger::flush(); + exit(); + } +} + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?> diff --git a/vendor/oauth-php/library/OAuthSession.php b/vendor/oauth-php/library/OAuthSession.php new file mode 100644 index 0000000..80ceeb7 --- /dev/null +++ b/vendor/oauth-php/library/OAuthSession.php @@ -0,0 +1,86 @@ +<?php + +/** + * Storage container for the oauth credentials, both server and consumer side. + * This is the factory to select the store you want to use + * + * @version $Id: OAuthSession.php 67 2010-01-12 18:42:04Z brunobg@corollarium.com $ + * @author brunobg@corollarium.com + * + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * Copyright (c) 2010 Corollarium Technologies + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +require_once dirname(__FILE__) . '/OAuthException2.php'; + +class OAuthSession +{ + static private $instance = false; + + /** + * Request an instance of the OAuthSession + */ + public static function instance ( $store = 'SESSION', $options = array() ) + { + if (!OAuthSession::$instance) + { + // Select the store you want to use + if (strpos($store, '/') === false) + { + $class = 'OAuthSession'.$store; + $file = dirname(__FILE__) . '/session/'.$class.'.php'; + } + else + { + $file = $store; + $store = basename($file, '.php'); + $class = $store; + } + + if (is_file($file)) + { + require_once $file; + + if (class_exists($class)) + { + OAuthSession::$instance = new $class($options); + } + else + { + throw new OAuthException2('Could not find class '.$class.' in file '.$file); + } + } + else + { + throw new OAuthException2('No OAuthSession for '.$store.' (file '.$file.')'); + } + } + return OAuthSession::$instance; + } +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/library/OAuthStore.php b/vendor/oauth-php/library/OAuthStore.php new file mode 100644 index 0000000..ff6db0f --- /dev/null +++ b/vendor/oauth-php/library/OAuthStore.php @@ -0,0 +1,91 @@ +<?php + +/** + * Storage container for the oauth credentials, both server and consumer side. + * This is the factory to select the store you want to use + * + * @version $Id: OAuthStore.php 182 2011-01-12 14:57:29Z brunobg@corollarium.com $ + * @author Marc Worrell <marcw@pobox.com> + * @date Nov 16, 2007 4:03:30 PM + * + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +require_once dirname(__FILE__) . '/OAuthException2.php'; + +class OAuthStore +{ + static private $instance = false; + + /** + * Request an instance of the OAuthStore + * + * @param string $store The storage system + * @param array $options To pass to the storage system + * @param boolean $forceNewInstance If true, forces the instantiation of a new store. + * @throws OAuthException2 + */ + public static function instance ( $store = 'MySQL', $options = array(), $forceNewInstance = false ) + { + if (!OAuthStore::$instance or $forceNewInstance) + { + // Select the store you want to use + if (strpos($store, '/') === false) + { + $class = 'OAuthStore'.$store; + $file = dirname(__FILE__) . '/store/'.$class.'.php'; + } + else + { + $file = $store; + $store = basename($file, '.php'); + $class = $store; + } + + if (is_file($file)) + { + require_once $file; + + if (class_exists($class)) + { + OAuthStore::$instance = new $class($options); + } + else + { + throw new OAuthException2('Could not find class '.$class.' in file '.$file); + } + } + else + { + throw new OAuthException2('No OAuthStore for '.$store.' (file '.$file.')'); + } + } + return OAuthStore::$instance; + } +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/library/body/OAuthBodyContentDisposition.php b/vendor/oauth-php/library/body/OAuthBodyContentDisposition.php new file mode 100644 index 0000000..02b1e42 --- /dev/null +++ b/vendor/oauth-php/library/body/OAuthBodyContentDisposition.php @@ -0,0 +1,129 @@ +<?php + +/** + * Add the extra headers for a PUT or POST request with a file. + * + * @version $Id$ + * @author Marc Worrell <marcw@pobox.com> + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +class OAuthBodyContentDisposition +{ + /** + * Builds the request string. + * + * The files array can be a combination of the following (either data or file): + * + * file => "path/to/file", filename=, mime=, data= + * + * @param array files (name => filedesc) (not urlencoded) + * @return array (headers, body) + */ + static function encodeBody ( $files ) + { + $headers = array(); + $body = null; + + // 1. Add all the files to the post + if (!empty($files)) + { + foreach ($files as $name => $f) + { + $data = false; + $filename = false; + + if (isset($f['filename'])) + { + $filename = $f['filename']; + } + + if (!empty($f['file'])) + { + $data = @file_get_contents($f['file']); + if ($data === false) + { + throw new OAuthException2(sprintf('Could not read the file "%s" for request body', $f['file'])); + } + if (empty($filename)) + { + $filename = basename($f['file']); + } + } + else if (isset($f['data'])) + { + $data = $f['data']; + } + + // When there is data, add it as a request body, otherwise silently skip the upload + if ($data !== false) + { + if (isset($headers['Content-Disposition'])) + { + throw new OAuthException2('Only a single file (or data) allowed in a signed PUT/POST request body.'); + } + + if (empty($filename)) + { + $filename = 'untitled'; + } + $mime = !empty($f['mime']) ? $f['mime'] : 'application/octet-stream'; + + $headers['Content-Disposition'] = 'attachment; filename="'.OAuthBodyContentDisposition::encodeParameterName($filename).'"'; + $headers['Content-Type'] = $mime; + + $body = $data; + } + + } + + // When we have a body, add the content-length + if (!is_null($body)) + { + $headers['Content-Length'] = strlen($body); + } + } + return array($headers, $body); + } + + + /** + * Encode a parameter's name for use in a multipart header. + * For now we do a simple filter that removes some unwanted characters. + * We might want to implement RFC1522 here. See http://tools.ietf.org/html/rfc1522 + * + * @param string name + * @return string + */ + static function encodeParameterName ( $name ) + { + return preg_replace('/[^\x20-\x7f]|"/', '-', $name); + } +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/library/body/OAuthBodyMultipartFormdata.php b/vendor/oauth-php/library/body/OAuthBodyMultipartFormdata.php new file mode 100644 index 0000000..a869e1e --- /dev/null +++ b/vendor/oauth-php/library/body/OAuthBodyMultipartFormdata.php @@ -0,0 +1,143 @@ +<?php + +/** + * Create the body for a multipart/form-data message. + * + * @version $Id: OAuthMultipartFormdata.php 6 2008-02-13 12:35:09Z marcw@pobox.com $ + * @author Marc Worrell <marcw@pobox.com> + * @date Jan 31, 2008 12:50:05 PM + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +class OAuthBodyMultipartFormdata +{ + /** + * Builds the request string. + * + * The files array can be a combination of the following (either data or file): + * + * file => "path/to/file", filename=, mime=, data= + * + * @param array params (name => value) (all names and values should be urlencoded) + * @param array files (name => filedesc) (not urlencoded) + * @return array (headers, body) + */ + static function encodeBody ( $params, $files ) + { + $headers = array(); + $body = ''; + $boundary = 'OAuthRequester_'.md5(uniqid('multipart') . microtime()); + $headers['Content-Type'] = 'multipart/form-data; boundary=' . $boundary; + + + // 1. Add the parameters to the post + if (!empty($params)) + { + foreach ($params as $name => $value) + { + $body .= '--'.$boundary."\r\n"; + $body .= 'Content-Disposition: form-data; name="'.OAuthBodyMultipartFormdata::encodeParameterName(rawurldecode($name)).'"'; + $body .= "\r\n\r\n"; + $body .= urldecode($value); + $body .= "\r\n"; + } + } + + // 2. Add all the files to the post + if (!empty($files)) + { + $untitled = 1; + + foreach ($files as $name => $f) + { + $data = false; + $filename = false; + + if (isset($f['filename'])) + { + $filename = $f['filename']; + } + + if (!empty($f['file'])) + { + $data = @file_get_contents($f['file']); + if ($data === false) + { + throw new OAuthException2(sprintf('Could not read the file "%s" for form-data part', $f['file'])); + } + if (empty($filename)) + { + $filename = basename($f['file']); + } + } + else if (isset($f['data'])) + { + $data = $f['data']; + } + + // When there is data, add it as a form-data part, otherwise silently skip the upload + if ($data !== false) + { + if (empty($filename)) + { + $filename = sprintf('untitled-%d', $untitled++); + } + $mime = !empty($f['mime']) ? $f['mime'] : 'application/octet-stream'; + $body .= '--'.$boundary."\r\n"; + $body .= 'Content-Disposition: form-data; name="'.OAuthBodyMultipartFormdata::encodeParameterName($name).'"; filename="'.OAuthBodyMultipartFormdata::encodeParameterName($filename).'"'."\r\n"; + $body .= 'Content-Type: '.$mime; + $body .= "\r\n\r\n"; + $body .= $data; + $body .= "\r\n"; + } + + } + } + $body .= '--'.$boundary."--\r\n"; + + $headers['Content-Length'] = strlen($body); + return array($headers, $body); + } + + + /** + * Encode a parameter's name for use in a multipart header. + * For now we do a simple filter that removes some unwanted characters. + * We might want to implement RFC1522 here. See http://tools.ietf.org/html/rfc1522 + * + * @param string name + * @return string + */ + static function encodeParameterName ( $name ) + { + return preg_replace('/[^\x20-\x7f]|"/', '-', $name); + } +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/library/discovery/xrds_parse.php b/vendor/oauth-php/library/discovery/xrds_parse.php new file mode 100644 index 0000000..c9cf949 --- /dev/null +++ b/vendor/oauth-php/library/discovery/xrds_parse.php @@ -0,0 +1,304 @@ +<?php + +/** + * Parse a XRDS discovery description to a simple array format. + * + * For now a simple parse of the document. Better error checking + * in a later version. + * + * @version $Id$ + * @author Marc Worrell <marcw@pobox.com> + * + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* example of use: + +header('content-type: text/plain'); +$file = file_get_contents('../../test/discovery/xrds-magnolia.xrds'); +$xrds = xrds_parse($file); +print_r($xrds); + + */ + +/** + * Parse the xrds file in the argument. The xrds description must have been + * fetched via curl or something else. + * + * TODO: more robust checking, support for more service documents + * TODO: support for URIs to definition instead of local xml:id + * + * @param string data contents of xrds file + * @exception Exception when the file is in an unknown format + * @return array + */ +function xrds_parse ( $data ) +{ + $oauth = array(); + $doc = @DOMDocument::loadXML($data); + if ($doc === false) + { + throw new Exception('Error in XML, can\'t load XRDS document'); + } + + $xpath = new DOMXPath($doc); + $xpath->registerNamespace('xrds', 'xri://$xrds'); + $xpath->registerNamespace('xrd', 'xri://$XRD*($v*2.0)'); + $xpath->registerNamespace('simple', 'http://xrds-simple.net/core/1.0'); + + // Yahoo! uses this namespace, with lowercase xrd in it + $xpath->registerNamespace('xrd2', 'xri://$xrd*($v*2.0)'); + + $uris = xrds_oauth_service_uris($xpath); + + foreach ($uris as $uri) + { + // TODO: support uris referring to service documents outside this one + if ($uri{0} == '#') + { + $id = substr($uri, 1); + $oauth = xrds_xrd_oauth($xpath, $id); + if (is_array($oauth) && !empty($oauth)) + { + return $oauth; + } + } + } + + return false; +} + + +/** + * Parse a XRD definition for OAuth and return the uris etc. + * + * @param XPath xpath + * @param string id + * @return array + */ +function xrds_xrd_oauth ( $xpath, $id ) +{ + $oauth = array(); + $xrd = $xpath->query('//xrds:XRDS/xrd:XRD[@xml:id="'.$id.'"]'); + if ($xrd->length == 0) + { + // Yahoo! uses another namespace + $xrd = $xpath->query('//xrds:XRDS/xrd2:XRD[@xml:id="'.$id.'"]'); + } + + if ($xrd->length >= 1) + { + $x = $xrd->item(0); + $services = array(); + foreach ($x->childNodes as $n) + { + switch ($n->nodeName) + { + case 'Type': + if ($n->nodeValue != 'xri://$xrds*simple') + { + // Not a simple XRDS document + return false; + } + break; + case 'Expires': + $oauth['expires'] = $n->nodeValue; + break; + case 'Service': + list($type,$service) = xrds_xrd_oauth_service($n); + if ($type) + { + $services[$type][xrds_priority($n)][] = $service; + } + break; + } + } + + // Flatten the services on priority + foreach ($services as $type => $service) + { + $oauth[$type] = xrds_priority_flatten($service); + } + } + else + { + $oauth = false; + } + return $oauth; +} + + +/** + * Parse a service definition for OAuth in a simple xrd element + * + * @param DOMElement n + * @return array (type, service desc) + */ +function xrds_xrd_oauth_service ( $n ) +{ + $service = array( + 'uri' => '', + 'signature_method' => array(), + 'parameters' => array() + ); + + $type = false; + foreach ($n->childNodes as $c) + { + $name = $c->nodeName; + $value = $c->nodeValue; + + if ($name == 'URI') + { + $service['uri'] = $value; + } + else if ($name == 'Type') + { + if (strncmp($value, 'http://oauth.net/core/1.0/endpoint/', 35) == 0) + { + $type = basename($value); + } + else if (strncmp($value, 'http://oauth.net/core/1.0/signature/', 36) == 0) + { + $service['signature_method'][] = basename($value); + } + else if (strncmp($value, 'http://oauth.net/core/1.0/parameters/', 37) == 0) + { + $service['parameters'][] = basename($value); + } + else if (strncmp($value, 'http://oauth.net/discovery/1.0/consumer-identity/', 49) == 0) + { + $type = 'consumer_identity'; + $service['method'] = basename($value); + unset($service['signature_method']); + unset($service['parameters']); + } + else + { + $service['unknown'][] = $value; + } + } + else if ($name == 'LocalID') + { + $service['consumer_key'] = $value; + } + else if ($name{0} != '#') + { + $service[strtolower($name)] = $value; + } + } + return array($type, $service); +} + + +/** + * Return the OAuth service uris in order of the priority. + * + * @param XPath xpath + * @return array + */ +function xrds_oauth_service_uris ( $xpath ) +{ + $uris = array(); + $xrd_oauth = $xpath->query('//xrds:XRDS/xrd:XRD/xrd:Service/xrd:Type[.=\'http://oauth.net/discovery/1.0\']'); + if ($xrd_oauth->length > 0) + { + $service = array(); + foreach ($xrd_oauth as $xo) + { + // Find the URI of the service definition + $cs = $xo->parentNode->childNodes; + foreach ($cs as $c) + { + if ($c->nodeName == 'URI') + { + $prio = xrds_priority($xo); + $service[$prio][] = $c->nodeValue; + } + } + } + $uris = xrds_priority_flatten($service); + } + return $uris; +} + + + +/** + * Flatten an array according to the priority + * + * @param array ps buckets per prio + * @return array one dimensional array + */ +function xrds_priority_flatten ( $ps ) +{ + $prio = array(); + $null = array(); + ksort($ps); + foreach ($ps as $idx => $bucket) + { + if (!empty($bucket)) + { + if ($idx == 'null') + { + $null = $bucket; + } + else + { + $prio = array_merge($prio, $bucket); + } + } + } + $prio = array_merge($prio, $bucket); + return $prio; +} + + +/** + * Fetch the priority of a element + * + * @param DOMElement elt + * @return mixed 'null' or int + */ +function xrds_priority ( $elt ) +{ + if ($elt->hasAttribute('priority')) + { + $prio = $elt->getAttribute('priority'); + if (is_numeric($prio)) + { + $prio = intval($prio); + } + } + else + { + $prio = 'null'; + } + return $prio; +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/library/discovery/xrds_parse.txt b/vendor/oauth-php/library/discovery/xrds_parse.txt new file mode 100644 index 0000000..fd867ea --- /dev/null +++ b/vendor/oauth-php/library/discovery/xrds_parse.txt @@ -0,0 +1,101 @@ +The xrds_parse.php script contains the function: + + function xrds_parse ( $data. ) + +$data Contains the contents of a XRDS XML file. +When the data is invalid XML then this will throw an exception. + +After parsing a XRDS definition it will return a datastructure much like the one below. + +Array +( + [expires] => 2008-04-13T07:34:58Z + [request] => Array + ( + [0] => Array + ( + [uri] => https://ma.gnolia.com/oauth/get_request_token + [signature_method] => Array + ( + [0] => HMAC-SHA1 + [1] => RSA-SHA1 + [2] => PLAINTEXT + ) + + [parameters] => Array + ( + [0] => auth-header + [1] => post-body + [2] => uri-query + ) + ) + ) + + [authorize] => Array + ( + [0] => Array + ( + [uri] => http://ma.gnolia.com/oauth/authorize + [signature_method] => Array + ( + ) + + [parameters] => Array + ( + [0] => auth-header + [1] => uri-query + ) + ) + ) + + [access] => Array + ( + [0] => Array + ( + [uri] => https://ma.gnolia.com/oauth/get_access_token + [signature_method] => Array + ( + [0] => HMAC-SHA1 + [1] => RSA-SHA1 + [2] => PLAINTEXT + ) + + [parameters] => Array + ( + [0] => auth-header + [1] => post-body + [2] => uri-query + ) + ) + ) + + [resource] => Array + ( + [0] => Array + ( + [uri] => + [signature_method] => Array + ( + [0] => HMAC-SHA1 + [1] => RSA-SHA1 + ) + + [parameters] => Array + ( + [0] => auth-header + [1] => post-body + [2] => uri-query + ) + ) + ) + + [consumer_identity] => Array + ( + [0] => Array + ( + [uri] => http://ma.gnolia.com/applications/new + [method] => oob + ) + ) +) + diff --git a/vendor/oauth-php/library/session/OAuthSessionAbstract.class.php b/vendor/oauth-php/library/session/OAuthSessionAbstract.class.php new file mode 100644 index 0000000..dcc80c1 --- /dev/null +++ b/vendor/oauth-php/library/session/OAuthSessionAbstract.class.php @@ -0,0 +1,44 @@ +<?php + +/** + * Abstract base class for OAuthStore implementations + * + * @version $Id$ + * @author Bruno Barberi Gnecco <brunobg@corollarium.com> + * + * The MIT License + * + * Copyright (c) 2010 Corollarium Technologies + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * This class is used to store Session information on the server. Most + * people will use the $_SESSION based implementation, but you may prefer + * a SQL, Memcache or other implementation. + * + */ +abstract class OAuthSessionAbstract +{ + abstract public function get ( $key ); + abstract public function set ( $key, $data ); +} + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/library/session/OAuthSessionSESSION.php b/vendor/oauth-php/library/session/OAuthSessionSESSION.php new file mode 100644 index 0000000..3201ecb --- /dev/null +++ b/vendor/oauth-php/library/session/OAuthSessionSESSION.php @@ -0,0 +1,63 @@ +<?php + +/** + * Abstract base class for OAuthStore implementations + * + * @version $Id$ + * @author Bruno Barberi Gnecco <brunobg@corollarium.com> + * + * The MIT License + * + * Copyright (c) 2010 Corollarium Technologies + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +require_once dirname(__FILE__) . '/OAuthSessionAbstract.class.php'; + +class OAuthSessionSESSION extends OAuthSessionAbstract +{ + public function __construct( $options = array() ) + { + } + + /** + * Gets a variable value + * + * @param string $key + * @return The value or null if not set. + */ + public function get ( $key ) + { + return @$_SESSION[$key]; + } + + /** + * Sets a variable value + * + * @param string $key The key + * @param any $data The data + */ + public function set ( $key, $data ) + { + $_SESSION[$key] = $data; + } +} + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/library/signature_method/OAuthSignatureMethod.class.php b/vendor/oauth-php/library/signature_method/OAuthSignatureMethod.class.php new file mode 100644 index 0000000..34ccb42 --- /dev/null +++ b/vendor/oauth-php/library/signature_method/OAuthSignatureMethod.class.php @@ -0,0 +1,69 @@ +<?php + +/** + * Interface for OAuth signature methods + * + * @version $Id$ + * @author Marc Worrell <marcw@pobox.com> + * @date Sep 8, 2008 12:04:35 PM + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +abstract class OAuthSignatureMethod +{ + /** + * Return the name of this signature + * + * @return string + */ + abstract public function name(); + + /** + * Return the signature for the given request + * + * @param OAuthRequest request + * @param string base_string + * @param string consumer_secret + * @param string token_secret + * @return string + */ + abstract public function signature ( $request, $base_string, $consumer_secret, $token_secret ); + + /** + * Check if the request signature corresponds to the one calculated for the request. + * + * @param OAuthRequest request + * @param string base_string data to be signed, usually the base string, can be a request body + * @param string consumer_secret + * @param string token_secret + * @param string signature from the request, still urlencoded + * @return string + */ + abstract public function verify ( $request, $base_string, $consumer_secret, $token_secret, $signature ); +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/library/signature_method/OAuthSignatureMethod_HMAC_SHA1.php b/vendor/oauth-php/library/signature_method/OAuthSignatureMethod_HMAC_SHA1.php new file mode 100644 index 0000000..e189c93 --- /dev/null +++ b/vendor/oauth-php/library/signature_method/OAuthSignatureMethod_HMAC_SHA1.php @@ -0,0 +1,115 @@ +<?php + +/** + * OAuth signature implementation using HMAC-SHA1 + * + * @version $Id$ + * @author Marc Worrell <marcw@pobox.com> + * @date Sep 8, 2008 12:21:19 PM + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +require_once dirname(__FILE__).'/OAuthSignatureMethod.class.php'; + + +class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod +{ + public function name () + { + return 'HMAC-SHA1'; + } + + + /** + * Calculate the signature using HMAC-SHA1 + * This function is copyright Andy Smith, 2007. + * + * @param OAuthRequest request + * @param string base_string + * @param string consumer_secret + * @param string token_secret + * @return string + */ + function signature ( $request, $base_string, $consumer_secret, $token_secret ) + { + $key = $request->urlencode($consumer_secret).'&'.$request->urlencode($token_secret); + if (function_exists('hash_hmac')) + { + $signature = base64_encode(hash_hmac("sha1", $base_string, $key, true)); + } + else + { + $blocksize = 64; + $hashfunc = 'sha1'; + if (strlen($key) > $blocksize) + { + $key = pack('H*', $hashfunc($key)); + } + $key = str_pad($key,$blocksize,chr(0x00)); + $ipad = str_repeat(chr(0x36),$blocksize); + $opad = str_repeat(chr(0x5c),$blocksize); + $hmac = pack( + 'H*',$hashfunc( + ($key^$opad).pack( + 'H*',$hashfunc( + ($key^$ipad).$base_string + ) + ) + ) + ); + $signature = base64_encode($hmac); + } + return $request->urlencode($signature); + } + + + /** + * Check if the request signature corresponds to the one calculated for the request. + * + * @param OAuthRequest request + * @param string base_string data to be signed, usually the base string, can be a request body + * @param string consumer_secret + * @param string token_secret + * @param string signature from the request, still urlencoded + * @return string + */ + public function verify ( $request, $base_string, $consumer_secret, $token_secret, $signature ) + { + $a = $request->urldecode($signature); + $b = $request->urldecode($this->signature($request, $base_string, $consumer_secret, $token_secret)); + + // We have to compare the decoded values + $valA = base64_decode($a); + $valB = base64_decode($b); + + // Crude binary comparison + return rawurlencode($valA) == rawurlencode($valB); + } +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/library/signature_method/OAuthSignatureMethod_HMAC_SHA256.php b/vendor/oauth-php/library/signature_method/OAuthSignatureMethod_HMAC_SHA256.php new file mode 100644 index 0000000..2d037dd --- /dev/null +++ b/vendor/oauth-php/library/signature_method/OAuthSignatureMethod_HMAC_SHA256.php @@ -0,0 +1,81 @@ +<?php + +/** + * OAuth signature implementation using HMAC-SHA256 + * + * @author André Noack <noack@data-quest.de> + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +require_once dirname(__FILE__).'/OAuthSignatureMethod.class.php'; + + +class OAuthSignatureMethod_HMAC_SHA256 extends OAuthSignatureMethod +{ + public function name () + { + return 'HMAC-SHA256'; + } + + + /** + * Calculate the signature using HMAC-SHA1 + * This function is copyright Andy Smith, 2007. + * + * @param OAuthRequest request + * @param string base_string + * @param string consumer_secret + * @param string token_secret + * @return string + */ + function signature ( $request, $base_string, $consumer_secret, $token_secret ) + { + $key = $request->urlencode($consumer_secret).'&'.$request->urlencode($token_secret); + $signature = base64_encode(hash_hmac("sha256", $base_string, $key, true)); + return $request->urlencode($signature); + } + + + /** + * Check if the request signature corresponds to the one calculated for the request. + * + * @param OAuthRequest request + * @param string base_string data to be signed, usually the base string, can be a request body + * @param string consumer_secret + * @param string token_secret + * @param string signature from the request, still urlencoded + * @return string + */ + public function verify ( $request, $base_string, $consumer_secret, $token_secret, $signature ) + { + $a = $request->urldecode($signature); + $b = $request->urldecode($this->signature($request, $base_string, $consumer_secret, $token_secret)); + + // We have to compare the decoded values + $valA = base64_decode($a); + $valB = base64_decode($b); + + // Crude binary comparison + return rawurlencode($valA) == rawurlencode($valB); + } +} diff --git a/vendor/oauth-php/library/signature_method/OAuthSignatureMethod_MD5.php b/vendor/oauth-php/library/signature_method/OAuthSignatureMethod_MD5.php new file mode 100644 index 0000000..a016709 --- /dev/null +++ b/vendor/oauth-php/library/signature_method/OAuthSignatureMethod_MD5.php @@ -0,0 +1,95 @@ +<?php + +/** + * OAuth signature implementation using MD5 + * + * @version $Id$ + * @author Marc Worrell <marcw@pobox.com> + * @date Sep 8, 2008 12:09:43 PM + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +require_once dirname(__FILE__).'/OAuthSignatureMethod.class.php'; + + +class OAuthSignatureMethod_MD5 extends OAuthSignatureMethod +{ + public function name () + { + return 'MD5'; + } + + + /** + * Calculate the signature using MD5 + * Binary md5 digest, as distinct from PHP's built-in hexdigest. + * This function is copyright Andy Smith, 2007. + * + * @param OAuthRequest request + * @param string base_string + * @param string consumer_secret + * @param string token_secret + * @return string + */ + function signature ( $request, $base_string, $consumer_secret, $token_secret ) + { + $s .= '&'.$request->urlencode($consumer_secret).'&'.$request->urlencode($token_secret); + $md5 = md5($base_string); + $bin = ''; + + for ($i = 0; $i < strlen($md5); $i += 2) + { + $bin .= chr(hexdec($md5{$i+1}) + hexdec($md5{$i}) * 16); + } + return $request->urlencode(base64_encode($bin)); + } + + + /** + * Check if the request signature corresponds to the one calculated for the request. + * + * @param OAuthRequest request + * @param string base_string data to be signed, usually the base string, can be a request body + * @param string consumer_secret + * @param string token_secret + * @param string signature from the request, still urlencoded + * @return string + */ + public function verify ( $request, $base_string, $consumer_secret, $token_secret, $signature ) + { + $a = $request->urldecode($signature); + $b = $request->urldecode($this->signature($request, $base_string, $consumer_secret, $token_secret)); + + // We have to compare the decoded values + $valA = base64_decode($a); + $valB = base64_decode($b); + + // Crude binary comparison + return rawurlencode($valA) == rawurlencode($valB); + } +} + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/library/signature_method/OAuthSignatureMethod_PLAINTEXT.php b/vendor/oauth-php/library/signature_method/OAuthSignatureMethod_PLAINTEXT.php new file mode 100644 index 0000000..92ef308 --- /dev/null +++ b/vendor/oauth-php/library/signature_method/OAuthSignatureMethod_PLAINTEXT.php @@ -0,0 +1,80 @@ +<?php + +/** + * OAuth signature implementation using PLAINTEXT + * + * @version $Id$ + * @author Marc Worrell <marcw@pobox.com> + * @date Sep 8, 2008 12:09:43 PM + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +require_once dirname(__FILE__).'/OAuthSignatureMethod.class.php'; + + +class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod +{ + public function name () + { + return 'PLAINTEXT'; + } + + + /** + * Calculate the signature using PLAINTEXT + * + * @param OAuthRequest request + * @param string base_string + * @param string consumer_secret + * @param string token_secret + * @return string + */ + function signature ( $request, $base_string, $consumer_secret, $token_secret ) + { + return $request->urlencode($request->urlencode($consumer_secret).'&'.$request->urlencode($token_secret)); + } + + + /** + * Check if the request signature corresponds to the one calculated for the request. + * + * @param OAuthRequest request + * @param string base_string data to be signed, usually the base string, can be a request body + * @param string consumer_secret + * @param string token_secret + * @param string signature from the request, still urlencoded + * @return string + */ + public function verify ( $request, $base_string, $consumer_secret, $token_secret, $signature ) + { + $a = $request->urldecode($signature); + $b = $request->urldecode($this->signature($request, $base_string, $consumer_secret, $token_secret)); + + return $request->urldecode($a) == $request->urldecode($b); + } +} + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/library/signature_method/OAuthSignatureMethod_RSA_SHA1.php b/vendor/oauth-php/library/signature_method/OAuthSignatureMethod_RSA_SHA1.php new file mode 100644 index 0000000..864dbfb --- /dev/null +++ b/vendor/oauth-php/library/signature_method/OAuthSignatureMethod_RSA_SHA1.php @@ -0,0 +1,139 @@ +<?php + +/** + * OAuth signature implementation using PLAINTEXT + * + * @version $Id$ + * @author Marc Worrell <marcw@pobox.com> + * @date Sep 8, 2008 12:00:14 PM + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +require_once dirname(__FILE__).'/OAuthSignatureMethod.class.php'; + +class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod +{ + public function name() + { + return 'RSA-SHA1'; + } + + + /** + * Fetch the public CERT key for the signature + * + * @param OAuthRequest request + * @return string public key + */ + protected function fetch_public_cert ( $request ) + { + // not implemented yet, ideas are: + // (1) do a lookup in a table of trusted certs keyed off of consumer + // (2) fetch via http using a url provided by the requester + // (3) some sort of specific discovery code based on request + // + // either way should return a string representation of the certificate + throw OAuthException2("OAuthSignatureMethod_RSA_SHA1::fetch_public_cert not implemented"); + } + + + /** + * Fetch the private CERT key for the signature + * + * @param OAuthRequest request + * @return string private key + */ + protected function fetch_private_cert ( $request ) + { + // not implemented yet, ideas are: + // (1) do a lookup in a table of trusted certs keyed off of consumer + // + // either way should return a string representation of the certificate + throw OAuthException2("OAuthSignatureMethod_RSA_SHA1::fetch_private_cert not implemented"); + } + + + /** + * Calculate the signature using RSA-SHA1 + * This function is copyright Andy Smith, 2008. + * + * @param OAuthRequest request + * @param string base_string + * @param string consumer_secret + * @param string token_secret + * @return string + */ + public function signature ( $request, $base_string, $consumer_secret, $token_secret ) + { + // Fetch the private key cert based on the request + $cert = $this->fetch_private_cert($request); + + // Pull the private key ID from the certificate + $privatekeyid = openssl_get_privatekey($cert); + + // Sign using the key + $sig = false; + $ok = openssl_sign($base_string, $sig, $privatekeyid); + + // Release the key resource + openssl_free_key($privatekeyid); + + return $request->urlencode(base64_encode($sig)); + } + + + /** + * Check if the request signature is the same as the one calculated for the request. + * + * @param OAuthRequest request + * @param string base_string + * @param string consumer_secret + * @param string token_secret + * @param string signature + * @return string + */ + public function verify ( $request, $base_string, $consumer_secret, $token_secret, $signature ) + { + $decoded_sig = base64_decode($request->urldecode($signature)); + + // Fetch the public key cert based on the request + $cert = $this->fetch_public_cert($request); + + // Pull the public key ID from the certificate + $publickeyid = openssl_get_publickey($cert); + + // Check the computed signature against the one passed in the query + $ok = openssl_verify($base_string, $decoded_sig, $publickeyid); + + // Release the key resource + openssl_free_key($publickeyid); + return $ok == 1; + } + +} + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/library/store/OAuthStore2Leg.php b/vendor/oauth-php/library/store/OAuthStore2Leg.php new file mode 100644 index 0000000..d86505b --- /dev/null +++ b/vendor/oauth-php/library/store/OAuthStore2Leg.php @@ -0,0 +1,118 @@ +<?php + +/** + * OAuthStore implementation for 2 legged OAuth. This 'store' just saves the + * consumer_token and consumer_secret. + * + * @version $Id$ + * @author Ben Hesketh <ben.hesketh@compassengine.com> + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +require_once dirname(__FILE__) . '/OAuthStoreAbstract.class.php'; + +class OAuthStore2Leg extends OAuthStoreAbstract +{ + protected $consumer_key; + protected $consumer_secret; + protected $signature_method = array('HMAC-SHA1'); + protected $token_type = false; + + /* + * Takes two options: consumer_key and consumer_secret + */ + public function __construct( $options = array() ) + { + if(isset($options['consumer_key']) && isset($options['consumer_secret'])) + { + $this->consumer_key = $options['consumer_key']; + $this->consumer_secret = $options['consumer_secret']; + if (isset($options['token_secret'])) + { + $this->token_secret = $options['token_secret']; + } + } + else + { + throw new OAuthException2("OAuthStore2Leg needs consumer_token and consumer_secret"); + } + } + + public function getSecretsForVerify ( $consumer_key, $token, $token_type = 'access' ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + public function getSecretsForSignature ( $uri, $user_id ) + { + return array( + 'consumer_key' => $this->consumer_key, + 'consumer_secret' => $this->consumer_secret, + 'signature_methods' => $this->signature_method, + 'token' => $this->token_type, + 'token_secret' => $this->token_secret + ); + } + public function getServerTokenSecrets ( $consumer_key, $token, $token_type, $user_id, $name = '' ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + public function addServerToken ( $consumer_key, $token_type, $token, $token_secret, $user_id, $options = array() ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + + public function deleteServer ( $consumer_key, $user_id, $user_is_admin = false ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + public function getServer( $consumer_key, $user_id, $user_is_admin = false ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + public function getServerForUri ( $uri, $user_id ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + public function listServerTokens ( $user_id ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + public function countServerTokens ( $consumer_key ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + public function getServerToken ( $consumer_key, $token, $user_id ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + public function deleteServerToken ( $consumer_key, $token, $user_id, $user_is_admin = false ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + public function setServerTokenTtl ( $consumer_key, $token, $token_ttl, $server_uri = NULL ) + { + //This method just needs to exist. It doesn't have to do anything! + } + + public function listServers ( $q = '', $user_id ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + public function updateServer ( $server, $user_id, $user_is_admin = false ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + + public function updateConsumer ( $consumer, $user_id, $user_is_admin = false ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + public function deleteConsumer ( $consumer_key, $user_id, $user_is_admin = false ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + public function getConsumer ( $consumer_key, $user_id, $user_is_admin = false ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + public function getConsumerStatic () { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + + public function addConsumerRequestToken ( $consumer_key, $options = array() ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + public function getConsumerRequestToken ( $token ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + public function deleteConsumerRequestToken ( $token ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + public function authorizeConsumerRequestToken ( $token, $user_id, $referrer_host = '' ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + public function countConsumerAccessTokens ( $consumer_key ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + public function exchangeConsumerRequestForAccessToken ( $token, $options = array() ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + public function getConsumerAccessToken ( $token, $user_id ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + public function deleteConsumerAccessToken ( $token, $user_id, $user_is_admin = false ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + public function setConsumerAccessTokenTtl ( $token, $ttl ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + + public function listConsumers ( $user_id ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + public function listConsumerApplications( $begin = 0, $total = 25 ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + public function listConsumerTokens ( $user_id ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + + public function checkServerNonce ( $consumer_key, $token, $timestamp, $nonce ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + + public function addLog ( $keys, $received, $sent, $base_string, $notes, $user_id = null ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + public function listLog ( $options, $user_id ) { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } + + public function install () { throw new OAuthException2("OAuthStore2Leg doesn't support " . __METHOD__); } +} + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/library/store/OAuthStoreAbstract.class.php b/vendor/oauth-php/library/store/OAuthStoreAbstract.class.php new file mode 100644 index 0000000..0b240ee --- /dev/null +++ b/vendor/oauth-php/library/store/OAuthStoreAbstract.class.php @@ -0,0 +1,151 @@ +<?php + +/** + * Abstract base class for OAuthStore implementations + * + * @version $Id$ + * @author Marc Worrell <marcw@pobox.com> + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +abstract class OAuthStoreAbstract +{ + abstract public function getSecretsForVerify ( $consumer_key, $token, $token_type = 'access' ); + abstract public function getSecretsForSignature ( $uri, $user_id ); + abstract public function getServerTokenSecrets ( $consumer_key, $token, $token_type, $user_id, $name = '' ); + abstract public function addServerToken ( $consumer_key, $token_type, $token, $token_secret, $user_id, $options = array() ); + + abstract public function deleteServer ( $consumer_key, $user_id, $user_is_admin = false ); + abstract public function getServer( $consumer_key, $user_id, $user_is_admin = false ); + abstract public function getServerForUri ( $uri, $user_id ); + abstract public function listServerTokens ( $user_id ); + abstract public function countServerTokens ( $consumer_key ); + abstract public function getServerToken ( $consumer_key, $token, $user_id ); + abstract public function setServerTokenTtl ( $consumer_key, $token, $token_ttl, $server_uri = NULL ); + abstract public function deleteServerToken ( $consumer_key, $token, $user_id, $user_is_admin = false ); + abstract public function listServers ( $q = '', $user_id ); + abstract public function updateServer ( $server, $user_id, $user_is_admin = false ); + + abstract public function updateConsumer ( $consumer, $user_id, $user_is_admin = false ); + abstract public function deleteConsumer ( $consumer_key, $user_id, $user_is_admin = false ); + abstract public function getConsumer ( $consumer_key, $user_id, $user_is_admin = false ); + abstract public function getConsumerStatic (); + + abstract public function addConsumerRequestToken ( $consumer_key, $options = array() ); + abstract public function getConsumerRequestToken ( $token ); + abstract public function deleteConsumerRequestToken ( $token ); + abstract public function authorizeConsumerRequestToken ( $token, $user_id, $referrer_host = '' ); + abstract public function countConsumerAccessTokens ( $consumer_key ); + abstract public function exchangeConsumerRequestForAccessToken ( $token, $options = array() ); + abstract public function getConsumerAccessToken ( $token, $user_id ); + abstract public function deleteConsumerAccessToken ( $token, $user_id, $user_is_admin = false ); + abstract public function setConsumerAccessTokenTtl ( $token, $ttl ); + + abstract public function listConsumers ( $user_id ); + abstract public function listConsumerApplications( $begin = 0, $total = 25 ); + abstract public function listConsumerTokens ( $user_id ); + + abstract public function checkServerNonce ( $consumer_key, $token, $timestamp, $nonce ); + + abstract public function addLog ( $keys, $received, $sent, $base_string, $notes, $user_id = null ); + abstract public function listLog ( $options, $user_id ); + + abstract public function install (); + + /** + * Fetch the current static consumer key for this site, create it when it was not found. + * The consumer secret for the consumer key is always empty. + * + * @return string consumer key + */ + + + /* ** Some handy utility functions ** */ + + /** + * Generate a unique key + * + * @param boolean unique force the key to be unique + * @return string + */ + public function generateKey ( $unique = false ) + { + $key = md5(uniqid(rand(), true)); + if ($unique) + { + list($usec,$sec) = explode(' ',microtime()); + $key .= dechex($usec).dechex($sec); + } + return $key; + } + + /** + * Check to see if a string is valid utf8 + * + * @param string $s + * @return boolean + */ + protected function isUTF8 ( $s ) + { + return preg_match('%(?: + [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte + |\xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs + |[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte + |\xED[\x80-\x9F][\x80-\xBF] # excluding surrogates + |\xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 + |[\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 + |\xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 + )+%xs', $s); + } + + + /** + * Make a string utf8, replacing all non-utf8 chars with a '.' + * + * @param string + * @return string + */ + protected function makeUTF8 ( $s ) + { + if (function_exists('iconv')) + { + do + { + $ok = true; + $text = @iconv('UTF-8', 'UTF-8//TRANSLIT', $s); + if (strlen($text) != strlen($s)) + { + // Remove the offending character... + $s = $text . '.' . substr($s, strlen($text) + 1); + $ok = false; + } + } + while (!$ok); + } + return $s; + } + +} + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/library/store/OAuthStoreAnyMeta.php b/vendor/oauth-php/library/store/OAuthStoreAnyMeta.php new file mode 100644 index 0000000..b619ec0 --- /dev/null +++ b/vendor/oauth-php/library/store/OAuthStoreAnyMeta.php @@ -0,0 +1,264 @@ +<?php + +/** + * Storage container for the oauth credentials, both server and consumer side. + * This file can only be used in conjunction with anyMeta. + * + * @version $Id: OAuthStoreAnyMeta.php 68 2010-01-12 18:59:23Z brunobg@corollarium.com $ + * @author Marc Worrell <marcw@pobox.com> + * @date Nov 16, 2007 4:03:30 PM + * + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +require_once dirname(__FILE__) . '/OAuthStoreMySQL.php'; + + +class OAuthStoreAnymeta extends OAuthStoreMySQL +{ + /** + * Construct the OAuthStoreAnymeta + * + * @param array options + */ + function __construct ( $options = array() ) + { + parent::__construct(array('conn' => any_db_conn())); + } + + + /** + * Add an entry to the log table + * + * @param array keys (osr_consumer_key, ost_token, ocr_consumer_key, oct_token) + * @param string received + * @param string sent + * @param string base_string + * @param string notes + * @param int (optional) user_id + */ + public function addLog ( $keys, $received, $sent, $base_string, $notes, $user_id = null ) + { + if (is_null($user_id) && isset($GLOBALS['any_auth'])) + { + $user_id = $GLOBALS['any_auth']->getUserId(); + } + parent::addLog($keys, $received, $sent, $base_string, $notes, $user_id); + } + + + /** + * Get a page of entries from the log. Returns the last 100 records + * matching the options given. + * + * @param array options + * @param int user_id current user + * @return array log records + */ + public function listLog ( $options, $user_id ) + { + $where = array(); + $args = array(); + if (empty($options)) + { + $where[] = 'olg_usa_id_ref = %d'; + $args[] = $user_id; + } + else + { + foreach ($options as $option => $value) + { + if (strlen($value) > 0) + { + switch ($option) + { + case 'osr_consumer_key': + case 'ocr_consumer_key': + case 'ost_token': + case 'oct_token': + $where[] = 'olg_'.$option.' = \'%s\''; + $args[] = $value; + break; + } + } + } + + $where[] = '(olg_usa_id_ref IS NULL OR olg_usa_id_ref = %d)'; + $args[] = $user_id; + } + + $rs = any_db_query_all_assoc(' + SELECT olg_id, + olg_osr_consumer_key AS osr_consumer_key, + olg_ost_token AS ost_token, + olg_ocr_consumer_key AS ocr_consumer_key, + olg_oct_token AS oct_token, + olg_usa_id_ref AS user_id, + olg_received AS received, + olg_sent AS sent, + olg_base_string AS base_string, + olg_notes AS notes, + olg_timestamp AS timestamp, + INET_NTOA(olg_remote_ip) AS remote_ip + FROM oauth_log + WHERE '.implode(' AND ', $where).' + ORDER BY olg_id DESC + LIMIT 0,100', $args); + + return $rs; + } + + + + /** + * Initialise the database + */ + public function install () + { + parent::install(); + + any_db_query("ALTER TABLE oauth_consumer_registry MODIFY ocr_usa_id_ref int(11) unsigned"); + any_db_query("ALTER TABLE oauth_consumer_token MODIFY oct_usa_id_ref int(11) unsigned not null"); + any_db_query("ALTER TABLE oauth_server_registry MODIFY osr_usa_id_ref int(11) unsigned"); + any_db_query("ALTER TABLE oauth_server_token MODIFY ost_usa_id_ref int(11) unsigned not null"); + any_db_query("ALTER TABLE oauth_log MODIFY olg_usa_id_ref int(11) unsigned"); + + any_db_alter_add_fk('oauth_consumer_registry', 'ocr_usa_id_ref', 'any_user_auth(usa_id_ref)', 'on update cascade on delete set null'); + any_db_alter_add_fk('oauth_consumer_token', 'oct_usa_id_ref', 'any_user_auth(usa_id_ref)', 'on update cascade on delete cascade'); + any_db_alter_add_fk('oauth_server_registry', 'osr_usa_id_ref', 'any_user_auth(usa_id_ref)', 'on update cascade on delete set null'); + any_db_alter_add_fk('oauth_server_token', 'ost_usa_id_ref', 'any_user_auth(usa_id_ref)', 'on update cascade on delete cascade'); + any_db_alter_add_fk('oauth_log', 'olg_usa_id_ref', 'any_user_auth(usa_id_ref)', 'on update cascade on delete cascade'); + } + + + + /** Some simple helper functions for querying the mysql db **/ + + /** + * Perform a query, ignore the results + * + * @param string sql + * @param vararg arguments (for sprintf) + */ + protected function query ( $sql ) + { + list($sql, $args) = $this->sql_args(func_get_args()); + any_db_query($sql, $args); + } + + + /** + * Perform a query, ignore the results + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return array + */ + protected function query_all_assoc ( $sql ) + { + list($sql, $args) = $this->sql_args(func_get_args()); + return any_db_query_all_assoc($sql, $args); + } + + + /** + * Perform a query, return the first row + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return array + */ + protected function query_row_assoc ( $sql ) + { + list($sql, $args) = $this->sql_args(func_get_args()); + return any_db_query_row_assoc($sql, $args); + } + + + /** + * Perform a query, return the first row + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return array + */ + protected function query_row ( $sql ) + { + list($sql, $args) = $this->sql_args(func_get_args()); + return any_db_query_row($sql, $args); + } + + + /** + * Perform a query, return the first column of the first row + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return mixed + */ + protected function query_one ( $sql ) + { + list($sql, $args) = $this->sql_args(func_get_args()); + return any_db_query_one($sql, $args); + } + + + /** + * Return the number of rows affected in the last query + * + * @return int + */ + protected function query_affected_rows () + { + return any_db_affected_rows(); + } + + + /** + * Return the id of the last inserted row + * + * @return int + */ + protected function query_insert_id () + { + return any_db_insert_id(); + } + + + private function sql_args ( $args ) + { + $sql = array_shift($args); + if (count($args) == 1 && is_array($args[0])) + { + $args = $args[0]; + } + return array($sql, $args); + } + +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/library/store/OAuthStoreMySQL.php b/vendor/oauth-php/library/store/OAuthStoreMySQL.php new file mode 100644 index 0000000..c568359 --- /dev/null +++ b/vendor/oauth-php/library/store/OAuthStoreMySQL.php @@ -0,0 +1,245 @@ +<?php + +/** + * Storage container for the oauth credentials, both server and consumer side. + * Based on MySQL + * + * @version $Id: OAuthStoreMySQL.php 85 2010-02-19 14:56:40Z brunobg@corollarium.com $ + * @author Marc Worrell <marcw@pobox.com> + * @date Nov 16, 2007 4:03:30 PM + * + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +require_once dirname(__FILE__) . '/OAuthStoreSQL.php'; + + +class OAuthStoreMySQL extends OAuthStoreSQL +{ + /** + * The MySQL connection + */ + protected $conn; + + /** + * Initialise the database + */ + public function install () + { + require_once dirname(__FILE__) . '/mysql/install.php'; + } + + + /* ** Some simple helper functions for querying the mysql db ** */ + + /** + * Perform a query, ignore the results + * + * @param string sql + * @param vararg arguments (for sprintf) + */ + protected function query ( $sql ) + { + $sql = $this->sql_printf(func_get_args()); + if (!($res = mysql_query($sql, $this->conn))) + { + $this->sql_errcheck($sql); + } + if (is_resource($res)) + { + mysql_free_result($res); + } + } + + + /** + * Perform a query, ignore the results + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return array + */ + protected function query_all_assoc ( $sql ) + { + $sql = $this->sql_printf(func_get_args()); + if (!($res = mysql_query($sql, $this->conn))) + { + $this->sql_errcheck($sql); + } + $rs = array(); + while ($row = mysql_fetch_assoc($res)) + { + $rs[] = $row; + } + mysql_free_result($res); + return $rs; + } + + + /** + * Perform a query, return the first row + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return array + */ + protected function query_row_assoc ( $sql ) + { + $sql = $this->sql_printf(func_get_args()); + if (!($res = mysql_query($sql, $this->conn))) + { + $this->sql_errcheck($sql); + } + if ($row = mysql_fetch_assoc($res)) + { + $rs = $row; + } + else + { + $rs = false; + } + mysql_free_result($res); + return $rs; + } + + + /** + * Perform a query, return the first row + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return array + */ + protected function query_row ( $sql ) + { + $sql = $this->sql_printf(func_get_args()); + if (!($res = mysql_query($sql, $this->conn))) + { + $this->sql_errcheck($sql); + } + if ($row = mysql_fetch_array($res)) + { + $rs = $row; + } + else + { + $rs = false; + } + mysql_free_result($res); + return $rs; + } + + + /** + * Perform a query, return the first column of the first row + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return mixed + */ + protected function query_one ( $sql ) + { + $sql = $this->sql_printf(func_get_args()); + if (!($res = mysql_query($sql, $this->conn))) + { + $this->sql_errcheck($sql); + } + $val = @mysql_result($res, 0, 0); + mysql_free_result($res); + return $val; + } + + + /** + * Return the number of rows affected in the last query + */ + protected function query_affected_rows () + { + return mysql_affected_rows($this->conn); + } + + + /** + * Return the id of the last inserted row + * + * @return int + */ + protected function query_insert_id () + { + return mysql_insert_id($this->conn); + } + + + protected function sql_printf ( $args ) + { + $sql = array_shift($args); + if (count($args) == 1 && is_array($args[0])) + { + $args = $args[0]; + } + $args = array_map(array($this, 'sql_escape_string'), $args); + return vsprintf($sql, $args); + } + + + protected function sql_escape_string ( $s ) + { + if (is_string($s)) + { + return mysql_real_escape_string($s, $this->conn); + } + else if (is_null($s)) + { + return NULL; + } + else if (is_bool($s)) + { + return intval($s); + } + else if (is_int($s) || is_float($s)) + { + return $s; + } + else + { + return mysql_real_escape_string(strval($s), $this->conn); + } + } + + + protected function sql_errcheck ( $sql ) + { + if (mysql_errno($this->conn)) + { + $msg = "SQL Error in OAuthStoreMySQL: ".mysql_error($this->conn)."\n\n" . $sql; + throw new OAuthException2($msg); + } + } +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/library/store/OAuthStoreMySQLi.php b/vendor/oauth-php/library/store/OAuthStoreMySQLi.php new file mode 100644 index 0000000..09d71bf --- /dev/null +++ b/vendor/oauth-php/library/store/OAuthStoreMySQLi.php @@ -0,0 +1,306 @@ +<?php + +/** + * Storage container for the oauth credentials, both server and consumer side. + * Based on MySQL + * + * @version $Id: OAuthStoreMySQLi.php 64 2009-08-16 19:37:00Z marcw@pobox.com $ + * @author Bruno Barberi Gnecco <brunobg@users.sf.net> Based on code by Marc Worrell <marcw@pobox.com> + * + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Modified from OAuthStoreMySQL to support MySQLi + */ + +require_once dirname(__FILE__) . '/OAuthStoreMySQL.php'; + + +class OAuthStoreMySQLi extends OAuthStoreMySQL +{ + + public function install() { + $sql = file_get_contents(dirname(__FILE__) . '/mysql/mysql.sql'); + $ps = explode('#--SPLIT--', $sql); + + foreach ($ps as $p) + { + $p = preg_replace('/^\s*#.*$/m', '', $p); + + $this->query($p); + $this->sql_errcheck($p); + } + } + + /** + * Construct the OAuthStoreMySQLi. + * In the options you have to supply either: + * - server, username, password and database (for a mysqli_connect) + * - conn (for the connection to be used) + * + * @param array options + */ + function __construct ( $options = array() ) + { + if (isset($options['conn'])) + { + $this->conn = $options['conn']; + } + else + { + if (isset($options['server'])) + { + $server = $options['server']; + $username = $options['username']; + + if (isset($options['password'])) + { + $this->conn = ($GLOBALS["___mysqli_ston"] = mysqli_connect($server, $username, $options['password'])); + } + else + { + $this->conn = ($GLOBALS["___mysqli_ston"] = mysqli_connect($server, $username)); + } + } + else + { + // Try the default mysql connect + $this->conn = ($GLOBALS["___mysqli_ston"] = mysqli_connect()); + } + + if ($this->conn === false) + { + throw new OAuthException2('Could not connect to MySQL database: ' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false))); + } + + if (isset($options['database'])) + { + /* TODO: security. mysqli_ doesn't seem to have an escape identifier function. + $escapeddb = mysqli_real_escape_string($options['database']); + if (!((bool)mysqli_query( $this->conn, "USE `$escapeddb`" ))) + { + $this->sql_errcheck(); + }*/ + } + $this->query('set character set utf8'); + } + } + + /** + * Perform a query, ignore the results + * + * @param string sql + * @param vararg arguments (for sprintf) + */ + protected function query ( $sql ) + { + $sql = $this->sql_printf(func_get_args()); + if (!($res = mysqli_query( $this->conn, $sql))) + { + $this->sql_errcheck($sql); + } + if (!is_bool($res)) + { + ((mysqli_free_result($res) || (is_object($res) && (get_class($res) == "mysqli_result"))) ? true : false); + } + } + + + /** + * Perform a query, ignore the results + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return array + */ + protected function query_all_assoc ( $sql ) + { + $sql = $this->sql_printf(func_get_args()); + if (!($res = mysqli_query( $this->conn, $sql))) + { + $this->sql_errcheck($sql); + } + $rs = array(); + while ($row = mysqli_fetch_assoc($res)) + { + $rs[] = $row; + } + ((mysqli_free_result($res) || (is_object($res) && (get_class($res) == "mysqli_result"))) ? true : false); + return $rs; + } + + + /** + * Perform a query, return the first row + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return array + */ + protected function query_row_assoc ( $sql ) + { + $sql = $this->sql_printf(func_get_args()); + if (!($res = mysqli_query( $this->conn, $sql))) + { + $this->sql_errcheck($sql); + } + if ($row = mysqli_fetch_assoc($res)) + { + $rs = $row; + } + else + { + $rs = false; + } + ((mysqli_free_result($res) || (is_object($res) && (get_class($res) == "mysqli_result"))) ? true : false); + return $rs; + } + + + /** + * Perform a query, return the first row + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return array + */ + protected function query_row ( $sql ) + { + $sql = $this->sql_printf(func_get_args()); + if (!($res = mysqli_query( $this->conn, $sql))) + { + $this->sql_errcheck($sql); + } + if ($row = mysqli_fetch_array($res)) + { + $rs = $row; + } + else + { + $rs = false; + } + ((mysqli_free_result($res) || (is_object($res) && (get_class($res) == "mysqli_result"))) ? true : false); + return $rs; + } + + + /** + * Perform a query, return the first column of the first row + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return mixed + */ + protected function query_one ( $sql ) + { + $sql = $this->sql_printf(func_get_args()); + if (!($res = mysqli_query( $this->conn, $sql))) + { + $this->sql_errcheck($sql); + } + if ($row = mysqli_fetch_assoc($res)) + { + $val = array_pop($row); + } + else + { + $val = false; + } + ((mysqli_free_result($res) || (is_object($res) && (get_class($res) == "mysqli_result"))) ? true : false); + return $val; + } + + + /** + * Return the number of rows affected in the last query + */ + protected function query_affected_rows () + { + return mysqli_affected_rows($this->conn); + } + + + /** + * Return the id of the last inserted row + * + * @return int + */ + protected function query_insert_id () + { + return ((is_null($___mysqli_res = mysqli_insert_id($this->conn))) ? false : $___mysqli_res); + } + + + protected function sql_printf ( $args ) + { + $sql = array_shift($args); + if (count($args) == 1 && is_array($args[0])) + { + $args = $args[0]; + } + $args = array_map(array($this, 'sql_escape_string'), $args); + return vsprintf($sql, $args); + } + + + protected function sql_escape_string ( $s ) + { + if (is_string($s)) + { + return mysqli_real_escape_string( $this->conn, $s); + } + else if (is_null($s)) + { + return NULL; + } + else if (is_bool($s)) + { + return intval($s); + } + else if (is_int($s) || is_float($s)) + { + return $s; + } + else + { + return mysqli_real_escape_string( $this->conn, strval($s)); + } + } + + + protected function sql_errcheck ( $sql ) + { + if (((is_object($this->conn)) ? mysqli_errno($this->conn) : (($___mysqli_res = mysqli_connect_errno()) ? $___mysqli_res : false))) + { + $msg = "SQL Error in OAuthStoreMySQL: ".((is_object($this->conn)) ? mysqli_error($this->conn) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false))."\n\n" . $sql; + throw new OAuthException2($msg); + } + } +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/library/store/OAuthStoreOracle.php b/vendor/oauth-php/library/store/OAuthStoreOracle.php new file mode 100644 index 0000000..ea905a2 --- /dev/null +++ b/vendor/oauth-php/library/store/OAuthStoreOracle.php @@ -0,0 +1,1541 @@ +<?php
+
+/**
+ * Added by Vinay Kant Sahu.
+ * Replaced all the MySQL queries with Oracle SPs. (ref: OAuthStoreSQL.php)
+ * vinaykant.sahu@gmail.com
+ *
+ * Storage container for the oauth credentials, both server and consumer side.
+ * Based on Oracle
+ *
+ * @author Vinay Kant Sahu <vinaykant.sahu@gmail.com>
+ * @date Aug 6, 2010
+ *
+ * The MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+require_once dirname(__FILE__) . '/OAuthStoreAbstract.class.php';
+
+abstract class OAuthStoreOracle extends OAuthStoreAbstract {
+ /**
+ * Maximum delta a timestamp may be off from a previous timestamp.
+ * Allows multiple consumers with some clock skew to work with the same token.
+ * Unit is seconds, default max skew is 10 minutes.
+ */
+ protected $max_timestamp_skew = MAX_TIMESTAMP_SKEW;
+
+ /**
+ * Default ttl for request tokens
+ */
+ protected $max_request_token_ttl = MAX_REQUEST_TOKEN_TIME;
+
+
+ /**
+ * Construct the OAuthStoreMySQL.
+ * In the options you have to supply either:
+ * - server, username, password and database (for a mysql_connect)
+ * - conn (for the connection to be used)
+ *
+ * @param array options
+ */
+ function __construct ( $options = array() ) {
+ if (isset($options['conn'])) {
+ $this->conn = $options['conn'];
+ }
+ else {
+ $this->conn=oci_connect(DBUSER,DBPASSWORD,DBHOST);
+
+ if ($this->conn === false) {
+ throw new OAuthException2('Could not connect to database');
+ }
+
+ // $this->query('set character set utf8');
+ }
+ }
+
+ /**
+ * Find stored credentials for the consumer key and token. Used by an OAuth server
+ * when verifying an OAuth request.
+ *
+ * @param string consumer_key
+ * @param string token
+ * @param string token_type false, 'request' or 'access'
+ * @exception OAuthException2 when no secrets where found
+ * @return array assoc (consumer_secret, token_secret, osr_id, ost_id, user_id)
+ */
+ public function getSecretsForVerify ($consumer_key, $token, $token_type = 'access' ) {
+ $sql = "BEGIN SP_GET_SECRETS_FOR_VERIFY(:P_CONSUMER_KEY, :P_TOKEN, :P_TOKEN_TYPE, :P_ROWS, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_CONSUMER_KEY', $consumer_key, 255);
+ oci_bind_by_name($stmt, ':P_TOKEN', $token, 255);
+ oci_bind_by_name($stmt, ':P_TOKEN_TYPE', $token_type, 255);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+ //Bind the ref cursor
+ $p_row = oci_new_cursor($this->conn);
+ oci_bind_by_name($stmt, ':P_ROWS', $p_row, -1, OCI_B_CURSOR);
+
+ //Execute the statement
+ oci_execute($stmt);
+
+ // treat the ref cursor as a statement resource
+ oci_execute($p_row, OCI_DEFAULT);
+ oci_fetch_all($p_row, $getSecretsForVerifyList, null, null, OCI_FETCHSTATEMENT_BY_ROW);
+
+ $rs =$getSecretsForVerifyList;
+ if (empty($rs)) {
+ throw new OAuthException2('The consumer_key "'.$consumer_key.'" token "'.$token.'" combination does not exist or is not enabled.');
+ }
+
+ return $rs[0];
+ }
+
+
+ /**
+ * Find the server details for signing a request, always looks for an access token.
+ * The returned credentials depend on which local user is making the request.
+ *
+ * The consumer_key must belong to the user or be public (user id is null)
+ *
+ * For signing we need all of the following:
+ *
+ * consumer_key consumer key associated with the server
+ * consumer_secret consumer secret associated with this server
+ * token access token associated with this server
+ * token_secret secret for the access token
+ * signature_methods signing methods supported by the server (array)
+ *
+ * @todo filter on token type (we should know how and with what to sign this request, and there might be old access tokens)
+ * @param string uri uri of the server
+ * @param int user_id id of the logged on user
+ * @param string name (optional) name of the token (case sensitive)
+ * @exception OAuthException2 when no credentials found
+ * @return array
+ */
+ public function getSecretsForSignature ( $uri, $user_id, $name = '' ) {
+ // Find a consumer key and token for the given uri
+ $ps = parse_url($uri);
+ $host = isset($ps['host']) ? $ps['host'] : 'localhost';
+ $path = isset($ps['path']) ? $ps['path'] : '';
+
+ if (empty($path) || substr($path, -1) != '/') {
+ $path .= '/';
+ }
+ //
+ $sql = "BEGIN SP_GET_SECRETS_FOR_SIGNATURE(:P_HOST, :P_PATH, :P_USER_ID, :P_NAME, :P_ROWS, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_HOST', $host, 255);
+ oci_bind_by_name($stmt, ':P_PATH', $path, 255);
+ oci_bind_by_name($stmt, ':P_USER_ID', $user_id, 20);
+ oci_bind_by_name($stmt, ':P_NAME', $name, 255);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+ //Bind the ref cursor
+ $p_row = oci_new_cursor($this->conn);
+ oci_bind_by_name($stmt, ':P_ROWS', $p_row, -1, OCI_B_CURSOR);
+
+ //Execute the statement
+ oci_execute($stmt);
+
+ // treat the ref cursor as a statement resource
+ oci_execute($p_row, OCI_DEFAULT);
+ oci_fetch_all($p_row, $getSecretsForSignatureList, null, null, OCI_FETCHSTATEMENT_BY_ROW);
+ $secrets = $getSecretsForSignatureList[0];
+ //
+ // The owner of the consumer_key is either the user or nobody (public consumer key)
+ /*$secrets = $this->query_row_assoc('
+ SELECT ocr_consumer_key as consumer_key,
+ ocr_consumer_secret as consumer_secret,
+ oct_token as token,
+ oct_token_secret as token_secret,
+ ocr_signature_methods as signature_methods
+ FROM oauth_consumer_registry
+ JOIN oauth_consumer_token ON oct_ocr_id_ref = ocr_id
+ WHERE ocr_server_uri_host = \'%s\'
+ AND ocr_server_uri_path = LEFT(\'%s\', LENGTH(ocr_server_uri_path))
+ AND (ocr_usa_id_ref = %s OR ocr_usa_id_ref IS NULL)
+ AND oct_usa_id_ref = %d
+ AND oct_token_type = \'access\'
+ AND oct_name = \'%s\'
+ AND oct_token_ttl >= NOW()
+ ORDER BY ocr_usa_id_ref DESC, ocr_consumer_secret DESC, LENGTH(ocr_server_uri_path) DESC
+ LIMIT 0,1
+ ', $host, $path, $user_id, $user_id, $name
+ );
+ */
+ if (empty($secrets)) {
+ throw new OAuthException2('No server tokens available for '.$uri);
+ }
+ $secrets['signature_methods'] = explode(',', $secrets['signature_methods']);
+ return $secrets;
+ }
+
+
+ /**
+ * Get the token and token secret we obtained from a server.
+ *
+ * @param string consumer_key
+ * @param string token
+ * @param string token_type
+ * @param int user_id the user owning the token
+ * @param string name optional name for a named token
+ * @exception OAuthException2 when no credentials found
+ * @return array
+ */
+ public function getServerTokenSecrets ($consumer_key,$token,$token_type,$user_id,$name = '')
+ {
+ if ($token_type != 'request' && $token_type != 'access')
+ {
+ throw new OAuthException2('Unkown token type "'.$token_type.'", must be either "request" or "access"');
+ }
+ //
+ $sql = "BEGIN SP_GET_SERVER_TOKEN_SECRETS(:P_CONSUMER_KEY, :P_TOKEN, :P_TOKEN_TYPE, :P_USER_ID, :P_ROWS, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_CONSUMER_KEY', $consumer_key, 255);
+ oci_bind_by_name($stmt, ':P_TOKEN', $token, 255);
+ oci_bind_by_name($stmt, ':P_TOKEN_TYPE', $token_type, 20);
+ oci_bind_by_name($stmt, ':P_USER_ID', $user_id, 255);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+ //Bind the ref cursor
+ $p_row = oci_new_cursor($this->conn);
+ oci_bind_by_name($stmt, ':P_ROWS', $p_row, -1, OCI_B_CURSOR);
+
+ //Execute the statement
+ oci_execute($stmt);
+
+ // treat the ref cursor as a statement resource
+ oci_execute($p_row, OCI_DEFAULT);
+ oci_fetch_all($p_row, $getServerTokenSecretsList, null, null, OCI_FETCHSTATEMENT_BY_ROW);
+ $r=$getServerTokenSecretsList[0];
+ //
+ // Take the most recent token of the given type
+ /*$r = $this->query_row_assoc('
+ SELECT ocr_consumer_key as consumer_key,
+ ocr_consumer_secret as consumer_secret,
+ oct_token as token,
+ oct_token_secret as token_secret,
+ oct_name as token_name,
+ ocr_signature_methods as signature_methods,
+ ocr_server_uri as server_uri,
+ ocr_request_token_uri as request_token_uri,
+ ocr_authorize_uri as authorize_uri,
+ ocr_access_token_uri as access_token_uri,
+ IF(oct_token_ttl >= \'9999-12-31\', NULL, UNIX_TIMESTAMP(oct_token_ttl) - UNIX_TIMESTAMP(NOW())) as token_ttl
+ FROM oauth_consumer_registry
+ JOIN oauth_consumer_token
+ ON oct_ocr_id_ref = ocr_id
+ WHERE ocr_consumer_key = \'%s\'
+ AND oct_token_type = \'%s\'
+ AND oct_token = \'%s\'
+ AND oct_usa_id_ref = %d
+ AND oct_token_ttl >= NOW()
+ ', $consumer_key, $token_type, $token, $user_id
+ );*/
+
+ if (empty($r))
+ {
+ throw new OAuthException2('Could not find a "'.$token_type.'" token for consumer "'.$consumer_key.'" and user '.$user_id);
+ }
+ if (isset($r['signature_methods']) && !empty($r['signature_methods']))
+ {
+ $r['signature_methods'] = explode(',',$r['signature_methods']);
+ }
+ else
+ {
+ $r['signature_methods'] = array();
+ }
+ return $r;
+ }
+
+
+ /**
+ * Add a request token we obtained from a server.
+ *
+ * @todo remove old tokens for this user and this ocr_id
+ * @param string consumer_key key of the server in the consumer registry
+ * @param string token_type one of 'request' or 'access'
+ * @param string token
+ * @param string token_secret
+ * @param int user_id the user owning the token
+ * @param array options extra options, name and token_ttl
+ * @exception OAuthException2 when server is not known
+ * @exception OAuthException2 when we received a duplicate token
+ */
+ public function addServerToken ( $consumer_key, $token_type, $token, $token_secret, $user_id, $options = array() )
+ {
+ if ($token_type != 'request' && $token_type != 'access')
+ {
+ throw new OAuthException2('Unknown token type "'.$token_type.'", must be either "request" or "access"');
+ }
+
+ // Maximum time to live for this token
+ if (isset($options['token_ttl']) && is_numeric($options['token_ttl']))
+ {
+ $ttl = intval($options['token_ttl']);
+ }
+ else if ($token_type == 'request')
+ {
+ $ttl =intval($this->max_request_token_ttl);
+ }
+ else
+ {
+ $ttl = NULL;
+ }
+
+
+
+ // Named tokens, unique per user/consumer key
+ if (isset($options['name']) && $options['name'] != '')
+ {
+ $name = $options['name'];
+ }
+ else
+ {
+ $name = '';
+ }
+ //
+ $sql = "BEGIN SP_ADD_SERVER_TOKEN(:P_CONSUMER_KEY, :P_USER_ID, :P_NAME, :P_TOKEN_TYPE, :P_TOKEN, :P_TOKEN_SECRET, :P_TOKEN_INTERVAL_IN_SEC, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_CONSUMER_KEY', $consumer_key, 255);
+ oci_bind_by_name($stmt, ':P_USER_ID', $user_id, 40);
+ oci_bind_by_name($stmt, ':P_NAME', $name, 255);
+ oci_bind_by_name($stmt, ':P_TOKEN_TYPE', $token_type, 20);
+ oci_bind_by_name($stmt, ':P_TOKEN', $token, 255);
+ oci_bind_by_name($stmt, ':P_TOKEN_SECRET', $token_secret, 255);
+ oci_bind_by_name($stmt, ':P_TOKEN_INTERVAL_IN_SEC', $ttl, 40);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+ //Execute the statement
+ oci_execute($stmt);
+ //
+
+
+
+ if (!$result)
+ {
+ throw new OAuthException2('Received duplicate token "'.$token.'" for the same consumer_key "'.$consumer_key.'"');
+ }
+ }
+
+
+ /**
+ * Delete a server key. This removes access to that site.
+ *
+ * @param string consumer_key
+ * @param int user_id user registering this server
+ * @param boolean user_is_admin
+ */
+ public function deleteServer ( $consumer_key, $user_id, $user_is_admin = false )
+ {
+
+ $sql = "BEGIN SP_DELETE_SERVER(:P_CONSUMER_KEY, :P_USER_ID, :P_USER_IS_ADMIN, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_CONSUMER_KEY', $consumer_key, 255);
+ oci_bind_by_name($stmt, ':P_USER_ID', $user_id, 40);
+ oci_bind_by_name($stmt, ':P_USER_IS_ADMIN', $user_is_admin, 255);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+ //Execute the statement
+ oci_execute($stmt);
+ }
+
+
+ /**
+ * Get a server from the consumer registry using the consumer key
+ *
+ * @param string consumer_key
+ * @param int user_id
+ * @param boolean user_is_admin (optional)
+ * @exception OAuthException2 when server is not found
+ * @return array
+ */
+ public function getServer ( $consumer_key, $user_id, $user_is_admin = false )
+ {
+
+ //
+ $sql = "BEGIN SP_GET_SERVER(:P_CONSUMER_KEY, :P_USER_ID, :P_ROWS, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_CONSUMER_KEY', $consumer_key, 255);
+ oci_bind_by_name($stmt, ':P_USER_ID', $user_id, 40);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+ //Bind the ref cursor
+ $p_row = oci_new_cursor($this->conn);
+ oci_bind_by_name($stmt, ':P_ROWS', $p_row, -1, OCI_B_CURSOR);
+
+ //Execute the statement
+ oci_execute($stmt);
+
+ // treat the ref cursor as a statement resource
+ oci_execute($p_row, OCI_DEFAULT);
+ oci_fetch_all($p_row, $getServerList, null, null, OCI_FETCHSTATEMENT_BY_ROW);
+ $r = $getServerList;
+ //
+ if (empty($r))
+ {
+ throw new OAuthException2('No server with consumer_key "'.$consumer_key.'" has been registered (for this user)');
+ }
+
+ if (isset($r['signature_methods']) && !empty($r['signature_methods']))
+ {
+ $r['signature_methods'] = explode(',',$r['signature_methods']);
+ }
+ else
+ {
+ $r['signature_methods'] = array();
+ }
+ return $r;
+ }
+
+
+
+ /**
+ * Find the server details that might be used for a request
+ *
+ * The consumer_key must belong to the user or be public (user id is null)
+ *
+ * @param string uri uri of the server
+ * @param int user_id id of the logged on user
+ * @exception OAuthException2 when no credentials found
+ * @return array
+ */
+ public function getServerForUri ( $uri, $user_id )
+ {
+ // Find a consumer key and token for the given uri
+ $ps = parse_url($uri);
+ $host = isset($ps['host']) ? $ps['host'] : 'localhost';
+ $path = isset($ps['path']) ? $ps['path'] : '';
+
+ if (empty($path) || substr($path, -1) != '/')
+ {
+ $path .= '/';
+ }
+
+
+ //
+ $sql = "BEGIN SP_GET_SERVER_FOR_URI(:P_HOST, :P_PATH,:P_USER_ID, :P_ROWS, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_HOST', $host, 255);
+ oci_bind_by_name($stmt, ':P_PATH', $path, 255);
+ oci_bind_by_name($stmt, ':P_USER_ID', $user_id, 40);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+ //Bind the ref cursor
+ $p_row = oci_new_cursor($this->conn);
+ oci_bind_by_name($stmt, ':P_ROWS', $p_row, -1, OCI_B_CURSOR);
+
+ //Execute the statement
+ oci_execute($stmt);
+
+ // treat the ref cursor as a statement resource
+ oci_execute($p_row, OCI_DEFAULT);
+ oci_fetch_all($p_row, $getServerForUriList, null, null, OCI_FETCHSTATEMENT_BY_ROW);
+ $server = $getServerForUriList;
+ //
+ if (empty($server))
+ {
+ throw new OAuthException2('No server available for '.$uri);
+ }
+ $server['signature_methods'] = explode(',', $server['signature_methods']);
+ return $server;
+ }
+
+
+ /**
+ * Get a list of all server token this user has access to.
+ *
+ * @param int usr_id
+ * @return array
+ */
+ public function listServerTokens ( $user_id )
+ {
+
+ $sql = "BEGIN SP_LIST_SERVER_TOKENS(:P_USER_ID, :P_ROWS, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_USER_ID', $user_id, 40);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+ //Bind the ref cursor
+ $p_row = oci_new_cursor($this->conn);
+ oci_bind_by_name($stmt, ':P_ROWS', $p_row, -1, OCI_B_CURSOR);
+
+ //Execute the statement
+ oci_execute($stmt);
+
+ // treat the ref cursor as a statement resource
+ oci_execute($p_row, OCI_DEFAULT);
+ oci_fetch_all($p_row, $listServerTokensList, null, null, OCI_FETCHSTATEMENT_BY_ROW);
+ $ts = $listServerTokensList;
+ return $ts;
+ }
+
+
+ /**
+ * Count how many tokens we have for the given server
+ *
+ * @param string consumer_key
+ * @return int
+ */
+ public function countServerTokens ( $consumer_key )
+ {
+
+ //
+ $count =0;
+ $sql = "BEGIN SP_COUNT_SERVICE_TOKENS(:P_CONSUMER_KEY, :P_COUNT, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_CONSUMER_KEY', $consumer_key, 255);
+ oci_bind_by_name($stmt, ':P_COUNT', $count, 40);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+ //Execute the statement
+ oci_execute($stmt);
+ //
+ return $count;
+ }
+
+
+ /**
+ * Get a specific server token for the given user
+ *
+ * @param string consumer_key
+ * @param string token
+ * @param int user_id
+ * @exception OAuthException2 when no such token found
+ * @return array
+ */
+ public function getServerToken ( $consumer_key, $token, $user_id )
+ {
+
+ $sql = "BEGIN SP_GET_SERVER_TOKEN(:P_CONSUMER_KEY, :P_USER_ID,:P_TOKEN, :P_ROWS, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_CONSUMER_KEY', $consumer_key, 255);
+ oci_bind_by_name($stmt, ':P_USER_ID', $user_id, 40);
+ oci_bind_by_name($stmt, ':P_TOKEN', $token, 255);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+ //Bind the ref cursor
+ $p_row = oci_new_cursor($this->conn);
+ oci_bind_by_name($stmt, ':P_ROWS', $p_row, -1, OCI_B_CURSOR);
+
+ //Execute the statement
+ oci_execute($stmt);
+
+ // treat the ref cursor as a statement resource
+ oci_execute($p_row, OCI_DEFAULT);
+ oci_fetch_all($p_row, $getServerTokenList, null, null, OCI_FETCHSTATEMENT_BY_ROW);
+ $ts = $getServerTokenList;
+ //
+
+ if (empty($ts))
+ {
+ throw new OAuthException2('No such consumer key ('.$consumer_key.') and token ('.$token.') combination for user "'.$user_id.'"');
+ }
+ return $ts;
+ }
+
+
+ /**
+ * Delete a token we obtained from a server.
+ *
+ * @param string consumer_key
+ * @param string token
+ * @param int user_id
+ * @param boolean user_is_admin
+ */
+ public function deleteServerToken ( $consumer_key, $token, $user_id, $user_is_admin = false )
+ {
+
+ //
+ $sql = "BEGIN SP_DELETE_SERVER_TOKEN(:P_CONSUMER_KEY, :P_USER_ID,:P_TOKEN, :P_USER_IS_ADMIN, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_CONSUMER_KEY', $consumer_key, 255);
+ oci_bind_by_name($stmt, ':P_USER_ID', $user_id, 40);
+ oci_bind_by_name($stmt, ':P_TOKEN', $token, 255);
+ oci_bind_by_name($stmt, ':P_USER_IS_ADMIN', $user_is_admin, 40);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+ //Execute the statement
+ oci_execute($stmt);
+ //
+
+ }
+
+
+ /**
+ * Set the ttl of a server access token. This is done when the
+ * server receives a valid request with a xoauth_token_ttl parameter in it.
+ *
+ * @param string consumer_key
+ * @param string token
+ * @param int token_ttl
+ */
+ public function setServerTokenTtl ( $consumer_key, $token, $token_ttl, $server_uri = NULL )
+ {
+ if ($token_ttl <= 0)
+ {
+ // Immediate delete when the token is past its ttl
+ $this->deleteServerToken($consumer_key, $token, 0, true);
+ }
+ else if ( $server_uri )
+ {
+ // TODO
+ throw new OAuthException2('server_uri not implemented in Oracle yet, sorry');
+ }
+ else
+ {
+ // Set maximum time to live for this token
+
+ //
+ $sql = "BEGIN SP_SET_SERVER_TOKEN_TTL(:P_TOKEN_TTL, :P_CONSUMER_KEY, :P_TOKEN, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_TOKEN_TTL', $token_ttl, 40);
+ oci_bind_by_name($stmt, ':P_CONSUMER_KEY', $consumer_key, 255);
+ oci_bind_by_name($stmt, ':P_TOKEN', $token, 255);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+ //Execute the statement
+ oci_execute($stmt);
+//
+ }
+ }
+
+
+ /**
+ * Get a list of all consumers from the consumer registry.
+ * The consumer keys belong to the user or are public (user id is null)
+ *
+ * @param string q query term
+ * @param int user_id
+ * @return array
+ */
+ public function listServers ( $q = '', $user_id )
+ {
+ $q = trim(str_replace('%', '', $q));
+ $args = array();
+
+
+ //
+ $sql = "BEGIN SP_LIST_SERVERS(:P_Q, :P_USER_ID, :P_ROWS, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_Q', $q, 255);
+ oci_bind_by_name($stmt, ':P_USER_ID', $user_id, 40);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+ //Bind the ref cursor
+ $p_row = oci_new_cursor($this->conn);
+ oci_bind_by_name($stmt, ':P_ROWS', $p_row, -1, OCI_B_CURSOR);
+
+ //Execute the statement
+ oci_execute($stmt);
+
+ // treat the ref cursor as a statement resource
+ oci_execute($p_row, OCI_DEFAULT);
+ oci_fetch_all($p_row, $listServersList, null, null, OCI_FETCHSTATEMENT_BY_ROW);
+ $servers = $listServersList;
+ //
+
+ return $servers;
+ }
+
+
+ /**
+ * Register or update a server for our site (we will be the consumer)
+ *
+ * (This is the registry at the consumers, registering servers ;-) )
+ *
+ * @param array server
+ * @param int user_id user registering this server
+ * @param boolean user_is_admin
+ * @exception OAuthException2 when fields are missing or on duplicate consumer_key
+ * @return consumer_key
+ */
+ public function updateServer ( $server, $user_id, $user_is_admin = false ) {
+ foreach (array('consumer_key', 'server_uri') as $f) {
+ if (empty($server[$f])) {
+ throw new OAuthException2('The field "'.$f.'" must be set and non empty');
+ }
+ }
+ $parts = parse_url($server['server_uri']);
+ $host = (isset($parts['host']) ? $parts['host'] : 'localhost');
+ $path = (isset($parts['path']) ? $parts['path'] : '/');
+
+ if (isset($server['signature_methods'])) {
+ if (is_array($server['signature_methods'])) {
+ $server['signature_methods'] = strtoupper(implode(',', $server['signature_methods']));
+ }
+ }
+ else {
+ $server['signature_methods'] = '';
+ }
+ // When the user is an admin, then the user can update the user_id of this record
+ if ($user_is_admin && array_key_exists('user_id', $server)) {
+ $flag=1;
+ }
+ if($flag) {
+ if (is_null($server['user_id'])) {
+ $ocr_usa_id_ref= NULL;
+ }
+ else {
+ $ocr_usa_id_ref = $server['user_id'];
+ }
+ }
+ else {
+ $flag=0;
+ $ocr_usa_id_ref=$user_id;
+ }
+ //sp
+ $sql = "BEGIN SP_UPDATE_SERVER(:P_CONSUMER_KEY, :P_USER_ID, :P_OCR_ID, :P_USER_IS_ADMIN,
+ :P_OCR_CONSUMER_SECRET, :P_OCR_SERVER_URI, :P_OCR_SERVER_URI_HOST, :P_OCR_SERVER_URI_PATH,
+ :P_OCR_REQUEST_TOKEN_URI, :P_OCR_AUTHORIZE_URI, :P_OCR_ACCESS_TOKEN_URI, :P_OCR_SIGNATURE_METHODS,
+ :P_OCR_USA_ID_REF, :P_UPDATE_P_OCR_USA_ID_REF_FLAG, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+ $server['request_token_uri'] = isset($server['request_token_uri']) ? $server['request_token_uri'] : '';
+ $server['authorize_uri'] = isset($server['authorize_uri']) ? $server['authorize_uri'] : '';
+ $server['access_token_uri'] = isset($server['access_token_uri']) ? $server['access_token_uri'] : '';
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_CONSUMER_KEY', $server['consumer_key'], 255);
+ oci_bind_by_name($stmt, ':P_USER_ID', $user_id, 40);
+ oci_bind_by_name($stmt, ':P_OCR_ID', $server['id'], 40);
+ oci_bind_by_name($stmt, ':P_USER_IS_ADMIN', $user_is_admin, 40);
+ oci_bind_by_name($stmt, ':P_OCR_CONSUMER_SECRET', $server['consumer_secret'], 255);
+ oci_bind_by_name($stmt, ':P_OCR_SERVER_URI', $server['server_uri'], 255);
+ oci_bind_by_name($stmt, ':P_OCR_SERVER_URI_HOST', strtolower($host), 255);
+ oci_bind_by_name($stmt, ':P_OCR_SERVER_URI_PATH', $path, 255);
+ oci_bind_by_name($stmt, ':P_OCR_REQUEST_TOKEN_URI', $server['request_token_uri'], 255);
+ oci_bind_by_name($stmt, ':P_OCR_AUTHORIZE_URI', $server['authorize_uri'], 255);
+ oci_bind_by_name($stmt, ':P_OCR_ACCESS_TOKEN_URI', $server['access_token_uri'], 255);
+ oci_bind_by_name($stmt, ':P_OCR_SIGNATURE_METHODS', $server['signature_methods'], 255);
+ oci_bind_by_name($stmt, ':P_OCR_USA_ID_REF', $ocr_usa_id_ref, 40);
+ oci_bind_by_name($stmt, ':P_UPDATE_P_OCR_USA_ID_REF_FLAG', $flag, 40);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+ //Execute the statement
+ oci_execute($stmt);
+
+ return $server['consumer_key'];
+ }
+
+ /**
+ * Insert/update a new consumer with this server (we will be the server)
+ * When this is a new consumer, then also generate the consumer key and secret.
+ * Never updates the consumer key and secret.
+ * When the id is set, then the key and secret must correspond to the entry
+ * being updated.
+ *
+ * (This is the registry at the server, registering consumers ;-) )
+ *
+ * @param array consumer
+ * @param int user_id user registering this consumer
+ * @param boolean user_is_admin
+ * @return string consumer key
+ */
+ public function updateConsumer ( $consumer, $user_id, $user_is_admin = false ) {
+ $consumer_key = $this->generateKey(true);
+ $consumer_secret = $this->generateKey();
+
+ $consumer['callback_uri'] = isset($consumer['callback_uri'])? $consumer['callback_uri']: '';
+ $consumer['application_uri'] = isset($consumer['application_uri'])? $consumer['application_uri']: '';
+ $consumer['application_title'] = isset($consumer['application_title'])? $consumer['application_title']: '';
+ $consumer['application_descr'] = isset($consumer['application_descr'])? $consumer['application_descr']: '';
+ $consumer['application_notes'] = isset($consumer['application_notes'])? $consumer['application_notes']: '';
+ $consumer['application_type'] = isset($consumer['application_type'])? $consumer['application_type']: '';
+ $consumer['application_commercial'] = isset($consumer['application_commercial'])?$consumer['application_commercial']:0;
+
+ //sp
+ $sql = "BEGIN SP_UPDATE_CONSUMER(:P_OSR_USA_ID_REF, :P_OSR_CONSUMER_KEY, :P_OSR_CONSUMER_SECRET, :P_OSR_REQUESTER_NAME, :P_OSR_REQUESTER_EMAIL, :P_OSR_CALLBACK_URI, :P_OSR_APPLICATION_URI, :P_OSR_APPLICATION_TITLE , :P_OSR_APPLICATION_DESCR, :P_OSR_APPLICATION_NOTES, :P_OSR_APPLICATION_TYPE, :P_OSR_APPLICATION_COMMERCIAL, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_OSR_USA_ID_REF', $user_id, 40);
+ oci_bind_by_name($stmt, ':P_OSR_CONSUMER_KEY', $consumer_key, 255);
+ oci_bind_by_name($stmt, ':P_OSR_CONSUMER_SECRET', $consumer_secret, 255);
+ oci_bind_by_name($stmt, ':P_OSR_REQUESTER_NAME', $consumer['requester_name'], 255);
+ oci_bind_by_name($stmt, ':P_OSR_REQUESTER_EMAIL', $consumer['requester_email'], 255);
+ oci_bind_by_name($stmt, ':P_OSR_CALLBACK_URI', $consumer['callback_uri'], 255);
+ oci_bind_by_name($stmt, ':P_OSR_APPLICATION_URI', $consumer['application_uri'], 255);
+ oci_bind_by_name($stmt, ':P_OSR_APPLICATION_TITLE', $consumer['application_title'], 255);
+ oci_bind_by_name($stmt, ':P_OSR_APPLICATION_DESCR', $consumer['application_descr'], 255);
+ oci_bind_by_name($stmt, ':P_OSR_APPLICATION_NOTES', $consumer['application_notes'], 255);
+ oci_bind_by_name($stmt, ':P_OSR_APPLICATION_TYPE', $consumer['application_type'], 255);
+ oci_bind_by_name($stmt, ':P_OSR_APPLICATION_COMMERCIAL', $consumer['application_commercial'], 40);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+ //Execute the statement
+ oci_execute($stmt);
+ echo $result;
+ return $consumer_key;
+ }
+
+
+
+ /**
+ * Delete a consumer key. This removes access to our site for all applications using this key.
+ *
+ * @param string consumer_key
+ * @param int user_id user registering this server
+ * @param boolean user_is_admin
+ */
+ public function deleteConsumer ( $consumer_key, $user_id, $user_is_admin = false )
+ {
+
+ //
+ $sql = "BEGIN SP_DELETE_CONSUMER(:P_CONSUMER_KEY, :P_USER_ID, :P_USER_IS_ADMIN, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_CONSUMER_KEY', $consumer_key, 255);
+ oci_bind_by_name($stmt, ':P_USER_ID', $user_id, 40);
+ oci_bind_by_name($stmt, ':P_USER_IS_ADMIN', $user_is_admin, 40);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+ //Execute the statement
+ oci_execute($stmt);
+ //
+ }
+
+
+
+ /**
+ * Fetch a consumer of this server, by consumer_key.
+ *
+ * @param string consumer_key
+ * @param int user_id
+ * @param boolean user_is_admin (optional)
+ * @exception OAuthException2 when consumer not found
+ * @return array
+ */
+ public function getConsumer ( $consumer_key, $user_id, $user_is_admin = false ) {
+
+ $sql = "BEGIN SP_GET_CONSUMER(:P_CONSUMER_KEY, :P_ROWS, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_CONSUMER_KEY', $consumer_key, 255);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+ //Bind the ref cursor
+ $p_row = oci_new_cursor($this->conn);
+ oci_bind_by_name($stmt, ':P_ROWS', $p_row, -1, OCI_B_CURSOR);
+
+ //Execute the statement
+ oci_execute($stmt);
+
+ // treat the ref cursor as a statement resource
+ oci_execute($p_row, OCI_DEFAULT);
+ oci_fetch_all($p_row, $getConsumerList, null, null, OCI_FETCHSTATEMENT_BY_ROW);
+
+ $consumer = $getConsumerList;
+
+ if (!is_array($consumer)) {
+ throw new OAuthException2('No consumer with consumer_key "'.$consumer_key.'"');
+ }
+
+ $c = array();
+ foreach ($consumer as $key => $value) {
+ $c[substr($key, 4)] = $value;
+ }
+ $c['user_id'] = $c['usa_id_ref'];
+
+ if (!$user_is_admin && !empty($c['user_id']) && $c['user_id'] != $user_id) {
+ throw new OAuthException2('No access to the consumer information for consumer_key "'.$consumer_key.'"');
+ }
+ return $c;
+ }
+
+
+ /**
+ * Fetch the static consumer key for this provider. The user for the static consumer
+ * key is NULL (no user, shared key). If the key did not exist then the key is created.
+ *
+ * @return string
+ */
+ public function getConsumerStatic ()
+ {
+
+ //
+ $sql = "BEGIN SP_GET_CONSUMER_STATIC_SELECT(:P_OSR_CONSUMER_KEY, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_OSR_CONSUMER_KEY', $consumer, 255);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+ //Execute the statement
+ oci_execute($stmt);
+
+ if (empty($consumer))
+ {
+ $consumer_key = 'sc-'.$this->generateKey(true);
+
+ $sql = "BEGIN SP_CONSUMER_STATIC_SAVE(:P_OSR_CONSUMER_KEY, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_OSR_CONSUMER_KEY', $consumer_key, 255);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+ //Execute the statement
+ oci_execute($stmt);
+
+
+ // Just make sure that if the consumer key is truncated that we get the truncated string
+ $consumer = $consumer_key;
+ }
+ return $consumer;
+ }
+
+
+ /**
+ * Add an unautorized request token to our server.
+ *
+ * @param string consumer_key
+ * @param array options (eg. token_ttl)
+ * @return array (token, token_secret)
+ */
+ public function addConsumerRequestToken ( $consumer_key, $options = array() )
+ {
+ $token = $this->generateKey(true);
+ $secret = !isset($options['secret']) ? $this->generateKey() : $options['secret'];
+
+
+ if (isset($options['token_ttl']) && is_numeric($options['token_ttl']))
+ {
+ $ttl = intval($options['token_ttl']);
+ }
+ else
+ {
+ $ttl = $this->max_request_token_ttl;
+ }
+
+ if (!isset($options['oauth_callback'])) {
+ // 1.0a Compatibility : store callback url associated with request token
+ $options['oauth_callback']='oob';
+ }
+ $options_oauth_callback =$options['oauth_callback'];
+ $sql = "BEGIN SP_ADD_CONSUMER_REQUEST_TOKEN(:P_TOKEN_TTL, :P_CONSUMER_KEY, :P_TOKEN, :P_TOKEN_SECRET, :P_CALLBACK_URL, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_TOKEN_TTL', $ttl, 20);
+ oci_bind_by_name($stmt, ':P_CONSUMER_KEY', $consumer_key, 255);
+ oci_bind_by_name($stmt, ':P_TOKEN', $token, 255);
+ oci_bind_by_name($stmt, ':P_TOKEN_SECRET', $secret, 255);
+ oci_bind_by_name($stmt, ':P_CALLBACK_URL', $options_oauth_callback, 255);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+ //Execute the statement
+ oci_execute($stmt);
+
+
+ $returnArray= array('token'=>$token, 'token_secret'=>$secret, 'token_ttl'=>$ttl);
+ return $returnArray;
+ }
+
+
+ /**
+ * Fetch the consumer request token, by request token.
+ *
+ * @param string token
+ * @return array token and consumer details
+ */
+ public function getConsumerRequestToken ( $token )
+ {
+
+ $sql = "BEGIN SP_GET_CONSUMER_REQUEST_TOKEN(:P_TOKEN, :P_ROWS, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_TOKEN', $token, 255);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+ //Bind the ref cursor
+ $p_row = oci_new_cursor($this->conn);
+ oci_bind_by_name($stmt, ':P_ROWS', $p_row, -1, OCI_B_CURSOR);
+
+ //Execute the statement
+ oci_execute($stmt);
+
+ // treat the ref cursor as a statement resource
+ oci_execute($p_row, OCI_DEFAULT);
+
+ oci_fetch_all($p_row, $rs, null, null, OCI_FETCHSTATEMENT_BY_ROW);
+
+ return $rs[0];
+ }
+
+
+ /**
+ * Delete a consumer token. The token must be a request or authorized token.
+ *
+ * @param string token
+ */
+ public function deleteConsumerRequestToken ( $token )
+ {
+
+ $sql = "BEGIN SP_DEL_CONSUMER_REQUEST_TOKEN(:P_TOKEN, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_TOKEN', $token, 255);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+ //Execute the statement
+ oci_execute($stmt);
+ }
+
+
+ /**
+ * Upgrade a request token to be an authorized request token.
+ *
+ * @param string token
+ * @param int user_id user authorizing the token
+ * @param string referrer_host used to set the referrer host for this token, for user feedback
+ */
+ public function authorizeConsumerRequestToken ( $token, $user_id, $referrer_host = '' )
+ {
+ // 1.0a Compatibility : create a token verifier
+ $verifier = substr(md5(rand()),0,10);
+
+ $sql = "BEGIN SP_AUTH_CONSUMER_REQ_TOKEN(:P_USER_ID, :P_REFERRER_HOST, :P_VERIFIER, :P_TOKEN, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_USER_ID', $user_id, 255);
+ oci_bind_by_name($stmt, ':P_REFERRER_HOST', $referrer_host, 255);
+ oci_bind_by_name($stmt, ':P_VERIFIER', $verifier, 255);
+ oci_bind_by_name($stmt, ':P_TOKEN', $token, 255);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+
+ //Execute the statement
+ oci_execute($stmt);
+
+ return $verifier;
+ }
+
+
+ /**
+ * Count the consumer access tokens for the given consumer.
+ *
+ * @param string consumer_key
+ * @return int
+ */
+ public function countConsumerAccessTokens ( $consumer_key )
+ {
+ /*$count = $this->query_one('
+ SELECT COUNT(ost_id)
+ FROM oauth_server_token
+ JOIN oauth_server_registry
+ ON ost_osr_id_ref = osr_id
+ WHERE ost_token_type = \'access\'
+ AND osr_consumer_key = \'%s\'
+ AND ost_token_ttl >= NOW()
+ ', $consumer_key);
+ */
+ $sql = "BEGIN SP_COUNT_CONSUMER_ACCESS_TOKEN(:P_CONSUMER_KEY, :P_COUNT, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_CONSUMER_KEY', $consumer_key, 255);
+ oci_bind_by_name($stmt, ':P_COUNT', $count, 20);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+
+ //Execute the statement
+ oci_execute($stmt);
+
+ return $count;
+ }
+
+
+ /**
+ * Exchange an authorized request token for new access token.
+ *
+ * @param string token
+ * @param array options options for the token, token_ttl
+ * @exception OAuthException2 when token could not be exchanged
+ * @return array (token, token_secret)
+ */
+ public function exchangeConsumerRequestForAccessToken ( $token, $options = array() )
+ {
+ $new_token = $this->generateKey(true);
+ $new_secret = $this->generateKey();
+
+ $sql = "BEGIN SP_EXCH_CONS_REQ_FOR_ACC_TOKEN(:P_TOKEN_TTL, :P_NEW_TOKEN, :P_TOKEN, :P_TOKEN_SECRET, :P_VERIFIER, :P_OUT_TOKEN_TTL, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_TOKEN_TTL', $options['token_ttl'], 255);
+ oci_bind_by_name($stmt, ':P_NEW_TOKEN', $new_token, 255);
+ oci_bind_by_name($stmt, ':P_TOKEN', $token, 255);
+ oci_bind_by_name($stmt, ':P_TOKEN_SECRET', $new_secret, 255);
+ oci_bind_by_name($stmt, ':P_VERIFIER', $options['verifier'], 255);
+ oci_bind_by_name($stmt, ':P_OUT_TOKEN_TTL', $ttl, 255);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+
+ //Execute the statement
+ oci_execute($stmt);
+
+ $ret = array('token' => $new_token, 'token_secret' => $new_secret);
+ if (is_numeric($ttl))
+ {
+ $ret['token_ttl'] = intval($ttl);
+ }
+ return $ret;
+ }
+
+
+ /**
+ * Fetch the consumer access token, by access token.
+ *
+ * @param string token
+ * @param int user_id
+ * @exception OAuthException2 when token is not found
+ * @return array token and consumer details
+ */
+ public function getConsumerAccessToken ( $token, $user_id )
+ {
+
+ $sql = "BEGIN SP_GET_CONSUMER_ACCESS_TOKEN(:P_USER_ID, :P_TOKEN, :P_ROWS :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_USER_ID',$user_id, 255);
+ oci_bind_by_name($stmt, ':P_TOKEN', $token, 255);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+ //Bind the ref cursor
+ $p_row = oci_new_cursor($this->conn);
+ oci_bind_by_name($stmt, ':P_ROWS', $p_row, -1, OCI_B_CURSOR);
+
+ //Execute the statement
+ oci_execute($stmt);
+
+ // treat the ref cursor as a statement resource
+ oci_execute($p_row, OCI_DEFAULT);
+ oci_fetch_all($p_row, $rs, null, null, OCI_FETCHSTATEMENT_BY_ROW);
+ if (empty($rs))
+ {
+ throw new OAuthException2('No server_token "'.$token.'" for user "'.$user_id.'"');
+ }
+ return $rs;
+ }
+
+
+ /**
+ * Delete a consumer access token.
+ *
+ * @param string token
+ * @param int user_id
+ * @param boolean user_is_admin
+ */
+ public function deleteConsumerAccessToken ( $token, $user_id, $user_is_admin = false )
+ {
+ /*if ($user_is_admin)
+ {
+ $this->query('
+ DELETE FROM oauth_server_token
+ WHERE ost_token = \'%s\'
+ AND ost_token_type = \'access\'
+ ', $token);
+ }
+ else
+ {
+ $this->query('
+ DELETE FROM oauth_server_token
+ WHERE ost_token = \'%s\'
+ AND ost_token_type = \'access\'
+ AND ost_usa_id_ref = %d
+ ', $token, $user_id);
+ }*/
+ $sql = "BEGIN SP_DEL_CONSUMER_ACCESS_TOKEN(:P_USER_ID, :P_TOKEN, :P_USER_IS_ADMIN, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_USER_ID', $user_id, 255);
+ oci_bind_by_name($stmt, ':P_TOKEN', $token, 255);
+ oci_bind_by_name($stmt, ':P_USER_IS_ADMIN', $user_is_admin, 20);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+
+ //Execute the statement
+ oci_execute($stmt);
+ }
+
+
+ /**
+ * Set the ttl of a consumer access token. This is done when the
+ * server receives a valid request with a xoauth_token_ttl parameter in it.
+ *
+ * @param string token
+ * @param int ttl
+ */
+ public function setConsumerAccessTokenTtl ( $token, $token_ttl )
+ {
+ if ($token_ttl <= 0)
+ {
+ // Immediate delete when the token is past its ttl
+ $this->deleteConsumerAccessToken($token, 0, true);
+ }
+ else
+ {
+ // Set maximum time to live for this token
+
+
+ $sql = "BEGIN SP_SET_CONSUMER_ACC_TOKEN_TTL(:P_TOKEN, :P_TOKEN_TTL, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_TOKEN', $token, 255);
+ oci_bind_by_name($stmt, ':P_TOKEN_TTL', $token_ttl, 20);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+
+ //Execute the statement
+ oci_execute($stmt);
+ }
+ }
+
+
+ /**
+ * Fetch a list of all consumer keys, secrets etc.
+ * Returns the public (user_id is null) and the keys owned by the user
+ *
+ * @param int user_id
+ * @return array
+ */
+ public function listConsumers ( $user_id )
+ {
+
+ $sql = "BEGIN SP_LIST_CONSUMERS(:P_USER_ID, :P_ROWS, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_USER_ID', $user_id, 255);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+ //Bind the ref cursor
+ $p_row = oci_new_cursor($this->conn);
+ oci_bind_by_name($stmt, ':P_ROWS', $p_row, -1, OCI_B_CURSOR);
+
+ //Execute the statement
+ oci_execute($stmt);
+
+ // treat the ref cursor as a statement resource
+ oci_execute($p_row, OCI_DEFAULT);
+ oci_fetch_all($p_row, $rs, null, null, OCI_FETCHSTATEMENT_BY_ROW);
+
+ return $rs;
+ }
+
+ /**
+ * List of all registered applications. Data returned has not sensitive
+ * information and therefore is suitable for public displaying.
+ *
+ * @param int $begin
+ * @param int $total
+ * @return array
+ */
+ public function listConsumerApplications($begin = 0, $total = 25)
+ {
+ // TODO
+ return array();
+ }
+
+ /**
+ * Fetch a list of all consumer tokens accessing the account of the given user.
+ *
+ * @param int user_id
+ * @return array
+ */
+ public function listConsumerTokens ( $user_id )
+ {
+
+ $sql = "BEGIN SP_LIST_CONSUMER_TOKENS(:P_USER_ID, :P_ROWS, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_USER_ID', $user_id, 255);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+ //Bind the ref cursor
+ $p_row = oci_new_cursor($this->conn);
+ oci_bind_by_name($stmt, ':P_ROWS', $p_row, -1, OCI_B_CURSOR);
+
+ //Execute the statement
+ oci_execute($stmt);
+
+ // treat the ref cursor as a statement resource
+ oci_execute($p_row, OCI_DEFAULT);
+ oci_fetch_all($p_row, $rs, null, null, OCI_FETCHSTATEMENT_BY_ROW);
+
+ return $rs;
+ }
+
+
+ /**
+ * Check an nonce/timestamp combination. Clears any nonce combinations
+ * that are older than the one received.
+ *
+ * @param string consumer_key
+ * @param string token
+ * @param int timestamp
+ * @param string nonce
+ * @exception OAuthException2 thrown when the timestamp is not in sequence or nonce is not unique
+ */
+ public function checkServerNonce ( $consumer_key, $token, $timestamp, $nonce )
+ {
+
+ $sql = "BEGIN SP_CHECK_SERVER_NONCE(:P_CONSUMER_KEY, :P_TOKEN, :P_TIMESTAMP, :P_MAX_TIMESTAMP_SKEW, :P_NONCE, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_CONSUMER_KEY', $consumer_key, 255);
+ oci_bind_by_name($stmt, ':P_TOKEN', $token, 255);
+ oci_bind_by_name($stmt, ':P_TIMESTAMP', $timestamp, 255);
+ oci_bind_by_name($stmt, ':P_MAX_TIMESTAMP_SKEW', $this->max_timestamp_skew, 20);
+ oci_bind_by_name($stmt, ':P_NONCE', $nonce, 255);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+
+ //Execute the statement
+ oci_execute($stmt);
+
+ }
+
+
+ /**
+ * Add an entry to the log table
+ *
+ * @param array keys (osr_consumer_key, ost_token, ocr_consumer_key, oct_token)
+ * @param string received
+ * @param string sent
+ * @param string base_string
+ * @param string notes
+ * @param int (optional) user_id
+ */
+ public function addLog ( $keys, $received, $sent, $base_string, $notes, $user_id = null )
+ {
+ $args = array();
+ $ps = array();
+ foreach ($keys as $key => $value)
+ {
+ $args[] = $value;
+ $ps[] = "olg_$key = '%s'";
+ }
+
+ if (!empty($_SERVER['REMOTE_ADDR']))
+ {
+ $remote_ip = $_SERVER['REMOTE_ADDR'];
+ }
+ else if (!empty($_SERVER['REMOTE_IP']))
+ {
+ $remote_ip = $_SERVER['REMOTE_IP'];
+ }
+ else
+ {
+ $remote_ip = '0.0.0.0';
+ }
+
+ // Build the SQL
+ $olg_received = $this->makeUTF8($received);
+ $olg_sent = $this->makeUTF8($sent);
+ $olg_base_string = $base_string;
+ $olg_notes = $this->makeUTF8($notes);
+ $olg_usa_id_ref = $user_id;
+ $olg_remote_ip = $remote_ip;
+
+
+
+ $sql = "BEGIN SP_ADD_LOG(:P_RECEIVED, :P_SENT, :P_BASE_STRING, :P_NOTES, :P_USA_ID_REF, :P_REMOTE_IP, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_RECEIVED', $olg_received, 255);
+ oci_bind_by_name($stmt, ':P_SENT', $olg_sent, 255);
+ oci_bind_by_name($stmt, ':P_BASE_STRING', $olg_base_string, 255);
+ oci_bind_by_name($stmt, ':P_NOTES', $olg_notes, 255);
+ oci_bind_by_name($stmt, ':P_USA_ID_REF', $olg_usa_id_ref, 255);
+ oci_bind_by_name($stmt, ':P_REMOTE_IP', $olg_remote_ip, 255);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+
+ //Execute the statement
+ oci_execute($stmt);
+ }
+
+
+ /**
+ * Get a page of entries from the log. Returns the last 100 records
+ * matching the options given.
+ *
+ * @param array options
+ * @param int user_id current user
+ * @return array log records
+ */
+ public function listLog ( $options, $user_id )
+ {
+
+ if (empty($options))
+ {
+ $optionsFlag=NULL;
+
+ }
+ else
+ {
+ $optionsFlag=1;
+
+ }
+
+ $sql = "BEGIN SP_LIST_LOG(:P_OPTION_FLAG, :P_USA_ID, :P_OSR_CONSUMER_KEY, :P_OCR_CONSUMER_KEY, :P_OST_TOKEN, :P_OCT_TOKEN, :P_ROWS, :P_RESULT); END;";
+
+ // parse sql
+ $stmt = oci_parse($this->conn, $sql) or die ('Can not parse query');
+
+ // Bind In and Out Variables
+ oci_bind_by_name($stmt, ':P_OPTION_FLAG', $optionsFlag, 255);
+ oci_bind_by_name($stmt, ':P_USA_ID', $user_id, 40);
+ oci_bind_by_name($stmt, ':P_OSR_CONSUMER_KEY', $options['osr_consumer_key'], 255);
+ oci_bind_by_name($stmt, ':P_OCR_CONSUMER_KEY', $options['ocr_consumer_key'], 255);
+ oci_bind_by_name($stmt, ':P_OST_TOKEN', $options['ost_token'], 255);
+ oci_bind_by_name($stmt, ':P_OCT_TOKEN', $options['oct_token'], 255);
+ oci_bind_by_name($stmt, ':P_RESULT', $result, 20);
+
+ //Bind the ref cursor
+ $p_row = oci_new_cursor($this->conn);
+ oci_bind_by_name($stmt, ':P_ROWS', $p_row, -1, OCI_B_CURSOR);
+
+ //Execute the statement
+ oci_execute($stmt);
+
+ // treat the ref cursor as a statement resource
+ oci_execute($p_row, OCI_DEFAULT);
+ oci_fetch_all($p_row, $rs, null, null, OCI_FETCHSTATEMENT_BY_ROW);
+
+ return $rs;
+ }
+
+ /**
+ * Initialise the database
+ */
+ public function install ()
+ {
+ require_once dirname(__FILE__) . '/oracle/install.php';
+ }
+}
+
+
+/* vi:set ts=4 sts=4 sw=4 binary noeol: */
+
+?>
\ No newline at end of file diff --git a/vendor/oauth-php/library/store/OAuthStorePDO.php b/vendor/oauth-php/library/store/OAuthStorePDO.php new file mode 100644 index 0000000..aa3a1b9 --- /dev/null +++ b/vendor/oauth-php/library/store/OAuthStorePDO.php @@ -0,0 +1,274 @@ +<?php + +/** + * Storage container for the oauth credentials, both server and consumer side. + * Based on MySQL + * + * @version $Id: OAuthStorePDO.php 64 2009-08-16 19:37:00Z marcw@pobox.com $ + * @author Bruno Barberi Gnecco <brunobg@users.sf.net> Based on code by Marc Worrell <marcw@pobox.com> + * + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +require_once dirname(__FILE__) . '/OAuthStoreSQL.php'; + + +class OAuthStorePDO extends OAuthStoreSQL +{ + private $conn; // PDO connection + private $lastaffectedrows; + + /** + * Construct the OAuthStorePDO. + * In the options you have to supply either: + * - dsn, username, password and database (for a new PDO connection) + * - conn (for the connection to be used) + * + * @param array options + */ + function __construct ( $options = array() ) + { + if (isset($options['conn'])) + { + $this->conn = $options['conn']; + } + else if (isset($options['dsn'])) + { + try + { + $this->conn = new PDO($options['dsn'], $options['username'], @$options['password']); + } + catch (PDOException $e) + { + throw new OAuthException2('Could not connect to PDO database: ' . $e->getMessage()); + } + + $this->query('set character set utf8'); + } + } + + /** + * Perform a query, ignore the results + * + * @param string sql + * @param vararg arguments (for sprintf) + */ + protected function query ( $sql ) + { + $sql = $this->sql_printf(func_get_args()); + try + { + $this->lastaffectedrows = $this->conn->exec($sql); + if ($this->lastaffectedrows === FALSE) { + $this->sql_errcheck($sql); + } + } + catch (PDOException $e) + { + $this->sql_errcheck($sql); + } + } + + + /** + * Perform a query, ignore the results + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return array + */ + protected function query_all_assoc ( $sql ) + { + $sql = $this->sql_printf(func_get_args()); + $result = array(); + + try + { + $stmt = $this->conn->query($sql); + + $result = $stmt->fetchAll(PDO::FETCH_ASSOC); + } + catch (PDOException $e) + { + $this->sql_errcheck($sql); + } + return $result; + } + + + /** + * Perform a query, return the first row + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return array + */ + protected function query_row_assoc ( $sql ) + { + $sql = $this->sql_printf(func_get_args()); + $result = $this->query_all_assoc($sql); + $val = array_pop($result); + return $val; + } + + + /** + * Perform a query, return the first row + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return array + */ + protected function query_row ( $sql ) + { + $sql = $this->sql_printf(func_get_args()); + try + { + $all = $this->conn->query($sql, PDO::FETCH_NUM); + $row = array(); + foreach ($all as $r) { + $row = $r; + break; + } + } + catch (PDOException $e) + { + $this->sql_errcheck($sql); + } + return $row; + } + + + /** + * Perform a query, return the first column of the first row + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return mixed + */ + protected function query_one ( $sql ) + { + $sql = $this->sql_printf(func_get_args()); + $row = $this->query_row($sql); + $val = array_pop($row); + return $val; + } + + + /** + * Return the number of rows affected in the last query + */ + protected function query_affected_rows () + { + return $this->lastaffectedrows; + } + + + /** + * Return the id of the last inserted row + * + * @return int + */ + protected function query_insert_id () + { + return $this->conn->lastInsertId(); + } + + + protected function sql_printf ( $args ) + { + $sql = array_shift($args); + if (count($args) == 1 && is_array($args[0])) + { + $args = $args[0]; + } + $args = array_map(array($this, 'sql_escape_string'), $args); + return vsprintf($sql, $args); + } + + + protected function sql_escape_string ( $s ) + { + if (is_string($s)) + { + $s = $this->conn->quote($s); + // kludge. Quote already adds quotes, and this conflicts with OAuthStoreSQL. + // so remove the quotes + $len = strlen($s); + if ($len == 0) + return $s; + + $startcut = 0; + while (isset($s[$startcut]) && $s[$startcut] == '\'') + $startcut++; + + $endcut = $len-1; + while (isset($s[$endcut]) && $s[$endcut] == '\'') + $endcut--; + + $s = substr($s, $startcut, $endcut-$startcut+1); + return $s; + } + else if (is_null($s)) + { + return NULL; + } + else if (is_bool($s)) + { + return intval($s); + } + else if (is_int($s) || is_float($s)) + { + return $s; + } + else + { + return $this->conn->quote(strval($s)); + } + } + + + protected function sql_errcheck ( $sql ) + { + $msg = "SQL Error in OAuthStoreMySQL: ". print_r($this->conn->errorInfo(), true) ."\n\n" . $sql; + $backtrace = debug_backtrace(); + $msg .= "\n\nAt file " . $backtrace[1]['file'] . ", line " . $backtrace[1]['line']; + throw new OAuthException2($msg); + } + + /** + * Initialise the database + */ + public function install () + { + // TODO: this depends on mysql extension + require_once dirname(__FILE__) . '/mysql/install.php'; + } + +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?> diff --git a/vendor/oauth-php/library/store/OAuthStorePostgreSQL.php b/vendor/oauth-php/library/store/OAuthStorePostgreSQL.php new file mode 100644 index 0000000..db55804 --- /dev/null +++ b/vendor/oauth-php/library/store/OAuthStorePostgreSQL.php @@ -0,0 +1,1981 @@ +<?php +/** + * OAuthStorePostgreSQL.php + * + * PHP Version 5.2 + * + * @author Elma R&D Team <rdteam@elma.fr> + * @link http://elma.fr + * + * @Id 2010-10-22 10:07:18 ndelanoe $ + * @version $Id: OAuthStorePostgreSQL.php 190 2011-03-22 09:16:01Z scherpenisse $ + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + **/ + +require_once dirname(__FILE__) . '/OAuthStoreAbstract.class.php'; + + +class OAuthStorePostgreSQL extends OAuthStoreAbstract +{ + /** + * Maximum delta a timestamp may be off from a previous timestamp. + * Allows multiple consumers with some clock skew to work with the same token. + * Unit is seconds, default max skew is 10 minutes. + */ + protected $max_timestamp_skew = 600; + + /** + * Default ttl for request tokens + */ + protected $max_request_token_ttl = 3600; + + /** + * Number of affected rowsby the last queries + */ + private $_lastAffectedRows = 0; + + public function install() + { + throw new OAuthException2('Not yet implemented, see postgresql/pgsql.sql'); + } + + /** + * Construct the OAuthStorePostgrSQL. + * In the options you have to supply either: + * - server, username, password and database (for a pg_connect) + * - connectionString (for a pg_connect) + * - conn (for the connection to be used) + * + * @param array options + */ + function __construct ( $options = array() ) + { + if (isset($options['conn'])) + { + $this->conn = $options['conn']; + } + else + { + if (isset($options['server'])) + { + $host = $options['server']; + $user = $options['username']; + $dbname = $options['database']; + + $connectionString = sprintf('host=%s dbname=%s user=%s', $host, $dbname, $user); + + if (isset($options['password'])) + { + $connectionString .= ' password=' . $options['password']; + } + + $this->conn = pg_connect($connectionString); + } + elseif (isset($options['connectionString'])) + { + $this->conn = pg_connect($options['connectionString']); + } + else { + + // Try the default pg connect + $this->conn = pg_connect(); + } + + if ($this->conn === false) + { + throw new OAuthException2('Could not connect to PostgresSQL database'); + } + } + } + + /** + * Find stored credentials for the consumer key and token. Used by an OAuth server + * when verifying an OAuth request. + * + * @param string consumer_key + * @param string token + * @param string token_type false, 'request' or 'access' + * @exception OAuthException2 when no secrets where found + * @return array assoc (consumer_secret, token_secret, osr_id, ost_id, user_id) + */ + public function getSecretsForVerify ( $consumer_key, $token, $token_type = 'access' ) + { + if ($token_type === false) + { + $rs = $this->query_row_assoc(' + SELECT osr_id, + osr_consumer_key as consumer_key, + osr_consumer_secret as consumer_secret + FROM oauth_server_registry + WHERE osr_consumer_key = \'%s\' + AND osr_enabled = \'1\' + ', + $consumer_key); + + if ($rs) + { + $rs['token'] = false; + $rs['token_secret'] = false; + $rs['user_id'] = false; + $rs['ost_id'] = false; + } + } + else + { + $rs = $this->query_row_assoc(' + SELECT osr_id, + ost_id, + ost_usa_id_ref as user_id, + osr_consumer_key as consumer_key, + osr_consumer_secret as consumer_secret, + ost_token as token, + ost_token_secret as token_secret + FROM oauth_server_registry + JOIN oauth_server_token + ON ost_osr_id_ref = osr_id + WHERE ost_token_type = \'%s\' + AND osr_consumer_key = \'%s\' + AND ost_token = \'%s\' + AND osr_enabled = \'1\' + AND ost_token_ttl >= NOW() + ', + $token_type, $consumer_key, $token); + } + + if (empty($rs)) + { + throw new OAuthException2('The consumer_key "'.$consumer_key.'" token "'.$token.'" combination does not exist or is not enabled.'); + } + return $rs; + } + + /** + * Find the server details for signing a request, always looks for an access token. + * The returned credentials depend on which local user is making the request. + * + * The consumer_key must belong to the user or be public (user id is null) + * + * For signing we need all of the following: + * + * consumer_key consumer key associated with the server + * consumer_secret consumer secret associated with this server + * token access token associated with this server + * token_secret secret for the access token + * signature_methods signing methods supported by the server (array) + * + * @todo filter on token type (we should know how and with what to sign this request, and there might be old access tokens) + * @param string uri uri of the server + * @param int user_id id of the logged on user + * @param string name (optional) name of the token (case sensitive) + * @exception OAuthException2 when no credentials found + * @return array + */ + public function getSecretsForSignature ( $uri, $user_id, $name = '' ) + { + // Find a consumer key and token for the given uri + $ps = parse_url($uri); + $host = isset($ps['host']) ? $ps['host'] : 'localhost'; + $path = isset($ps['path']) ? $ps['path'] : ''; + + if (empty($path) || substr($path, -1) != '/') + { + $path .= '/'; + } + + // The owner of the consumer_key is either the user or nobody (public consumer key) + $secrets = $this->query_row_assoc(' + SELECT ocr_consumer_key as consumer_key, + ocr_consumer_secret as consumer_secret, + oct_token as token, + oct_token_secret as token_secret, + ocr_signature_methods as signature_methods + FROM oauth_consumer_registry + JOIN oauth_consumer_token ON oct_ocr_id_ref = ocr_id + WHERE ocr_server_uri_host = \'%s\' + AND ocr_server_uri_path = SUBSTR(\'%s\', 1, LENGTH(ocr_server_uri_path)) + AND (ocr_usa_id_ref = \'%s\' OR ocr_usa_id_ref IS NULL) + AND oct_usa_id_ref = \'%d\' + AND oct_token_type = \'access\' + AND oct_name = \'%s\' + AND oct_token_ttl >= NOW() + ORDER BY ocr_usa_id_ref DESC, ocr_consumer_secret DESC, LENGTH(ocr_server_uri_path) DESC + LIMIT 1 + ', $host, $path, $user_id, $user_id, $name + ); + + if (empty($secrets)) + { + throw new OAuthException2('No server tokens available for '.$uri); + } + $secrets['signature_methods'] = explode(',', $secrets['signature_methods']); + return $secrets; + } + + /** + * Get the token and token secret we obtained from a server. + * + * @param string consumer_key + * @param string token + * @param string token_type + * @param int user_id the user owning the token + * @param string name optional name for a named token + * @exception OAuthException2 when no credentials found + * @return array + */ + public function getServerTokenSecrets ( $consumer_key, $token, $token_type, $user_id, $name = '' ) + { + if ($token_type != 'request' && $token_type != 'access') + { + throw new OAuthException2('Unkown token type "'.$token_type.'", must be either "request" or "access"'); + } + + // Take the most recent token of the given type + $r = $this->query_row_assoc(' + SELECT ocr_consumer_key as consumer_key, + ocr_consumer_secret as consumer_secret, + oct_token as token, + oct_token_secret as token_secret, + oct_name as token_name, + ocr_signature_methods as signature_methods, + ocr_server_uri as server_uri, + ocr_request_token_uri as request_token_uri, + ocr_authorize_uri as authorize_uri, + ocr_access_token_uri as access_token_uri, + CASE WHEN oct_token_ttl >= \'9999-12-31\' THEN NULL ELSE oct_token_ttl - NOW() END as token_ttl + FROM oauth_consumer_registry + JOIN oauth_consumer_token + ON oct_ocr_id_ref = ocr_id + WHERE ocr_consumer_key = \'%s\' + AND oct_token_type = \'%s\' + AND oct_token = \'%s\' + AND oct_usa_id_ref = \'%d\' + AND oct_token_ttl >= NOW() + ', $consumer_key, $token_type, $token, $user_id + ); + + if (empty($r)) + { + throw new OAuthException2('Could not find a "'.$token_type.'" token for consumer "'.$consumer_key.'" and user '.$user_id); + } + if (isset($r['signature_methods']) && !empty($r['signature_methods'])) + { + $r['signature_methods'] = explode(',',$r['signature_methods']); + } + else + { + $r['signature_methods'] = array(); + } + return $r; + } + + + /** + * Add a request token we obtained from a server. + * + * @todo remove old tokens for this user and this ocr_id + * @param string consumer_key key of the server in the consumer registry + * @param string token_type one of 'request' or 'access' + * @param string token + * @param string token_secret + * @param int user_id the user owning the token + * @param array options extra options, name and token_ttl + * @exception OAuthException2 when server is not known + * @exception OAuthException2 when we received a duplicate token + */ + public function addServerToken ( $consumer_key, $token_type, $token, $token_secret, $user_id, $options = array() ) + { + if ($token_type != 'request' && $token_type != 'access') + { + throw new OAuthException2('Unknown token type "'.$token_type.'", must be either "request" or "access"'); + } + + // Maximum time to live for this token + if (isset($options['token_ttl']) && is_numeric($options['token_ttl'])) + { + $ttl = 'NOW() + INTERVAL \''.intval($options['token_ttl']).' SECOND\''; + } + else if ($token_type == 'request') + { + $ttl = 'NOW() + INTERVAL \''.$this->max_request_token_ttl.' SECOND\''; + } + else + { + $ttl = "'9999-12-31'"; + } + + if (isset($options['server_uri'])) + { + $ocr_id = $this->query_one(' + SELECT ocr_id + FROM oauth_consumer_registry + WHERE ocr_consumer_key = \'%s\' + AND ocr_usa_id_ref = \'%d\' + AND ocr_server_uri = \'%s\' + ', $consumer_key, $user_id, $options['server_uri']); + } + else + { + $ocr_id = $this->query_one(' + SELECT ocr_id + FROM oauth_consumer_registry + WHERE ocr_consumer_key = \'%s\' + AND ocr_usa_id_ref = \'%d\' + ', $consumer_key, $user_id); + } + + if (empty($ocr_id)) + { + throw new OAuthException2('No server associated with consumer_key "'.$consumer_key.'"'); + } + + // Named tokens, unique per user/consumer key + if (isset($options['name']) && $options['name'] != '') + { + $name = $options['name']; + } + else + { + $name = ''; + } + + // Delete any old tokens with the same type and name for this user/server combination + $this->query(' + DELETE FROM oauth_consumer_token + WHERE oct_ocr_id_ref = %d + AND oct_usa_id_ref = \'%d\' + AND oct_token_type::text = LOWER(\'%s\')::text + AND oct_name = \'%s\' + ', + $ocr_id, + $user_id, + $token_type, + $name); + + // Insert the new token + $this->query(' + INSERT INTO + oauth_consumer_token( + oct_ocr_id_ref, + oct_usa_id_ref, + oct_name, + oct_token, + oct_token_secret, + oct_token_type, + oct_timestamp, + oct_token_ttl + ) + VALUES (%d,%d,\'%s\',\'%s\',\'%s\',\'%s\',NOW(),'.$ttl.')', + $ocr_id, + $user_id, + $name, + $token, + $token_secret, + $token_type); + + if (!$this->query_affected_rows()) + { + throw new OAuthException2('Received duplicate token "'.$token.'" for the same consumer_key "'.$consumer_key.'"'); + } + } + + /** + * Delete a server key. This removes access to that site. + * + * @param string consumer_key + * @param int user_id user registering this server + * @param boolean user_is_admin + */ + public function deleteServer ( $consumer_key, $user_id, $user_is_admin = false ) + { + if ($user_is_admin) + { + $this->query(' + DELETE FROM oauth_consumer_registry + WHERE ocr_consumer_key = \'%s\' + AND (ocr_usa_id_ref = \'%d\' OR ocr_usa_id_ref IS NULL) + ', $consumer_key, $user_id); + } + else + { + $this->query(' + DELETE FROM oauth_consumer_registry + WHERE ocr_consumer_key = \'%s\' + AND ocr_usa_id_ref = \'%d\' + ', $consumer_key, $user_id); + } + } + + + /** + * Get a server from the consumer registry using the consumer key + * + * @param string consumer_key + * @param int user_id + * @param boolean user_is_admin (optional) + * @exception OAuthException2 when server is not found + * @return array + */ + public function getServer ( $consumer_key, $user_id, $user_is_admin = false ) + { + $r = $this->query_row_assoc(' + SELECT ocr_id as id, + ocr_usa_id_ref as user_id, + ocr_consumer_key as consumer_key, + ocr_consumer_secret as consumer_secret, + ocr_signature_methods as signature_methods, + ocr_server_uri as server_uri, + ocr_request_token_uri as request_token_uri, + ocr_authorize_uri as authorize_uri, + ocr_access_token_uri as access_token_uri + FROM oauth_consumer_registry + WHERE ocr_consumer_key = \'%s\' + AND (ocr_usa_id_ref = \'%d\' OR ocr_usa_id_ref IS NULL) + ', $consumer_key, $user_id); + + if (empty($r)) + { + throw new OAuthException2('No server with consumer_key "'.$consumer_key.'" has been registered (for this user)'); + } + + if (isset($r['signature_methods']) && !empty($r['signature_methods'])) + { + $r['signature_methods'] = explode(',',$r['signature_methods']); + } + else + { + $r['signature_methods'] = array(); + } + return $r; + } + + + /** + * Find the server details that might be used for a request + * + * The consumer_key must belong to the user or be public (user id is null) + * + * @param string uri uri of the server + * @param int user_id id of the logged on user + * @exception OAuthException2 when no credentials found + * @return array + */ + public function getServerForUri ( $uri, $user_id ) + { + // Find a consumer key and token for the given uri + $ps = parse_url($uri); + $host = isset($ps['host']) ? $ps['host'] : 'localhost'; + $path = isset($ps['path']) ? $ps['path'] : ''; + + if (empty($path) || substr($path, -1) != '/') + { + $path .= '/'; + } + + // The owner of the consumer_key is either the user or nobody (public consumer key) + $server = $this->query_row_assoc(' + SELECT ocr_id as id, + ocr_usa_id_ref as user_id, + ocr_consumer_key as consumer_key, + ocr_consumer_secret as consumer_secret, + ocr_signature_methods as signature_methods, + ocr_server_uri as server_uri, + ocr_request_token_uri as request_token_uri, + ocr_authorize_uri as authorize_uri, + ocr_access_token_uri as access_token_uri + FROM oauth_consumer_registry + WHERE ocr_server_uri_host = \'%s\' + AND ocr_server_uri_path = SUBSTR(\'%s\', 1, LENGTH(ocr_server_uri_path)) + AND (ocr_usa_id_ref = \'%s\' OR ocr_usa_id_ref IS NULL) + ORDER BY ocr_usa_id_ref DESC, consumer_secret DESC, LENGTH(ocr_server_uri_path) DESC + LIMIT 1 + ', $host, $path, $user_id + ); + + if (empty($server)) + { + throw new OAuthException2('No server available for '.$uri); + } + $server['signature_methods'] = explode(',', $server['signature_methods']); + return $server; + } + + /** + * Get a list of all server token this user has access to. + * + * @param int usr_id + * @return array + */ + public function listServerTokens ( $user_id ) + { + $ts = $this->query_all_assoc(' + SELECT ocr_consumer_key as consumer_key, + ocr_consumer_secret as consumer_secret, + oct_id as token_id, + oct_token as token, + oct_token_secret as token_secret, + oct_usa_id_ref as user_id, + ocr_signature_methods as signature_methods, + ocr_server_uri as server_uri, + ocr_server_uri_host as server_uri_host, + ocr_server_uri_path as server_uri_path, + ocr_request_token_uri as request_token_uri, + ocr_authorize_uri as authorize_uri, + ocr_access_token_uri as access_token_uri, + oct_timestamp as timestamp + FROM oauth_consumer_registry + JOIN oauth_consumer_token + ON oct_ocr_id_ref = ocr_id + WHERE oct_usa_id_ref = \'%d\' + AND oct_token_type = \'access\' + AND oct_token_ttl >= NOW() + ORDER BY ocr_server_uri_host, ocr_server_uri_path + ', $user_id); + return $ts; + } + + /** + * Count how many tokens we have for the given server + * + * @param string consumer_key + * @return int + */ + public function countServerTokens ( $consumer_key ) + { + $count = $this->query_one(' + SELECT COUNT(oct_id) + FROM oauth_consumer_token + JOIN oauth_consumer_registry + ON oct_ocr_id_ref = ocr_id + WHERE oct_token_type = \'access\' + AND ocr_consumer_key = \'%s\' + AND oct_token_ttl >= NOW() + ', $consumer_key); + + return $count; + } + + /** + * Get a specific server token for the given user + * + * @param string consumer_key + * @param string token + * @param int user_id + * @exception OAuthException2 when no such token found + * @return array + */ + public function getServerToken ( $consumer_key, $token, $user_id ) + { + $ts = $this->query_row_assoc(' + SELECT ocr_consumer_key as consumer_key, + ocr_consumer_secret as consumer_secret, + oct_token as token, + oct_token_secret as token_secret, + oct_usa_id_ref as usr_id, + ocr_signature_methods as signature_methods, + ocr_server_uri as server_uri, + ocr_server_uri_host as server_uri_host, + ocr_server_uri_path as server_uri_path, + ocr_request_token_uri as request_token_uri, + ocr_authorize_uri as authorize_uri, + ocr_access_token_uri as access_token_uri, + oct_timestamp as timestamp + FROM oauth_consumer_registry + JOIN oauth_consumer_token + ON oct_ocr_id_ref = ocr_id + WHERE ocr_consumer_key = \'%s\' + AND oct_usa_id_ref = \'%d\' + AND oct_token_type = \'access\' + AND oct_token = \'%s\' + AND oct_token_ttl >= NOW() + ', $consumer_key, $user_id, $token); + + if (empty($ts)) + { + throw new OAuthException2('No such consumer key ('.$consumer_key.') and token ('.$token.') combination for user "'.$user_id.'"'); + } + return $ts; + } + + + /** + * Delete a token we obtained from a server. + * + * @param string consumer_key + * @param string token + * @param int user_id + * @param boolean user_is_admin + */ + public function deleteServerToken ( $consumer_key, $token, $user_id, $user_is_admin = false ) + { + if ($user_is_admin) + { + $this->query(' + DELETE FROM oauth_consumer_token + USING oauth_consumer_registry + WHERE + oct_ocr_id_ref = ocr_id + AND ocr_consumer_key = \'%s\' + AND oct_token = \'%s\' + ', $consumer_key, $token); + } + else + { + $this->query(' + DELETE FROM oauth_consumer_token + USING oauth_consumer_registry + WHERE + oct_ocr_id_ref = ocr_id + AND ocr_consumer_key = \'%s\' + AND oct_token = \'%s\' + AND oct_usa_id_ref = \'%d\' + ', $consumer_key, $token, $user_id); + } + } + + /** + * Set the ttl of a server access token. This is done when the + * server receives a valid request with a xoauth_token_ttl parameter in it. + * + * @param string consumer_key + * @param string token + * @param int token_ttl + */ + public function setServerTokenTtl ( $consumer_key, $token, $token_ttl, $server_uri = NULL ) + { + if ($token_ttl <= 0) + { + // Immediate delete when the token is past its ttl + $this->deleteServerToken($consumer_key, $token, 0, true); + } + else if ( $server_uri ) + { + // Set maximum time to live for this token + $this->query(' + UPDATE oauth_consumer_token + SET ost_token_ttl = (NOW() + INTERVAL \'%d SECOND\') + WHERE ocr_consumer_key = \'%s\' + AND ocr_server_uri = \'%s\' + AND oct_ocr_id_ref = ocr_id + AND oct_token = \'%s\' + ', $token_ttl, $server_uri, $consumer_key, $token); + + // Set maximum time to live for this token + $this->query(' + UPDATE oauth_consumer_registry + SET ost_token_ttl = (NOW() + INTERVAL \'%d SECOND\') + WHERE ocr_consumer_key = \'%s\' + AND ocr_server_uri = \'%s\' + AND oct_ocr_id_ref = ocr_id + AND oct_token = \'%s\' + ', $token_ttl, $server_uri, $consumer_key, $token); + } + else + { + // Set maximum time to live for this token + $this->query(' + UPDATE oauth_consumer_token + SET ost_token_ttl = (NOW() + INTERVAL \'%d SECOND\') + WHERE ocr_consumer_key = \'%s\' + AND oct_ocr_id_ref = ocr_id + AND oct_token = \'%s\' + ', $token_ttl, $consumer_key, $token); + + // Set maximum time to live for this token + $this->query(' + UPDATE oauth_consumer_registry + SET ost_token_ttl = (NOW() + INTERVAL \'%d SECOND\') + WHERE ocr_consumer_key = \'%s\' + AND oct_ocr_id_ref = ocr_id + AND oct_token = \'%s\' + ', $token_ttl, $consumer_key, $token); + } + } + + /** + * Get a list of all consumers from the consumer registry. + * The consumer keys belong to the user or are public (user id is null) + * + * @param string q query term + * @param int user_id + * @return array + */ + public function listServers ( $q = '', $user_id ) + { + $q = trim(str_replace('%', '', $q)); + $args = array(); + + if (!empty($q)) + { + $where = ' WHERE ( ocr_consumer_key like \'%%%s%%\' + OR ocr_server_uri like \'%%%s%%\' + OR ocr_server_uri_host like \'%%%s%%\' + OR ocr_server_uri_path like \'%%%s%%\') + AND (ocr_usa_id_ref = \'%d\' OR ocr_usa_id_ref IS NULL) + '; + + $args[] = $q; + $args[] = $q; + $args[] = $q; + $args[] = $q; + $args[] = $user_id; + } + else + { + $where = ' WHERE ocr_usa_id_ref = \'%d\' OR ocr_usa_id_ref IS NULL'; + $args[] = $user_id; + } + + $servers = $this->query_all_assoc(' + SELECT ocr_id as id, + ocr_usa_id_ref as user_id, + ocr_consumer_key as consumer_key, + ocr_consumer_secret as consumer_secret, + ocr_signature_methods as signature_methods, + ocr_server_uri as server_uri, + ocr_server_uri_host as server_uri_host, + ocr_server_uri_path as server_uri_path, + ocr_request_token_uri as request_token_uri, + ocr_authorize_uri as authorize_uri, + ocr_access_token_uri as access_token_uri + FROM oauth_consumer_registry + '.$where.' + ORDER BY ocr_server_uri_host, ocr_server_uri_path + ', $args); + return $servers; + } + + /** + * Register or update a server for our site (we will be the consumer) + * + * (This is the registry at the consumers, registering servers ;-) ) + * + * @param array server + * @param int user_id user registering this server + * @param boolean user_is_admin + * @exception OAuthException2 when fields are missing or on duplicate consumer_key + * @return consumer_key + */ + public function updateServer ( $server, $user_id, $user_is_admin = false ) + { + foreach (array('consumer_key', 'server_uri') as $f) + { + if (empty($server[$f])) + { + throw new OAuthException2('The field "'.$f.'" must be set and non empty'); + } + } + + if (!empty($server['id'])) + { + $exists = $this->query_one(' + SELECT ocr_id + FROM oauth_consumer_registry + WHERE ocr_consumer_key = \'%s\' + AND ocr_id <> %d + AND (ocr_usa_id_ref = \'%d\' OR ocr_usa_id_ref IS NULL) + ', $server['consumer_key'], $server['id'], $user_id); + } + else + { + $exists = $this->query_one(' + SELECT ocr_id + FROM oauth_consumer_registry + WHERE ocr_consumer_key = \'%s\' + AND (ocr_usa_id_ref = \'%d\' OR ocr_usa_id_ref IS NULL) + ', $server['consumer_key'], $user_id); + } + + if ($exists) + { + throw new OAuthException2('The server with key "'.$server['consumer_key'].'" has already been registered'); + } + + $parts = parse_url($server['server_uri']); + $host = (isset($parts['host']) ? $parts['host'] : 'localhost'); + $path = (isset($parts['path']) ? $parts['path'] : '/'); + + if (isset($server['signature_methods'])) + { + if (is_array($server['signature_methods'])) + { + $server['signature_methods'] = strtoupper(implode(',', $server['signature_methods'])); + } + } + else + { + $server['signature_methods'] = ''; + } + + // When the user is an admin, then the user can update the user_id of this record + if ($user_is_admin && array_key_exists('user_id', $server)) + { + if (is_null($server['user_id'])) + { + $update_user = ', ocr_usa_id_ref = NULL'; + } + else + { + $update_user = ', ocr_usa_id_ref = \''. intval($server['user_id']) . '\''; + } + } + else + { + $update_user = ''; + } + + if (!empty($server['id'])) + { + // Check if the current user can update this server definition + if (!$user_is_admin) + { + $ocr_usa_id_ref = $this->query_one(' + SELECT ocr_usa_id_ref + FROM oauth_consumer_registry + WHERE ocr_id = %d + ', $server['id']); + + if ($ocr_usa_id_ref != $user_id) + { + throw new OAuthException2('The user "'.$user_id.'" is not allowed to update this server'); + } + } + + // Update the consumer registration + $this->query(' + UPDATE oauth_consumer_registry + SET ocr_consumer_key = \'%s\', + ocr_consumer_secret = \'%s\', + ocr_server_uri = \'%s\', + ocr_server_uri_host = \'%s\', + ocr_server_uri_path = \'%s\', + ocr_timestamp = NOW(), + ocr_request_token_uri = \'%s\', + ocr_authorize_uri = \'%s\', + ocr_access_token_uri = \'%s\', + ocr_signature_methods = \'%s\' + '.$update_user.' + WHERE ocr_id = %d + ', + $server['consumer_key'], + $server['consumer_secret'], + $server['server_uri'], + strtolower($host), + $path, + isset($server['request_token_uri']) ? $server['request_token_uri'] : '', + isset($server['authorize_uri']) ? $server['authorize_uri'] : '', + isset($server['access_token_uri']) ? $server['access_token_uri'] : '', + $server['signature_methods'], + $server['id'] + ); + } + else + { + $update_user_field = ''; + $update_user_value = ''; + if (empty($update_user)) + { + // Per default the user owning the key is the user registering the key + $update_user_field = ', ocr_usa_id_ref'; + $update_user_value = ', ' . intval($user_id); + } + + $this->query(' + INSERT INTO oauth_consumer_registry ( + ocr_consumer_key , + ocr_consumer_secret , + ocr_server_uri , + ocr_server_uri_host , + ocr_server_uri_path , + ocr_timestamp , + ocr_request_token_uri, + ocr_authorize_uri , + ocr_access_token_uri , + ocr_signature_methods' . $update_user_field . ' + ) + VALUES (\'%s\', \'%s\', \'%s\', \'%s\', \'%s\', NOW(), \'%s\', \'%s\', \'%s\', \'%s\''. $update_user_value . ')', + $server['consumer_key'], + $server['consumer_secret'], + $server['server_uri'], + strtolower($host), + $path, + isset($server['request_token_uri']) ? $server['request_token_uri'] : '', + isset($server['authorize_uri']) ? $server['authorize_uri'] : '', + isset($server['access_token_uri']) ? $server['access_token_uri'] : '', + $server['signature_methods'] + ); + + $ocr_id = $this->query_insert_id('oauth_consumer_registry', 'ocr_id'); + } + return $server['consumer_key']; + } + + + /** + * Insert/update a new consumer with this server (we will be the server) + * When this is a new consumer, then also generate the consumer key and secret. + * Never updates the consumer key and secret. + * When the id is set, then the key and secret must correspond to the entry + * being updated. + * + * (This is the registry at the server, registering consumers ;-) ) + * + * @param array consumer + * @param int user_id user registering this consumer + * @param boolean user_is_admin + * @return string consumer key + */ + public function updateConsumer ( $consumer, $user_id, $user_is_admin = false ) + { + if (!$user_is_admin) + { + foreach (array('requester_name', 'requester_email') as $f) + { + if (empty($consumer[$f])) + { + throw new OAuthException2('The field "'.$f.'" must be set and non empty'); + } + } + } + + if (!empty($consumer['id'])) + { + if (empty($consumer['consumer_key'])) + { + throw new OAuthException2('The field "consumer_key" must be set and non empty'); + } + if (!$user_is_admin && empty($consumer['consumer_secret'])) + { + throw new OAuthException2('The field "consumer_secret" must be set and non empty'); + } + + // Check if the current user can update this server definition + if (!$user_is_admin) + { + $osr_usa_id_ref = $this->query_one(' + SELECT osr_usa_id_ref + FROM oauth_server_registry + WHERE osr_id = %d + ', $consumer['id']); + + if ($osr_usa_id_ref != $user_id) + { + throw new OAuthException2('The user "'.$user_id.'" is not allowed to update this consumer'); + } + } + else + { + // User is an admin, allow a key owner to be changed or key to be shared + if (array_key_exists('user_id',$consumer)) + { + if (is_null($consumer['user_id'])) + { + $this->query(' + UPDATE oauth_server_registry + SET osr_usa_id_ref = NULL + WHERE osr_id = %d + ', $consumer['id']); + } + else + { + $this->query(' + UPDATE oauth_server_registry + SET osr_usa_id_ref = \'%d\' + WHERE osr_id = %d + ', $consumer['user_id'], $consumer['id']); + } + } + } + + $this->query(' + UPDATE oauth_server_registry + SET osr_requester_name = \'%s\', + osr_requester_email = \'%s\', + osr_callback_uri = \'%s\', + osr_application_uri = \'%s\', + osr_application_title = \'%s\', + osr_application_descr = \'%s\', + osr_application_notes = \'%s\', + osr_application_type = \'%s\', + osr_application_commercial = IF(%d,\'1\',\'0\'), + osr_timestamp = NOW() + WHERE osr_id = %d + AND osr_consumer_key = \'%s\' + AND osr_consumer_secret = \'%s\' + ', + $consumer['requester_name'], + $consumer['requester_email'], + isset($consumer['callback_uri']) ? $consumer['callback_uri'] : '', + isset($consumer['application_uri']) ? $consumer['application_uri'] : '', + isset($consumer['application_title']) ? $consumer['application_title'] : '', + isset($consumer['application_descr']) ? $consumer['application_descr'] : '', + isset($consumer['application_notes']) ? $consumer['application_notes'] : '', + isset($consumer['application_type']) ? $consumer['application_type'] : '', + isset($consumer['application_commercial']) ? $consumer['application_commercial'] : 0, + $consumer['id'], + $consumer['consumer_key'], + $consumer['consumer_secret'] + ); + + + $consumer_key = $consumer['consumer_key']; + } + else + { + $consumer_key = $this->generateKey(true); + $consumer_secret= $this->generateKey(); + + // When the user is an admin, then the user can be forced to something else that the user + if ($user_is_admin && array_key_exists('user_id',$consumer)) + { + if (is_null($consumer['user_id'])) + { + $owner_id = 'NULL'; + } + else + { + $owner_id = intval($consumer['user_id']); + } + } + else + { + // No admin, take the user id as the owner id. + $owner_id = intval($user_id); + } + + $this->query(' + INSERT INTO oauth_server_registry ( + osr_enabled, + osr_status, + osr_usa_id_ref, + osr_consumer_key, + osr_consumer_secret, + osr_requester_name, + osr_requester_email, + osr_callback_uri, + osr_application_uri, + osr_application_title, + osr_application_descr, + osr_application_notes, + osr_application_type, + osr_application_commercial, + osr_timestamp, + osr_issue_date + ) + VALUES (\'1\', \'active\', \'%s\', \'%s\', \'%s\', \'%s\', \'%s\', \'%s\', \'%s\', \'%s\', \'%s\', \'%s\', \'%s\', \'%d\', NOW(), NOW()) + ', + $owner_id, + $consumer_key, + $consumer_secret, + $consumer['requester_name'], + $consumer['requester_email'], + isset($consumer['callback_uri']) ? $consumer['callback_uri'] : '', + isset($consumer['application_uri']) ? $consumer['application_uri'] : '', + isset($consumer['application_title']) ? $consumer['application_title'] : '', + isset($consumer['application_descr']) ? $consumer['application_descr'] : '', + isset($consumer['application_notes']) ? $consumer['application_notes'] : '', + isset($consumer['application_type']) ? $consumer['application_type'] : '', + isset($consumer['application_commercial']) ? $consumer['application_commercial'] : 0 + ); + } + return $consumer_key; + + } + + /** + * Delete a consumer key. This removes access to our site for all applications using this key. + * + * @param string consumer_key + * @param int user_id user registering this server + * @param boolean user_is_admin + */ + public function deleteConsumer ( $consumer_key, $user_id, $user_is_admin = false ) + { + if ($user_is_admin) + { + $this->query(' + DELETE FROM oauth_server_registry + WHERE osr_consumer_key = \'%s\' + AND (osr_usa_id_ref = \'%d\' OR osr_usa_id_ref IS NULL) + ', $consumer_key, $user_id); + } + else + { + $this->query(' + DELETE FROM oauth_server_registry + WHERE osr_consumer_key = \'%s\' + AND osr_usa_id_ref = \'%d\' + ', $consumer_key, $user_id); + } + } + + /** + * Fetch a consumer of this server, by consumer_key. + * + * @param string consumer_key + * @param int user_id + * @param boolean user_is_admin (optional) + * @exception OAuthException2 when consumer not found + * @return array + */ + public function getConsumer ( $consumer_key, $user_id, $user_is_admin = false ) + { + $consumer = $this->query_row_assoc(' + SELECT * + FROM oauth_server_registry + WHERE osr_consumer_key = \'%s\' + ', $consumer_key); + + if (!is_array($consumer)) + { + throw new OAuthException2('No consumer with consumer_key "'.$consumer_key.'"'); + } + + $c = array(); + foreach ($consumer as $key => $value) + { + $c[substr($key, 4)] = $value; + } + $c['user_id'] = $c['usa_id_ref']; + + if (!$user_is_admin && !empty($c['user_id']) && $c['user_id'] != $user_id) + { + throw new OAuthException2('No access to the consumer information for consumer_key "'.$consumer_key.'"'); + } + return $c; + } + + + /** + * Fetch the static consumer key for this provider. The user for the static consumer + * key is NULL (no user, shared key). If the key did not exist then the key is created. + * + * @return string + */ + public function getConsumerStatic () + { + $consumer = $this->query_one(' + SELECT osr_consumer_key + FROM oauth_server_registry + WHERE osr_consumer_key LIKE \'sc-%%\' + AND osr_usa_id_ref IS NULL + '); + + if (empty($consumer)) + { + $consumer_key = 'sc-'.$this->generateKey(true); + $this->query(' + INSERT INTO oauth_server_registry ( + osr_enabled, + osr_status, + osr_usa_id_ref, + osr_consumer_key, + osr_consumer_secret, + osr_requester_name, + osr_requester_email, + osr_callback_uri, + osr_application_uri, + osr_application_title, + osr_application_descr, + osr_application_notes, + osr_application_type, + osr_application_commercial, + osr_timestamp, + osr_issue_date + ) + VALUES (\'1\',\'active\', NULL, \'%s\', \'\', \'\', \'\', \'\', \'\', \'Static shared consumer key\', \'\', \'Static shared consumer key\', \'\', 0, NOW(), NOW()) + ', + $consumer_key + ); + + // Just make sure that if the consumer key is truncated that we get the truncated string + $consumer = $this->getConsumerStatic(); + } + return $consumer; + } + + /** + * Add an unautorized request token to our server. + * + * @param string consumer_key + * @param array options (eg. token_ttl) + * @return array (token, token_secret) + */ + public function addConsumerRequestToken ( $consumer_key, $options = array() ) + { + $token = $this->generateKey(true); + $secret = !isset($options['secret']) ? $this->generateKey() : $options['secret']; + $osr_id = $this->query_one(' + SELECT osr_id + FROM oauth_server_registry + WHERE osr_consumer_key = \'%s\' + AND osr_enabled = \'1\' + ', $consumer_key); + + if (!$osr_id) + { + throw new OAuthException2('No server with consumer_key "'.$consumer_key.'" or consumer_key is disabled'); + } + + if (isset($options['token_ttl']) && is_numeric($options['token_ttl'])) + { + $ttl = intval($options['token_ttl']); + } + else + { + $ttl = $this->max_request_token_ttl; + } + + if (!isset($options['oauth_callback'])) { + // 1.0a Compatibility : store callback url associated with request token + $options['oauth_callback']='oob'; + } + + $this->query(' + INSERT INTO oauth_server_token ( + ost_osr_id_ref, + ost_usa_id_ref, + ost_token, + ost_token_secret, + ost_token_type, + ost_token_ttl, + ost_callback_url + ) + VALUES (%d, \'1\', \'%s\', \'%s\', \'request\', NOW() + INTERVAL \'%d SECOND\', \'%s\')', + $osr_id, $token, $secret, $ttl, $options['oauth_callback']); + + return array('token'=>$token, 'token_secret'=>$secret, 'token_ttl'=>$ttl); + } + + /** + * Fetch the consumer request token, by request token. + * + * @param string token + * @return array token and consumer details + */ + public function getConsumerRequestToken ( $token ) + { + $rs = $this->query_row_assoc(' + SELECT ost_token as token, + ost_token_secret as token_secret, + osr_consumer_key as consumer_key, + osr_consumer_secret as consumer_secret, + ost_token_type as token_type, + ost_callback_url as callback_url, + osr_application_title as application_title, + osr_application_descr as application_descr, + osr_application_uri as application_uri + FROM oauth_server_token + JOIN oauth_server_registry + ON ost_osr_id_ref = osr_id + WHERE ost_token_type = \'request\' + AND ost_token = \'%s\' + AND ost_token_ttl >= NOW() + ', $token); + + return $rs; + } + + /** + * Delete a consumer token. The token must be a request or authorized token. + * + * @param string token + */ + public function deleteConsumerRequestToken ( $token ) + { + $this->query(' + DELETE FROM oauth_server_token + WHERE ost_token = \'%s\' + AND ost_token_type = \'request\' + ', $token); + } + + /** + * Upgrade a request token to be an authorized request token. + * + * @param string token + * @param int user_id user authorizing the token + * @param string referrer_host used to set the referrer host for this token, for user feedback + */ + public function authorizeConsumerRequestToken ( $token, $user_id, $referrer_host = '' ) + { + // 1.0a Compatibility : create a token verifier + $verifier = substr(md5(rand()),0,10); + + $this->query(' + UPDATE oauth_server_token + SET ost_authorized = \'1\', + ost_usa_id_ref = \'%d\', + ost_timestamp = NOW(), + ost_referrer_host = \'%s\', + ost_verifier = \'%s\' + WHERE ost_token = \'%s\' + AND ost_token_type = \'request\' + ', $user_id, $referrer_host, $verifier, $token); + return $verifier; + } + + /** + * Count the consumer access tokens for the given consumer. + * + * @param string consumer_key + * @return int + */ + public function countConsumerAccessTokens ( $consumer_key ) + { + $count = $this->query_one(' + SELECT COUNT(ost_id) + FROM oauth_server_token + JOIN oauth_server_registry + ON ost_osr_id_ref = osr_id + WHERE ost_token_type = \'access\' + AND osr_consumer_key = \'%s\' + AND ost_token_ttl >= NOW() + ', $consumer_key); + + return $count; + } + + /** + * Exchange an authorized request token for new access token. + * + * @param string token + * @param array options options for the token, token_ttl + * @exception OAuthException2 when token could not be exchanged + * @return array (token, token_secret) + */ + public function exchangeConsumerRequestForAccessToken ( $token, $options = array() ) + { + $new_token = $this->generateKey(true); + $new_secret = $this->generateKey(); + + // Maximum time to live for this token + if (isset($options['token_ttl']) && is_numeric($options['token_ttl'])) + { + $ttl_sql = '(NOW() + INTERVAL \''.intval($options['token_ttl']).' SECOND\')'; + } + else + { + $ttl_sql = "'9999-12-31'"; + } + + if (isset($options['verifier'])) { + $verifier = $options['verifier']; + + // 1.0a Compatibility : check token against oauth_verifier + $this->query(' + UPDATE oauth_server_token + SET ost_token = \'%s\', + ost_token_secret = \'%s\', + ost_token_type = \'access\', + ost_timestamp = NOW(), + ost_token_ttl = '.$ttl_sql.' + WHERE ost_token = \'%s\' + AND ost_token_type = \'request\' + AND ost_authorized = \'1\' + AND ost_token_ttl >= NOW() + AND ost_verifier = \'%s\' + ', $new_token, $new_secret, $token, $verifier); + } else { + + // 1.0 + $this->query(' + UPDATE oauth_server_token + SET ost_token = \'%s\', + ost_token_secret = \'%s\', + ost_token_type = \'access\', + ost_timestamp = NOW(), + ost_token_ttl = '.$ttl_sql.' + WHERE ost_token = \'%s\' + AND ost_token_type = \'request\' + AND ost_authorized = \'1\' + AND ost_token_ttl >= NOW() + ', $new_token, $new_secret, $token); + } + + if ($this->query_affected_rows() != 1) + { + throw new OAuthException2('Can\'t exchange request token "'.$token.'" for access token. No such token or not authorized'); + } + + $ret = array('token' => $new_token, 'token_secret' => $new_secret); + $ttl = $this->query_one(' + SELECT (CASE WHEN ost_token_ttl >= \'9999-12-31\' THEN NULL ELSE ost_token_ttl - NOW() END) as token_ttl + FROM oauth_server_token + WHERE ost_token = \'%s\'', $new_token); + + if (is_numeric($ttl)) + { + $ret['token_ttl'] = intval($ttl); + } + return $ret; + } + + /** + * Fetch the consumer access token, by access token. + * + * @param string token + * @param int user_id + * @exception OAuthException2 when token is not found + * @return array token and consumer details + */ + public function getConsumerAccessToken ( $token, $user_id ) + { + $rs = $this->query_row_assoc(' + SELECT ost_token as token, + ost_token_secret as token_secret, + ost_referrer_host as token_referrer_host, + osr_consumer_key as consumer_key, + osr_consumer_secret as consumer_secret, + osr_application_uri as application_uri, + osr_application_title as application_title, + osr_application_descr as application_descr, + osr_callback_uri as callback_uri + FROM oauth_server_token + JOIN oauth_server_registry + ON ost_osr_id_ref = osr_id + WHERE ost_token_type = \'access\' + AND ost_token = \'%s\' + AND ost_usa_id_ref = \'%d\' + AND ost_token_ttl >= NOW() + ', $token, $user_id); + + if (empty($rs)) + { + throw new OAuthException2('No server_token "'.$token.'" for user "'.$user_id.'"'); + } + return $rs; + } + + /** + * Delete a consumer access token. + * + * @param string token + * @param int user_id + * @param boolean user_is_admin + */ + public function deleteConsumerAccessToken ( $token, $user_id, $user_is_admin = false ) + { + if ($user_is_admin) + { + $this->query(' + DELETE FROM oauth_server_token + WHERE ost_token = \'%s\' + AND ost_token_type = \'access\' + ', $token); + } + else + { + $this->query(' + DELETE FROM oauth_server_token + WHERE ost_token = \'%s\' + AND ost_token_type = \'access\' + AND ost_usa_id_ref = \'%d\' + ', $token, $user_id); + } + } + + /** + * Set the ttl of a consumer access token. This is done when the + * server receives a valid request with a xoauth_token_ttl parameter in it. + * + * @param string token + * @param int ttl + */ + public function setConsumerAccessTokenTtl ( $token, $token_ttl ) + { + if ($token_ttl <= 0) + { + // Immediate delete when the token is past its ttl + $this->deleteConsumerAccessToken($token, 0, true); + } + else + { + // Set maximum time to live for this token + $this->query(' + UPDATE oauth_server_token + SET ost_token_ttl = (NOW() + INTERVAL \'%d SECOND\') + WHERE ost_token = \'%s\' + AND ost_token_type = \'access\' + ', $token_ttl, $token); + } + } + + /** + * Fetch a list of all consumer keys, secrets etc. + * Returns the public (user_id is null) and the keys owned by the user + * + * @param int user_id + * @return array + */ + public function listConsumers ( $user_id ) + { + $rs = $this->query_all_assoc(' + SELECT osr_id as id, + osr_usa_id_ref as user_id, + osr_consumer_key as consumer_key, + osr_consumer_secret as consumer_secret, + osr_enabled as enabled, + osr_status as status, + osr_issue_date as issue_date, + osr_application_uri as application_uri, + osr_application_title as application_title, + osr_application_descr as application_descr, + osr_requester_name as requester_name, + osr_requester_email as requester_email, + osr_callback_uri as callback_uri + FROM oauth_server_registry + WHERE (osr_usa_id_ref = \'%d\' OR osr_usa_id_ref IS NULL) + ORDER BY osr_application_title + ', $user_id); + return $rs; + } + + /** + * List of all registered applications. Data returned has not sensitive + * information and therefore is suitable for public displaying. + * + * @param int $begin + * @param int $total + * @return array + */ + public function listConsumerApplications($begin = 0, $total = 25) + { + $rs = $this->query_all_assoc(' + SELECT osr_id as id, + osr_enabled as enabled, + osr_status as status, + osr_issue_date as issue_date, + osr_application_uri as application_uri, + osr_application_title as application_title, + osr_application_descr as application_descr + FROM oauth_server_registry + ORDER BY osr_application_title + '); + // TODO: pagination + return $rs; + } + + + /** + * Fetch a list of all consumer tokens accessing the account of the given user. + * + * @param int user_id + * @return array + */ + public function listConsumerTokens ( $user_id ) + { + $rs = $this->query_all_assoc(' + SELECT osr_consumer_key as consumer_key, + osr_consumer_secret as consumer_secret, + osr_enabled as enabled, + osr_status as status, + osr_application_uri as application_uri, + osr_application_title as application_title, + osr_application_descr as application_descr, + ost_timestamp as timestamp, + ost_token as token, + ost_token_secret as token_secret, + ost_referrer_host as token_referrer_host, + osr_callback_uri as callback_uri + FROM oauth_server_registry + JOIN oauth_server_token + ON ost_osr_id_ref = osr_id + WHERE ost_usa_id_ref = \'%d\' + AND ost_token_type = \'access\' + AND ost_token_ttl >= NOW() + ORDER BY osr_application_title + ', $user_id); + return $rs; + } + + + /** + * Check an nonce/timestamp combination. Clears any nonce combinations + * that are older than the one received. + * + * @param string consumer_key + * @param string token + * @param int timestamp + * @param string nonce + * @exception OAuthException2 thrown when the timestamp is not in sequence or nonce is not unique + */ + public function checkServerNonce ( $consumer_key, $token, $timestamp, $nonce ) + { + /* removed in Appendix A of RFC 5849 + $r = $this->query_row(' + SELECT MAX(osn_timestamp) + FROM oauth_server_nonce + WHERE osn_consumer_key = \'%s\' + AND osn_token = \'%s\' + ', $timestamp, $this->max_timestamp_skew, $consumer_key, $token); + + if (!empty($r) && $r[1] === 't') + { + throw new OAuthException2('Timestamp is out of sequence. Request rejected. Got '.$timestamp.' last max is '.$r[0].' allowed skew is '.$this->max_timestamp_skew); + } + */ + + // Insert the new combination + $this->query(' + INSERT INTO oauth_server_nonce ( + osn_consumer_key, + osn_token, + osn_timestamp, + osn_nonce + ) + VALUES (\'%s\', \'%s\', %d, \'%s\')', + $consumer_key, $token, $timestamp, $nonce); + + if ($this->query_affected_rows() == 0) + { + throw new OAuthException2('Duplicate timestamp/nonce combination, possible replay attack. Request rejected.'); + } + + // Clean up all timestamps older than the one we just received + $this->query(' + DELETE FROM oauth_server_nonce + WHERE osn_consumer_key = \'%s\' + AND osn_token = \'%s\' + AND osn_timestamp < %d - %d + ', $consumer_key, $token, $timestamp, $this->max_timestamp_skew); + } + + /** + * Add an entry to the log table + * + * @param array keys (osr_consumer_key, ost_token, ocr_consumer_key, oct_token) + * @param string received + * @param string sent + * @param string base_string + * @param string notes + * @param int (optional) user_id + */ + public function addLog ( $keys, $received, $sent, $base_string, $notes, $user_id = null ) + { + $args = array(); + $ps = array(); + foreach ($keys as $key => $value) + { + $args[] = $value; + $ps["olg_$key"] = "'%s'"; + } + + if (!empty($_SERVER['REMOTE_ADDR'])) + { + $remote_ip = $_SERVER['REMOTE_ADDR']; + } + else if (!empty($_SERVER['REMOTE_IP'])) + { + $remote_ip = $_SERVER['REMOTE_IP']; + } + else + { + $remote_ip = '0.0.0.0'; + } + + // Build the SQL + $ps['olg_received'] = "'%s'"; $args[] = $this->makeUTF8($received); + $ps['olg_sent'] = "'%s'"; $args[] = $this->makeUTF8($sent); + $ps['olg_base_string'] = "'%s'"; $args[] = $base_string; + $ps['olg_notes'] = "'%s'"; $args[] = $this->makeUTF8($notes); + $ps['olg_usa_id_ref'] = "NULLIF('%d', '0')"; $args[] = $user_id; + $ps['olg_remote_ip'] = "NULLIF('%s','0.0.0.0')::inet"; $args[] = $remote_ip; + + $this->query(' + INSERT INTO oauth_log ('.implode(',', array_keys($ps)) . ') + VALUES(' . implode(',', $ps) . ')', + $args + ); + } + + /** + * Get a page of entries from the log. Returns the last 100 records + * matching the options given. + * + * @param array options + * @param int user_id current user + * @return array log records + */ + public function listLog ( $options, $user_id ) + { + $where = array(); + $args = array(); + if (empty($options)) + { + $where[] = 'olg_usa_id_ref = \'%d\''; + $args[] = $user_id; + } + else + { + foreach ($options as $option => $value) + { + if (strlen($value) > 0) + { + switch ($option) + { + case 'osr_consumer_key': + case 'ocr_consumer_key': + case 'ost_token': + case 'oct_token': + $where[] = 'olg_'.$option.' = \'%s\''; + $args[] = $value; + break; + } + } + } + + $where[] = '(olg_usa_id_ref IS NULL OR olg_usa_id_ref = \'%d\')'; + $args[] = $user_id; + } + + $rs = $this->query_all_assoc(' + SELECT olg_id, + olg_osr_consumer_key AS osr_consumer_key, + olg_ost_token AS ost_token, + olg_ocr_consumer_key AS ocr_consumer_key, + olg_oct_token AS oct_token, + olg_usa_id_ref AS user_id, + olg_received AS received, + olg_sent AS sent, + olg_base_string AS base_string, + olg_notes AS notes, + olg_timestamp AS timestamp, + olg_remote_ip AS remote_ip + FROM oauth_log + WHERE '.implode(' AND ', $where).' + ORDER BY olg_id DESC + LIMIT 0,100', $args); + + return $rs; + } + + + /* ** Some simple helper functions for querying the pgsql db ** */ + + /** + * Perform a query, ignore the results + * + * @param string sql + * @param vararg arguments (for sprintf) + */ + protected function query ( $sql ) + { + $sql = $this->sql_printf(func_get_args()); + if (!($res = pg_query($this->conn, $sql))) + { + $this->sql_errcheck($sql); + } + $this->_lastAffectedRows = pg_affected_rows($res); + if (is_resource($res)) + { + pg_free_result($res); + } + } + + + /** + * Perform a query, return all rows + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return array + */ + protected function query_all_assoc ( $sql ) + { + $sql = $this->sql_printf(func_get_args()); + if (!($res = pg_query($this->conn, $sql))) + { + $this->sql_errcheck($sql); + } + $rs = array(); + while ($row = pg_fetch_assoc($res)) + { + $rs[] = $row; + } + pg_free_result($res); + return $rs; + } + + + /** + * Perform a query, return the first row + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return array + */ + protected function query_row_assoc ( $sql ) + { + $sql = $this->sql_printf(func_get_args()); + + if (!($res = pg_query($this->conn, $sql))) + { + $this->sql_errcheck($sql); + } + if ($row = pg_fetch_assoc($res)) + { + $rs = $row; + } + else + { + $rs = false; + } + pg_free_result($res); + return $rs; + } + + /** + * Perform a query, return the first row + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return array + */ + protected function query_row ( $sql ) + { + $sql = $this->sql_printf(func_get_args()); + if (!($res = pg_query($this->conn, $sql))) + { + $this->sql_errcheck($sql); + } + if ($row = pg_fetch_array($res)) + { + $rs = $row; + } + else + { + $rs = false; + } + pg_free_result($res); + return $rs; + } + + + /** + * Perform a query, return the first column of the first row + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return mixed + */ + protected function query_one ( $sql ) + { + $sql = $this->sql_printf(func_get_args()); + if (!($res = pg_query($this->conn, $sql))) + { + $this->sql_errcheck($sql); + } + $val = pg_fetch_row($res); + if ($val && isset($val[0])) { + $val = $val[0]; + } + pg_free_result($res); + return $val; + } + + + /** + * Return the number of rows affected in the last query + */ + protected function query_affected_rows () + { + return $this->_lastAffectedRows; + } + + + /** + * Return the id of the last inserted row + * + * @return int + */ + protected function query_insert_id ( $tableName, $primaryKey = null ) + { + $sequenceName = $tableName; + if ($primaryKey) { + $sequenceName .= "_$primaryKey"; + } + $sequenceName .= '_seq'; + + $sql = " + SELECT + CURRVAL('%s') + "; + $args = array($sql, $sequenceName); + $sql = $this->sql_printf($args); + if (!($res = pg_query($this->conn, $sql))) { + return 0; + } + $val = pg_fetch_row($res, 0); + if ($val && isset($val[0])) { + $val = $val[0]; + } + + pg_free_result($res); + return $val; + } + + + protected function sql_printf ( $args ) + { + $sql = array_shift($args); + if (count($args) == 1 && is_array($args[0])) + { + $args = $args[0]; + } + $args = array_map(array($this, 'sql_escape_string'), $args); + return vsprintf($sql, $args); + } + + + protected function sql_escape_string ( $s ) + { + if (is_string($s)) + { + return pg_escape_string($this->conn, $s); + } + else if (is_null($s)) + { + return NULL; + } + else if (is_bool($s)) + { + return intval($s); + } + else if (is_int($s) || is_float($s)) + { + return $s; + } + else + { + return pg_escape_string($this->conn, strval($s)); + } + } + + + protected function sql_errcheck ( $sql ) + { + $msg = "SQL Error in OAuthStorePostgreSQL: ".pg_last_error($this->conn)."\n\n" . $sql; + throw new OAuthException2($msg); + } +} diff --git a/vendor/oauth-php/library/store/OAuthStoreSQL.php b/vendor/oauth-php/library/store/OAuthStoreSQL.php new file mode 100644 index 0000000..45a4887 --- /dev/null +++ b/vendor/oauth-php/library/store/OAuthStoreSQL.php @@ -0,0 +1,1843 @@ +<?php + +/** + * Storage container for the oauth credentials, both server and consumer side. + * Based on MySQL + * + * @version $Id: OAuthStoreMySQL.php 76 2010-01-27 19:51:17Z brunobg@corollarium.com $ + * @author Marc Worrell <marcw@pobox.com> + * @date Nov 16, 2007 4:03:30 PM + * + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +require_once dirname(__FILE__) . '/OAuthStoreAbstract.class.php'; + + +abstract class OAuthStoreSQL extends OAuthStoreAbstract +{ + /** + * Maximum delta a timestamp may be off from a previous timestamp. + * Allows multiple consumers with some clock skew to work with the same token. + * Unit is seconds, default max skew is 10 minutes. + */ + protected $max_timestamp_skew = 600; + + /** + * Default ttl for request tokens + */ + protected $max_request_token_ttl = 3600; + + + /** + * Construct the OAuthStoreMySQL. + * In the options you have to supply either: + * - server, username, password and database (for a mysql_connect) + * - conn (for the connection to be used) + * + * @param array options + */ + function __construct ( $options = array() ) + { + if (isset($options['conn'])) + { + $this->conn = $options['conn']; + } + else + { + if (isset($options['server'])) + { + $server = $options['server']; + $username = $options['username']; + + if (isset($options['password'])) + { + $this->conn = mysql_connect($server, $username, $options['password']); + } + else + { + $this->conn = mysql_connect($server, $username); + } + } + else + { + // Try the default mysql connect + $this->conn = mysql_connect(); + } + + if ($this->conn === false) + { + throw new OAuthException2('Could not connect to MySQL database: ' . mysql_error()); + } + + if (isset($options['database'])) + { + if (!mysql_select_db($options['database'], $this->conn)) + { + $this->sql_errcheck(); + } + } + $this->query('set character set utf8'); + } + } + + + /** + * Find stored credentials for the consumer key and token. Used by an OAuth server + * when verifying an OAuth request. + * + * @param string consumer_key + * @param string token + * @param string token_type false, 'request' or 'access' + * @exception OAuthException2 when no secrets where found + * @return array assoc (consumer_secret, token_secret, osr_id, ost_id, user_id) + */ + public function getSecretsForVerify ( $consumer_key, $token, $token_type = 'access' ) + { + if ($token_type === false) + { + $rs = $this->query_row_assoc(' + SELECT osr_id, + osr_consumer_key as consumer_key, + osr_consumer_secret as consumer_secret + FROM oauth_server_registry + WHERE osr_consumer_key = \'%s\' + AND osr_enabled = 1 + ', + $consumer_key); + + if ($rs) + { + $rs['token'] = false; + $rs['token_secret'] = false; + $rs['user_id'] = false; + $rs['ost_id'] = false; + } + } + else + { + $rs = $this->query_row_assoc(' + SELECT osr_id, + ost_id, + ost_usa_id_ref as user_id, + osr_consumer_key as consumer_key, + osr_consumer_secret as consumer_secret, + ost_token as token, + ost_token_secret as token_secret + FROM oauth_server_registry + JOIN oauth_server_token + ON ost_osr_id_ref = osr_id + WHERE ost_token_type = \'%s\' + AND osr_consumer_key = \'%s\' + AND ost_token = \'%s\' + AND osr_enabled = 1 + AND ost_token_ttl >= NOW() + ', + $token_type, $consumer_key, $token); + } + + if (empty($rs)) + { + throw new OAuthException2('The consumer_key "'.$consumer_key.'" token "'.$token.'" combination does not exist or is not enabled.'); + } + return $rs; + } + + + /** + * Find the server details for signing a request, always looks for an access token. + * The returned credentials depend on which local user is making the request. + * + * The consumer_key must belong to the user or be public (user id is null) + * + * For signing we need all of the following: + * + * consumer_key consumer key associated with the server + * consumer_secret consumer secret associated with this server + * token access token associated with this server + * token_secret secret for the access token + * signature_methods signing methods supported by the server (array) + * + * @todo filter on token type (we should know how and with what to sign this request, and there might be old access tokens) + * @param string uri uri of the server + * @param int user_id id of the logged on user + * @param string name (optional) name of the token (case sensitive) + * @exception OAuthException2 when no credentials found + * @return array + */ + public function getSecretsForSignature ( $uri, $user_id, $name = '' ) + { + // Find a consumer key and token for the given uri + $ps = parse_url($uri); + $host = isset($ps['host']) ? $ps['host'] : 'localhost'; + $path = isset($ps['path']) ? $ps['path'] : ''; + + if (empty($path) || substr($path, -1) != '/') + { + $path .= '/'; + } + + // The owner of the consumer_key is either the user or nobody (public consumer key) + $secrets = $this->query_row_assoc(' + SELECT ocr_consumer_key as consumer_key, + ocr_consumer_secret as consumer_secret, + oct_token as token, + oct_token_secret as token_secret, + ocr_signature_methods as signature_methods + FROM oauth_consumer_registry + JOIN oauth_consumer_token ON oct_ocr_id_ref = ocr_id + WHERE ocr_server_uri_host = \'%s\' + AND ocr_server_uri_path = LEFT(\'%s\', LENGTH(ocr_server_uri_path)) + AND (ocr_usa_id_ref = \'%d\' OR ocr_usa_id_ref IS NULL) + AND oct_usa_id_ref = \'%d\' + AND oct_token_type = \'access\' + AND oct_name = \'%s\' + AND oct_token_ttl >= NOW() + ORDER BY ocr_usa_id_ref DESC, ocr_consumer_secret DESC, LENGTH(ocr_server_uri_path) DESC + LIMIT 0,1 + ', $host, $path, $user_id, $user_id, $name + ); + + if (empty($secrets)) + { + throw new OAuthException2('No server tokens available for '.$uri); + } + $secrets['signature_methods'] = explode(',', $secrets['signature_methods']); + return $secrets; + } + + + /** + * Get the token and token secret we obtained from a server. + * + * @param string consumer_key + * @param string token + * @param string token_type + * @param int user_id the user owning the token + * @param string name optional name for a named token + * @exception OAuthException2 when no credentials found + * @return array + */ + public function getServerTokenSecrets ( $consumer_key, $token, $token_type, $user_id, $name = '' ) + { + if ($token_type != 'request' && $token_type != 'access') + { + throw new OAuthException2('Unkown token type "'.$token_type.'", must be either "request" or "access"'); + } + + // Take the most recent token of the given type + $r = $this->query_row_assoc(' + SELECT ocr_consumer_key as consumer_key, + ocr_consumer_secret as consumer_secret, + oct_token as token, + oct_token_secret as token_secret, + oct_name as token_name, + ocr_signature_methods as signature_methods, + ocr_server_uri as server_uri, + ocr_request_token_uri as request_token_uri, + ocr_authorize_uri as authorize_uri, + ocr_access_token_uri as access_token_uri, + IF(oct_token_ttl >= \'9999-12-31\', NULL, UNIX_TIMESTAMP(oct_token_ttl) - UNIX_TIMESTAMP(NOW())) as token_ttl + FROM oauth_consumer_registry + JOIN oauth_consumer_token + ON oct_ocr_id_ref = ocr_id + WHERE ocr_consumer_key = \'%s\' + AND oct_token_type = \'%s\' + AND oct_token = \'%s\' + AND oct_usa_id_ref = %d + AND oct_token_ttl >= NOW() + ', $consumer_key, $token_type, $token, $user_id + ); + + if (empty($r)) + { + throw new OAuthException2('Could not find a "'.$token_type.'" token for consumer "'.$consumer_key.'" and user '.$user_id); + } + if (isset($r['signature_methods']) && !empty($r['signature_methods'])) + { + $r['signature_methods'] = explode(',',$r['signature_methods']); + } + else + { + $r['signature_methods'] = array(); + } + return $r; + } + + + /** + * Add a request token we obtained from a server. + * + * @todo remove old tokens for this user and this ocr_id + * @param string consumer_key key of the server in the consumer registry + * @param string token_type one of 'request' or 'access' + * @param string token + * @param string token_secret + * @param int user_id the user owning the token + * @param array options extra options, server_uri, name and token_ttl + * @exception OAuthException2 when server is not known + * @exception OAuthException2 when we received a duplicate token + */ + public function addServerToken ( $consumer_key, $token_type, $token, $token_secret, $user_id, $options = array() ) + { + if ($token_type != 'request' && $token_type != 'access') + { + throw new OAuthException2('Unknown token type "'.$token_type.'", must be either "request" or "access"'); + } + + // Maximum time to live for this token + if (isset($options['token_ttl']) && is_numeric($options['token_ttl'])) + { + $ttl = 'DATE_ADD(NOW(), INTERVAL '.intval($options['token_ttl']).' SECOND)'; + } + else if ($token_type == 'request') + { + $ttl = 'DATE_ADD(NOW(), INTERVAL '.$this->max_request_token_ttl.' SECOND)'; + } + else + { + $ttl = "'9999-12-31'"; + } + + if (isset($options['server_uri'])) + { + $ocr_id = $this->query_one(' + SELECT ocr_id + FROM oauth_consumer_registry + WHERE ocr_consumer_key = \'%s\' + AND (ocr_usa_id_ref = %d OR ocr_usa_id_ref IS NULL) + AND ocr_server_uri = \'%s\' + ', $consumer_key, $user_id, $options['server_uri']); + } + else + { + $ocr_id = $this->query_one(' + SELECT ocr_id + FROM oauth_consumer_registry + WHERE ocr_consumer_key = \'%s\' + AND (ocr_usa_id_ref = %d OR ocr_usa_id_ref IS NULL) + ', $consumer_key, $user_id); + } + + if (empty($ocr_id)) + { + throw new OAuthException2('No server associated with consumer_key "'.$consumer_key.'"'); + } + + // Named tokens, unique per user/consumer key + if (isset($options['name']) && $options['name'] != '') + { + $name = $options['name']; + } + else + { + $name = ''; + } + + // Delete any old tokens with the same type and name for this user/server combination + $this->query(' + DELETE FROM oauth_consumer_token + WHERE oct_ocr_id_ref = %d + AND oct_usa_id_ref = %d + AND oct_token_type = LOWER(\'%s\') + AND oct_name = \'%s\' + ', + $ocr_id, + $user_id, + $token_type, + $name); + + // Insert the new token + $this->query(' + INSERT IGNORE INTO oauth_consumer_token + SET oct_ocr_id_ref = %d, + oct_usa_id_ref = %d, + oct_name = \'%s\', + oct_token = \'%s\', + oct_token_secret= \'%s\', + oct_token_type = LOWER(\'%s\'), + oct_timestamp = NOW(), + oct_token_ttl = '.$ttl.' + ', + $ocr_id, + $user_id, + $name, + $token, + $token_secret, + $token_type); + + if (!$this->query_affected_rows()) + { + throw new OAuthException2('Received duplicate token "'.$token.'" for the same consumer_key "'.$consumer_key.'"'); + } + } + + + /** + * Delete a server key. This removes access to that site. + * + * @param string consumer_key + * @param int user_id user registering this server + * @param boolean user_is_admin + */ + public function deleteServer ( $consumer_key, $user_id, $user_is_admin = false ) + { + if ($user_is_admin) + { + $this->query(' + DELETE FROM oauth_consumer_registry + WHERE ocr_consumer_key = \'%s\' + AND (ocr_usa_id_ref = %d OR ocr_usa_id_ref IS NULL) + ', $consumer_key, $user_id); + } + else + { + $this->query(' + DELETE FROM oauth_consumer_registry + WHERE ocr_consumer_key = \'%s\' + AND ocr_usa_id_ref = %d + ', $consumer_key, $user_id); + } + } + + + /** + * Get a server from the consumer registry using the consumer key + * + * @param string consumer_key + * @param int user_id + * @param boolean user_is_admin (optional) + * @exception OAuthException2 when server is not found + * @return array + */ + public function getServer ( $consumer_key, $user_id, $user_is_admin = false ) + { + $r = $this->query_row_assoc(' + SELECT ocr_id as id, + ocr_usa_id_ref as user_id, + ocr_consumer_key as consumer_key, + ocr_consumer_secret as consumer_secret, + ocr_signature_methods as signature_methods, + ocr_server_uri as server_uri, + ocr_request_token_uri as request_token_uri, + ocr_authorize_uri as authorize_uri, + ocr_access_token_uri as access_token_uri + FROM oauth_consumer_registry + WHERE ocr_consumer_key = \'%s\' + AND (ocr_usa_id_ref = %d OR ocr_usa_id_ref IS NULL) + ', $consumer_key, $user_id); + + if (empty($r)) + { + throw new OAuthException2('No server with consumer_key "'.$consumer_key.'" has been registered (for this user)'); + } + + if (isset($r['signature_methods']) && !empty($r['signature_methods'])) + { + $r['signature_methods'] = explode(',',$r['signature_methods']); + } + else + { + $r['signature_methods'] = array(); + } + return $r; + } + + + + /** + * Find the server details that might be used for a request + * + * The consumer_key must belong to the user or be public (user id is null) + * + * @param string uri uri of the server + * @param int user_id id of the logged on user + * @exception OAuthException2 when no credentials found + * @return array + */ + public function getServerForUri ( $uri, $user_id ) + { + // Find a consumer key and token for the given uri + $ps = parse_url($uri); + $host = isset($ps['host']) ? $ps['host'] : 'localhost'; + $path = isset($ps['path']) ? $ps['path'] : ''; + + if (empty($path) || substr($path, -1) != '/') + { + $path .= '/'; + } + + // The owner of the consumer_key is either the user or nobody (public consumer key) + $server = $this->query_row_assoc(' + SELECT ocr_id as id, + ocr_usa_id_ref as user_id, + ocr_consumer_key as consumer_key, + ocr_consumer_secret as consumer_secret, + ocr_signature_methods as signature_methods, + ocr_server_uri as server_uri, + ocr_request_token_uri as request_token_uri, + ocr_authorize_uri as authorize_uri, + ocr_access_token_uri as access_token_uri + FROM oauth_consumer_registry + WHERE ocr_server_uri_host = \'%s\' + AND ocr_server_uri_path = LEFT(\'%s\', LENGTH(ocr_server_uri_path)) + AND (ocr_usa_id_ref = \'%d\' OR ocr_usa_id_ref IS NULL) + ORDER BY ocr_usa_id_ref DESC, consumer_secret DESC, LENGTH(ocr_server_uri_path) DESC + LIMIT 0,1 + ', $host, $path, $user_id + ); + + if (empty($server)) + { + throw new OAuthException2('No server available for '.$uri); + } + $server['signature_methods'] = explode(',', $server['signature_methods']); + return $server; + } + + + /** + * Get a list of all server token this user has access to. + * + * @param int usr_id + * @return array + */ + public function listServerTokens ( $user_id ) + { + $ts = $this->query_all_assoc(' + SELECT ocr_consumer_key as consumer_key, + ocr_consumer_secret as consumer_secret, + oct_id as token_id, + oct_token as token, + oct_token_secret as token_secret, + oct_usa_id_ref as user_id, + ocr_signature_methods as signature_methods, + ocr_server_uri as server_uri, + ocr_server_uri_host as server_uri_host, + ocr_server_uri_path as server_uri_path, + ocr_request_token_uri as request_token_uri, + ocr_authorize_uri as authorize_uri, + ocr_access_token_uri as access_token_uri, + oct_timestamp as timestamp + FROM oauth_consumer_registry + JOIN oauth_consumer_token + ON oct_ocr_id_ref = ocr_id + WHERE oct_usa_id_ref = %d + AND oct_token_type = \'access\' + AND oct_token_ttl >= NOW() + ORDER BY ocr_server_uri_host, ocr_server_uri_path + ', $user_id); + return $ts; + } + + + /** + * Count how many tokens we have for the given server + * + * @param string consumer_key + * @return int + */ + public function countServerTokens ( $consumer_key ) + { + $count = $this->query_one(' + SELECT COUNT(oct_id) + FROM oauth_consumer_token + JOIN oauth_consumer_registry + ON oct_ocr_id_ref = ocr_id + WHERE oct_token_type = \'access\' + AND ocr_consumer_key = \'%s\' + AND oct_token_ttl >= NOW() + ', $consumer_key); + + return $count; + } + + + /** + * Get a specific server token for the given user + * + * @param string consumer_key + * @param string token + * @param int user_id + * @exception OAuthException2 when no such token found + * @return array + */ + public function getServerToken ( $consumer_key, $token, $user_id ) + { + $ts = $this->query_row_assoc(' + SELECT ocr_consumer_key as consumer_key, + ocr_consumer_secret as consumer_secret, + oct_token as token, + oct_token_secret as token_secret, + oct_usa_id_ref as usr_id, + ocr_signature_methods as signature_methods, + ocr_server_uri as server_uri, + ocr_server_uri_host as server_uri_host, + ocr_server_uri_path as server_uri_path, + ocr_request_token_uri as request_token_uri, + ocr_authorize_uri as authorize_uri, + ocr_access_token_uri as access_token_uri, + oct_timestamp as timestamp + FROM oauth_consumer_registry + JOIN oauth_consumer_token + ON oct_ocr_id_ref = ocr_id + WHERE ocr_consumer_key = \'%s\' + AND oct_usa_id_ref = %d + AND oct_token_type = \'access\' + AND oct_token = \'%s\' + AND oct_token_ttl >= NOW() + ', $consumer_key, $user_id, $token); + + if (empty($ts)) + { + throw new OAuthException2('No such consumer key ('.$consumer_key.') and token ('.$token.') combination for user "'.$user_id.'"'); + } + return $ts; + } + + + /** + * Delete a token we obtained from a server. + * + * @param string consumer_key + * @param string token + * @param int user_id + * @param boolean user_is_admin + */ + public function deleteServerToken ( $consumer_key, $token, $user_id, $user_is_admin = false ) + { + if ($user_is_admin) + { + $this->query(' + DELETE oauth_consumer_token + FROM oauth_consumer_token + JOIN oauth_consumer_registry + ON oct_ocr_id_ref = ocr_id + WHERE ocr_consumer_key = \'%s\' + AND oct_token = \'%s\' + ', $consumer_key, $token); + } + else + { + $this->query(' + DELETE oauth_consumer_token + FROM oauth_consumer_token + JOIN oauth_consumer_registry + ON oct_ocr_id_ref = ocr_id + WHERE ocr_consumer_key = \'%s\' + AND oct_token = \'%s\' + AND oct_usa_id_ref = %d + ', $consumer_key, $token, $user_id); + } + } + + + /** + * Set the ttl of a server access token. This is done when the + * server receives a valid request with a xoauth_token_ttl parameter in it. + * + * @param string consumer_key + * @param string token + * @param int token_ttl + */ + public function setServerTokenTtl ( $consumer_key, $token, $token_ttl, $server_uri = NULL ) + { + if ($token_ttl <= 0) + { + // Immediate delete when the token is past its ttl + $this->deleteServerToken($consumer_key, $token, 0, true); + } + else if ( $server_uri ) + { + // Set maximum time to live for this token + $this->query(' + UPDATE oauth_consumer_token, oauth_consumer_registry + SET ost_token_ttl = DATE_ADD(NOW(), INTERVAL %d SECOND) + WHERE ocr_consumer_key = \'%s\' + AND ocr_server_uri = \'%s\' + AND oct_ocr_id_ref = ocr_id + AND oct_token = \'%s\' + ', $token_ttl, $server_uri, $consumer_key, $token); + } + else + { + // Set maximum time to live for this token + $this->query(' + UPDATE oauth_consumer_token, oauth_consumer_registry + SET ost_token_ttl = DATE_ADD(NOW(), INTERVAL %d SECOND) + WHERE ocr_consumer_key = \'%s\' + AND oct_ocr_id_ref = ocr_id + AND oct_token = \'%s\' + ', $token_ttl, $consumer_key, $token); + } + } + + + /** + * Get a list of all consumers from the consumer registry. + * The consumer keys belong to the user or are public (user id is null) + * + * @param string q query term + * @param int user_id + * @return array + */ + public function listServers ( $q = '', $user_id ) + { + $q = trim(str_replace('%', '', $q)); + $args = array(); + + if (!empty($q)) + { + $where = ' WHERE ( ocr_consumer_key like \'%%%s%%\' + OR ocr_server_uri like \'%%%s%%\' + OR ocr_server_uri_host like \'%%%s%%\' + OR ocr_server_uri_path like \'%%%s%%\') + AND (ocr_usa_id_ref = %d OR ocr_usa_id_ref IS NULL) + '; + + $args[] = $q; + $args[] = $q; + $args[] = $q; + $args[] = $q; + $args[] = $user_id; + } + else + { + $where = ' WHERE ocr_usa_id_ref = %d OR ocr_usa_id_ref IS NULL'; + $args[] = $user_id; + } + + $servers = $this->query_all_assoc(' + SELECT ocr_id as id, + ocr_usa_id_ref as user_id, + ocr_consumer_key as consumer_key, + ocr_consumer_secret as consumer_secret, + ocr_signature_methods as signature_methods, + ocr_server_uri as server_uri, + ocr_server_uri_host as server_uri_host, + ocr_server_uri_path as server_uri_path, + ocr_request_token_uri as request_token_uri, + ocr_authorize_uri as authorize_uri, + ocr_access_token_uri as access_token_uri + FROM oauth_consumer_registry + '.$where.' + ORDER BY ocr_server_uri_host, ocr_server_uri_path + ', $args); + return $servers; + } + + + /** + * Register or update a server for our site (we will be the consumer) + * + * (This is the registry at the consumers, registering servers ;-) ) + * + * @param array server + * @param int user_id user registering this server + * @param boolean user_is_admin + * @exception OAuthException2 when fields are missing or on duplicate consumer_key + * @return consumer_key + */ + public function updateServer ( $server, $user_id, $user_is_admin = false ) + { + foreach (array('consumer_key', 'server_uri') as $f) + { + if (empty($server[$f])) + { + throw new OAuthException2('The field "'.$f.'" must be set and non empty'); + } + } + + if (!empty($server['id'])) + { + $exists = $this->query_one(' + SELECT ocr_id + FROM oauth_consumer_registry + WHERE ocr_consumer_key = \'%s\' + AND ocr_id <> %d + AND (ocr_usa_id_ref = %d OR ocr_usa_id_ref IS NULL) + ', $server['consumer_key'], $server['id'], $user_id); + } + else + { + $exists = $this->query_one(' + SELECT ocr_id + FROM oauth_consumer_registry + WHERE ocr_consumer_key = \'%s\' + AND (ocr_usa_id_ref = %d OR ocr_usa_id_ref IS NULL) + ', $server['consumer_key'], $user_id); + } + + if ($exists) + { + throw new OAuthException2('The server with key "'.$server['consumer_key'].'" has already been registered'); + } + + $parts = parse_url($server['server_uri']); + $host = (isset($parts['host']) ? $parts['host'] : 'localhost'); + $path = (isset($parts['path']) ? $parts['path'] : '/'); + + if (isset($server['signature_methods'])) + { + if (is_array($server['signature_methods'])) + { + $server['signature_methods'] = strtoupper(implode(',', $server['signature_methods'])); + } + } + else + { + $server['signature_methods'] = ''; + } + + // When the user is an admin, then the user can update the user_id of this record + if ($user_is_admin && array_key_exists('user_id', $server)) + { + if (is_null($server['user_id'])) + { + $update_user = ', ocr_usa_id_ref = NULL'; + } + else + { + $update_user = ', ocr_usa_id_ref = '.intval($server['user_id']); + } + } + else + { + $update_user = ''; + } + + if (!empty($server['id'])) + { + // Check if the current user can update this server definition + if (!$user_is_admin) + { + $ocr_usa_id_ref = $this->query_one(' + SELECT ocr_usa_id_ref + FROM oauth_consumer_registry + WHERE ocr_id = %d + ', $server['id']); + + if ($ocr_usa_id_ref != $user_id) + { + throw new OAuthException2('The user "'.$user_id.'" is not allowed to update this server'); + } + } + + // Update the consumer registration + $this->query(' + UPDATE oauth_consumer_registry + SET ocr_consumer_key = \'%s\', + ocr_consumer_secret = \'%s\', + ocr_server_uri = \'%s\', + ocr_server_uri_host = \'%s\', + ocr_server_uri_path = \'%s\', + ocr_timestamp = NOW(), + ocr_request_token_uri = \'%s\', + ocr_authorize_uri = \'%s\', + ocr_access_token_uri = \'%s\', + ocr_signature_methods = \'%s\' + '.$update_user.' + WHERE ocr_id = %d + ', + $server['consumer_key'], + $server['consumer_secret'], + $server['server_uri'], + strtolower($host), + $path, + isset($server['request_token_uri']) ? $server['request_token_uri'] : '', + isset($server['authorize_uri']) ? $server['authorize_uri'] : '', + isset($server['access_token_uri']) ? $server['access_token_uri'] : '', + $server['signature_methods'], + $server['id'] + ); + } + else + { + if (empty($update_user)) + { + // Per default the user owning the key is the user registering the key + $update_user = ', ocr_usa_id_ref = '.intval($user_id); + } + + $this->query(' + INSERT INTO oauth_consumer_registry + SET ocr_consumer_key = \'%s\', + ocr_consumer_secret = \'%s\', + ocr_server_uri = \'%s\', + ocr_server_uri_host = \'%s\', + ocr_server_uri_path = \'%s\', + ocr_timestamp = NOW(), + ocr_request_token_uri = \'%s\', + ocr_authorize_uri = \'%s\', + ocr_access_token_uri = \'%s\', + ocr_signature_methods = \'%s\' + '.$update_user, + $server['consumer_key'], + $server['consumer_secret'], + $server['server_uri'], + strtolower($host), + $path, + isset($server['request_token_uri']) ? $server['request_token_uri'] : '', + isset($server['authorize_uri']) ? $server['authorize_uri'] : '', + isset($server['access_token_uri']) ? $server['access_token_uri'] : '', + $server['signature_methods'] + ); + + $ocr_id = $this->query_insert_id(); + } + return $server['consumer_key']; + } + + + /** + * Insert/update a new consumer with this server (we will be the server) + * When this is a new consumer, then also generate the consumer key and secret. + * Never updates the consumer key and secret. + * When the id is set, then the key and secret must correspond to the entry + * being updated. + * + * (This is the registry at the server, registering consumers ;-) ) + * + * @param array consumer + * @param int user_id user registering this consumer + * @param boolean user_is_admin + * @return string consumer key + */ + public function updateConsumer ( $consumer, $user_id, $user_is_admin = false ) + { + if (!$user_is_admin) + { + foreach (array('requester_name', 'requester_email') as $f) + { + if (empty($consumer[$f])) + { + throw new OAuthException2('The field "'.$f.'" must be set and non empty'); + } + } + } + + if (!empty($consumer['id'])) + { + if (empty($consumer['consumer_key'])) + { + throw new OAuthException2('The field "consumer_key" must be set and non empty'); + } + if (!$user_is_admin && empty($consumer['consumer_secret'])) + { + throw new OAuthException2('The field "consumer_secret" must be set and non empty'); + } + + // Check if the current user can update this server definition + if (!$user_is_admin) + { + $osr_usa_id_ref = $this->query_one(' + SELECT osr_usa_id_ref + FROM oauth_server_registry + WHERE osr_id = %d + ', $consumer['id']); + + if ($osr_usa_id_ref != $user_id) + { + throw new OAuthException2('The user "'.$user_id.'" is not allowed to update this consumer'); + } + } + else + { + // User is an admin, allow a key owner to be changed or key to be shared + if (array_key_exists('user_id',$consumer)) + { + if (is_null($consumer['user_id'])) + { + $this->query(' + UPDATE oauth_server_registry + SET osr_usa_id_ref = NULL + WHERE osr_id = %d + ', $consumer['id']); + } + else + { + $this->query(' + UPDATE oauth_server_registry + SET osr_usa_id_ref = %d + WHERE osr_id = %d + ', $consumer['user_id'], $consumer['id']); + } + } + } + + $this->query(' + UPDATE oauth_server_registry + SET osr_requester_name = \'%s\', + osr_requester_email = \'%s\', + osr_callback_uri = \'%s\', + osr_application_uri = \'%s\', + osr_application_title = \'%s\', + osr_application_descr = \'%s\', + osr_application_notes = \'%s\', + osr_application_type = \'%s\', + osr_application_commercial = IF(%d,1,0), + osr_timestamp = NOW() + WHERE osr_id = %d + AND osr_consumer_key = \'%s\' + AND osr_consumer_secret = \'%s\' + ', + $consumer['requester_name'], + $consumer['requester_email'], + isset($consumer['callback_uri']) ? $consumer['callback_uri'] : '', + isset($consumer['application_uri']) ? $consumer['application_uri'] : '', + isset($consumer['application_title']) ? $consumer['application_title'] : '', + isset($consumer['application_descr']) ? $consumer['application_descr'] : '', + isset($consumer['application_notes']) ? $consumer['application_notes'] : '', + isset($consumer['application_type']) ? $consumer['application_type'] : '', + isset($consumer['application_commercial']) ? $consumer['application_commercial'] : 0, + $consumer['id'], + $consumer['consumer_key'], + $consumer['consumer_secret'] + ); + + + $consumer_key = $consumer['consumer_key']; + } + else + { + $consumer_key = $this->generateKey(true); + $consumer_secret= $this->generateKey(); + + // When the user is an admin, then the user can be forced to something else that the user + if ($user_is_admin && array_key_exists('user_id',$consumer)) + { + if (is_null($consumer['user_id'])) + { + $owner_id = 'NULL'; + } + else + { + $owner_id = intval($consumer['user_id']); + } + } + else + { + // No admin, take the user id as the owner id. + $owner_id = intval($user_id); + } + + $this->query(' + INSERT INTO oauth_server_registry + SET osr_enabled = 1, + osr_status = \'active\', + osr_usa_id_ref = \'%s\', + osr_consumer_key = \'%s\', + osr_consumer_secret = \'%s\', + osr_requester_name = \'%s\', + osr_requester_email = \'%s\', + osr_callback_uri = \'%s\', + osr_application_uri = \'%s\', + osr_application_title = \'%s\', + osr_application_descr = \'%s\', + osr_application_notes = \'%s\', + osr_application_type = \'%s\', + osr_application_commercial = IF(%d,1,0), + osr_timestamp = NOW(), + osr_issue_date = NOW() + ', + $owner_id, + $consumer_key, + $consumer_secret, + $consumer['requester_name'], + $consumer['requester_email'], + isset($consumer['callback_uri']) ? $consumer['callback_uri'] : '', + isset($consumer['application_uri']) ? $consumer['application_uri'] : '', + isset($consumer['application_title']) ? $consumer['application_title'] : '', + isset($consumer['application_descr']) ? $consumer['application_descr'] : '', + isset($consumer['application_notes']) ? $consumer['application_notes'] : '', + isset($consumer['application_type']) ? $consumer['application_type'] : '', + isset($consumer['application_commercial']) ? $consumer['application_commercial'] : 0 + ); + } + return $consumer_key; + + } + + + + /** + * Delete a consumer key. This removes access to our site for all applications using this key. + * + * @param string consumer_key + * @param int user_id user registering this server + * @param boolean user_is_admin + */ + public function deleteConsumer ( $consumer_key, $user_id, $user_is_admin = false ) + { + if ($user_is_admin) + { + $this->query(' + DELETE FROM oauth_server_registry + WHERE osr_consumer_key = \'%s\' + AND (osr_usa_id_ref = %d OR osr_usa_id_ref IS NULL) + ', $consumer_key, $user_id); + } + else + { + $this->query(' + DELETE FROM oauth_server_registry + WHERE osr_consumer_key = \'%s\' + AND osr_usa_id_ref = %d + ', $consumer_key, $user_id); + } + } + + + + /** + * Fetch a consumer of this server, by consumer_key. + * + * @param string consumer_key + * @param int user_id + * @param boolean user_is_admin (optional) + * @exception OAuthException2 when consumer not found + * @return array + */ + public function getConsumer ( $consumer_key, $user_id, $user_is_admin = false ) + { + $consumer = $this->query_row_assoc(' + SELECT * + FROM oauth_server_registry + WHERE osr_consumer_key = \'%s\' + ', $consumer_key); + + if (!is_array($consumer)) + { + throw new OAuthException2('No consumer with consumer_key "'.$consumer_key.'"'); + } + + $c = array(); + foreach ($consumer as $key => $value) + { + $c[substr($key, 4)] = $value; + } + $c['user_id'] = $c['usa_id_ref']; + + if (!$user_is_admin && !empty($c['user_id']) && $c['user_id'] != $user_id) + { + throw new OAuthException2('No access to the consumer information for consumer_key "'.$consumer_key.'"'); + } + return $c; + } + + + /** + * Fetch the static consumer key for this provider. The user for the static consumer + * key is NULL (no user, shared key). If the key did not exist then the key is created. + * + * @return string + */ + public function getConsumerStatic () + { + $consumer = $this->query_one(' + SELECT osr_consumer_key + FROM oauth_server_registry + WHERE osr_consumer_key LIKE \'sc-%%\' + AND osr_usa_id_ref IS NULL + '); + + if (empty($consumer)) + { + $consumer_key = 'sc-'.$this->generateKey(true); + $this->query(' + INSERT INTO oauth_server_registry + SET osr_enabled = 1, + osr_status = \'active\', + osr_usa_id_ref = NULL, + osr_consumer_key = \'%s\', + osr_consumer_secret = \'\', + osr_requester_name = \'\', + osr_requester_email = \'\', + osr_callback_uri = \'\', + osr_application_uri = \'\', + osr_application_title = \'Static shared consumer key\', + osr_application_descr = \'\', + osr_application_notes = \'Static shared consumer key\', + osr_application_type = \'\', + osr_application_commercial = 0, + osr_timestamp = NOW(), + osr_issue_date = NOW() + ', + $consumer_key + ); + + // Just make sure that if the consumer key is truncated that we get the truncated string + $consumer = $this->getConsumerStatic(); + } + return $consumer; + } + + + /** + * Add an unautorized request token to our server. + * + * @param string consumer_key + * @param array options (eg. token_ttl) + * @return array (token, token_secret) + */ + public function addConsumerRequestToken ( $consumer_key, $options = array() ) + { + $token = $this->generateKey(true); + $secret = !isset($options['secret']) ? $this->generateKey() : $options['secret']; + $osr_id = $this->query_one(' + SELECT osr_id + FROM oauth_server_registry + WHERE osr_consumer_key = \'%s\' + AND osr_enabled = 1 + ', $consumer_key); + + if (!$osr_id) + { + throw new OAuthException2('No server with consumer_key "'.$consumer_key.'" or consumer_key is disabled'); + } + + if (isset($options['token_ttl']) && is_numeric($options['token_ttl'])) + { + $ttl = intval($options['token_ttl']); + } + else + { + $ttl = $this->max_request_token_ttl; + } + + if (!isset($options['oauth_callback'])) { + // 1.0a Compatibility : store callback url associated with request token + $options['oauth_callback']='oob'; + } + + $this->query(' + INSERT INTO oauth_server_token + SET ost_osr_id_ref = %d, + ost_usa_id_ref = 1, + ost_token = \'%s\', + ost_token_secret = \'%s\', + ost_token_type = \'request\', + ost_token_ttl = DATE_ADD(NOW(), INTERVAL %d SECOND), + ost_callback_url = \'%s\' + ON DUPLICATE KEY UPDATE + ost_osr_id_ref = VALUES(ost_osr_id_ref), + ost_usa_id_ref = VALUES(ost_usa_id_ref), + ost_token = VALUES(ost_token), + ost_token_secret = VALUES(ost_token_secret), + ost_token_type = VALUES(ost_token_type), + ost_token_ttl = VALUES(ost_token_ttl), + ost_callback_url = VALUES(ost_callback_url), + ost_timestamp = NOW() + ', $osr_id, $token, $secret, $ttl, $options['oauth_callback']); + + return array('token'=>$token, 'token_secret'=>$secret, 'token_ttl'=>$ttl); + } + + + /** + * Fetch the consumer request token, by request token. + * + * @param string token + * @return array token and consumer details + */ + public function getConsumerRequestToken ( $token ) + { + $rs = $this->query_row_assoc(' + SELECT ost_token as token, + ost_token_secret as token_secret, + osr_consumer_key as consumer_key, + osr_consumer_secret as consumer_secret, + ost_token_type as token_type, + ost_callback_url as callback_url, + osr_application_title as application_title, + osr_application_descr as application_descr, + osr_application_uri as application_uri + FROM oauth_server_token + JOIN oauth_server_registry + ON ost_osr_id_ref = osr_id + WHERE ost_token_type = \'request\' + AND ost_token = \'%s\' + AND ost_token_ttl >= NOW() + ', $token); + + return $rs; + } + + + /** + * Delete a consumer token. The token must be a request or authorized token. + * + * @param string token + */ + public function deleteConsumerRequestToken ( $token ) + { + $this->query(' + DELETE FROM oauth_server_token + WHERE ost_token = \'%s\' + AND ost_token_type = \'request\' + ', $token); + } + + + /** + * Upgrade a request token to be an authorized request token. + * + * @param string token + * @param int user_id user authorizing the token + * @param string referrer_host used to set the referrer host for this token, for user feedback + */ + public function authorizeConsumerRequestToken ( $token, $user_id, $referrer_host = '' ) + { + // 1.0a Compatibility : create a token verifier + $verifier = substr(md5(rand()),0,10); + + $this->query(' + UPDATE oauth_server_token + SET ost_authorized = 1, + ost_usa_id_ref = %d, + ost_timestamp = NOW(), + ost_referrer_host = \'%s\', + ost_verifier = \'%s\' + WHERE ost_token = \'%s\' + AND ost_token_type = \'request\' + ', $user_id, $referrer_host, $verifier, $token); + return $verifier; + } + + + /** + * Count the consumer access tokens for the given consumer. + * + * @param string consumer_key + * @return int + */ + public function countConsumerAccessTokens ( $consumer_key ) + { + $count = $this->query_one(' + SELECT COUNT(ost_id) + FROM oauth_server_token + JOIN oauth_server_registry + ON ost_osr_id_ref = osr_id + WHERE ost_token_type = \'access\' + AND osr_consumer_key = \'%s\' + AND ost_token_ttl >= NOW() + ', $consumer_key); + + return $count; + } + + + /** + * Exchange an authorized request token for new access token. + * + * @param string token + * @param array options options for the token, token_ttl + * @exception OAuthException2 when token could not be exchanged + * @return array (token, token_secret) + */ + public function exchangeConsumerRequestForAccessToken ( $token, $options = array() ) + { + $new_token = $this->generateKey(true); + $new_secret = $this->generateKey(); + + // Maximum time to live for this token + if (isset($options['token_ttl']) && is_numeric($options['token_ttl'])) + { + $ttl_sql = 'DATE_ADD(NOW(), INTERVAL '.intval($options['token_ttl']).' SECOND)'; + } + else + { + $ttl_sql = "'9999-12-31'"; + } + + if (isset($options['verifier'])) { + $verifier = $options['verifier']; + + // 1.0a Compatibility : check token against oauth_verifier + $this->query(' + UPDATE oauth_server_token + SET ost_token = \'%s\', + ost_token_secret = \'%s\', + ost_token_type = \'access\', + ost_timestamp = NOW(), + ost_token_ttl = '.$ttl_sql.' + WHERE ost_token = \'%s\' + AND ost_token_type = \'request\' + AND ost_authorized = 1 + AND ost_token_ttl >= NOW() + AND ost_verifier = \'%s\' + ', $new_token, $new_secret, $token, $verifier); + } else { + + // 1.0 + $this->query(' + UPDATE oauth_server_token + SET ost_token = \'%s\', + ost_token_secret = \'%s\', + ost_token_type = \'access\', + ost_timestamp = NOW(), + ost_token_ttl = '.$ttl_sql.' + WHERE ost_token = \'%s\' + AND ost_token_type = \'request\' + AND ost_authorized = 1 + AND ost_token_ttl >= NOW() + ', $new_token, $new_secret, $token); + } + + if ($this->query_affected_rows() != 1) + { + throw new OAuthException2('Can\'t exchange request token "'.$token.'" for access token. No such token or not authorized'); + } + + $ret = array('token' => $new_token, 'token_secret' => $new_secret); + $ttl = $this->query_one(' + SELECT IF(ost_token_ttl >= \'9999-12-31\', NULL, UNIX_TIMESTAMP(ost_token_ttl) - UNIX_TIMESTAMP(NOW())) as token_ttl + FROM oauth_server_token + WHERE ost_token = \'%s\'', $new_token); + + if (is_numeric($ttl)) + { + $ret['token_ttl'] = intval($ttl); + } + return $ret; + } + + + /** + * Fetch the consumer access token, by access token. + * + * @param string token + * @param int user_id + * @exception OAuthException2 when token is not found + * @return array token and consumer details + */ + public function getConsumerAccessToken ( $token, $user_id ) + { + $rs = $this->query_row_assoc(' + SELECT ost_token as token, + ost_token_secret as token_secret, + ost_referrer_host as token_referrer_host, + osr_consumer_key as consumer_key, + osr_consumer_secret as consumer_secret, + osr_application_uri as application_uri, + osr_application_title as application_title, + osr_application_descr as application_descr, + osr_callback_uri as callback_uri + FROM oauth_server_token + JOIN oauth_server_registry + ON ost_osr_id_ref = osr_id + WHERE ost_token_type = \'access\' + AND ost_token = \'%s\' + AND ost_usa_id_ref = %d + AND ost_token_ttl >= NOW() + ', $token, $user_id); + + if (empty($rs)) + { + throw new OAuthException2('No server_token "'.$token.'" for user "'.$user_id.'"'); + } + return $rs; + } + + + /** + * Delete a consumer access token. + * + * @param string token + * @param int user_id + * @param boolean user_is_admin + */ + public function deleteConsumerAccessToken ( $token, $user_id, $user_is_admin = false ) + { + if ($user_is_admin) + { + $this->query(' + DELETE FROM oauth_server_token + WHERE ost_token = \'%s\' + AND ost_token_type = \'access\' + ', $token); + } + else + { + $this->query(' + DELETE FROM oauth_server_token + WHERE ost_token = \'%s\' + AND ost_token_type = \'access\' + AND ost_usa_id_ref = %d + ', $token, $user_id); + } + } + + + /** + * Set the ttl of a consumer access token. This is done when the + * server receives a valid request with a xoauth_token_ttl parameter in it. + * + * @param string token + * @param int ttl + */ + public function setConsumerAccessTokenTtl ( $token, $token_ttl ) + { + if ($token_ttl <= 0) + { + // Immediate delete when the token is past its ttl + $this->deleteConsumerAccessToken($token, 0, true); + } + else + { + // Set maximum time to live for this token + $this->query(' + UPDATE oauth_server_token + SET ost_token_ttl = DATE_ADD(NOW(), INTERVAL %d SECOND) + WHERE ost_token = \'%s\' + AND ost_token_type = \'access\' + ', $token_ttl, $token); + } + } + + + /** + * Fetch a list of all consumer keys, secrets etc. + * Returns the public (user_id is null) and the keys owned by the user + * + * @param int user_id + * @return array + */ + public function listConsumers ( $user_id ) + { + $rs = $this->query_all_assoc(' + SELECT osr_id as id, + osr_usa_id_ref as user_id, + osr_consumer_key as consumer_key, + osr_consumer_secret as consumer_secret, + osr_enabled as enabled, + osr_status as status, + osr_issue_date as issue_date, + osr_application_uri as application_uri, + osr_application_title as application_title, + osr_application_descr as application_descr, + osr_application_type as application_type, + osr_application_commercial as application_commercial, + osr_requester_name as requester_name, + osr_requester_email as requester_email, + osr_callback_uri as callback_uri + FROM oauth_server_registry + WHERE (osr_usa_id_ref = %d OR osr_usa_id_ref IS NULL) + ORDER BY osr_application_title + ', $user_id); + return $rs; + } + + /** + * List of all registered applications. Data returned has not sensitive + * information and therefore is suitable for public displaying. + * + * @param int $begin + * @param int $total + * @return array + */ + public function listConsumerApplications($begin = 0, $total = 25) + { + $rs = $this->query_all_assoc(' + SELECT osr_id as id, + osr_enabled as enabled, + osr_status as status, + osr_issue_date as issue_date, + osr_application_uri as application_uri, + osr_application_title as application_title, + osr_application_descr as application_descr + FROM oauth_server_registry + ORDER BY osr_application_title + '); + // TODO: pagination + return $rs; + } + + /** + * Fetch a list of all consumer tokens accessing the account of the given user. + * + * @param int user_id + * @return array + */ + public function listConsumerTokens ( $user_id ) + { + $rs = $this->query_all_assoc(' + SELECT osr_consumer_key as consumer_key, + osr_consumer_secret as consumer_secret, + osr_enabled as enabled, + osr_status as status, + osr_application_uri as application_uri, + osr_application_title as application_title, + osr_application_descr as application_descr, + ost_timestamp as timestamp, + ost_token as token, + ost_token_secret as token_secret, + ost_referrer_host as token_referrer_host, + osr_callback_uri as callback_uri + FROM oauth_server_registry + JOIN oauth_server_token + ON ost_osr_id_ref = osr_id + WHERE ost_usa_id_ref = %d + AND ost_token_type = \'access\' + AND ost_token_ttl >= NOW() + ORDER BY osr_application_title + ', $user_id); + return $rs; + } + + + /** + * Check an nonce/timestamp combination. Clears any nonce combinations + * that are older than the one received. + * + * @param string consumer_key + * @param string token + * @param int timestamp + * @param string nonce + * @exception OAuthException2 thrown when the timestamp is not in sequence or nonce is not unique + */ + public function checkServerNonce ( $consumer_key, $token, $timestamp, $nonce ) + { + /* removed in Appendix A of RFC 5849 + $r = $this->query_row(' + SELECT MAX(osn_timestamp), MAX(osn_timestamp) > %d + %d + FROM oauth_server_nonce + WHERE osn_consumer_key = \'%s\' + AND osn_token = \'%s\' + ', $timestamp, $this->max_timestamp_skew, $consumer_key, $token); + + if (!empty($r) && $r[1]) + { + throw new OAuthException2('Timestamp is out of sequence. Request rejected. Got '.$timestamp.' last max is '.$r[0].' allowed skew is '.$this->max_timestamp_skew); + }*/ + + // Insert the new combination + $this->query(' + INSERT IGNORE INTO oauth_server_nonce + SET osn_consumer_key = \'%s\', + osn_token = \'%s\', + osn_timestamp = %d, + osn_nonce = \'%s\' + ', $consumer_key, $token, $timestamp, $nonce); + + if ($this->query_affected_rows() == 0) + { + throw new OAuthException2('Duplicate timestamp/nonce combination, possible replay attack. Request rejected.'); + } + + // Clean up all timestamps older than the one we just received + $this->query(' + DELETE FROM oauth_server_nonce + WHERE osn_consumer_key = \'%s\' + AND osn_token = \'%s\' + AND osn_timestamp < %d - %d + ', $consumer_key, $token, $timestamp, $this->max_timestamp_skew); + } + + + /** + * Add an entry to the log table + * + * @param array keys (osr_consumer_key, ost_token, ocr_consumer_key, oct_token) + * @param string received + * @param string sent + * @param string base_string + * @param string notes + * @param int (optional) user_id + */ + public function addLog ( $keys, $received, $sent, $base_string, $notes, $user_id = null ) + { + $args = array(); + $ps = array(); + foreach ($keys as $key => $value) + { + $args[] = $value; + $ps[] = "olg_$key = '%s'"; + } + + if (!empty($_SERVER['REMOTE_ADDR'])) + { + $remote_ip = $_SERVER['REMOTE_ADDR']; + } + else if (!empty($_SERVER['REMOTE_IP'])) + { + $remote_ip = $_SERVER['REMOTE_IP']; + } + else + { + $remote_ip = '0.0.0.0'; + } + + // Build the SQL + $ps[] = "olg_received = '%s'"; $args[] = $this->makeUTF8($received); + $ps[] = "olg_sent = '%s'"; $args[] = $this->makeUTF8($sent); + $ps[] = "olg_base_string= '%s'"; $args[] = $base_string; + $ps[] = "olg_notes = '%s'"; $args[] = $this->makeUTF8($notes); + $ps[] = "olg_usa_id_ref = NULLIF(%d,0)"; $args[] = $user_id; + $ps[] = "olg_remote_ip = IFNULL(INET_ATON('%s'),0)"; $args[] = $remote_ip; + + $this->query('INSERT INTO oauth_log SET '.implode(',', $ps), $args); + } + + + /** + * Get a page of entries from the log. Returns the last 100 records + * matching the options given. + * + * @param array options + * @param int user_id current user + * @return array log records + */ + public function listLog ( $options, $user_id ) + { + $where = array(); + $args = array(); + if (empty($options)) + { + $where[] = 'olg_usa_id_ref = %d'; + $args[] = $user_id; + } + else + { + foreach ($options as $option => $value) + { + if (strlen($value) > 0) + { + switch ($option) + { + case 'osr_consumer_key': + case 'ocr_consumer_key': + case 'ost_token': + case 'oct_token': + $where[] = 'olg_'.$option.' = \'%s\''; + $args[] = $value; + break; + } + } + } + + $where[] = '(olg_usa_id_ref IS NULL OR olg_usa_id_ref = %d)'; + $args[] = $user_id; + } + + $rs = $this->query_all_assoc(' + SELECT olg_id, + olg_osr_consumer_key AS osr_consumer_key, + olg_ost_token AS ost_token, + olg_ocr_consumer_key AS ocr_consumer_key, + olg_oct_token AS oct_token, + olg_usa_id_ref AS user_id, + olg_received AS received, + olg_sent AS sent, + olg_base_string AS base_string, + olg_notes AS notes, + olg_timestamp AS timestamp, + INET_NTOA(olg_remote_ip) AS remote_ip + FROM oauth_log + WHERE '.implode(' AND ', $where).' + ORDER BY olg_id DESC + LIMIT 0,100', $args); + + return $rs; + } + + + /* ** Some simple helper functions for querying the mysql db ** */ + + /** + * Perform a query, ignore the results + * + * @param string sql + * @param vararg arguments (for sprintf) + */ + abstract protected function query ( $sql ); + + + /** + * Perform a query, ignore the results + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return array + */ + abstract protected function query_all_assoc ( $sql ); + + + /** + * Perform a query, return the first row + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return array + */ + abstract protected function query_row_assoc ( $sql ); + + /** + * Perform a query, return the first row + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return array + */ + abstract protected function query_row ( $sql ); + + + /** + * Perform a query, return the first column of the first row + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return mixed + */ + abstract protected function query_one ( $sql ); + + + /** + * Return the number of rows affected in the last query + */ + abstract protected function query_affected_rows (); + + + /** + * Return the id of the last inserted row + * + * @return int + */ + abstract protected function query_insert_id (); + + + abstract protected function sql_printf ( $args ); + + + abstract protected function sql_escape_string ( $s ); + + + abstract protected function sql_errcheck ( $sql ); +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/library/store/OAuthStoreSession.php b/vendor/oauth-php/library/store/OAuthStoreSession.php new file mode 100644 index 0000000..ecb30c8 --- /dev/null +++ b/vendor/oauth-php/library/store/OAuthStoreSession.php @@ -0,0 +1,157 @@ +<?php + +/** + * OAuthSession is a really *dirty* storage. It's useful for testing and may + * be enough for some very simple applications, but it's not recommended for + * production use. + * + * @version $Id: OAuthStoreSession.php 183 2011-01-14 11:43:27Z brunobg@corollarium.com $ + * @author BBG + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +require_once dirname(__FILE__) . '/OAuthStoreAbstract.class.php'; + +class OAuthStoreSession extends OAuthStoreAbstract +{ + private $session; + + /* + * Takes two options: consumer_key and consumer_secret + */ + public function __construct( $options = array() ) + { + if (!session_id()) { + session_start(); + } + if(isset($options['consumer_key']) && isset($options['consumer_secret'])) + { + $this->session = &$_SESSION['oauth_' . $options['consumer_key']]; + $this->session['consumer_key'] = $options['consumer_key']; + $this->session['consumer_secret'] = $options['consumer_secret']; + $this->session['signature_methods'] = array('HMAC-SHA1'); + $this->session['server_uri'] = $options['server_uri']; + $this->session['request_token_uri'] = $options['request_token_uri']; + $this->session['authorize_uri'] = $options['authorize_uri']; + $this->session['access_token_uri'] = $options['access_token_uri']; + + } + else + { + throw new OAuthException2("OAuthStoreSession needs consumer_key and consumer_secret"); + } + } + + public function getSecretsForVerify ( $consumer_key, $token, $token_type = 'access' ) { throw new OAuthException2("OAuthStoreSession doesn't support " . __METHOD__); } + public function getSecretsForSignature ( $uri, $user_id ) + { + return $this->session; + } + + public function getServerTokenSecrets ( $consumer_key, $token, $token_type, $user_id, $name = '') + { + if ($consumer_key != $this->session['consumer_key']) { + return array(); + } + return array( + 'consumer_key' => $consumer_key, + 'consumer_secret' => $this->session['consumer_secret'], + 'token' => $token, + 'token_secret' => $this->session['token_secret'], + 'token_name' => $name, + 'signature_methods' => $this->session['signature_methods'], + 'server_uri' => $this->session['server_uri'], + 'request_token_uri' => $this->session['request_token_uri'], + 'authorize_uri' => $this->session['authorize_uri'], + 'access_token_uri' => $this->session['access_token_uri'], + 'token_ttl' => 3600, + ); + } + + public function addServerToken ( $consumer_key, $token_type, $token, $token_secret, $user_id, $options = array() ) + { + $this->session['token_type'] = $token_type; + $this->session['token'] = $token; + $this->session['token_secret'] = $token_secret; + } + + public function deleteServer ( $consumer_key, $user_id, $user_is_admin = false ) { throw new OAuthException2("OAuthStoreSession doesn't support " . __METHOD__); } + public function getServer( $consumer_key, $user_id, $user_is_admin = false ) { + return array( + 'id' => 0, + 'user_id' => $user_id, + 'consumer_key' => $this->session['consumer_key'], + 'consumer_secret' => $this->session['consumer_secret'], + 'signature_methods' => $this->session['signature_methods'], + 'server_uri' => $this->session['server_uri'], + 'request_token_uri' => $this->session['request_token_uri'], + 'authorize_uri' => $this->session['authorize_uri'], + 'access_token_uri' => $this->session['access_token_uri'], + ); + } + + public function getServerForUri ( $uri, $user_id ) { throw new OAuthException2("OAuthStoreSession doesn't support " . __METHOD__); } + public function listServerTokens ( $user_id ) { throw new OAuthException2("OAuthStoreSession doesn't support " . __METHOD__); } + public function countServerTokens ( $consumer_key ) { throw new OAuthException2("OAuthStoreSession doesn't support " . __METHOD__); } + public function getServerToken ( $consumer_key, $token, $user_id ) { throw new OAuthException2("OAuthStoreSession doesn't support " . __METHOD__); } + public function deleteServerToken ( $consumer_key, $token, $user_id, $user_is_admin = false ) { + // TODO + } + + public function setServerTokenTtl ( $consumer_key, $token, $token_ttl, $server_uri = NULL ) + { + //This method just needs to exist. It doesn't have to do anything! + } + + public function listServers ( $q = '', $user_id ) { throw new OAuthException2("OAuthStoreSession doesn't support " . __METHOD__); } + public function updateServer ( $server, $user_id, $user_is_admin = false ) { throw new OAuthException2("OAuthStoreSession doesn't support " . __METHOD__); } + + public function updateConsumer ( $consumer, $user_id, $user_is_admin = false ) { throw new OAuthException2("OAuthStoreSession doesn't support " . __METHOD__); } + public function deleteConsumer ( $consumer_key, $user_id, $user_is_admin = false ) { throw new OAuthException2("OAuthStoreSession doesn't support " . __METHOD__); } + public function getConsumer ( $consumer_key, $user_id, $user_is_admin = false ) { throw new OAuthException2("OAuthStoreSession doesn't support " . __METHOD__); } + public function getConsumerStatic () { throw new OAuthException2("OAuthStoreSession doesn't support " . __METHOD__); } + + public function addConsumerRequestToken ( $consumer_key, $options = array() ) { throw new OAuthException2("OAuthStoreSession doesn't support " . __METHOD__); } + public function getConsumerRequestToken ( $token ) { throw new OAuthException2("OAuthStoreSession doesn't support " . __METHOD__); } + public function deleteConsumerRequestToken ( $token ) { throw new OAuthException2("OAuthStoreSession doesn't support " . __METHOD__); } + public function authorizeConsumerRequestToken ( $token, $user_id, $referrer_host = '' ) { throw new OAuthException2("OAuthStoreSession doesn't support " . __METHOD__); } + public function countConsumerAccessTokens ( $consumer_key ) { throw new OAuthException2("OAuthStoreSession doesn't support " . __METHOD__); } + public function exchangeConsumerRequestForAccessToken ( $token, $options = array() ) { throw new OAuthException2("OAuthStoreSession doesn't support " . __METHOD__); } + public function getConsumerAccessToken ( $token, $user_id ) { throw new OAuthException2("OAuthStoreSession doesn't support " . __METHOD__); } + public function deleteConsumerAccessToken ( $token, $user_id, $user_is_admin = false ) { throw new OAuthException2("OAuthStoreSession doesn't support " . __METHOD__); } + public function setConsumerAccessTokenTtl ( $token, $ttl ) { throw new OAuthException2("OAuthStoreSession doesn't support " . __METHOD__); } + + public function listConsumers ( $user_id ) { throw new OAuthException2("OAuthStoreSession doesn't support " . __METHOD__); } + public function listConsumerApplications( $begin = 0, $total = 25 ) { throw new OAuthException2("OAuthStoreSession doesn't support " . __METHOD__); } + public function listConsumerTokens ( $user_id ) { throw new OAuthException2("OAuthStoreSession doesn't support " . __METHOD__); } + + public function checkServerNonce ( $consumer_key, $token, $timestamp, $nonce ) { throw new OAuthException2("OAuthStoreSession doesn't support " . __METHOD__); } + + public function addLog ( $keys, $received, $sent, $base_string, $notes, $user_id = null ) { throw new OAuthException2("OAuthStoreSession doesn't support " . __METHOD__); } + public function listLog ( $options, $user_id ) { throw new OAuthException2("OAuthStoreSession doesn't support " . __METHOD__); } + + public function install () { throw new OAuthException2("OAuthStoreSession doesn't support " . __METHOD__); } +} + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/library/store/mysql/install.php b/vendor/oauth-php/library/store/mysql/install.php new file mode 100644 index 0000000..0015da5 --- /dev/null +++ b/vendor/oauth-php/library/store/mysql/install.php @@ -0,0 +1,32 @@ +<?php + +/** + * Installs all tables in the mysql.sql file, using the default mysql connection + */ + +/* Change and uncomment this when you need to: */ + +/* +mysql_connect('localhost', 'root'); +if (mysql_errno()) +{ + die(' Error '.mysql_errno().': '.mysql_error()); +} +mysql_select_db('test'); +*/ + +$sql = file_get_contents(dirname(__FILE__) . '/mysql.sql'); +$ps = explode('#--SPLIT--', $sql); + +foreach ($ps as $p) +{ + $p = preg_replace('/^\s*#.*$/m', '', $p); + + mysql_query($p); + if (mysql_errno()) + { + die(' Error '.mysql_errno().': '.mysql_error()); + } +} + +?>
\ No newline at end of file diff --git a/vendor/oauth-php/library/store/mysql/mysql.sql b/vendor/oauth-php/library/store/mysql/mysql.sql new file mode 100644 index 0000000..ca8eb08 --- /dev/null +++ b/vendor/oauth-php/library/store/mysql/mysql.sql @@ -0,0 +1,240 @@ +# Datamodel for OAuthStoreMySQL +# +# You need to add the foreign key constraints for the user ids your are using. +# I have commented the constraints out, just look for 'usa_id_ref' to enable them. +# +# The --SPLIT-- markers are used by the install.php script +# +# @version $Id: mysql.sql 188 2011-02-25 14:40:26Z scherpenisse $ +# @author Marc Worrell +# + +# Changes: +# +# 2011-02-25 +# ALTER TABLE oauth_consumer_token MODIFY oct_token varchar(255) binary not null; +# ALTER TABLE oauth_consumer_token MODIFY oct_token_secret varchar(255) binary not null; +# +# 2010-09-15 +# ALTER TABLE oauth_server_token MODIFY ost_referrer_host varchar(128) not null default ''; +# +# 2010-07-22 +# ALTER TABLE oauth_consumer_registry DROP INDEX ocr_consumer_key; +# ALTER TABLE oauth_consumer_registry ADD UNIQUE ocr_consumer_key(ocr_consumer_key,ocr_usa_id_ref,ocr_server_uri) +# +# 2010-04-20 (on 103 and 110) +# ALTER TABLE oauth_consumer_registry MODIFY ocr_consumer_key varchar(128) binary not null; +# ALTER TABLE oauth_consumer_registry MODIFY ocr_consumer_secret varchar(128) binary not null; +# +# 2010-04-20 (on 103 and 110) +# ALTER TABLE oauth_server_token ADD ost_verifier char(10); +# ALTER TABLE oauth_server_token ADD ost_callback_url varchar(512); +# +# 2008-10-15 (on r48) Added ttl to consumer and server tokens, added named server tokens +# +# ALTER TABLE oauth_server_token +# ADD ost_token_ttl datetime not null default '9999-12-31', +# ADD KEY (ost_token_ttl); +# +# ALTER TABLE oauth_consumer_token +# ADD oct_name varchar(64) binary not null default '', +# ADD oct_token_ttl datetime not null default '9999-12-31', +# DROP KEY oct_usa_id_ref, +# ADD UNIQUE KEY (oct_usa_id_ref, oct_ocr_id_ref, oct_token_type, oct_name), +# ADD KEY (oct_token_ttl); +# +# 2008-09-09 (on r5) Added referrer host to server access token +# +# ALTER TABLE oauth_server_token ADD ost_referrer_host VARCHAR(128) NOT NULL; +# + + +# +# Log table to hold all OAuth request when you enabled logging +# + +CREATE TABLE IF NOT EXISTS oauth_log ( + olg_id int(11) not null auto_increment, + olg_osr_consumer_key varchar(64) binary, + olg_ost_token varchar(64) binary, + olg_ocr_consumer_key varchar(64) binary, + olg_oct_token varchar(64) binary, + olg_usa_id_ref int(11), + olg_received text not null, + olg_sent text not null, + olg_base_string text not null, + olg_notes text not null, + olg_timestamp timestamp not null default current_timestamp, + olg_remote_ip bigint not null, + + primary key (olg_id), + key (olg_osr_consumer_key, olg_id), + key (olg_ost_token, olg_id), + key (olg_ocr_consumer_key, olg_id), + key (olg_oct_token, olg_id), + key (olg_usa_id_ref, olg_id) + +# , foreign key (olg_usa_id_ref) references any_user_auth (usa_id_ref) +# on update cascade +# on delete cascade +) engine=InnoDB default charset=utf8; + +#--SPLIT-- + +# +# /////////////////// CONSUMER SIDE /////////////////// +# + +# This is a registry of all consumer codes we got from other servers +# The consumer_key/secret is obtained from the server +# We also register the server uri, so that we can find the consumer key and secret +# for a certain server. From that server we can check if we have a token for a +# particular user. + +CREATE TABLE IF NOT EXISTS oauth_consumer_registry ( + ocr_id int(11) not null auto_increment, + ocr_usa_id_ref int(11), + ocr_consumer_key varchar(128) binary not null, + ocr_consumer_secret varchar(128) binary not null, + ocr_signature_methods varchar(255) not null default 'HMAC-SHA1,PLAINTEXT', + ocr_server_uri varchar(255) not null, + ocr_server_uri_host varchar(128) not null, + ocr_server_uri_path varchar(128) binary not null, + + ocr_request_token_uri varchar(255) not null, + ocr_authorize_uri varchar(255) not null, + ocr_access_token_uri varchar(255) not null, + ocr_timestamp timestamp not null default current_timestamp, + + primary key (ocr_id), + unique key (ocr_consumer_key, ocr_usa_id_ref, ocr_server_uri), + key (ocr_server_uri), + key (ocr_server_uri_host, ocr_server_uri_path), + key (ocr_usa_id_ref) + +# , foreign key (ocr_usa_id_ref) references any_user_auth(usa_id_ref) +# on update cascade +# on delete set null +) engine=InnoDB default charset=utf8; + +#--SPLIT-- + +# Table used to sign requests for sending to a server by the consumer +# The key is defined for a particular user. Only one single named +# key is allowed per user/server combination + +CREATE TABLE IF NOT EXISTS oauth_consumer_token ( + oct_id int(11) not null auto_increment, + oct_ocr_id_ref int(11) not null, + oct_usa_id_ref int(11) not null, + oct_name varchar(64) binary not null default '', + oct_token varchar(255) binary not null, + oct_token_secret varchar(255) binary not null, + oct_token_type enum('request','authorized','access'), + oct_token_ttl datetime not null default '9999-12-31', + oct_timestamp timestamp not null default current_timestamp, + + primary key (oct_id), + unique key (oct_ocr_id_ref, oct_token), + unique key (oct_usa_id_ref, oct_ocr_id_ref, oct_token_type, oct_name), + key (oct_token_ttl), + + foreign key (oct_ocr_id_ref) references oauth_consumer_registry (ocr_id) + on update cascade + on delete cascade + +# , foreign key (oct_usa_id_ref) references any_user_auth (usa_id_ref) +# on update cascade +# on delete cascade +) engine=InnoDB default charset=utf8; + +#--SPLIT-- + + +# +# ////////////////// SERVER SIDE ///////////////// +# + +# Table holding consumer key/secret combos an user issued to consumers. +# Used for verification of incoming requests. + +CREATE TABLE IF NOT EXISTS oauth_server_registry ( + osr_id int(11) not null auto_increment, + osr_usa_id_ref int(11), + osr_consumer_key varchar(64) binary not null, + osr_consumer_secret varchar(64) binary not null, + osr_enabled tinyint(1) not null default '1', + osr_status varchar(16) not null, + osr_requester_name varchar(64) not null, + osr_requester_email varchar(64) not null, + osr_callback_uri varchar(255) not null, + osr_application_uri varchar(255) not null, + osr_application_title varchar(80) not null, + osr_application_descr text not null, + osr_application_notes text not null, + osr_application_type varchar(20) not null, + osr_application_commercial tinyint(1) not null default '0', + osr_issue_date datetime not null, + osr_timestamp timestamp not null default current_timestamp, + + primary key (osr_id), + unique key (osr_consumer_key), + key (osr_usa_id_ref) + +# , foreign key (osr_usa_id_ref) references any_user_auth(usa_id_ref) +# on update cascade +# on delete set null +) engine=InnoDB default charset=utf8; + +#--SPLIT-- + +# Nonce used by a certain consumer, every used nonce should be unique, this prevents +# replaying attacks. We need to store all timestamp/nonce combinations for the +# maximum timestamp received. + +CREATE TABLE IF NOT EXISTS oauth_server_nonce ( + osn_id int(11) not null auto_increment, + osn_consumer_key varchar(64) binary not null, + osn_token varchar(64) binary not null, + osn_timestamp bigint not null, + osn_nonce varchar(80) binary not null, + + primary key (osn_id), + unique key (osn_consumer_key, osn_token, osn_timestamp, osn_nonce) +) engine=InnoDB default charset=utf8; + +#--SPLIT-- + +# Table used to verify signed requests sent to a server by the consumer +# When the verification is succesful then the associated user id is returned. + +CREATE TABLE IF NOT EXISTS oauth_server_token ( + ost_id int(11) not null auto_increment, + ost_osr_id_ref int(11) not null, + ost_usa_id_ref int(11) not null, + ost_token varchar(64) binary not null, + ost_token_secret varchar(64) binary not null, + ost_token_type enum('request','access'), + ost_authorized tinyint(1) not null default '0', + ost_referrer_host varchar(128) not null default '', + ost_token_ttl datetime not null default '9999-12-31', + ost_timestamp timestamp not null default current_timestamp, + ost_verifier char(10), + ost_callback_url varchar(512), + + primary key (ost_id), + unique key (ost_token), + key (ost_osr_id_ref), + key (ost_token_ttl), + + foreign key (ost_osr_id_ref) references oauth_server_registry (osr_id) + on update cascade + on delete cascade + +# , foreign key (ost_usa_id_ref) references any_user_auth (usa_id_ref) +# on update cascade +# on delete cascade +) engine=InnoDB default charset=utf8; + + + diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/1_Tables/TABLES.sql b/vendor/oauth-php/library/store/oracle/OracleDB/1_Tables/TABLES.sql new file mode 100644 index 0000000..3d4fa22 --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/1_Tables/TABLES.sql @@ -0,0 +1,114 @@ +CREATE TABLE oauth_log
+(
+ olg_id number,
+ olg_osr_consumer_key varchar2(64),
+ olg_ost_token varchar2(64),
+ olg_ocr_consumer_key varchar2(64),
+ olg_oct_token varchar2(64),
+ olg_usa_id_ref number,
+ olg_received varchar2(500),
+ olg_sent varchar2(500),
+ olg_base_string varchar2(500),
+ olg_notes varchar2(500),
+ olg_timestamp date default sysdate,
+ olg_remote_ip varchar2(50)
+);
+
+alter table oauth_log
+ add constraint oauth_log_pk primary key (olg_id);
+
+
+CREATE TABLE oauth_consumer_registry
+(
+ ocr_id number,
+ ocr_usa_id_ref number,
+ ocr_consumer_key varchar2(64),
+ ocr_consumer_secret varchar2(64),
+ ocr_signature_methods varchar2(255)default 'HMAC-SHA1,PLAINTEXT',
+ ocr_server_uri varchar2(255),
+ ocr_server_uri_host varchar2(128),
+ ocr_server_uri_path varchar2(128),
+ ocr_request_token_uri varchar2(255),
+ ocr_authorize_uri varchar2(255),
+ ocr_access_token_uri varchar2(255),
+ ocr_timestamp date default sysdate
+)
+
+alter table oauth_consumer_registry
+ add constraint oauth_consumer_registry_pk primary key (ocr_id);
+
+
+CREATE TABLE oauth_consumer_token
+(
+ oct_id number,
+ oct_ocr_id_ref number,
+ oct_usa_id_ref number,
+ oct_name varchar2(64) default '',
+ oct_token varchar2(64),
+ oct_token_secret varchar2(64),
+ oct_token_type varchar2(20), -- enum('request','authorized','access'),
+ oct_token_ttl date default TO_DATE('9999.12.31', 'yyyy.mm.dd'),
+ oct_timestamp date default sysdate
+);
+
+alter table oauth_consumer_token
+ add constraint oauth_consumer_token_pk primary key (oct_id);
+
+
+CREATE TABLE oauth_server_registry
+(
+ osr_id number,
+ osr_usa_id_ref number,
+ osr_consumer_key varchar2(64),
+ osr_consumer_secret varchar2(64),
+ osr_enabled integer default '1',
+ osr_status varchar2(16),
+ osr_requester_name varchar2(64),
+ osr_requester_email varchar2(64),
+ osr_callback_uri varchar2(255),
+ osr_application_uri varchar2(255),
+ osr_application_title varchar2(80),
+ osr_application_descr varchar2(500),
+ osr_application_notes varchar2(500),
+ osr_application_type varchar2(20),
+ osr_application_commercial integer default '0',
+ osr_issue_date date,
+ osr_timestamp date default sysdate
+);
+
+
+alter table oauth_server_registry
+ add constraint oauth_server_registry_pk primary key (osr_id);
+
+
+CREATE TABLE oauth_server_nonce
+(
+ osn_id number,
+ osn_consumer_key varchar2(64),
+ osn_token varchar2(64),
+ osn_timestamp number,
+ osn_nonce varchar2(80)
+);
+
+alter table oauth_server_nonce
+ add constraint oauth_server_nonce_pk primary key (osn_id);
+
+
+CREATE TABLE oauth_server_token
+(
+ ost_id number,
+ ost_osr_id_ref number,
+ ost_usa_id_ref number,
+ ost_token varchar2(64),
+ ost_token_secret varchar2(64),
+ ost_token_type varchar2(20), -- enum('request','access'),
+ ost_authorized integer default '0',
+ ost_referrer_host varchar2(128),
+ ost_token_ttl date default TO_DATE('9999.12.31', 'yyyy.mm.dd'),
+ ost_timestamp date default sysdate,
+ ost_verifier varchar2(10),
+ ost_callback_url varchar2(512)
+);
+
+alter table oauth_server_token
+ add constraint oauth_server_token_pk primary key (ost_id);
\ No newline at end of file diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/2_Sequences/SEQUENCES.sql b/vendor/oauth-php/library/store/oracle/OracleDB/2_Sequences/SEQUENCES.sql new file mode 100644 index 0000000..53e4227 --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/2_Sequences/SEQUENCES.sql @@ -0,0 +1,9 @@ +CREATE SEQUENCE SEQ_OCT_ID NOCACHE;
+
+CREATE SEQUENCE SEQ_OCR_ID NOCACHE;
+
+CREATE SEQUENCE SEQ_OSR_ID NOCACHE;
+
+CREATE SEQUENCE SEQ_OSN_ID NOCACHE;
+
+CREATE SEQUENCE SEQ_OLG_ID NOCACHE;
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_ADD_CONSUMER_REQUEST_TOKEN.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_ADD_CONSUMER_REQUEST_TOKEN.prc new file mode 100644 index 0000000..efb9536 --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_ADD_CONSUMER_REQUEST_TOKEN.prc @@ -0,0 +1,71 @@ +CREATE OR REPLACE PROCEDURE SP_ADD_CONSUMER_REQUEST_TOKEN
+(
+P_TOKEN_TTL IN NUMBER, -- IN SECOND
+P_CONSUMER_KEY IN VARCHAR2,
+P_TOKEN IN VARCHAR2,
+P_TOKEN_SECRET IN VARCHAR2,
+P_CALLBACK_URL IN VARCHAR2,
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- PROCEDURE TO Add an unautorized request token to our server.
+
+V_OSR_ID NUMBER;
+V_OSR_ID_REF NUMBER;
+
+V_EXC_NO_SERVER_EXIST EXCEPTION;
+BEGIN
+
+ P_RESULT := 0;
+
+ BEGIN
+ SELECT OSR_ID INTO V_OSR_ID
+ FROM OAUTH_SERVER_REGISTRY
+ WHERE OSR_CONSUMER_KEY = P_CONSUMER_KEY
+ AND OSR_ENABLED = 1;
+ EXCEPTION
+ WHEN NO_DATA_FOUND THEN
+ RAISE V_EXC_NO_SERVER_EXIST;
+ END;
+
+
+BEGIN
+ SELECT OST_OSR_ID_REF INTO V_OSR_ID_REF
+ FROM OAUTH_SERVER_TOKEN
+ WHERE OST_OSR_ID_REF = V_OSR_ID;
+
+ UPDATE OAUTH_SERVER_TOKEN
+ SET OST_OSR_ID_REF = V_OSR_ID,
+ OST_USA_ID_REF = 1,
+ OST_TOKEN = P_TOKEN,
+ OST_TOKEN_SECRET = P_TOKEN_SECRET,
+ OST_TOKEN_TYPE = 'REQUEST',
+ OST_TOKEN_TTL = SYSDATE + (P_TOKEN_TTL/(24*60*60)),
+ OST_CALLBACK_URL = P_CALLBACK_URL,
+ OST_TIMESTAMP = SYSDATE
+ WHERE OST_OSR_ID_REF = V_OSR_ID_REF;
+
+
+ EXCEPTION
+ WHEN NO_DATA_FOUND THEN
+
+ INSERT INTO OAUTH_SERVER_TOKEN
+ (OST_ID, OST_OSR_ID_REF, OST_USA_ID_REF, OST_TOKEN, OST_TOKEN_SECRET, OST_TOKEN_TYPE,
+ OST_TOKEN_TTL, OST_CALLBACK_URL)
+ VALUES
+ (SEQ_OCT_ID.NEXTVAL, V_OSR_ID, 1, P_TOKEN, P_TOKEN_SECRET, 'REQUEST', SYSDATE + (P_TOKEN_TTL/(24*60*60)),
+ P_CALLBACK_URL);
+
+ END;
+
+
+EXCEPTION
+WHEN V_EXC_NO_SERVER_EXIST THEN
+P_RESULT := 2; -- NO_SERVER_EXIST
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_ADD_LOG.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_ADD_LOG.prc new file mode 100644 index 0000000..329499d --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_ADD_LOG.prc @@ -0,0 +1,31 @@ +CREATE OR REPLACE PROCEDURE SP_ADD_LOG
+(
+P_RECEIVED IN VARCHAR2,
+P_SENT IN VARCHAR2,
+P_BASE_STRING IN VARCHAR2,
+P_NOTES IN VARCHAR2,
+P_USA_ID_REF IN NUMBER,
+P_REMOTE_IP IN VARCHAR2,
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- PROCEDURE TO Add an entry to the log table
+
+BEGIN
+
+ P_RESULT := 0;
+
+ INSERT INTO oauth_log
+ (OLG_ID, olg_received, olg_sent, olg_base_string, olg_notes, olg_usa_id_ref, olg_remote_ip)
+ VALUES
+ (SEQ_OLG_ID.NEXTVAL, P_RECEIVED, P_SENT, P_BASE_STRING, P_NOTES, NVL(P_USA_ID_REF, 0), P_REMOTE_IP);
+
+
+EXCEPTION
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_ADD_SERVER_TOKEN.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_ADD_SERVER_TOKEN.prc new file mode 100644 index 0000000..371134c --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_ADD_SERVER_TOKEN.prc @@ -0,0 +1,55 @@ +CREATE OR REPLACE PROCEDURE SP_ADD_SERVER_TOKEN
+(
+P_CONSUMER_KEY IN VARCHAR2,
+P_USER_ID IN NUMBER,
+P_NAME IN VARCHAR2,
+P_TOKEN_TYPE IN VARCHAR2,
+P_TOKEN IN VARCHAR2,
+P_TOKEN_SECRET IN VARCHAR2,
+P_TOKEN_INTERVAL_IN_SEC IN NUMBER,
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- Add a request token we obtained from a server.
+V_OCR_ID NUMBER;
+V_TOKEN_TTL DATE;
+
+V_EXC_INVALID_CONSUMER_KEY EXCEPTION;
+BEGIN
+P_RESULT := 0;
+
+ BEGIN
+ SELECT OCR_ID INTO V_OCR_ID FROM OAUTH_CONSUMER_REGISTRY
+ WHERE OCR_CONSUMER_KEY = P_CONSUMER_KEY AND OCR_USA_ID_REF = P_USER_ID;
+ EXCEPTION
+ WHEN NO_DATA_FOUND THEN
+ RAISE V_EXC_INVALID_CONSUMER_KEY;
+ END;
+
+ DELETE FROM OAUTH_CONSUMER_TOKEN
+ WHERE OCT_OCR_ID_REF = V_OCR_ID
+ AND OCT_USA_ID_REF = P_USER_ID
+ AND UPPER(OCT_TOKEN_TYPE) = UPPER(P_TOKEN_TYPE)
+ AND OCT_NAME = P_NAME;
+
+ IF P_TOKEN_INTERVAL_IN_SEC IS NOT NULL THEN
+ V_TOKEN_TTL := SYSDATE + (P_TOKEN_INTERVAL_IN_SEC/(24*60*60));
+ ELSE
+ V_TOKEN_TTL := TO_DATE('9999.12.31', 'yyyy.mm.dd');
+ END IF;
+
+ INSERT INTO OAUTH_CONSUMER_TOKEN
+ (OCT_ID, OCT_OCR_ID_REF,OCT_USA_ID_REF, OCT_NAME, OCT_TOKEN, OCT_TOKEN_SECRET, OCT_TOKEN_TYPE, OCT_TIMESTAMP, OCT_TOKEN_TTL)
+ VALUES
+ (SEQ_OCT_ID.NEXTVAL, V_OCR_ID, P_USER_ID, P_NAME, P_TOKEN, P_TOKEN_SECRET, UPPER(P_TOKEN_TYPE), SYSDATE, V_TOKEN_TTL);
+
+EXCEPTION
+WHEN V_EXC_INVALID_CONSUMER_KEY THEN
+P_RESULT := 2; -- INVALID_CONSUMER_KEY
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_AUTH_CONSUMER_REQ_TOKEN.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_AUTH_CONSUMER_REQ_TOKEN.prc new file mode 100644 index 0000000..c369349 --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_AUTH_CONSUMER_REQ_TOKEN.prc @@ -0,0 +1,32 @@ +CREATE OR REPLACE PROCEDURE SP_AUTH_CONSUMER_REQ_TOKEN
+(
+P_USER_ID IN NUMBER,
+P_REFERRER_HOST IN VARCHAR2,
+P_VERIFIER IN VARCHAR2,
+P_TOKEN IN VARCHAR2,
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- PROCEDURE TO Fetch the consumer request token, by request token.
+BEGIN
+P_RESULT := 0;
+
+
+UPDATE OAUTH_SERVER_TOKEN
+ SET OST_AUTHORIZED = 1,
+ OST_USA_ID_REF = P_USER_ID,
+ OST_TIMESTAMP = SYSDATE,
+ OST_REFERRER_HOST = P_REFERRER_HOST,
+ OST_VERIFIER = P_VERIFIER
+ WHERE OST_TOKEN = P_TOKEN
+ AND OST_TOKEN_TYPE = 'REQUEST';
+
+
+EXCEPTION
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_CHECK_SERVER_NONCE.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_CHECK_SERVER_NONCE.prc new file mode 100644 index 0000000..765dd3b --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_CHECK_SERVER_NONCE.prc @@ -0,0 +1,82 @@ +CREATE OR REPLACE PROCEDURE SP_CHECK_SERVER_NONCE
+(
+P_CONSUMER_KEY IN VARCHAR2,
+P_TOKEN IN VARCHAR2,
+P_TIMESTAMP IN NUMBER,
+P_MAX_TIMESTAMP_SKEW IN NUMBER,
+P_NONCE IN VARCHAR2,
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- PROCEDURE TO Check an nonce/timestamp combination. Clears any nonce combinations
+ -- that are older than the one received.
+V_IS_MAX NUMBER;
+V_MAX_TIMESTAMP NUMBER;
+V_IS_DUPLICATE_TIMESTAMP NUMBER;
+
+V_EXC_INVALID_TIMESTAMP EXCEPTION;
+V_EXC_DUPLICATE_TIMESTAMP EXCEPTION;
+BEGIN
+
+ P_RESULT := 0;
+
+ -- removed in Appendix A of RFC 5849
+ -- BEGIN
+ -- SELECT MAX(OSN_TIMESTAMP),
+ -- CASE
+ -- WHEN MAX(OSN_TIMESTAMP) > (P_TIMESTAMP + P_MAX_TIMESTAMP_SKEW) THEN 1 ELSE 0
+ -- END "IS_MAX" INTO V_MAX_TIMESTAMP, V_IS_MAX
+ -- FROM OAUTH_SERVER_NONCE
+ -- WHERE OSN_CONSUMER_KEY = P_CONSUMER_KEY
+ -- AND OSN_TOKEN = P_TOKEN;
+ --
+ -- IF V_IS_MAX = 1 THEN
+ -- RAISE V_EXC_INVALID_TIMESTAMP;
+ -- END IF;
+ --
+ -- EXCEPTION
+ -- WHEN NO_DATA_FOUND THEN
+ -- NULL;
+ -- END;
+
+ BEGIN
+ SELECT 1 INTO V_IS_DUPLICATE_TIMESTAMP FROM DUAL WHERE EXISTS
+ (SELECT OSN_ID FROM OAUTH_SERVER_NONCE
+ WHERE OSN_CONSUMER_KEY = P_CONSUMER_KEY
+ AND OSN_TOKEN = P_TOKEN
+ AND OSN_TIMESTAMP = P_TIMESTAMP
+ AND OSN_NONCE = P_NONCE);
+
+ IF V_IS_DUPLICATE_TIMESTAMP = 1 THEN
+ RAISE V_EXC_DUPLICATE_TIMESTAMP;
+ END IF;
+ EXCEPTION
+ WHEN NO_DATA_FOUND THEN
+ NULL;
+ END;
+
+ -- Insert the new combination
+ INSERT INTO OAUTH_SERVER_NONCE
+ (OSN_ID, OSN_CONSUMER_KEY, OSN_TOKEN, OSN_TIMESTAMP, OSN_NONCE)
+ VALUES
+ (SEQ_OSN_ID.NEXTVAL, P_CONSUMER_KEY, P_TOKEN, P_TIMESTAMP, P_NONCE);
+
+ -- Clean up all timestamps older than the one we just received
+ DELETE FROM OAUTH_SERVER_NONCE
+ WHERE OSN_CONSUMER_KEY = P_CONSUMER_KEY
+ AND OSN_TOKEN = P_TOKEN
+ AND OSN_TIMESTAMP < (P_TIMESTAMP - P_MAX_TIMESTAMP_SKEW);
+
+
+EXCEPTION
+WHEN V_EXC_INVALID_TIMESTAMP THEN
+P_RESULT := 2; -- INVALID_TIMESTAMP
+WHEN V_EXC_DUPLICATE_TIMESTAMP THEN
+P_RESULT := 3; -- DUPLICATE_TIMESTAMP
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_CONSUMER_STATIC_SAVE.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_CONSUMER_STATIC_SAVE.prc new file mode 100644 index 0000000..047c77b --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_CONSUMER_STATIC_SAVE.prc @@ -0,0 +1,28 @@ +CREATE OR REPLACE PROCEDURE SP_CONSUMER_STATIC_SAVE
+(
+P_OSR_CONSUMER_KEY IN VARCHAR2,
+P_RESULT OUT NUMBER
+)
+AS
+
+-- PROCEDURE TO Fetch the static consumer key for this provider.
+BEGIN
+P_RESULT := 0;
+
+
+ INSERT INTO OAUTH_SERVER_REGISTRY
+ (OSR_ID, OSR_ENABLED, OSR_STATUS, OSR_USA_ID_REF, OSR_CONSUMER_KEY, OSR_CONSUMER_SECRET, OSR_REQUESTER_NAME, OSR_REQUESTER_EMAIL, OSR_CALLBACK_URI,
+ OSR_APPLICATION_URI, OSR_APPLICATION_TITLE, OSR_APPLICATION_DESCR, OSR_APPLICATION_NOTES,
+ OSR_APPLICATION_TYPE, OSR_APPLICATION_COMMERCIAL, OSR_TIMESTAMP,OSR_ISSUE_DATE)
+ VALUES
+ (SEQ_OSR_ID.NEXTVAL, 1, 'ACTIVE', NULL, P_OSR_CONSUMER_KEY, '\', '\', '\', '\', '\',
+ 'STATIC SHARED CONSUMER KEY', '\', 'STATIC SHARED CONSUMER KEY', '\', 0, SYSDATE, SYSDATE);
+
+
+EXCEPTION
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_COUNT_CONSUMER_ACCESS_TOKEN.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_COUNT_CONSUMER_ACCESS_TOKEN.prc new file mode 100644 index 0000000..f7099b9 --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_COUNT_CONSUMER_ACCESS_TOKEN.prc @@ -0,0 +1,27 @@ +CREATE OR REPLACE PROCEDURE SP_COUNT_CONSUMER_ACCESS_TOKEN
+(
+P_CONSUMER_KEY IN VARCHAR2,
+P_COUNT OUT NUMBER,
+P_RESULT OUT NUMBER
+)
+AS
+-- PROCEDURE TO Count the consumer access tokens for the given consumer.
+BEGIN
+P_RESULT := 0;
+
+SELECT COUNT(OST_ID) INTO P_COUNT
+ FROM OAUTH_SERVER_TOKEN
+ JOIN OAUTH_SERVER_REGISTRY
+ ON OST_OSR_ID_REF = OSR_ID
+ WHERE OST_TOKEN_TYPE = 'ACCESS'
+ AND OSR_CONSUMER_KEY = P_CONSUMER_KEY
+ AND OST_TOKEN_TTL >= SYSDATE;
+
+
+EXCEPTION
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_COUNT_SERVICE_TOKENS.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_COUNT_SERVICE_TOKENS.prc new file mode 100644 index 0000000..c73b366 --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_COUNT_SERVICE_TOKENS.prc @@ -0,0 +1,28 @@ +CREATE OR REPLACE PROCEDURE SP_COUNT_SERVICE_TOKENS
+(
+P_CONSUMER_KEY IN VARCHAR2,
+P_COUNT OUT NUMBER,
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- PROCEDURE TO Count how many tokens we have for the given server
+BEGIN
+P_RESULT := 0;
+
+ SELECT COUNT(OCT_ID) INTO P_COUNT
+ FROM OAUTH_CONSUMER_TOKEN
+ JOIN OAUTH_CONSUMER_REGISTRY
+ ON OCT_OCR_ID_REF = OCR_ID
+ WHERE OCT_TOKEN_TYPE = 'ACCESS'
+ AND OCR_CONSUMER_KEY = P_CONSUMER_KEY
+ AND OCT_TOKEN_TTL >= SYSDATE;
+
+
+EXCEPTION
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_DELETE_CONSUMER.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_DELETE_CONSUMER.prc new file mode 100644 index 0000000..3f18562 --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_DELETE_CONSUMER.prc @@ -0,0 +1,35 @@ +CREATE OR REPLACE PROCEDURE SP_DELETE_CONSUMER
+(
+P_CONSUMER_KEY IN VARCHAR2,
+P_USER_ID IN NUMBER,
+P_USER_IS_ADMIN IN NUMBER, --0:NO; 1:YES
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- Delete a consumer key. This removes access to our site for all applications using this key.
+
+BEGIN
+P_RESULT := 0;
+
+IF P_USER_IS_ADMIN = 1 THEN
+
+ DELETE FROM OAUTH_SERVER_REGISTRY
+ WHERE OSR_CONSUMER_KEY = P_CONSUMER_KEY
+ AND (OSR_USA_ID_REF = P_USER_ID OR OSR_USA_ID_REF IS NULL);
+
+ELSIF P_USER_IS_ADMIN = 0 THEN
+
+ DELETE FROM OAUTH_SERVER_REGISTRY
+ WHERE OSR_CONSUMER_KEY = P_CONSUMER_KEY
+ AND OSR_USA_ID_REF = P_USER_ID;
+
+END IF;
+
+EXCEPTION
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_DELETE_SERVER.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_DELETE_SERVER.prc new file mode 100644 index 0000000..ba259de --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_DELETE_SERVER.prc @@ -0,0 +1,35 @@ +CREATE OR REPLACE PROCEDURE SP_DELETE_SERVER
+(
+P_CONSUMER_KEY IN VARCHAR2,
+P_USER_ID IN NUMBER,
+P_USER_IS_ADMIN IN NUMBER, --0:NO; 1:YES
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- Delete a server key. This removes access to that site.
+
+BEGIN
+P_RESULT := 0;
+
+IF P_USER_IS_ADMIN = 1 THEN
+
+ DELETE FROM OAUTH_CONSUMER_REGISTRY
+ WHERE OCR_CONSUMER_KEY = P_CONSUMER_KEY
+ AND (OCR_USA_ID_REF = P_USER_ID OR OCR_USA_ID_REF IS NULL);
+
+ELSIF P_USER_IS_ADMIN = 0 THEN
+
+ DELETE FROM OAUTH_CONSUMER_REGISTRY
+ WHERE OCR_CONSUMER_KEY = P_CONSUMER_KEY
+ AND OCR_USA_ID_REF = P_USER_ID;
+
+END IF;
+
+EXCEPTION
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_DELETE_SERVER_TOKEN.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_DELETE_SERVER_TOKEN.prc new file mode 100644 index 0000000..de9d450 --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_DELETE_SERVER_TOKEN.prc @@ -0,0 +1,37 @@ +CREATE OR REPLACE PROCEDURE SP_DELETE_SERVER_TOKEN
+(
+P_CONSUMER_KEY IN VARCHAR2,
+P_USER_ID IN NUMBER,
+P_TOKEN IN VARCHAR2,
+P_USER_IS_ADMIN IN NUMBER, --0:NO; 1:YES
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- Delete a token we obtained from a server.
+
+BEGIN
+P_RESULT := 0;
+
+IF P_USER_IS_ADMIN = 1 THEN
+
+ DELETE FROM OAUTH_CONSUMER_TOKEN
+ WHERE OCT_TOKEN = P_TOKEN
+ AND OCT_OCR_ID_REF IN (SELECT OCR_ID FROM OAUTH_CONSUMER_REGISTRY WHERE OCR_CONSUMER_KEY = P_CONSUMER_KEY);
+
+ELSIF P_USER_IS_ADMIN = 0 THEN
+
+ DELETE FROM OAUTH_CONSUMER_TOKEN
+ WHERE OCT_TOKEN = P_TOKEN
+ AND OCT_USA_ID_REF = P_USER_ID
+ AND OCT_OCR_ID_REF IN (SELECT OCR_ID FROM OAUTH_CONSUMER_REGISTRY WHERE OCR_CONSUMER_KEY = P_CONSUMER_KEY);
+
+END IF;
+
+EXCEPTION
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_DEL_CONSUMER_ACCESS_TOKEN.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_DEL_CONSUMER_ACCESS_TOKEN.prc new file mode 100644 index 0000000..4281bdb --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_DEL_CONSUMER_ACCESS_TOKEN.prc @@ -0,0 +1,33 @@ +CREATE OR REPLACE PROCEDURE SP_DEL_CONSUMER_ACCESS_TOKEN
+(
+P_USER_ID IN NUMBER,
+P_TOKEN IN VARCHAR2,
+P_USER_IS_ADMIN IN NUMBER, -- 1:YES; 0:NO
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- PROCEDURE TO Delete a consumer access token.
+
+BEGIN
+
+ P_RESULT := 0;
+
+ IF P_USER_IS_ADMIN = 1 THEN
+ DELETE FROM OAUTH_SERVER_TOKEN
+ WHERE OST_TOKEN = P_TOKEN
+ AND OST_TOKEN_TYPE = 'ACCESS';
+ ELSE
+ DELETE FROM OAUTH_SERVER_TOKEN
+ WHERE OST_TOKEN = P_TOKEN
+ AND OST_TOKEN_TYPE = 'ACCESS'
+ AND OST_USA_ID_REF = P_USER_ID;
+ END IF;
+
+EXCEPTION
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_DEL_CONSUMER_REQUEST_TOKEN.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_DEL_CONSUMER_REQUEST_TOKEN.prc new file mode 100644 index 0000000..01678d6 --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_DEL_CONSUMER_REQUEST_TOKEN.prc @@ -0,0 +1,25 @@ +CREATE OR REPLACE PROCEDURE SP_DEL_CONSUMER_REQUEST_TOKEN
+(
+P_TOKEN IN VARCHAR2,
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- PROCEDURE TO Delete a consumer token. The token must be a request or authorized token.
+
+BEGIN
+
+ P_RESULT := 0;
+
+ DELETE FROM OAUTH_SERVER_TOKEN
+ WHERE OST_TOKEN = P_TOKEN
+ AND OST_TOKEN_TYPE = 'REQUEST';
+
+
+EXCEPTION
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_EXCH_CONS_REQ_FOR_ACC_TOKEN.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_EXCH_CONS_REQ_FOR_ACC_TOKEN.prc new file mode 100644 index 0000000..66a53ed --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_EXCH_CONS_REQ_FOR_ACC_TOKEN.prc @@ -0,0 +1,96 @@ +CREATE OR REPLACE PROCEDURE SP_EXCH_CONS_REQ_FOR_ACC_TOKEN
+(
+P_TOKEN_TTL IN NUMBER, -- IN SECOND
+P_NEW_TOKEN IN VARCHAR2,
+P_TOKEN IN VARCHAR2,
+P_TOKEN_SECRET IN VARCHAR2,
+P_VERIFIER IN VARCHAR2,
+P_OUT_TOKEN_TTL OUT NUMBER,
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- PROCEDURE TO Add an unautorized request token to our server.
+
+V_TOKEN_EXIST NUMBER;
+
+
+V_EXC_NO_TOKEN_EXIST EXCEPTION;
+BEGIN
+
+ P_RESULT := 0;
+
+ IF P_VERIFIER IS NOT NULL THEN
+
+ BEGIN
+ SELECT 1 INTO V_TOKEN_EXIST FROM DUAL WHERE EXISTS
+ (SELECT OST_TOKEN FROM OAUTH_SERVER_TOKEN
+ WHERE OST_TOKEN = P_TOKEN
+ AND OST_TOKEN_TYPE = 'REQUEST'
+ AND OST_AUTHORIZED = 1
+ AND OST_TOKEN_TTL >= SYSDATE
+ AND OST_VERIFIER = P_VERIFIER);
+ EXCEPTION
+ WHEN NO_DATA_FOUND THEN
+ RAISE V_EXC_NO_TOKEN_EXIST;
+ END;
+
+ UPDATE OAUTH_SERVER_TOKEN
+ SET OST_TOKEN = P_NEW_TOKEN,
+ OST_TOKEN_SECRET = P_TOKEN_SECRET,
+ OST_TOKEN_TYPE = 'ACCESS',
+ OST_TIMESTAMP = SYSDATE,
+ OST_TOKEN_TTL = NVL(SYSDATE + (P_TOKEN_TTL/(24*60*60)), TO_DATE('9999.12.31', 'yyyy.mm.dd'))
+ WHERE OST_TOKEN = P_TOKEN
+ AND OST_TOKEN_TYPE = 'REQUEST'
+ AND OST_AUTHORIZED = 1
+ AND OST_TOKEN_TTL >= SYSDATE
+ AND OST_VERIFIER = P_VERIFIER;
+
+ ELSE
+ BEGIN
+ SELECT 1 INTO V_TOKEN_EXIST FROM DUAL WHERE EXISTS
+ (SELECT OST_TOKEN FROM OAUTH_SERVER_TOKEN
+ WHERE OST_TOKEN = P_TOKEN
+ AND OST_TOKEN_TYPE = 'REQUEST'
+ AND OST_AUTHORIZED = 1
+ AND OST_TOKEN_TTL >= SYSDATE);
+ EXCEPTION
+ WHEN NO_DATA_FOUND THEN
+ RAISE V_EXC_NO_TOKEN_EXIST;
+ END;
+
+ UPDATE OAUTH_SERVER_TOKEN
+ SET OST_TOKEN = P_NEW_TOKEN,
+ OST_TOKEN_SECRET = P_TOKEN_SECRET,
+ OST_TOKEN_TYPE = 'ACCESS',
+ OST_TIMESTAMP = SYSDATE,
+ OST_TOKEN_TTL = NVL(SYSDATE + (P_TOKEN_TTL/(24*60*60)), TO_DATE('9999.12.31', 'yyyy.mm.dd'))
+ WHERE OST_TOKEN = P_TOKEN
+ AND OST_TOKEN_TYPE = 'REQUEST'
+ AND OST_AUTHORIZED = 1
+ AND OST_TOKEN_TTL >= SYSDATE;
+
+
+ END IF;
+
+ SELECT CASE
+ WHEN OST_TOKEN_TTL >= TO_DATE('9999.12.31', 'yyyy.mm.dd') THEN NULL ELSE (OST_TOKEN_TTL - SYSDATE)*24*60*60
+ END "TOKEN_TTL" INTO P_OUT_TOKEN_TTL
+ FROM OAUTH_SERVER_TOKEN
+ WHERE OST_TOKEN = P_NEW_TOKEN;
+
+
+
+
+
+
+EXCEPTION
+WHEN V_EXC_NO_TOKEN_EXIST THEN
+P_RESULT := 2; -- NO_TOKEN_EXIST
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_CONSUMER.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_CONSUMER.prc new file mode 100644 index 0000000..4225ff2 --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_CONSUMER.prc @@ -0,0 +1,41 @@ +CREATE OR REPLACE PROCEDURE SP_GET_CONSUMER
+(
+P_CONSUMER_KEY IN STRING,
+P_ROWS OUT TYPES.REF_CURSOR,
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- PROCEDURE TO Fetch a consumer of this server, by consumer_key.
+BEGIN
+P_RESULT := 0;
+
+OPEN P_ROWS FOR
+ SELECT OSR_ID "osr_id",
+ OSR_USA_ID_REF "osr_usa_id_ref",
+ OSR_CONSUMER_KEY "osr_consumer_key",
+ OSR_CONSUMER_SECRET "osr_consumer_secret",
+ OSR_ENABLED "osr_enabled",
+ OSR_STATUS "osr_status",
+ OSR_REQUESTER_NAME "osr_requester_name",
+ OSR_REQUESTER_EMAIL "osr_requester_email",
+ OSR_CALLBACK_URI "osr_callback_uri",
+ OSR_APPLICATION_URI "osr_application_uri",
+ OSR_APPLICATION_TITLE "osr_application_title",
+ OSR_APPLICATION_DESCR "osr_application_descr",
+ OSR_APPLICATION_NOTES "osr_application_notes",
+ OSR_APPLICATION_TYPE "osr_application_type",
+ OSR_APPLICATION_COMMERCIAL "osr_application_commercial",
+ OSR_ISSUE_DATE "osr_issue_date",
+ OSR_TIMESTAMP "osr_timestamp"
+ FROM OAUTH_SERVER_REGISTRY
+ WHERE OSR_CONSUMER_KEY = P_CONSUMER_KEY;
+
+
+EXCEPTION
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_CONSUMER_ACCESS_TOKEN.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_CONSUMER_ACCESS_TOKEN.prc new file mode 100644 index 0000000..0db2ea9 --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_CONSUMER_ACCESS_TOKEN.prc @@ -0,0 +1,43 @@ +CREATE OR REPLACE PROCEDURE SP_GET_CONSUMER_ACCESS_TOKEN
+(
+P_USER_ID IN NUMBER,
+P_TOKEN IN VARCHAR2,
+P_ROWS OUT TYPES.REF_CURSOR,
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- PROCEDURE TO Fetch the consumer access token, by access token.
+
+BEGIN
+
+ P_RESULT := 0;
+
+
+ OPEN P_ROWS FOR
+ SELECT OST_TOKEN "token",
+ OST_TOKEN_SECRET "token_secret",
+ OST_REFERRER_HOST "token_referrer_host",
+ OSR_CONSUMER_KEY "consumer_key",
+ OSR_CONSUMER_SECRET "consumer_secret",
+ OSR_APPLICATION_URI "application_uri",
+ OSR_APPLICATION_TITLE "application_title",
+ OSR_APPLICATION_DESCR "application_descr",
+ OSR_CALLBACK_URI "callback_uri"
+ FROM OAUTH_SERVER_TOKEN
+ JOIN OAUTH_SERVER_REGISTRY
+ ON OST_OSR_ID_REF = OSR_ID
+ WHERE OST_TOKEN_TYPE = 'ACCESS'
+ AND OST_TOKEN = P_TOKEN
+ AND OST_USA_ID_REF = P_USER_ID
+ AND OST_TOKEN_TTL >= SYSDATE;
+
+
+
+EXCEPTION
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_CONSUMER_REQUEST_TOKEN.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_CONSUMER_REQUEST_TOKEN.prc new file mode 100644 index 0000000..6d3b590 --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_CONSUMER_REQUEST_TOKEN.prc @@ -0,0 +1,41 @@ +CREATE OR REPLACE PROCEDURE SP_GET_CONSUMER_REQUEST_TOKEN
+(
+P_TOKEN IN VARCHAR2,
+P_ROWS OUT TYPES.REF_CURSOR,
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- PROCEDURE TO Fetch the consumer request token, by request token.
+BEGIN
+P_RESULT := 0;
+
+OPEN P_ROWS FOR
+
+SELECT OST_TOKEN "token",
+ OST_TOKEN_SECRET "token_secret",
+ OSR_CONSUMER_KEY "consumer_key",
+ OSR_CONSUMER_SECRET "consumer_secret",
+ OST_TOKEN_TYPE "token_type",
+ OST_CALLBACK_URL "callback_url",
+ OSR_APPLICATION_TITLE "application_title",
+ OSR_APPLICATION_DESCR "application_descr",
+ OSR_APPLICATION_URI "application_uri"
+ FROM OAUTH_SERVER_TOKEN
+ JOIN OAUTH_SERVER_REGISTRY
+ ON OST_OSR_ID_REF = OSR_ID
+ WHERE OST_TOKEN_TYPE = 'REQUEST'
+ AND OST_TOKEN = P_TOKEN
+ AND OST_TOKEN_TTL >= SYSDATE;
+
+
+
+
+
+EXCEPTION
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_CONSUMER_STATIC_SELECT.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_CONSUMER_STATIC_SELECT.prc new file mode 100644 index 0000000..1126ef6 --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_CONSUMER_STATIC_SELECT.prc @@ -0,0 +1,25 @@ +CREATE OR REPLACE PROCEDURE SP_GET_CONSUMER_STATIC_SELECT
+(
+P_OSR_CONSUMER_KEY OUT VARCHAR2,
+P_RESULT OUT NUMBER
+)
+AS
+
+-- PROCEDURE TO Fetch the static consumer key for this provider.
+BEGIN
+P_RESULT := 0;
+
+
+ SELECT OSR_CONSUMER_KEY INTO P_OSR_CONSUMER_KEY
+ FROM OAUTH_SERVER_REGISTRY
+ WHERE OSR_CONSUMER_KEY LIKE 'sc-%%'
+ AND OSR_USA_ID_REF IS NULL;
+
+
+EXCEPTION
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_SECRETS_FOR_SIGNATURE.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_SECRETS_FOR_SIGNATURE.prc new file mode 100644 index 0000000..2af7847 --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_SECRETS_FOR_SIGNATURE.prc @@ -0,0 +1,43 @@ +CREATE OR REPLACE PROCEDURE SP_GET_SECRETS_FOR_SIGNATURE
+(
+P_HOST IN VARCHAR2,
+P_PATH IN VARCHAR2,
+P_USER_ID IN NUMBER,
+P_NAME IN VARCHAR2,
+P_ROWS OUT TYPES.REF_CURSOR,
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- PROCEDURE TO Find the server details for signing a request, always looks for an access token.
+ -- The returned credentials depend on which local user is making the request.
+BEGIN
+P_RESULT := 0;
+
+ OPEN P_ROWS FOR
+ SELECT * FROM (
+ SELECT OCR_CONSUMER_KEY "consumer_key",
+ OCR_CONSUMER_SECRET "consumer_secret",
+ OCT_TOKEN "token",
+ OCT_TOKEN_SECRET "token_secret",
+ OCR_SIGNATURE_METHODS "signature_methods"
+ FROM OAUTH_CONSUMER_REGISTRY
+ JOIN OAUTH_CONSUMER_TOKEN ON OCT_OCR_ID_REF = OCR_ID
+ WHERE OCR_SERVER_URI_HOST = P_HOST
+ AND OCR_SERVER_URI_PATH = SUBSTR(P_PATH, 1, LENGTH(OCR_SERVER_URI_PATH))
+ AND (OCR_USA_ID_REF = P_USER_ID OR OCR_USA_ID_REF IS NULL)
+ AND OCT_USA_ID_REF = P_USER_ID
+ AND OCT_TOKEN_TYPE = 'ACCESS'
+ AND OCT_NAME = P_NAME
+ AND OCT_TOKEN_TTL >= SYSDATE
+ ORDER BY OCR_USA_ID_REF DESC, OCR_CONSUMER_SECRET DESC, LENGTH(OCR_SERVER_URI_PATH) DESC
+ ) WHERE ROWNUM<=1;
+
+
+EXCEPTION
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_SECRETS_FOR_VERIFY.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_SECRETS_FOR_VERIFY.prc new file mode 100644 index 0000000..4fbb435 --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_SECRETS_FOR_VERIFY.prc @@ -0,0 +1,52 @@ +CREATE OR REPLACE PROCEDURE SP_GET_SECRETS_FOR_VERIFY
+(
+P_CONSUMER_KEY IN VARCHAR2,
+P_TOKEN IN VARCHAR2,
+P_TOKEN_TYPE IN VARCHAR2,
+P_ROWS OUT TYPES.REF_CURSOR,
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- PROCEDURE to Find stored credentials for the consumer key and token. Used by an OAuth server
+ -- when verifying an OAuth request.
+
+BEGIN
+P_RESULT := 0;
+
+IF P_TOKEN_TYPE IS NULL THEN
+ OPEN P_ROWS FOR
+ SELECT OSR.OSR_ID "osr_id",
+ OSR.OSR_CONSUMER_KEY "consumer_key",
+ OSR.OSR_CONSUMER_SECRET "consumer_secret"
+ FROM OAUTH_SERVER_REGISTRY OSR
+ WHERE OSR.OSR_CONSUMER_KEY = P_CONSUMER_KEY
+ AND OSR.OSR_ENABLED = 1;
+ELSE
+ OPEN P_ROWS FOR
+ SELECT OSR.OSR_ID "osr_id",
+ OST.OST_ID "ost_id",
+ OST.OST_USA_ID_REF "user_id",
+ OSR.OSR_CONSUMER_KEY "consumer_key",
+ OSR.OSR_CONSUMER_SECRET "consumer_secret",
+ OST.OST_TOKEN "token",
+ OST.OST_TOKEN_SECRET "token_secret"
+ FROM OAUTH_SERVER_REGISTRY OSR, OAUTH_SERVER_TOKEN OST
+ WHERE OST.OST_OSR_ID_REF = OSR.OSR_ID
+ AND upper(OST.OST_TOKEN_TYPE) = upper(P_TOKEN_TYPE)
+ AND OSR.OSR_CONSUMER_KEY = P_CONSUMER_KEY
+ AND OST.OST_TOKEN = P_TOKEN
+ AND OSR.OSR_ENABLED = 1
+ AND OST.OST_TOKEN_TTL >= SYSDATE;
+
+END IF;
+
+
+
+EXCEPTION
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_SERVER.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_SERVER.prc new file mode 100644 index 0000000..af7d275 --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_SERVER.prc @@ -0,0 +1,35 @@ +CREATE OR REPLACE PROCEDURE SP_GET_SERVER
+(
+P_CONSUMER_KEY IN VARCHAR2,
+P_USER_ID IN NUMBER,
+P_ROWS OUT TYPES.REF_CURSOR,
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- PROCEDURE TO Get a server from the consumer registry using the consumer key
+BEGIN
+P_RESULT := 0;
+
+OPEN P_ROWS FOR
+ SELECT OCR_ID "id",
+ OCR_USA_ID_REF "user_id",
+ OCR_CONSUMER_KEY "consumer_key",
+ OCR_CONSUMER_SECRET "consumer_secret",
+ OCR_SIGNATURE_METHODS "signature_methods",
+ OCR_SERVER_URI "server_uri",
+ OCR_REQUEST_TOKEN_URI "request_token_uri",
+ OCR_AUTHORIZE_URI "authorize_uri",
+ OCR_ACCESS_TOKEN_URI "access_token_uri"
+ FROM OAUTH_CONSUMER_REGISTRY
+ WHERE OCR_CONSUMER_KEY = P_CONSUMER_KEY
+ AND (OCR_USA_ID_REF = P_USER_ID OR OCR_USA_ID_REF IS NULL);
+
+
+EXCEPTION
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_SERVER_FOR_URI.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_SERVER_FOR_URI.prc new file mode 100644 index 0000000..d838b51 --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_SERVER_FOR_URI.prc @@ -0,0 +1,41 @@ +CREATE OR REPLACE PROCEDURE SP_GET_SERVER_FOR_URI
+(
+P_HOST IN VARCHAR2,
+P_PATH IN VARCHAR2,
+P_USER_ID IN NUMBER,
+P_ROWS OUT TYPES.REF_CURSOR,
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- PROCEDURE TO Find the server details that might be used for a request
+BEGIN
+P_RESULT := 0;
+
+OPEN P_ROWS FOR
+SELECT * FROM (
+ SELECT OCR_ID "id",
+ OCR_USA_ID_REF "user_id",
+ OCR_CONSUMER_KEY "consumer_key",
+ OCR_CONSUMER_SECRET "consumer_secret",
+ OCR_SIGNATURE_METHODS "signature_methods",
+ OCR_SERVER_URI "server_uri",
+ OCR_REQUEST_TOKEN_URI "request_token_uri",
+ OCR_AUTHORIZE_URI "authorize_uri",
+ OCR_ACCESS_TOKEN_URI "access_token_uri"
+ FROM OAUTH_CONSUMER_REGISTRY
+ WHERE OCR_SERVER_URI_HOST = P_HOST
+ AND OCR_SERVER_URI_PATH = SUBSTR(P_PATH, 1, LENGTH(OCR_SERVER_URI_PATH))
+ AND (OCR_USA_ID_REF = P_USER_ID OR OCR_USA_ID_REF IS NULL)
+ ORDER BY ocr_usa_id_ref DESC, OCR_CONSUMER_KEY DESC, LENGTH(ocr_server_uri_path) DESC
+) WHERE ROWNUM<=1;
+
+
+
+EXCEPTION
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_SERVER_TOKEN.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_SERVER_TOKEN.prc new file mode 100644 index 0000000..fefbe8a --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_SERVER_TOKEN.prc @@ -0,0 +1,45 @@ +CREATE OR REPLACE PROCEDURE SP_GET_SERVER_TOKEN
+(
+P_CONSUMER_KEY IN VARCHAR2,
+P_USER_ID IN NUMBER,
+P_TOKEN IN VARCHAR2,
+P_ROWS OUT TYPES.REF_CURSOR,
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- PROCEDURE TO Get a specific server token for the given user
+BEGIN
+P_RESULT := 0;
+
+OPEN P_ROWS FOR
+ SELECT OCR_CONSUMER_KEY "consumer_key",
+ OCR_CONSUMER_SECRET "consumer_secret",
+ OCT_TOKEN "token",
+ OCT_TOKEN_SECRET "token_secret",
+ OCT_USA_ID_REF "usr_id",
+ OCR_SIGNATURE_METHODS "signature_methods",
+ OCR_SERVER_URI "server_uri",
+ OCR_SERVER_URI_HOST "server_uri_host",
+ OCR_SERVER_URI_PATH "server_uri_path",
+ OCR_REQUEST_TOKEN_URI "request_token_uri",
+ OCR_AUTHORIZE_URI "authorize_uri",
+ OCR_ACCESS_TOKEN_URI "access_token_uri",
+ OCT_TIMESTAMP "timestamp"
+ FROM OAUTH_CONSUMER_REGISTRY
+ JOIN OAUTH_CONSUMER_TOKEN
+ ON OCT_OCR_ID_REF = OCR_ID
+ WHERE OCR_CONSUMER_KEY = P_CONSUMER_KEY
+ AND OCT_USA_ID_REF = P_USER_ID
+ AND OCT_TOKEN_TYPE = 'ACCESS'
+ AND OCT_TOKEN = P_TOKEN
+ AND OCT_TOKEN_TTL >= SYSDATE;
+
+
+EXCEPTION
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_SERVER_TOKEN_SECRETS.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_SERVER_TOKEN_SECRETS.prc new file mode 100644 index 0000000..95eec88 --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_GET_SERVER_TOKEN_SECRETS.prc @@ -0,0 +1,47 @@ +CREATE OR REPLACE PROCEDURE SP_GET_SERVER_TOKEN_SECRETS
+(
+P_CONSUMER_KEY IN VARCHAR2,
+P_TOKEN IN VARCHAR2,
+P_TOKEN_TYPE IN VARCHAR2,
+P_USER_ID IN NUMBER,
+P_ROWS OUT TYPES.REF_CURSOR,
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- Get the token and token secret we obtained from a server.
+
+BEGIN
+P_RESULT := 0;
+
+
+ OPEN P_ROWS FOR
+ SELECT OCR.OCR_CONSUMER_KEY "consumer_key",
+ OCR.OCR_CONSUMER_SECRET "consumer_secret",
+ OCT.OCT_TOKEN "token",
+ OCT.OCT_TOKEN_SECRET "token_secret",
+ OCT.OCT_NAME "token_name",
+ OCR.OCR_SIGNATURE_METHODS "signature_methods",
+ OCR.OCR_SERVER_URI "server_uri",
+ OCR.OCR_REQUEST_TOKEN_URI "request_token_uri",
+ OCR.OCR_AUTHORIZE_URI "authorize_uri",
+ OCR.OCR_ACCESS_TOKEN_URI "access_token_uri",
+ CASE WHEN OCT.OCT_TOKEN_TTL >= TO_DATE('9999.12.31', 'yyyy.mm.dd') THEN NULL
+ ELSE OCT.OCT_TOKEN_TTL - SYSDATE
+ END "token_ttl"
+ FROM OAUTH_CONSUMER_REGISTRY OCR, OAUTH_CONSUMER_TOKEN OCT
+ WHERE OCT.OCT_OCR_ID_REF = OCR_ID
+ AND OCR.OCR_CONSUMER_KEY = P_CONSUMER_KEY
+ AND upper(OCT.OCT_TOKEN_TYPE) = upper(P_TOKEN_TYPE)
+ AND OCT.OCT_TOKEN = P_TOKEN
+ AND OCT.OCT_USA_ID_REF = P_USER_ID
+ AND OCT.OCT_TOKEN_TTL >= SYSDATE;
+
+
+EXCEPTION
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_LIST_CONSUMERS.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_LIST_CONSUMERS.prc new file mode 100644 index 0000000..bb42465 --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_LIST_CONSUMERS.prc @@ -0,0 +1,41 @@ +CREATE OR REPLACE PROCEDURE SP_LIST_CONSUMERS
+(
+P_USER_ID IN NUMBER,
+P_ROWS OUT TYPES.REF_CURSOR,
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- PROCEDURE TO Fetch a list of all consumer keys, secrets etc.
+ -- Returns the public (user_id is null) and the keys owned by the user
+
+BEGIN
+
+ P_RESULT := 0;
+
+ OPEN P_ROWS FOR
+ SELECT OSR_ID "id",
+ OSR_USA_ID_REF "user_id",
+ OSR_CONSUMER_KEY "consumer_key",
+ OSR_CONSUMER_SECRET "consumer_secret",
+ OSR_ENABLED "enabled",
+ OSR_STATUS "status",
+ OSR_ISSUE_DATE "issue_date",
+ OSR_APPLICATION_URI "application_uri",
+ OSR_APPLICATION_TITLE "application_title",
+ OSR_APPLICATION_DESCR "application_descr",
+ OSR_REQUESTER_NAME "requester_name",
+ OSR_REQUESTER_EMAIL "requester_email",
+ OSR_CALLBACK_URI "callback_uri"
+ FROM OAUTH_SERVER_REGISTRY
+ WHERE (OSR_USA_ID_REF = P_USER_ID OR OSR_USA_ID_REF IS NULL)
+ ORDER BY OSR_APPLICATION_TITLE;
+
+
+EXCEPTION
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_LIST_CONSUMER_TOKENS.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_LIST_CONSUMER_TOKENS.prc new file mode 100644 index 0000000..dae9c72 --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_LIST_CONSUMER_TOKENS.prc @@ -0,0 +1,43 @@ +CREATE OR REPLACE PROCEDURE SP_LIST_CONSUMER_TOKENS
+(
+P_USER_ID IN NUMBER,
+P_ROWS OUT TYPES.REF_CURSOR,
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- PROCEDURE TO Fetch a list of all consumer tokens accessing the account of the given user.
+
+BEGIN
+
+ P_RESULT := 0;
+
+ OPEN P_ROWS FOR
+ SELECT OSR_CONSUMER_KEY "consumer_key",
+ OSR_CONSUMER_SECRET "consumer_secret",
+ OSR_ENABLED "enabled",
+ OSR_STATUS "status",
+ OSR_APPLICATION_URI "application_uri",
+ OSR_APPLICATION_TITLE "application_title",
+ OSR_APPLICATION_DESCR "application_descr",
+ OST_TIMESTAMP "timestamp",
+ OST_TOKEN "token",
+ OST_TOKEN_SECRET "token_secret",
+ OST_REFERRER_HOST "token_referrer_host",
+ OSR_CALLBACK_URI "callback_uri"
+ FROM OAUTH_SERVER_REGISTRY
+ JOIN OAUTH_SERVER_TOKEN
+ ON OST_OSR_ID_REF = OSR_ID
+ WHERE OST_USA_ID_REF = P_USER_ID
+ AND OST_TOKEN_TYPE = 'ACCESS'
+ AND OST_TOKEN_TTL >= SYSDATE
+ ORDER BY OSR_APPLICATION_TITLE;
+
+
+EXCEPTION
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_LIST_LOG.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_LIST_LOG.prc new file mode 100644 index 0000000..275950e --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_LIST_LOG.prc @@ -0,0 +1,75 @@ +CREATE OR REPLACE PROCEDURE SP_LIST_LOG
+(
+P_OPTION_FLAG IN NUMBER, -- 0:NULL; 1:OTHERWISE
+P_USA_ID IN NUMBER,
+P_OSR_CONSUMER_KEY IN VARCHAR2,
+P_OCR_CONSUMER_KEY IN VARCHAR2,
+P_OST_TOKEN IN VARCHAR2,
+P_OCT_TOKEN IN VARCHAR2,
+P_ROWS OUT TYPES.REF_CURSOR,
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- PROCEDURE TO Get a page of entries from the log. Returns the last 100 records
+ -- matching the options given.
+
+BEGIN
+
+ P_RESULT := 0;
+
+ IF P_OPTION_FLAG IS NULL OR P_OPTION_FLAG = 0 THEN
+ OPEN P_ROWS FOR
+ SELECT * FROM (
+ SELECT OLG_ID "olg_id",
+ OLG_OSR_CONSUMER_KEY "osr_consumer_key",
+ OLG_OST_TOKEN "ost_token",
+ OLG_OCR_CONSUMER_KEY "ocr_consumer_key",
+ OLG_OCT_TOKEN "oct_token",
+ OLG_USA_ID_REF "user_id",
+ OLG_RECEIVED "received",
+ OLG_SENT "sent",
+ OLG_BASE_STRING "base_string",
+ OLG_NOTES "notes",
+ OLG_TIMESTAMP "timestamp",
+ -- INET_NTOA(OLG_REMOTE_IP) "remote_ip"
+ OLG_REMOTE_IP "remote_ip"
+ FROM OAUTH_LOG
+ WHERE OLG_USA_ID_REF = P_USA_ID
+ ORDER BY OLG_ID DESC
+ ) WHERE ROWNUM<=100;
+ ELSE
+ OPEN P_ROWS FOR
+ SELECT * FROM (
+ SELECT OLG_ID "olg_id",
+ OLG_OSR_CONSUMER_KEY "osr_consumer_key",
+ OLG_OST_TOKEN "ost_token",
+ OLG_OCR_CONSUMER_KEY "ocr_consumer_key",
+ OLG_OCT_TOKEN "oct_token",
+ OLG_USA_ID_REF "user_id",
+ OLG_RECEIVED "received",
+ OLG_SENT "sent",
+ OLG_BASE_STRING "base_string",
+ OLG_NOTES "notes",
+ OLG_TIMESTAMP "timestamp",
+ -- INET_NTOA(OLG_REMOTE_IP) "remote_ip"
+ OLG_REMOTE_IP "remote_ip"
+ FROM OAUTH_LOG
+ WHERE OLG_OSR_CONSUMER_KEY = P_OSR_CONSUMER_KEY
+ AND OLG_OCR_CONSUMER_KEY = P_OCR_CONSUMER_KEY
+ AND OLG_OST_TOKEN = P_OST_TOKEN
+ AND OLG_OCT_TOKEN = P_OCT_TOKEN
+ AND (OLG_USA_ID_REF IS NULL OR OLG_USA_ID_REF = P_USA_ID)
+ ORDER BY OLG_ID DESC
+ ) WHERE ROWNUM<=100;
+
+ END IF;
+
+
+EXCEPTION
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_LIST_SERVERS.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_LIST_SERVERS.prc new file mode 100644 index 0000000..51dd39a --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_LIST_SERVERS.prc @@ -0,0 +1,66 @@ +CREATE OR REPLACE PROCEDURE SP_LIST_SERVERS
+(
+P_Q IN VARCHAR2,
+P_USER_ID IN NUMBER,
+P_ROWS OUT TYPES.REF_CURSOR,
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- PROCEDURE TO Get a list of all consumers from the consumer registry.
+BEGIN
+P_RESULT := 0;
+
+IF P_Q IS NOT NULL THEN
+
+ OPEN P_ROWS FOR
+ SELECT OCR_ID "id",
+ OCR_USA_ID_REF "user_id",
+ OCR_CONSUMER_KEY "consumer_key",
+ OCR_CONSUMER_SECRET "consumer_secret",
+ OCR_SIGNATURE_METHODS "signature_methods",
+ OCR_SERVER_URI "server_uri",
+ OCR_SERVER_URI_HOST "server_uri_host",
+ OCR_SERVER_URI_PATH "server_uri_path",
+ OCR_REQUEST_TOKEN_URI "request_token_uri",
+ OCR_AUTHORIZE_URI "authorize_uri",
+ OCR_ACCESS_TOKEN_URI "access_token_uri"
+ FROM OAUTH_CONSUMER_REGISTRY
+ WHERE ( OCR_CONSUMER_KEY LIKE '%'|| P_Q ||'%'
+ OR OCR_SERVER_URI LIKE '%'|| P_Q ||'%'
+ OR OCR_SERVER_URI_HOST LIKE '%'|| P_Q ||'%'
+ OR OCR_SERVER_URI_PATH LIKE '%'|| P_Q ||'%')
+ AND (OCR_USA_ID_REF = P_USER_ID OR OCR_USA_ID_REF IS NULL)
+ ORDER BY OCR_SERVER_URI_HOST, OCR_SERVER_URI_PATH;
+
+ELSE
+
+ OPEN P_ROWS FOR
+ SELECT OCR_ID "id",
+ OCR_USA_ID_REF "user_id",
+ OCR_CONSUMER_KEY "consumer_key",
+ OCR_CONSUMER_SECRET "consumer_secret",
+ OCR_SIGNATURE_METHODS "signature_methods",
+ OCR_SERVER_URI "server_uri",
+ OCR_SERVER_URI_HOST "server_uri_host",
+ OCR_SERVER_URI_PATH "server_uri_path",
+ OCR_REQUEST_TOKEN_URI "request_token_uri",
+ OCR_AUTHORIZE_URI "authorize_uri",
+ OCR_ACCESS_TOKEN_URI "access_token_uri"
+ FROM OAUTH_CONSUMER_REGISTRY
+ WHERE OCR_USA_ID_REF = P_USER_ID OR OCR_USA_ID_REF IS NULL
+ ORDER BY OCR_SERVER_URI_HOST, OCR_SERVER_URI_PATH;
+
+END IF;
+
+
+
+
+
+EXCEPTION
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_LIST_SERVER_TOKENS.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_LIST_SERVER_TOKENS.prc new file mode 100644 index 0000000..baa62c0 --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_LIST_SERVER_TOKENS.prc @@ -0,0 +1,45 @@ +CREATE OR REPLACE PROCEDURE SP_LIST_SERVER_TOKENS
+(
+P_USER_ID IN NUMBER,
+P_ROWS OUT TYPES.REF_CURSOR,
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- PROCEDURE TO Find the server details that might be used for a request
+BEGIN
+P_RESULT := 0;
+
+OPEN P_ROWS FOR
+ SELECT OCR_CONSUMER_KEY "consumer_key",
+ OCR_CONSUMER_SECRET "consumer_secret",
+ OCT_ID "token_id",
+ OCT_TOKEN "token",
+ OCT_TOKEN_SECRET "token_secret",
+ OCT_USA_ID_REF "user_id",
+ OCR_SIGNATURE_METHODS "signature_methods",
+ OCR_SERVER_URI "server_uri",
+ OCR_SERVER_URI_HOST "server_uri_host",
+ OCR_SERVER_URI_PATH "server_uri_path",
+ OCR_REQUEST_TOKEN_URI "request_token_uri",
+ OCR_AUTHORIZE_URI "authorize_uri",
+ OCR_ACCESS_TOKEN_URI "access_token_uri",
+ OCT_TIMESTAMP "timestamp"
+ FROM OAUTH_CONSUMER_REGISTRY
+ JOIN OAUTH_CONSUMER_TOKEN
+ ON OCT_OCR_ID_REF = OCR_ID
+ WHERE OCT_USA_ID_REF = P_USER_ID
+ AND OCT_TOKEN_TYPE = 'ACCESS'
+ AND OCT_TOKEN_TTL >= SYSDATE
+ ORDER BY OCR_SERVER_URI_HOST, OCR_SERVER_URI_PATH;
+
+
+
+
+EXCEPTION
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_SET_CONSUMER_ACC_TOKEN_TTL.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_SET_CONSUMER_ACC_TOKEN_TTL.prc new file mode 100644 index 0000000..e5a96c9 --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_SET_CONSUMER_ACC_TOKEN_TTL.prc @@ -0,0 +1,28 @@ +CREATE OR REPLACE PROCEDURE SP_SET_CONSUMER_ACC_TOKEN_TTL
+(
+P_TOKEN IN VARCHAR2,
+P_TOKEN_TTL IN NUMBER,
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- PROCEDURE TO Set the ttl of a consumer access token. This is done when the
+ -- server receives a valid request with a xoauth_token_ttl parameter in it.
+
+BEGIN
+
+ P_RESULT := 0;
+
+ UPDATE OAUTH_SERVER_TOKEN
+ SET OST_TOKEN_TTL = SYSDATE + (P_TOKEN_TTL/(24*60*60))
+ WHERE OST_TOKEN = P_TOKEN
+ AND OST_TOKEN_TYPE = 'ACCESS';
+
+
+EXCEPTION
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_SET_SERVER_TOKEN_TTL.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_SET_SERVER_TOKEN_TTL.prc new file mode 100644 index 0000000..34a99de --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_SET_SERVER_TOKEN_TTL.prc @@ -0,0 +1,29 @@ +CREATE OR REPLACE PROCEDURE SP_SET_SERVER_TOKEN_TTL
+(
+P_TOKEN_TTL IN NUMBER, -- IN SECOND
+P_CONSUMER_KEY IN VARCHAR2,
+P_TOKEN IN VARCHAR2,
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- PROCEDURE TO Set the ttl of a server access token.
+
+BEGIN
+
+ P_RESULT := 0;
+
+
+UPDATE OAUTH_CONSUMER_TOKEN
+SET OCT_TOKEN_TTL = SYSDATE + (P_TOKEN_TTL/(24*60*60)) -- DATE_ADD(NOW(), INTERVAL %D SECOND)
+WHERE OCT_TOKEN = P_TOKEN
+AND OCT_OCR_ID_REF IN (SELECT OCR_ID FROM OAUTH_CONSUMER_REGISTRY WHERE OCR_CONSUMER_KEY = P_CONSUMER_KEY);
+
+
+EXCEPTION
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_UPDATE_CONSUMER.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_UPDATE_CONSUMER.prc new file mode 100644 index 0000000..a79e64c --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_UPDATE_CONSUMER.prc @@ -0,0 +1,40 @@ +CREATE OR REPLACE PROCEDURE SP_UPDATE_CONSUMER
+(
+P_OSR_USA_ID_REF IN NUMBER,
+P_OSR_CONSUMER_KEY IN VARCHAR2,
+P_OSR_CONSUMER_SECRET IN VARCHAR2,
+P_OSR_REQUESTER_NAME IN VARCHAR2,
+P_OSR_REQUESTER_EMAIL IN VARCHAR2,
+P_OSR_CALLBACK_URI IN VARCHAR2,
+P_OSR_APPLICATION_URI IN VARCHAR2,
+P_OSR_APPLICATION_TITLE IN VARCHAR2,
+P_OSR_APPLICATION_DESCR IN VARCHAR2,
+P_OSR_APPLICATION_NOTES IN VARCHAR2,
+P_OSR_APPLICATION_TYPE IN VARCHAR2,
+P_OSR_APPLICATION_COMMERCIAL IN INTEGER,
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- PROCEDURE TO Insert a new consumer with this server (we will be the server)
+BEGIN
+P_RESULT := 0;
+
+
+ INSERT INTO OAUTH_SERVER_REGISTRY
+ ( OSR_ID, OSR_ENABLED, OSR_STATUS,OSR_USA_ID_REF,OSR_CONSUMER_KEY, OSR_CONSUMER_SECRET,OSR_REQUESTER_NAME,
+ OSR_REQUESTER_EMAIL, OSR_CALLBACK_URI, OSR_APPLICATION_URI, OSR_APPLICATION_TITLE, OSR_APPLICATION_DESCR,
+ OSR_APPLICATION_NOTES, OSR_APPLICATION_TYPE, OSR_APPLICATION_COMMERCIAL, OSR_TIMESTAMP, OSR_ISSUE_DATE)
+ VALUES
+ ( SEQ_OSR_ID.NEXTVAL, 1, 'ACTIVE', P_OSR_USA_ID_REF, P_OSR_CONSUMER_KEY, P_OSR_CONSUMER_SECRET,P_OSR_REQUESTER_NAME,
+ P_OSR_REQUESTER_EMAIL, P_OSR_CALLBACK_URI, P_OSR_APPLICATION_URI, P_OSR_APPLICATION_TITLE, P_OSR_APPLICATION_DESCR,
+ P_OSR_APPLICATION_NOTES, P_OSR_APPLICATION_TYPE, P_OSR_APPLICATION_COMMERCIAL, SYSDATE, SYSDATE);
+
+
+EXCEPTION
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_UPDATE_SERVER.prc b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_UPDATE_SERVER.prc new file mode 100644 index 0000000..7826eb6 --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/OracleDB/3_Procedures/SP_UPDATE_SERVER.prc @@ -0,0 +1,139 @@ +CREATE OR REPLACE PROCEDURE SP_UPDATE_SERVER
+(
+P_CONSUMER_KEY IN VARCHAR2,
+P_USER_ID IN NUMBER,
+P_OCR_ID IN NUMBER,
+P_USER_IS_ADMIN IN NUMBER, -- 0:NO; 1:YES;
+P_OCR_CONSUMER_SECRET IN VARCHAR2,
+P_OCR_SERVER_URI IN VARCHAR2,
+P_OCR_SERVER_URI_HOST IN VARCHAR2,
+P_OCR_SERVER_URI_PATH IN VARCHAR2,
+P_OCR_REQUEST_TOKEN_URI IN VARCHAR2,
+P_OCR_AUTHORIZE_URI IN VARCHAR2,
+P_OCR_ACCESS_TOKEN_URI IN VARCHAR2,
+P_OCR_SIGNATURE_METHODS IN VARCHAR2,
+P_OCR_USA_ID_REF IN NUMBER,
+P_UPDATE_P_OCR_USA_ID_REF_FLAG IN NUMBER, -- 1:TRUE; 0:FALSE
+P_RESULT OUT NUMBER
+)
+AS
+
+ -- Add a request token we obtained from a server.
+V_OCR_ID_EXIST NUMBER;
+V_OCR_USA_ID_REF NUMBER;
+
+V_EXC_DUPLICATE_CONSUMER_KEY EXCEPTION;
+V_EXC_UNAUTHORISED_USER_ID EXCEPTION;
+BEGIN
+P_RESULT := 0;
+
+V_OCR_USA_ID_REF := P_OCR_USA_ID_REF;
+
+ IF P_OCR_ID IS NOT NULL THEN
+ BEGIN
+ SELECT 1 INTO V_OCR_ID_EXIST FROM DUAL WHERE EXISTS
+ (SELECT OCR_ID FROM OAUTH_CONSUMER_REGISTRY
+ WHERE OCR_CONSUMER_KEY = P_CONSUMER_KEY
+ AND OCR_ID != P_OCR_ID
+ AND (OCR_USA_ID_REF = P_USER_ID OR OCR_USA_ID_REF IS NULL));
+
+ EXCEPTION
+ WHEN NO_DATA_FOUND THEN
+ V_OCR_ID_EXIST :=0;
+ END;
+ ELSE
+ BEGIN
+ SELECT 1 INTO V_OCR_ID_EXIST FROM DUAL WHERE EXISTS
+ (SELECT OCR_ID FROM OAUTH_CONSUMER_REGISTRY
+ WHERE OCR_CONSUMER_KEY = P_CONSUMER_KEY
+ AND (OCR_USA_ID_REF = P_USER_ID OR OCR_USA_ID_REF IS NULL));
+
+ EXCEPTION
+ WHEN NO_DATA_FOUND THEN
+ V_OCR_ID_EXIST :=0;
+ END;
+ END IF;
+
+ IF V_OCR_ID_EXIST = 1 THEN
+ RAISE V_EXC_DUPLICATE_CONSUMER_KEY;
+ END IF;
+
+
+ IF P_OCR_ID IS NOT NULL THEN
+ IF P_USER_IS_ADMIN != 1 THEN
+ BEGIN
+ SELECT OCR_USA_ID_REF INTO V_OCR_USA_ID_REF
+ FROM OAUTH_CONSUMER_REGISTRY
+ WHERE OCR_ID = P_OCR_ID;
+
+ EXCEPTION
+ WHEN NO_DATA_FOUND THEN
+ NULL;
+ END;
+
+ IF V_OCR_USA_ID_REF != P_USER_ID THEN
+ RAISE V_EXC_UNAUTHORISED_USER_ID;
+ END IF;
+ END IF;
+
+ IF P_UPDATE_P_OCR_USA_ID_REF_FLAG = 0 THEN
+
+ UPDATE OAUTH_CONSUMER_REGISTRY
+ SET OCR_CONSUMER_KEY = P_CONSUMER_KEY,
+ OCR_CONSUMER_SECRET = P_OCR_CONSUMER_SECRET,
+ OCR_SERVER_URI = P_OCR_SERVER_URI,
+ OCR_SERVER_URI_HOST = P_OCR_SERVER_URI_HOST,
+ OCR_SERVER_URI_PATH = P_OCR_SERVER_URI_PATH,
+ OCR_TIMESTAMP = SYSDATE,
+ OCR_REQUEST_TOKEN_URI = P_OCR_REQUEST_TOKEN_URI,
+ OCR_AUTHORIZE_URI = P_OCR_AUTHORIZE_URI,
+ OCR_ACCESS_TOKEN_URI = P_OCR_ACCESS_TOKEN_URI,
+ OCR_SIGNATURE_METHODS = P_OCR_SIGNATURE_METHODS
+ WHERE OCR_ID = P_OCR_ID;
+
+ ELSIF P_UPDATE_P_OCR_USA_ID_REF_FLAG = 1 THEN
+ UPDATE OAUTH_CONSUMER_REGISTRY
+ SET OCR_CONSUMER_KEY = P_CONSUMER_KEY,
+ OCR_CONSUMER_SECRET = P_OCR_CONSUMER_SECRET,
+ OCR_SERVER_URI = P_OCR_SERVER_URI,
+ OCR_SERVER_URI_HOST = P_OCR_SERVER_URI_HOST,
+ OCR_SERVER_URI_PATH = P_OCR_SERVER_URI_PATH,
+ OCR_TIMESTAMP = SYSDATE,
+ OCR_REQUEST_TOKEN_URI = P_OCR_REQUEST_TOKEN_URI,
+ OCR_AUTHORIZE_URI = P_OCR_AUTHORIZE_URI,
+ OCR_ACCESS_TOKEN_URI = P_OCR_ACCESS_TOKEN_URI,
+ OCR_SIGNATURE_METHODS = P_OCR_SIGNATURE_METHODS,
+ OCR_USA_ID_REF = P_OCR_USA_ID_REF
+ WHERE OCR_ID = P_OCR_ID;
+
+ END IF;
+
+ ELSE
+ IF P_UPDATE_P_OCR_USA_ID_REF_FLAG = 0 THEN
+ V_OCR_USA_ID_REF := P_USER_ID;
+ END IF;
+
+ INSERT INTO OAUTH_CONSUMER_REGISTRY
+ (OCR_ID, OCR_CONSUMER_KEY ,OCR_CONSUMER_SECRET, OCR_SERVER_URI, OCR_SERVER_URI_HOST, OCR_SERVER_URI_PATH,
+ OCR_TIMESTAMP, OCR_REQUEST_TOKEN_URI, OCR_AUTHORIZE_URI, OCR_ACCESS_TOKEN_URI, OCR_SIGNATURE_METHODS,
+ OCR_USA_ID_REF)
+ VALUES
+ (SEQ_OCR_ID.NEXTVAL, P_CONSUMER_KEY, P_OCR_CONSUMER_SECRET, P_OCR_SERVER_URI, P_OCR_SERVER_URI_HOST, P_OCR_SERVER_URI_PATH,
+ SYSDATE, P_OCR_REQUEST_TOKEN_URI, P_OCR_AUTHORIZE_URI, P_OCR_ACCESS_TOKEN_URI, P_OCR_SIGNATURE_METHODS,
+ V_OCR_USA_ID_REF);
+
+ END IF;
+
+
+EXCEPTION
+WHEN V_EXC_DUPLICATE_CONSUMER_KEY THEN
+P_RESULT := 2; -- DUPLICATE_CONSUMER_KEY
+WHEN V_EXC_UNAUTHORISED_USER_ID THEN
+P_RESULT := 3; -- UNAUTHORISED_USER_ID
+
+WHEN OTHERS THEN
+-- CALL THE FUNCTION TO LOG ERRORS
+ROLLBACK;
+P_RESULT := 1; -- ERROR
+END;
+/
diff --git a/vendor/oauth-php/library/store/oracle/install.php b/vendor/oauth-php/library/store/oracle/install.php new file mode 100644 index 0000000..5a80f04 --- /dev/null +++ b/vendor/oauth-php/library/store/oracle/install.php @@ -0,0 +1,28 @@ +<?php
+
+
+/**
+ Added by Vinay Kant Sahu.
+vinaykant.sahu@gmail.com
+ * Storage container for the oauth credentials, both server and consumer side.
+ * Based on Oracle
+ *
+
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+
+
+
+ */
+
+echo 'Right now we do not have Oracle DB installer.
+Please find OracleDB folder here with this Table, Sequences and Procedures. You need to manually install/create DB schema and SP with your oracle DB. ';
+?>
\ No newline at end of file diff --git a/vendor/oauth-php/library/store/postgresql/pgsql.sql b/vendor/oauth-php/library/store/postgresql/pgsql.sql new file mode 100644 index 0000000..8f0e4d3 --- /dev/null +++ b/vendor/oauth-php/library/store/postgresql/pgsql.sql @@ -0,0 +1,166 @@ +# +# Log table to hold all OAuth request when you enabled logging +# + +CREATE TABLE oauth_log ( + olg_id serial primary key, + olg_osr_consumer_key varchar(64), + olg_ost_token varchar(64), + olg_ocr_consumer_key varchar(64), + olg_oct_token varchar(64), + olg_usa_id_ref text, + olg_received text not null, + olg_sent text not null, + olg_base_string text not null, + olg_notes text not null, + olg_timestamp timestamp not null default current_timestamp, + olg_remote_ip inet not null +); + +COMMENT ON TABLE oauth_log IS 'Log table to hold all OAuth request when you enabled logging'; + + +# +# /////////////////// CONSUMER SIDE /////////////////// +# + +# This is a registry of all consumer codes we got from other servers +# The consumer_key/secret is obtained from the server +# We also register the server uri, so that we can find the consumer key and secret +# for a certain server. From that server we can check if we have a token for a +# particular user. + +CREATE TABLE oauth_consumer_registry ( + ocr_id serial primary key, + ocr_usa_id_ref text, + ocr_consumer_key varchar(128) not null, + ocr_consumer_secret varchar(128) not null, + ocr_signature_methods varchar(255) not null default 'HMAC-SHA1,PLAINTEXT', + ocr_server_uri varchar(255) not null, + ocr_server_uri_host varchar(128) not null, + ocr_server_uri_path varchar(128) not null, + + ocr_request_token_uri varchar(255) not null, + ocr_authorize_uri varchar(255) not null, + ocr_access_token_uri varchar(255) not null, + ocr_timestamp timestamp not null default current_timestamp, + + unique (ocr_consumer_key, ocr_usa_id_ref, ocr_server_uri) +); + +COMMENT ON TABLE oauth_consumer_registry IS 'This is a registry of all consumer codes we got from other servers'; + +# Table used to sign requests for sending to a server by the consumer +# The key is defined for a particular user. Only one single named +# key is allowed per user/server combination + +-- Create enum type token_type +CREATE TYPE consumer_token_type AS ENUM ( + 'request', + 'authorized', + 'access' +); + +CREATE TABLE oauth_consumer_token ( + oct_id serial primary key, + oct_ocr_id_ref integer not null, + oct_usa_id_ref text not null, + oct_name varchar(64) not null default '', + oct_token varchar(64) not null, + oct_token_secret varchar(64) not null, + oct_token_type consumer_token_type, + oct_token_ttl timestamp not null default timestamp '9999-12-31', + oct_timestamp timestamp not null default current_timestamp, + + unique (oct_ocr_id_ref, oct_token), + unique (oct_usa_id_ref, oct_ocr_id_ref, oct_token_type, oct_name), + + foreign key (oct_ocr_id_ref) references oauth_consumer_registry (ocr_id) + on update cascade + on delete cascade +); + + +COMMENT ON TABLE oauth_consumer_token IS 'Table used to sign requests for sending to a server by the consumer'; + +# +# ////////////////// SERVER SIDE ///////////////// +# + +# Table holding consumer key/secret combos an user issued to consumers. +# Used for verification of incoming requests. + +CREATE TABLE oauth_server_registry ( + osr_id serial primary key, + osr_usa_id_ref text, + osr_consumer_key varchar(64) not null, + osr_consumer_secret varchar(64) not null, + osr_enabled boolean not null default true, + osr_status varchar(16) not null, + osr_requester_name varchar(64) not null, + osr_requester_email varchar(64) not null, + osr_callback_uri varchar(255) not null, + osr_application_uri varchar(255) not null, + osr_application_title varchar(80) not null, + osr_application_descr text not null, + osr_application_notes text not null, + osr_application_type varchar(20) not null, + osr_application_commercial boolean not null default false, + osr_issue_date timestamp not null, + osr_timestamp timestamp not null default current_timestamp, + + unique (osr_consumer_key) +); + + +COMMENT ON TABLE oauth_server_registry IS 'Table holding consumer key/secret combos an user issued to consumers'; + +# Nonce used by a certain consumer, every used nonce should be unique, this prevents +# replaying attacks. We need to store all timestamp/nonce combinations for the +# maximum timestamp received. + +CREATE TABLE oauth_server_nonce ( + osn_id serial primary key, + osn_consumer_key varchar(64) not null, + osn_token varchar(64) not null, + osn_timestamp bigint not null, + osn_nonce varchar(80) not null, + + unique (osn_consumer_key, osn_token, osn_timestamp, osn_nonce) +); + + +COMMENT ON TABLE oauth_server_nonce IS 'Nonce used by a certain consumer, every used nonce should be unique, this prevents replaying attacks'; + +# Table used to verify signed requests sent to a server by the consumer +# When the verification is succesful then the associated user id is returned. + +-- Create enum type token_type +CREATE TYPE server_token_type AS ENUM ( + 'request', + 'access' +); + +CREATE TABLE oauth_server_token ( + ost_id serial primary key, + ost_osr_id_ref integer not null, + ost_usa_id_ref text not null, + ost_token varchar(64) not null, + ost_token_secret varchar(64) not null, + ost_token_type server_token_type, + ost_authorized boolean not null default false, + ost_referrer_host varchar(128) not null default '', + ost_token_ttl timestamp not null default timestamp '9999-12-31', + ost_timestamp timestamp not null default current_timestamp, + ost_verifier char(10), + ost_callback_url varchar(512), + + unique (ost_token), + + foreign key (ost_osr_id_ref) references oauth_server_registry (osr_id) + on update cascade + on delete cascade +); + + +COMMENT ON TABLE oauth_server_token IS 'Table used to verify signed requests sent to a server by the consumer'; diff --git a/vendor/oauth-php/test/discovery/xrds-fireeagle.xrds b/vendor/oauth-php/test/discovery/xrds-fireeagle.xrds new file mode 100644 index 0000000..0f5eba2 --- /dev/null +++ b/vendor/oauth-php/test/discovery/xrds-fireeagle.xrds @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="UTF-8"?> +<XRDS xmlns="xri://$xrds"> + + <!-- FireEagle User-Centric OAuth Configuration --> + <XRD xml:id="oauth" xmlns:simple="http://xrds-simple.net/core/1.0" xmlns="xri://$XRD*($v*2.0)" version="2.0"> + + <Type>xri://$xrds*simple</Type> + <Expires>2008-04-15T00:25:30-07:00</Expires> + + <!-- Request Token --> + <Service> + <Type>http://oauth.net/core/1.0/endpoint/request</Type> + + <Type>http://oauth.net/core/1.0/parameters/auth-header</Type> + <Type>http://oauth.net/core/1.0/parameters/post-body</Type> + <Type>http://oauth.net/core/1.0/parameters/uri-query</Type> + <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type> + <Type>http://oauth.net/core/1.0/signature/PLAINTEXT</Type> + + <URI>https://fireeagle.yahooapis.com/oauth/request_token</URI> + </Service> + + <!-- User Authorization --> + <Service> + <Type>http://oauth.net/core/1.0/endpoint/authorize</Type> + + <Type>http://oauth.net/core/1.0/parameters/auth-header</Type> + <Type>http://oauth.net/core/1.0/parameters/uri-query</Type> + + <URI>https://fireeagle.yahooapis.com/oauth/access_token</URI> + </Service> + + <!-- Access Token --> + <Service> + <Type>http://oauth.net/core/1.0/endpoint/access</Type> + + <Type>http://oauth.net/core/1.0/parameters/auth-header</Type> + <Type>http://oauth.net/core/1.0/parameters/post-body</Type> + <Type>http://oauth.net/core/1.0/parameters/uri-query</Type> + <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type> + <Type>http://oauth.net/core/1.0/signature/PLAINTEXT</Type> + + <URI>http://fireeagle.yahoo.net/oauth/authorize</URI> + </Service> + + <!-- Protected Resources --> + <Service> + <Type>http://oauth.net/core/1.0/endpoint/resource</Type> + + <Type>http://oauth.net/core/1.0/parameters/auth-header</Type> + <Type>http://oauth.net/core/1.0/parameters/post-body</Type> + <Type>http://oauth.net/core/1.0/parameters/uri-query</Type> + <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type> + <Type>http://oauth.net/core/1.0/signature/PLAINTEXT</Type> + </Service> + + <!-- Consumer Identity --> + + <!-- Manual Consumer Identity Allocation --> + <Service> + <Type>http://oauth.net/discovery/1.0/consumer-identity/oob</Type> + <URI>https://fireeagle.yahoo.net/developer/create</URI> + </Service> + </XRD> + + <!-- Global Resource Definition --> + + <XRD xmlns="xri://$XRD*($v*2.0)" version="2.0"> + <Type>xri://$xrds*simple</Type> + + <!-- OAuth Endpoints Definition --> + <Service> + <Type>http://oauth.net/discovery/1.0</Type> + <URI>#oauth</URI> + </Service> + </XRD> + +</XRDS>
\ No newline at end of file diff --git a/vendor/oauth-php/test/discovery/xrds-getsatisfaction.xrds b/vendor/oauth-php/test/discovery/xrds-getsatisfaction.xrds new file mode 100644 index 0000000..ab94b5b --- /dev/null +++ b/vendor/oauth-php/test/discovery/xrds-getsatisfaction.xrds @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="UTF-8"?> +<XRDS xmlns="xri://$xrds"> + + <XRD xml:id="oauth" xmlns:simple="http://xrds-simple.net/core/1.0" xmlns="xri://$XRD*($v*2.0)" version="2.0"> + <Type>xri://$xrds*simple</Type> + <Expires>2008-04-30T23:59:59Z</Expires> + + <!-- Request Token --> + <Service> + <Type>http://oauth.net/core/1.0/endpoint/request</Type> + + <Type>http://oauth.net/core/1.0/parameters/auth-header</Type> + <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type> + + <URI>http://getsatisfaction.com/api/request_token</URI> + </Service> + + <Service> + <Type>http://oauth.net/core/1.0/endpoint/authorize</Type> + + <Type>http://oauth.net/core/1.0/parameters/uri-query</Type> + + <URI>http://getsatisfaction.com/api/authorize</URI> + </Service> + + <!-- Access Token --> + <Service> + <Type>http://oauth.net/core/1.0/endpoint/access</Type> + + <Type>http://oauth.net/core/1.0/parameters/auth-header</Type> + <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type> + + <URI>http://getsatisfaction.com/api/access_token</URI> + </Service> + + <!-- Protected Resources --> + <!-- + + To test successful access token grant, make a request against + + http://api.getsatisfaction.com/me + + The API should respond with hCard of the user who authorized the token + --> + <Service> + <Type>http://oauth.net/core/1.0/endpoint/resource</Type> + + <Type>http://oauth.net/core/1.0/parameters/auth-header</Type> + <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type> + + </Service> + + <!-- Consumer Identity --> + + <Service> + <Type>http://oauth.net/discovery/1.0/consumer-identity/oob</Type> + <URI>http://getsatisfaction.com/me/extensions/new</URI> + </Service> + </XRD> + + <!-- Global Resource Definition --> + + <XRD xmlns="xri://$XRD*($v*2.0)" version="2.0"> + <Type>xri://$xrds*simple</Type> + + <!-- OAuth Endpoints Definition --> + <Service priority="10"> + <Type>http://oauth.net/discovery/1.0</Type> + <URI>#oauth</URI> + </Service> + </XRD> + +</XRDS>
\ No newline at end of file diff --git a/vendor/oauth-php/test/discovery/xrds-magnolia.xrds b/vendor/oauth-php/test/discovery/xrds-magnolia.xrds new file mode 100644 index 0000000..361b5c9 --- /dev/null +++ b/vendor/oauth-php/test/discovery/xrds-magnolia.xrds @@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="UTF-8"?> +<XRDS xmlns="xri://$xrds"> + + <!-- Ma.gnolia OAuth Configuration --> + <XRD xml:id="oauth" xmlns="xri://$XRD*($v*2.0)" version="2.0"> + + <Type>xri://$xrds*simple</Type> + <Expires>2008-04-13T07:34:58Z</Expires> + + <!-- Request Token --> + <Service> + <Type>http://oauth.net/core/1.0/endpoint/request</Type> + + <Type>http://oauth.net/core/1.0/parameters/auth-header</Type> + <Type>http://oauth.net/core/1.0/parameters/post-body</Type> + <Type>http://oauth.net/core/1.0/parameters/uri-query</Type> + <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type> + <Type>http://oauth.net/core/1.0/signature/RSA-SHA1</Type> + <Type>http://oauth.net/core/1.0/signature/PLAINTEXT</Type> + + <URI>https://ma.gnolia.com/oauth/get_request_token</URI> + </Service> + + <!-- User Authorization (HTTPS Prefered) --> + <Service> + <Type>http://oauth.net/core/1.0/endpoint/authorize</Type> + + <Type>http://oauth.net/core/1.0/parameters/auth-header</Type> + <Type>http://oauth.net/core/1.0/parameters/uri-query</Type> + + <URI priority="10">https://ma.gnolia.com/oauth/authorize</URI> + <URI priority="20">http://ma.gnolia.com/oauth/authorize</URI> + </Service> + + <!-- Access Token --> + <Service> + <Type>http://oauth.net/core/1.0/endpoint/access</Type> + + <Type>http://oauth.net/core/1.0/parameters/auth-header</Type> + <Type>http://oauth.net/core/1.0/parameters/post-body</Type> + <Type>http://oauth.net/core/1.0/parameters/uri-query</Type> + <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type> + <Type>http://oauth.net/core/1.0/signature/RSA-SHA1</Type> + <Type>http://oauth.net/core/1.0/signature/PLAINTEXT</Type> + + <URI>https://ma.gnolia.com/oauth/get_access_token</URI> + </Service> + + <!-- Protected Resources --> + <Service> + <Type>http://oauth.net/core/1.0/endpoint/resource</Type> + + <Type>http://oauth.net/core/1.0/parameters/auth-header</Type> + <Type>http://oauth.net/core/1.0/parameters/post-body</Type> + <Type>http://oauth.net/core/1.0/parameters/uri-query</Type> + <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type> + <Type>http://oauth.net/core/1.0/signature/RSA-SHA1</Type> + </Service> + + <!-- Consumer Identity --> + + <!-- Manual Consumer Identity Allocation --> + <Service> + <Type>http://oauth.net/discovery/1.0/consumer-identity/oob</Type> + <URI>http://ma.gnolia.com/applications/new</URI> + </Service> + </XRD> + + <!-- Global Resource Definition --> + + <XRD xmlns="xri://$XRD*($v*2.0)" version="2.0"> + <Type>xri://$xrds*simple</Type> + + <!-- OAuth Endpoints Definition --> + <Service priority="10"> + <Type>http://oauth.net/discovery/1.0</Type> + <URI>#oauth</URI> + </Service> + </XRD> + +</XRDS>
\ No newline at end of file diff --git a/vendor/oauth-php/test/oauth_test.php b/vendor/oauth-php/test/oauth_test.php new file mode 100644 index 0000000..c7d174b --- /dev/null +++ b/vendor/oauth-php/test/oauth_test.php @@ -0,0 +1,188 @@ +<?php + +/** + * Tests of OAuth implementation. + * + * @version $Id$ + * @author Marc Worrell <marcw@pobox.com> + * @date Nov 29, 2007 3:46:56 PM + * @see http://wiki.oauth.net/TestCases + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +require_once dirname(__FILE__) . '/../library/OAuthRequest.php'; +require_once dirname(__FILE__) . '/../library/OAuthRequester.php'; +require_once dirname(__FILE__) . '/../library/OAuthRequestSigner.php'; +require_once dirname(__FILE__) . '/../library/OAuthRequestVerifier.php'; + +if (!function_exists('getallheaders')) +{ + function getallheaders() + { + return array(); + } +} + + +oauth_test(); + +function oauth_test () +{ + error_reporting(E_ALL); + + header('Content-Type: text/plain; charset=utf-8'); + + echo "Performing OAuth module tests.\n\n"; + echo "See also: http://wiki.oauth.net/TestCases\n\n"; + + assert_options(ASSERT_CALLBACK, 'oauth_assert_handler'); + assert_options(ASSERT_WARNING, 0); + + $req = new OAuthRequest('http://www.example.com', 'GET'); + + echo "***** Parameter Encoding *****\n\n"; + + assert('$req->urlencode(\'abcABC123\') == \'abcABC123\''); + assert('$req->urlencode(\'-._~\') == \'-._~\''); + assert('$req->urlencode(\'%\') == \'%25\''); + assert('$req->urlencode(\'&=*\') == \'%26%3D%2A\''); + assert('$req->urlencode(\'&=*\') == \'%26%3D%2A\''); + assert('$req->urlencode("\n") == \'%0A\''); + assert('$req->urlencode(" ") == \'%20\''); + assert('$req->urlencode("\x7f") == \'%7F\''); + + + echo "***** Normalize Request Parameters *****\n\n"; + + $req = new OAuthRequest('http://example.com/?name', 'GET'); + assert('$req->getNormalizedParams() == \'name=\''); + + $req = new OAuthRequest('http://example.com/?a=b', 'GET'); + assert('$req->getNormalizedParams() == \'a=b\''); + + $req = new OAuthRequest('http://example.com/?a=b&c=d', 'GET'); + assert('$req->getNormalizedParams() == \'a=b&c=d\''); + + // At this moment we don't support two parameters with the same name + // so I changed this test case to "a=" and "b=" and not "a=" and "a=" + $req = new OAuthRequest('http://example.com/?b=x!y&a=x+y', 'GET'); + assert('$req->getNormalizedParams() == \'a=x%2By&b=x%21y\''); + + $req = new OAuthRequest('http://example.com/?x!y=a&x=a', 'GET'); + assert('$req->getNormalizedParams() == \'x=a&x%21y=a\''); + + + echo "***** Base String *****\n\n"; + + $req = new OAuthRequest('http://example.com/?n=v', 'GET'); + assert('$req->signatureBaseString() == \'GET&http%3A%2F%2Fexample.com%2F&n%3Dv\''); + + $req = new OAuthRequest( + 'https://photos.example.net/request_token', + 'POST', + 'oauth_version=1.0&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_timestamp=1191242090&oauth_nonce=hsu94j3884jdopsl&oauth_signature_method=PLAINTEXT&oauth_signature=ignored', + array('X-OAuth-Test' => true)); + assert('$req->signatureBaseString() == \'POST&https%3A%2F%2Fphotos.example.net%2Frequest_token&oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dhsu94j3884jdopsl%26oauth_signature_method%3DPLAINTEXT%26oauth_timestamp%3D1191242090%26oauth_version%3D1.0\''); + + $req = new OAuthRequest( + 'http://photos.example.net/photos?file=vacation.jpg&size=original&oauth_version=1.0&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_token=nnch734d00sl2jdk&oauth_timestamp=1191242096&oauth_nonce=kllo9940pd9333jh&oauth_signature=ignored&oauth_signature_method=HMAC-SHA1', + 'GET'); + assert('$req->signatureBaseString() == \'GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal\''); + + + echo "***** HMAC-SHA1 *****\nRequest signing\n"; + + OAuthStore::instance('MySQL', array('conn'=>false)); + $req = new OAuthRequestSigner('http://photos.example.net/photos?file=vacation.jpg&size=original', 'GET'); + + assert('$req->urldecode($req->calculateDataSignature(\'bs\', \'cs\', \'\', \'HMAC-SHA1\')) == \'egQqG5AJep5sJ7anhXju1unge2I=\''); + assert('$req->urldecode($req->calculateDataSignature(\'bs\', \'cs\', \'ts\', \'HMAC-SHA1\')) == \'VZVjXceV7JgPq/dOTnNmEfO0Fv8=\''); + + $secrets = array( + 'consumer_key' => 'dpf43f3p2l4k3l03', + 'consumer_secret' => 'kd94hf93k423kf44', + 'token' => 'nnch734d00sl2jdk', + 'token_secret' => 'pfkkdhi9sl3r4s00', + 'signature_methods' => array('HMAC-SHA1'), + 'nonce' => 'kllo9940pd9333jh', + 'timestamp' => '1191242096' + ); + $req->sign(0, $secrets); + assert('$req->getParam(\'oauth_signature\', true) == \'tR3+Ty81lMeYAr/Fid0kMTYa/WM=\''); + + echo "***** HMAC-SHA1 *****\nRequest verification\n"; + + $req = new OAuthRequestVerifier( + 'http://photos.example.net/photos?file=vacation.jpg&size=original' + .'&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_token=nnch734d00sl2jdk' + .'&oauth_signature_method=HMAC-SHA1&oauth_nonce=kllo9940pd9333jh' + .'&oauth_timestamp=1191242096&oauth_version=1.0' + .'&oauth_signature='.rawurlencode('tR3+Ty81lMeYAr/Fid0kMTYa/WM=') + , 'GET'); + + $req->verifySignature('kd94hf93k423kf44', 'pfkkdhi9sl3r4s00'); + + echo "\n"; + echo "***** Yahoo! test case ******\n\n"; + + OAuthStore::instance('MySQL', array('conn'=>false)); + $req = new OAuthRequestSigner('http://example.com:80/photo', 'GET'); + + $req->setParam('title', 'taken with a 30% orange filter'); + $req->setParam('file', 'mountain & water view'); + $req->setParam('format', 'jpeg'); + $req->setParam('include', array('date','aperture')); + + $secrets = array( + 'consumer_key' => '1234=asdf=4567', + 'consumer_secret' => 'erks823*43=asd&123ls%23', + 'token' => 'asdf-4354=asew-5698', + 'token_secret' => 'dis9$#$Js009%==', + 'signature_methods' => array('HMAC-SHA1'), + 'nonce' => '3jd834jd9', + 'timestamp' => '12303202302' + ); + $req->sign(0, $secrets); + + // echo "Basestring:\n",$req->signatureBaseString(), "\n\n"; + + //echo "queryString:\n",$req->getQueryString(), "\n\n"; + assert('$req->getQueryString() == \'title=taken%20with%20a%2030%25%20orange%20filter&file=mountain%20%26%20water%20view&format=jpeg&include=date&include=aperture\''); + + //echo "oauth_signature:\n",$req->getParam('oauth_signature', true),"\n\n"; + assert('$req->getParam(\'oauth_signature\', true) == \'jMdUSR1vOr3SzNv3gZ5DDDuGirA=\''); + + echo "\n\nFinished.\n"; +} + + +function oauth_assert_handler ( $file, $line, $code ) +{ + echo "\nAssertion failed in $file:$line + $code\n\n"; +} + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?>
\ No newline at end of file diff --git a/vendor/phpass/PasswordHash.php b/vendor/phpass/PasswordHash.php new file mode 100644 index 0000000..93c826a --- /dev/null +++ b/vendor/phpass/PasswordHash.php @@ -0,0 +1,253 @@ +<?php +# +# Portable PHP password hashing framework. +# +# Version 0.3 / genuine. +# +# Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in +# the public domain. Revised in subsequent years, still public domain. +# +# There's absolutely no warranty. +# +# The homepage URL for this framework is: +# +# http://www.openwall.com/phpass/ +# +# Please be sure to update the Version line if you edit this file in any way. +# It is suggested that you leave the main version number intact, but indicate +# your project name (after the slash) and add your own revision information. +# +# Please do not change the "private" password hashing method implemented in +# here, thereby making your hashes incompatible. However, if you must, please +# change the hash type identifier (the "$P$") to something different. +# +# Obviously, since this code is in the public domain, the above are not +# requirements (there can be none), but merely suggestions. +# +class PasswordHash { + var $itoa64; + var $iteration_count_log2; + var $portable_hashes; + var $random_state; + + function __construct($iteration_count_log2, $portable_hashes) + { + $this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; + + if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31) + $iteration_count_log2 = 8; + $this->iteration_count_log2 = $iteration_count_log2; + + $this->portable_hashes = $portable_hashes; + + $this->random_state = microtime(); + if (function_exists('getmypid')) + $this->random_state .= getmypid(); + } + + function get_random_bytes($count) + { + $output = ''; + if (is_readable('/dev/urandom') && + ($fh = @fopen('/dev/urandom', 'rb'))) { + $output = fread($fh, $count); + fclose($fh); + } + + if (strlen($output) < $count) { + $output = ''; + for ($i = 0; $i < $count; $i += 16) { + $this->random_state = + md5(microtime() . $this->random_state); + $output .= + pack('H*', md5($this->random_state)); + } + $output = substr($output, 0, $count); + } + + return $output; + } + + function encode64($input, $count) + { + $output = ''; + $i = 0; + do { + $value = ord($input[$i++]); + $output .= $this->itoa64[$value & 0x3f]; + if ($i < $count) + $value |= ord($input[$i]) << 8; + $output .= $this->itoa64[($value >> 6) & 0x3f]; + if ($i++ >= $count) + break; + if ($i < $count) + $value |= ord($input[$i]) << 16; + $output .= $this->itoa64[($value >> 12) & 0x3f]; + if ($i++ >= $count) + break; + $output .= $this->itoa64[($value >> 18) & 0x3f]; + } while ($i < $count); + + return $output; + } + + function gensalt_private($input) + { + $output = '$P$'; + $output .= $this->itoa64[min($this->iteration_count_log2 + + ((PHP_VERSION >= '5') ? 5 : 3), 30)]; + $output .= $this->encode64($input, 6); + + return $output; + } + + function crypt_private($password, $setting) + { + $output = '*0'; + if (substr($setting, 0, 2) == $output) + $output = '*1'; + + $id = substr($setting, 0, 3); + # We use "$P$", phpBB3 uses "$H$" for the same thing + if ($id != '$P$' && $id != '$H$') + return $output; + + $count_log2 = strpos($this->itoa64, $setting[3]); + if ($count_log2 < 7 || $count_log2 > 30) + return $output; + + $count = 1 << $count_log2; + + $salt = substr($setting, 4, 8); + if (strlen($salt) != 8) + return $output; + + # We're kind of forced to use MD5 here since it's the only + # cryptographic primitive available in all versions of PHP + # currently in use. To implement our own low-level crypto + # in PHP would result in much worse performance and + # consequently in lower iteration counts and hashes that are + # quicker to crack (by non-PHP code). + if (PHP_VERSION >= '5') { + $hash = md5($salt . $password, TRUE); + do { + $hash = md5($hash . $password, TRUE); + } while (--$count); + } else { + $hash = pack('H*', md5($salt . $password)); + do { + $hash = pack('H*', md5($hash . $password)); + } while (--$count); + } + + $output = substr($setting, 0, 12); + $output .= $this->encode64($hash, 16); + + return $output; + } + + function gensalt_extended($input) + { + $count_log2 = min($this->iteration_count_log2 + 8, 24); + # This should be odd to not reveal weak DES keys, and the + # maximum valid value is (2**24 - 1) which is odd anyway. + $count = (1 << $count_log2) - 1; + + $output = '_'; + $output .= $this->itoa64[$count & 0x3f]; + $output .= $this->itoa64[($count >> 6) & 0x3f]; + $output .= $this->itoa64[($count >> 12) & 0x3f]; + $output .= $this->itoa64[($count >> 18) & 0x3f]; + + $output .= $this->encode64($input, 3); + + return $output; + } + + function gensalt_blowfish($input) + { + # This one needs to use a different order of characters and a + # different encoding scheme from the one in encode64() above. + # We care because the last character in our encoded string will + # only represent 2 bits. While two known implementations of + # bcrypt will happily accept and correct a salt string which + # has the 4 unused bits set to non-zero, we do not want to take + # chances and we also do not want to waste an additional byte + # of entropy. + $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + + $output = '$2a$'; + $output .= chr(ord('0') + $this->iteration_count_log2 / 10); + $output .= chr(ord('0') + $this->iteration_count_log2 % 10); + $output .= '$'; + + $i = 0; + do { + $c1 = ord($input[$i++]); + $output .= $itoa64[$c1 >> 2]; + $c1 = ($c1 & 0x03) << 4; + if ($i >= 16) { + $output .= $itoa64[$c1]; + break; + } + + $c2 = ord($input[$i++]); + $c1 |= $c2 >> 4; + $output .= $itoa64[$c1]; + $c1 = ($c2 & 0x0f) << 2; + + $c2 = ord($input[$i++]); + $c1 |= $c2 >> 6; + $output .= $itoa64[$c1]; + $output .= $itoa64[$c2 & 0x3f]; + } while (1); + + return $output; + } + + function HashPassword($password) + { + $random = ''; + + if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) { + $random = $this->get_random_bytes(16); + $hash = + crypt($password, $this->gensalt_blowfish($random)); + if (strlen($hash) == 60) + return $hash; + } + + if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) { + if (strlen($random) < 3) + $random = $this->get_random_bytes(3); + $hash = + crypt($password, $this->gensalt_extended($random)); + if (strlen($hash) == 20) + return $hash; + } + + if (strlen($random) < 6) + $random = $this->get_random_bytes(6); + $hash = + $this->crypt_private($password, + $this->gensalt_private($random)); + if (strlen($hash) == 34) + return $hash; + + # Returning '*' on error is safe here, but would _not_ be safe + # in a crypt(3)-like function used _both_ for generating new + # hashes and for validating passwords against existing hashes. + return '*'; + } + + function CheckPassword($password, $stored_hash) + { + $hash = $this->crypt_private($password, $stored_hash); + if ($hash[0] == '*') + $hash = crypt($password, $stored_hash); + + return $hash == $stored_hash; + } +} + +?> diff --git a/vendor/sasl/LICENSE b/vendor/sasl/LICENSE new file mode 100644 index 0000000..f731633 --- /dev/null +++ b/vendor/sasl/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2001-2005, Manuel Lemos
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the Manuel Lemos nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file diff --git a/vendor/sasl/basic_sasl_client.php b/vendor/sasl/basic_sasl_client.php new file mode 100644 index 0000000..b2972b5 --- /dev/null +++ b/vendor/sasl/basic_sasl_client.php @@ -0,0 +1,61 @@ +<?php +/* + * basic_sasl_client.php + * + * @(#) $Id: basic_sasl_client.php,v 1.1 2004/11/17 08:01:23 mlemos Exp $ + * + */ + +define("SASL_BASIC_STATE_START", 0); +define("SASL_BASIC_STATE_DONE", 1); + +class basic_sasl_client_class +{ + var $credentials=array(); + var $state=SASL_BASIC_STATE_START; + + Function Initialize(&$client) + { + return(1); + } + + Function Start(&$client, &$message, &$interactions) + { + if($this->state!=SASL_BASIC_STATE_START) + { + $client->error="Basic authentication state is not at the start"; + return(SASL_FAIL); + } + $this->credentials=array( + "user"=>"", + "password"=>"" + ); + $defaults=array( + ); + $status=$client->GetCredentials($this->credentials,$defaults,$interactions); + if($status==SASL_CONTINUE) + { + $message=$this->credentials["user"].":".$this->credentials["password"]; + $this->state=SASL_BASIC_STATE_DONE; + } + else + Unset($message); + return($status); + } + + Function Step(&$client, $response, &$message, &$interactions) + { + switch($this->state) + { + case SASL_BASIC_STATE_DONE: + $client->error="Basic authentication was finished without success"; + return(SASL_FAIL); + default: + $client->error="invalid Basic authentication step state"; + return(SASL_FAIL); + } + return(SASL_CONTINUE); + } +}; + +?>
\ No newline at end of file diff --git a/vendor/sasl/cram_md5_sasl_client.php b/vendor/sasl/cram_md5_sasl_client.php new file mode 100644 index 0000000..69bd625 --- /dev/null +++ b/vendor/sasl/cram_md5_sasl_client.php @@ -0,0 +1,67 @@ +<?php +/* + * cram_md5_sasl_client.php + * + * @(#) $Id: cram_md5_sasl_client.php,v 1.3 2004/11/17 08:00:37 mlemos Exp $ + * + */ + +define("SASL_CRAM_MD5_STATE_START", 0); +define("SASL_CRAM_MD5_STATE_RESPOND_CHALLENGE", 1); +define("SASL_CRAM_MD5_STATE_DONE", 2); + +class cram_md5_sasl_client_class +{ + var $credentials=array(); + var $state=SASL_CRAM_MD5_STATE_START; + + Function Initialize(&$client) + { + return(1); + } + + Function HMACMD5($key,$text) + { + $key=(strlen($key)<64 ? str_pad($key,64,"\0") : substr($key,0,64)); + return(md5((str_repeat("\x5c", 64)^$key).pack("H32", md5((str_repeat("\x36", 64)^$key).$text)))); + } + + Function Start(&$client, &$message, &$interactions) + { + if($this->state!=SASL_CRAM_MD5_STATE_START) + { + $client->error="CRAM-MD5 authentication state is not at the start"; + return(SASL_FAIL); + } + $this->credentials=array( + "user"=>"", + "password"=>"" + ); + $defaults=array(); + $status=$client->GetCredentials($this->credentials,$defaults,$interactions); + if($status==SASL_CONTINUE) + $this->state=SASL_CRAM_MD5_STATE_RESPOND_CHALLENGE; + Unset($message); + return($status); + } + + Function Step(&$client, $response, &$message, &$interactions) + { + switch($this->state) + { + case SASL_CRAM_MD5_STATE_RESPOND_CHALLENGE: + $message=$this->credentials["user"]." ".$this->HMACMD5($this->credentials["password"], $response); + $this->state=SASL_CRAM_MD5_STATE_DONE; + break; + case SASL_CRAM_MD5_STATE_DONE: + $client->error="CRAM-MD5 authentication was finished without success"; + return(SASL_FAIL); + default: + $client->error="invalid CRAM-MD5 authentication step state"; + return(SASL_FAIL); + } + return(SASL_CONTINUE); + } +}; + +?>
\ No newline at end of file diff --git a/vendor/sasl/digest_sasl_client.php b/vendor/sasl/digest_sasl_client.php new file mode 100644 index 0000000..924887d --- /dev/null +++ b/vendor/sasl/digest_sasl_client.php @@ -0,0 +1,135 @@ +<?php +/* + * digest_sasl_client.php + * + * @(#) $Id: digest_sasl_client.php,v 1.1 2005/10/27 05:24:15 mlemos Exp $ + * + */ + +define('SASL_DIGEST_STATE_START', 0); +define('SASL_DIGEST_STATE_RESPOND_CHALLENGE', 1); +define('SASL_DIGEST_STATE_DONE', 2); + +class digest_sasl_client_class +{ + var $credentials=array(); + var $state=SASL_DIGEST_STATE_START; + + Function unq($string) + { + return(($string[0]=='"' && $string[strlen($string)-1]=='"') ? substr($string, 1, strlen($string)-2) : $string); + } + + Function H($data) + { + return md5($data); + } + + Function KD($secret, $data) + { + return $this->H($secret.':'.$data); + } + + Function Initialize(&$client) + { + return(1); + } + + Function Start(&$client, &$message, &$interactions) + { + if($this->state!=SASL_DIGEST_STATE_START) + { + $client->error='Digest authentication state is not at the start'; + return(SASL_FAIL); + } + $this->credentials=array( + 'user'=>'', + 'password'=>'', + 'uri'=>'', + 'method'=>'', + 'session'=>'' + ); + $defaults=array(); + $status=$client->GetCredentials($this->credentials,$defaults,$interactions); + if($status==SASL_CONTINUE) + $this->state=SASL_DIGEST_STATE_RESPOND_CHALLENGE; + Unset($message); + return($status); + } + + Function Step(&$client, $response, &$message, &$interactions) + { + switch($this->state) + { + case SASL_DIGEST_STATE_RESPOND_CHALLENGE: + $values=explode(',',$response); + $parameters=array(); + for($v=0; $v<count($values); $v++) + $parameters[strtok(trim($values[$v]), '=')]=strtok(''); + + $message='username="'.$this->credentials['user'].'"'; + if(!IsSet($parameters[$p='realm']) + && !IsSet($parameters[$p='nonce'])) + { + $client->error='Digest authentication parameter '.$p.' is missing from the server response'; + return(SASL_FAIL); + } + $message.=', realm='.$parameters['realm']; + $message.=', nonce='.$parameters['nonce']; + $message.=', uri="'.$this->credentials['uri'].'"'; + if(IsSet($parameters['algorithm'])) + { + $algorithm=$this->unq($parameters['algorithm']); + $message.=', algorithm='.$parameters['algorithm']; + } + else + $algorithm=''; + + $realm=$this->unq($parameters['realm']); + $nonce=$this->unq($parameters['nonce']); + if(IsSet($parameters['qop'])) + { + switch($qop=$this->unq($parameters['qop'])) + { + case "auth": + $cnonce=$this->credentials['session']; + break; + default: + $client->error='Digest authentication quality of protection '.$qop.' is not yet supported'; + return(SASL_FAIL); + } + } + $nc_value='00000001'; + if(IsSet($parameters['qop']) + && !strcmp($algorithm, 'MD5-sess')) + $A1=$this->H($this->credentials['user'].':'. $realm.':'. $this->credentials['password']).':'.$nonce.':'.$cnonce; + else + $A1=$this->credentials['user'].':'. $realm.':'. $this->credentials['password']; + $A2=$this->credentials['method'].':'.$this->credentials['uri']; + if(IsSet($parameters['qop'])) + $response=$this->KD($this->H($A1), $nonce.':'. $nc_value.':'. $cnonce.':'. $qop.':'. $this->H($A2)); + else + $response=$this->KD($this->H($A1), $nonce.':'. $this->H($A2)); + $message.=', response="'.$response.'"'; + if(IsSet($parameters['opaque'])) + $message.=', opaque='.$parameters['opaque']; + if(IsSet($parameters['qop'])) + $message.=', qop="'.$qop.'"'; + $message.=', nc='.$nc_value; + if(IsSet($parameters['qop'])) + $message.=', cnonce="'.$cnonce.'"'; + $client->encode_response=0; + $this->state=SASL_DIGEST_STATE_DONE; + break; + case SASL_DIGEST_STATE_DONE: + $client->error='Digest authentication was finished without success'; + return(SASL_FAIL); + default: + $client->error='invalid Digest authentication step state'; + return(SASL_FAIL); + } + return(SASL_CONTINUE); + } +}; + +?>
\ No newline at end of file diff --git a/vendor/sasl/documentation/sasl_class.html b/vendor/sasl/documentation/sasl_class.html new file mode 100644 index 0000000..6948ab8 --- /dev/null +++ b/vendor/sasl/documentation/sasl_class.html @@ -0,0 +1,193 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> +<head> +<title>Class: Simple Authentication and Security Layer client</title> +</head> +<body> +<center><h1>Class: Simple Authentication and Security Layer client</h1></center> +<hr /> +<ul> +<p><b>Version:</b> <tt>@(#) $Id: sasl.php,v 1.11 2005/10/31 18:43:27 mlemos Exp $</tt></p> +<h2><a name="table_of_contents">Contents</a></h2> +<ul> +<li><a href="#2.1.1">Summary</a></li> +<ul> +<li><a href="#3.2.0">Name</a></li> +<li><a href="#3.2.0.0">Author</a></li> +<li><a href="#3.2.0.1">Copyright</a></li> +<li><a href="#3.2.0.2">Version</a></li> +<li><a href="#3.2.0.3">Purpose</a></li> +<li><a href="#3.2.0.4">Usage</a></li> +</ul> +<li><a href="#4.1.1">Variables</a></li> +<ul> +<li><a href="#5.2.4">error</a></li> +<li><a href="#5.2.5">mechanism</a></li> +<li><a href="#5.2.6">encode_response</a></li> +</ul> +<li><a href="#6.1.1">Functions</a></li> +<ul> +<li><a href="#7.2.5">SetCredential</a></li> +<li><a href="#9.2.6">GetCredentials</a></li> +<li><a href="#11.2.7">Start</a></li> +<li><a href="#13.2.8">Step</a></li> +</ul> +</ul> +<p><a href="#table_of_contents">Top of the table of contents</a></p> +</ul> +<hr /> +<ul> +<h2><li><a name="2.1.1">Summary</a></li></h2> +<ul> +<h3><a name="3.2.0">Name</a></h3> +<p>Simple Authentication and Security Layer client</p> +<h3><a name="3.2.0.0">Author</a></h3> +<p>Manuel Lemos (<a href="mailto:mlemos-at-acm.org">mlemos-at-acm.org</a>)</p> +<h3><a name="3.2.0.1">Copyright</a></h3> +<p>Copyright © (C) Manuel Lemos 2004</p> +<h3><a name="3.2.0.2">Version</a></h3> +<p>@(#) $Id: sasl.php,v 1.11 2005/10/31 18:43:27 mlemos Exp $</p> +<h3><a name="3.2.0.3">Purpose</a></h3> +<p>Provide a common interface to plug-in driver classes that implement different mechanisms for authentication used by clients of standard protocols like SMTP, POP3, IMAP, HTTP, etc.. Currently the supported authentication mechanisms are: <tt>PLAIN</tt>, <tt>LOGIN</tt>, <tt>CRAM-MD5</tt>, <tt>Digest</tt> and <tt>NTML</tt> (Windows or Samba).</p> +<h3><a name="3.2.0.4">Usage</a></h3> +<p>.</p> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> +<hr /> +<ul> +<h2><li><a name="variables"></a><a name="4.1.1">Variables</a></li></h2> +<ul> +<li><tt><a href="#variable_error">error</a></tt></li><br /> +<li><tt><a href="#variable_mechanism">mechanism</a></tt></li><br /> +<li><tt><a href="#variable_encode_response">encode_response</a></tt></li><br /> +<p><a href="#table_of_contents">Table of contents</a></p> +<h3><a name="variable_error"></a><li><a name="5.2.4">error</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>''</tt></p> +<h3>Purpose</h3> +<p>Store the message that is returned when an error occurs.</p> +<h3>Usage</h3> +<p>Check this variable to understand what happened when a call to any of the class functions has failed.</p> +<p> This class uses cumulative error handling. This means that if one class functions that may fail is called and this variable was already set to an error message due to a failure in a previous call to the same or other function, the function will also fail and does not do anything.</p> +<p> This allows programs using this class to safely call several functions that may fail and only check the failure condition after the last function call.</p> +<p> Just set this variable to an empty string to clear the error condition.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_mechanism"></a><li><a name="5.2.5">mechanism</a></li></h3> +<h3>Type</h3> +<p><tt><i>string</i></tt></p> +<h3>Default value</h3> +<p><tt>''</tt></p> +<h3>Purpose</h3> +<p>Store the name of the mechanism that was selected during the call to the <tt><a href="#function_Start">Start</a></tt> function.</p> +<h3>Usage</h3> +<p>You can access this variable but do not change it.</p> +<p><a href="#variables">Variables</a></p> +<h3><a name="variable_encode_response"></a><li><a name="5.2.6">encode_response</a></li></h3> +<h3>Type</h3> +<p><tt><i>bool</i></tt></p> +<h3>Default value</h3> +<p><tt>1</tt></p> +<h3>Purpose</h3> +<p>Let the drivers inform the applications whether responses need to be encoded.</p> +<h3>Usage</h3> +<p>Applications should check this variable before sending authentication responses to the server to determine if the responses need to be encoded, eventually with base64 algorithm.</p> +<p><a href="#variables">Variables</a></p> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> +<hr /> +<ul> +<h2><li><a name="functions"></a><a name="6.1.1">Functions</a></li></h2> +<ul> +<li><tt><a href="#function_SetCredential">SetCredential</a></tt></li><br /> +<li><tt><a href="#function_GetCredentials">GetCredentials</a></tt></li><br /> +<li><tt><a href="#function_Start">Start</a></tt></li><br /> +<li><tt><a href="#function_Step">Step</a></tt></li><br /> +<p><a href="#table_of_contents">Table of contents</a></p> +<h3><a name="function_SetCredential"></a><li><a name="7.2.5">SetCredential</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i></i> SetCredential(</tt><ul> +<tt><i>string</i> </tt><tt><a href="#argument_SetCredential_key">key</a></tt><tt>,</tt><br /> +<tt><i>string</i> </tt><tt><a href="#argument_SetCredential_value">value</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Store the value of a credential that may be used by any of the supported mechanisms to process the authentication messages and responses.</p> +<h3>Usage</h3> +<p>Call this function before starting the authentication dialog to pass all the credential values that be needed to use the type of authentication that the applications may need.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_SetCredential_key">key</a></b></tt> - Specify the name of the credential key.</p> +<p><tt><b><a name="argument_SetCredential_value">value</a></b></tt> - Specify the value for the credential.</p> +</ul> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_GetCredentials"></a><li><a name="9.2.6">GetCredentials</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>int</i> GetCredentials(</tt><ul> +<tt>(input and output) <i>array</i> </tt><tt><a href="#argument_GetCredentials_credentials">credentials</a></tt><tt>,</tt><br /> +<tt>(input and output) <i>array</i> </tt><tt><a href="#argument_GetCredentials_defaults">defaults</a></tt><tt>,</tt><br /> +<tt>(input and output) <i>array</i> </tt><tt><a href="#argument_GetCredentials_interactions">interactions</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Retrieve the values of one or more credentials to be used by the authentication mechanism classes.</p> +<h3>Usage</h3> +<p>This is meant to be used by authentication mechanism driver classes to retrieve the credentials that may be neede.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_GetCredentials_credentials">credentials</a></b></tt> - Reference to an associative array variable with all the credentials that are being requested. The function initializes this associative array values.</p> +<p><tt><b><a name="argument_GetCredentials_defaults">defaults</a></b></tt> - Associative arrays with default values for credentials that may have not been defined.</p> +<p><tt><b><a name="argument_GetCredentials_interactions">interactions</a></b></tt> - Not yet in use. It is meant to provide context information to retrieve credentials that may be obtained interacting with the user.</p> +</ul> +<h3>Return value</h3> +<p>The function may return <tt>SASL_CONTINUE</tt> if it succeeded, or <tt>SASL_NOMECH</tt> if it was not possible to retrieve one of the requested credentials.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_Start"></a><li><a name="11.2.7">Start</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>int</i> Start(</tt><ul> +<tt>(input and output) <i>array</i> </tt><tt><a href="#argument_Start_mechanisms">mechanisms</a></tt><tt>,</tt><br /> +<tt>(output) <i>string &</i> </tt><tt><a href="#argument_Start_message">message</a></tt><tt>,</tt><br /> +<tt>(input and output) <i>array</i> </tt><tt><a href="#argument_Start_interactions">interactions</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Process the initial authentication step initializing the driver class that implements the first of the list of requested mechanisms that is supported by this SASL client library implementation.</p> +<h3>Usage</h3> +<p>Call this function specifying a list of mechanisms that the server supports. If the <tt><a href="#argument_Start_message">message</a></tt> argument returns a string, it should be sent to the server as initial message. Check the <tt><a href="#variable_encode_response">encode_response</a></tt> variable to determine whether the initial message needs to be encoded, eventually with base64 algorithm, before it is sent to the server.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_Start_mechanisms">mechanisms</a></b></tt> - Define the list of names of authentication mechanisms supported by the that should be tried.</p> +<p><tt><b><a name="argument_Start_message">message</a></b></tt> - Return the initial message that should be sent to the server to start the authentication dialog. If this value is undefined, no message should be sent to the server.</p> +<p><tt><b><a name="argument_Start_interactions">interactions</a></b></tt> - Not yet in use. It is meant to provide context information to interact with the end user.</p> +</ul> +<h3>Return value</h3> +<p>The function may return <tt>SASL_CONTINUE</tt> if it could start one of the requested authentication mechanisms. It may return <tt>SASL_NOMECH</tt> if it was not possible to start any of the requested mechanisms. It returns <tt>SASL_FAIL</tt> or other value in case of error.</p> +<p><a href="#functions">Functions</a></p> +<h3><a name="function_Step"></a><li><a name="13.2.8">Step</a></li></h3> +<h3>Synopsis</h3> +<p><tt><i>int</i> Step(</tt><ul> +<tt><i>string</i> </tt><tt><a href="#argument_Step_response">response</a></tt><tt>,</tt><br /> +<tt>(output) <i>string &</i> </tt><tt><a href="#argument_Step_message">message</a></tt><tt>,</tt><br /> +<tt>(input and output) <i>array</i> </tt><tt><a href="#argument_Step_interactions">interactions</a></tt></ul> +<tt>)</tt></p> +<h3>Purpose</h3> +<p>Process the authentication steps after the initial step, until the authetication iteration dialog is complete.</p> +<h3>Usage</h3> +<p>Call this function iteratively after a successful initial step calling the <tt><a href="#function_Start">Start</a></tt> function.</p> +<h3>Arguments</h3> +<ul> +<p><tt><b><a name="argument_Step_response">response</a></b></tt> - Pass the response returned by the server to the previous step.</p> +<p><tt><b><a name="argument_Step_message">message</a></b></tt> - Return the message that should be sent to the server to continue the authentication dialog. If this value is undefined, no message should be sent to the server.</p> +<p><tt><b><a name="argument_Step_interactions">interactions</a></b></tt> - Not yet in use. It is meant to provide context information to interact with the end user.</p> +</ul> +<h3>Return value</h3> +<p>The function returns <tt>SASL_CONTINUE</tt> if step was processed successfully, or returns <tt>SASL_FAIL</tt> in case of error.</p> +<p><a href="#functions">Functions</a></p> +<p><a href="#table_of_contents">Table of contents</a></p> +</ul> +</ul> + +<hr /> +<address>Manuel Lemos (<a href="mailto:mlemos-at-acm.org">mlemos-at-acm.org</a>)</address> +</body> +</html> diff --git a/vendor/sasl/login_sasl_client.php b/vendor/sasl/login_sasl_client.php new file mode 100644 index 0000000..923d16e --- /dev/null +++ b/vendor/sasl/login_sasl_client.php @@ -0,0 +1,69 @@ +<?php +/* + * login_sasl_client.php + * + * @(#) $Id: login_sasl_client.php,v 1.2 2004/11/17 08:00:37 mlemos Exp $ + * + */ + +define("SASL_LOGIN_STATE_START", 0); +define("SASL_LOGIN_STATE_IDENTIFY_USER", 1); +define("SASL_LOGIN_STATE_IDENTIFY_PASSWORD", 2); +define("SASL_LOGIN_STATE_DONE", 3); + +class login_sasl_client_class +{ + var $credentials=array(); + var $state=SASL_LOGIN_STATE_START; + + Function Initialize(&$client) + { + return(1); + } + + Function Start(&$client, &$message, &$interactions) + { + if($this->state!=SASL_LOGIN_STATE_START) + { + $client->error="LOGIN authentication state is not at the start"; + return(SASL_FAIL); + } + $this->credentials=array( + "user"=>"", + "password"=>"", + "realm"=>"" + ); + $defaults=array( + "realm"=>"" + ); + $status=$client->GetCredentials($this->credentials,$defaults,$interactions); + if($status==SASL_CONTINUE) + $this->state=SASL_LOGIN_STATE_IDENTIFY_USER; + Unset($message); + return($status); + } + + Function Step(&$client, $response, &$message, &$interactions) + { + switch($this->state) + { + case SASL_LOGIN_STATE_IDENTIFY_USER: + $message=$this->credentials["user"].(strlen($this->credentials["realm"]) ? "@".$this->credentials["realm"] : ""); + $this->state=SASL_LOGIN_STATE_IDENTIFY_PASSWORD; + break; + case SASL_LOGIN_STATE_IDENTIFY_PASSWORD: + $message=$this->credentials["password"]; + $this->state=SASL_LOGIN_STATE_DONE; + break; + case SASL_LOGIN_STATE_DONE: + $client->error="LOGIN authentication was finished without success"; + break; + default: + $client->error="invalid LOGIN authentication step state"; + return(SASL_FAIL); + } + return(SASL_CONTINUE); + } +}; + +?>
\ No newline at end of file diff --git a/vendor/sasl/ntlm_sasl_client.php b/vendor/sasl/ntlm_sasl_client.php new file mode 100644 index 0000000..406edf2 --- /dev/null +++ b/vendor/sasl/ntlm_sasl_client.php @@ -0,0 +1,180 @@ +<?php +/* + * ntlm_sasl_client.php + * + * @(#) $Id: ntlm_sasl_client.php,v 1.3 2004/11/17 08:00:37 mlemos Exp $ + * + */ + +define("SASL_NTLM_STATE_START", 0); +define("SASL_NTLM_STATE_IDENTIFY_DOMAIN", 1); +define("SASL_NTLM_STATE_RESPOND_CHALLENGE", 2); +define("SASL_NTLM_STATE_DONE", 3); + +class ntlm_sasl_client_class +{ + var $credentials=array(); + var $state=SASL_NTLM_STATE_START; + + Function Initialize(&$client) + { + if(!function_exists($function="mcrypt_encrypt") + || !function_exists($function="mhash")) + { + $extensions=array( + "mcrypt_encrypt"=>"mcrypt", + "mhash"=>"mhash" + ); + $client->error="the extension ".$extensions[$function]." required by the NTLM SASL client class is not available in this PHP configuration"; + return(0); + } + return(1); + } + + Function ASCIIToUnicode($ascii) + { + for($unicode="",$a=0;$a<strlen($ascii);$a++) + $unicode.=substr($ascii,$a,1).chr(0); + return($unicode); + } + + Function TypeMsg1($domain,$workstation) + { + $domain_length=strlen($domain); + $workstation_length=strlen($workstation); + $workstation_offset=32; + $domain_offset=$workstation_offset+$workstation_length; + return( + "NTLMSSP\0". + "\x01\x00\x00\x00". + "\x07\x32\x00\x00". + pack("v",$domain_length). + pack("v",$domain_length). + pack("V",$domain_offset). + pack("v",$workstation_length). + pack("v",$workstation_length). + pack("V",$workstation_offset). + $workstation. + $domain + ); + } + + Function NTLMResponse($challenge,$password) + { + $unicode=$this->ASCIIToUnicode($password); + $md4=mhash(MHASH_MD4,$unicode); + $padded=$md4.str_repeat(chr(0),21-strlen($md4)); + $iv_size=mcrypt_get_iv_size(MCRYPT_DES,MCRYPT_MODE_ECB); + $iv=mcrypt_create_iv($iv_size,MCRYPT_RAND); + for($response="",$third=0;$third<21;$third+=7) + { + for($packed="",$p=$third;$p<$third+7;$p++) + $packed.=str_pad(decbin(ord(substr($padded,$p,1))),8,"0",STR_PAD_LEFT); + for($key="",$p=0;$p<strlen($packed);$p+=7) + { + $s=substr($packed,$p,7); + $b=$s.((substr_count($s,"1") % 2) ? "0" : "1"); + $key.=chr(bindec($b)); + } + $ciphertext=mcrypt_encrypt(MCRYPT_DES,$key,$challenge,MCRYPT_MODE_ECB,$iv); + $response.=$ciphertext; + } + return $response; + } + + Function TypeMsg3($ntlm_response,$user,$domain,$workstation) + { + $domain_unicode=$this->ASCIIToUnicode($domain); + $domain_length=strlen($domain_unicode); + $domain_offset=64; + $user_unicode=$this->ASCIIToUnicode($user); + $user_length=strlen($user_unicode); + $user_offset=$domain_offset+$domain_length; + $workstation_unicode=$this->ASCIIToUnicode($workstation); + $workstation_length=strlen($workstation_unicode); + $workstation_offset=$user_offset+$user_length; + $lm=""; + $lm_length=strlen($lm); + $lm_offset=$workstation_offset+$workstation_length; + $ntlm=$ntlm_response; + $ntlm_length=strlen($ntlm); + $ntlm_offset=$lm_offset+$lm_length; + $session=""; + $session_length=strlen($session); + $session_offset=$ntlm_offset+$ntlm_length; + return( + "NTLMSSP\0". + "\x03\x00\x00\x00". + pack("v",$lm_length). + pack("v",$lm_length). + pack("V",$lm_offset). + pack("v",$ntlm_length). + pack("v",$ntlm_length). + pack("V",$ntlm_offset). + pack("v",$domain_length). + pack("v",$domain_length). + pack("V",$domain_offset). + pack("v",$user_length). + pack("v",$user_length). + pack("V",$user_offset). + pack("v",$workstation_length). + pack("v",$workstation_length). + pack("V",$workstation_offset). + pack("v",$session_length). + pack("v",$session_length). + pack("V",$session_offset). + "\x01\x02\x00\x00". + $domain_unicode. + $user_unicode. + $workstation_unicode. + $lm. + $ntlm + ); + } + + Function Start(&$client, &$message, &$interactions) + { + if($this->state!=SASL_NTLM_STATE_START) + { + $client->error="NTLM authentication state is not at the start"; + return(SASL_FAIL); + } + $this->credentials=array( + "user"=>"", + "password"=>"", + "realm"=>"", + "workstation"=>"" + ); + $defaults=array(); + $status=$client->GetCredentials($this->credentials,$defaults,$interactions); + if($status==SASL_CONTINUE) + $this->state=SASL_NTLM_STATE_IDENTIFY_DOMAIN; + Unset($message); + return($status); + } + + Function Step(&$client, $response, &$message, &$interactions) + { + switch($this->state) + { + case SASL_NTLM_STATE_IDENTIFY_DOMAIN: + $message=$this->TypeMsg1($this->credentials["realm"],$this->credentials["workstation"]); + $this->state=SASL_NTLM_STATE_RESPOND_CHALLENGE; + break; + case SASL_NTLM_STATE_RESPOND_CHALLENGE: + $ntlm_response=$this->NTLMResponse(substr($response,24,8),$this->credentials["password"]); + $message=$this->TypeMsg3($ntlm_response,$this->credentials["user"],$this->credentials["realm"],$this->credentials["workstation"]); + $this->state=SASL_NTLM_STATE_DONE; + break; + case SASL_NTLM_STATE_DONE: + $client->error="NTLM authentication was finished without success"; + return(SASL_FAIL); + default: + $client->error="invalid NTLM authentication step state"; + return(SASL_FAIL); + } + return(SASL_CONTINUE); + } +}; + +?>
\ No newline at end of file diff --git a/vendor/sasl/plain_sasl_client.php b/vendor/sasl/plain_sasl_client.php new file mode 100644 index 0000000..c7feed0 --- /dev/null +++ b/vendor/sasl/plain_sasl_client.php @@ -0,0 +1,99 @@ +<?php +/* + * plain_sasl_client.php + * + * @(#) $Id: plain_sasl_client.php,v 1.2 2004/11/17 08:00:37 mlemos Exp $ + * + */ + +define("SASL_PLAIN_STATE_START", 0); +define("SASL_PLAIN_STATE_IDENTIFY", 1); +define("SASL_PLAIN_STATE_DONE", 2); + +define("SASL_PLAIN_DEFAULT_MODE", 0); +define("SASL_PLAIN_EXIM_MODE", 1); +define("SASL_PLAIN_EXIM_DOCUMENTATION_MODE", 2); + +class plain_sasl_client_class +{ + var $credentials=array(); + var $state=SASL_PLAIN_STATE_START; + + Function Initialize(&$client) + { + return(1); + } + + Function Start(&$client, &$message, &$interactions) + { + if($this->state!=SASL_PLAIN_STATE_START) + { + $client->error="PLAIN authentication state is not at the start"; + return(SASL_FAIL); + } + $this->credentials=array( + "user"=>"", + "password"=>"", + "realm"=>"", + "mode"=>"" + ); + $defaults=array( + "realm"=>"", + "mode"=>"" + ); + $status=$client->GetCredentials($this->credentials,$defaults,$interactions); + if($status==SASL_CONTINUE) + { + switch($this->credentials["mode"]) + { + case SASL_PLAIN_EXIM_MODE: + $message=$this->credentials["user"]."\0".$this->credentials["password"]."\0"; + break; + case SASL_PLAIN_EXIM_DOCUMENTATION_MODE: + $message="\0".$this->credentials["user"]."\0".$this->credentials["password"]; + break; + default: + $message=$this->credentials["user"]."\0".$this->credentials["user"].(strlen($this->credentials["realm"]) ? "@".$this->credentials["realm"] : "")."\0".$this->credentials["password"]; + break; + } + $this->state=SASL_PLAIN_STATE_DONE; + } + else + Unset($message); + return($status); + } + + Function Step(&$client, $response, &$message, &$interactions) + { + switch($this->state) + { +/* + case SASL_PLAIN_STATE_IDENTIFY: + switch($this->credentials["mode"]) + { + case SASL_PLAIN_EXIM_MODE: + $message=$this->credentials["user"]."\0".$this->credentials["password"]."\0"; + break; + case SASL_PLAIN_EXIM_DOCUMENTATION_MODE: + $message="\0".$this->credentials["user"]."\0".$this->credentials["password"]; + break; + default: + $message=$this->credentials["user"]."\0".$this->credentials["user"].(strlen($this->credentials["realm"]) ? "@".$this->credentials["realm"] : "")."\0".$this->credentials["password"]; + break; + } + var_dump($message); + $this->state=SASL_PLAIN_STATE_DONE; + break; +*/ + case SASL_PLAIN_STATE_DONE: + $client->error="PLAIN authentication was finished without success"; + return(SASL_FAIL); + default: + $client->error="invalid PLAIN authentication step state"; + return(SASL_FAIL); + } + return(SASL_CONTINUE); + } +}; + +?>
\ No newline at end of file diff --git a/vendor/sasl/sasl.php b/vendor/sasl/sasl.php new file mode 100644 index 0000000..7be020a --- /dev/null +++ b/vendor/sasl/sasl.php @@ -0,0 +1,422 @@ +<?php +/* + * sasl.php + * + * @(#) $Id: sasl.php,v 1.11 2005/10/31 18:43:27 mlemos Exp $ + * + */ + +define("SASL_INTERACT", 2); +define("SASL_CONTINUE", 1); +define("SASL_OK", 0); +define("SASL_FAIL", -1); +define("SASL_NOMECH", -4); + +class sasl_interact_class +{ + var $id; + var $challenge; + var $prompt; + var $default_result; + var $result; +}; + +/* +{metadocument}<?xml version="1.0" encoding="ISO-8859-1" ?> +<class> + + <package>net.manuellemos.sasl</package> + + <version>@(#) $Id: sasl.php,v 1.11 2005/10/31 18:43:27 mlemos Exp $</version> + <copyright>Copyright © (C) Manuel Lemos 2004</copyright> + <title>Simple Authentication and Security Layer client</title> + <author>Manuel Lemos</author> + <authoraddress>mlemos-at-acm.org</authoraddress> + + <documentation> + <idiom>en</idiom> + <purpose>Provide a common interface to plug-in driver classes that + implement different mechanisms for authentication used by clients of + standard protocols like SMTP, POP3, IMAP, HTTP, etc.. Currently the + supported authentication mechanisms are: <tt>PLAIN</tt>, + <tt>LOGIN</tt>, <tt>CRAM-MD5</tt>, <tt>Digest</tt> and <tt>NTML</tt> + (Windows or Samba).</purpose> + <usage>.</usage> + </documentation> + +{/metadocument} +*/ + +class sasl_client_class +{ + /* Public variables */ + +/* +{metadocument} + <variable> + <name>error</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Store the message that is returned when an error + occurs.</purpose> + <usage>Check this variable to understand what happened when a call to + any of the class functions has failed.<paragraphbreak /> + This class uses cumulative error handling. This means that if one + class functions that may fail is called and this variable was + already set to an error message due to a failure in a previous call + to the same or other function, the function will also fail and does + not do anything.<paragraphbreak /> + This allows programs using this class to safely call several + functions that may fail and only check the failure condition after + the last function call.<paragraphbreak /> + Just set this variable to an empty string to clear the error + condition.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $error=''; + +/* +{metadocument} + <variable> + <name>mechanism</name> + <type>STRING</type> + <value></value> + <documentation> + <purpose>Store the name of the mechanism that was selected during the + call to the <functionlink>Start</functionlink> function.</purpose> + <usage>You can access this variable but do not change it.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $mechanism=''; + +/* +{metadocument} + <variable> + <name>encode_response</name> + <type>BOOLEAN</type> + <value>1</value> + <documentation> + <purpose>Let the drivers inform the applications whether responses + need to be encoded.</purpose> + <usage>Applications should check this variable before sending + authentication responses to the server to determine if the + responses need to be encoded, eventually with base64 algorithm.</usage> + </documentation> + </variable> +{/metadocument} +*/ + var $encode_response=1; + + /* Private variables */ + + var $driver; + var $drivers=array( + "Digest" => array("digest_sasl_client_class", "digest_sasl_client.php" ), + "CRAM-MD5" => array("cram_md5_sasl_client_class", "cram_md5_sasl_client.php" ), + "LOGIN" => array("login_sasl_client_class", "login_sasl_client.php" ), + "NTLM" => array("ntlm_sasl_client_class", "ntlm_sasl_client.php" ), + "PLAIN" => array("plain_sasl_client_class", "plain_sasl_client.php" ), + "Basic" => array("basic_sasl_client_class", "basic_sasl_client.php" ) + ); + var $credentials=array(); + + /* Public functions */ + +/* +{metadocument} + <function> + <name>SetCredential</name> + <type>VOID</type> + <documentation> + <purpose>Store the value of a credential that may be used by any of + the supported mechanisms to process the authentication messages and + responses.</purpose> + <usage>Call this function before starting the authentication dialog + to pass all the credential values that be needed to use the type + of authentication that the applications may need.</usage> + <returnvalue>.</returnvalue> + </documentation> + <argument> + <name>key</name> + <type>STRING</type> + <documentation> + <purpose>Specify the name of the credential key.</purpose> + </documentation> + </argument> + <argument> + <name>value</name> + <type>STRING</type> + <documentation> + <purpose>Specify the value for the credential.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function SetCredential($key,$value) + { + $this->credentials[$key]=$value; + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>GetCredentials</name> + <type>INTEGER</type> + <documentation> + <purpose>Retrieve the values of one or more credentials to be used by + the authentication mechanism classes.</purpose> + <usage>This is meant to be used by authentication mechanism driver + classes to retrieve the credentials that may be neede.</usage> + <returnvalue>The function may return <tt>SASL_CONTINUE</tt> if it + succeeded, or <tt>SASL_NOMECH</tt> if it was not possible to + retrieve one of the requested credentials.</returnvalue> + </documentation> + <argument> + <name>credentials</name> + <type>HASH</type> + <documentation> + <purpose>Reference to an associative array variable with all the + credentials that are being requested. The function initializes + this associative array values.</purpose> + </documentation> + </argument> + <argument> + <name>defaults</name> + <type>HASH</type> + <documentation> + <purpose>Associative arrays with default values for credentials + that may have not been defined.</purpose> + </documentation> + </argument> + <argument> + <name>interactions</name> + <type>ARRAY</type> + <documentation> + <purpose>Not yet in use. It is meant to provide context + information to retrieve credentials that may be obtained + interacting with the user.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function GetCredentials(&$credentials,$defaults,&$interactions) + { + Reset($credentials); + $end=(GetType($key=Key($credentials))!="string"); + for(;!$end;) + { + if(!IsSet($this->credentials[$key])) + { + if(IsSet($defaults[$key])) + $credentials[$key]=$defaults[$key]; + else + { + $this->error="the requested credential ".$key." is not defined"; + return(SASL_NOMECH); + } + } + else + $credentials[$key]=$this->credentials[$key]; + Next($credentials); + $end=(GetType($key=Key($credentials))!="string"); + } + return(SASL_CONTINUE); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>Start</name> + <type>INTEGER</type> + <documentation> + <purpose>Process the initial authentication step initializing the + driver class that implements the first of the list of requested + mechanisms that is supported by this SASL client library + implementation.</purpose> + <usage>Call this function specifying a list of mechanisms that the + server supports. If the <argumentlink> + <argument>message</argument> + <function>Start</function> + </argumentlink> argument returns a string, it should be sent to + the server as initial message. Check the + <variablelink>encode_response</variablelink> variable to determine + whether the initial message needs to be encoded, eventually with + base64 algorithm, before it is sent to the server.</usage> + <returnvalue>The function may return <tt>SASL_CONTINUE</tt> if it + could start one of the requested authentication mechanisms. It + may return <tt>SASL_NOMECH</tt> if it was not possible to start + any of the requested mechanisms. It returns <tt>SASL_FAIL</tt> or + other value in case of error.</returnvalue> + </documentation> + <argument> + <name>mechanisms</name> + <type>ARRAY</type> + <inout /> + <documentation> + <purpose>Define the list of names of authentication mechanisms + supported by the that should be tried.</purpose> + </documentation> + </argument> + <argument> + <name>message</name> + <type>STRING</type> + <out /> + <documentation> + <purpose>Return the initial message that should be sent to the + server to start the authentication dialog. If this value is + undefined, no message should be sent to the server.</purpose> + </documentation> + </argument> + <argument> + <name>interactions</name> + <type>ARRAY</type> + <documentation> + <purpose>Not yet in use. It is meant to provide context + information to interact with the end user.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function Start($mechanisms, &$message, &$interactions) + { + if(strlen($this->error)) + return(SASL_FAIL); + if(IsSet($this->driver)) + return($this->driver->Start($this,$message,$interactions)); + $no_mechanism_error=""; + for($m=0;$m<count($mechanisms);$m++) + { + $mechanism=$mechanisms[$m]; + if(IsSet($this->drivers[$mechanism])) + { + if(!class_exists($this->drivers[$mechanism][0])) + require(dirname(__FILE__)."/".$this->drivers[$mechanism][1]); + $this->driver=new $this->drivers[$mechanism][0]; + if($this->driver->Initialize($this)) + { + $this->encode_response=1; + $status=$this->driver->Start($this,$message,$interactions); + switch($status) + { + case SASL_NOMECH: + Unset($this->driver); + if(strlen($no_mechanism_error)==0) + $no_mechanism_error=$this->error; + $this->error=""; + break; + case SASL_CONTINUE: + $this->mechanism=$mechanism; + return($status); + default: + Unset($this->driver); + $this->error=""; + return($status); + } + } + else + { + Unset($this->driver); + if(strlen($no_mechanism_error)==0) + $no_mechanism_error=$this->error; + $this->error=""; + } + } + } + $this->error=(strlen($no_mechanism_error) ? $no_mechanism_error : "it was not requested any of the authentication mechanisms that are supported"); + return(SASL_NOMECH); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +/* +{metadocument} + <function> + <name>Step</name> + <type>INTEGER</type> + <documentation> + <purpose>Process the authentication steps after the initial step, + until the authetication iteration dialog is complete.</purpose> + <usage>Call this function iteratively after a successful initial + step calling the <functionlink>Start</functionlink> function.</usage> + <returnvalue>The function returns <tt>SASL_CONTINUE</tt> if step was + processed successfully, or returns <tt>SASL_FAIL</tt> in case of + error.</returnvalue> + </documentation> + <argument> + <name>response</name> + <type>STRING</type> + <in /> + <documentation> + <purpose>Pass the response returned by the server to the previous + step.</purpose> + </documentation> + </argument> + <argument> + <name>message</name> + <type>STRING</type> + <out /> + <documentation> + <purpose>Return the message that should be sent to the server to + continue the authentication dialog. If this value is undefined, + no message should be sent to the server.</purpose> + </documentation> + </argument> + <argument> + <name>interactions</name> + <type>ARRAY</type> + <documentation> + <purpose>Not yet in use. It is meant to provide context + information to interact with the end user.</purpose> + </documentation> + </argument> + <do> +{/metadocument} +*/ + Function Step($response, &$message, &$interactions) + { + if(strlen($this->error)) + return(SASL_FAIL); + return($this->driver->Step($this,$response,$message,$interactions)); + } +/* +{metadocument} + </do> + </function> +{/metadocument} +*/ + +}; + +/* + +{metadocument} +</class> +{/metadocument} + +*/ + +?>
\ No newline at end of file diff --git a/vendor/studip_ws/dispatcher.php b/vendor/studip_ws/dispatcher.php new file mode 100644 index 0000000..d472a59 --- /dev/null +++ b/vendor/studip_ws/dispatcher.php @@ -0,0 +1,215 @@ +<?php + +/* + * dispatcher.php - <short-description> + * + * Copyright (C) 2006 - Marcus Lunzenauer <mlunzena@uos.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + + +/** + * <ClassDescription> + * + * @package studip + * @subpackage ws + * + * @abstract + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id: dispatcher.php 3888 2006-09-06 13:27:19Z mlunzena $ + */ + +class Studip_Ws_Dispatcher { + + + /** + * <FieldDescription> + * + * @access private + * @var array + */ + var $api_methods = array(); + + + /** + * Constructor. Give an unlimited number of services' class names as + * arguments. + * + * @param mixed $services,... an unlimited number or an array of class names + * of services to include + * + * @return void + */ + function __construct($services = array() /*, ... */) { + + if (!is_array($services)) + $services = func_get_args(); + + foreach ($services as $service_name) { + $this->add_service($service_name); + } + } + + + /** + * <MethodDescription> + * + * @param string <description> + * + * @return bool <description> + */ + function add_service($service_name) { + + if (!is_string($service_name)) { + trigger_error('Arguments must be strings.', E_USER_WARNING); + return FALSE; + } + + # not a service + if (!class_exists($service_name) || + !$this->is_a_service($service_name)) { + trigger_error(sprintf('Service "%s" does not exist.', $service_name), + E_USER_WARNING); + return FALSE; + } + + $service = new $service_name(); + + $api_methods = $service->get_api_methods(); + + foreach ($api_methods as $method_name => $method) { + if (isset($this->api_methods[$method_name])) { + trigger_error(sprintf('Method %s already defined.', $method_name), + E_USER_ERROR); + return FALSE; + } + + $this->api_methods[$method_name] =& $api_methods[$method_name]; + } + + return TRUE; + } + + /** + * This method is called to verify the existence of a mapped function. + * + * @param string the function's name + * + * @return boolean returns TRUE, if the dispatcher can invoke the given + * function, FALSE otherwise + */ + function responds_to($function) { + return isset($this->api_methods[(string)$function]); + } + + + /** + * This method is responsible to call the given function with the given + * arguments. + * + * @param string the name of the function to invoke + * @param array an array of arguments + * + * @return mixed the return value of the invoked function + */ + function &invoke($method0, $argument_array) { + + # find service that provides $method + if (!isset($this->api_methods[$method0])) + return $this->throw_exception('No service responds to "%s".', $method0); + + $service = $this->api_methods[$method0]->service; + + # calling before filter + $before = $service->before_filter($method0, $argument_array); + if ($before === FALSE || is_a($before, 'Studip_Ws_Fault')) { + $msg = $before ? $before->get_message() : 'before_filter activated.'; + $exception = $this->throw_exception($msg); + return $exception; + } + + $method = Studip_Ws_Dispatcher::map_function($method0); + + # call actual function + $result = call_user_func_array(array(&$service, $method), $argument_array); + + # calling after filter + $service->after_filter($method0, $argument_array, $result); + + if (is_a($result, 'Studip_Ws_Fault')) { + $exception = $this->throw_exception($result->get_message()); + return $exception; + } + + return $result; + } + + + /** + * Replacement for "x instanceof Studip_Ws_Service". + * + * @todo Should not this be elsewhere? + * + * @access private + * + * @param mixed a string or an object to get checked + * + * @return bool returns TRUE if the argument was a Studip_Ws_Service + */ + function is_a_service($class) { + + if (!is_string($class)) { + if (is_object($class)) { + $class = get_class($class); + } else { + trigger_error('Argument has to be a string or an object.', + E_USER_ERROR); + return FALSE; + } + } + + if (strcasecmp($class, 'Studip_Ws_Service') === 0) + return TRUE; + + if ($parent = get_parent_class($class)) + return Studip_Ws_Dispatcher::is_a_service($parent); + + return FALSE; + } + + + /** + * Maps a RPC operation name to it's real world function name. + * + * @access private + * + * @param string <description> + * + * @return string <description> + */ + function map_function($function) { + return $function . '_action'; + } + + + /** + * <MethodDescription> + * + * @access private + * + * @param type <description> + * + * @return type <description> + */ + function throw_exception($message/*, ...*/) { + $args = func_get_args(); + trigger_error(vsprintf(array_shift($args), $args), E_USER_ERROR); + return NULL; + } +} diff --git a/vendor/studip_ws/fault.php b/vendor/studip_ws/fault.php new file mode 100644 index 0000000..9e6fdeb --- /dev/null +++ b/vendor/studip_ws/fault.php @@ -0,0 +1,58 @@ +<?php + +/* + * fault.php - Abstraction of service's faults + * + * Copyright (C) 2006 - Marcus Lunzenauer <mlunzena@uos.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + + +/** + * Abstraction of service's faults + * + * @package studip + * @subpackage ws + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id: fault.php 3888 2006-09-06 13:27:19Z mlunzena $ + */ + +class Studip_Ws_Fault { + + + /** + * The fault's message. + * + * @access private + * @var string + */ + var $message; + + + /** + * Constructor. + * + * @param string the fault's message + * + * @return void + */ + function __construct($message) { + $this->message = (string) $message; + } + + + /** + * Returns the faults message. + * + * @return string <description> + */ + function get_message() { + return $this->message; + } +} diff --git a/vendor/studip_ws/jsonrpc_dispatcher.php b/vendor/studip_ws/jsonrpc_dispatcher.php new file mode 100644 index 0000000..91cddae --- /dev/null +++ b/vendor/studip_ws/jsonrpc_dispatcher.php @@ -0,0 +1,154 @@ +<?php + +/* + * jsonrpc_dispatcher.php - <short-description> + * + * Copyright (C) 2006 - Marcus Lunzenauer <mlunzena@uos.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + + +/** + * <ClassDescription> + * + * @package studip + * @subpackage ws + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id: jsonrpc_dispatcher.php 3888 2006-09-06 13:27:19Z mlunzena $ + */ + +class Studip_Ws_JsonrpcDispatcher extends Studip_Ws_Dispatcher { + + + /** + * <MethodDescription> + * + * @param type <description> + * + * @return type <description> + */ + function dispatch($msg = NULL) { + + # ensure correct invocation + if (is_null($msg) || !is_a($msg, 'jsonrpcmsg')) + return $this->throw_exception('functions_parameters_type must not be '. + 'phpvals.'); + + # get decoded parameters + $len = $msg->getNumParams(); + $argument_array = array(); + for ($i = 0; $i < $len; ++$i) + $argument_array[] = php_jsonrpc_decode($msg->getParam($i)); + + # return result + return new jsonrpcresp( + php_jsonrpc_encode($this->invoke($msg->method(), $argument_array))); + } + + + /** + * <MethodDescription> + * + * @param type <description> + * + * @return type <description> + */ + function throw_exception($message/*, ...*/) { + $args = func_get_args(); + return new jsonrpcresp(0, $GLOBALS['xmlrpcerruser'] + 1, + vsprintf(array_shift($args), $args)); + } + + + /** + * Class method that composes the dispatch map from the available methods. + * + * @return array This service's dispatch map. + * + */ + function get_dispatch_map() { + $dispatch_map = array(); + foreach ($this->api_methods as $method_name => $method) + $dispatch_map[$method_name] = $this->map_method($method); + return $dispatch_map; + } + + + /** + * <MethodDescription> + * + * @param type <description> + * + * @return type <description> + */ + function map_method($method) { + + # TODO validate method + + ## 1. function + $function = array(&$this, 'dispatch'); + + ## 2. signature + $signature = array(array()); + + # return value + $signature[0][] = $this->translate_type($method->returns); + + # arguments + foreach ($method->expects as $type) + $signature[0][] = $this->translate_type($type); + + ## 3. docstring + $docstring = $method->description; + + return compact('function', 'signature', 'docstring'); + } + + + /** + * <MethodDescription> + * + * @param type <description> + * + * @return type <description> + */ + function translate_type($type0) { + + switch ($type = Studip_Ws_Type::get_type($type0)) { + case STUDIP_WS_TYPE_INT: + return $GLOBALS['xmlrpcInt']; + + case STUDIP_WS_TYPE_STRING: + return $GLOBALS['xmlrpcString']; + + case STUDIP_WS_TYPE_BASE64: + return $GLOBALS['xmlrpcBase64']; + + case STUDIP_WS_TYPE_BOOL: + return $GLOBALS['xmlrpcBoolean']; + + case STUDIP_WS_TYPE_FLOAT: + return $GLOBALS['xmlrpcDouble']; + + case STUDIP_WS_TYPE_NULL: + return $GLOBALS['xmlrpcBoolean']; + + case STUDIP_WS_TYPE_ARRAY: + return $GLOBALS['xmlrpcArray']; + + case STUDIP_WS_TYPE_STRUCT: + return $GLOBALS['xmlrpcStruct']; + } + + trigger_error(sprintf('Type %s could not be found.', + var_export($type, TRUE)), + E_USER_ERROR); + return $GLOBALS['xmlrpcString']; + } +} diff --git a/vendor/studip_ws/method.php b/vendor/studip_ws/method.php new file mode 100644 index 0000000..3861764 --- /dev/null +++ b/vendor/studip_ws/method.php @@ -0,0 +1,106 @@ +<?php + +/* + * method.php - ADT for methods of services. + * + * Copyright (C) 2006 - Marcus Lunzenauer <mlunzena@uos.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + + +/** + * <ClassDescription> + * + * @package <package> + * @package <package> + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id: method.php 3888 2006-09-06 13:27:19Z mlunzena $ + */ + +class Studip_Ws_Method { + + + /** + * <FieldDescription> + * + * @access private + * @var <type> + */ + var $service; + + + /** + * <FieldDescription> + * + * @access private + * @var <type> + */ + var $name; + + + /** + * <FieldDescription> + * + * @access private + * @var <type> + */ + var $expects; + + + /** + * <FieldDescription> + * + * @access private + * @var <type> + */ + var $returns; + + + /** + * <FieldDescription> + * + * @access private + * @var <type> + */ + var $description; + + + /** + * <MethodDescription> + * + * @param type <description> + * + * @return type <description> + */ + function __construct(&$service, $name, + $expects = NULL, + $returns = NULL, + $description = '') { + + # check $expects + if (is_null($expects)) + $expects = array(); + else if (!is_array($expects)) { + trigger_error('Third argument is expected to be an array.', + E_USER_ERROR); + return; + } + + $this->service =& $service; + $this->name = $name; + $this->description = (string) $description; + $this->expects = $expects; + $this->returns = $returns; + + foreach ($this->expects as $key => $entry) + $this->expects[$key] = Studip_Ws_Type::translate($entry); + + $this->returns = Studip_Ws_Type::translate($this->returns); + } +} diff --git a/vendor/studip_ws/service.php b/vendor/studip_ws/service.php new file mode 100644 index 0000000..ec90257 --- /dev/null +++ b/vendor/studip_ws/service.php @@ -0,0 +1,106 @@ +<?php + +/* + * service.php - Abstract super class of all Stud.IP webservices. + * + * Copyright (C) 2006 - Marcus Lunzenauer <mlunzena@uos.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + + +/** + * This class is the abstract superclass of all available Stud.IP webservices. + * You have to extend it when implementing your own webservice. + * + * @package studip + * @subpackage ws + * + * @abstract + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id: service.php 3888 2006-09-06 13:27:19Z mlunzena $ + */ + +class Studip_Ws_Service { + + + /** + * <FieldDescription> + * + * @access private + * @var array + */ + var $api_methods = array(); + + + /** + * This method is called before every service method. + * + * @param string the function's name. + * @param array an array of arguments that will be delivered to the function. + * + * @return mixed if this method returns a "Studip_Ws_Fault" or "FALSE", + * further processing will be aborted and a "Studip_Ws_Fault" + * delivered. + */ + function before_filter(&$name, &$args) { + } + + + /** + * This method is called after every service method. You may modify the + * result of that call. This way you can easily implement filters. + * + * @param string the function's name. + * @param array an array of arguments that were delivered to the function. + * @param mixed the result of the last service method call. + * + * @return void + */ + function after_filter(&$name, &$args, &$result) { + } + + + /** + * <MethodDescription> + * + * @access protected + * + * @param string the methods name + * @param array <description> + * @param mixed <description> + * @param string the description of the method + * + * @return void + */ + function add_api_method($name, $expects = NULL, $returns = NULL, + $description = NULL) { + + # check $name + if (!method_exists($this, $name . '_action')) + trigger_error(sprintf('No such method exists: %s.', $name), E_USER_ERROR); + + if (isset($this->api_methods[$name])) { + trigger_error(sprintf('Method %s already added.', $name), E_USER_ERROR); + return NULL; + } + + return $this->api_methods[$name] = + new Studip_Ws_Method($this, $name, $expects, $returns, $description); + } + + + /** + * Returns the defined API methods of this service. + * + * @return array the API methods + */ + function &get_api_methods() { + return $this->api_methods; + } +} diff --git a/vendor/studip_ws/soap_dispatcher.php b/vendor/studip_ws/soap_dispatcher.php new file mode 100644 index 0000000..1b490cb --- /dev/null +++ b/vendor/studip_ws/soap_dispatcher.php @@ -0,0 +1,239 @@ +<?php + +/* + * soap_dispatcher.php - Delegate for Stud.IP SOAP Server. + * + * Copyright (C) 2006 - Marcus Lunzenauer <mlunzena@uos.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + + +/** + * <ClassDescription> + * + * @package studip + * @subpackage ws + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id: soap_dispatcher.php 3888 2006-09-06 13:27:19Z mlunzena $ + */ + +class Studip_Ws_SoapDispatcher extends Studip_Ws_Dispatcher + /* implements SoapServerDelegate */ { + + + /** + * <FieldDescription> + * + * @access private + * @var array + */ + var $types = array(); + + + /** + * <MethodDescription> + * + * @param type <description> + * + * @return type <description> + */ + function throw_exception($message/*, ...*/) { + $args = func_get_args(); + return new soap_fault('Client', '', vsprintf(array_shift($args), $args)); + } + + + /** + * <MethodDescription> + * + * @param type <description> + * + * @return type <description> + */ + function register_operations(&$server) { + + $namespace = $server->wsdl->schemaTargetNamespace; + + # 1st pass: register operations + foreach ($this->api_methods as $method) { + + # return value + $this->store_type($method->returns); + $returns = array('returns' => $this->type_to_name_wns($method->returns)); + + # arguments + $expects = array(); + foreach ($method->expects as $name => $argument) { + $expects['param'.$name] = + $this->type_to_name_wns($method->expects[$name]); + $this->store_type($method->expects[$name]); + } + + $server->register($method->name, + $expects, + $returns, + $namespace, + $namespace . '#' . $method->name, + 'rpc', + 'encoded', + $method->description); + } + + + # recursively find all types + foreach ($this->types as $key => $type) + $this->store_type_recursive($this->types[$key]); + + # 2nd pass: register types + foreach ($this->types as $type) { + + $type_class = Studip_Ws_Type::get_type($type); + + if ($type_class === STUDIP_WS_TYPE_ARRAY) { + $name = $this->type_to_name($type); + $element_type = Studip_Ws_Type::get_element_type($type); + $element_name = $this->type_to_name_wns($element_type); + $server->wsdl->addComplexType($name, + 'complexType', + 'array', + '', + 'SOAP-ENC:Array', + array(), + array(array( + 'ref' => 'SOAP-ENC:arrayType', + 'wsdl:arrayType' => $element_name . '[]')), + $element_name); + } + + else if ($type_class === STUDIP_WS_TYPE_STRUCT) { + + $elements = array(); + $name = Studip_Ws_Type::get_element_type($type); + foreach (Studip_Ws_Type::get_struct_elements($name) as $element) { + $elements[$element->name] = array( + 'name' => $element->name, + 'type' => $this->type_to_name_wns($element->type), + 'minOccurs' => 0); + } + + $server->wsdl->addComplexType($name, + 'complexType', + 'struct', + 'all', + '', + $elements); + } + } + } + + + /** + * <MethodDescription> + * + * @param mixed <description> + * + * @return string <description> + */ + function type_to_name_wns(&$type) { + return sprintf('%s:%s', + Studip_Ws_Type::is_complex_type($type) ? 'tns' : 'xsd', + $this->type_to_name($type)); + } + + + /** + * <MethodDescription> + * + * @param mixed <description> + * + * @return string <description> + */ + function type_to_name(&$type) { + + $type_class = Studip_Ws_Type::get_type($type); + + if ($type_class === STUDIP_WS_TYPE_ARRAY) { + for ($name = '', $element_type = $type; + Studip_Ws_Type::get_type($element_type) === STUDIP_WS_TYPE_ARRAY; + $element_type = Studip_Ws_Type::get_element_type($element_type)) { + $name .= 'Array'; + } + return $this->type_to_name($element_type) . $name; + } + + else if ($type_class === STUDIP_WS_TYPE_STRUCT) { + return Studip_Ws_Type::get_element_type($type); + } + + else { + + $mapping = array( + STUDIP_WS_TYPE_INT => 'int', + STUDIP_WS_TYPE_STRING => 'string', + STUDIP_WS_TYPE_BASE64 => 'base64', + STUDIP_WS_TYPE_BOOL => 'boolean', + STUDIP_WS_TYPE_FLOAT => 'double', + STUDIP_WS_TYPE_NULL => 'boolean'); + + if (isset($mapping[$type_class])) + return $mapping[$type_class]; + } + + trigger_error(sprintf('Type not known: %s', var_export($type, TRUE)), + E_USER_ERROR); + return 'any'; + } + + + /** + * <MethodDescription> + * + * @param mixed <description> + * + * @return boolean <description> + */ + function store_type(&$type) { + if (isset($this->types[$name = $this->type_to_name_wns($type)])) + return FALSE; + + if (Studip_Ws_Type::is_primitive_type($type)) + return FALSE; + + $this->types[$name] =& $type; + return TRUE; + } + + + /** + * <MethodDescription> + * + * @param mixed <description> + * + * @return void + */ + function store_type_recursive(&$type) { + + + $type_class = Studip_Ws_Type::get_type($type); + + if ($type_class === STUDIP_WS_TYPE_ARRAY) { + $element_type =& Studip_Ws_Type::get_element_type($type); + $this->store_type($element_type); + $this->store_type_recursive($element_type); + } + + else if ($type_class === STUDIP_WS_TYPE_STRUCT) { + $struct =& Studip_Ws_Type::get_element_type($type); + foreach (Studip_Ws_Type::get_struct_elements($struct) as $element) { + if ($this->store_type($element->type)) + $this->store_type_recursive($element->type); + } + } + } +} diff --git a/vendor/studip_ws/struct.php b/vendor/studip_ws/struct.php new file mode 100644 index 0000000..9b0873d --- /dev/null +++ b/vendor/studip_ws/struct.php @@ -0,0 +1,177 @@ +<?php + +/* + * struct.php - Abstraction for complex type. + * + * Copyright (C) 2006 - Marcus Lunzenauer <mlunzena@uos.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + + +/** + * Abstraction for complex type + * + * @package studip + * @subpackage ws + * + * @abstract + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id: struct.php 3888 2006-09-06 13:27:19Z mlunzena $ + */ + +class Studip_Ws_Struct { + + + /** + * Class-level constructor. Initialize your struct within using + * ClassName::add_element('name', 'type', $options) + * + * @abstract + * + * @return void + */ + function init() { + } + + + /** + * <MethodDescription> + * + * @access protected + * + * @param string <description> + * @param mixed <description> + * @param array <description> + * + * @return type <description> + */ + function add_element($name = NULL, $type = NULL, $options = array()) { + + # static var setup + static $elements; + if (is_null($elements)) + $elements = array(); + + # setter functionality + if (!is_null($name)) { + + + # no doublets + if (isset($elements[$name])) { + trigger_error(sprintf('Element %s already defined.', $name), + E_USER_ERROR); + return; + } + + # store it + $elements[$name] = new Studip_Ws_StructElement($name, $type, $options); + + return; + } + + # getter functionality + return $elements; + } + + + /** + * <MethodDescription> + * + * @param string <description> + * + * @return array <description> + */ + function &get_struct_elements($class = NULL) { + + static $once; + + # call 'init' once + if (is_null($once)) { + + # guess class name if not given (does not work in PHP5 anymore?!) + if (is_null($class)) { + $backtrace = debug_backtrace(); + $class = $backtrace[0]['class']; + } + + # call "class" constructor + call_user_func(array($class, 'init')); + + $once = call_user_func(array($class, 'add_element')); + } + + return $once; + } + + function __toString() { + return get_class($this); + } +} + + +/** + * Abstraction for complex type elements. + * + * @package studip + * @subpackage ws + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id: struct.php 3888 2006-09-06 13:27:19Z mlunzena $ + */ + +class Studip_Ws_StructElement { + + + /** + * The name of the element. + * + * @access private + * @var string + */ + var $name; + + + /** + * The type of the element. + * + * @access private + * @var mixed + */ + var $type; + + + /** + * Options for the element. + * + * @access private + * @var array + */ + var $options; + + + /** + * Constructor. + * + * @param string the name of the element. + * @param mixed the type of the element. + * @param array options for the element. + * + * @return void + */ + function __construct($name, $type, $options = array()) { + $this->name = (string) $name; + $this->type = Studip_Ws_Type::translate($type); + $this->options = $options; + } + + function __toString() { + return get_class($this); + } +} diff --git a/vendor/studip_ws/studip_ws.php b/vendor/studip_ws/studip_ws.php new file mode 100644 index 0000000..e40b695 --- /dev/null +++ b/vendor/studip_ws/studip_ws.php @@ -0,0 +1,20 @@ +<?php + +/* + * studip_ws.php - <short-description> + * + * Copyright (C) 2006 - Marcus Lunzenauer <mlunzena@uos.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + + +require_once 'dispatcher.php'; +require_once 'fault.php'; +require_once 'method.php'; +require_once 'service.php'; +require_once 'struct.php'; +require_once 'type.php'; diff --git a/vendor/studip_ws/type.php b/vendor/studip_ws/type.php new file mode 100644 index 0000000..0085e4f --- /dev/null +++ b/vendor/studip_ws/type.php @@ -0,0 +1,214 @@ +<?php + +/* + * type.php - <short-description> + * + * Copyright (C) 2006 - Marcus Lunzenauer <mlunzena@uos.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + + +define('STUDIP_WS_TYPE_INT', 'int'); +define('STUDIP_WS_TYPE_STRING', 'string'); +define('STUDIP_WS_TYPE_BASE64', 'base64'); +define('STUDIP_WS_TYPE_BOOL', 'bool'); +define('STUDIP_WS_TYPE_FLOAT', 'float'); +define('STUDIP_WS_TYPE_ARRAY', 'array'); +define('STUDIP_WS_TYPE_STRUCT', 'struct'); +define('STUDIP_WS_TYPE_NULL', 'null'); + + +/** + * <ClassDescription> + * + * @package studip + * @subpackage ws + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id: type.php 3888 2006-09-06 13:27:19Z mlunzena $ + */ + +class Studip_Ws_Type { + + + /** + * <MethodDescription> + * + * @param mixed <description> + * + * @return mixed <description> + */ + public static function translate($type) { + + # complex types + if (is_string($type) && class_exists($type)) + return array(STUDIP_WS_TYPE_STRUCT => $type); + + # array types + if (is_array($type)) { + if (!sizeof($type)) { + trigger_error('Array of missing type.', E_USER_ERROR); + return array(STUDIP_WS_TYPE_NULL => NULL); + } + + $element_type = current($type); + if (is_null($element_type)) { + trigger_error(sprintf('Array of unknown type: %s', + var_export($element_type, TRUE)), + E_USER_ERROR); + return array(STUDIP_WS_TYPE_NULL => NULL); + } + + return array(STUDIP_WS_TYPE_ARRAY => + Studip_Ws_Type::translate($element_type)); + } + + # basic types + if (is_string($type)) + switch ($type) { + + case 'int': + case 'integer': + return array(STUDIP_WS_TYPE_INT => NULL); + + case 'string': + case 'text': + return array(STUDIP_WS_TYPE_STRING => NULL); + + case 'base64': + return array(STUDIP_WS_TYPE_BASE64 => NULL); + + case 'bool': + case 'boolean': + return array(STUDIP_WS_TYPE_BOOL => NULL); + + case 'float': + case 'double': + return array(STUDIP_WS_TYPE_FLOAT => NULL); + + case 'null': + return array(STUDIP_WS_TYPE_NULL => NULL); + } + + # type by example + $type_checkers = array( + 'is_bool' => STUDIP_WS_TYPE_BOOL, + 'is_float' => STUDIP_WS_TYPE_FLOAT, + 'is_int' => STUDIP_WS_TYPE_INT, + 'is_string' => STUDIP_WS_TYPE_STRING, + 'is_null' => STUDIP_WS_TYPE_NULL, + ); + foreach ($type_checkers as $function => $replacement) + if ($function($type)) + return array($replacement => NULL); + + trigger_error('"' . gettype($type) . '" is not a valid type.'); + return array(STUDIP_WS_TYPE_NULL => NULL); + } + + + /** + * <MethodDescription> + * + * @param mixed <description> + * + * @return string <description> + * + * @todo name ist falsch + */ + public static function get_type($type) { + + if (is_array($type)) + return key($type); + + trigger_error(sprintf('$type has to be an array, but is: "%s"', + var_export($type, TRUE)), + E_USER_ERROR); + return STUDIP_WS_TYPE_NULL; + } + + + /** + * <MethodDescription> + * + * @param mixed <description> + * + * @return mixed <description> + * + * @todo name ist falsch + */ + function get_element_type($type) { + if (is_array($type)) + return current($type); + trigger_error(sprintf('\$type has to be an array, but is: "%s"', + var_export($type, TRUE)), + E_USER_ERROR); + return STUDIP_WS_TYPE_NULL; + } + + + /** + * <MethodDescription> + * + * @param type <description> + * + * @return type <description> + */ + function is_complex_type($type0) { + $type = Studip_Ws_Type::get_type($type0); + return $type === STUDIP_WS_TYPE_ARRAY || $type === STUDIP_WS_TYPE_STRUCT; + } + + + /** + * <MethodDescription> + * + * @param type <description> + * + * @return type <description> + */ + function is_primitive_type($type) { + return !Studip_Ws_Type::is_complex_type($type); + } + + + /** + * <MethodDescription> + * + * @param type <description> + * + * @return type <description> + */ + function get_struct_elements($struct) { + + # check argument; has to be a class + if (!class_exists($struct)) { + trigger_error(sprintf('Class definition missing: "%s"', $struct), + E_USER_ERROR); + return NULL; + } + + $result = array(); + + # either a struct or a duck typing struct + # (= responds to 'get_struct_elements') + if (is_callable(array($struct, 'get_struct_elements'))) { + $result = call_user_func(array($struct, 'get_struct_elements'), $struct); + } + + # just a class + else { + foreach (get_class_vars($struct) as $var_name => $var_value) { + $result[] = new Studip_Ws_StructElement($var_name, + STUDIP_WS_TYPE_STRING); + } + } + + return $result; + } +} diff --git a/vendor/studip_ws/xmlrpc_dispatcher.php b/vendor/studip_ws/xmlrpc_dispatcher.php new file mode 100644 index 0000000..396543c --- /dev/null +++ b/vendor/studip_ws/xmlrpc_dispatcher.php @@ -0,0 +1,164 @@ +<?php + +/* + * xmlrpc_dispatcher.php - <short-description> + * + * Copyright (C) 2006 - Marcus Lunzenauer <mlunzena@uos.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + + +/** + * <ClassDescription> + * + * @package studip + * @subpackage ws + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id: xmlrpc_dispatcher.php 3888 2006-09-06 13:27:19Z mlunzena $ + */ + +class Studip_Ws_XmlrpcDispatcher extends Studip_Ws_Dispatcher { + + + /** + * <MethodDescription> + * + * @param string <description> + * + * @return mixed <description> + */ + function dispatch($msg = NULL) { + + # ensure correct invocation + if (is_null($msg) || !is_a($msg, 'xmlrpcmsg')) + return $this->throw_exception('functions_parameters_type must not be '. + 'phpvals.'); + + # get decoded parameters + $len = $msg->getNumParams(); + $argument_array = array(); + for ($i = 0; $i < $len; ++$i) + $argument_array[] = php_xmlrpc_decode($msg->getParam($i)); + + # return result + return new xmlrpcresp( + php_xmlrpc_encode($this->invoke($msg->method(), $argument_array))); + } + + + /** + * <MethodDescription> + * + * @param string <description> + * + * @return mixed <description> + */ + function throw_exception($message/*, ...*/) { + $args = func_get_args(); + return new xmlrpcresp(0, $GLOBALS['xmlrpcerruser'] + 1, + vsprintf(array_shift($args), $args)); + } + + + /** + * Class method that composes the dispatch map from the available methods. + * + * @return array This service's dispatch map. + * + */ + function get_dispatch_map() { + $dispatch_map = array(); + foreach ($this->api_methods as $method_name => $method) + if ($mapped = $this->map_method($method)) { + $dispatch_map[$method_name] = $mapped; + } + return $dispatch_map; + } + + + /** + * <MethodDescription> + * + * @param mixed <description> + * + * @return array <description> + */ + function map_method($method) { + + # TODO validate method + try { + $reflection = new ReflectionMethod($method->service, "{$method->name}_action"); + $parameters = $reflection->getParameters(); + } catch (Exception $e) { + return false; + } + + ## 1. function + $function = array(&$this, 'dispatch'); + + ## 2. signature + $signature = [[]]; + $signature_docs = [['']]; + + # return value + $signature[0][] = $this->translate_type($method->returns); + + # arguments + foreach ($method->expects as $index => $type) { + $signature[0][] = $this->translate_type($type); + $signature_docs[0][] = isset($parameters[$index]) ? $parameters[$index]->getName() : ''; + } + + ## 3. docstring + $docstring = $method->description; + + return compact('function', 'signature', 'docstring', 'signature_docs'); + } + + + /** + * <MethodDescription> + * + * @param mixed <description> + * + * @return mixed <description> + */ + function translate_type($type0) { + switch ($type = Studip_Ws_Type::get_type($type0)) { + case STUDIP_WS_TYPE_INT: + return PhpXmlRpc\Value::$xmlrpcInt; + + case STUDIP_WS_TYPE_STRING: + return PhpXmlRpc\Value::$xmlrpcString; + + case STUDIP_WS_TYPE_BASE64: + return PhpXmlRpc\Value::$xmlrpcBase64; + + case STUDIP_WS_TYPE_BOOL: + return PhpXmlRpc\Value::$xmlrpcBoolean; + + case STUDIP_WS_TYPE_FLOAT: + return PhpXmlRpc\Value::$xmlrpcDouble; + + case STUDIP_WS_TYPE_NULL: + return PhpXmlRpc\Value::$xmlrpcNull; + + case STUDIP_WS_TYPE_ARRAY: + return PhpXmlRpc\Value::$xmlrpcArray; + + case STUDIP_WS_TYPE_STRUCT: + return PhpXmlRpc\Value::$xmlrpcStruct; + } + + trigger_error(sprintf('Type %s could not be found.', + var_export($type, TRUE)), + E_USER_ERROR); + return PhpXmlRpc\Value::$xmlrpcString; + } +} diff --git a/vendor/trails/extras/tramp.php b/vendor/trails/extras/tramp.php new file mode 100644 index 0000000..9292a96 --- /dev/null +++ b/vendor/trails/extras/tramp.php @@ -0,0 +1,55 @@ +<?php + +# Copyright (c) 2007 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +class Tramp_Controller extends Trails_Controller { + + /** + * Extracts action and args from a string. + * + * @param string the processed string + * + * @return arraye an array with two elements - a string containing the + * action and an array of strings representing the args + */ + protected function extract_action_and_args($string) { + return array($this->get_verb(), explode('/', $string)); + } + + + protected function map_action($action) { + return strtolower($action); + } + + + protected function get_verb() { + + $verb = strtoupper(isset($_REQUEST['_method']) + ? $_REQUEST['_method'] : @$_SERVER['REQUEST_METHOD']); + + if (!preg_match('/^DELETE|GET|POST|PUT|HEAD|OPTIONS$/', $verb)) { + throw new Trails_Exception(405); + } + + return $verb; + } +} diff --git a/vendor/trails/src/HEADER.php b/vendor/trails/src/HEADER.php new file mode 100644 index 0000000..75bb516 --- /dev/null +++ b/vendor/trails/src/HEADER.php @@ -0,0 +1,27 @@ +<? + +# Copyright (c) 2007 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +/** + * The version of the trails library. + */ +define('TRAILS_VERSION', '0.6.9'); diff --git a/vendor/trails/src/controller.php b/vendor/trails/src/controller.php new file mode 100644 index 0000000..c8e7b8e --- /dev/null +++ b/vendor/trails/src/controller.php @@ -0,0 +1,438 @@ +<?php + +/** + * A Trails_Controller is responsible for matching the unconsumed part of an URI + * to an action using the left over words as arguments for that action. The + * action is then mapped to method of the controller instance which is called + * with the just mentioned arguments. That method can send the #render_action, + * #render_template, #render_text, #render_nothing or #redirect method. + * Otherwise the #render_action is called with the current action as argument. + * If the action method sets instance variables during performing, they will be + * be used as attributes for the flexi-template opened by #render_action or + * #render_template. A controller's response's body is populated with the output + * of the #render_* methods. The action methods can add additional headers or + * change the status of that response. + * + * @package trails + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id: trails.php 7001 2008-04-04 11:20:27Z mlunzena $ + */ + +class Trails_Controller { + + + /** + * @ignore + */ + protected + $dispatcher, + $response, + $performed, + $layout; + + + /** + * Constructor. + * + * @param mixed the dispatcher who creates this instance + * + * @return void + */ + function __construct($dispatcher) { + $this->dispatcher = $dispatcher; + $this->erase_response(); + } + + + /** + * Resets the response of the controller + * + * @return void + */ + function erase_response() { + $this->performed = FALSE; + $this->response = new Trails_Response(); + } + + + /** + * Return this controller's response + * + * @return mixed the controller's response + */ + function get_response() { + return $this->response; + } + + + /** + * This method extracts an action string and further arguments from it's + * parameter. The action string is mapped to a method being called afterwards + * using the said arguments. That method is called and a response object is + * generated, populated and sent back to the dispatcher. + * + * @param type <description> + * + * @return type <description> + */ + function perform($unconsumed) { + + list($action, $args, $format) = $this->extract_action_and_args($unconsumed); + + $this->format = isset($format) ? $format : 'html'; + + $before_filter_result = $this->before_filter($action, $args); + + # send action to controller + # TODO (mlunzena) shouldn't the after filter be triggered too? + if (!(FALSE === $before_filter_result || $this->performed)) { + + $callable = $this->map_action($action); + + if (is_callable($callable)) { + call_user_func_array($callable, $args); + } + else { + $this->does_not_understand($action, $args); + } + + if (!$this->performed) { + $this->render_action($action); + } + + $this->after_filter($action, $args); + } + + return $this->response; + } + + + /** + * Extracts action and args from a string. + * + * @param string the processed string + * + * @return array an array with two elements - a string containing the + * action and an array of strings representing the args + */ + function extract_action_and_args($string) { + + if ('' === $string) { + return $this->default_action_and_args(); + } + + // find optional file extension + $format = NULL; + if (preg_match('/^(.*[^\/.])\.(\w+)$/', $string, $matches)) { + list($_, $string, $format) = $matches; + } + + // TODO this should possibly remove empty tokens + $args = explode('/', $string); + $action = array_shift($args); + return array($action, $args, $format); + } + + /** + * Return the default action and arguments + * + * @return an array containing the action, an array of args and the format + * + */ + function default_action_and_args() { + return array('index', array(), NULL); + } + + /** + * Maps the action to an actual method name. + * + * @param string the action + * + * @return string the mapped method name + */ + function map_action($action) { + return array(&$this, $action . '_action'); + } + + + /** + * Callback function being called before an action is executed. If this + * function does not return FALSE, the action will be called, otherwise + * an error will be generated and processing will be aborted. If this function + * already #rendered or #redirected, further processing of the action is + * withheld. + * + * @param string Name of the action to perform. + * @param array An array of arguments to the action. + * + * @return bool + */ + function before_filter(&$action, &$args) { + } + + + /** + * Callback function being called after an action is executed. + * + * @param string Name of the action to perform. + * @param array An array of arguments to the action. + * + * @return void + */ + function after_filter($action, $args) { + } + + + /** + * <MethodDescription> + * + * @param type <description> + * @param type <description> + * + * @return void + */ + function does_not_understand($action, $args) { + throw new Trails_UnknownAction("No action responded to '$action'."); + } + + + /** + * <MethodDescription> + * + * @param string <description> + * + * @return void + */ + function redirect($to) { + + if ($this->performed) { + throw new Trails_DoubleRenderError(); + } + + $this->performed = TRUE; + + # get uri; keep absolute URIs + $url = preg_match('#^(/|\w+://)#', $to) + ? $to + : $this->url_for($to); + + $this->response->add_header('Location', $url)->set_status(302); + } + + + /** + * Renders the given text as the body of the response. + * + * @param string the text to be rendered + * + * @return void + */ + function render_text($text = ' ') { + + if ($this->performed) { + throw new Trails_DoubleRenderError(); + } + + $this->performed = TRUE; + + $this->response->set_body($text); + } + + + /** + * Renders the empty string as the response's body. + * + * @return void + */ + function render_nothing() { + $this->render_text(''); + } + + + /** + * Renders the template of the given action as the response's body. + * + * @param string the action + * + * @return void + */ + function render_action($action) { + $this->render_template($this->get_default_template($action), $this->layout); + } + + + function get_default_template($action) + { + $class = get_class($this); + $controller_name = + Trails_Inflector::underscore(substr($class, 0, -10)); + return $controller_name.'/'.$action; + } + + + /** + * Renders a template using an optional layout template. + * + * @param mixed a flexi template + * @param mixes a flexi template which is used as layout + * + * @return void + */ + function render_template($template_name, $layout = NULL) { + + # open template + $factory = $this->get_template_factory(); + $template = $factory->open($template_name); + + # template requires setup ? + switch (get_class($template)) { + case 'Flexi_JsTemplate': + $this->set_content_type('text/javascript'); + break; + } + + $template->set_attributes($this->get_assigned_variables()); + + if (isset($layout)) { + $template->set_layout($layout); + } + + $this->render_text($template->render()); + } + + + /** + * Create and return a template factory for this controller. + * + * @return a Flexi_TemplateFactory + */ + function get_template_factory() { + return new Flexi_TemplateFactory($this->dispatcher->trails_root . + '/views/'); + } + + + /** + * This method returns all the set instance variables to be used as attributes + * for a template. This controller is returned too as value for + * key 'controller'. + * + * @return array an associative array of variables for the template + */ + function get_assigned_variables() { + + $assigns = array(); + $protected = get_class_vars(get_class($this)); + + foreach (get_object_vars($this) as $var => $value) { + if (!array_key_exists($var, $protected)) { + $assigns[$var] =& $this->$var; + } + } + + $assigns['controller'] = $this; + + return $assigns; + } + + + /** + * Sets the layout to be used by this controller per default. + * + * @param mixed a flexi template to be used as layout + * + * @return void + */ + function set_layout($layout) { + $this->layout = $layout; + } + + + /** + * Returns a URL to a specified route to your Trails application. + * + * Example: + * Your Trails application is located at 'http://example.com/dispatch.php'. + * So your dispatcher's trails_uri is set to 'http://example.com/dispatch.php' + * If you want the URL to your 'wiki' controller with action 'show' and + * parameter 'page' you should send: + * + * $url = $controller->url_for('wiki/show', 'page'); + * + * $url should then contain 'http://example.com/dispatch.php/wiki/show/page'. + * + * The first parameter is a string containing the controller and optionally an + * action: + * + * - "{controller}/{action}" + * - "path/to/controller/action" + * - "controller" + * + * This "controller/action" string is not url encoded. You may provide + * additional parameter which will be urlencoded and concatenated with + * slashes: + * + * $controller->url_for('wiki/show', 'page'); + * -> 'wiki/show/page' + * + * $controller->url_for('wiki/show', 'page', 'one and a half'); + * -> 'wiki/show/page/one+and+a+half' + * + * @param string a string containing a controller and optionally an action + * @param strings optional arguments + * + * @return string a URL to this route + */ + function url_for($to/*, ...*/) { + + # urlencode all but the first argument + $args = func_get_args(); + $args = array_map('urlencode', $args); + $args[0] = $to; + + return $this->dispatcher->trails_uri . '/' . join('/', $args); + } + + + /** + * <MethodDescription> + * + * @param type <description> + * + * @return type <description> + */ + function set_status($status, $reason_phrase = NULL) { + $this->response->set_status($status, $reason_phrase); + } + + + /** + * Sets the content type of the controller's response. + * + * @param string the content type + * + * @return void + */ + function set_content_type($type) { + $this->response->add_header('Content-Type', $type); + } + + + /** + * Exception handler called when the performance of an action raises an + * exception. + * + * @param object the thrown exception + * + * @return object a response object + */ + function rescue($exception) { + return $this->dispatcher->trails_error($exception); + } + + function respond_to($ext) { + return $this->format === $ext; + } +} diff --git a/vendor/trails/src/dispatcher.php b/vendor/trails/src/dispatcher.php new file mode 100644 index 0000000..b1c5ff5 --- /dev/null +++ b/vendor/trails/src/dispatcher.php @@ -0,0 +1,268 @@ +<?php + +/** + * The Dispatcher is used to map an incoming HTTP request to a Controller + * producing a response which is then rendered. To initialize an instance of + * class Trails_Dispatcher you have to give three configuration settings: + * + * trails_root - the absolute file path to a directory containing the + * applications controllers, views etc. + * trails_uri - the URI to which routes to mapped Controller/Actions + * are appended + * default_controller - the route to a controller, that is used if no + * controller is given, that is the route is equal to '/' + * + * After instantiation of a dispatcher you have to call method #dispatch with + * the request uri to be mapped to a controller/action pair. + * + * @package trails + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id: trails.php 7001 2008-04-04 11:20:27Z mlunzena $ + */ + +class Trails_Dispatcher { + + # TODO (mlunzena) Konfiguration muss anders geschehen + + /** + * This is the absolute file path to the trails application directory. + * + * @access public + * @var string + */ + public $trails_root; + + + /** + * This is the URI to which routes to controller/actions are appended. + * + * @access public + * @var string + */ + public $trails_uri; + + + /** + * This variable contains the route to the default controller. + * + * @access public + * @var string + */ + public $default_controller; + + + /** + * Constructor. + * + * @param string absolute file path to a directory containing the + * applications controllers, views etc. + * @param string the URI to which routes to mapped Controller/Actions + * are appended + * @param string the route to a controller, that is used if no + * controller is given, that is the route is equal to '/' + * + * @return void + */ + function __construct($trails_root, + $trails_uri, + $default_controller) { + + $this->trails_root = $trails_root; + $this->trails_uri = $trails_uri; + $this->default_controller = $default_controller; + } + + + /** + * Maps a string to a response which is then rendered. + * + * @param string The requested URI. + * + * @return void + */ + function dispatch($uri) { + + # E_USER_ERROR|E_USER_WARNING|E_USER_NOTICE|E_RECOVERABLE_ERROR = 5888 + $old_handler = set_error_handler(array($this, 'error_handler'), 5888); + + ob_start(); + $level = ob_get_level(); + + $this->map_uri_to_response($this->clean_request_uri((string) $uri))->output(); + + while (ob_get_level() >= $level) { + ob_end_flush(); + } + + if (isset($old_handler)) { + set_error_handler($old_handler); + } + } + + + /** + * Maps an URI to a response by figuring out first what controller to + * instantiate, then delegating the unconsumed part of the URI to the + * controller who returns an appropriate response object or throws a + * Trails_Exception. + * + * @param string the URI string + * + * @return mixed a response object + */ + function map_uri_to_response($uri) { + + try { + + list($controller_path, $unconsumed) = + '' === $uri + ? $this->default_route() + : $this->parse($uri); + + $controller = $this->load_controller($controller_path); + + $response = $controller->perform($unconsumed); + + } catch (Exception $e) { + + $response = isset($controller) ? $controller->rescue($e) + : $this->trails_error($e); + } + + return $response; + } + + + /** + * + * @return array an array containing the default controller and an + * empty unconsumed route + */ + function default_route() { + if (!$this->file_exists($this->default_controller . '.php')) { + throw new Trails_MissingFile( + "Default controller '{$this->default_controller}' not found'"); + } + return array($this->default_controller, ''); + } + + + function trails_error($exception) { + ob_clean(); + + # show details for local requests + $detailed = @$_SERVER['REMOTE_ADDR'] === '127.0.0.1'; + + $body = sprintf('<html><head><title>Trails Error</title></head>'. + '<body><h1>%s</h1><pre>%s</pre></body></html>', + htmlentities($exception->__toString()), + $detailed + ? htmlentities($exception->getTraceAsString()) + : ''); + + if ($exception instanceof Trails_Exception) { + $response = new Trails_Response($body, + $exception->headers, + $exception->getCode(), + $exception->getMessage()); + } + else { + $response = new Trails_Response($body, array(), 500, + $exception->getMessage()); + } + + return $response; + } + + + /** + * Clean up URI string by removing the query part and leading slashes. + * + * @param string an URI string + * + * @return string the cleaned string + */ + function clean_request_uri($uri) { + if (FALSE !== ($pos = strpos($uri, '?'))) { + $uri = substr($uri, 0, $pos); + } + return ltrim($uri, '/'); + } + + + /** + * <MethodDescription> + * + * @param type <description> + * @param type <description> + * + * @return type <description> + */ + function parse($unconsumed, $controller = NULL) { + list($head, $tail) = $this->split_on_first_slash($unconsumed); + + if (!preg_match('/^\w+$/', $head)) { + throw new Trails_RoutingError("No route matches '$head'"); + } + + $controller = (isset($controller) ? $controller . '/' : '') . $head; + + if ($this->file_exists($controller . '.php')) { + return array($controller, $tail); + } + else if ($this->file_exists($controller)) { + return $this->parse($tail, $controller); + } + + throw new Trails_RoutingError("No route matches '$head'"); + } + + function split_on_first_slash($str) { + preg_match(":([^/]*)(/+)?(.*):", $str, $matches); + return array($matches[1], $matches[3]); + } + + function file_exists($path) { + return file_exists("{$this->trails_root}/controllers/$path"); + } + + /** + * Loads the controller file for a given controller path and return an + * instance of that controller. If an error occures, an exception will be + * thrown. + * + * @param string the relative controller path + * + * @return TrailsController an instance of that controller + */ + function load_controller($controller) { + require_once "{$this->trails_root}/controllers/{$controller}.php"; + $class = Trails_Inflector::camelize($controller) . 'Controller'; + if (!class_exists($class)) { + throw new Trails_UnknownController("Controller missing: '$class'"); + } + return new $class($this); + } + + + /** + * This method transforms E_USER_* and E_RECOVERABLE_ERROR to + * Trails_Exceptions. + * + * @param integer the level of the error raised + * @param string the error message + * @param string the filename that the error was raised in + * @param integer the line number the error was raised at + * @param array an array of every variable that existed in the scope the + * error was triggered in + * + * @throws Trails_Exception + * + * @return void + */ + function error_handler($errno, $string, $file, $line, $context) { + throw new Trails_Exception(500, $string); + } +} diff --git a/vendor/trails/src/exception.php b/vendor/trails/src/exception.php new file mode 100644 index 0000000..4c21831 --- /dev/null +++ b/vendor/trails/src/exception.php @@ -0,0 +1,101 @@ +<?php + +/** + * TODO + * + * @package trails + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id: trails.php 7001 2008-04-04 11:20:27Z mlunzena $ + */ + +class Trails_Exception extends Exception { + + /** + * <FieldDescription> + * + * @access private + * @var <type> + */ + public $headers; + + + /** + * @param int the status code to be set in the response + * @param string a human readable presentation of the status code + * @param array a hash of additional headers to be set in the response + * + * @return void + */ + function __construct($status = 500, $reason = NULL, $headers = array()) { + if ($reason === NULL) { + $reason = Trails_Response::get_reason($status); + } + parent::__construct($reason, $status); + $this->headers = $headers; + } + + + /** + * <MethodDescription> + * + * @param type <description> + * + * @return type <description> + */ + function __toString() { + return "{$this->code} {$this->message}"; + } +} + + +class Trails_DoubleRenderError extends Trails_Exception { + + function __construct() { + $message = + "Render and/or redirect were called multiple times in this action. ". + "Please note that you may only call render OR redirect, and at most ". + "once per action."; + parent::__construct(500, $message); + } +} + + +class Trails_MissingFile extends Trails_Exception { + function __construct($message) { + parent::__construct(500, $message); + } +} + + +class Trails_RoutingError extends Trails_Exception { + + function __construct($message) { + parent::__construct(400, $message); + } +} + + +class Trails_UnknownAction extends Trails_Exception { + + function __construct($message) { + parent::__construct(404, $message); + } +} + + +class Trails_UnknownController extends Trails_Exception { + + function __construct($message) { + parent::__construct(404, $message); + } +} + + +class Trails_SessionRequiredException extends Trails_Exception { + function __construct() { + $message = "Tried to access a non existing session."; + parent::__construct(500, $message); + } +} diff --git a/vendor/trails/src/flash.php b/vendor/trails/src/flash.php new file mode 100644 index 0000000..cee08e5 --- /dev/null +++ b/vendor/trails/src/flash.php @@ -0,0 +1,241 @@ +<?php + +/** + * The flash provides a way to pass temporary objects between actions. + * Anything you place in the flash will be exposed to the very next action and + * then cleared out. This is a great way of doing notices and alerts, such as + * a create action that sets + * <tt>$flash->set('notice', "Successfully created")</tt> + * before redirecting to a display action that can then expose the flash to its + * template. + * + * @package trails + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id: trails.php 7001 2008-04-04 11:20:27Z mlunzena $ + */ + +class Trails_Flash implements ArrayAccess { + + + /** + * @ignore + */ + public + $flash = array(), $used = array(); + + + /** + * <MethodDescription> + * + * @return type <description> + */ + static function instance() { + + if (!isset($_SESSION)) { + throw new Trails_SessionRequiredException(); + } + + + if (!isset($_SESSION['trails_flash'])) { + $_SESSION['trails_flash'] = new Trails_Flash(); + } + return $_SESSION['trails_flash']; + } + + + function offsetExists($offset) { + return isset($this->flash[$offset]); + } + + + function offsetGet($offset) { + return $this->get($offset); + } + + + function offsetSet($offset, $value) { + $this->set($offset, $value); + } + + + function offsetUnset($offset) { + unset($this->flash[$offset], $this->used[$offset]); + } + + + /** + * Used internally by the <tt>keep</tt> and <tt>discard</tt> methods + * use() # marks the entire flash as used + * use('msg') # marks the "msg" entry as used + * use(null, false) # marks the entire flash as unused + * # (keeps it around for one more action) + * use('msg', false) # marks the "msg" entry as unused + * # (keeps it around for one more action) + * + * @param mixed a key. + * @param bool used flag. + * + * @return void + */ + function _use($k = NULL, $v = TRUE) { + if ($k) { + $this->used[$k] = $v; + } + else { + foreach ($this->used as $k => $value) { + $this->_use($k, $v); + } + } + } + + + /** + * Marks the entire flash or a single flash entry to be discarded by the end + * of the current action. + * + * $flash->discard() # discards entire flash + * # (it'll still be available for the + * # current action) + * $flash->discard('warning') # discard the "warning" entry + * # (it'll still be available for the + * # current action) + * + * @param mixed a key. + * + * @return void + */ + function discard($k = NULL) { + $this->_use($k); + } + + + /** + * Returns the value to the specified key. + * + * @param mixed a key. + * + * @return mixed the key's value. + */ + function &get($k) { + $return = NULL; + if (isset($this->flash[$k])) { + $return =& $this->flash[$k]; + } + return $return; + } + + + /** + * Keeps either the entire current flash or a specific flash entry available + * for the next action: + * + * $flash->keep() # keeps the entire flash + * $flash->keep('notice') # keeps only the "notice" entry, the rest of + * # the flash is discarded + * + * @param mixed a key. + * + * @return void + */ + function keep($k = NULL) { + $this->_use($k, FALSE); + } + + + /** + * Sets a key's value. + * + * @param mixed a key. + * @param mixed its value. + * + * @return void + */ + function set($k, $v) { + $this->keep($k); + $this->flash[$k] = $v; + } + + + /** + * Sets a key's value by reference. + * + * @param mixed a key. + * @param mixed its value. + * + * @return void + */ + function set_ref($k, &$v) { + $this->keep($k); + $this->flash[$k] =& $v; + } + + + + /** + * <MethodDescription> + * + * @return type <description> + */ + function sweep() { + + # remove used values + foreach (array_keys($this->flash) as $k) { + if ($this->used[$k]) { + unset($this->flash[$k], $this->used[$k]); + } else { + $this->_use($k); + } + } + + # cleanup if someone meddled with flash or used + $fkeys = array_keys($this->flash); + $ukeys = array_keys($this->used); + foreach (array_diff($fkeys, $ukeys) as $k => $v) { + unset($this->used[$k]); + } + } + + + /** + * <MethodDescription> + * + * @return type <description> + */ + function __toString() { + $values = array(); + foreach ($this->flash as $k => $v) { + $values[] = sprintf("'%s': [%s, '%s']", + $k, var_export($v, TRUE), + $this->used[$k] ? "used" : "unused"); + } + return "{" . join(", ", $values) . "}\n"; + } + + + /** + * <MethodDescription> + * + * @param type <description> + * + * @return type <description> + */ + function __sleep() { + $this->sweep(); + return array('flash', 'used'); + } + + + /** + * <MethodDescription> + * + * @param type <description> + * + * @return type <description> + */ + function __wakeUp() { + $this->discard(); + } +} + diff --git a/vendor/trails/src/inflector.php b/vendor/trails/src/inflector.php new file mode 100644 index 0000000..6646eee --- /dev/null +++ b/vendor/trails/src/inflector.php @@ -0,0 +1,49 @@ +<?php + +/** + * The Inflector class is a namespace for inflections methods. + * + * @package trails + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id: trails.php 7001 2008-04-04 11:20:27Z mlunzena $ + */ + +class Trails_Inflector { + + + /** + * Returns a camelized string from a lower case and underscored string by + * replacing slash with underscore and upper-casing each letter preceded + * by an underscore. TODO + * + * @param string String to camelize. + * + * @return string Camelized string. + */ + static function camelize($word) { + $parts = explode('/', $word); + foreach ($parts as $key => $part) { + $parts[$key] = str_replace(' ', '', + ucwords(str_replace('_', ' ', $part))); + } + return join('_', $parts); + } + + + /** + * <MethodDescription> + * + * @param type <description> + * + * @return type <description> + */ + static function underscore($word) { + $parts = explode('_', $word); + foreach ($parts as $key => $part) { + $parts[$key] = preg_replace('/(?<=\w)([A-Z])/', '_\\1', $part); + } + return strtolower(join('/', $parts)); + } +} diff --git a/vendor/trails/src/response.php b/vendor/trails/src/response.php new file mode 100644 index 0000000..60376b3 --- /dev/null +++ b/vendor/trails/src/response.php @@ -0,0 +1,166 @@ +<?php + +/** + * This class represents a response returned by a controller that was asked to + * perform for a given request. A Trails_Response contains the body, status and + * additional headers which can be renderer back to the client. + * + * @package trails + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id: trails.php 7001 2008-04-04 11:20:27Z mlunzena $ + */ + +class Trails_Response { + + + /** + * @ignore + */ + public + $body = '', + $status, + $reason, + $headers = array(); + + + /** + * Constructor. + * + * @param string the body of the response defaulting to '' + * @param array an array of additional headers defaulting to an + * empty array + * @param integer the status code of the response defaulting to a + * regular 200 + * @param string the descriptional reason for a status code defaulting to + * the standard reason phrases defined in RFC 2616 + * + * @return void + */ + function __construct($body = '', $headers = array(), + $status = NULL, $reason = NULL) { + + $this->set_body($body); + + $this->headers = $headers; + + if (isset($status)) { + $this->set_status($status, $reason); + } + } + + + /** + * Sets the body of the response. + * + * @param string the body + * + * @return mixed this response object. Useful for cascading method calls. + */ + function set_body($body) { + $this->body = $body; + return $this; + } + + + /** + * Sets the status code and an optional custom reason. If none is given, the + * standard reason phrase as of RFC 2616 is used. + * + * @param integer the status code + * @param string the custom reason, defaulting to the one given in RFC 2616 + * + * @return mixed this response object. Useful for cascading method calls. + */ + function set_status($status, $reason = NULL) { + $this->status = $status; + $this->reason = isset($reason) ? $reason : $this->get_reason($status); + return $this; + } + + + /** + * Returns the reason phrase of this response according to RFC2616. + * + * @param int the response's status + * + * @return string the reason phrase for this response's status + */ + function get_reason($status) { + $reason = array( + 100 => 'Continue', 'Switching Protocols', + 200 => 'OK', 'Created', 'Accepted', 'Non-Authoritative Information', + 'No Content', 'Reset Content', 'Partial Content', + 300 => 'Multiple Choices', 'Moved Permanently', 'Found', 'See Other', + 'Not Modified', 'Use Proxy', '(Unused)', 'Temporary Redirect', + 400 => 'Bad Request', 'Unauthorized', 'Payment Required','Forbidden', + 'Not Found', 'Method Not Allowed', 'Not Acceptable', + 'Proxy Authentication Required', 'Request Timeout', 'Conflict', + 'Gone', 'Length Required', 'Precondition Failed', + 'Request Entity Too Large', 'Request-URI Too Long', + 'Unsupported Media Type', 'Requested Range Not Satisfiable', + 'Expectation Failed', + 500 => 'Internal Server Error', 'Not Implemented', 'Bad Gateway', + 'Service Unavailable', 'Gateway Timeout', + 'HTTP Version Not Supported'); + + return isset($reason[$status]) ? $reason[$status] : ''; + } + + + /** + * Adds an additional header to the response. + * + * @param string the left hand key part + * @param string the right hand value part + * + * @return mixed this response object. Useful for cascading method calls. + */ + function add_header($key, $value) { + $this->headers[$key] = $value; + return $this; + } + + + /** + * Outputs this response to the client using "echo" and "header". + * + * @return void + */ + function output() { + if (isset($this->status)) { + $this->send_header(sprintf('HTTP/1.1 %d %s', + $this->status, $this->reason), + TRUE, + $this->status); + } + + foreach ($this->headers as $k => $v) { + $this->send_header("$k: $v"); + } + + echo $this->body; + } + + + /** + * Internally used function to actually send headers + * + * @param string the HTTP header + * @param bool optional; TRUE if previously sent header should be + * replaced - FALSE otherwise (default) + * @param integer optional; the HTTP response code + * + * @return void + */ + function send_header($header, $replace = FALSE, $status = NULL) { + if (isset($status)) { + header($header, $replace, $status); + } + else { + header($header, $replace); + } + } +} + diff --git a/vendor/trails/src/trails.php b/vendor/trails/src/trails.php new file mode 100644 index 0000000..2e9e8e2 --- /dev/null +++ b/vendor/trails/src/trails.php @@ -0,0 +1,16 @@ +<?php +function fgc($files/*, ... */) { + $result = ''; + $files = func_get_args(); + foreach ($files as $file) { + $string = file_get_contents(dirname(__FILE__)."/$file.php"); + $result .= preg_replace("/^(<\?(php)?)|(\?>)\s*\v+$/", "", $string); + } + return $result; +} +?> +<?= "<?php" ?> +<?= fgc("HEADER") ?> + +<?= fgc("dispatcher", "response", "controller", + "inflector", "flash", "exception") ?> diff --git a/vendor/trails/trails-abridged.php b/vendor/trails/trails-abridged.php new file mode 100644 index 0000000..ce39971 --- /dev/null +++ b/vendor/trails/trails-abridged.php @@ -0,0 +1,2 @@ +<?php + define('TRAILS_VERSION','0.6.9');class Trails_Dispatcher{public$trails_root;public$trails_uri;public$default_controller;function __construct($trails_root,$trails_uri,$default_controller){$this->trails_root=$trails_root;$this->trails_uri=$trails_uri;$this->default_controller=$default_controller;}function dispatch($uri){$old_handler=set_error_handler(array($this,'error_handler'),5888);ob_start();$level=ob_get_level();$this->map_uri_to_response($this->clean_request_uri((string)$uri))->output();while(ob_get_level()>=$level){ob_end_flush();}if(isset($old_handler)){set_error_handler($old_handler);}}function map_uri_to_response($uri){try{list($controller_path,$unconsumed)=''===$uri?$this->default_route():$this->parse($uri);$controller=$this->load_controller($controller_path);$response=$controller->perform($unconsumed);}catch(Exception$e){$response=isset($controller)?$controller->rescue($e):$this->trails_error($e);}return$response;}function default_route(){if(!$this->file_exists($this->default_controller.'.php')){throw new Trails_MissingFile("Default controller '{$this->default_controller}' not found'");}return array($this->default_controller,'');}function trails_error($exception){ob_clean();$detailed=@$_SERVER['REMOTE_ADDR']==='127.0.0.1';$body=sprintf('<html><head><title>Trails Error</title></head>'.'<body><h1>%s</h1><pre>%s</pre></body></html>',htmlentities($exception->__toString()),$detailed?htmlentities($exception->getTraceAsString()):'');if($exception instanceof Trails_Exception){$response=new Trails_Response($body,$exception->headers,$exception->getCode(),$exception->getMessage());}else{$response=new Trails_Response($body,array(),500,$exception->getMessage());}return$response;}function clean_request_uri($uri){if(FALSE!==($pos=strpos($uri,'?'))){$uri=substr($uri,0,$pos);}return ltrim($uri,'/');}function parse($unconsumed,$controller=NULL){list($head,$tail)=$this->split_on_first_slash($unconsumed);if(!preg_match('/^\w+$/',$head)){throw new Trails_RoutingError("No route matches '$head'");}$controller=(isset($controller)?$controller.'/':'').$head;if($this->file_exists($controller.'.php')){return array($controller,$tail);}else if($this->file_exists($controller)){return$this->parse($tail,$controller);}throw new Trails_RoutingError("No route matches '$head'");}function split_on_first_slash($str){preg_match(":([^/]*)(/+)?(.*):",$str,$matches);return array($matches[1],$matches[3]);}function file_exists($path){return file_exists("{$this->trails_root}/controllers/$path");}function load_controller($controller){require_once"{$this->trails_root}/controllers/{$controller}.php";$class=Trails_Inflector::camelize($controller).'Controller';if(!class_exists($class)){throw new Trails_UnknownController("Controller missing: '$class'");}return new$class($this);}function error_handler($errno,$string,$file,$line,$context){throw new Trails_Exception(500,$string);}}class Trails_Response{public$body='',$status,$reason,$headers=array();function __construct($body='',$headers=array(),$status=NULL,$reason=NULL){$this->set_body($body);$this->headers=$headers;if(isset($status)){$this->set_status($status,$reason);}}function set_body($body){$this->body=$body;return$this;}function set_status($status,$reason=NULL){$this->status=$status;$this->reason=isset($reason)?$reason:$this->get_reason($status);return$this;}function get_reason($status){$reason=array(100=>'Continue','Switching Protocols',200=>'OK','Created','Accepted','Non-Authoritative Information','No Content','Reset Content','Partial Content',300=>'Multiple Choices','Moved Permanently','Found','See Other','Not Modified','Use Proxy','(Unused)','Temporary Redirect',400=>'Bad Request','Unauthorized','Payment Required','Forbidden','Not Found','Method Not Allowed','Not Acceptable','Proxy Authentication Required','Request Timeout','Conflict','Gone','Length Required','Precondition Failed','Request Entity Too Large','Request-URI Too Long','Unsupported Media Type','Requested Range Not Satisfiable','Expectation Failed',500=>'Internal Server Error','Not Implemented','Bad Gateway','Service Unavailable','Gateway Timeout','HTTP Version Not Supported');return isset($reason[$status])?$reason[$status]:'';}function add_header($key,$value){$this->headers[$key]=$value;return$this;}function output(){if(isset($this->status)){$this->send_header(sprintf('HTTP/1.1 %d %s',$this->status,$this->reason),TRUE,$this->status);}foreach($this->headers as$k=>$v){$this->send_header("$k: $v");}echo$this->body;}function send_header($header,$replace=FALSE,$status=NULL){if(isset($status)){header($header,$replace,$status);}else{header($header,$replace);}}}class Trails_Controller{protected$dispatcher,$response,$performed,$layout;function __construct($dispatcher){$this->dispatcher=$dispatcher;$this->erase_response();}function erase_response(){$this->performed=FALSE;$this->response=new Trails_Response();}function get_response(){return$this->response;}function perform($unconsumed){list($action,$args,$format)=$this->extract_action_and_args($unconsumed);$this->format=isset($format)?$format:'html';$before_filter_result=$this->before_filter($action,$args);if(!(FALSE===$before_filter_result||$this->performed)){$callable=$this->map_action($action);if(is_callable($callable)){call_user_func_array($callable,$args);}else{$this->does_not_understand($action,$args);}if(!$this->performed){$this->render_action($action);}$this->after_filter($action,$args);}return$this->response;}function extract_action_and_args($string){if(''===$string){return$this->default_action_and_args();}$format=NULL;if(preg_match('/^(.*[^\/.])\.(\w+)$/',$string,$matches)){list($_,$string,$format)=$matches;}$args=explode('/',$string);$action=array_shift($args);return array($action,$args,$format);}function default_action_and_args(){return array('index',array(),NULL);}function map_action($action){return array(&$this,$action.'_action');}function before_filter(&$action,&$args){}function after_filter($action,$args){}function does_not_understand($action,$args){throw new Trails_UnknownAction("No action responded to '$action'.");}function redirect($to){if($this->performed){throw new Trails_DoubleRenderError();}$this->performed=TRUE;$url=preg_match('#^(/|\w+://)#',$to)?$to:$this->url_for($to);$this->response->add_header('Location',$url)->set_status(302);}function render_text($text=' '){if($this->performed){throw new Trails_DoubleRenderError();}$this->performed=TRUE;$this->response->set_body($text);}function render_nothing(){$this->render_text('');}function render_action($action){$this->render_template($this->get_default_template($action),$this->layout);}function get_default_template($action){$class=get_class($this);$controller_name=Trails_Inflector::underscore(substr($class,0,-10));return$controller_name.'/'.$action;}function render_template($template_name,$layout=NULL){$factory=$this->get_template_factory();$template=$factory->open($template_name);switch(get_class($template)){case'Flexi_JsTemplate':$this->set_content_type('text/javascript');break;}$template->set_attributes($this->get_assigned_variables());if(isset($layout)){$template->set_layout($layout);}$this->render_text($template->render());}function get_template_factory(){return new Flexi_TemplateFactory($this->dispatcher->trails_root.'/views/');}function get_assigned_variables(){$assigns=array();$protected=get_class_vars(get_class($this));foreach(get_object_vars($this)as$var=>$value){if(!array_key_exists($var,$protected)){$assigns[$var]=&$this->$var;}}$assigns['controller']=$this;return$assigns;}function set_layout($layout){$this->layout=$layout;}function url_for($to){$args=func_get_args();$args=array_map('urlencode',$args);$args[0]=$to;return$this->dispatcher->trails_uri.'/'.join('/',$args);}function set_status($status,$reason_phrase=NULL){$this->response->set_status($status,$reason_phrase);}function set_content_type($type){$this->response->add_header('Content-Type',$type);}function rescue($exception){return$this->dispatcher->trails_error($exception);}function respond_to($ext){return$this->format===$ext;}}class Trails_Inflector{static function camelize($word){$parts=explode('/',$word);foreach($parts as$key=>$part){$parts[$key]=str_replace(' ','',ucwords(str_replace('_',' ',$part)));}return join('_',$parts);}static function underscore($word){$parts=explode('_',$word);foreach($parts as$key=>$part){$parts[$key]=preg_replace('/(?<=\w)([A-Z])/','_\\1',$part);}return strtolower(join('/',$parts));}}class Trails_Flash implements ArrayAccess{public$flash=array(),$used=array();static function instance(){if(!isset($_SESSION)){throw new Trails_SessionRequiredException();}if(!isset($_SESSION['trails_flash'])){$_SESSION['trails_flash']=new Trails_Flash();}return$_SESSION['trails_flash'];}function offsetExists($offset){return isset($this->flash[$offset]);}function offsetGet($offset){return$this->get($offset);}function offsetSet($offset,$value){$this->set($offset,$value);}function offsetUnset($offset){unset($this->flash[$offset],$this->used[$offset]);}function _use($k=NULL,$v=TRUE){if($k){$this->used[$k]=$v;}else{foreach($this->used as$k=>$value){$this->_use($k,$v);}}}function discard($k=NULL){$this->_use($k);}function&get($k){$return=NULL;if(isset($this->flash[$k])){$return=&$this->flash[$k];}return$return;}function keep($k=NULL){$this->_use($k,FALSE);}function set($k,$v){$this->keep($k);$this->flash[$k]=$v;}function set_ref($k,&$v){$this->keep($k);$this->flash[$k]=&$v;}function sweep(){foreach(array_keys($this->flash)as$k){if($this->used[$k]){unset($this->flash[$k],$this->used[$k]);}else{$this->_use($k);}}$fkeys=array_keys($this->flash);$ukeys=array_keys($this->used);foreach(array_diff($fkeys,$ukeys)as$k=>$v){unset($this->used[$k]);}}function __toString(){$values=array();foreach($this->flash as$k=>$v){$values[]=sprintf("'%s': [%s, '%s']",$k,var_export($v,TRUE),$this->used[$k]?"used":"unused");}return"{".join(", ",$values)."}\n";}function __sleep(){$this->sweep();return array('flash','used');}function __wakeUp(){$this->discard();}}class Trails_Exception extends Exception{public$headers;function __construct($status=500,$reason=NULL,$headers=array()){if($reason===NULL){$reason=Trails_Response::get_reason($status);}parent::__construct($reason,$status);$this->headers=$headers;}function __toString(){return"{$this->code} {$this->message}";}}class Trails_DoubleRenderError extends Trails_Exception{function __construct(){$message="Render and/or redirect were called multiple times in this action. "."Please note that you may only call render OR redirect, and at most "."once per action.";parent::__construct(500,$message);}}class Trails_MissingFile extends Trails_Exception{function __construct($message){parent::__construct(500,$message);}}class Trails_RoutingError extends Trails_Exception{function __construct($message){parent::__construct(400,$message);}}class Trails_UnknownAction extends Trails_Exception{function __construct($message){parent::__construct(404,$message);}}class Trails_UnknownController extends Trails_Exception{function __construct($message){parent::__construct(404,$message);}}class Trails_SessionRequiredException extends Trails_Exception{function __construct(){$message="Tried to access a non existing session.";parent::__construct(500,$message);}}
\ No newline at end of file diff --git a/vendor/trails/trails.php b/vendor/trails/trails.php new file mode 100644 index 0000000..a6ca003 --- /dev/null +++ b/vendor/trails/trails.php @@ -0,0 +1,1291 @@ +<?php + +# Copyright (c) 2007 - Marcus Lunzenauer <mlunzena@uos.de> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +/** + * The version of the trails library. + */ +define('TRAILS_VERSION', '0.6.9'); + + + +/** + * The Dispatcher is used to map an incoming HTTP request to a Controller + * producing a response which is then rendered. To initialize an instance of + * class Trails_Dispatcher you have to give three configuration settings: + * + * trails_root - the absolute file path to a directory containing the + * applications controllers, views etc. + * trails_uri - the URI to which routes to mapped Controller/Actions + * are appended + * default_controller - the route to a controller, that is used if no + * controller is given, that is the route is equal to '/' + * + * After instantiation of a dispatcher you have to call method #dispatch with + * the request uri to be mapped to a controller/action pair. + * + * @package trails + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id: trails.php 7001 2008-04-04 11:20:27Z mlunzena $ + */ + +class Trails_Dispatcher { + + # TODO (mlunzena) Konfiguration muss anders geschehen + + /** + * This is the absolute file path to the trails application directory. + * + * @access public + * @var string + */ + public $trails_root; + + + /** + * This is the URI to which routes to controller/actions are appended. + * + * @access public + * @var string + */ + public $trails_uri; + + + /** + * This variable contains the route to the default controller. + * + * @access public + * @var string + */ + public $default_controller; + + + /** + * Constructor. + * + * @param string absolute file path to a directory containing the + * applications controllers, views etc. + * @param string the URI to which routes to mapped Controller/Actions + * are appended + * @param string the route to a controller, that is used if no + * controller is given, that is the route is equal to '/' + * + * @return void + */ + function __construct($trails_root, + $trails_uri, + $default_controller) { + + $this->trails_root = $trails_root; + $this->trails_uri = $trails_uri; + $this->default_controller = $default_controller; + } + + + /** + * Maps a string to a response which is then rendered. + * + * @param string The requested URI. + * + * @return void + */ + function dispatch($uri) { + + # E_USER_ERROR|E_USER_WARNING|E_USER_NOTICE|E_RECOVERABLE_ERROR = 5888 + $old_handler = set_error_handler(array($this, 'error_handler'), 5888); + + ob_start(); + $level = ob_get_level(); + + $this->map_uri_to_response($this->clean_request_uri((string) $uri))->output(); + + while (ob_get_level() >= $level) { + ob_end_flush(); + } + + if (isset($old_handler)) { + set_error_handler($old_handler); + } + } + + + /** + * Maps an URI to a response by figuring out first what controller to + * instantiate, then delegating the unconsumed part of the URI to the + * controller who returns an appropriate response object or throws a + * Trails_Exception. + * + * @param string the URI string + * + * @return mixed a response object + */ + function map_uri_to_response($uri) { + + try { + + list($controller_path, $unconsumed) = + '' === $uri + ? $this->default_route() + : $this->parse($uri); + + $controller = $this->load_controller($controller_path); + + $response = $controller->perform($unconsumed); + + } catch (Exception $e) { + + $response = isset($controller) ? $controller->rescue($e) + : $this->trails_error($e); + } + + return $response; + } + + + /** + * + * @return array an array containing the default controller and an + * empty unconsumed route + */ + function default_route() { + if (!$this->file_exists($this->default_controller . '.php')) { + throw new Trails_MissingFile( + "Default controller '{$this->default_controller}' not found'"); + } + return array($this->default_controller, ''); + } + + + function trails_error($exception) { + ob_clean(); + + # show details for local requests + $detailed = @$_SERVER['REMOTE_ADDR'] === '127.0.0.1'; + + $body = sprintf('<html><head><title>Trails Error</title></head>'. + '<body><h1>%s</h1><pre>%s</pre></body></html>', + htmlentities($exception->__toString()), + $detailed + ? htmlentities($exception->getTraceAsString()) + : ''); + + if ($exception instanceof Trails_Exception) { + $response = new Trails_Response($body, + $exception->headers, + $exception->getCode(), + $exception->getMessage()); + } + else { + $response = new Trails_Response($body, array(), 500, + $exception->getMessage()); + } + + return $response; + } + + + /** + * Clean up URI string by removing the query part and leading slashes. + * + * @param string an URI string + * + * @return string the cleaned string + */ + function clean_request_uri($uri) { + if (FALSE !== ($pos = strpos($uri, '?'))) { + $uri = substr($uri, 0, $pos); + } + return ltrim($uri, '/'); + } + + + /** + * <MethodDescription> + * + * @param type <description> + * @param type <description> + * + * @return type <description> + */ + function parse($unconsumed, $controller = NULL) { + list($head, $tail) = $this->split_on_first_slash($unconsumed); + + if (!preg_match('/^\w+$/', $head)) { + throw new Trails_RoutingError("No route matches '$head'"); + } + + $controller = (isset($controller) ? $controller . '/' : '') . $head; + + if ($this->file_exists($controller . '.php')) { + return array($controller, $tail); + } + else if ($this->file_exists($controller)) { + return $this->parse($tail, $controller); + } + + throw new Trails_RoutingError("No route matches '$head'"); + } + + function split_on_first_slash($str) { + preg_match(":([^/]*)(/+)?(.*):", $str, $matches); + return array($matches[1], $matches[3]); + } + + function file_exists($path) { + return file_exists("{$this->trails_root}/controllers/$path"); + } + + /** + * Loads the controller file for a given controller path and return an + * instance of that controller. If an error occures, an exception will be + * thrown. + * + * @param string the relative controller path + * + * @return TrailsController an instance of that controller + */ + function load_controller($controller) { + require_once "{$this->trails_root}/controllers/{$controller}.php"; + $class = Trails_Inflector::camelize($controller) . 'Controller'; + if (!class_exists($class)) { + throw new Trails_UnknownController("Controller missing: '$class'"); + } + return new $class($this); + } + + + /** + * This method transforms E_USER_* and E_RECOVERABLE_ERROR to + * Trails_Exceptions. + * + * @param integer the level of the error raised + * @param string the error message + * @param string the filename that the error was raised in + * @param integer the line number the error was raised at + * @param array an array of every variable that existed in the scope the + * error was triggered in + * + * @throws Trails_Exception + * + * @return void + */ + function error_handler($errno, $string, $file, $line, $context) { + throw new Trails_Exception(500, $string); + } +} + + +/** + * This class represents a response returned by a controller that was asked to + * perform for a given request. A Trails_Response contains the body, status and + * additional headers which can be renderer back to the client. + * + * @package trails + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id: trails.php 7001 2008-04-04 11:20:27Z mlunzena $ + */ + +class Trails_Response { + + + /** + * @ignore + */ + public + $body = '', + $status, + $reason, + $headers = array(); + + + /** + * Constructor. + * + * @param string the body of the response defaulting to '' + * @param array an array of additional headers defaulting to an + * empty array + * @param integer the status code of the response defaulting to a + * regular 200 + * @param string the descriptional reason for a status code defaulting to + * the standard reason phrases defined in RFC 2616 + * + * @return void + */ + function __construct($body = '', $headers = array(), + $status = NULL, $reason = NULL) { + + $this->set_body($body); + + $this->headers = $headers; + + if (isset($status)) { + $this->set_status($status, $reason); + } + } + + + /** + * Sets the body of the response. + * + * @param string the body + * + * @return mixed this response object. Useful for cascading method calls. + */ + function set_body($body) { + $this->body = $body; + return $this; + } + + + /** + * Sets the status code and an optional custom reason. If none is given, the + * standard reason phrase as of RFC 2616 is used. + * + * @param integer the status code + * @param string the custom reason, defaulting to the one given in RFC 2616 + * + * @return mixed this response object. Useful for cascading method calls. + */ + function set_status($status, $reason = NULL) { + $this->status = $status; + $this->reason = isset($reason) ? $reason : $this->get_reason($status); + return $this; + } + + + /** + * Returns the reason phrase of this response according to RFC2616. + * + * @param int the response's status + * + * @return string the reason phrase for this response's status + */ + function get_reason($status) { + $reason = array( + 100 => 'Continue', 'Switching Protocols', + 200 => 'OK', 'Created', 'Accepted', 'Non-Authoritative Information', + 'No Content', 'Reset Content', 'Partial Content', + 300 => 'Multiple Choices', 'Moved Permanently', 'Found', 'See Other', + 'Not Modified', 'Use Proxy', '(Unused)', 'Temporary Redirect', + 400 => 'Bad Request', 'Unauthorized', 'Payment Required','Forbidden', + 'Not Found', 'Method Not Allowed', 'Not Acceptable', + 'Proxy Authentication Required', 'Request Timeout', 'Conflict', + 'Gone', 'Length Required', 'Precondition Failed', + 'Request Entity Too Large', 'Request-URI Too Long', + 'Unsupported Media Type', 'Requested Range Not Satisfiable', + 'Expectation Failed', + 500 => 'Internal Server Error', 'Not Implemented', 'Bad Gateway', + 'Service Unavailable', 'Gateway Timeout', + 'HTTP Version Not Supported'); + + return isset($reason[$status]) ? $reason[$status] : ''; + } + + + /** + * Adds an additional header to the response. + * + * @param string the left hand key part + * @param string the right hand value part + * + * @return mixed this response object. Useful for cascading method calls. + */ + function add_header($key, $value) { + $this->headers[$key] = $value; + return $this; + } + + + /** + * Outputs this response to the client using "echo" and "header". + * + * @return void + */ + function output() { + if (isset($this->status)) { + $this->send_header(sprintf('HTTP/1.1 %d %s', + $this->status, $this->reason), + TRUE, + $this->status); + } + + foreach ($this->headers as $k => $v) { + $this->send_header("$k: $v"); + } + + echo $this->body; + } + + + /** + * Internally used function to actually send headers + * + * @param string the HTTP header + * @param bool optional; TRUE if previously sent header should be + * replaced - FALSE otherwise (default) + * @param integer optional; the HTTP response code + * + * @return void + */ + function send_header($header, $replace = FALSE, $status = NULL) { + if (isset($status)) { + header($header, $replace, $status); + } + else { + header($header, $replace); + } + } +} + + + +/** + * A Trails_Controller is responsible for matching the unconsumed part of an URI + * to an action using the left over words as arguments for that action. The + * action is then mapped to method of the controller instance which is called + * with the just mentioned arguments. That method can send the #render_action, + * #render_template, #render_text, #render_nothing or #redirect method. + * Otherwise the #render_action is called with the current action as argument. + * If the action method sets instance variables during performing, they will be + * be used as attributes for the flexi-template opened by #render_action or + * #render_template. A controller's response's body is populated with the output + * of the #render_* methods. The action methods can add additional headers or + * change the status of that response. + * + * @package trails + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id: trails.php 7001 2008-04-04 11:20:27Z mlunzena $ + */ + +class Trails_Controller { + + + /** + * @ignore + */ + protected + $dispatcher, + $response, + $performed, + $layout; + + + /** + * Constructor. + * + * @param mixed the dispatcher who creates this instance + * + * @return void + */ + function __construct($dispatcher) { + $this->dispatcher = $dispatcher; + $this->erase_response(); + } + + + /** + * Resets the response of the controller + * + * @return void + */ + function erase_response() { + $this->performed = FALSE; + $this->response = new Trails_Response(); + } + + + /** + * Return this controller's response + * + * @return mixed the controller's response + */ + function get_response() { + return $this->response; + } + + + /** + * This method extracts an action string and further arguments from it's + * parameter. The action string is mapped to a method being called afterwards + * using the said arguments. That method is called and a response object is + * generated, populated and sent back to the dispatcher. + * + * @param type <description> + * + * @return type <description> + */ + function perform($unconsumed) { + + list($action, $args, $format) = $this->extract_action_and_args($unconsumed); + + $this->format = isset($format) ? $format : 'html'; + + $before_filter_result = $this->before_filter($action, $args); + + # send action to controller + # TODO (mlunzena) shouldn't the after filter be triggered too? + if (!(FALSE === $before_filter_result || $this->performed)) { + + $callable = $this->map_action($action); + + if (is_callable($callable)) { + call_user_func_array($callable, $args); + } + else { + $this->does_not_understand($action, $args); + } + + if (!$this->performed) { + $this->render_action($action); + } + + $this->after_filter($action, $args); + } + + return $this->response; + } + + + /** + * Extracts action and args from a string. + * + * @param string the processed string + * + * @return array an array with two elements - a string containing the + * action and an array of strings representing the args + */ + function extract_action_and_args($string) { + + if ('' === $string) { + return $this->default_action_and_args(); + } + + // find optional file extension + $format = NULL; + if (preg_match('/^(.*[^\/.])\.(\w+)$/', $string, $matches)) { + list($_, $string, $format) = $matches; + } + + // TODO this should possibly remove empty tokens + $args = explode('/', $string); + $action = array_shift($args); + return array($action, $args, $format); + } + + /** + * Return the default action and arguments + * + * @return an array containing the action, an array of args and the format + * + */ + function default_action_and_args() { + return array('index', array(), NULL); + } + + /** + * Maps the action to an actual method name. + * + * @param string the action + * + * @return string the mapped method name + */ + function map_action($action) { + return array(&$this, $action . '_action'); + } + + + /** + * Callback function being called before an action is executed. If this + * function does not return FALSE, the action will be called, otherwise + * an error will be generated and processing will be aborted. If this function + * already #rendered or #redirected, further processing of the action is + * withheld. + * + * @param string Name of the action to perform. + * @param array An array of arguments to the action. + * + * @return bool + */ + function before_filter(&$action, &$args) { + } + + + /** + * Callback function being called after an action is executed. + * + * @param string Name of the action to perform. + * @param array An array of arguments to the action. + * + * @return void + */ + function after_filter($action, $args) { + } + + + /** + * <MethodDescription> + * + * @param type <description> + * @param type <description> + * + * @return void + */ + function does_not_understand($action, $args) { + throw new Trails_UnknownAction("No action responded to '$action'."); + } + + + /** + * <MethodDescription> + * + * @param string <description> + * + * @return void + */ + function redirect($to) { + + if ($this->performed) { + throw new Trails_DoubleRenderError(); + } + + $this->performed = TRUE; + + # get uri; keep absolute URIs + $url = preg_match('#^(/|\w+://)#', $to) + ? $to + : $this->url_for($to); + + $this->response->add_header('Location', $url)->set_status(302); + } + + + /** + * Renders the given text as the body of the response. + * + * @param string the text to be rendered + * + * @return void + */ + function render_text($text = ' ') { + + if ($this->performed) { + throw new Trails_DoubleRenderError(); + } + + $this->performed = TRUE; + + $this->response->set_body($text); + } + + + /** + * Renders the empty string as the response's body. + * + * @return void + */ + function render_nothing() { + $this->render_text(''); + } + + + /** + * Renders the template of the given action as the response's body. + * + * @param string the action + * + * @return void + */ + function render_action($action) { + $this->render_template($this->get_default_template($action), $this->layout); + } + + + function get_default_template($action) + { + $class = get_class($this); + $controller_name = + Trails_Inflector::underscore(substr($class, 0, -10)); + return $controller_name.'/'.$action; + } + + + /** + * Renders a template using an optional layout template. + * + * @param mixed a flexi template + * @param mixes a flexi template which is used as layout + * + * @return void + */ + function render_template($template_name, $layout = NULL) { + + # open template + $factory = $this->get_template_factory(); + $template = $factory->open($template_name); + + # template requires setup ? + switch (get_class($template)) { + case 'Flexi_JsTemplate': + $this->set_content_type('text/javascript'); + break; + } + + $template->set_attributes($this->get_assigned_variables()); + + if (isset($layout)) { + $template->set_layout($layout); + } + + $this->render_text($template->render()); + } + + + /** + * Create and return a template factory for this controller. + * + * @return a Flexi_TemplateFactory + */ + function get_template_factory() { + return new Flexi_TemplateFactory($this->dispatcher->trails_root . + '/views/'); + } + + + /** + * This method returns all the set instance variables to be used as attributes + * for a template. This controller is returned too as value for + * key 'controller'. + * + * @return array an associative array of variables for the template + */ + function get_assigned_variables() { + + $assigns = array(); + $protected = get_class_vars(get_class($this)); + + foreach (get_object_vars($this) as $var => $value) { + if (!array_key_exists($var, $protected)) { + $assigns[$var] =& $this->$var; + } + } + + $assigns['controller'] = $this; + + return $assigns; + } + + + /** + * Sets the layout to be used by this controller per default. + * + * @param mixed a flexi template to be used as layout + * + * @return void + */ + function set_layout($layout) { + $this->layout = $layout; + } + + + /** + * Returns a URL to a specified route to your Trails application. + * + * Example: + * Your Trails application is located at 'http://example.com/dispatch.php'. + * So your dispatcher's trails_uri is set to 'http://example.com/dispatch.php' + * If you want the URL to your 'wiki' controller with action 'show' and + * parameter 'page' you should send: + * + * $url = $controller->url_for('wiki/show', 'page'); + * + * $url should then contain 'http://example.com/dispatch.php/wiki/show/page'. + * + * The first parameter is a string containing the controller and optionally an + * action: + * + * - "{controller}/{action}" + * - "path/to/controller/action" + * - "controller" + * + * This "controller/action" string is not url encoded. You may provide + * additional parameter which will be urlencoded and concatenated with + * slashes: + * + * $controller->url_for('wiki/show', 'page'); + * -> 'wiki/show/page' + * + * $controller->url_for('wiki/show', 'page', 'one and a half'); + * -> 'wiki/show/page/one+and+a+half' + * + * @param string a string containing a controller and optionally an action + * @param strings optional arguments + * + * @return string a URL to this route + */ + function url_for($to/*, ...*/) { + + # urlencode all but the first argument + $args = func_get_args(); + $args = array_map('urlencode', $args); + $args[0] = $to; + + return $this->dispatcher->trails_uri . '/' . join('/', $args); + } + + + /** + * <MethodDescription> + * + * @param type <description> + * + * @return type <description> + */ + function set_status($status, $reason_phrase = NULL) { + $this->response->set_status($status, $reason_phrase); + } + + + /** + * Sets the content type of the controller's response. + * + * @param string the content type + * + * @return void + */ + function set_content_type($type) { + $this->response->add_header('Content-Type', $type); + } + + + /** + * Exception handler called when the performance of an action raises an + * exception. + * + * @param object the thrown exception + * + * @return object a response object + */ + function rescue($exception) { + return $this->dispatcher->trails_error($exception); + } + + function respond_to($ext) { + return $this->format === $ext; + } +} + + +/** + * The Inflector class is a namespace for inflections methods. + * + * @package trails + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id: trails.php 7001 2008-04-04 11:20:27Z mlunzena $ + */ + +class Trails_Inflector { + + + /** + * Returns a camelized string from a lower case and underscored string by + * replacing slash with underscore and upper-casing each letter preceded + * by an underscore. TODO + * + * @param string String to camelize. + * + * @return string Camelized string. + */ + static function camelize($word) { + $parts = explode('/', $word); + foreach ($parts as $key => $part) { + $parts[$key] = str_replace(' ', '', + ucwords(str_replace('_', ' ', $part))); + } + return join('_', $parts); + } + + + /** + * <MethodDescription> + * + * @param type <description> + * + * @return type <description> + */ + static function underscore($word) { + $parts = explode('_', $word); + foreach ($parts as $key => $part) { + $parts[$key] = preg_replace('/(?<=\w)([A-Z])/', '_\\1', $part); + } + return strtolower(join('/', $parts)); + } +} + + +/** + * The flash provides a way to pass temporary objects between actions. + * Anything you place in the flash will be exposed to the very next action and + * then cleared out. This is a great way of doing notices and alerts, such as + * a create action that sets + * <tt>$flash->set('notice', "Successfully created")</tt> + * before redirecting to a display action that can then expose the flash to its + * template. + * + * @package trails + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id: trails.php 7001 2008-04-04 11:20:27Z mlunzena $ + */ + +class Trails_Flash implements ArrayAccess { + + + /** + * @ignore + */ + public + $flash = array(), $used = array(); + + + /** + * <MethodDescription> + * + * @return type <description> + */ + static function instance() { + + if (!isset($_SESSION)) { + throw new Trails_SessionRequiredException(); + } + + + if (!isset($_SESSION['trails_flash'])) { + $_SESSION['trails_flash'] = new Trails_Flash(); + } + return $_SESSION['trails_flash']; + } + + + function offsetExists($offset) { + return isset($this->flash[$offset]); + } + + + function offsetGet($offset) { + return $this->get($offset); + } + + + function offsetSet($offset, $value) { + $this->set($offset, $value); + } + + + function offsetUnset($offset) { + unset($this->flash[$offset], $this->used[$offset]); + } + + + /** + * Used internally by the <tt>keep</tt> and <tt>discard</tt> methods + * use() # marks the entire flash as used + * use('msg') # marks the "msg" entry as used + * use(null, false) # marks the entire flash as unused + * # (keeps it around for one more action) + * use('msg', false) # marks the "msg" entry as unused + * # (keeps it around for one more action) + * + * @param mixed a key. + * @param bool used flag. + * + * @return void + */ + function _use($k = NULL, $v = TRUE) { + if ($k) { + $this->used[$k] = $v; + } + else { + foreach ($this->used as $k => $value) { + $this->_use($k, $v); + } + } + } + + + /** + * Marks the entire flash or a single flash entry to be discarded by the end + * of the current action. + * + * $flash->discard() # discards entire flash + * # (it'll still be available for the + * # current action) + * $flash->discard('warning') # discard the "warning" entry + * # (it'll still be available for the + * # current action) + * + * @param mixed a key. + * + * @return void + */ + function discard($k = NULL) { + $this->_use($k); + } + + + /** + * Returns the value to the specified key. + * + * @param mixed a key. + * + * @return mixed the key's value. + */ + function &get($k) { + $return = NULL; + if (isset($this->flash[$k])) { + $return =& $this->flash[$k]; + } + return $return; + } + + + /** + * Keeps either the entire current flash or a specific flash entry available + * for the next action: + * + * $flash->keep() # keeps the entire flash + * $flash->keep('notice') # keeps only the "notice" entry, the rest of + * # the flash is discarded + * + * @param mixed a key. + * + * @return void + */ + function keep($k = NULL) { + $this->_use($k, FALSE); + } + + + /** + * Sets a key's value. + * + * @param mixed a key. + * @param mixed its value. + * + * @return void + */ + function set($k, $v) { + $this->keep($k); + $this->flash[$k] = $v; + } + + + /** + * Sets a key's value by reference. + * + * @param mixed a key. + * @param mixed its value. + * + * @return void + */ + function set_ref($k, &$v) { + $this->keep($k); + $this->flash[$k] =& $v; + } + + + + /** + * <MethodDescription> + * + * @return type <description> + */ + function sweep() { + + # remove used values + foreach (array_keys($this->flash) as $k) { + if ($this->used[$k]) { + unset($this->flash[$k], $this->used[$k]); + } else { + $this->_use($k); + } + } + + # cleanup if someone meddled with flash or used + $fkeys = array_keys($this->flash); + $ukeys = array_keys($this->used); + foreach (array_diff($fkeys, $ukeys) as $k => $v) { + unset($this->used[$k]); + } + } + + + /** + * <MethodDescription> + * + * @return type <description> + */ + function __toString() { + $values = array(); + foreach ($this->flash as $k => $v) { + $values[] = sprintf("'%s': [%s, '%s']", + $k, var_export($v, TRUE), + $this->used[$k] ? "used" : "unused"); + } + return "{" . join(", ", $values) . "}\n"; + } + + + /** + * <MethodDescription> + * + * @param type <description> + * + * @return type <description> + */ + function __sleep() { + $this->sweep(); + return array('flash', 'used'); + } + + + /** + * <MethodDescription> + * + * @param type <description> + * + * @return type <description> + */ + function __wakeUp() { + $this->discard(); + } +} + + + +/** + * TODO + * + * @package trails + * + * @author mlunzena + * @copyright (c) Authors + * @version $Id: trails.php 7001 2008-04-04 11:20:27Z mlunzena $ + */ + +class Trails_Exception extends Exception { + + /** + * <FieldDescription> + * + * @access private + * @var <type> + */ + public $headers; + + + /** + * @param int the status code to be set in the response + * @param string a human readable presentation of the status code + * @param array a hash of additional headers to be set in the response + * + * @return void + */ + function __construct($status = 500, $reason = NULL, $headers = array()) { + if ($reason === NULL) { + $reason = Trails_Response::get_reason($status); + } + parent::__construct($reason, $status); + $this->headers = $headers; + } + + + /** + * <MethodDescription> + * + * @param type <description> + * + * @return type <description> + */ + function __toString() { + return "{$this->code} {$this->message}"; + } +} + + +class Trails_DoubleRenderError extends Trails_Exception { + + function __construct() { + $message = + "Render and/or redirect were called multiple times in this action. ". + "Please note that you may only call render OR redirect, and at most ". + "once per action."; + parent::__construct(500, $message); + } +} + + +class Trails_MissingFile extends Trails_Exception { + function __construct($message) { + parent::__construct(500, $message); + } +} + + +class Trails_RoutingError extends Trails_Exception { + + function __construct($message) { + parent::__construct(400, $message); + } +} + + +class Trails_UnknownAction extends Trails_Exception { + + function __construct($message) { + parent::__construct(404, $message); + } +} + + +class Trails_UnknownController extends Trails_Exception { + + function __construct($message) { + parent::__construct(404, $message); + } +} + + +class Trails_SessionRequiredException extends Trails_Exception { + function __construct() { + $message = "Tried to access a non existing session."; + parent::__construct(500, $message); + } +} diff --git a/vendor/write_excel/BIFFwriter.php b/vendor/write_excel/BIFFwriter.php new file mode 100644 index 0000000..75d7be3 --- /dev/null +++ b/vendor/write_excel/BIFFwriter.php @@ -0,0 +1,209 @@ +<?php
+/*
+* Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
+*
+* The majority of this is _NOT_ my code. I simply ported it from the
+* PERL Spreadsheet::WriteExcel module.
+*
+* The author of the Spreadsheet::WriteExcel module is John McNamara
+* <jmcnamara@cpan.org>
+*
+* I _DO_ maintain this code, and John McNamara has nothing to do with the
+* porting of this code to PHP. Any questions directly related to this
+* class library should be directed to me.
+*
+* License Information:
+*
+* Spreadsheet::WriteExcel: A library for generating Excel Spreadsheets
+* Copyright (C) 2002 Xavier Noguer xnoguer@rezebra.com
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/**
+* Class for writing Excel BIFF records.
+*
+* From "MICROSOFT EXCEL BINARY FILE FORMAT" by Mark O'Brien (Microsoft Corporation):
+*
+* BIFF (BInary File Format) is the file format in which Excel documents are
+* saved on disk. A BIFF file is a complete description of an Excel document.
+* BIFF files consist of sequences of variable-length records. There are many
+* different types of BIFF records. For example, one record type describes a
+* formula entered into a cell; one describes the size and location of a
+* window into a document; another describes a picture format.
+*
+* @author Xavier Noguer <xnoguer@rezebra.com>
+* @package Spreadsheet_WriteExcel
+*/
+
+class BIFFWriter
+{
+ var $_BIFF_version = 0x0500;
+
+/**
+* Constructor
+*
+* @access public
+*/
+ function __construct()
+ {
+ // The byte order of this architecture. 0 => little endian, 1 => big endian
+ $this->_byte_order = '';
+ // The string containing the data of the BIFF stream
+ $this->_data = '';
+ // Should be the same as strlen($this->_data)
+ $this->_datasize = 0;
+ // The maximun length for a BIFF record. See _add_continue()
+ $this->_limit = 2080;
+ // Set the byte order
+ $this->_set_byte_order();
+ }
+
+/**
+* Determine the byte order and store it as class data to avoid
+* recalculating it for each call to new().
+*
+* @access private
+*/
+ function _set_byte_order()
+ {
+ if ($this->_byte_order == '')
+ {
+ // Check if "pack" gives the required IEEE 64bit float
+ $teststr = pack("d", 1.2345);
+ $number = pack("C8", 0x8D, 0x97, 0x6E, 0x12, 0x83, 0xC0, 0xF3, 0x3F);
+ if ($number == $teststr) {
+ $byte_order = 0; // Little Endian
+ }
+ elseif ($number == strrev($teststr)){
+ $byte_order = 1; // Big Endian
+ }
+ else {
+ // Give up. I'll fix this in a later version.
+ die("Required floating point format not supported ".
+ "on this platform. See the portability section ".
+ "of the documentation."
+ );
+ }
+ }
+ $this->_byte_order = $byte_order;
+ }
+
+/**
+* General storage function
+*
+* @param string $data binary data to prepend
+* @access private
+*/
+ function _prepend($data)
+ {
+ if (strlen($data) > $this->_limit) {
+ $data = $this->_add_continue($data);
+ }
+ $this->_data = $data.$this->_data;
+ $this->_datasize += strlen($data);
+ }
+
+/**
+* General storage function
+*
+* @param string $data binary data to append
+* @access private
+*/
+ function _append($data)
+ {
+ if (strlen($data) > $this->_limit) {
+ $data = $this->_add_continue($data);
+ }
+ $this->_data = $this->_data.$data;
+ $this->_datasize += strlen($data);
+ }
+
+/**
+* Writes Excel BOF record to indicate the beginning of a stream or
+* sub-stream in the BIFF file.
+*
+* @param integer $type type of BIFF file to write: 0x0005 Workbook, 0x0010 Worksheet.
+* @access private
+*/
+ function _store_bof($type)
+ {
+ $record = 0x0809; // Record identifier
+ $length = 0x0008; // Number of bytes to follow
+ $version = $this->_BIFF_version;
+
+ // According to the SDK $build and $year should be set to zero.
+ // However, this throws a warning in Excel 5. So, use these
+ // magic numbers.
+ $build = 0x096C;
+ $year = 0x07C9;
+
+ $header = pack("vv", $record, $length);
+ $data = pack("vvvv", $version, $type, $build, $year);
+ $this->_prepend($header.$data);
+ }
+
+/**
+* Writes Excel EOF record to indicate the end of a BIFF stream.
+*
+* @access private
+*/
+ function _store_eof()
+ {
+ $record = 0x000A; // Record identifier
+ $length = 0x0000; // Number of bytes to follow
+ $header = pack("vv", $record, $length);
+ $this->_append($header);
+ }
+
+/**
+* Excel limits the size of BIFF records. In Excel 5 the limit is 2084 bytes. In
+* Excel 97 the limit is 8228 bytes. Records that are longer than these limits
+* must be split up into CONTINUE blocks.
+*
+* This function takes a long BIFF record and inserts CONTINUE records as
+* necessary.
+*
+* @param string $data The original binary data to be written
+* @return string A very convenient string of continue blocks
+* @access private
+*/
+ function _add_continue($data)
+ {
+ $limit = $this->_limit;
+ $record = 0x003C; // Record identifier
+
+ // The first 2080/8224 bytes remain intact. However, we have to change
+ // the length field of the record.
+ $tmp = substr($data, 0, 2).pack("v", $limit-4).substr($data, 4, $limit - 4);
+
+ $header = pack("vv", $record, $limit); // Headers for continue records
+
+ // Retrieve chunks of 2080/8224 bytes +4 for the header.
+ for($i = $limit; $i < strlen($data) - $limit; $i += $limit)
+ {
+ $tmp .= $header;
+ $tmp .= substr($data, $i, $limit);
+ }
+
+ // Retrieve the last chunk of data
+ $header = pack("vv", $record, strlen($data) - $i);
+ $tmp .= $header;
+ $tmp .= substr($data,$i,strlen($data) - $i);
+
+ return($tmp);
+ }
+}
+?>
\ No newline at end of file diff --git a/vendor/write_excel/Format.php b/vendor/write_excel/Format.php new file mode 100644 index 0000000..9af415e --- /dev/null +++ b/vendor/write_excel/Format.php @@ -0,0 +1,637 @@ +<?php
+/*
+* Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
+*
+* The majority of this is _NOT_ my code. I simply ported it from the
+* PERL Spreadsheet::WriteExcel module.
+*
+* The author of the Spreadsheet::WriteExcel module is John McNamara
+* <jmcnamara@cpan.org>
+*
+* I _DO_ maintain this code, and John McNamara has nothing to do with the
+* porting of this code to PHP. Any questions directly related to this
+* class library should be directed to me.
+*
+* License Information:
+*
+* Spreadsheet::WriteExcel: A library for generating Excel Spreadsheets
+* Copyright (C) 2002 Xavier Noguer xnoguer@rezebra.com
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/**
+* Class for generating Excel XF records (formats)
+*
+* @author Xavier Noguer <xnoguer@rezebra.com>
+* @package Spreadsheet_WriteExcel
+*/
+
+class Format
+{
+ /**
+ * Constructor
+ *
+ * @access public
+ * @param integer $index the XF index for the format.
+ * @param array $properties array with properties to be set on initialization.
+ */
+ function __construct($index = 0,$properties = array())
+ {
+ $this->xf_index = $index;
+
+ $this->font_index = 0;
+ $this->font = 'Arial';
+ $this->size = 10;
+ $this->bold = 0x0190;
+ $this->_italic = 0;
+ $this->color = 0x7FFF;
+ $this->_underline = 0;
+ $this->font_strikeout = 0;
+ $this->font_outline = 0;
+ $this->font_shadow = 0;
+ $this->font_script = 0;
+ $this->font_family = 0;
+ $this->font_charset = 0;
+
+ $this->_num_format = 0;
+
+ $this->hidden = 0;
+ $this->locked = 1;
+
+ $this->_text_h_align = 0;
+ $this->_text_wrap = 0;
+ $this->text_v_align = 2;
+ $this->text_justlast = 0;
+ $this->rotation = 0;
+
+ $this->fg_color = 0x40;
+ $this->bg_color = 0x41;
+
+ $this->pattern = 0;
+
+ $this->bottom = 0;
+ $this->top = 0;
+ $this->left = 0;
+ $this->right = 0;
+
+ $this->bottom_color = 0x40;
+ $this->top_color = 0x40;
+ $this->left_color = 0x40;
+ $this->right_color = 0x40;
+
+ // Set properties passed to Workbook::add_format()
+ foreach($properties as $property => $value)
+ {
+ if(method_exists($this,"set_$property"))
+ {
+ $aux = 'set_'.$property;
+ $this->$aux($value);
+ }
+ }
+ }
+
+ /**
+ * Generate an Excel BIFF XF record (style or cell).
+ *
+ * @param string $style The type of the XF record ('style' or 'cell').
+ * @return string The XF record
+ */
+ function get_xf($style)
+ {
+ // Set the type of the XF record and some of the attributes.
+ if ($style == "style") {
+ $style = 0xFFF5;
+ }
+ else {
+ $style = $this->locked;
+ $style |= $this->hidden << 1;
+ }
+
+ // Flags to indicate if attributes have been set.
+ $atr_num = ($this->_num_format != 0)?1:0;
+ $atr_fnt = ($this->font_index != 0)?1:0;
+ $atr_alc = ($this->_text_wrap)?1:0;
+ $atr_bdr = ($this->bottom ||
+ $this->top ||
+ $this->left ||
+ $this->right)?1:0;
+ $atr_pat = (($this->fg_color != 0x40) ||
+ ($this->bg_color != 0x41) ||
+ $this->pattern)?1:0;
+ $atr_prot = 0;
+
+ // Zero the default border colour if the border has not been set.
+ if ($this->bottom == 0) {
+ $this->bottom_color = 0;
+ }
+ if ($this->top == 0) {
+ $this->top_color = 0;
+ }
+ if ($this->right == 0) {
+ $this->right_color = 0;
+ }
+ if ($this->left == 0) {
+ $this->left_color = 0;
+ }
+
+ $record = 0x00E0; // Record identifier
+ $length = 0x0010; // Number of bytes to follow
+
+ $ifnt = $this->font_index; // Index to FONT record
+ $ifmt = $this->_num_format; // Index to FORMAT record
+
+ $align = $this->_text_h_align; // Alignment
+ $align |= $this->_text_wrap << 3;
+ $align |= $this->text_v_align << 4;
+ $align |= $this->text_justlast << 7;
+ $align |= $this->rotation << 8;
+ $align |= $atr_num << 10;
+ $align |= $atr_fnt << 11;
+ $align |= $atr_alc << 12;
+ $align |= $atr_bdr << 13;
+ $align |= $atr_pat << 14;
+ $align |= $atr_prot << 15;
+
+ $icv = $this->fg_color; // fg and bg pattern colors
+ $icv |= $this->bg_color << 7;
+
+ $fill = $this->pattern; // Fill and border line style
+ $fill |= $this->bottom << 6;
+ $fill |= $this->bottom_color << 9;
+
+ $border1 = $this->top; // Border line style and color
+ $border1 |= $this->left << 3;
+ $border1 |= $this->right << 6;
+ $border1 |= $this->top_color << 9;
+
+ $border2 = $this->left_color; // Border color
+ $border2 |= $this->right_color << 7;
+
+ $header = pack("vv", $record, $length);
+ $data = pack("vvvvvvvv", $ifnt, $ifmt, $style, $align,
+ $icv, $fill,
+ $border1, $border2);
+ return($header.$data);
+ }
+
+ /**
+ * Generate an Excel BIFF FONT record.
+ *
+ * @see Workbook::_store_all_fonts()
+ * @return string The FONT record
+ */
+ function get_font()
+ {
+ $dyHeight = $this->size * 20; // Height of font (1/20 of a point)
+ $icv = $this->color; // Index to color palette
+ $bls = $this->bold; // Bold style
+ $sss = $this->font_script; // Superscript/subscript
+ $uls = $this->_underline; // Underline
+ $bFamily = $this->font_family; // Font family
+ $bCharSet = $this->font_charset; // Character set
+ $rgch = $this->font; // Font name
+
+ $cch = strlen($rgch); // Length of font name
+ $record = 0x31; // Record identifier
+ $length = 0x0F + $cch; // Record length
+ $reserved = 0x00; // Reserved
+ $grbit = 0x00; // Font attributes
+ if ($this->_italic) {
+ $grbit |= 0x02;
+ }
+ if ($this->font_strikeout) {
+ $grbit |= 0x08;
+ }
+ if ($this->font_outline) {
+ $grbit |= 0x10;
+ }
+ if ($this->font_shadow) {
+ $grbit |= 0x20;
+ }
+
+ $header = pack("vv", $record, $length);
+ $data = pack("vvvvvCCCCC", $dyHeight, $grbit, $icv, $bls,
+ $sss, $uls, $bFamily,
+ $bCharSet, $reserved, $cch);
+ return($header . $data. $this->font);
+ }
+
+ /**
+ * Returns a unique hash key for a font. Used by Workbook->_store_all_fonts()
+ *
+ * The elements that form the key are arranged to increase the probability of
+ * generating a unique key. Elements that hold a large range of numbers
+ * (eg. _color) are placed between two binary elements such as _italic
+ *
+ * @return string A key for this font
+ */
+ function get_font_key()
+ {
+ $key = "$this->font$this->size";
+ $key .= "$this->font_script$this->_underline";
+ $key .= "$this->font_strikeout$this->bold$this->font_outline";
+ $key .= "$this->font_family$this->font_charset";
+ $key .= "$this->font_shadow$this->color$this->_italic";
+ $key = str_replace(" ","_",$key);
+ return ($key);
+ }
+
+ /**
+ * Returns the index used by Worksheet->_XF()
+ *
+ * @return integer The index for the XF record
+ */
+ function get_xf_index()
+ {
+ return($this->xf_index);
+ }
+
+ /**
+ * Used in conjunction with the set_xxx_color methods to convert a color
+ * string into a number. Color range is 0..63 but we will restrict it
+ * to 8..63 to comply with Gnumeric. Colors 0..7 are repeated in 8..15.
+ *
+ * @param string $name_color name of the color (i.e.: 'blue', 'red', etc..). Optional.
+ * @return integer The color index
+ */
+ function _get_color($name_color = '')
+ {
+ $colors = array(
+ 'aqua' => 0x0F,
+ 'cyan' => 0x0F,
+ 'black' => 0x08,
+ 'blue' => 0x0C,
+ 'brown' => 0x10,
+ 'magenta' => 0x0E,
+ 'fuchsia' => 0x0E,
+ 'gray' => 0x17,
+ 'grey' => 0x17,
+ 'green' => 0x11,
+ 'lime' => 0x0B,
+ 'navy' => 0x12,
+ 'orange' => 0x35,
+ 'purple' => 0x14,
+ 'red' => 0x0A,
+ 'silver' => 0x16,
+ 'white' => 0x09,
+ 'yellow' => 0x0D
+ );
+
+ // Return the default color, 0x7FFF, if undef,
+ if($name_color == '') {
+ return(0x7FFF);
+ }
+
+ // or the color string converted to an integer,
+ if(isset($colors[$name_color])) {
+ return($colors[$name_color]);
+ }
+
+ // or the default color if string is unrecognised,
+ if(preg_match("/\D/",$name_color)) {
+ return(0x7FFF);
+ }
+
+ // or an index < 8 mapped into the correct range,
+ if($name_color < 8) {
+ return($name_color + 8);
+ }
+
+ // or the default color if arg is outside range,
+ if($name_color > 63) {
+ return(0x7FFF);
+ }
+
+ // or an integer in the valid range
+ return($name_color);
+ }
+
+ /**
+ * Set cell alignment.
+ *
+ * @access public
+ * @param string $location alignment for the cell ('left', 'right', etc...).
+ */
+ function set_align($location)
+ {
+ if (preg_match("/\d/",$location)) {
+ return; // Ignore numbers
+ }
+
+ $location = strtolower($location);
+
+ if ($location == 'left')
+ $this->_text_h_align = 1;
+ if ($location == 'centre')
+ $this->_text_h_align = 2;
+ if ($location == 'center')
+ $this->_text_h_align = 2;
+ if ($location == 'right')
+ $this->_text_h_align = 3;
+ if ($location == 'fill')
+ $this->_text_h_align = 4;
+ if ($location == 'justify')
+ $this->_text_h_align = 5;
+ if ($location == 'merge')
+ $this->_text_h_align = 6;
+ if ($location == 'equal_space') // For T.K.
+ $this->_text_h_align = 7;
+ if ($location == 'top')
+ $this->text_v_align = 0;
+ if ($location == 'vcentre')
+ $this->text_v_align = 1;
+ if ($location == 'vcenter')
+ $this->text_v_align = 1;
+ if ($location == 'bottom')
+ $this->text_v_align = 2;
+ if ($location == 'vjustify')
+ $this->text_v_align = 3;
+ if ($location == 'vequal_space') // For T.K.
+ $this->text_v_align = 4;
+ }
+
+ /**
+ * This is an alias for the unintuitive set_align('merge')
+ *
+ * @access public
+ */
+ function set_merge()
+ {
+ $this->set_align('merge');
+ }
+
+ /**
+ * Bold has a range 0x64..0x3E8.
+ * 0x190 is normal. 0x2BC is bold.
+ *
+ * @access public
+ * @param integer $weight Weight for the text, 0 maps to 0x190, 1 maps to 0x2BC.
+ It's Optional, default is 1 (bold).
+ */
+ function set_bold($weight = 1)
+ {
+ if($weight == 1) {
+ $weight = 0x2BC; // Bold text
+ }
+ if($weight == 0) {
+ $weight = 0x190; // Normal text
+ }
+ if($weight < 0x064) {
+ $weight = 0x190; // Lower bound
+ }
+ if($weight > 0x3E8) {
+ $weight = 0x190; // Upper bound
+ }
+ $this->bold = $weight;
+ }
+
+
+ /************************************
+ * FUNCTIONS FOR SETTING CELLS BORDERS
+ */
+
+ /**
+ * Sets the bottom border of the cell
+ *
+ * @access public
+ * @param integer $style style of the cell border. 1 => thin, 2 => thick.
+ */
+ function set_bottom($style)
+ {
+ $this->bottom = $style;
+ }
+
+ /**
+ * Sets the top border of the cell
+ *
+ * @access public
+ * @param integer $style style of the cell top border. 1 => thin, 2 => thick.
+ */
+ function set_top($style)
+ {
+ $this->top = $style;
+ }
+
+ /**
+ * Sets the left border of the cell
+ *
+ * @access public
+ * @param integer $style style of the cell left border. 1 => thin, 2 => thick.
+ */
+ function set_left($style)
+ {
+ $this->left = $style;
+ }
+
+ /**
+ * Sets the right border of the cell
+ *
+ * @access public
+ * @param integer $style style of the cell right border. 1 => thin, 2 => thick.
+ */
+ function set_right($style)
+ {
+ $this->right = $style;
+ }
+
+
+ /**
+ * Set cells borders to the same style
+ *
+ * @access public
+ * @param integer $style style to apply for all cell borders. 1 => thin, 2 => thick.
+ */
+ function set_border($style)
+ {
+ $this->set_bottom($style);
+ $this->set_top($style);
+ $this->set_left($style);
+ $this->set_right($style);
+ }
+
+
+ /*******************************************
+ * FUNCTIONS FOR SETTING CELLS BORDERS COLORS
+ */
+
+ /**
+ * Sets all the cell's borders to the same color
+ *
+ * @access public
+ * @param mixed $color The color we are setting. Either a string (like 'blue'),
+ * or an integer (like 0x41).
+ */
+ function set_border_color($color)
+ {
+ $this->set_bottom_color($color);
+ $this->set_top_color($color);
+ $this->set_left_color($color);
+ $this->set_right_color($color);
+ }
+
+ /**
+ * Sets the cell's bottom border color
+ *
+ * @access public
+ * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]).
+ */
+ function set_bottom_color($color)
+ {
+ $value = $this->_get_color($color);
+ $this->bottom_color = $value;
+ }
+
+ /**
+ * Sets the cell's top border color
+ *
+ * @access public
+ * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]).
+ */
+ function set_top_color($color)
+ {
+ $value = $this->_get_color($color);
+ $this->top_color = $value;
+ }
+
+ /**
+ * Sets the cell's left border color
+ *
+ * @access public
+ * @param mixed $color either a string (like 'blue'), or an integer (like 0x41).
+ */
+ function set_left_color($color)
+ {
+ $value = $this->_get_color($color);
+ $this->left_color = $value;
+ }
+
+ /**
+ * Sets the cell's right border color
+ *
+ * @access public
+ * @param mixed $color either a string (like 'blue'), or an integer (like 0x41).
+ */
+ function set_right_color($color)
+ {
+ $value = $this->_get_color($color);
+ $this->right_color = $value;
+ }
+
+
+ /**
+ * Sets the cell's foreground color
+ *
+ * @access public
+ * @param mixed $color either a string (like 'blue'), or an integer (like 0x41).
+ */
+ function set_fg_color($color)
+ {
+ $value = $this->_get_color($color);
+ $this->fg_color = $value;
+ }
+
+ /**
+ * Sets the cell's background color
+ *
+ * @access public
+ * @param mixed $color either a string (like 'blue'), or an integer (like 0x41).
+ */
+ function set_bg_color($color)
+ {
+ $value = $this->_get_color($color);
+ $this->bg_color = $value;
+ }
+
+ /**
+ * Sets the cell's color
+ *
+ * @access public
+ * @param mixed $color either a string (like 'blue'), or an integer (like 0x41).
+ */
+ function set_color($color)
+ {
+ $value = $this->_get_color($color);
+ $this->color = $value;
+ }
+
+ /**
+ * Sets the pattern attribute of a cell
+ *
+ * @access public
+ * @param integer $arg Optional. Defaults to 1.
+ */
+ function set_pattern($arg = 1)
+ {
+ $this->pattern = $arg;
+ }
+
+ /**
+ * Sets the underline of the text
+ *
+ * @access public
+ * @param integer $underline The value for underline. Possible values are:
+ * 1 => underline, 2 => double underline.
+ */
+ function set_underline($underline)
+ {
+ $this->_underline = $underline;
+ }
+
+ /**
+ * Sets the font style as italic
+ *
+ * @access public
+ */
+ function set_italic()
+ {
+ $this->_italic = 1;
+ }
+
+ /**
+ * Sets the font size
+ *
+ * @access public
+ * @param integer $size The font size (in pixels I think).
+ */
+ function set_size($size)
+ {
+ $this->size = $size;
+ }
+
+ /**
+ * Sets the num format
+ *
+ * @access public
+ * @param integer $num_format The num format.
+ */
+ function set_num_format($num_format)
+ {
+ $this->_num_format = $num_format;
+ }
+
+ /**
+ * Sets text wrapping
+ *
+ * @access public
+ * @param integer $text_wrap Optional. 0 => no text wrapping, 1 => text wrapping.
+ * Defaults to 1.
+ */
+ function set_text_wrap($text_wrap = 1)
+ {
+ $this->_text_wrap = $text_wrap;
+ }
+}
+?>
\ No newline at end of file diff --git a/vendor/write_excel/OLEwriter.php b/vendor/write_excel/OLEwriter.php new file mode 100644 index 0000000..17bdfd6 --- /dev/null +++ b/vendor/write_excel/OLEwriter.php @@ -0,0 +1,414 @@ +<?php
+/*
+* Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
+*
+* The majority of this is _NOT_ my code. I simply ported it from the
+* PERL Spreadsheet::WriteExcel module.
+*
+* The author of the Spreadsheet::WriteExcel module is John McNamara
+* <jmcnamara@cpan.org>
+*
+* I _DO_ maintain this code, and John McNamara has nothing to do with the
+* porting of this code to PHP. Any questions directly related to this
+* class library should be directed to me.
+*
+* License Information:
+*
+* Spreadsheet::WriteExcel: A library for generating Excel Spreadsheets
+* Copyright (C) 2002 Xavier Noguer xnoguer@rezebra.com
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/**
+* Class for creating OLE streams for Excel Spreadsheets
+*
+* @author Xavier Noguer <xnoguer@rezebra.com>
+* @package Spreadsheet_WriteExcel
+*/
+class OLEwriter
+{
+ /**
+ * Filename for the OLE stream
+ * @var string
+ * @see _initialize()
+ */
+ var $_OLEfilename;
+
+ /**
+ * Filehandle for the OLE stream
+ * @var resource
+ */
+ var $_filehandle;
+
+ /**
+ * Name of the temporal file in case OLE stream goes to stdout
+ * @var string
+ */
+ var $_tmp_filename;
+
+ /**
+ * Variable for preventing closing two times
+ * @var integer
+ */
+ var $_fileclosed;
+
+ /**
+ * Size of the data to be written to the OLE stream
+ * @var integer
+ */
+ var $_biffsize;
+
+ /**
+ * Real data size to be written to the OLE stream
+ * @var integer
+ */
+ var $_booksize;
+
+ /**
+ * Number of big blocks in the OLE stream
+ * @var integer
+ */
+ var $_big_blocks;
+
+ /**
+ * Number of list blocks in the OLE stream
+ * @var integer
+ */
+ var $_list_blocks;
+
+ /**
+ * Number of big blocks in the OLE stream
+ * @var integer
+ */
+ var $_root_start;
+
+ /**
+ * Class for creating an OLEwriter
+ *
+ * @param string $OLEfilename the name of the file for the OLE stream
+ */
+ function __construct($OLEfilename)
+ {
+ $this->_OLEfilename = $OLEfilename;
+ $this->_filehandle = "";
+ $this->_tmp_filename = "";
+ $this->_fileclosed = 0;
+ //$this->_size_allowed = 0;
+ $this->_biffsize = 0;
+ $this->_booksize = 0;
+ $this->_big_blocks = 0;
+ $this->_list_blocks = 0;
+ $this->_root_start = 0;
+ //$this->_block_count = 4;
+ $this->_initialize();
+ }
+
+/**
+* Check for a valid filename and store the filehandle.
+* Filehandle "-" writes to STDOUT
+*/
+ function _initialize()
+ {
+ $OLEfile = $this->_OLEfilename;
+
+ if(($OLEfile == '-') or ($OLEfile == ''))
+ {
+ $this->_tmp_filename = tempnam("/tmp", "OLEwriter");
+ $fh = fopen($this->_tmp_filename,"wb");
+ if ($fh == false) {
+ die("Can't create temporary file.");
+ }
+ }
+ else
+ {
+ // Create a new file, open for writing (in binmode)
+ $fh = fopen($OLEfile,"wb");
+ if ($fh == false) {
+ die("Can't open $OLEfile. It may be in use or protected.");
+ }
+ }
+
+ // Store filehandle
+ $this->_filehandle = $fh;
+ }
+
+
+ /**
+ * Set the size of the data to be written to the OLE stream.
+ * The maximun size comes from this:
+ * $big_blocks = (109 depot block x (128 -1 marker word)
+ * - (1 x end words)) = 13842
+ * $maxsize = $big_blocks * 512 bytes = 7087104
+ *
+ * @access public
+ * @see Workbook::store_OLE_file()
+ * @param integer $biffsize The size of the data to be written to the OLE stream
+ * @return integer 1 for success
+ */
+ function set_size($biffsize)
+ {
+ $maxsize = 7087104; // TODO: extend max size
+
+ if ($biffsize > $maxsize) {
+ die("Maximum file size, $maxsize, exceeded.");
+ }
+
+ $this->_biffsize = $biffsize;
+ // Set the min file size to 4k to avoid having to use small blocks
+ if ($biffsize > 4096) {
+ $this->_booksize = $biffsize;
+ }
+ else {
+ $this->_booksize = 4096;
+ }
+ //$this->_size_allowed = 1;
+ return(1);
+ }
+
+
+ /**
+ * Calculate various sizes needed for the OLE stream
+ */
+ function _calculate_sizes()
+ {
+ $datasize = $this->_booksize;
+ if ($datasize % 512 == 0) {
+ $this->_big_blocks = $datasize/512;
+ }
+ else {
+ $this->_big_blocks = floor($datasize/512) + 1;
+ }
+ // There are 127 list blocks and 1 marker blocks for each big block
+ // depot + 1 end of chain block
+ $this->_list_blocks = floor(($this->_big_blocks)/127) + 1;
+ $this->_root_start = $this->_big_blocks;
+ }
+
+ /**
+ * Write root entry, big block list and close the filehandle.
+ * This routine is used to explicitly close the open filehandle without
+ * having to wait for DESTROY.
+ *
+ * @access public
+ * @see Workbook::store_OLE_file()
+ */
+ function close()
+ {
+ //return if not $this->{_size_allowed};
+ $this->_write_padding();
+ $this->_write_property_storage();
+ $this->_write_big_block_depot();
+ // Close the filehandle
+ fclose($this->_filehandle);
+ if(($this->_OLEfilename == '-') or ($this->_OLEfilename == ''))
+ {
+ $fh = fopen($this->_tmp_filename, "rb");
+ if ($fh == false) {
+ die("Can't read temporary file.");
+ }
+ fpassthru($fh);
+ // Delete the temporary file.
+ @unlink($this->_tmp_filename);
+ }
+ $this->_fileclosed = 1;
+ }
+
+
+ /**
+ * Write BIFF data to OLE file.
+ *
+ * @param string $data string of bytes to be written
+ */
+ function write($data) //por ahora sólo a STDOUT
+ {
+ fwrite($this->_filehandle,$data,strlen($data));
+ }
+
+
+ /**
+ * Write OLE header block.
+ */
+ function write_header()
+ {
+ $this->_calculate_sizes();
+ $root_start = $this->_root_start;
+ $num_lists = $this->_list_blocks;
+ $id = pack("nnnn", 0xD0CF, 0x11E0, 0xA1B1, 0x1AE1);
+ $unknown1 = pack("VVVV", 0x00, 0x00, 0x00, 0x00);
+ $unknown2 = pack("vv", 0x3E, 0x03);
+ $unknown3 = pack("v", -2);
+ $unknown4 = pack("v", 0x09);
+ $unknown5 = pack("VVV", 0x06, 0x00, 0x00);
+ $num_bbd_blocks = pack("V", $num_lists);
+ $root_startblock = pack("V", $root_start);
+ $unknown6 = pack("VV", 0x00, 0x1000);
+ $sbd_startblock = pack("V", -2);
+ $unknown7 = pack("VVV", 0x00, -2 ,0x00);
+ $unused = pack("V", -1);
+
+ fwrite($this->_filehandle,$id);
+ fwrite($this->_filehandle,$unknown1);
+ fwrite($this->_filehandle,$unknown2);
+ fwrite($this->_filehandle,$unknown3);
+ fwrite($this->_filehandle,$unknown4);
+ fwrite($this->_filehandle,$unknown5);
+ fwrite($this->_filehandle,$num_bbd_blocks);
+ fwrite($this->_filehandle,$root_startblock);
+ fwrite($this->_filehandle,$unknown6);
+ fwrite($this->_filehandle,$sbd_startblock);
+ fwrite($this->_filehandle,$unknown7);
+
+ for($i=1; $i <= $num_lists; $i++)
+ {
+ $root_start++;
+ fwrite($this->_filehandle,pack("V",$root_start));
+ }
+ for($i = $num_lists; $i <=108; $i++)
+ {
+ fwrite($this->_filehandle,$unused);
+ }
+ }
+
+
+ /**
+ * Write big block depot.
+ */
+ function _write_big_block_depot()
+ {
+ $num_blocks = $this->_big_blocks;
+ $num_lists = $this->_list_blocks;
+ $total_blocks = $num_lists *128;
+ $used_blocks = $num_blocks + $num_lists +2;
+
+ $marker = pack("V", -3);
+ $end_of_chain = pack("V", -2);
+ $unused = pack("V", -1);
+
+ for($i=1; $i < $num_blocks; $i++)
+ {
+ fwrite($this->_filehandle,pack("V",$i));
+ }
+ fwrite($this->_filehandle,$end_of_chain);
+ fwrite($this->_filehandle,$end_of_chain);
+ for($i=0; $i < $num_lists; $i++)
+ {
+ fwrite($this->_filehandle,$marker);
+ }
+ for($i=$used_blocks; $i <= $total_blocks; $i++)
+ {
+ fwrite($this->_filehandle,$unused);
+ }
+ }
+
+/**
+* Write property storage. TODO: add summary sheets
+*/
+ function _write_property_storage()
+ {
+ //$rootsize = -2;
+ /*************** name type dir start size */
+ $this->_write_pps("Root Entry", 0x05, 1, -2, 0x00);
+ $this->_write_pps("Book", 0x02, -1, 0x00, $this->_booksize);
+ $this->_write_pps('', 0x00, -1, 0x00, 0x0000);
+ $this->_write_pps('', 0x00, -1, 0x00, 0x0000);
+ }
+
+/**
+* Write property sheet in property storage
+*
+* @param string $name name of the property storage.
+* @param integer $type type of the property storage.
+* @param integer $dir dir of the property storage.
+* @param integer $start start of the property storage.
+* @param integer $size size of the property storage.
+* @access private
+*/
+ function _write_pps($name,$type,$dir,$start,$size)
+ {
+ $length = 0;
+ $rawname = '';
+
+ if ($name != '')
+ {
+ $name = $name . "\0";
+ for($i=0;$i<strlen($name);$i++)
+ {
+ // Simulate a Unicode string
+ $rawname .= pack("H*",dechex(ord($name{$i}))).pack("C",0);
+ }
+ $length = strlen($name) * 2;
+ }
+
+ $zero = pack("C", 0);
+ $pps_sizeofname = pack("v", $length); // 0x40
+ $pps_type = pack("v", $type); // 0x42
+ $pps_prev = pack("V", -1); // 0x44
+ $pps_next = pack("V", -1); // 0x48
+ $pps_dir = pack("V", $dir); // 0x4c
+
+ $unknown1 = pack("V", 0);
+
+ $pps_ts1s = pack("V", 0); // 0x64
+ $pps_ts1d = pack("V", 0); // 0x68
+ $pps_ts2s = pack("V", 0); // 0x6c
+ $pps_ts2d = pack("V", 0); // 0x70
+ $pps_sb = pack("V", $start); // 0x74
+ $pps_size = pack("V", $size); // 0x78
+
+
+ fwrite($this->_filehandle,$rawname);
+ for($i=0; $i < (64 -$length); $i++) {
+ fwrite($this->_filehandle,$zero);
+ }
+ fwrite($this->_filehandle,$pps_sizeofname);
+ fwrite($this->_filehandle,$pps_type);
+ fwrite($this->_filehandle,$pps_prev);
+ fwrite($this->_filehandle,$pps_next);
+ fwrite($this->_filehandle,$pps_dir);
+ for($i=0; $i < 5; $i++) {
+ fwrite($this->_filehandle,$unknown1);
+ }
+ fwrite($this->_filehandle,$pps_ts1s);
+ fwrite($this->_filehandle,$pps_ts1d);
+ fwrite($this->_filehandle,$pps_ts2d);
+ fwrite($this->_filehandle,$pps_ts2d);
+ fwrite($this->_filehandle,$pps_sb);
+ fwrite($this->_filehandle,$pps_size);
+ fwrite($this->_filehandle,$unknown1);
+ }
+
+ /**
+ * Pad the end of the file
+ */
+ function _write_padding()
+ {
+ $biffsize = $this->_biffsize;
+ if ($biffsize < 4096) {
+ $min_size = 4096;
+ }
+ else {
+ $min_size = 512;
+ }
+ if ($biffsize % $min_size != 0)
+ {
+ $padding = $min_size - ($biffsize % $min_size);
+ for($i=0; $i < $padding; $i++) {
+ fwrite($this->_filehandle,"\0");
+ }
+ }
+ }
+}
+?>
\ No newline at end of file diff --git a/vendor/write_excel/Parser.php b/vendor/write_excel/Parser.php new file mode 100644 index 0000000..1b7030e --- /dev/null +++ b/vendor/write_excel/Parser.php @@ -0,0 +1,996 @@ +<?php +/** +* Class for parsing Excel formulas +* +* License Information: +* +* Spreadsheet::WriteExcel: A library for generating Excel Spreadsheets +* Copyright (C) 2002 Xavier Noguer xnoguer@rezebra.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/** +* @const ADD token identifier for character "+" +*/ +define('ADD',"+"); + +/** +* @const SUB token identifier for character "-" +*/ +define('SUB',"-"); + +/** +* @const EQUAL token identifier for character "=" +*/ +define('EQUAL',"="); + +/** +* @const MUL token identifier for character "*" +*/ +define('MUL',"*"); + +/** +* @const DIV token identifier for character "/" +*/ +define('DIV',"/"); + +/** +* @const OPEN token identifier for character "(" +*/ +define('OPEN',"("); + +/** +* @const CLOSE token identifier for character ")" +*/ +define('CLOSE',")"); + +/** +* @const COMA token identifier for character "," +*/ +define('COMA',","); + +/** +* Class for parsing Excel formulas +* +* @author Xavier Noguer <xnoguer@rezebra.com> +* @package Spreadsheet_WriteExcel +*/ +class Parser + { +/** +* The class constructor +* +* @param integer $byte_order The byte order (Little endian or Big endian) of the architecture + (optional). 1 => big endian, 0 (default) => little endian. +*/ + function __construct($byte_order = 0) + { + $this->_current_char = 0; // The index of the character we are currently looking at. + $this->_current_token = ''; // The token we are working on. + $this->_formula = ""; // The formula to parse. + $this->_lookahead = ''; // The character ahead of the current char. + $this->_parse_tree = ''; // The parse tree to be generated. + $this->_initialize_hashes(); // Initialize the hashes: ptg's and function's ptg's + $this->_byte_order = $byte_order; // Little Endian or Big Endian + $this->_func_args = 0; // Number of arguments for the current function + $this->_volatile = 0; + } + +/** +* Initialize the ptg and function hashes. +*/ + function _initialize_hashes() + { + // The Excel ptg indices + $this->ptg = array( + 'ptgExp' => 0x01, + 'ptgTbl' => 0x02, + 'ptgAdd' => 0x03, + 'ptgSub' => 0x04, + 'ptgMul' => 0x05, + 'ptgDiv' => 0x06, + 'ptgPower' => 0x07, + 'ptgConcat' => 0x08, + 'ptgLT' => 0x09, + 'ptgLE' => 0x0A, + 'ptgEQ' => 0x0B, + 'ptgGE' => 0x0C, + 'ptgGT' => 0x0D, + 'ptgNE' => 0x0E, + 'ptgIsect' => 0x0F, + 'ptgUnion' => 0x10, + 'ptgRange' => 0x11, + 'ptgUplus' => 0x12, + 'ptgUminus' => 0x13, + 'ptgPercent' => 0x14, + 'ptgParen' => 0x15, + 'ptgMissArg' => 0x16, + 'ptgStr' => 0x17, + 'ptgAttr' => 0x19, + 'ptgSheet' => 0x1A, + 'ptgEndSheet' => 0x1B, + 'ptgErr' => 0x1C, + 'ptgBool' => 0x1D, + 'ptgInt' => 0x1E, + 'ptgNum' => 0x1F, + 'ptgArray' => 0x20, + 'ptgFunc' => 0x21, + 'ptgFuncVar' => 0x22, + 'ptgName' => 0x23, + 'ptgRef' => 0x24, + 'ptgArea' => 0x25, + 'ptgMemArea' => 0x26, + 'ptgMemErr' => 0x27, + 'ptgMemNoMem' => 0x28, + 'ptgMemFunc' => 0x29, + 'ptgRefErr' => 0x2A, + 'ptgAreaErr' => 0x2B, + 'ptgRefN' => 0x2C, + 'ptgAreaN' => 0x2D, + 'ptgMemAreaN' => 0x2E, + 'ptgMemNoMemN' => 0x2F, + 'ptgNameX' => 0x39, + 'ptgRef3d' => 0x3A, + 'ptgArea3d' => 0x3B, + 'ptgRefErr3d' => 0x3C, + 'ptgAreaErr3d' => 0x3D, + 'ptgArrayV' => 0x40, + 'ptgFuncV' => 0x41, + 'ptgFuncVarV' => 0x42, + 'ptgNameV' => 0x43, + 'ptgRefV' => 0x44, + 'ptgAreaV' => 0x45, + 'ptgMemAreaV' => 0x46, + 'ptgMemErrV' => 0x47, + 'ptgMemNoMemV' => 0x48, + 'ptgMemFuncV' => 0x49, + 'ptgRefErrV' => 0x4A, + 'ptgAreaErrV' => 0x4B, + 'ptgRefNV' => 0x4C, + 'ptgAreaNV' => 0x4D, + 'ptgMemAreaNV' => 0x4E, + 'ptgMemNoMemN' => 0x4F, + 'ptgFuncCEV' => 0x58, + 'ptgNameXV' => 0x59, + 'ptgRef3dV' => 0x5A, + 'ptgArea3dV' => 0x5B, + 'ptgRefErr3dV' => 0x5C, + 'ptgAreaErr3d' => 0x5D, + 'ptgArrayA' => 0x60, + 'ptgFuncA' => 0x61, + 'ptgFuncVarA' => 0x62, + 'ptgNameA' => 0x63, + 'ptgRefA' => 0x64, + 'ptgAreaA' => 0x65, + 'ptgMemAreaA' => 0x66, + 'ptgMemErrA' => 0x67, + 'ptgMemNoMemA' => 0x68, + 'ptgMemFuncA' => 0x69, + 'ptgRefErrA' => 0x6A, + 'ptgAreaErrA' => 0x6B, + 'ptgRefNA' => 0x6C, + 'ptgAreaNA' => 0x6D, + 'ptgMemAreaNA' => 0x6E, + 'ptgMemNoMemN' => 0x6F, + 'ptgFuncCEA' => 0x78, + 'ptgNameXA' => 0x79, + 'ptgRef3dA' => 0x7A, + 'ptgArea3dA' => 0x7B, + 'ptgRefErr3dA' => 0x7C, + 'ptgAreaErr3d' => 0x7D + ); + + // Thanks to Michael Meeks and Gnumeric for the initial arg values. + // + // The following hash was generated by "function_locale.pl" in the distro. + // Refer to function_locale.pl for non-English function names. + // + // The array elements are as follow: + // ptg: The Excel function ptg code. + // args: The number of arguments that the function takes: + // >=0 is a fixed number of arguments. + // -1 is a variable number of arguments. + // class: The reference, value or array class of the function args. + // vol: The function is volatile. + // + $this->_functions = array( + // function ptg args class vol + 'COUNT' => array( 0, -1, 0, 0 ), + 'IF' => array( 1, -1, 1, 0 ), + 'ISNA' => array( 2, 1, 1, 0 ), + 'ISERROR' => array( 3, 1, 1, 0 ), + 'SUM' => array( 4, -1, 0, 0 ), + 'AVERAGE' => array( 5, -1, 0, 0 ), + 'MIN' => array( 6, -1, 0, 0 ), + 'MAX' => array( 7, -1, 0, 0 ), + 'ROW' => array( 8, -1, 0, 0 ), + 'COLUMN' => array( 9, -1, 0, 0 ), + 'NA' => array( 10, 0, 0, 0 ), + 'NPV' => array( 11, -1, 1, 0 ), + 'STDEV' => array( 12, -1, 0, 0 ), + 'DOLLAR' => array( 13, -1, 1, 0 ), + 'FIXED' => array( 14, -1, 1, 0 ), + 'SIN' => array( 15, 1, 1, 0 ), + 'COS' => array( 16, 1, 1, 0 ), + 'TAN' => array( 17, 1, 1, 0 ), + 'ATAN' => array( 18, 1, 1, 0 ), + 'PI' => array( 19, 0, 1, 0 ), + 'SQRT' => array( 20, 1, 1, 0 ), + 'EXP' => array( 21, 1, 1, 0 ), + 'LN' => array( 22, 1, 1, 0 ), + 'LOG10' => array( 23, 1, 1, 0 ), + 'ABS' => array( 24, 1, 1, 0 ), + 'INT' => array( 25, 1, 1, 0 ), + 'SIGN' => array( 26, 1, 1, 0 ), + 'ROUND' => array( 27, 2, 1, 0 ), + 'LOOKUP' => array( 28, -1, 0, 0 ), + 'INDEX' => array( 29, -1, 0, 1 ), + 'REPT' => array( 30, 2, 1, 0 ), + 'MID' => array( 31, 3, 1, 0 ), + 'LEN' => array( 32, 1, 1, 0 ), + 'VALUE' => array( 33, 1, 1, 0 ), + 'TRUE' => array( 34, 0, 1, 0 ), + 'FALSE' => array( 35, 0, 1, 0 ), + 'AND' => array( 36, -1, 0, 0 ), + 'OR' => array( 37, -1, 0, 0 ), + 'NOT' => array( 38, 1, 1, 0 ), + 'MOD' => array( 39, 2, 1, 0 ), + 'DCOUNT' => array( 40, 3, 0, 0 ), + 'DSUM' => array( 41, 3, 0, 0 ), + 'DAVERAGE' => array( 42, 3, 0, 0 ), + 'DMIN' => array( 43, 3, 0, 0 ), + 'DMAX' => array( 44, 3, 0, 0 ), + 'DSTDEV' => array( 45, 3, 0, 0 ), + 'VAR' => array( 46, -1, 0, 0 ), + 'DVAR' => array( 47, 3, 0, 0 ), + 'TEXT' => array( 48, 2, 1, 0 ), + 'LINEST' => array( 49, -1, 0, 0 ), + 'TREND' => array( 50, -1, 0, 0 ), + 'LOGEST' => array( 51, -1, 0, 0 ), + 'GROWTH' => array( 52, -1, 0, 0 ), + 'PV' => array( 56, -1, 1, 0 ), + 'FV' => array( 57, -1, 1, 0 ), + 'NPER' => array( 58, -1, 1, 0 ), + 'PMT' => array( 59, -1, 1, 0 ), + 'RATE' => array( 60, -1, 1, 0 ), + 'MIRR' => array( 61, 3, 0, 0 ), + 'IRR' => array( 62, -1, 0, 0 ), + 'RAND' => array( 63, 0, 1, 1 ), + 'MATCH' => array( 64, -1, 0, 0 ), + 'DATE' => array( 65, 3, 1, 0 ), + 'TIME' => array( 66, 3, 1, 0 ), + 'DAY' => array( 67, 1, 1, 0 ), + 'MONTH' => array( 68, 1, 1, 0 ), + 'YEAR' => array( 69, 1, 1, 0 ), + 'WEEKDAY' => array( 70, -1, 1, 0 ), + 'HOUR' => array( 71, 1, 1, 0 ), + 'MINUTE' => array( 72, 1, 1, 0 ), + 'SECOND' => array( 73, 1, 1, 0 ), + 'NOW' => array( 74, 0, 1, 1 ), + 'AREAS' => array( 75, 1, 0, 1 ), + 'ROWS' => array( 76, 1, 0, 1 ), + 'COLUMNS' => array( 77, 1, 0, 1 ), + 'OFFSET' => array( 78, -1, 0, 1 ), + 'SEARCH' => array( 82, -1, 1, 0 ), + 'TRANSPOSE' => array( 83, 1, 1, 0 ), + 'TYPE' => array( 86, 1, 1, 0 ), + 'ATAN2' => array( 97, 2, 1, 0 ), + 'ASIN' => array( 98, 1, 1, 0 ), + 'ACOS' => array( 99, 1, 1, 0 ), + 'CHOOSE' => array( 100, -1, 1, 0 ), + 'HLOOKUP' => array( 101, -1, 0, 0 ), + 'VLOOKUP' => array( 102, -1, 0, 0 ), + 'ISREF' => array( 105, 1, 0, 0 ), + 'LOG' => array( 109, -1, 1, 0 ), + 'CHAR' => array( 111, 1, 1, 0 ), + 'LOWER' => array( 112, 1, 1, 0 ), + 'UPPER' => array( 113, 1, 1, 0 ), + 'PROPER' => array( 114, 1, 1, 0 ), + 'LEFT' => array( 115, -1, 1, 0 ), + 'RIGHT' => array( 116, -1, 1, 0 ), + 'EXACT' => array( 117, 2, 1, 0 ), + 'TRIM' => array( 118, 1, 1, 0 ), + 'REPLACE' => array( 119, 4, 1, 0 ), + 'SUBSTITUTE' => array( 120, -1, 1, 0 ), + 'CODE' => array( 121, 1, 1, 0 ), + 'FIND' => array( 124, -1, 1, 0 ), + 'CELL' => array( 125, -1, 0, 1 ), + 'ISERR' => array( 126, 1, 1, 0 ), + 'ISTEXT' => array( 127, 1, 1, 0 ), + 'ISNUMBER' => array( 128, 1, 1, 0 ), + 'ISBLANK' => array( 129, 1, 1, 0 ), + 'T' => array( 130, 1, 0, 0 ), + 'N' => array( 131, 1, 0, 0 ), + 'DATEVALUE' => array( 140, 1, 1, 0 ), + 'TIMEVALUE' => array( 141, 1, 1, 0 ), + 'SLN' => array( 142, 3, 1, 0 ), + 'SYD' => array( 143, 4, 1, 0 ), + 'DDB' => array( 144, -1, 1, 0 ), + 'INDIRECT' => array( 148, -1, 1, 1 ), + 'CALL' => array( 150, -1, 1, 0 ), + 'CLEAN' => array( 162, 1, 1, 0 ), + 'MDETERM' => array( 163, 1, 2, 0 ), + 'MINVERSE' => array( 164, 1, 2, 0 ), + 'MMULT' => array( 165, 2, 2, 0 ), + 'IPMT' => array( 167, -1, 1, 0 ), + 'PPMT' => array( 168, -1, 1, 0 ), + 'COUNTA' => array( 169, -1, 0, 0 ), + 'PRODUCT' => array( 183, -1, 0, 0 ), + 'FACT' => array( 184, 1, 1, 0 ), + 'DPRODUCT' => array( 189, 3, 0, 0 ), + 'ISNONTEXT' => array( 190, 1, 1, 0 ), + 'STDEVP' => array( 193, -1, 0, 0 ), + 'VARP' => array( 194, -1, 0, 0 ), + 'DSTDEVP' => array( 195, 3, 0, 0 ), + 'DVARP' => array( 196, 3, 0, 0 ), + 'TRUNC' => array( 197, -1, 1, 0 ), + 'ISLOGICAL' => array( 198, 1, 1, 0 ), + 'DCOUNTA' => array( 199, 3, 0, 0 ), + 'ROUNDUP' => array( 212, 2, 1, 0 ), + 'ROUNDDOWN' => array( 213, 2, 1, 0 ), + 'RANK' => array( 216, -1, 0, 0 ), + 'ADDRESS' => array( 219, -1, 1, 0 ), + 'DAYS360' => array( 220, -1, 1, 0 ), + 'TODAY' => array( 221, 0, 1, 1 ), + 'VDB' => array( 222, -1, 1, 0 ), + 'MEDIAN' => array( 227, -1, 0, 0 ), + 'SUMPRODUCT' => array( 228, -1, 2, 0 ), + 'SINH' => array( 229, 1, 1, 0 ), + 'COSH' => array( 230, 1, 1, 0 ), + 'TANH' => array( 231, 1, 1, 0 ), + 'ASINH' => array( 232, 1, 1, 0 ), + 'ACOSH' => array( 233, 1, 1, 0 ), + 'ATANH' => array( 234, 1, 1, 0 ), + 'DGET' => array( 235, 3, 0, 0 ), + 'INFO' => array( 244, 1, 1, 1 ), + 'DB' => array( 247, -1, 1, 0 ), + 'FREQUENCY' => array( 252, 2, 0, 0 ), + 'ERROR.TYPE' => array( 261, 1, 1, 0 ), + 'REGISTER.ID' => array( 267, -1, 1, 0 ), + 'AVEDEV' => array( 269, -1, 0, 0 ), + 'BETADIST' => array( 270, -1, 1, 0 ), + 'GAMMALN' => array( 271, 1, 1, 0 ), + 'BETAINV' => array( 272, -1, 1, 0 ), + 'BINOMDIST' => array( 273, 4, 1, 0 ), + 'CHIDIST' => array( 274, 2, 1, 0 ), + 'CHIINV' => array( 275, 2, 1, 0 ), + 'COMBIN' => array( 276, 2, 1, 0 ), + 'CONFIDENCE' => array( 277, 3, 1, 0 ), + 'CRITBINOM' => array( 278, 3, 1, 0 ), + 'EVEN' => array( 279, 1, 1, 0 ), + 'EXPONDIST' => array( 280, 3, 1, 0 ), + 'FDIST' => array( 281, 3, 1, 0 ), + 'FINV' => array( 282, 3, 1, 0 ), + 'FISHER' => array( 283, 1, 1, 0 ), + 'FISHERINV' => array( 284, 1, 1, 0 ), + 'FLOOR' => array( 285, 2, 1, 0 ), + 'GAMMADIST' => array( 286, 4, 1, 0 ), + 'GAMMAINV' => array( 287, 3, 1, 0 ), + 'CEILING' => array( 288, 2, 1, 0 ), + 'HYPGEOMDIST' => array( 289, 4, 1, 0 ), + 'LOGNORMDIST' => array( 290, 3, 1, 0 ), + 'LOGINV' => array( 291, 3, 1, 0 ), + 'NEGBINOMDIST' => array( 292, 3, 1, 0 ), + 'NORMDIST' => array( 293, 4, 1, 0 ), + 'NORMSDIST' => array( 294, 1, 1, 0 ), + 'NORMINV' => array( 295, 3, 1, 0 ), + 'NORMSINV' => array( 296, 1, 1, 0 ), + 'STANDARDIZE' => array( 297, 3, 1, 0 ), + 'ODD' => array( 298, 1, 1, 0 ), + 'PERMUT' => array( 299, 2, 1, 0 ), + 'POISSON' => array( 300, 3, 1, 0 ), + 'TDIST' => array( 301, 3, 1, 0 ), + 'WEIBULL' => array( 302, 4, 1, 0 ), + 'SUMXMY2' => array( 303, 2, 2, 0 ), + 'SUMX2MY2' => array( 304, 2, 2, 0 ), + 'SUMX2PY2' => array( 305, 2, 2, 0 ), + 'CHITEST' => array( 306, 2, 2, 0 ), + 'CORREL' => array( 307, 2, 2, 0 ), + 'COVAR' => array( 308, 2, 2, 0 ), + 'FORECAST' => array( 309, 3, 2, 0 ), + 'FTEST' => array( 310, 2, 2, 0 ), + 'INTERCEPT' => array( 311, 2, 2, 0 ), + 'PEARSON' => array( 312, 2, 2, 0 ), + 'RSQ' => array( 313, 2, 2, 0 ), + 'STEYX' => array( 314, 2, 2, 0 ), + 'SLOPE' => array( 315, 2, 2, 0 ), + 'TTEST' => array( 316, 4, 2, 0 ), + 'PROB' => array( 317, -1, 2, 0 ), + 'DEVSQ' => array( 318, -1, 0, 0 ), + 'GEOMEAN' => array( 319, -1, 0, 0 ), + 'HARMEAN' => array( 320, -1, 0, 0 ), + 'SUMSQ' => array( 321, -1, 0, 0 ), + 'KURT' => array( 322, -1, 0, 0 ), + 'SKEW' => array( 323, -1, 0, 0 ), + 'ZTEST' => array( 324, -1, 0, 0 ), + 'LARGE' => array( 325, 2, 0, 0 ), + 'SMALL' => array( 326, 2, 0, 0 ), + 'QUARTILE' => array( 327, 2, 0, 0 ), + 'PERCENTILE' => array( 328, 2, 0, 0 ), + 'PERCENTRANK' => array( 329, -1, 0, 0 ), + 'MODE' => array( 330, -1, 2, 0 ), + 'TRIMMEAN' => array( 331, 2, 0, 0 ), + 'TINV' => array( 332, 2, 1, 0 ), + 'CONCATENATE' => array( 336, -1, 1, 0 ), + 'POWER' => array( 337, 2, 1, 0 ), + 'RADIANS' => array( 342, 1, 1, 0 ), + 'DEGREES' => array( 343, 1, 1, 0 ), + 'SUBTOTAL' => array( 344, -1, 0, 0 ), + 'SUMIF' => array( 345, -1, 0, 0 ), + 'COUNTIF' => array( 346, 2, 0, 0 ), + 'COUNTBLANK' => array( 347, 1, 0, 0 ), + 'ROMAN' => array( 354, -1, 1, 0 ) + ); + } + +/** +* Convert a token to the proper ptg value. +* +* @param mixed $token The token to convert. +*/ + function _convert($token) + { + if(is_numeric($token)) + { + return($this->_convert_number($token)); + } + // match references like A1 + elseif(preg_match("/^([A-I]?[A-Z])(\d+)$/",$token)) + { + return($this->_convert_ref2d($token)); + } + // match ranges like A1:B2 + elseif(preg_match("/^([A-I]?[A-Z])(\d+)\:([A-I]?[A-Z])(\d+)$/",$token)) + { + return($this->_convert_range2d($token)); + } + // match ranges like A1..B2 + elseif(preg_match("/^([A-I]?[A-Z])(\d+)\.\.([A-I]?[A-Z])(\d+)$/",$token)) + { + return($this->_convert_range2d($token)); + } + elseif(isset($this->ptg[$token])) // operators (including parentheses) + { + return(pack("C", $this->ptg[$token])); + } + elseif(preg_match("/[A-Z0-9�-�\.]+/",$token)) + { + return($this->_convert_function($token,$this->_func_args)); + } + // if it's an argument, ignore the token (the argument remains) + elseif($token == 'arg') + { + $this->_func_args++; + return(''); + } + die("Unknown token $token"); + } + +/** +* Convert a number token to ptgInt or ptgNum +* +* @param mixed $num an integer or double for conersion to its ptg value +*/ + function _convert_number($num) + { + // Integer in the range 0..2**16-1 + if ((preg_match("/^\d+$/",$num)) and ($num <= 65535)) { + return pack("Cv", $this->ptg['ptgInt'], $num); + } + else // A float + { + if($this->_byte_order) // if it's Big Endian + { + $num = strrev($num); + } + return pack("Cd", $this->ptg['ptgNum'], $num); + } + } + +/** +* Convert a function to a ptgFunc or ptgFuncVarV depending on the number of +* args that it takes. +* +* @param string $token The name of the function for convertion to ptg value. +* @param integer $num_args The number of arguments the function recieves. +*/ + function _convert_function($token, $num_args) + { + $this->_func_args = 0; // re initialize the number of arguments + $args = $this->_functions[$token][1]; + $volatile = $this->_functions[$token][3]; + + if($volatile) { + $this->_volatile = 1; + } + // Fixed number of args eg. TIME($i,$j,$k). + if ($args >= 0) + { + return(pack("Cv", $this->ptg['ptgFuncV'], $this->_functions[$token][0])); + } + // Variable number of args eg. SUM($i,$j,$k, ..). + if ($args == -1) { + return(pack("CCv", $this->ptg['ptgFuncVarV'], $num_args, $this->_functions[$token][0])); + } + } + +/** +* Convert an Excel range such as A1:D4 to a ptgRefV. +* +* @param string $range An Excel range in the A1:A2 or A1..A2 format. +*/ + function _convert_range2d($range) + { + $class = 2; // as far as I know, this is magick. + + // Split the range into 2 cell refs + if(preg_match("/^([A-I]?[A-Z])(\d+)\:([A-I]?[A-Z])(\d+)$/",$range)) { + list($cell1, $cell2) = explode(':', $range); + } + elseif(preg_match("/^([A-I]?[A-Z])(\d+)\.\.([A-I]?[A-Z])(\d+)$/",$range)) { + list($cell1, $cell2) = explode('\.\.', $range); + } + else { + die("Unknown range separator"); + } + + // Convert the cell references + list($row1, $col1) = $this->_cell_to_packed_rowcol($cell1); + list($row2, $col2) = $this->_cell_to_packed_rowcol($cell2); + + // The ptg value depends on the class of the ptg. + if ($class == 0) { + $ptgArea = pack("C", $this->ptg['ptgArea']); + } + elseif ($class == 1) { + $ptgArea = pack("C", $this->ptg['ptgAreaV']); + } + elseif ($class == 2) { + $ptgArea = pack("C", $this->ptg['ptgAreaA']); + } + else{ + die("Unknown class "); + } + + return($ptgArea . $row1 . $row2 . $col1. $col2); + } + +/** +* Convert an Excel reference such as A1, $B2, C$3 or $D$4 to a ptgRefV. +* +* @param string $cell An Excel cell reference +*/ + function _convert_ref2d($cell) + { + $class = 2; // as far as I know, this is magick. + + // Convert the cell reference + list($row, $col) = $this->_cell_to_packed_rowcol($cell); + + // The ptg value depends on the class of the ptg. + if ($class == 0) { + $ptgRef = pack("C", $this->ptg['ptgRef']); + } + elseif ($class == 1) { + $ptgRef = pack("C", $this->ptg['ptgRefV']); + } + elseif ($class == 2) { + $ptgRef = pack("C", $this->ptg['ptgRefA']); + } + else{ + die("Unknown class "); + } + return $ptgRef.$row.$col; + } + +/** +* pack() row and column into the required 3 byte format. +* +* @param string $cell The Excel cell reference to be packed +*/ + function _cell_to_packed_rowcol($cell) + { + list($row, $col, $row_rel, $col_rel) = $this->_cell_to_rowcol($cell); + if ($col >= 256) { + die("Column in: $cell greater than 255 "); + } + if ($row >= 16384) { + die("Row in: $cell greater than 16384 "); + } + + // Set the high bits to indicate if row or col are relative. + $row |= $col_rel << 14; + $row |= $row_rel << 15; + + $row = pack('v', $row); + $col = pack('C', $col); + + return (array($row, $col)); + } + +/** +* Convert an Excel cell reference such as A1 or $B2 or C$3 or $D$4 to a zero +* indexed row and column number. Also returns two boolean values to indicate +* whether the row or column are relative references. +* +* @param string $cell The Excel cell reference in A1 format. +*/ + function _cell_to_rowcol($cell) + { + preg_match('/(\$)?([A-I]?[A-Z])(\$)?(\d+)/',$cell,$match); + // return absolute column if there is a $ in the ref + $col_rel = empty($match[1]) ? 1 : 0; + $col_ref = $match[2]; + $row_rel = empty($match[3]) ? 1 : 0; + $row = $match[4]; + + // Convert base26 column string to a number. + $expn = strlen($col_ref) - 1; + $col = 0; + for($i=0; $i < strlen($col_ref); $i++) + { + $col += (ord($col_ref{$i}) - ord('A') + 1) * pow(26, $expn); + $expn--; + } + + // Convert 1-index to zero-index + $row--; + $col--; + + return(array($row, $col, $row_rel, $col_rel)); + } + +/** +* Advance to the next valid token. +*/ + function _advance() + { + $i = $this->_current_char; + // eat up white spaces + if($i < strlen($this->_formula)) + { + while($this->_formula{$i} == " ") + { + $i++; + } + if($i < strlen($this->_formula) - 1) + { + $this->_lookahead = $this->_formula{$i+1}; + } + $token = ""; + } + while($i < strlen($this->_formula)) + { + $token .= $this->_formula{$i}; + if($this->_match($token) != '') + { + if($i < strlen($this->_formula) - 1) + { + $this->_lookahead = $this->_formula{$i+1}; + } + $this->_current_char = $i + 1; + $this->_current_token = $token; + return(1); + } + $this->_lookahead = $this->_formula{$i+2}; + $i++; + } + //die("Lexical error ".$this->_current_char); + } + +/** +* Checks if it's a valid token. +* +* @param mixed $token The token to check. +*/ + function _match($token) + { + switch($token) + { + case ADD: + return($token); + break; + case SUB: + return($token); + break; + case MUL: + return($token); + break; + case DIV: + return($token); + break; + case OPEN: + return($token); + break; + case CLOSE: + return($token); + break; + case COMA: + return($token); + break; + default: + // if it's a reference + if(preg_match("/^[A-I]?[A-Z][0-9]+$/i", $token) and + !preg_match("/[0-9]/",$this->_lookahead) and + ($this->_lookahead != ':') and ($this->_lookahead != '.')) + { + return($token); + } + // if it's a range (A1:A2) + elseif(preg_match("/^[A-I]?[A-Z][0-9]+:[A-I]?[A-Z][0-9]+$/i", $token) and + !preg_match("/[0-9]/",$this->_lookahead)) + { + return($token); + } + // if it's a range (A1..A2) + elseif(preg_match("/^[A-I]?[A-Z][0-9]+\.\.[A-I]?[A-Z][0-9]+$/i", $token) and + !preg_match("/[0-9]/",$this->_lookahead)) + { + return($token); + } + elseif(is_numeric($token) and !is_numeric($token.$this->_lookahead)) + { + return($token); + } + // if it's a function call + elseif(preg_match("/^[A-Z0-9�-�\.]+$/i", $token) and ($this->_lookahead == "(")) + + { + return($token); + } + return ''; + } + } + +/** +* The parsing method. It parses a formula. +* +* @access public +* @param string $formula The formula to parse, without the initial equal sign (=). +*/ + function parse($formula) + { + $this->_current_char = 0; + $this->_formula = $formula; + $this->_lookahead = $formula{1}; + $this->_advance(); + $this->_parse_tree = $this->_expression(); + } + +/** +* It parses a expression. It assumes the following rule: +* Expr -> Term [("+" | "-") Term] +* +* @return mixed The parsed ptg'd tree +*/ + function _expression() + { + $result = $this->_term(); + while ($this->_current_token == ADD or $this->_current_token == SUB) + { + if ($this->_current_token == ADD) + { + $this->_advance(); + $result = $this->_create_tree('ptgAdd', $result, $this->_term()); + } + else + { + $this->_advance(); + $result = $this->_create_tree('ptgSub', $result, $this->_term()); + } + } + return $result; + } + +/** +* This function just introduces a ptgParen element in the tree, so that Excel +* doesn't get confused when working with a parenthesized formula afterwards. +* +* @see _fact +* @return mixed The parsed ptg'd tree +*/ + function _parenthesized_expression() + { + $result = $this->_create_tree('ptgParen', $this->_expression(), ''); + return($result); + } + +/** +* It parses a term. It assumes the following rule: +* Term -> Fact [("*" | "/") Fact] +* +* @return mixed The parsed ptg'd tree +*/ + function _term() + { + $result = $this->_fact(); + while ($this->_current_token == MUL || $this->_current_token == DIV) + { + if ($this->_current_token == MUL) + { + $this->_advance(); + $result = $this->_create_tree('ptgMul', $result, $this->_fact()); + } + else + { + $this->_advance(); + $result = $this->_create_tree('ptgDiv', $result, $this->_fact()); + } + } + return($result); + } + +/** +* It parses a factor. It assumes the following rule: +* Fact -> ( Expr ) +* | CellRef +* | CellRange +* | Number +* | Function +* +* @return mixed The parsed ptg'd tree +*/ + function _fact() + { + if ($this->_current_token == OPEN) + { + $this->_advance(); // eat the "(" + $result = $this->_parenthesized_expression();//$this->_expression(); + + if ($this->_current_token != CLOSE) { + die("')' token expected."); + } + $this->_advance(); // eat the ")" + return($result); + } + // if it's a reference + if (preg_match("/^[A-I]?[A-Z][0-9]+$/i", $this->_current_token)) + { + $result = $this->_create_tree($this->_current_token, '', ''); + $this->_advance(); + return($result); + } + // if it's a range + elseif (preg_match("/^[A-I]?[A-Z][0-9]+:[A-I]?[A-Z][0-9]+$/i", $this->_current_token) or + preg_match("/^[A-I]?[A-Z][0-9]+\.\.[A-I]?[A-Z][0-9]+$/i", $this->_current_token)) + { + $result = $this->_current_token; + $this->_advance(); + return($result); + } + elseif (is_numeric($this->_current_token)) + { + $result = $this->_create_tree($this->_current_token, '', ''); + $this->_advance(); + return($result); + } + // if it's a function call + elseif (preg_match("/^[A-Z0-9�-�\.]+$/i", $this->_current_token)) + { + $result = $this->_func(); + return($result); + } + die("Sintactic error: ".$this->_current_token.", lookahead: ". + $this->_lookahead.", current char: ".$this->_current_char); + } + +/** +* It parses a function call. It assumes the following rule: +* Func -> ( Expr [,Expr]* ) +* +*/ + function _func() + { + $num_args = 0; // number of arguments received + $function = $this->_current_token; + $this->_advance(); + $this->_advance(); // eat the "(" + while($this->_current_token != ')') + { + if($num_args > 0) + { + if($this->_current_token == COMA) { + $this->_advance(); // eat the "," + } + else { + die("Sintactic error: coma expected $num_args"); + } + $result = $this->_create_tree('arg', $result, $this->_expression()); + } + else { + $result = $this->_create_tree('arg', '', $this->_expression()); + } + $num_args++; + } + $args = $this->_functions[$function][1]; + // If fixed number of args eg. TIME($i,$j,$k). Check that the number of args is valid. + if (($args >= 0) and ($args != $num_args)) + { + die("Incorrect number of arguments in function $function() "); + } + + $result = $this->_create_tree($function, $result, ''); + $this->_advance(); // eat the ")" + return($result); + } + +/** +* Creates a tree. In fact an array which may have one or two arrays (sub-trees) +* as elements. +* +* @param mixed $value The value of this node. +* @param mixed $left The left array (sub-tree) or a final node. +* @param mixed $right The right array (sub-tree) or a final node. +*/ + function _create_tree($value, $left, $right) + { + return array('value' => $value, 'left' => $left, 'right' => $right); + } + +/** +* Builds a string containing the tree in reverse polish notation (What you +* would use in a HP calculator stack). +* The following tree: +* +* + +* / \ +* 2 3 +* +* produces: "23+" +* +* The following tree: +* +* + +* / \ +* 3 * +* / \ +* 6 A1 +* +* produces: "36A1*+" +* +* In fact all operands, functions, references, etc... are written as ptg's +* +* @access public +* @param array $tree The optional tree to convert. +*/ + function to_reverse_polish($tree = array()) + { + $polish = ""; // the string we are going to return + if (empty($tree)) // If it's the first call use _parse_tree + { + $tree = $this->_parse_tree; + } + if (is_array($tree['left'])) + { + $polish .= $this->to_reverse_polish($tree['left']); + } + elseif($tree['left'] != '') // It's a final node + { + $polish .= $this->_convert($tree['left']); //$tree['left']; + } + if (is_array($tree['right'])) + { + $polish .= $this->to_reverse_polish($tree['right']); + } + elseif($tree['right'] != '') // It's a final node + { + $polish .= $this->_convert($tree['right']); + } + $polish .= $this->_convert($tree['value']); + return $polish; + } + } +?> diff --git a/vendor/write_excel/Workbook.php b/vendor/write_excel/Workbook.php new file mode 100644 index 0000000..eff68c0 --- /dev/null +++ b/vendor/write_excel/Workbook.php @@ -0,0 +1,953 @@ +<?php
+/*
+* Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
+*
+* The majority of this is _NOT_ my code. I simply ported it from the
+* PERL Spreadsheet::WriteExcel module.
+*
+* The author of the Spreadsheet::WriteExcel module is John McNamara
+* <jmcnamara@cpan.org>
+*
+* I _DO_ maintain this code, and John McNamara has nothing to do with the
+* porting of this code to PHP. Any questions directly related to this
+* class library should be directed to me.
+*
+* License Information:
+*
+* Spreadsheet::WriteExcel: A library for generating Excel Spreadsheets
+* Copyright (C) 2002 Xavier Noguer xnoguer@rezebra.com
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+require_once('Format.php');
+require_once('OLEwriter.php');
+require_once('BIFFwriter.php');
+
+/**
+* Class for generating Excel Spreadsheets
+*
+* @author Xavier Noguer <xnoguer@rezebra.com>
+* @package Spreadsheet_WriteExcel
+*/
+
+class Workbook extends BIFFwriter
+{
+ /**
+ * Class constructor
+ *
+ * @param string filename for storing the workbook. "-" for writing to stdout.
+ */
+ function __construct($filename)
+ {
+ parent::__construct();
+
+ $this->_filename = $filename;
+ $this->parser = new Parser($this->_byte_order);
+ $this->_1904 = 0;
+ $this->activesheet = 0;
+ $this->firstsheet = 0;
+ $this->selected = 0;
+ $this->xf_index = 16; // 15 style XF's and 1 cell XF.
+ $this->_fileclosed = 0;
+ $this->_biffsize = 0;
+ $this->sheetname = "Sheet";
+ $this->tmp_format = new Format();
+ $this->worksheets = array();
+ $this->sheetnames = array();
+ $this->formats = array();
+ $this->palette = array();
+
+ // Add the default format for hyperlinks
+ $this->url_format =& $this->add_format(array('color' => 'blue', 'underline' => 1));
+
+ // Check for a filename
+ //if ($this->_filename == '') {
+ // die('Filename required by Spreadsheet::WriteExcel->new()');
+ //}
+
+ # Warn if tmpfiles can't be used.
+ //$this->tmpfile_warning();
+ $this->_set_palette_xl97();
+ }
+
+ /**
+ * Calls finalization methods and explicitly close the OLEwriter file
+ * handle.
+ */
+ function close()
+ {
+ if ($this->_fileclosed) { // Prevent close() from being called twice.
+ return;
+ }
+ $this->store_workbook();
+ $this->_fileclosed = 1;
+ }
+
+
+ /**
+ * An accessor for the _worksheets[] array
+ * Returns an array of the worksheet objects in a workbook
+ *
+ * @return array
+ */
+ function sheets()
+ {
+ return($this->worksheets());
+ }
+
+ /**
+ * An accessor for the _worksheets[] array.
+ *
+ * @return array
+ */
+ function worksheets()
+ {
+ return($this->worksheets);
+ }
+
+ /**
+ * Add a new worksheet to the Excel workbook.
+ * TODO: Add accessor for $this->{_sheetname} for international Excel versions.
+ *
+ * @access public
+ * @param string $name the optional name of the worksheet
+ * @return &object reference to a worksheet object
+ */
+ function &add_worksheet($name = '')
+ {
+ $index = count($this->worksheets);
+ $sheetname = $this->sheetname;
+
+ if($name == '') {
+ $name = $sheetname.($index+1);
+ }
+
+ // Check that sheetname is <= 31 chars (Excel limit).
+ if(strlen($name) > 31) {
+ die("Sheetname $name must be <= 31 chars");
+ }
+
+ // Check that the worksheet name doesn't already exist: a fatal Excel error.
+ for($i=0; $i < count($this->worksheets); $i++)
+ {
+ if($name == $this->worksheets[$i]->get_name()) {
+ die("Worksheet '$name' already exists");
+ }
+ }
+
+ $worksheet = new Worksheet($name,$index,$this->activesheet,
+ $this->firstsheet,$this->url_format,
+ $this->parser);
+ $this->worksheets[$index] = &$worksheet; // Store ref for iterator
+ $this->sheetnames[$index] = $name; // Store EXTERNSHEET names
+ //$this->parser->set_ext_sheet($name,$index); // Store names in Formula.php
+ return($worksheet);
+ }
+
+ /**
+ * DEPRECATED!! Use add_worksheet instead
+ *
+ * @access public
+ * @deprecated Use add_worksheet instead
+ * @param string $name the optional name of the worksheet
+ * @return &object reference to a worksheet object
+ */
+ function &addworksheet($name = '')
+ {
+ return($this->add_worksheet($name));
+ }
+
+ /**
+ * Add a new format to the Excel workbook. This adds an XF record and
+ * a FONT record. Also, pass any properties to the Format constructor.
+ *
+ * @access public
+ * @param array $properties array with properties for initializing the format (see Format.php)
+ * @return &object reference to an XF format
+ */
+ function &add_format($properties = array())
+ {
+ $format = new Format($this->xf_index,$properties);
+ $this->xf_index += 1;
+ $this->formats[] = &$format;
+ return($format);
+ }
+
+ /**
+ * DEPRECATED!! Use add_format instead
+ *
+ * @access public
+ * @deprecated Use add_format instead
+ * @param array $properties array with properties for initializing the format (see Format.php)
+ * @return &object reference to an XF format
+ */
+ function &addformat($properties = array())
+ {
+ return($this->add_format($properties));
+ }
+
+
+ /**
+ * Change the RGB components of the elements in the colour palette.
+ *
+ * @access public
+ * @param integer $index colour index
+ * @param integer $red red RGB value [0-255]
+ * @param integer $green green RGB value [0-255]
+ * @param integer $blue blue RGB value [0-255]
+ * @return integer The palette index for the custom color
+ */
+ function set_custom_color($index,$red,$green,$blue)
+ {
+ // Match a HTML #xxyyzz style parameter
+ /*if (defined $_[1] and $_[1] =~ /^#(\w\w)(\w\w)(\w\w)/ ) {
+ @_ = ($_[0], hex $1, hex $2, hex $3);
+ }*/
+
+ // Check that the colour index is the right range
+ if ($index < 8 or $index > 64) {
+ die("Color index $index outside range: 8 <= index <= 64");
+ }
+
+ // Check that the colour components are in the right range
+ if ( ($red < 0 or $red > 255) ||
+ ($green < 0 or $green > 255) ||
+ ($blue < 0 or $blue > 255) )
+ {
+ die("Color component outside range: 0 <= color <= 255");
+ }
+
+ $index -= 8; // Adjust colour index (wingless dragonfly)
+
+ // Set the RGB value
+ $this->palette[$index] = array($red, $green, $blue, 0);
+ return($index + 8);
+ }
+
+ /**
+ * Sets the colour palette to the Excel 97+ default.
+ */
+ function _set_palette_xl97()
+ {
+ $this->palette = array(
+ array(0x00, 0x00, 0x00, 0x00), // 8
+ array(0xff, 0xff, 0xff, 0x00), // 9
+ array(0xff, 0x00, 0x00, 0x00), // 10
+ array(0x00, 0xff, 0x00, 0x00), // 11
+ array(0x00, 0x00, 0xff, 0x00), // 12
+ array(0xff, 0xff, 0x00, 0x00), // 13
+ array(0xff, 0x00, 0xff, 0x00), // 14
+ array(0x00, 0xff, 0xff, 0x00), // 15
+ array(0x80, 0x00, 0x00, 0x00), // 16
+ array(0x00, 0x80, 0x00, 0x00), // 17
+ array(0x00, 0x00, 0x80, 0x00), // 18
+ array(0x80, 0x80, 0x00, 0x00), // 19
+ array(0x80, 0x00, 0x80, 0x00), // 20
+ array(0x00, 0x80, 0x80, 0x00), // 21
+ array(0xc0, 0xc0, 0xc0, 0x00), // 22
+ array(0x80, 0x80, 0x80, 0x00), // 23
+ array(0x99, 0x99, 0xff, 0x00), // 24
+ array(0x99, 0x33, 0x66, 0x00), // 25
+ array(0xff, 0xff, 0xcc, 0x00), // 26
+ array(0xcc, 0xff, 0xff, 0x00), // 27
+ array(0x66, 0x00, 0x66, 0x00), // 28
+ array(0xff, 0x80, 0x80, 0x00), // 29
+ array(0x00, 0x66, 0xcc, 0x00), // 30
+ array(0xcc, 0xcc, 0xff, 0x00), // 31
+ array(0x00, 0x00, 0x80, 0x00), // 32
+ array(0xff, 0x00, 0xff, 0x00), // 33
+ array(0xff, 0xff, 0x00, 0x00), // 34
+ array(0x00, 0xff, 0xff, 0x00), // 35
+ array(0x80, 0x00, 0x80, 0x00), // 36
+ array(0x80, 0x00, 0x00, 0x00), // 37
+ array(0x00, 0x80, 0x80, 0x00), // 38
+ array(0x00, 0x00, 0xff, 0x00), // 39
+ array(0x00, 0xcc, 0xff, 0x00), // 40
+ array(0xcc, 0xff, 0xff, 0x00), // 41
+ array(0xcc, 0xff, 0xcc, 0x00), // 42
+ array(0xff, 0xff, 0x99, 0x00), // 43
+ array(0x99, 0xcc, 0xff, 0x00), // 44
+ array(0xff, 0x99, 0xcc, 0x00), // 45
+ array(0xcc, 0x99, 0xff, 0x00), // 46
+ array(0xff, 0xcc, 0x99, 0x00), // 47
+ array(0x33, 0x66, 0xff, 0x00), // 48
+ array(0x33, 0xcc, 0xcc, 0x00), // 49
+ array(0x99, 0xcc, 0x00, 0x00), // 50
+ array(0xff, 0xcc, 0x00, 0x00), // 51
+ array(0xff, 0x99, 0x00, 0x00), // 52
+ array(0xff, 0x66, 0x00, 0x00), // 53
+ array(0x66, 0x66, 0x99, 0x00), // 54
+ array(0x96, 0x96, 0x96, 0x00), // 55
+ array(0x00, 0x33, 0x66, 0x00), // 56
+ array(0x33, 0x99, 0x66, 0x00), // 57
+ array(0x00, 0x33, 0x00, 0x00), // 58
+ array(0x33, 0x33, 0x00, 0x00), // 59
+ array(0x99, 0x33, 0x00, 0x00), // 60
+ array(0x99, 0x33, 0x66, 0x00), // 61
+ array(0x33, 0x33, 0x99, 0x00), // 62
+ array(0x33, 0x33, 0x33, 0x00), // 63
+ );
+ }
+
+
+ ###############################################################################
+ #
+ # _tmpfile_warning()
+ #
+ # Check that tmp files can be created for use in Worksheet.pm. A CGI, mod_perl
+ # or IIS might not have permission to create tmp files. The test is here rather
+ # than in Worksheet.pm so that only one warning is given.
+ #
+ /*sub _tmpfile_warning {
+
+ my $fh = IO::File->new_tmpfile();
+
+ if ((not defined $fh) && ($^W)) {
+ carp("Unable to create tmp files via IO::File->new_tmpfile(). " .
+ "Storing data in memory")
+ }
+ }*/
+
+ /**
+ * Assemble worksheets into a workbook and send the BIFF data to an OLE
+ * storage.
+ */
+ function store_workbook()
+ {
+ // Ensure that at least one worksheet has been selected.
+ if ($this->activesheet == 0) {
+ $this->worksheets[0]->selected = 1;
+ }
+
+ // Calculate the number of selected worksheet tabs and call the finalization
+ // methods for each worksheet
+ for($i=0; $i < count($this->worksheets); $i++)
+ {
+ if($this->worksheets[$i]->selected)
+ $this->selected++;
+ $this->worksheets[$i]->close($this->sheetnames);
+ }
+
+ // Add Workbook globals
+ $this->_store_bof(0x0005);
+ $this->_store_externs(); // For print area and repeat rows
+ $this->_store_names(); // For print area and repeat rows
+ $this->_store_window1();
+ $this->_store_1904();
+ $this->_store_all_fonts();
+ $this->_store_all_num_formats();
+ $this->_store_all_xfs();
+ $this->_store_all_styles();
+ $this->_store_palette();
+ $this->_calc_sheet_offsets();
+
+ // Add BOUNDSHEET records
+ for($i=0; $i < count($this->worksheets); $i++) {
+ $this->_store_boundsheet($this->worksheets[$i]->name,$this->worksheets[$i]->offset);
+ }
+
+ // End Workbook globals
+ $this->_store_eof();
+
+ // Store the workbook in an OLE container
+ $this->_store_OLE_file();
+ }
+
+ /**
+ * Store the workbook in an OLE container if the total size of the workbook data
+ * is less than ~ 7MB.
+ */
+ function _store_OLE_file()
+ {
+ $OLE = new OLEwriter($this->_filename);
+ // Write Worksheet data if data <~ 7MB
+ if ($OLE->set_size($this->_biffsize))
+ {
+ $OLE->write_header();
+ $OLE->write($this->_data);
+ foreach($this->worksheets as $sheet)
+ {
+ while ($tmp = $sheet->get_data()) {
+ $OLE->write($tmp);
+ }
+ }
+ }
+ $OLE->close();
+ }
+
+ /**
+ * Calculate offsets for Worksheet BOF records.
+ */
+ function _calc_sheet_offsets()
+ {
+ $BOF = 11;
+ $EOF = 4;
+ $offset = $this->_datasize;
+ for($i=0; $i < count($this->worksheets); $i++) {
+ $offset += $BOF + strlen($this->worksheets[$i]->name);
+ }
+ $offset += $EOF;
+ for($i=0; $i < count($this->worksheets); $i++) {
+ $this->worksheets[$i]->offset = $offset;
+ $offset += $this->worksheets[$i]->_datasize;
+ }
+ $this->_biffsize = $offset;
+ }
+
+ /**
+ * Store the Excel FONT records.
+ */
+ function _store_all_fonts()
+ {
+ // tmp_format is added by new(). We use this to write the default XF's
+ $format = $this->tmp_format;
+ $font = $format->get_font();
+
+ // Note: Fonts are 0-indexed. According to the SDK there is no index 4,
+ // so the following fonts are 0, 1, 2, 3, 5
+ //
+ for($i=1; $i <= 5; $i++){
+ $this->_append($font);
+ }
+
+ // Iterate through the XF objects and write a FONT record if it isn't the
+ // same as the default FONT and if it hasn't already been used.
+ //
+ $fonts = array();
+ $index = 6; // The first user defined FONT
+
+ $key = $format->get_font_key(); // The default font from _tmp_format
+ $fonts[$key] = 0; // Index of the default font
+
+ for($i=0; $i < count($this->formats); $i++) {
+ $key = $this->formats[$i]->get_font_key();
+ if (isset($fonts[$key])) {
+ // FONT has already been used
+ $this->formats[$i]->font_index = $fonts[$key];
+ }
+ else {
+ // Add a new FONT record
+ $fonts[$key] = $index;
+ $this->formats[$i]->font_index = $index;
+ $index++;
+ $font = $this->formats[$i]->get_font();
+ $this->_append($font);
+ }
+ }
+ }
+
+ /**
+ * Store user defined numerical formats i.e. FORMAT records
+ */
+ function _store_all_num_formats()
+ {
+ // Leaning num_format syndrome
+ $hash_num_formats = array();
+ $num_formats = array();
+ $index = 164;
+
+ // Iterate through the XF objects and write a FORMAT record if it isn't a
+ // built-in format type and if the FORMAT string hasn't already been used.
+ //
+ for($i=0; $i < count($this->formats); $i++)
+ {
+ $num_format = $this->formats[$i]->_num_format;
+
+ // Check if $num_format is an index to a built-in format.
+ // Also check for a string of zeros, which is a valid format string
+ // but would evaluate to zero.
+ //
+ if (!preg_match("/^0+\d/",$num_format))
+ {
+ if (preg_match("/^\d+$/",$num_format)) { // built-in format
+ continue;
+ }
+ }
+
+ if (isset($hash_num_formats[$num_format])) {
+ // FORMAT has already been used
+ $this->formats[$i]->_num_format = $hash_num_formats[$num_format];
+ }
+ else
+ {
+ // Add a new FORMAT
+ $hash_num_formats[$num_format] = $index;
+ $this->formats[$i]->_num_format = $index;
+ array_push($num_formats,$num_format);
+ $index++;
+ }
+ }
+
+ // Write the new FORMAT records starting from 0xA4
+ $index = 164;
+ foreach ($num_formats as $num_format)
+ {
+ $this->_store_num_format($num_format,$index);
+ $index++;
+ }
+ }
+
+ /**
+ * Write all XF records.
+ */
+ function _store_all_xfs()
+ {
+ // tmp_format is added by the constructor. We use this to write the default XF's
+ // The default font index is 0
+ //
+ $format = $this->tmp_format;
+ for ($i=0; $i <= 14; $i++)
+ {
+ $xf = $format->get_xf('style'); // Style XF
+ $this->_append($xf);
+ }
+
+ $xf = $format->get_xf('cell'); // Cell XF
+ $this->_append($xf);
+
+ // User defined XFs
+ for($i=0; $i < count($this->formats); $i++)
+ {
+ $xf = $this->formats[$i]->get_xf('cell');
+ $this->_append($xf);
+ }
+ }
+
+ /**
+ * Write all STYLE records.
+ */
+ function _store_all_styles()
+ {
+ $this->_store_style();
+ }
+
+ /**
+ * Write the EXTERNCOUNT and EXTERNSHEET records. These are used as indexes for
+ * the NAME records.
+ */
+ function _store_externs()
+ {
+ // Create EXTERNCOUNT with number of worksheets
+ $this->_store_externcount(count($this->worksheets));
+
+ // Create EXTERNSHEET for each worksheet
+ foreach ($this->sheetnames as $sheetname) {
+ $this->_store_externsheet($sheetname);
+ }
+ }
+
+ /**
+ * Write the NAME record to define the print area and the repeat rows and cols.
+ */
+ function _store_names()
+ {
+ // Create the print area NAME records
+ foreach ($this->worksheets as $worksheet)
+ {
+ // Write a Name record if the print area has been defined
+ if (isset($worksheet->_print_rowmin))
+ {
+ $this->store_name_short(
+ $worksheet->index,
+ 0x06, // NAME type
+ $worksheet->_print_rowmin,
+ $worksheet->_print_rowmax,
+ $worksheet->_print_colmin,
+ $worksheet->_print_colmax
+ );
+ }
+ }
+
+ // Create the print title NAME records
+ foreach ($this->worksheets as $worksheet)
+ {
+ $rowmin = $worksheet->_title_rowmin;
+ $rowmax = $worksheet->_title_rowmax;
+ $colmin = $worksheet->_title_colmin;
+ $colmax = $worksheet->_title_colmax;
+
+ // Determine if row + col, row, col or nothing has been defined
+ // and write the appropriate record
+ //
+ if (isset($rowmin) && isset($colmin))
+ {
+ // Row and column titles have been defined.
+ // Row title has been defined.
+ $this->store_name_long(
+ $worksheet->index,
+ 0x07, // NAME type
+ $rowmin,
+ $rowmax,
+ $colmin,
+ $colmax
+ );
+ }
+ elseif (isset($rowmin))
+ {
+ // Row title has been defined.
+ $this->store_name_short(
+ $worksheet->index,
+ 0x07, // NAME type
+ $rowmin,
+ $rowmax,
+ 0x00,
+ 0xff
+ );
+ }
+ elseif (isset($colmin))
+ {
+ // Column title has been defined.
+ $this->store_name_short(
+ $worksheet->index,
+ 0x07, // NAME type
+ 0x0000,
+ 0x3fff,
+ $colmin,
+ $colmax
+ );
+ }
+ else {
+ // Print title hasn't been defined.
+ }
+ }
+ }
+
+
+
+
+ /******************************************************************************
+ *
+ * BIFF RECORDS
+ *
+ */
+
+ /**
+ * Write Excel BIFF WINDOW1 record.
+ */
+ function _store_window1()
+ {
+ $record = 0x003D; // Record identifier
+ $length = 0x0012; // Number of bytes to follow
+
+ $xWn = 0x0000; // Horizontal position of window
+ $yWn = 0x0000; // Vertical position of window
+ $dxWn = 0x25BC; // Width of window
+ $dyWn = 0x1572; // Height of window
+
+ $grbit = 0x0038; // Option flags
+ $ctabsel = $this->selected; // Number of workbook tabs selected
+ $wTabRatio = 0x0258; // Tab to scrollbar ratio
+
+ $itabFirst = $this->firstsheet; // 1st displayed worksheet
+ $itabCur = $this->activesheet; // Active worksheet
+
+ $header = pack("vv", $record, $length);
+ $data = pack("vvvvvvvvv", $xWn, $yWn, $dxWn, $dyWn,
+ $grbit,
+ $itabCur, $itabFirst,
+ $ctabsel, $wTabRatio);
+ $this->_append($header.$data);
+ }
+
+ /**
+ * Writes Excel BIFF BOUNDSHEET record.
+ *
+ * @param string $sheetname Worksheet name
+ * @param integer $offset Location of worksheet BOF
+ */
+ function _store_boundsheet($sheetname,$offset)
+ {
+ $record = 0x0085; // Record identifier
+ $length = 0x07 + strlen($sheetname); // Number of bytes to follow
+
+ $grbit = 0x0000; // Sheet identifier
+ $cch = strlen($sheetname); // Length of sheet name
+
+ $header = pack("vv", $record, $length);
+ $data = pack("VvC", $offset, $grbit, $cch);
+ $this->_append($header.$data.$sheetname);
+ }
+
+ /**
+ * Write Excel BIFF STYLE records.
+ */
+ function _store_style()
+ {
+ $record = 0x0293; // Record identifier
+ $length = 0x0004; // Bytes to follow
+
+ $ixfe = 0x8000; // Index to style XF
+ $BuiltIn = 0x00; // Built-in style
+ $iLevel = 0xff; // Outline style level
+
+ $header = pack("vv", $record, $length);
+ $data = pack("vCC", $ixfe, $BuiltIn, $iLevel);
+ $this->_append($header.$data);
+ }
+
+
+ /**
+ * Writes Excel FORMAT record for non "built-in" numerical formats.
+ *
+ * @param string $format Custom format string
+ * @param integer $ifmt Format index code
+ */
+ function _store_num_format($format,$ifmt)
+ {
+ $record = 0x041E; // Record identifier
+ $length = 0x03 + strlen($format); // Number of bytes to follow
+
+ $cch = strlen($format); // Length of format string
+
+ $header = pack("vv", $record, $length);
+ $data = pack("vC", $ifmt, $cch);
+ $this->_append($header.$data.$format);
+ }
+
+ /**
+ * Write Excel 1904 record to indicate the date system in use.
+ */
+ function _store_1904()
+ {
+ $record = 0x0022; // Record identifier
+ $length = 0x0002; // Bytes to follow
+
+ $f1904 = $this->_1904; // Flag for 1904 date system
+
+ $header = pack("vv", $record, $length);
+ $data = pack("v", $f1904);
+ $this->_append($header.$data);
+ }
+
+
+ /**
+ * Write BIFF record EXTERNCOUNT to indicate the number of external sheet
+ * references in the workbook.
+ *
+ * Excel only stores references to external sheets that are used in NAME.
+ * The workbook NAME record is required to define the print area and the repeat
+ * rows and columns.
+ *
+ * A similar method is used in Worksheet.php for a slightly different purpose.
+ *
+ * @param integer $cxals Number of external references
+ */
+ function _store_externcount($cxals)
+ {
+ $record = 0x0016; // Record identifier
+ $length = 0x0002; // Number of bytes to follow
+
+ $header = pack("vv", $record, $length);
+ $data = pack("v", $cxals);
+ $this->_append($header.$data);
+ }
+
+
+ /**
+ * Writes the Excel BIFF EXTERNSHEET record. These references are used by
+ * formulas. NAME record is required to define the print area and the repeat
+ * rows and columns.
+ *
+ * A similar method is used in Worksheet.php for a slightly different purpose.
+ *
+ * @param string $sheetname Worksheet name
+ */
+ function _store_externsheet($sheetname)
+ {
+ $record = 0x0017; // Record identifier
+ $length = 0x02 + strlen($sheetname); // Number of bytes to follow
+
+ $cch = strlen($sheetname); // Length of sheet name
+ $rgch = 0x03; // Filename encoding
+
+ $header = pack("vv", $record, $length);
+ $data = pack("CC", $cch, $rgch);
+ $this->_append($header.$data.$sheetname);
+ }
+
+
+ /**
+ * Store the NAME record in the short format that is used for storing the print
+ * area, repeat rows only and repeat columns only.
+ *
+ * @param integer $index Sheet index
+ * @param integer $type Built-in name type
+ * @param integer $rowmin Start row
+ * @param integer $rowmax End row
+ * @param integer $colmin Start colum
+ * @param integer $colmax End column
+ */
+ function store_name_short($index,$type,$rowmin,$rowmax,$colmin,$colmax)
+ {
+ $record = 0x0018; // Record identifier
+ $length = 0x0024; // Number of bytes to follow
+
+ $grbit = 0x0020; // Option flags
+ $chKey = 0x00; // Keyboard shortcut
+ $cch = 0x01; // Length of text name
+ $cce = 0x0015; // Length of text definition
+ $ixals = $index + 1; // Sheet index
+ $itab = $ixals; // Equal to ixals
+ $cchCustMenu = 0x00; // Length of cust menu text
+ $cchDescription = 0x00; // Length of description text
+ $cchHelptopic = 0x00; // Length of help topic text
+ $cchStatustext = 0x00; // Length of status bar text
+ $rgch = $type; // Built-in name type
+
+ $unknown03 = 0x3b;
+ $unknown04 = 0xffff-$index;
+ $unknown05 = 0x0000;
+ $unknown06 = 0x0000;
+ $unknown07 = 0x1087;
+ $unknown08 = 0x8005;
+
+ $header = pack("vv", $record, $length);
+ $data = pack("v", $grbit);
+ $data .= pack("C", $chKey);
+ $data .= pack("C", $cch);
+ $data .= pack("v", $cce);
+ $data .= pack("v", $ixals);
+ $data .= pack("v", $itab);
+ $data .= pack("C", $cchCustMenu);
+ $data .= pack("C", $cchDescription);
+ $data .= pack("C", $cchHelptopic);
+ $data .= pack("C", $cchStatustext);
+ $data .= pack("C", $rgch);
+ $data .= pack("C", $unknown03);
+ $data .= pack("v", $unknown04);
+ $data .= pack("v", $unknown05);
+ $data .= pack("v", $unknown06);
+ $data .= pack("v", $unknown07);
+ $data .= pack("v", $unknown08);
+ $data .= pack("v", $index);
+ $data .= pack("v", $index);
+ $data .= pack("v", $rowmin);
+ $data .= pack("v", $rowmax);
+ $data .= pack("C", $colmin);
+ $data .= pack("C", $colmax);
+ $this->_append($header.$data);
+ }
+
+
+ /**
+ * Store the NAME record in the long format that is used for storing the repeat
+ * rows and columns when both are specified. This share a lot of code with
+ * _store_name_short() but we use a separate method to keep the code clean.
+ * Code abstraction for reuse can be carried too far, and I should know. ;-)
+ *
+ * @param integer $index Sheet index
+ * @param integer $type Built-in name type
+ * @param integer $rowmin Start row
+ * @param integer $rowmax End row
+ * @param integer $colmin Start colum
+ * @param integer $colmax End column
+ */
+ function store_name_long($index,$type,$rowmin,$rowmax,$colmin,$colmax)
+ {
+ $record = 0x0018; // Record identifier
+ $length = 0x003d; // Number of bytes to follow
+ $grbit = 0x0020; // Option flags
+ $chKey = 0x00; // Keyboard shortcut
+ $cch = 0x01; // Length of text name
+ $cce = 0x002e; // Length of text definition
+ $ixals = $index + 1; // Sheet index
+ $itab = $ixals; // Equal to ixals
+ $cchCustMenu = 0x00; // Length of cust menu text
+ $cchDescription = 0x00; // Length of description text
+ $cchHelptopic = 0x00; // Length of help topic text
+ $cchStatustext = 0x00; // Length of status bar text
+ $rgch = $type; // Built-in name type
+
+ $unknown01 = 0x29;
+ $unknown02 = 0x002b;
+ $unknown03 = 0x3b;
+ $unknown04 = 0xffff-$index;
+ $unknown05 = 0x0000;
+ $unknown06 = 0x0000;
+ $unknown07 = 0x1087;
+ $unknown08 = 0x8008;
+
+ $header = pack("vv", $record, $length);
+ $data = pack("v", $grbit);
+ $data .= pack("C", $chKey);
+ $data .= pack("C", $cch);
+ $data .= pack("v", $cce);
+ $data .= pack("v", $ixals);
+ $data .= pack("v", $itab);
+ $data .= pack("C", $cchCustMenu);
+ $data .= pack("C", $cchDescription);
+ $data .= pack("C", $cchHelptopic);
+ $data .= pack("C", $cchStatustext);
+ $data .= pack("C", $rgch);
+ $data .= pack("C", $unknown01);
+ $data .= pack("v", $unknown02);
+ // Column definition
+ $data .= pack("C", $unknown03);
+ $data .= pack("v", $unknown04);
+ $data .= pack("v", $unknown05);
+ $data .= pack("v", $unknown06);
+ $data .= pack("v", $unknown07);
+ $data .= pack("v", $unknown08);
+ $data .= pack("v", $index);
+ $data .= pack("v", $index);
+ $data .= pack("v", 0x0000);
+ $data .= pack("v", 0x3fff);
+ $data .= pack("C", $colmin);
+ $data .= pack("C", $colmax);
+ // Row definition
+ $data .= pack("C", $unknown03);
+ $data .= pack("v", $unknown04);
+ $data .= pack("v", $unknown05);
+ $data .= pack("v", $unknown06);
+ $data .= pack("v", $unknown07);
+ $data .= pack("v", $unknown08);
+ $data .= pack("v", $index);
+ $data .= pack("v", $index);
+ $data .= pack("v", $rowmin);
+ $data .= pack("v", $rowmax);
+ $data .= pack("C", 0x00);
+ $data .= pack("C", 0xff);
+ // End of data
+ $data .= pack("C", 0x10);
+ $this->_append($header.$data);
+ }
+
+
+ /**
+ * Stores the PALETTE biff record.
+ */
+ function _store_palette()
+ {
+ $aref = $this->palette;
+
+ $record = 0x0092; // Record identifier
+ $length = 2 + 4 * count($aref); // Number of bytes to follow
+ $ccv = count($aref); // Number of RGB values to follow
+ $data = ''; // The RGB data
+
+ // Pack the RGB data
+ foreach($aref as $color)
+ {
+ foreach($color as $byte) {
+ $data .= pack("C",$byte);
+ }
+ }
+
+ $header = pack("vvv", $record, $length, $ccv);
+ $this->_append($header.$data);
+ }
+}
+?>
\ No newline at end of file diff --git a/vendor/write_excel/Worksheet.php b/vendor/write_excel/Worksheet.php new file mode 100644 index 0000000..ecb3d8c --- /dev/null +++ b/vendor/write_excel/Worksheet.php @@ -0,0 +1,2835 @@ +<?php +/* +* Module written/ported by Xavier Noguer <xnoguer@rezebra.com> +* +* The majority of this is _NOT_ my code. I simply ported it from the +* PERL Spreadsheet::WriteExcel module. +* +* The author of the Spreadsheet::WriteExcel module is John McNamara +* <jmcnamara@cpan.org> +* +* I _DO_ maintain this code, and John McNamara has nothing to do with the +* porting of this code to PHP. Any questions directly related to this +* class library should be directed to me. +* +* License Information: +* +* Spreadsheet::WriteExcel: A library for generating Excel Spreadsheets +* Copyright (C) 2002 Xavier Noguer xnoguer@rezebra.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +require_once('Parser.php'); +require_once('BIFFwriter.php'); + +/** +* Class for generating Excel Spreadsheets +* +* @author Xavier Noguer <xnoguer@rezebra.com> +* @package Spreadsheet_WriteExcel +*/ + +class Worksheet extends BIFFwriter +{ + + /** + * Constructor + * + * @param string $name The name of the new worksheet + * @param integer $index The index of the new worksheet + * @param mixed &$activesheet The current activesheet of the workbook we belong to + * @param mixed &$firstsheet The first worksheet in the workbook we belong to + * @param mixed &$url_format The default format for hyperlinks + * @param mixed &$parser The formula parser created for the Workbook + */ + function __construct($name,$index,&$activesheet,&$firstsheet,&$url_format,&$parser) + { + parent::__construct(); + $rowmax = 65536; // 16384 in Excel 5 + $colmax = 256; + $strmax = 255; + + $this->name = $name; + $this->index = $index; + $this->activesheet = &$activesheet; + $this->firstsheet = &$firstsheet; + $this->_url_format = $url_format; + $this->_parser = &$parser; + + $this->ext_sheets = array(); + $this->_using_tmpfile = 1; + $this->_filehandle = ""; + $this->fileclosed = 0; + $this->offset = 0; + $this->xls_rowmax = $rowmax; + $this->xls_colmax = $colmax; + $this->xls_strmax = $strmax; + $this->dim_rowmin = $rowmax +1; + $this->dim_rowmax = 0; + $this->dim_colmin = $colmax +1; + $this->dim_colmax = 0; + $this->colinfo = array(); + $this->_selection = array(0,0,0,0); + $this->_panes = array(); + $this->_active_pane = 3; + $this->_frozen = 0; + $this->selected = 0; + + $this->_paper_size = 0x0; + $this->_orientation = 0x1; + $this->_header = ''; + $this->_footer = ''; + $this->_hcenter = 0; + $this->_vcenter = 0; + $this->_margin_head = 0.50; + $this->_margin_foot = 0.50; + $this->_margin_left = 0.75; + $this->_margin_right = 0.75; + $this->_margin_top = 1.00; + $this->_margin_bottom = 1.00; + + $this->_title_rowmin = NULL; + $this->_title_rowmax = NULL; + $this->_title_colmin = NULL; + $this->_title_colmax = NULL; + $this->_print_rowmin = NULL; + $this->_print_rowmax = NULL; + $this->_print_colmin = NULL; + $this->_print_colmax = NULL; + + $this->_print_gridlines = 1; + $this->_print_headers = 0; + + $this->_fit_page = 0; + $this->_fit_width = 0; + $this->_fit_height = 0; + + $this->_hbreaks = array(); + $this->_vbreaks = array(); + + $this->_protect = 0; + $this->_password = NULL; + + $this->col_sizes = array(); + $this->row_sizes = array(); + + $this->_zoom = 100; + $this->_print_scale = 100; + + $this->_initialize(); + } + + /** + * Open a tmp file to store the majority of the Worksheet data. If this fails, + * for example due to write permissions, store the data in memory. This can be + * slow for large files. + */ + function _initialize() + { + // Open tmp file for storing Worksheet data + $fh = tmpfile(); + if ( $fh) { + // Store filehandle + $this->_filehandle = $fh; + } + else { + // If tmpfile() fails store data in memory + $this->_using_tmpfile = 0; + } + } + + /** + * Add data to the beginning of the workbook (note the reverse order) + * and to the end of the workbook. + * + * @access public + * @see Workbook::store_workbook() + * @param array $sheetnames The array of sheetnames from the Workbook this + * worksheet belongs to + */ + function close($sheetnames) + { + $num_sheets = count($sheetnames); + + /*********************************************** + * Prepend in reverse order!! + */ + + // Prepend the sheet dimensions + $this->_store_dimensions(); + + // Prepend the sheet password + $this->_store_password(); + + // Prepend the sheet protection + $this->_store_protect(); + + // Prepend the page setup + $this->_store_setup(); + + // Prepend the bottom margin + $this->_store_margin_bottom(); + + // Prepend the top margin + $this->_store_margin_top(); + + // Prepend the right margin + $this->_store_margin_right(); + + // Prepend the left margin + $this->_store_margin_left(); + + // Prepend the page vertical centering + $this->store_vcenter(); + + // Prepend the page horizontal centering + $this->store_hcenter(); + + // Prepend the page footer + $this->store_footer(); + + // Prepend the page header + $this->store_header(); + + // Prepend the vertical page breaks + $this->_store_vbreak(); + + // Prepend the horizontal page breaks + $this->_store_hbreak(); + + // Prepend WSBOOL + $this->_store_wsbool(); + + // Prepend GRIDSET + $this->_store_gridset(); + + // Prepend PRINTGRIDLINES + $this->_store_print_gridlines(); + + // Prepend PRINTHEADERS + $this->_store_print_headers(); + + // Prepend EXTERNSHEET references + for ($i = $num_sheets; $i > 0; $i--) { + $sheetname = $sheetnames[$i-1]; + $this->_store_externsheet($sheetname); + } + + // Prepend the EXTERNCOUNT of external references. + $this->_store_externcount($num_sheets); + + // Prepend the COLINFO records if they exist + if (!empty($this->colinfo)){ + for($i=0; $i < count($this->colinfo); $i++) + { + $this->_store_colinfo($this->colinfo[$i]); + } + $this->_store_defcol(); + } + + // Prepend the BOF record + $this->_store_bof(0x0010); + + /* + * End of prepend. Read upwards from here. + ***********************************************/ + + // Append + $this->_store_window2(); + $this->_store_zoom(); + if(!empty($this->_panes)) + $this->_store_panes($this->_panes); + $this->_store_selection($this->_selection); + $this->_store_eof(); + } + + /** + * Retrieve the worksheet name. This is usefull when creating worksheets + * without a name. + * + * @access public + * @return string The worksheet's name + */ + function get_name() + { + return($this->name); + } + + /** + * Retrieves data from memory in one chunk, or from disk in $buffer + * sized chunks. + * + * @return string The data + */ + function get_data() + { + $buffer = 4096; + + // Return data stored in memory + if (isset($this->_data)) { + $tmp = $this->_data; + unset($this->_data); + $fh = $this->_filehandle; + if ($this->_using_tmpfile) { + fseek($fh, 0); + } + return($tmp); + } + // Return data stored on disk + if ($this->_using_tmpfile) { + if ($tmp = fread($this->_filehandle, $buffer)) { + return($tmp); + } + } + + // No data to return + return(''); + } + + /** + * Set this worksheet as a selected worksheet, i.e. the worksheet has its tab + * highlighted. + * + * @access public + */ + function select() + { + $this->selected = 1; + } + + /** + * Set this worksheet as the active worksheet, i.e. the worksheet that is + * displayed when the workbook is opened. Also set it as selected. + * + * @access public + */ + function activate() + { + $this->selected = 1; + $this->activesheet =& $this->index; + } + + /** + * Set this worksheet as the first visible sheet. This is necessary + * when there are a large number of worksheets and the activated + * worksheet is not visible on the screen. + * + * @access public + */ + function set_first_sheet() + { + $this->firstsheet = $this->index; + } + + /** + * Set the worksheet protection flag to prevent accidental modification and to + * hide formulas if the locked and hidden format properties have been set. + * + * @access public + * @param string $password The password to use for protecting the sheet. + */ + function protect($password) + { + $this->_protect = 1; + $this->_password = $this->_encode_password($password); + } + + /** + * Set the width of a single column or a range of columns. + * + * @access public + * @see _store_colinfo() + * @param integer $firstcol first column on the range + * @param integer $lastcol last column on the range + * @param integer $width width to set + * @param mixed $format The optional XF format to apply to the columns + * @param integer $hidden The optional hidden atribute + */ + function set_column($firstcol, $lastcol, $width, $format = 0, $hidden = 0) + { + $this->colinfo[] = array($firstcol, $lastcol, $width, $format, $hidden); + + // Set width to zero if column is hidden + $width = ($hidden) ? 0 : $width; + + for($col = $firstcol; $col <= $lastcol; $col++) { + $this->col_sizes[$col] = $width; + } + } + + /** + * Set which cell or cells are selected in a worksheet + * + * @access public + * @param integer $first_row first row in the selected quadrant + * @param integer $first_column first column in the selected quadrant + * @param integer $last_row last row in the selected quadrant + * @param integer $last_column last column in the selected quadrant + * @see _store_selection() + */ + function set_selection($first_row,$first_column,$last_row,$last_column) + { + $this->_selection = array($first_row,$first_column,$last_row,$last_column); + } + + /** + * Set panes and mark them as frozen. + * + * @access public + * @param array $panes This is the only parameter received and is composed of the following: + * 0 => Vertical split position, + * 1 => Horizontal split position + * 2 => Top row visible + * 3 => Leftmost column visible + * 4 => Active pane + */ + function freeze_panes($panes) + { + $this->_frozen = 1; + $this->_panes = $panes; + } + + /** + * Set panes and mark them as unfrozen. + * + * @access public + * @param array $panes This is the only parameter received and is composed of the following: + * 0 => Vertical split position, + * 1 => Horizontal split position + * 2 => Top row visible + * 3 => Leftmost column visible + * 4 => Active pane + */ + function thaw_panes($panes) + { + $this->_frozen = 0; + $this->_panes = $panes; + } + + /** + * Set the page orientation as portrait. + * + * @access public + */ + function set_portrait() + { + $this->_orientation = 1; + } + + /** + * Set the page orientation as landscape. + * + * @access public + */ + function set_landscape() + { + $this->_orientation = 0; + } + + /** + * Set the paper type. Ex. 1 = US Letter, 9 = A4 + * + * @access public + * @param integer $size The type of paper size to use + */ + function set_paper($size = 0) + { + $this->_paper_size = $size; + } + + + /** + * Set the page header caption and optional margin. + * + * @access public + * @param string $string The header text + * @param float $margin optional head margin in inches. + */ + function set_header($string,$margin = 0.50) + { + if (strlen($string) >= 255) { + //carp 'Header string must be less than 255 characters'; + return; + } + $this->_header = $string; + $this->_margin_head = $margin; + } + + /** + * Set the page footer caption and optional margin. + * + * @access public + * @param string $string The footer text + * @param float $margin optional foot margin in inches. + */ + function set_footer($string,$margin = 0.50) + { + if (strlen($string) >= 255) { + //carp 'Footer string must be less than 255 characters'; + return; + } + $this->_footer = $string; + $this->_margin_foot = $margin; + } + + /** + * Center the page horinzontally. + * + * @access public + * @param integer $center the optional value for centering. Defaults to 1 (center). + */ + function center_horizontally($center = 1) + { + $this->_hcenter = $center; + } + + /** + * Center the page horinzontally. + * + * @access public + * @param integer $center the optional value for centering. Defaults to 1 (center). + */ + function center_vertically($center = 1) + { + $this->_vcenter = $center; + } + + /** + * Set all the page margins to the same value in inches. + * + * @access public + * @param float $margin The margin to set in inches + */ + function set_margins($margin) + { + $this->set_margin_left($margin); + $this->set_margin_right($margin); + $this->set_margin_top($margin); + $this->set_margin_bottom($margin); + } + + /** + * Set the left and right margins to the same value in inches. + * + * @access public + * @param float $margin The margin to set in inches + */ + function set_margins_LR($margin) + { + $this->set_margin_left($margin); + $this->set_margin_right($margin); + } + + /** + * Set the top and bottom margins to the same value in inches. + * + * @access public + * @param float $margin The margin to set in inches + */ + function set_margins_TB($margin) + { + $this->set_margin_top($margin); + $this->set_margin_bottom($margin); + } + + /** + * Set the left margin in inches. + * + * @access public + * @param float $margin The margin to set in inches + */ + function set_margin_left($margin = 0.75) + { + $this->_margin_left = $margin; + } + + /** + * Set the right margin in inches. + * + * @access public + * @param float $margin The margin to set in inches + */ + function set_margin_right($margin = 0.75) + { + $this->_margin_right = $margin; + } + + /** + * Set the top margin in inches. + * + * @access public + * @param float $margin The margin to set in inches + */ + function set_margin_top($margin = 1.00) + { + $this->_margin_top = $margin; + } + + /** + * Set the bottom margin in inches. + * + * @access public + * @param float $margin The margin to set in inches + */ + function set_margin_bottom($margin = 1.00) + { + $this->_margin_bottom = $margin; + } + + /** + * Set the rows to repeat at the top of each printed page. See also the + * _store_name_xxxx() methods in Workbook.php + * + * @access public + * @param integer $first_row First row to repeat + * @param integer $last_row Last row to repeat. Optional. + */ + function repeat_rows($first_row, $last_row = NULL) + { + $this->_title_rowmin = $first_row; + if(isset($last_row)) { //Second row is optional + $this->_title_rowmax = $last_row; + } + else { + $this->_title_rowmax = $first_row; + } + } + + /** + * Set the columns to repeat at the left hand side of each printed page. + * See also the _store_names() methods in Workbook.php + * + * @access public + * @param integer $first_col First column to repeat + * @param integer $last_col Last column to repeat. Optional. + */ + function repeat_columns($first_col, $last_col = NULL) + { + $this->_title_colmin = $first_col; + if(isset($last_col)) { // Second col is optional + $this->_title_colmax = $last_col; + } + else { + $this->_title_colmax = $first_col; + } + } + + /** + * Set the area of each worksheet that will be printed. + * + * @access public + * @see Workbook::_store_names() + * @param integer $first_row First row of the area to print + * @param integer $first_col First column of the area to print + * @param integer $last_row Last row of the area to print + * @param integer $last_col Last column of the area to print + */ + function print_area($first_row, $first_col, $last_row, $last_col) + { + $this->_print_rowmin = $first_row; + $this->_print_colmin = $first_col; + $this->_print_rowmax = $last_row; + $this->_print_colmax = $last_col; + } + + + /** + * Set the option to hide gridlines on the printed page. + * + * @access public + * @see _store_print_gridlines(), _store_gridset() + */ + function hide_gridlines() + { + $this->_print_gridlines = 0; + } + + /** + * Set the option to print the row and column headers on the printed page. + * See also the _store_print_headers() method below. + * + * @access public + * @see _store_print_headers() + * @param integer $print Whether to print the headers or not. Defaults to 1 (print). + */ + function print_row_col_headers($print = 1) + { + $this->_print_headers = $print; + } + + /** + * Store the vertical and horizontal number of pages that will define the + * maximum area printed. It doesn't seem to work with OpenOffice. + * + * @access public + * @param integer $width Maximun width of printed area in pages + * @param integer $heigth Maximun heigth of printed area in pages + * @see set_print_scale() + */ + function fit_to_pages($width, $height) + { + $this->_fit_page = 1; + $this->_fit_width = $width; + $this->_fit_height = $height; + } + + /** + * Store the horizontal page breaks on a worksheet (for printing). + * The breaks represent the row after which the break is inserted. + * + * @access public + * @param array $breaks Array containing the horizontal page breaks + */ + function set_h_pagebreaks($breaks) + { + foreach($breaks as $break) { + array_push($this->_hbreaks,$break); + } + } + + /** + * Store the vertical page breaks on a worksheet (for printing). + * The breaks represent the column after which the break is inserted. + * + * @access public + * @param array $breaks Array containing the vertical page breaks + */ + function set_v_pagebreaks($breaks) + { + foreach($breaks as $break) { + array_push($this->_vbreaks,$break); + } + } + + + /** + * Set the worksheet zoom factor. + * + * @access public + * @param integer $scale The zoom factor + */ + function set_zoom($scale = 100) + { + // Confine the scale to Excel's range + if ($scale < 10 or $scale > 400) { + //carp "Zoom factor $scale outside range: 10 <= zoom <= 400"; + $scale = 100; + } + + $this->_zoom = floor($scale); + } + + /** + * Set the scale factor for the printed page. + * It turns off the "fit to page" option + * + * @access public + * @param integer $scale The optional scale factor. Defaults to 100 + */ + function set_print_scale($scale = 100) + { + // Confine the scale to Excel's range + if ($scale < 10 or $scale > 400) + { + // REPLACE THIS FOR A WARNING + die("Print scale $scale outside range: 10 <= zoom <= 400"); + $scale = 100; + } + + // Turn off "fit to page" option + $this->_fit_page = 0; + + $this->_print_scale = floor($scale); + } + + /** + * Map to the appropriate write method acording to the token recieved. + * + * @access public + * @param integer $row The row of the cell we are writing to + * @param integer $col The column of the cell we are writing to + * @param mixed $token What we are writing + * @param mixed $format The optional format to apply to the cell + */ + function write($row, $col, $token, $format = 0) + { + // Check for a cell reference in A1 notation and substitute row and column + /*if ($_[0] =~ /^\D/) { + @_ = $this->_substitute_cellref(@_); + }*/ + + /* + # Match an array ref. + if (ref $token eq "ARRAY") { + return $this->write_row(@_); + }*/ + + // Match number + if (preg_match("/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/",$token)) { + return $this->write_number($row,$col,$token,$format); + } + // Match http or ftp URL + elseif (preg_match("/^[fh]tt?p:\/\//",$token)) { + return $this->write_url($row, $col, $token, $format); + } + // Match mailto: + elseif (preg_match("/^mailto:/",$token)) { + return $this->write_url($row, $col, $token, $format); + } + // Match internal or external sheet link + elseif (preg_match("/^(?:in|ex)ternal:/",$token)) { + return $this->write_url($row, $col, $token, $format); + } + // Match formula + elseif (preg_match("/^=/",$token)) { + return $this->write_formula($row, $col, $token, $format); + } + // Match formula + elseif (preg_match("/^@/",$token)) { + return $this->write_formula($row, $col, $token, $format); + } + // Match blank + elseif ($token == '') { + return $this->write_blank($row,$col,$format); + } + // Default: match string + else { + return $this->write_string($row,$col,$token,$format); + } + } + + /** + * Returns an index to the XF record in the workbook + * + * @param mixed $format The optional XF format + * @return integer The XF record index + */ + function _XF(&$format) + { + if($format != 0) + { + return($format->get_xf_index()); + } + else + { + return(0x0F); + } + } + + + /****************************************************************************** + ******************************************************************************* + * + * Internal methods + */ + + + /** + * Store Worksheet data in memory using the parent's class append() or to a + * temporary file, the default. + * + * @param string $data The binary data to append + */ + function _append($data) + { + if ($this->_using_tmpfile) + { + // Add CONTINUE records if necessary + if (strlen($data) > $this->_limit) { + $data = $this->_add_continue($data); + } + fwrite($this->_filehandle,$data); + $this->_datasize += strlen($data); + } + else { + parent::_append($data); + } + } + + /** + * Substitute an Excel cell reference in A1 notation for zero based row and + * column values in an argument list. + * + * Ex: ("A4", "Hello") is converted to (3, 0, "Hello"). + * + * @param string $cell The cell reference. Or range of cells. + * @return array + */ + function _substitute_cellref($cell) + { + $cell = strtoupper($cell); + + // Convert a column range: 'A:A' or 'B:G' + if (preg_match("/([A-I]?[A-Z]):([A-I]?[A-Z])/",$cell,$match)) { + list($no_use, $col1) = $this->_cell_to_rowcol($match[1] .'1'); // Add a dummy row + list($no_use, $col2) = $this->_cell_to_rowcol($match[2] .'1'); // Add a dummy row + return(array($col1, $col2)); + } + + // Convert a cell range: 'A1:B7' + if (preg_match("/\$?([A-I]?[A-Z]\$?\d+):\$?([A-I]?[A-Z]\$?\d+)/",$cell,$match)) { + list($row1, $col1) = $this->_cell_to_rowcol($match[1]); + list($row2, $col2) = $this->_cell_to_rowcol($match[2]); + return(array($row1, $col1, $row2, $col2)); + } + + // Convert a cell reference: 'A1' or 'AD2000' + if (preg_match("/\$?([A-I]?[A-Z]\$?\d+)/",$cell)) { + list($row1, $col1) = $this->_cell_to_rowcol($match[1]); + return(array($row1, $col1)); + } + + die("Unknown cell reference $cell "); + } + + /** + * Convert an Excel cell reference in A1 notation to a zero based row and column + * reference; converts C1 to (0, 2). + * + * @param string $cell The cell reference. + * @return array containing (row, column) + */ + function _cell_to_rowcol($cell) + { + preg_match("/\$?([A-I]?[A-Z])\$?(\d+)/",$cell,$match); + $col = $match[1]; + $row = $match[2]; + + // Convert base26 column string to number + $chars = explode('', $col); + $expn = 0; + $col = 0; + + while ($chars) { + $char = array_pop($chars); // LS char first + $col += (ord($char) -ord('A') +1) * pow(26,$expn); + $expn++; + } + + // Convert 1-index to zero-index + $row--; + $col--; + + return(array($row, $col)); + } + + /** + * Based on the algorithm provided by Daniel Rentz of OpenOffice. + * + * @param string $plaintext The password to be encoded in plaintext. + * @return string The encoded password + */ + function _encode_password($plaintext) + { + $password = 0x0000; + $i = 1; // char position + + // split the plain text password in its component characters + $chars = preg_split('//', $plaintext, -1, PREG_SPLIT_NO_EMPTY); + foreach($chars as $char) + { + $value = ord($char) << $i; // shifted ASCII value + $bit_16 = $value & 0x8000; // the bit 16 + $bit_16 >>= 15; // 0x0000 or 0x0001 + //$bit_17 = $value & 0x00010000; + //$bit_17 >>= 15; + $value &= 0x7fff; // first 15 bits + $password ^= ($value | $bit_16); + //$password ^= ($value | $bit_16 | $bit_17); + $i++; + } + + $password ^= strlen($plaintext); + $password ^= 0xCE4B; + + return($password); + } + + /****************************************************************************** + ******************************************************************************* + * + * BIFF RECORDS + */ + + + /** + * Write a double to the specified row and column (zero indexed). + * An integer can be written as a double. Excel will display an + * integer. $format is optional. + * + * Returns 0 : normal termination + * -2 : row or column out of range + * + * @access public + * @param integer $row Zero indexed row + * @param integer $col Zero indexed column + * @param float $num The number to write + * @param mixed $format The optional XF format + */ + function write_number($row, $col, $num, $format = 0) + { + $record = 0x0203; // Record identifier + $length = 0x000E; // Number of bytes to follow + $xf = $this->_XF($format); // The cell format + + // Check that row and col are valid and store max and min values + if ($row >= $this->xls_rowmax) + { + return(-2); + } + if ($col >= $this->xls_colmax) + { + return(-2); + } + if ($row < $this->dim_rowmin) + { + $this->dim_rowmin = $row; + } + if ($row > $this->dim_rowmax) + { + $this->dim_rowmax = $row; + } + if ($col < $this->dim_colmin) + { + $this->dim_colmin = $col; + } + if ($col > $this->dim_colmax) + { + $this->dim_colmax = $col; + } + + $header = pack("vv", $record, $length); + $data = pack("vvv", $row, $col, $xf); + $xl_double = pack("d", $num); + if ($this->_byte_order) // if it's Big Endian + { + $xl_double = strrev($xl_double); + } + + $this->_append($header.$data.$xl_double); + return(0); + } + + /** + * Write a string to the specified row and column (zero indexed). + * NOTE: there is an Excel 5 defined limit of 255 characters. + * $format is optional. + * Returns 0 : normal termination + * -1 : insufficient number of arguments + * -2 : row or column out of range + * -3 : long string truncated to 255 chars + * + * @access public + * @param integer $row Zero indexed row + * @param integer $col Zero indexed column + * @param string $str The string to write + * @param mixed $format The XF format for the cell + */ + function write_string($row, $col, $str, $format = 0) + { + $strlen = strlen($str); + $record = 0x0204; // Record identifier + $length = 0x0008 + $strlen; // Bytes to follow + $xf = $this->_XF($format); // The cell format + + $str_error = 0; + + // Check that row and col are valid and store max and min values + if ($row >= $this->xls_rowmax) + { + return(-2); + } + if ($col >= $this->xls_colmax) + { + return(-2); + } + if ($row < $this->dim_rowmin) + { + $this->dim_rowmin = $row; + } + if ($row > $this->dim_rowmax) + { + $this->dim_rowmax = $row; + } + if ($col < $this->dim_colmin) + { + $this->dim_colmin = $col; + } + if ($col > $this->dim_colmax) + { + $this->dim_colmax = $col; + } + + if ($strlen > $this->xls_strmax) // LABEL must be < 255 chars + { + $str = substr($str, 0, $this->xls_strmax); + $length = 0x0008 + $this->xls_strmax; + $strlen = $this->xls_strmax; + $str_error = -3; + } + + $header = pack("vv", $record, $length); + $data = pack("vvvv", $row, $col, $xf, $strlen); + $this->_append($header.$data.$str); + return($str_error); + } + + /** + * Writes a note associated with the cell given by the row and column. + * NOTE records don't have a length limit. + * + * @access public + * @param integer $row Zero indexed row + * @param integer $col Zero indexed column + * @param string $note The note to write + */ + function write_note($row, $col, $note) + { + $note_length = strlen($note); + $record = 0x001C; // Record identifier + $max_length = 2048; // Maximun length for a NOTE record + //$length = 0x0006 + $note_length; // Bytes to follow + + // Check that row and col are valid and store max and min values + if ($row >= $this->xls_rowmax) + { + return(-2); + } + if ($col >= $this->xls_colmax) + { + return(-2); + } + if ($row < $this->dim_rowmin) + { + $this->dim_rowmin = $row; + } + if ($row > $this->dim_rowmax) + { + $this->dim_rowmax = $row; + } + if ($col < $this->dim_colmin) + { + $this->dim_colmin = $col; + } + if ($col > $this->dim_colmax) + { + $this->dim_colmax = $col; + } + + // Length for this record is no more than 2048 + 6 + $length = 0x0006 + min($note_length, 2048); + $header = pack("vv", $record, $length); + $data = pack("vvv", $row, $col, $note_length); + $this->_append($header.$data.substr($note, 0, 2048)); + + for($i = $max_length; $i < $note_length; $i += $max_length) + { + $chunk = substr($note, $i, $max_length); + $length = 0x0006 + strlen($chunk); + $header = pack("vv", $record, $length); + $data = pack("vvv", -1, 0, strlen($chunk)); + $this->_append($header.$data.$chunk); + } + return(0); + } + + /** + * Write a blank cell to the specified row and column (zero indexed). + * A blank cell is used to specify formatting without adding a string + * or a number. + * + * A blank cell without a format serves no purpose. Therefore, we don't write + * a BLANK record unless a format is specified. This is mainly an optimisation + * for the write_row() and write_col() methods. + * + * Returns 0 : normal termination (including no format) + * -1 : insufficient number of arguments + * -2 : row or column out of range + * + * @access public + * @param integer $row Zero indexed row + * @param integer $col Zero indexed column + * @param mixed $format The XF format + */ + function write_blank($row, $col, $format = 0) + { + // Don't write a blank cell unless it has a format + if ($format == 0) + { + return(0); + } + + $record = 0x0201; // Record identifier + $length = 0x0006; // Number of bytes to follow + $xf = $this->_XF($format); // The cell format + + // Check that row and col are valid and store max and min values + if ($row >= $this->xls_rowmax) + { + return(-2); + } + if ($col >= $this->xls_colmax) + { + return(-2); + } + if ($row < $this->dim_rowmin) + { + $this->dim_rowmin = $row; + } + if ($row > $this->dim_rowmax) + { + $this->dim_rowmax = $row; + } + if ($col < $this->dim_colmin) + { + $this->dim_colmin = $col; + } + if ($col > $this->dim_colmax) + { + $this->dim_colmax = $col; + } + + $header = pack("vv", $record, $length); + $data = pack("vvv", $row, $col, $xf); + $this->_append($header.$data); + return 0; + } + + /** + * Write a formula to the specified row and column (zero indexed). + * The textual representation of the formula is passed to the parser in + * Parser.php which returns a packed binary string. + * + * Returns 0 : normal termination + * -2 : row or column out of range + * + * @access public + * @param integer $row Zero indexed row + * @param integer $col Zero indexed column + * @param string $formula The formula text string + * @param mixed $format The optional XF format + */ + function write_formula($row, $col, $formula, $format = 0) + { + $record = 0x0006; // Record identifier + + // Excel normally stores the last calculated value of the formula in $num. + // Clearly we are not in a position to calculate this a priori. Instead + // we set $num to zero and set the option flags in $grbit to ensure + // automatic calculation of the formula when the file is opened. + // + $xf = $this->_XF($format); // The cell format + $num = 0x00; // Current value of formula + $grbit = 0x03; // Option flags + $chn = 0x0000; // Must be zero + + + // Check that row and col are valid and store max and min values + if ($row >= $this->xls_rowmax) + { + return(-2); + } + if ($col >= $this->xls_colmax) + { + return(-2); + } + if ($row < $this->dim_rowmin) + { + $this->dim_rowmin = $row; + } + if ($row > $this->dim_rowmax) + { + $this->dim_rowmax = $row; + } + if ($col < $this->dim_colmin) + { + $this->dim_colmin = $col; + } + if ($col > $this->dim_colmax) + { + $this->dim_colmax = $col; + } + + // Strip the '=' or '@' sign at the beginning of the formula string + if (preg_match("/^=/", $formula)) { + $formula = preg_replace("/(^=)/","",$formula); + } + elseif(preg_match("/^@/", $formula)) { + $formula = preg_replace("/(^@)/","",$formula); + } + else { + die("Unrecognised character for formula"); + } + + // Parse the formula using the parser in Parser.php + //$tree = new Parser($this->_byte_order); + $this->_parser->parse($formula); + //$tree->parse($formula); + $formula = $this->_parser->to_reverse_polish(); + + $formlen = strlen($formula); // Length of the binary string + $length = 0x16 + $formlen; // Length of the record data + + $header = pack("vv", $record, $length); + $data = pack("vvvdvVv", $row, $col, $xf, $num, + $grbit, $chn, $formlen); + + $this->_append($header.$data.$formula); + return 0; + } + + /** + * Write a hyperlink. This is comprised of two elements: the visible label and + * the invisible link. The visible label is the same as the link unless an + * alternative string is specified. The label is written using the + * write_string() method. Therefore the 255 characters string limit applies. + * $string and $format are optional and their order is interchangeable. + * + * The hyperlink can be to a http, ftp, mail, internal sheet, or external + * directory url. + * + * Returns 0 : normal termination + * -1 : insufficient number of arguments + * -2 : row or column out of range + * -3 : long string truncated to 255 chars + * + * @access public + * @param integer $row Row + * @param integer $col Column + * @param string $url URL string + * @param string $string Alternative label + * @param mixed $format The cell format + */ + function write_url($row, $col, $url, $string = '', $format = 0) + { + // Add start row and col to arg list + return($this->_write_url_range($row, $col, $row, $col, $url, $string, $format)); + } + + /** + * This is the more general form of write_url(). It allows a hyperlink to be + * written to a range of cells. This function also decides the type of hyperlink + * to be written. These are either, Web (http, ftp, mailto), Internal + * (Sheet1!A1) or external ('c:\temp\foo.xls#Sheet1!A1'). + * + * See also write_url() above for a general description and return values. + * + * @param integer $row1 Start row + * @param integer $col1 Start column + * @param integer $row2 End row + * @param integer $col2 End column + * @param string $url URL string + * @param string $string Alternative label + * @param mixed $format The cell format + */ + + function _write_url_range($row1, $col1, $row2, $col2, $url, $string = '', $format = 0) + { + // Check for internal/external sheet links or default to web link + if (preg_match('[^internal:]', $url)) { + return($this->_write_url_internal($row1, $col1, $row2, $col2, $url, $string, $format)); + } + if (preg_match('[^external:]', $url)) { + return($this->_write_url_external($row1, $col1, $row2, $col2, $url, $string, $format)); + } + return($this->_write_url_web($row1, $col1, $row2, $col2, $url, $string, $format)); + } + + + /** + * Used to write http, ftp and mailto hyperlinks. + * The link type ($options) is 0x03 is the same as absolute dir ref without + * sheet. However it is differentiated by the $unknown2 data stream. + * + * @see write_url() + * @param integer $row1 Start row + * @param integer $col1 Start column + * @param integer $row2 End row + * @param integer $col2 End column + * @param string $url URL string + * @param string $str Alternative label + * @param mixed $format The cell format + */ + function _write_url_web($row1, $col1, $row2, $col2, $url, $str, $format = 0) + { + $record = 0x01B8; // Record identifier + $length = 0x00000; // Bytes to follow + + if($format == 0) { + $format = $this->_url_format; + } + + // Write the visible label using the write_string() method. + if($str == '') { + $str = $url; + } + $str_error = $this->write_string($row1, $col1, $str, $format); + if ($str_error == -2) { + return($str_error); + } + + // Pack the undocumented parts of the hyperlink stream + $unknown1 = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000"); + $unknown2 = pack("H*", "E0C9EA79F9BACE118C8200AA004BA90B"); + + // Pack the option flags + $options = pack("V", 0x03); + + // Convert URL to a null terminated wchar string + $url = join("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY)); + $url = $url . "\0\0\0"; + + // Pack the length of the URL + $url_len = pack("V", strlen($url)); + + // Calculate the data length + $length = 0x34 + strlen($url); + + // Pack the header data + $header = pack("vv", $record, $length); + $data = pack("vvvv", $row1, $row2, $col1, $col2); + + // Write the packed data + $this->_append( $header. $data. + $unknown1. $options. + $unknown2. $url_len. $url); + return($str_error); + } + + /** + * Used to write internal reference hyperlinks such as "Sheet1!A1". + * + * @see write_url() + * @param integer $row1 Start row + * @param integer $col1 Start column + * @param integer $row2 End row + * @param integer $col2 End column + * @param string $url URL string + * @param string $str Alternative label + * @param mixed $format The cell format + */ + function _write_url_internal($row1, $col1, $row2, $col2, $url, $str, $format = 0) + { + $record = 0x01B8; // Record identifier + $length = 0x00000; // Bytes to follow + + if ($format == 0) { + $format = $this->_url_format; + } + + // Strip URL type + $url = preg_replace('s[^internal:]', '', $url); + + // Write the visible label + if($str == '') { + $str = $url; + } + $str_error = $this->write_string($row1, $col1, $str, $format); + if ($str_error == -2) { + return($str_error); + } + + // Pack the undocumented parts of the hyperlink stream + $unknown1 = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000"); + + // Pack the option flags + $options = pack("V", 0x08); + + // Convert the URL type and to a null terminated wchar string + $url = join("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY)); + $url = $url . "\0\0\0"; + + // Pack the length of the URL as chars (not wchars) + $url_len = pack("V", floor(strlen($url)/2)); + + // Calculate the data length + $length = 0x24 + strlen($url); + + // Pack the header data + $header = pack("vv", $record, $length); + $data = pack("vvvv", $row1, $row2, $col1, $col2); + + // Write the packed data + $this->_append($header. $data. + $unknown1. $options. + $url_len. $url); + return($str_error); + } + + /** + * Write links to external directory names such as 'c:\foo.xls', + * c:\foo.xls#Sheet1!A1', '../../foo.xls'. and '../../foo.xls#Sheet1!A1'. + * + * Note: Excel writes some relative links with the $dir_long string. We ignore + * these cases for the sake of simpler code. + * + * @see write_url() + * @param integer $row1 Start row + * @param integer $col1 Start column + * @param integer $row2 End row + * @param integer $col2 End column + * @param string $url URL string + * @param string $str Alternative label + * @param mixed $format The cell format + */ + function _write_url_external($row1, $col1, $row2, $col2, $url, $str, $format = 0) + { + // Network drives are different. We will handle them separately + // MS/Novell network drives and shares start with \\ + if (preg_match('[^external:\\\\]', $url)) { + return($this->_write_url_external_net($row1, $col1, $row2, $col2, $url, $str, $format)); + } + + $record = 0x01B8; // Record identifier + $length = 0x00000; // Bytes to follow + + if ($format == 0) { + $format = $this->_url_format; + } + + // Strip URL type and change Unix dir separator to Dos style (if needed) + // + $url = preg_replace('[^external:]', '', $url); + $url = preg_replace('[/]', "\\", $url); + + // Write the visible label + if ($str == '') { + $str = preg_replace('[\#]', ' - ', $url); + } + $str_error = $this->write_string($row1, $col1, $str, $format); + if ($str_error == -2) { + return($str_error); + } + + // Determine if the link is relative or absolute: + // relative if link contains no dir separator, "somefile.xls" + // relative if link starts with up-dir, "..\..\somefile.xls" + // otherwise, absolute + + $absolute = 0x02; // Bit mask + if (!preg_match('[\\]', $url)) { + $absolute = 0x00; + } + if (preg_match('[^\.\.\\]', $url)) { + $absolute = 0x00; + } + + // Determine if the link contains a sheet reference and change some of the + // parameters accordingly. + // Split the dir name and sheet name (if it exists) + list($dir_long , $sheet) = explode('/\#/', $url); + $link_type = 0x01 | $absolute; + + if (isset($sheet)) { + $link_type |= 0x08; + $sheet_len = pack("V", strlen($sheet) + 0x01); + $sheet = join("\0", explode('', $sheet)); + $sheet .= "\0\0\0"; + } + else { + $sheet_len = ''; + $sheet = ''; + } + + // Pack the link type + $link_type = pack("V", $link_type); + + // Calculate the up-level dir count e.g.. (..\..\..\ == 3) + $up_count = preg_match_all("/\.\.\\/", $dir_long, $useless); + $up_count = pack("v", $up_count); + + // Store the short dos dir name (null terminated) + $dir_short = preg_replace('/\.\.\\/', '', $dir_long) . "\0"; + + // Store the long dir name as a wchar string (non-null terminated) + $dir_long = join("\0", explode('', $dir_long)); + $dir_long = $dir_long . "\0"; + + // Pack the lengths of the dir strings + $dir_short_len = pack("V", strlen($dir_short) ); + $dir_long_len = pack("V", strlen($dir_long) ); + $stream_len = pack("V", strlen($dir_long) + 0x06); + + // Pack the undocumented parts of the hyperlink stream + $unknown1 = pack("H*",'D0C9EA79F9BACE118C8200AA004BA90B02000000' ); + $unknown2 = pack("H*",'0303000000000000C000000000000046' ); + $unknown3 = pack("H*",'FFFFADDE000000000000000000000000000000000000000'); + $unknown4 = pack("v", 0x03 ); + + // Pack the main data stream + $data = pack("vvvv", $row1, $row2, $col1, $col2) . + $unknown1 . + $link_type . + $unknown2 . + $up_count . + $dir_short_len. + $dir_short . + $unknown3 . + $stream_len . + $dir_long_len . + $unknown4 . + $dir_long . + $sheet_len . + $sheet ; + + // Pack the header data + $length = strlen($data); + $header = pack("vv", $record, $length); + + // Write the packed data + $this->_append($header. $data); + return($str_error); + } + + + /* + ############################################################################### + # + # write_url_xxx($row1, $col1, $row2, $col2, $url, $string, $format) + # + # Write links to external MS/Novell network drives and shares such as + # '//NETWORK/share/foo.xls' and '//NETWORK/share/foo.xls#Sheet1!A1'. + # + # See also write_url() above for a general description and return values. + # + sub _write_url_external_net { + + my $this = shift; + + my $record = 0x01B8; # Record identifier + my $length = 0x00000; # Bytes to follow + + my $row1 = $_[0]; # Start row + my $col1 = $_[1]; # Start column + my $row2 = $_[2]; # End row + my $col2 = $_[3]; # End column + my $url = $_[4]; # URL string + my $str = $_[5]; # Alternative label + my $xf = $_[6] || $this->{_url_format};# The cell format + + + # Strip URL type and change Unix dir separator to Dos style (if needed) + # + $url =~ s[^external:][]; + $url =~ s[/][\\]g; + + + # Write the visible label + ($str = $url) =~ s[\#][ - ] unless defined $str; + my $str_error = $this->write_string($row1, $col1, $str, $xf); + return $str_error if $str_error == -2; + + + # Determine if the link contains a sheet reference and change some of the + # parameters accordingly. + # Split the dir name and sheet name (if it exists) + # + my ($dir_long , $sheet) = split /\#/, $url; + my $link_type = 0x0103; # Always absolute + my $sheet_len; + + if (defined $sheet) { + $link_type |= 0x08; + $sheet_len = pack("V", length($sheet) + 0x01); + $sheet = join("\0", split('', $sheet)); + $sheet .= "\0\0\0"; + } + else { + $sheet_len = ''; + $sheet = ''; + } + + # Pack the link type + $link_type = pack("V", $link_type); + + + # Make the string null terminated + $dir_long = $dir_long . "\0"; + + + # Pack the lengths of the dir string + my $dir_long_len = pack("V", length $dir_long); + + + # Store the long dir name as a wchar string (non-null terminated) + $dir_long = join("\0", split('', $dir_long)); + $dir_long = $dir_long . "\0"; + + + # Pack the undocumented part of the hyperlink stream + my $unknown1 = pack("H*",'D0C9EA79F9BACE118C8200AA004BA90B02000000'); + + + # Pack the main data stream + my $data = pack("vvvv", $row1, $row2, $col1, $col2) . + $unknown1 . + $link_type . + $dir_long_len . + $dir_long . + $sheet_len . + $sheet ; + + + # Pack the header data + $length = length $data; + my $header = pack("vv", $record, $length); + + + # Write the packed data + $this->_append( $header, $data); + + return $str_error; +}*/ + + /** + * This method is used to set the height and XF format for a row. + * Writes the BIFF record ROW. + * + * @access public + * @param integer $row The row to set + * @param integer $height Height we are giving to the row. + * Use NULL to set XF without setting height + * @param mixed $format XF format we are giving to the row + */ + function set_row($row, $height, $format = 0) + { + $record = 0x0208; // Record identifier + $length = 0x0010; // Number of bytes to follow + + $colMic = 0x0000; // First defined column + $colMac = 0x0000; // Last defined column + $irwMac = 0x0000; // Used by Excel to optimise loading + $reserved = 0x0000; // Reserved + $grbit = 0x01C0; // Option flags. (monkey) see $1 do + $ixfe = $this->_XF($format); // XF index + + // Use set_row($row, NULL, $XF) to set XF without setting height + if ($height != NULL) { + $miyRw = $height * 20; // row height + } + else { + $miyRw = 0xff; // default row height is 256 + } + + $header = pack("vv", $record, $length); + $data = pack("vvvvvvvv", $row, $colMic, $colMac, $miyRw, + $irwMac,$reserved, $grbit, $ixfe); + $this->_append($header.$data); + } + + /** + * Writes Excel DIMENSIONS to define the area in which there is data. + */ + function _store_dimensions() + { + $record = 0x0000; // Record identifier + $length = 0x000A; // Number of bytes to follow + $row_min = $this->dim_rowmin; // First row + $row_max = $this->dim_rowmax; // Last row plus 1 + $col_min = $this->dim_colmin; // First column + $col_max = $this->dim_colmax; // Last column plus 1 + $reserved = 0x0000; // Reserved by Excel + + $header = pack("vv", $record, $length); + $data = pack("vvvvv", $row_min, $row_max, + $col_min, $col_max, $reserved); + $this->_prepend($header.$data); + } + + /** + * Write BIFF record Window2. + */ + function _store_window2() + { + $record = 0x023E; // Record identifier + $length = 0x000A; // Number of bytes to follow + + $grbit = 0x00B6; // Option flags + $rwTop = 0x0000; // Top row visible in window + $colLeft = 0x0000; // Leftmost column visible in window + $rgbHdr = 0x00000000; // Row/column heading and gridline color + + // The options flags that comprise $grbit + $fDspFmla = 0; // 0 - bit + $fDspGrid = 1; // 1 + $fDspRwCol = 1; // 2 + $fFrozen = $this->_frozen; // 3 + $fDspZeros = 1; // 4 + $fDefaultHdr = 1; // 5 + $fArabic = 0; // 6 + $fDspGuts = 1; // 7 + $fFrozenNoSplit = 0; // 0 - bit + $fSelected = $this->selected; // 1 + $fPaged = 1; // 2 + + $grbit = $fDspFmla; + $grbit |= $fDspGrid << 1; + $grbit |= $fDspRwCol << 2; + $grbit |= $fFrozen << 3; + $grbit |= $fDspZeros << 4; + $grbit |= $fDefaultHdr << 5; + $grbit |= $fArabic << 6; + $grbit |= $fDspGuts << 7; + $grbit |= $fFrozenNoSplit << 8; + $grbit |= $fSelected << 9; + $grbit |= $fPaged << 10; + + $header = pack("vv", $record, $length); + $data = pack("vvvV", $grbit, $rwTop, $colLeft, $rgbHdr); + $this->_append($header.$data); + } + + /** + * Write BIFF record DEFCOLWIDTH if COLINFO records are in use. + */ + function _store_defcol() + { + $record = 0x0055; // Record identifier + $length = 0x0002; // Number of bytes to follow + $colwidth = 0x0008; // Default column width + + $header = pack("vv", $record, $length); + $data = pack("v", $colwidth); + $this->_prepend($header.$data); + } + + /** + * Write BIFF record COLINFO to define column widths + * + * Note: The SDK says the record length is 0x0B but Excel writes a 0x0C + * length record. + * + * @param array $col_array This is the only parameter received and is composed of the following: + * 0 => First formatted column, + * 1 => Last formatted column, + * 2 => Col width (8.43 is Excel default), + * 3 => The optional XF format of the column, + * 4 => Option flags. + */ + function _store_colinfo($col_array) + { + if(isset($col_array[0])) { + $colFirst = $col_array[0]; + } + if(isset($col_array[1])) { + $colLast = $col_array[1]; + } + if(isset($col_array[2])) { + $coldx = $col_array[2]; + } + else { + $coldx = 8.43; + } + if(isset($col_array[3])) { + $format = $col_array[3]; + } + else { + $format = 0; + } + if(isset($col_array[4])) { + $grbit = $col_array[4]; + } + else { + $grbit = 0; + } + $record = 0x007D; // Record identifier + $length = 0x000B; // Number of bytes to follow + + $coldx += 0.72; // Fudge. Excel subtracts 0.72 !? + $coldx *= 256; // Convert to units of 1/256 of a char + + $ixfe = $this->_XF($format); + $reserved = 0x00; // Reserved + + $header = pack("vv", $record, $length); + $data = pack("vvvvvC", $colFirst, $colLast, $coldx, + $ixfe, $grbit, $reserved); + $this->_prepend($header.$data); + } + + /** + * Write BIFF record SELECTION. + * + * @param array $array array containing ($rwFirst,$colFirst,$rwLast,$colLast) + * @see set_selection() + */ + function _store_selection($array) + { + list($rwFirst,$colFirst,$rwLast,$colLast) = $array; + $record = 0x001D; // Record identifier + $length = 0x000F; // Number of bytes to follow + + $pnn = $this->_active_pane; // Pane position + $rwAct = $rwFirst; // Active row + $colAct = $colFirst; // Active column + $irefAct = 0; // Active cell ref + $cref = 1; // Number of refs + + if (!isset($rwLast)) { + $rwLast = $rwFirst; // Last row in reference + } + if (!isset($colLast)) { + $colLast = $colFirst; // Last col in reference + } + + // Swap last row/col for first row/col as necessary + if ($rwFirst > $rwLast) + { + list($rwFirst, $rwLast) = array($rwLast, $rwFirst); + } + + if ($colFirst > $colLast) + { + list($colFirst, $colLast) = array($colLast, $colFirst); + } + + $header = pack("vv", $record, $length); + $data = pack("CvvvvvvCC", $pnn, $rwAct, $colAct, + $irefAct, $cref, + $rwFirst, $rwLast, + $colFirst, $colLast); + $this->_append($header.$data); + } + + + /** + * Write BIFF record EXTERNCOUNT to indicate the number of external sheet + * references in a worksheet. + * + * Excel only stores references to external sheets that are used in formulas. + * For simplicity we store references to all the sheets in the workbook + * regardless of whether they are used or not. This reduces the overall + * complexity and eliminates the need for a two way dialogue between the formula + * parser the worksheet objects. + * + * @param integer $count The number of external sheet references in this worksheet + */ + function _store_externcount($count) + { + $record = 0x0016; // Record identifier + $length = 0x0002; // Number of bytes to follow + + $header = pack("vv", $record, $length); + $data = pack("v", $count); + $this->_prepend($header.$data); + } + + /** + * Writes the Excel BIFF EXTERNSHEET record. These references are used by + * formulas. A formula references a sheet name via an index. Since we store a + * reference to all of the external worksheets the EXTERNSHEET index is the same + * as the worksheet index. + * + * @param string $sheetname The name of a external worksheet + */ + function _store_externsheet($sheetname) + { + $record = 0x0017; // Record identifier + + // References to the current sheet are encoded differently to references to + // external sheets. + // + if ($this->name == $sheetname) { + $sheetname = ''; + $length = 0x02; // The following 2 bytes + $cch = 1; // The following byte + $rgch = 0x02; // Self reference + } + else { + $length = 0x02 + strlen($sheetname); + $cch = strlen($sheetname); + $rgch = 0x03; // Reference to a sheet in the current workbook + } + + $header = pack("vv", $record, $length); + $data = pack("CC", $cch, $rgch); + $this->_prepend($header.$data.$sheetname); + } + + /** + * Writes the Excel BIFF PANE record. + * The panes can either be frozen or thawed (unfrozen). + * Frozen panes are specified in terms of an integer number of rows and columns. + * Thawed panes are specified in terms of Excel's units for rows and columns. + * + * @param array $panes This is the only parameter received and is composed of the following: + * 0 => Vertical split position, + * 1 => Horizontal split position + * 2 => Top row visible + * 3 => Leftmost column visible + * 4 => Active pane + */ + function _store_panes($panes) + { + $y = $panes[0]; + $x = $panes[1]; + $rwTop = $panes[2]; + $colLeft = $panes[3]; + if(count($panes) > 4) { // if Active pane was received + $pnnAct = $panes[4]; + } + else { + $pnnAct = NULL; + } + $record = 0x0041; // Record identifier + $length = 0x000A; // Number of bytes to follow + + // Code specific to frozen or thawed panes. + if ($this->_frozen) { + // Set default values for $rwTop and $colLeft + if(!isset($rwTop)) { + $rwTop = $y; + } + if(!isset($colLeft)) { + $colLeft = $x; + } + } + else { + // Set default values for $rwTop and $colLeft + if(!isset($rwTop)) { + $rwTop = 0; + } + if(!isset($colLeft)) { + $colLeft = 0; + } + + // Convert Excel's row and column units to the internal units. + // The default row height is 12.75 + // The default column width is 8.43 + // The following slope and intersection values were interpolated. + // + $y = 20*$y + 255; + $x = 113.879*$x + 390; + } + + + // Determine which pane should be active. There is also the undocumented + // option to override this should it be necessary: may be removed later. + // + if (!isset($pnnAct)) + { + if ($x != 0 and $y != 0) + $pnnAct = 0; // Bottom right + if ($x != 0 and $y == 0) + $pnnAct = 1; // Top right + if ($x == 0 and $y != 0) + $pnnAct = 2; // Bottom left + if ($x == 0 and $y == 0) + $pnnAct = 3; // Top left + } + + $this->_active_pane = $pnnAct; // Used in _store_selection + + $header = pack("vv", $record, $length); + $data = pack("vvvvv", $x, $y, $rwTop, $colLeft, $pnnAct); + $this->_append($header.$data); + } + + /** + * Store the page setup SETUP BIFF record. + */ + function _store_setup() + { + $record = 0x00A1; // Record identifier + $length = 0x0022; // Number of bytes to follow + + $iPaperSize = $this->_paper_size; // Paper size + $iScale = $this->_print_scale; // Print scaling factor + $iPageStart = 0x01; // Starting page number + $iFitWidth = $this->_fit_width; // Fit to number of pages wide + $iFitHeight = $this->_fit_height; // Fit to number of pages high + $grbit = 0x00; // Option flags + $iRes = 0x0258; // Print resolution + $iVRes = 0x0258; // Vertical print resolution + $numHdr = $this->_margin_head; // Header Margin + $numFtr = $this->_margin_foot; // Footer Margin + $iCopies = 0x01; // Number of copies + + $fLeftToRight = 0x0; // Print over then down + $fLandscape = $this->_orientation; // Page orientation + $fNoPls = 0x0; // Setup not read from printer + $fNoColor = 0x0; // Print black and white + $fDraft = 0x0; // Print draft quality + $fNotes = 0x0; // Print notes + $fNoOrient = 0x0; // Orientation not set + $fUsePage = 0x0; // Use custom starting page + + $grbit = $fLeftToRight; + $grbit |= $fLandscape << 1; + $grbit |= $fNoPls << 2; + $grbit |= $fNoColor << 3; + $grbit |= $fDraft << 4; + $grbit |= $fNotes << 5; + $grbit |= $fNoOrient << 6; + $grbit |= $fUsePage << 7; + + $numHdr = pack("d", $numHdr); + $numFtr = pack("d", $numFtr); + if ($this->_byte_order) // if it's Big Endian + { + $numHdr = strrev($numHdr); + $numFtr = strrev($numFtr); + } + + $header = pack("vv", $record, $length); + $data1 = pack("vvvvvvvv", $iPaperSize, + $iScale, + $iPageStart, + $iFitWidth, + $iFitHeight, + $grbit, + $iRes, + $iVRes); + $data2 = $numHdr .$numFtr; + $data3 = pack("v", $iCopies); + $this->_prepend($header.$data1.$data2.$data3); + } + + /** + * Store the header caption BIFF record. + */ + function store_header() + { + $record = 0x0014; // Record identifier + + $str = $this->_header; // header string + $cch = strlen($str); // Length of header string + $length = 1 + $cch; // Bytes to follow + + $header = pack("vv", $record, $length); + $data = pack("C", $cch); + + $this->_append($header.$data.$str); + } + + /** + * Store the footer caption BIFF record. + */ + function store_footer() + { + $record = 0x0015; // Record identifier + + $str = $this->_footer; // Footer string + $cch = strlen($str); // Length of footer string + $length = 1 + $cch; // Bytes to follow + + $header = pack("vv", $record, $length); + $data = pack("C", $cch); + + $this->_append($header.$data.$str); + } + + /** + * Store the horizontal centering HCENTER BIFF record. + */ + function store_hcenter() + { + $record = 0x0083; // Record identifier + $length = 0x0002; // Bytes to follow + + $fHCenter = $this->_hcenter; // Horizontal centering + + $header = pack("vv", $record, $length); + $data = pack("v", $fHCenter); + + $this->_append($header.$data); + } + + /** + * Store the vertical centering VCENTER BIFF record. + */ + function store_vcenter() + { + $record = 0x0084; // Record identifier + $length = 0x0002; // Bytes to follow + + $fVCenter = $this->_vcenter; // Horizontal centering + + $header = pack("vv", $record, $length); + $data = pack("v", $fVCenter); + $this->_append($header.$data); + } + + /** + * Store the LEFTMARGIN BIFF record. + */ + function _store_margin_left() + { + $record = 0x0026; // Record identifier + $length = 0x0008; // Bytes to follow + + $margin = $this->_margin_left; // Margin in inches + + $header = pack("vv", $record, $length); + $data = pack("d", $margin); + if ($this->_byte_order) // if it's Big Endian + { + $data = strrev($data); + } + + $this->_append($header.$data); + } + + /** + * Store the RIGHTMARGIN BIFF record. + */ + function _store_margin_right() + { + $record = 0x0027; // Record identifier + $length = 0x0008; // Bytes to follow + + $margin = $this->_margin_right; // Margin in inches + + $header = pack("vv", $record, $length); + $data = pack("d", $margin); + if ($this->_byte_order) // if it's Big Endian + { + $data = strrev($data); + } + + $this->_append($header.$data); + } + + /** + * Store the TOPMARGIN BIFF record. + */ + function _store_margin_top() + { + $record = 0x0028; // Record identifier + $length = 0x0008; // Bytes to follow + + $margin = $this->_margin_top; // Margin in inches + + $header = pack("vv", $record, $length); + $data = pack("d", $margin); + if ($this->_byte_order) // if it's Big Endian + { + $data = strrev($data); + } + + $this->_append($header.$data); + } + + /** + * Store the BOTTOMMARGIN BIFF record. + */ + function _store_margin_bottom() + { + $record = 0x0029; // Record identifier + $length = 0x0008; // Bytes to follow + + $margin = $this->_margin_bottom; // Margin in inches + + $header = pack("vv", $record, $length); + $data = pack("d", $margin); + if ($this->_byte_order) // if it's Big Endian + { + $data = strrev($data); + } + + $this->_append($header.$data); + } + + /** + * This is an Excel97/2000 method. It is required to perform more complicated + * merging than the normal set_align('merge'). It merges the area given by + * its arguments. + * + * @access public + * @param integer $first_row First row of the area to merge + * @param integer $first_col First column of the area to merge + * @param integer $last_row Last row of the area to merge + * @param integer $last_col Last column of the area to merge + */ + function merge_cells($first_row, $first_col, $last_row, $last_col) + { + $record = 0x00E5; // Record identifier + $length = 0x000A; // Bytes to follow + $cref = 1; // Number of refs + + // Swap last row/col for first row/col as necessary + if ($first_row > $last_row) { + list($first_row, $last_row) = array($last_row, $first_row); + } + + if ($first_col > $last_col) { + list($first_col, $last_col) = array($last_col, $first_col); + } + + $header = pack("vv", $record, $length); + $data = pack("vvvvv", $cref, $first_row, $last_row, + $first_col, $last_col); + + $this->_append($header.$data); + } + + /** + * Write the PRINTHEADERS BIFF record. + */ + function _store_print_headers() + { + $record = 0x002a; // Record identifier + $length = 0x0002; // Bytes to follow + + $fPrintRwCol = $this->_print_headers; // Boolean flag + + $header = pack("vv", $record, $length); + $data = pack("v", $fPrintRwCol); + $this->_prepend($header.$data); + } + + /** + * Write the PRINTGRIDLINES BIFF record. Must be used in conjunction with the + * GRIDSET record. + */ + function _store_print_gridlines() + { + $record = 0x002b; // Record identifier + $length = 0x0002; // Bytes to follow + + $fPrintGrid = $this->_print_gridlines; // Boolean flag + + $header = pack("vv", $record, $length); + $data = pack("v", $fPrintGrid); + $this->_prepend($header.$data); + } + + /** + * Write the GRIDSET BIFF record. Must be used in conjunction with the + * PRINTGRIDLINES record. + */ + function _store_gridset() + { + $record = 0x0082; // Record identifier + $length = 0x0002; // Bytes to follow + + $fGridSet = !($this->_print_gridlines); // Boolean flag + + $header = pack("vv", $record, $length); + $data = pack("v", $fGridSet); + $this->_prepend($header.$data); + } + + /** + * Write the WSBOOL BIFF record, mainly for fit-to-page. Used in conjunction + * with the SETUP record. + */ + function _store_wsbool() + { + $record = 0x0081; // Record identifier + $length = 0x0002; // Bytes to follow + + // The only option that is of interest is the flag for fit to page. So we + // set all the options in one go. + // + if ($this->_fit_page) { + $grbit = 0x05c1; + } + else { + $grbit = 0x04c1; + } + + $header = pack("vv", $record, $length); + $data = pack("v", $grbit); + $this->_prepend($header.$data); + } + + + /** + * Write the HORIZONTALPAGEBREAKS BIFF record. + */ + function _store_hbreak() + { + // Return if the user hasn't specified pagebreaks + if(empty($this->_hbreaks)) { + return; + } + + // Sort and filter array of page breaks + $breaks = $this->_hbreaks; + sort($breaks,SORT_NUMERIC); + if($breaks[0] == 0) { // don't use first break if it's 0 + array_shift($breaks); + } + + $record = 0x001b; // Record identifier + $cbrk = count($breaks); // Number of page breaks + $length = ($cbrk + 1) * 2; // Bytes to follow + + $header = pack("vv", $record, $length); + $data = pack("v", $cbrk); + + // Append each page break + foreach($breaks as $break) { + $data .= pack("v", $break); + } + + $this->_prepend($header.$data); + } + + + /** + * Write the VERTICALPAGEBREAKS BIFF record. + */ + function _store_vbreak() + { + // Return if the user hasn't specified pagebreaks + if(empty($this->_vbreaks)) { + return; + } + + // 1000 vertical pagebreaks appears to be an internal Excel 5 limit. + // It is slightly higher in Excel 97/200, approx. 1026 + $breaks = array_slice($this->_vbreaks,0,1000); + + // Sort and filter array of page breaks + sort($breaks,SORT_NUMERIC); + if($breaks[0] == 0) { // don't use first break if it's 0 + array_shift($breaks); + } + + $record = 0x001a; // Record identifier + $cbrk = count($breaks); // Number of page breaks + $length = ($cbrk + 1) * 2; // Bytes to follow + + $header = pack("vv", $record, $length); + $data = pack("v", $cbrk); + + // Append each page break + foreach ($breaks as $break) { + $data .= pack("v", $break); + } + + $this->_prepend($header.$data); + } + + /** + * Set the Biff PROTECT record to indicate that the worksheet is protected. + */ + function _store_protect() + { + // Exit unless sheet protection has been specified + if($this->_protect == 0) { + return; + } + + $record = 0x0012; // Record identifier + $length = 0x0002; // Bytes to follow + + $fLock = $this->_protect; // Worksheet is protected + + $header = pack("vv", $record, $length); + $data = pack("v", $fLock); + + $this->_prepend($header.$data); + } + + /** + * Write the worksheet PASSWORD record. + */ + function _store_password() + { + // Exit unless sheet protection and password have been specified + if(($this->_protect == 0) or (!isset($this->_password))) { + return; + } + + $record = 0x0013; // Record identifier + $length = 0x0002; // Bytes to follow + + $wPassword = $this->_password; // Encoded password + + $header = pack("vv", $record, $length); + $data = pack("v", $wPassword); + + $this->_prepend($header.$data); + } + + /** + * Insert a 24bit bitmap image in a worksheet. The main record required is + * IMDATA but it must be proceeded by a OBJ record to define its position. + * + * @access public + * @param integer $row The row we are going to insert the bitmap into + * @param integer $col The column we are going to insert the bitmap into + * @param string $bitmap The bitmap filename + * @param integer $x The horizontal position (offset) of the image inside the cell. + * @param integer $y The vertical position (offset) of the image inside the cell. + * @param integer $scale_x The horizontal scale + * @param integer $scale_y The vertical scale + */ + function insert_bitmap($row, $col, $bitmap, $x = 0, $y = 0, $scale_x = 1, $scale_y = 1) + { + list($width, $height, $size, $data) = $this->_process_bitmap($bitmap); + + // Scale the frame of the image. + $width *= $scale_x; + $height *= $scale_y; + + // Calculate the vertices of the image and write the OBJ record + $this->_position_image($col, $row, $x, $y, $width, $height); + + // Write the IMDATA record to store the bitmap data + $record = 0x007f; + $length = 8 + $size; + $cf = 0x09; + $env = 0x01; + $lcb = $size; + + $header = pack("vvvvV", $record, $length, $cf, $env, $lcb); + $this->_append($header.$data); + } + + /** + * Calculate the vertices that define the position of the image as required by + * the OBJ record. + * + * +------------+------------+ + * | A | B | + * +-----+------------+------------+ + * | |(x1,y1) | | + * | 1 |(A1)._______|______ | + * | | | | | + * | | | | | + * +-----+----| BITMAP |-----+ + * | | | | | + * | 2 | |______________. | + * | | | (B2)| + * | | | (x2,y2)| + * +---- +------------+------------+ + * + * Example of a bitmap that covers some of the area from cell A1 to cell B2. + * + * Based on the width and height of the bitmap we need to calculate 8 vars: + * $col_start, $row_start, $col_end, $row_end, $x1, $y1, $x2, $y2. + * The width and height of the cells are also variable and have to be taken into + * account. + * The values of $col_start and $row_start are passed in from the calling + * function. The values of $col_end and $row_end are calculated by subtracting + * the width and height of the bitmap from the width and height of the + * underlying cells. + * The vertices are expressed as a percentage of the underlying cell width as + * follows (rhs values are in pixels): + * + * x1 = X / W *1024 + * y1 = Y / H *256 + * x2 = (X-1) / W *1024 + * y2 = (Y-1) / H *256 + * + * Where: X is distance from the left side of the underlying cell + * Y is distance from the top of the underlying cell + * W is the width of the cell + * H is the height of the cell + * + * @note the SDK incorrectly states that the height should be expressed as a + * percentage of 1024. + * @param integer $col_start Col containing upper left corner of object + * @param integer $row_start Row containing top left corner of object + * @param integer $x1 Distance to left side of object + * @param integer $y1 Distance to top of object + * @param integer $width Width of image frame + * @param integer $height Height of image frame + */ + function _position_image($col_start, $row_start, $x1, $y1, $width, $height) + { + // Initialise end cell to the same as the start cell + $col_end = $col_start; // Col containing lower right corner of object + $row_end = $row_start; // Row containing bottom right corner of object + + // Zero the specified offset if greater than the cell dimensions + if ($x1 >= $this->size_col($col_start)) + { + $x1 = 0; + } + if ($y1 >= $this->size_row($row_start)) + { + $y1 = 0; + } + + $width = $width + $x1 -1; + $height = $height + $y1 -1; + + // Subtract the underlying cell widths to find the end cell of the image + while ($width >= $this->size_col($col_end)) { + $width -= $this->size_col($col_end); + $col_end++; + } + + // Subtract the underlying cell heights to find the end cell of the image + while ($height >= $this->size_row($row_end)) { + $height -= $this->size_row($row_end); + $row_end++; + } + + // Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell + // with zero eight or width. + // + if ($this->size_col($col_start) == 0) + return; + if ($this->size_col($col_end) == 0) + return; + if ($this->size_row($row_start) == 0) + return; + if ($this->size_row($row_end) == 0) + return; + + // Convert the pixel values to the percentage value expected by Excel + $x1 = $x1 / $this->size_col($col_start) * 1024; + $y1 = $y1 / $this->size_row($row_start) * 256; + $x2 = $width / $this->size_col($col_end) * 1024; // Distance to right side of object + $y2 = $height / $this->size_row($row_end) * 256; // Distance to bottom of object + + $this->_store_obj_picture( $col_start, $x1, + $row_start, $y1, + $col_end, $x2, + $row_end, $y2 + ); + } + + /** + * Convert the width of a cell from user's units to pixels. By interpolation + * the relationship is: y = 7x +5. If the width hasn't been set by the user we + * use the default value. If the col is hidden we use a value of zero. + * + * @param integer $col The column + * @return integer The width in pixels + */ + function size_col($col) + { + // Look up the cell value to see if it has been changed + if (isset($this->col_sizes[$col])) { + if ($this->col_sizes[$col] == 0) { + return(0); + } + else { + return(floor(7 * $this->col_sizes[$col] + 5)); + } + } + else { + return(64); + } + } + + /** + * Convert the height of a cell from user's units to pixels. By interpolation + * the relationship is: y = 4/3x. If the height hasn't been set by the user we + * use the default value. If the row is hidden we use a value of zero. (Not + * possible to hide row yet). + * + * @param integer $row The row + * @return integer The width in pixels + */ + function size_row($row) + { + // Look up the cell value to see if it has been changed + if (isset($this->row_sizes[$row])) { + if ($this->row_sizes[$row] == 0) { + return(0); + } + else { + return(floor(4/3 * $this->row_sizes[$row])); + } + } + else { + return(17); + } + } + + /** + * Store the OBJ record that precedes an IMDATA record. This could be generalise + * to support other Excel objects. + * + * @param integer $colL Column containing upper left corner of object + * @param integer $dxL Distance from left side of cell + * @param integer $rwT Row containing top left corner of object + * @param integer $dyT Distance from top of cell + * @param integer $colR Column containing lower right corner of object + * @param integer $dxR Distance from right of cell + * @param integer $rwB Row containing bottom right corner of object + * @param integer $dyB Distance from bottom of cell + */ + function _store_obj_picture($colL,$dxL,$rwT,$dyT,$colR,$dxR,$rwB,$dyB) + { + $record = 0x005d; // Record identifier + $length = 0x003c; // Bytes to follow + + $cObj = 0x0001; // Count of objects in file (set to 1) + $OT = 0x0008; // Object type. 8 = Picture + $id = 0x0001; // Object ID + $grbit = 0x0614; // Option flags + + $cbMacro = 0x0000; // Length of FMLA structure + $Reserved1 = 0x0000; // Reserved + $Reserved2 = 0x0000; // Reserved + + $icvBack = 0x09; // Background colour + $icvFore = 0x09; // Foreground colour + $fls = 0x00; // Fill pattern + $fAuto = 0x00; // Automatic fill + $icv = 0x08; // Line colour + $lns = 0xff; // Line style + $lnw = 0x01; // Line weight + $fAutoB = 0x00; // Automatic border + $frs = 0x0000; // Frame style + $cf = 0x0009; // Image format, 9 = bitmap + $Reserved3 = 0x0000; // Reserved + $cbPictFmla = 0x0000; // Length of FMLA structure + $Reserved4 = 0x0000; // Reserved + $grbit2 = 0x0001; // Option flags + $Reserved5 = 0x0000; // Reserved + + + $header = pack("vv", $record, $length); + $data = pack("V", $cObj); + $data .= pack("v", $OT); + $data .= pack("v", $id); + $data .= pack("v", $grbit); + $data .= pack("v", $colL); + $data .= pack("v", $dxL); + $data .= pack("v", $rwT); + $data .= pack("v", $dyT); + $data .= pack("v", $colR); + $data .= pack("v", $dxR); + $data .= pack("v", $rwB); + $data .= pack("v", $dyB); + $data .= pack("v", $cbMacro); + $data .= pack("V", $Reserved1); + $data .= pack("v", $Reserved2); + $data .= pack("C", $icvBack); + $data .= pack("C", $icvFore); + $data .= pack("C", $fls); + $data .= pack("C", $fAuto); + $data .= pack("C", $icv); + $data .= pack("C", $lns); + $data .= pack("C", $lnw); + $data .= pack("C", $fAutoB); + $data .= pack("v", $frs); + $data .= pack("V", $cf); + $data .= pack("v", $Reserved3); + $data .= pack("v", $cbPictFmla); + $data .= pack("v", $Reserved4); + $data .= pack("v", $grbit2); + $data .= pack("V", $Reserved5); + + $this->_append($header.$data); + } + + /** + * Convert a 24 bit bitmap into the modified internal format used by Windows. + * This is described in BITMAPCOREHEADER and BITMAPCOREINFO structures in the + * MSDN library. + * + * @param string $bitmap The bitmap to process + * @return array Array with data and properties of the bitmap + */ + function _process_bitmap($bitmap) + { + // Open file. + $bmp_fd = fopen($bitmap,"rb"); + if (!$bmp_fd) { + die("Couldn't import $bitmap"); + } + + // Slurp the file into a string. + $data = fread($bmp_fd, filesize($bitmap)); + + // Check that the file is big enough to be a bitmap. + if (strlen($data) <= 0x36) { + die("$bitmap doesn't contain enough data.\n"); + } + + // The first 2 bytes are used to identify the bitmap. + $identity = unpack("A2", $data); + if ($identity[''] != "BM") { + die("$bitmap doesn't appear to be a valid bitmap image.\n"); + } + + // Remove bitmap data: ID. + $data = substr($data, 2); + + // Read and remove the bitmap size. This is more reliable than reading + // the data size at offset 0x22. + // + $size_array = unpack("V", substr($data, 0, 4)); + $size = $size_array['']; + $data = substr($data, 4); + $size -= 0x36; // Subtract size of bitmap header. + $size += 0x0C; // Add size of BIFF header. + + // Remove bitmap data: reserved, offset, header length. + $data = substr($data, 12); + + // Read and remove the bitmap width and height. Verify the sizes. + $width_and_height = unpack("V2", substr($data, 0, 8)); + $width = $width_and_height[1]; + $height = $width_and_height[2]; + $data = substr($data, 8); + if ($width > 0xFFFF) { + die("$bitmap: largest image width supported is 65k.\n"); + } + if ($height > 0xFFFF) { + die("$bitmap: largest image height supported is 65k.\n"); + } + + // Read and remove the bitmap planes and bpp data. Verify them. + $planes_and_bitcount = unpack("v2", substr($data, 0, 4)); + $data = substr($data, 4); + if ($planes_and_bitcount[2] != 24) { // Bitcount + die("$bitmap isn't a 24bit true color bitmap.\n"); + } + if ($planes_and_bitcount[1] != 1) { + die("$bitmap: only 1 plane supported in bitmap image.\n"); + } + + // Read and remove the bitmap compression. Verify compression. + $compression = unpack("V", substr($data, 0, 4)); + $data = substr($data, 4); + + //$compression = 0; + if ($compression[""] != 0) { + die("$bitmap: compression not supported in bitmap image.\n"); + } + + // Remove bitmap data: data size, hres, vres, colours, imp. colours. + $data = substr($data, 20); + + // Add the BITMAPCOREHEADER data + $header = pack("Vvvvv", 0x000c, $width, $height, 0x01, 0x18); + $data = $header . $data; + + return (array($width, $height, $size, $data)); + } + + /** + * Store the window zoom factor. This should be a reduced fraction but for + * simplicity we will store all fractions with a numerator of 100. + */ + function _store_zoom() + { + // If scale is 100 we don't need to write a record + if ($this->_zoom == 100) { + return; + } + + $record = 0x00A0; // Record identifier + $length = 0x0004; // Bytes to follow + + $header = pack("vv", $record, $length); + $data = pack("vv", $this->_zoom, 100); + $this->_append($header.$data); + } +} +?> diff --git a/vendor/write_excel/thanks b/vendor/write_excel/thanks new file mode 100644 index 0000000..b128adf --- /dev/null +++ b/vendor/write_excel/thanks @@ -0,0 +1,7 @@ +Inmortalizing those who have contributed(*) to Spreadsheet_WriteExcel:
+ Quentin Bennett
+ Kenneth G. Chin
+ Tomislav Goles
+
+
+(*) According to my own personal definition of contribution.
\ No newline at end of file |
