diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/CHANGES b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/CHANGES
new file mode 100644
index 00000000..2c82d57e
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/CHANGES
@@ -0,0 +1,15 @@
+Changelog for qCal, since Version 0.0.2
+---------------------------------------------
+
+January 14, 2010: v0.0.2
+--------------------
+
+ * Integrated qCal_DateTime sub-components into the library.
+ * Removed old qCal_Date component (replaced by qCal_DateTime, qCal_DateV2,
+ which has been renamed qCal_Date, and qCal_Time)
+ * Added full documentation for the library at http://qcal.lukevisinoni.com/ as
+ well as inside the "docs" sub-folder.
+ * Added this changelog.
+ * Added README and VERSION files.
+
+-- End of Changes --
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/LICENSE b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/LICENSE
new file mode 100644
index 00000000..fc8a5de7
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/LICENSE
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/README b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/README
new file mode 100644
index 00000000..cc1ccb9d
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/README
@@ -0,0 +1,33 @@
+qCal iCalendar Library for PHP5, by Luke Visinoni
+-------------------------------------------------------------------------------
+
+qCal is an object-oriented, open-source iCalendar library for PHP5.
+It is released under the LGPL license.
+
+Homepage: http://qcal.lukevisinoni.com/
+Documentation: http://qcal.lukevisinoni.com/doku.php?id=start
+Mailing List: http://groups.google.com/group/qcal
+Bugs: http://code.google.com/p/qcal/issues/list
+Repository: https://qcal.googlecode.com/svn/
+
+Installation
+------------
+
+This library requires no installation. At least not in the traditional sense.
+Simply upload to a path within PHP's include path and include
+"lib/autoload.php".
+
+For more info, visit http://qcal.lukevisinoni.com/doku.php?id=installation
+
+Show your support!
+------------------
+
+If you use the library and would like to show your support, feel free to make a
+donation. I accept donations via PayPal. You can send me money at the following
+e-mail address.
+
+ luke.visinoni@gmail.com
+
+I didn't write this library to make money, but I do appreciate donations. In
+fact, those users who donate to the project are by far cooler than those who
+don't. I like donors better than non-donors. LOL!
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/VERSION b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/VERSION
new file mode 100644
index 00000000..38c4ba3f
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/VERSION
@@ -0,0 +1 @@
+qCal-v0.0.2
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/autoload.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/autoload.php
new file mode 100644
index 00000000..ee8f1243
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/autoload.php
@@ -0,0 +1,15 @@
+
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component.php
new file mode 100644
index 00000000..7e28ffd7
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component.php
@@ -0,0 +1,523 @@
+ '-// Some Property Id//',
+ * 'someotherproperty' => null,
+ * qCal_Property_Version(2.0),
+ * ), array(
+ * qCal_Component_Daylight(),
+ * ));
+ */
+ public function __construct($properties = array(), $components = array()) {
+
+ foreach ($components as $component) {
+ // if value is an array, then each value inside of it will be a component
+ if ($component instanceof qCal_Component) {
+ $this->attach($component);
+ }
+ else throw new qCal_Exception_InvalidComponent('The second argument is optional, but if provided, must be an array of components');
+ }
+ foreach ($properties as $name => $value) {
+ // if value is an array, then each value inside of it will be a property
+ if (is_array($value)) {
+ foreach ($value as $val) {
+ if ($val instanceof qCal_Property) {
+ $this->addProperty($val);
+ } else {
+ $this->addProperty($name, $val);
+ }
+ }
+ } else {
+ if ($value instanceof qCal_Property) {
+ $this->addProperty($value);
+ } else {
+ $this->addProperty($name, $value);
+ }
+ }
+ }
+ // I think it would make more sense to do validation at render time. That way you don't have
+ // to have all of the required components and properties when you instantiate. Also, that way
+ // components don't need to be aware of eachother until render time (or until validate() is called
+ // explicitly). @todo
+ // $this->validate();
+
+ }
+ /**
+ * @todo (lazy load functionality) Check that this is a valid component. This method is sort of lazy-loaded. It only gets called
+ * if the user has requested data that requires validation and the component has not been validated already.
+ * @todo Shouldn't this loop over children and validate them too? Maybe optionally?
+ */
+ public function validate() {
+
+ // if we're missing any required properties and they have no default, throw an exception
+ $properties = array();
+ foreach ($this->getProperties() as $property) {
+ if (is_array($property)) {
+ foreach ($property as $prop) {
+ $properties[] = $prop->getName();
+ }
+ } else {
+ $properties[] = $property->getName();
+ }
+ }
+ $missing = array_diff($this->requiredProperties, array_unique($properties));
+ foreach ($missing as $propertyname) {
+ // the property factory will throw an exception if it's passed a null value for a property with no default
+ try {
+ $property = qCal_Property::factory($propertyname, null);
+ $this->addProperty($property);
+ } catch (qCal_Exception_InvalidPropertyValue $e) {
+ // if that's the case, catch the exception and throw a missing property exception
+ throw new qCal_Exception_MissingProperty($this->getName() . " component requires " . $propertyname . " property");
+ }
+ }
+ // this allows per-component validation :)
+ $this->doValidation();
+
+ }
+ /**
+ * Returns the component name
+ * @return string
+ */
+ public function getName() {
+
+ return $this->name;
+
+ }
+ /**
+ * Returns true if this component can be attached to $component
+ * I'm sure there's a better way to do this, but this works for now
+ */
+ public function canAttachTo(qCal_Component $component) {
+
+ if (in_array($component->getName(), $this->allowedComponents)) return true;
+
+ }
+ /**
+ * Attach a component to this component (alarm inside event for example)
+ * @todo There may be an issue with the way this is done. When parsing a file, if a component
+ * or property with a tzid comes before its corresponding vtimezone component, an exception
+ * will be thrown. I'm don't think the RFC specifies that requirement (that timezone components
+ * must come before their corresponding tzids)
+ * @todo Sub-components such as Vevent need to be able to access the main vcalendar object
+ * for several reasons.
+ * - If a vtodo has a tzid, it needs to be able to determine that the corresponding
+ * vtimezone component is available.
+ * - If components need to relate to eachother, they can only find eachother through
+ * the main vcalendar object.
+ * - Freebusy time can only be determined by polling all components in the main vcalendar
+ * object.
+ * - More to come probably
+ */
+ public function attach(qCal_Component $component) {
+
+ if (!$component->canAttachTo($this)) {
+ throw new qCal_Exception_InvalidComponent($component->getName() . ' cannot be attached to ' . $this->getName());
+ }
+ $component->setParent($this);
+ // make sure if a timezone is requested that it is available...
+ $timezones = $this->getTimezones();
+ $tzids = array_keys($timezones);
+ // we only need to check if tzid exists if we are attaching something other than a timezone...
+ if (!($component instanceof qCal_Component_Vtimezone)) {
+ foreach ($component->getProperties() as $pname => $properties) {
+ $pname = strtoupper($pname); // probably redundant...
+ foreach ($properties as $property) {
+ switch ($pname) {
+ case "TZID":
+ $tzid = $property->getValue();
+ if (!array_key_exists($tzid, $tzids)) {
+ throw new qCal_Exception_MissingComponent('TZID "' . $tzid . '" not defined');
+ }
+ break;
+ }
+ $params = $property->getParams();
+ foreach ($params as $param => $val) {
+ $param = strtoupper($param); // probably redundant...
+ switch ($param) {
+ case "TZID":
+ $tzid = $val;
+ if (!array_key_exists($tzid, $tzids)) {
+ throw new qCal_Exception_MissingComponent('TZID "' . $tzid . '" not defined');
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ $this->children[$component->getName()][] = $component;
+
+ }
+ /**
+ * Set the parent of this component
+ * @todo I'm not sure this will suffice. See the attach method for reasoning behind this.
+ */
+ public function setParent(qCal_Component $component) {
+
+ $this->parent = $component;
+
+ }
+ /**
+ * Get the parent of this component (if there is one)
+ */
+ public function getParent() {
+
+ return $this->parent;
+
+ }
+ /**
+ * The only thing I need this for so far is the parser, but it may come in handy for the facade as well
+ */
+ static public function factory($name, $properties = array()) {
+
+ if (empty($name)) return false;
+ // capitalize
+ $component = ucfirst(strtolower($name));
+ $className = "qCal_Component_" . $component;
+ $fileName = str_replace("_", DIRECTORY_SEPARATOR, $className) . ".php";
+ qCal_Loader::loadFile($fileName);
+ $class = new $className($properties);
+ return $class;
+
+ }
+
+ /**
+ * I'm not sure how this should work. Not sure if it should be setProperty,
+ * addProperty, both? Because properties on some components can be set multiple
+ * times, while some properties have multiple values. :( I am trying to consider
+ * a case where somebody needs to open a calendar, change a few properties on a
+ * component (change event time for instance). I think the way I'll handle properties
+ * that can be set multiple times is I'll create a method do delete properties based
+ * on values, parameters, etc. since they don't really have IDs. So I tihnk I'll go
+ * with addProperty :)
+ */
+ public function addProperty($property, $value = null, $params = array()) {
+
+ if (!($property instanceof qCal_Property)) {
+ $property = qCal_Property::factory($property, $value, $params);
+ }
+ if (!$property->of($this)) {
+ throw new qCal_Exception_InvalidProperty($this->getName() . " component does not allow " . $property->getName() . " property");
+ }
+ if (!$property->allowMultiple()) {
+ unset($this->properties[$property->getName()]);
+ }
+ $this->properties[$property->getName()][] = $property;
+
+ }
+ /**
+ * Returns property of this component by name
+ *
+ * @todo Since the same property can appear in a component more than once, this method
+ * doesn't make that much sense unless it returns all of the instances of the property
+ * @return array of qCal_Property
+ */
+ public function getProperty($name) {
+
+ $name = strtoupper($name);
+ if ($this->hasProperty($name)) {
+ return $this->properties[$name];
+ }
+
+ }
+
+ /**
+ * Returns true if this component contains a property of $name
+ *
+ * @return boolean
+ */
+ public function hasProperty($name) {
+
+ $name = strtoupper($name);
+ return isset($this->properties[$name]);
+
+ }
+
+ /**
+ * Returns true if this component contains a property of $name
+ *
+ * @return boolean
+ */
+ public function hasComponent($name) {
+
+ $name = strtoupper($name);
+ return isset($this->children[$name]);
+
+ }
+ /**
+ * Returns the child component requested
+ */
+ public function getComponent($name) {
+
+ $name = strtoupper($name);
+ if ($this->hasComponent($name)) {
+ return $this->children[$name];
+ }
+
+ }
+
+ /*
+ public function clearProperties() {
+
+ $this->properties = array();
+
+ }
+
+ public function clearChildren() {
+
+ $this->children = array();
+
+ }
+ */
+
+ public function getProperties() {
+
+ return $this->properties;
+
+ }
+
+ public function getChildren() {
+
+ return $this->children;
+
+ }
+
+ /**
+ * Gets the parent-most component in the tree. I would really like to come up
+ * with a cleaner way to access other components from within a component, but oh well.
+ */
+ public function getRootComponent() {
+
+ $parent = $this;
+ while (!($parent instanceof qCal_Component_Vcalendar)) {
+ if (!$parent->getParent()) break;
+ $parent = $parent->getParent();
+ }
+ return $parent;
+
+ }
+
+ /**
+ * Renders the calendar, by default in icalendar format. If you pass
+ * in a renderer, it will use that instead
+ *
+ * @return mixed Depends on the renderer
+ * @todo Would it make more sense to pass the component to the renderer, or the renderer
+ * to the component? I'm not sure components should know about rendering.
+ */
+ public function render(qCal_Renderer $renderer = null) {
+
+ $this->validate();
+ if (is_null($renderer)) $renderer = new qCal_Renderer_iCalendar();
+ return $renderer->render($this);
+
+ }
+
+ /**
+ * Output the icalendar component as a string (render it)
+ */
+ public function __toString() {
+
+ return $this->render();
+
+ }
+
+ /**
+ * getFreeBusyTime
+ * Looks through all of the data in the calendar and returns a qCal_Component_Vfreebusy object
+ * with free/busy time from $startdate to $enddate. The component will contain all components, but some
+ * may have their transparency set to "transparent".
+ * @todo This cannot be finished until recurring events are finished, since free/busy does not allow
+ * recurrence rules, each instance of a recurrence would need to be calculated out and passed into the free/busy
+ * component, so that the component would contain concrete instances of each event recurrence.
+ */
+ public function getFreeBusyTime() {
+
+ $root = $this->getRootComponent();
+ foreach ($root->getChildren() as $children) {
+ foreach ($children as $child) {
+ // now get the object's free/busy time
+ }
+ }
+
+ }
+ /**
+ * getTimeZones
+ */
+ public function getTimezones() {
+
+ $tzs = array();
+ $root = $this->getRootComponent();
+ foreach ($root->getChildren() as $children) {
+ foreach ($children as $child) {
+ // if the child is a vtimezone, add it to the results
+ // @todo make sure that tzid is available, throw exception otherwise
+ if ($child instanceof qCal_Component_Vtimezone) {
+ $tzid = $child->getTzid();
+ $tzid = strtoupper($tzid);
+ $tzs[$tzid] = $child;
+ }
+ }
+ }
+ return $tzs;
+
+ }
+ /**
+ * Get a specific timezone by tzid
+ * @param string The timezone identifier
+ */
+ public function getTimezone($tzid) {
+
+ $tzid = strtoupper($tzid);
+ $root = $this->getRootComponent();
+ $timezones = $root->getTimezones();
+ if (array_key_exists($tzid, $timezones)) {
+ return $timezones[$tzid];
+ }
+ return false;
+
+ }
+ /**
+ * Allows for components to get and set property values by calling
+ * qCal_Component::getPropertyName() and qCal_Component::setPropertyName('2.0') where propertyName is the property name
+ * to be set and $val is the property value.
+ * This is just a convenience facade, it isn't going to be used within the library as much as by end-users
+ * @todo I can't decided whether to maybe get rid of the facade methods at least for now since some properties
+ * can potentially return multiple values and that makes the interface inconsistent
+ */
+ public function __call($method, $params) {
+
+ $firstthree = substr($method, 0, 3);
+ $name = substr($method, 3);
+ if ($firstthree == "get") {
+ // if property is allowed multiple times, an array is returned, otherwise just the one component
+ if ($this->hasProperty($name)) {
+ $property = $this->getProperty($name);
+ if (!$property[0]->allowMultiple()) {
+ return $property[0];
+ } else {
+ return $property;
+ }
+ }
+ } elseif ($firstthree == "set") {
+ $value = isset($params[0]) ? $params[0] : null;
+ $params = isset($params[1]) ? $params[1] : array();
+ $property = qCal_Property::factory($name, $value, $params);
+ $this->addProperty($property);
+ } elseif ($firstthree == "add") {
+ // add property type
+ $property = qCal_Property::factory($name, $params);
+ $this->addProperty($property);
+ return $this;
+ }
+ // throw exception here?
+ // throw new qCal_Exception();
+
+ }
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component/Daylight.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component/Daylight.php
new file mode 100644
index 00000000..ea361a07
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component/Daylight.php
@@ -0,0 +1,367 @@
+getAction();
+ switch(strtoupper($action->getValue())) {
+ case "AUDIO":
+ // action, trigger (already covered by parent constructor)
+ // attach can only occur once
+ $attach = $this->getProperty('ATTACH');
+ if (count($attach) > 1) {
+ throw new qCal_Exception_InvalidProperty('VALARM audio component can contain one and only one ATTACH property');
+ }
+ break;
+ case "DISPLAY":
+ // action, trigger, description
+ if (!$this->hasProperty('DESCRIPTION')) {
+ throw new qCal_Exception_MissingProperty("DISPLAY VALARM component requires DESCRIPTION property");
+ }
+ break;
+ case "EMAIL":
+ // action, description, trigger, summary
+ if (!$this->hasProperty('DESCRIPTION')) {
+ throw new qCal_Exception_MissingProperty("EMAIL VALARM component requires DESCRIPTION property");
+ }
+ if (!$this->hasProperty('SUMMARY')) {
+ throw new qCal_Exception_MissingProperty("EMAIL VALARM component requires SUMMARY property");
+ }
+ break;
+ case "PROCEDURE":
+ // action, attach, trigger
+ $attach = $this->getProperty('ATTACH');
+ if (count($attach) > 1) {
+ throw new qCal_Exception_InvalidProperty('VALARM procedure component can contain one and only one ATTACH property');
+ }
+ if (count($attach) < 1) {
+ throw new qCal_Exception_MissingProperty("PROCEDURE VALARM component requires ATTACH property");
+ }
+ break;
+ }
+ if ($this->hasProperty('DURATION')) {
+ if (!$this->hasProperty('REPEAT')) {
+ throw new qCal_Exception_MissingProperty("VALARM component with a DURATION property requires a REPEAT property");
+ }
+ }
+ if ($this->hasProperty('REPEAT')) {
+ if (!$this->hasProperty('DURATION')) {
+ throw new qCal_Exception_MissingProperty("VALARM component with a REPEAT property requires a DURATION property");
+ }
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component/Vcalendar.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component/Vcalendar.php
new file mode 100644
index 00000000..4478e94a
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component/Vcalendar.php
@@ -0,0 +1,59 @@
+getProperties();
+ $propnames = array_keys($properties);
+ if (in_array('DTEND', $propnames) && in_array('DURATION', $propnames)) {
+ throw new qCal_Exception_InvalidProperty('DTEND and DURATION cannot both occur in the same VEVENT component');
+ }
+ if (in_array('DTSTART', $propnames)) {
+ $dtstart = $this->getProperty('dtstart');
+ $dtstart = $dtstart[0];
+ // check that if dtstart is a DATE that dtend is a DATE
+ if ($dtstart->getType() == 'DATE') {
+ if (in_array('DTEND', $propnames)) {
+ $dtend = $this->getProperty('dtend');
+ $dtend = $dtend[0];
+ if ($dtend->getType() != 'DATE') {
+ throw new qCal_Exception_InvalidProperty('If DTSTART property is specified as a DATE property, so must DTEND');
+ }
+ }
+ }
+ // check that dtstart comes before dtend
+ if (in_array('DTEND', $propnames)) {
+ $dtend = $this->getProperty('dtend');
+ $dtend = $dtend[0];
+ $startdate = strtotime($dtstart->getValue());
+ $enddate = strtotime($dtend->getValue());
+ if ($startdate > $enddate) {
+ throw new qCal_Exception_InvalidProperty('DTSTART property must come before DTEND');
+ }
+ }
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component/Vfreebusy.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component/Vfreebusy.php
new file mode 100644
index 00000000..003a67da
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component/Vfreebusy.php
@@ -0,0 +1,137 @@
+getChildren();
+ if (!array_key_exists('DAYLIGHT', $children) && !array_key_exists('STANDARD', $children)) {
+ throw new qCal_Exception_MissingComponent('Either a STANDARD or DAYLIGHT component is required within a VTIMEZONE component');
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component/Vtodo.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component/Vtodo.php
new file mode 100644
index 00000000..702ede26
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Component/Vtodo.php
@@ -0,0 +1,81 @@
+ "january",
+ 2 => "february",
+ 3 => "march",
+ 4 => "april",
+ 5 => "may",
+ 6 => "june",
+ 7 => "july",
+ 8 => "august",
+ 9 => "september",
+ 10 => "october",
+ 11 => "november",
+ 12 => "december",
+ );
+ /**
+ * @var array The month in a two-dimensional array (picture a calendar)
+ */
+ protected $monthMap = array();
+ /**
+ * Class constructor
+ * @param int The year of this date
+ * @param int The month of this date
+ * @param int The day of this date
+ */
+ public function __construct($year = null, $month = null, $day = null, $rollover = false) {
+
+ $this->setDate($year, $month, $day, $rollover);
+
+ }
+ /**
+ * Set the date of this class
+ * The date defaults to now. If any part of the date is missing, it will default to whatever "now"'s
+ * date portion is. For instance, if the year provided is 2006 and no other portion is given, it will
+ * default to today's month and day, but in the year 2006. If, for any reason the date defaults to a
+ * nonsensical date, an exception will be thrown. For instance, if you specify the year as 2006, and
+ * the current date is february 29th, an exception will be thrown because the 29th of February does not
+ * exist in 2006.
+ * @param int The year of this date
+ * @param int The month of this date
+ * @param int The day of this date
+ * @throws qCal_Date_Exception_InvalidDate
+ */
+ protected function setDate($year = null, $month = null, $day = null, $rollover = false) {
+
+ $now = getdate();
+ if (is_null($year)) {
+ $year = $now['year'];
+ }
+ if (is_null($month)) {
+ $month = $now['mon'];
+ }
+ if (is_null($day)) {
+ $day = $now['mday'];
+ }
+
+ $this->date = gmmktime(0, 0, 0, $month, $day, $year);
+ $this->dateArray = self::gmgetdate($this->date);
+ if (!$rollover) {
+ if ($this->dateArray["mday"] != $day || $this->dateArray["mon"] != $month || $this->dateArray["year"] != $year) {
+ throw new qCal_DateTime_Exception_InvalidDate("Invalid date specified for qCal_Date: \"{$month}/{$day}/{$year}\"");
+ }
+ }
+
+ // @todo Look into how much more efficient it might be to call date() only once and then break apart the result...
+ $formatString = "d|D|j|l|N|S|w|z|W|F|m|M|n|t|L|o|y|Y|c|r|U";
+ $keys = explode("|", $formatString);
+ $vals = explode("|", gmdate($formatString, $this->date));
+ $this->dateArray = array_merge($this->dateArray, array_combine($keys, $vals));
+ return $this;
+
+ }
+ /**
+ * This is a factory method. It allows you to create a date by string or by another date object (to make a copy)
+ */
+ public static function factory($date) {
+
+ if (is_integer($date)) {
+ // @todo Handle timestamps
+ }
+ if (is_string($date)) {
+ if (!$timestamp = strtotime($date)) {
+ // if unix timestamp can't be created throw an exception
+ throw new qCal_Date_Exception_InvalidDate("Invalid or ambiguous date string passed to qCal_Date::factory()");
+ }
+ }
+
+ $date = self::gmgetdate($timestamp);
+ $newdate = gmmktime(0, 0, 0, $date['mon'], $date['mday'], $date['year']);
+ $newdate = self::gmgetdate($newdate);
+ return new qCal_Date($newdate['year'], $newdate['mon'], $newdate['mday']);
+
+ }
+ /**
+ * Set the format that should be used when calling either __toString() or format() without an argument.
+ * @param string $format
+ */
+ public function setFormat($format) {
+
+ $this->format = (string) $format;
+ return $this;
+
+ }
+ /**
+ * Formats the date according to either the existing $this->format, or if the $format arg is passed
+ * in, it uses that.
+ * @param string The format that is to be used (according to php's date function). Only date-related metacharacters work.
+ */
+ public function format($format) {
+
+ $escape = false;
+ $meta = str_split($format);
+ $output = array();
+ foreach($meta as $char) {
+ if ($char == '\\') {
+ $escape = true;
+ continue;
+ }
+ if (!$escape && array_key_exists($char, $this->dateArray)) {
+ $output[] = $this->dateArray[$char];
+ } else {
+ $output[] = $char;
+ }
+ // reset this to false after every iteration that wasn't "continued"
+ $escape = false;
+ }
+ return implode($output);
+
+ }
+
+ /**
+ * Getters
+ * The next dozen or so methods are just your standard getters for things such as month, day, year, week day, etc.
+ */
+
+ /**
+ * Get the month (number) of this date
+ * @return integer A number between 1 and 12 inclusively
+ */
+ public function getMonth() {
+
+ return $this->dateArray["mon"];
+
+ }
+ /**
+ * Get the month of this date
+ * @return string The actual name of the month, capitalized
+ */
+ public function getMonthName() {
+
+ return $this->dateArray["month"];
+
+ }
+ /**
+ * Get the day of the month
+ * @return integer A number between 1 and 31 inclusively
+ */
+ public function getDay() {
+
+ return $this->dateArray["mday"];
+
+ }
+ /**
+ * Get the day of the year
+ * @return integer A number between 0 and 365 inclusively
+ */
+ public function getYearDay($startFromOne = false) {
+
+ $yearDay = $this->dateArray["yday"] + (integer) $startFromOne;
+ return $yearDay;
+
+ }
+ /**
+ * Find how many days until the end of the year.
+ * For instance, if the date is December 25th, there are 6 days until the end of the year
+ */
+ public function getNumDaysUntilEndOfYear() {
+
+ $yearday = $this->getYearDay(true);
+ return $this->getNumDaysInYear() - $yearday;
+
+ }
+ /**
+ * Get how many months until the end of the year
+ * @todo This is really rudimentary. There is more to this, but this works for now...
+ */
+ public function getNumMonthsUntilEndOfYear() {
+
+ return 12 - $this->getMonth();
+
+ }
+ /**
+ * Get the amount of days in the year (365 unless it is a leap-year, then it's 366)
+ */
+ public function getNumDaysInYear() {
+
+ return ($this->isLeapYear()) ? 366 : 365;
+
+ }
+ /**
+ * Return the first day of the month as a qCal_Date object
+ * @return qCal_Date The first day of the month
+ */
+ public function getFirstDayOfMonth() {
+
+ return new qCal_Date($this->getYear(), $this->getMonth(), 1);
+
+ }
+ /**
+ * Return the last day of the month as a qCal_Date object
+ * @return qCal_Date The last day of the month
+ */
+ public function getLastDayOfMonth() {
+
+ $lastday = $this->format("t");
+ return new qCal_Date($this->getYear(), $this->getMonth(), $lastday);
+
+ }
+ /**
+ * Get the number of days until the end of the month
+ */
+ public function getNumDaysUntilEndOfMonth() {
+
+ return $this->getNumDaysInMonth() - $this->getDay();
+
+ }
+ /**
+ * Get the year
+ * @return integer The year of this date, for example 1999
+ */
+ public function getYear() {
+
+ return $this->dateArray["year"];
+
+ }
+ /**
+ * Get the day of the week
+ * @return integer A number between 0 (for Sunday) and 6 (for Saturday).
+ */
+ public function getWeekDay() {
+
+ return $this->dateArray["wday"];
+
+ }
+ /**
+ * Get the day of the week
+ * @return string The actual name of the day of the week, capitalized
+ */
+ public function getWeekDayName() {
+
+ return $this->dateArray["weekday"];
+
+ }
+ /**
+ * Get the amount of days in the current month of this year
+ * @return integer The number of days in the month
+ */
+ public function getNumDaysInMonth() {
+
+ return $this->dateArray["t"];
+
+ }
+ /**
+ * Get the week of the year
+ * @return integer The week of the year (0-51 I think)
+ * @todo This is not accurate if the week start isn't monday. I need to adjust for that
+ */
+ public function getWeekOfYear() {
+
+ return $this->dateArray["W"];
+
+ }
+ /**
+ * Get how many weeks until the end of the year
+ * @todo This is really rudimentary. There is more to this, but this works for now...
+ */
+ public function getWeeksUntilEndOfYear() {
+
+ return 52 - $this->getWeekOfYear();
+
+ }
+ /**
+ * Determine if this is a leap year
+ */
+ public function isLeapYear() {
+
+ return (boolean) $this->dateArray["L"];
+
+ }
+ /**
+ * Get a unix timestamp for the date
+ * @return integer The amount of seconds since unix epoch (January 1, 1970 UTC)
+ */
+ public function getUnixTimestamp() {
+
+ return $this->dateArray[0];
+
+ }
+
+ /**
+ * Date magic
+ * This component is capable of doing some really convenient things with dates.
+ * It is capable of determining things such as how many days until the end of the year,
+ * which monday of the month it is (ie: third monday in february), etc.
+ */
+
+ /**
+ * Determine the number or Tuesdays (or whatever day of the week this date is) since the
+ * beginning or end of the month.
+ * @param integer $xth A positive or negative number that determines which weekday of the month we want
+ * @param string|integer $weekday Either Sunday-Saturday or 0-6 to specify the weekday we want
+ * @param string|integer $month Either January-December or 1-12 to specify the month we want
+ * @param integer $year A valid year to specify which year we want
+ */
+ public function getXthWeekdayOfMonth($xth, $weekday = null, $month = null, $year = null) {
+
+ $negpos = substr($xth, 0, 1);
+ if ($negpos == "+" || $negpos == "-") {
+ $xth = (integer) substr($xth, 1);
+ } else {
+ $negpos = "+";
+ }
+
+ if (is_null($weekday)) {
+ $weekday = $this->getWeekday();
+ }
+
+ if (ctype_digit((string) $weekday)) {
+ if (!array_key_exists($weekday, $this->weekdays)) {
+ throw new qCal_Date_Exception_InvalidWeekday("\"$weekday\" is not a valid weekday.");
+ }
+ } else {
+ $weekday = strtolower($weekday);
+ if (!in_array($weekday, $this->weekdays)) {
+ throw new qCal_Date_Exception_InvalidWeekday("\"$weekday\" is not a valid weekday.");
+ }
+ $wdays = array_flip($this->weekdays);
+ $weekday = $wdays[$weekday];
+ }
+
+ if (is_null($month)) {
+ $month = $this->getMonth();
+ }
+
+ if (ctype_digit((string) $month)) {
+ if (!array_key_exists($month, $this->months)) {
+ throw new qCal_Date_Exception_InvalidMonth("\"$month\" is not a valid month.");
+ }
+ } else {
+ $month = strtolower($month);
+ if (!in_array($month, $this->months)) {
+ throw new qCal_Date_Exception_InvalidMonth("\"$month\" is not a valid month.");
+ }
+ $mons = array_flip($this->months);
+ $month = $mons[$month];
+ }
+
+ if (is_null($year)) {
+ $year = $this->getYear();
+ }
+
+ if (!ctype_digit((string) $year) || strlen($year) != 4) {
+ throw new qCal_Date_Exception_InvalidYear("\"$year\" is not a valid year.");
+ }
+
+ // now, using the year, month and numbered weekday, we need to find the actual day of the month...
+ $firstofmonth = new qCal_Date($year, $month, 1);
+ $numdaysinmonth = $firstofmonth->getNumDaysInMonth();
+ $numweekdays = 0; // the number of weekdays that have occurred (in the loop)
+ $foundday = false;
+ if ($negpos == "+") {
+ $day = 1;
+ $wday = $firstofmonth->getWeekday();
+ // while we are in the current month, loop
+ while ($day <= $numdaysinmonth) {
+ // if the specified weekday == the current week day in the loop
+ if ($weekday == $wday) {
+ $numweekdays++;
+ if ($numweekdays == $xth) {
+ // break out of the loop, we've found the right day! yay!
+ $foundday = $day;
+ break;
+ }
+ }
+ if ($wday == 6) $wday = 0; // reset to Sunday after Saturday
+ else $wday++;
+ $day++;
+ }
+ } else {
+ $day = $numdaysinmonth;
+ $lastofmonth = $firstofmonth->getLastDayOfMonth();
+ $wday = $lastofmonth->getWeekday();
+ while ($day >= 1) {
+ if ($weekday == $wday) {
+ $numweekdays++;
+ if ($numweekdays == $xth) {
+ // break out of the loop, we've found the right day! yay!
+ $foundday = $day;
+ break;
+ }
+ }
+ if ($wday == 0) $wday = 6; // reset to Saturday after Sunday
+ else $wday--;
+ $day--;
+ }
+ }
+
+ if ($foundday && checkdate($month, $day, $year)) {
+ $date = new qCal_Date($year, $month, $day);
+ } else {
+ if ($day == 32) {
+ throw new qCal_DateTime_Exception_InvalidDate("You have specified an incorrect number of days for qCal_Date::getXthWeekdayOfMonth()");
+ } else {
+ throw new qCal_DateTime_Exception_InvalidDate("You have entered an invalid date.");
+ }
+ }
+
+ return $date;
+
+ }
+ /**
+ * Determine the number or Tuesdays (or whatever day of the week this date is) since the
+ * beginning or end of the year.
+ */
+ public function getXthWeekdayOfYear($xth, $weekday = null, $year = null) {
+
+ $negpos = substr($xth, 0, 1);
+ if ($negpos == "+" || $negpos == "-") {
+ $xth = (integer) substr($xth, 1);
+ } else {
+ $negpos = "+";
+ }
+
+ if (is_null($weekday)) {
+ $weekday = $this->getWeekday();
+ }
+
+ if (ctype_digit((string) $weekday)) {
+ if (!array_key_exists($weekday, $this->weekdays)) {
+ throw new qCal_Date_Exception_InvalidWeekday("\"$weekday\" is not a valid weekday.");
+ }
+ } else {
+ $weekday = strtolower($weekday);
+ if (!in_array($weekday, $this->weekdays)) {
+ throw new qCal_Date_Exception_InvalidWeekday("\"$weekday\" is not a valid weekday.");
+ }
+ $wdays = array_flip($this->weekdays);
+ $weekday = $wdays[$weekday];
+ }
+
+ if (is_null($year)) {
+ $year = $this->getYear();
+ }
+
+ if (!ctype_digit((string) $year) || strlen($year) != 4) {
+ throw new qCal_Date_Exception_InvalidYear("\"$year\" is not a valid year.");
+ }
+
+ // now find the specified day by counting either forwards or backwards to the day in question
+ $firstofyear = new qCal_Date($year, 1, 1);
+ $numdaysinyear = ($firstofyear->isLeapYear()) ? 366 : 365;
+ $numweekdays = 0; // the number of weekdays that have occurred within the loop
+ $found = false; // whether or not the specified day has been found
+ if ($negpos == "+") {
+ // count forward
+ // loop over every day of every month looking for the right one
+ $day = 1;
+ $wday = $firstofyear->getWeekDay();
+ while ($day <= $numdaysinyear) {
+ // if the specified weekday == the current week day in the loop
+ if ($weekday == $wday) {
+ $numweekdays++;
+ if ($numweekdays == $xth) {
+ // break out of the loop, we've found the right day! yay!
+ $found = $day;
+ break;
+ }
+ }
+ if ($wday == 6) $wday = 0; // reset to Sunday after Saturday
+ else $wday++;
+ $day++;
+ }
+ } else {
+ // count backward
+ $lastofyear = new qCal_Date($year, 12, 31);
+ // count forward
+ // loop over every day of every month looking for the right one
+ $day = $numdaysinyear;
+ $wday = $lastofyear->getWeekDay();
+ while ($day >= 1) {
+ // if the specified weekday == the current week day in the loop
+ if ($weekday == $wday) {
+ $numweekdays++;
+ if ($numweekdays == $xth) {
+ // break out of the loop, we've found the right day! yay!
+ $found = $day;
+ break;
+ }
+ }
+ if ($wday == 0) $wday = 6; // reset to Saturday after Sunday
+ else $wday--;
+ $day--;
+ }
+ }
+
+ // @todo: Can't use checkdate here, so find another validation method...
+ if ($found) {
+ $date = new qCal_Date($year, 1, $found, true); // takes advantage of the rollover feature :)
+ } else {
+ throw new qCal_DateTime_Exception_InvalidDate("You have specified an incorrect number of days for qCal_Date::getXthWeekdayOfYear()");
+ }
+
+ return $date;
+
+ }
+
+ /**
+ * Magic methods
+ */
+
+ /**
+ * Output the date as a string. Options are as follows:
+ * @return string The formatted date
+ */
+ public function __toString() {
+
+ return $this->format($this->format);
+
+ }
+
+ /**
+ * Static methods
+ */
+
+ /**
+ * Because PHP does not provide a gmgetdate() function, I borrowed this one from the
+ * comments on the getdate() function page on php.net
+ * @param integer The timestamp to use to create the date
+ */
+ public static function gmgetdate($timestamp = null) {
+
+ $k = array('seconds','minutes','hours','mday','wday','mon','year','yday','weekday','month',0);
+ return(array_combine($k, split(":", gmdate('s:i:G:j:w:n:Y:z:l:F:U', is_null($timestamp) ? time() : $timestamp))));
+
+ }
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime.php
new file mode 100644
index 00000000..3de5929e
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime.php
@@ -0,0 +1,190 @@
+setDate($date);
+ $this->setTime($time);
+
+ }
+ /**
+ * Generate a datetime object via string
+ * @todo Should this accept qCal_Date and qCal_DateTime objects?
+ */
+ public static function factory($datetime, $timezone = null) {
+
+ if (is_null($timezone) || !($timezone instanceof qCal_Timezone)) {
+ // @todo Make sure this doesn't cause any issues
+ // detect if we're working with a UTC string like "19970101T180000Z", where the Z means use UTC time
+ if (strtolower(substr($datetime, -1)) == "z") {
+ $timezone = "UTC";
+ }
+ $timezone = qCal_Timezone::factory($timezone);
+ }
+ // get the default timezone so we can set it back to it later
+ $tz = date_default_timezone_get();
+ // set the timezone to GMT temporarily
+ date_default_timezone_set("GMT");
+
+ // handles unix timestamp
+ if (is_integer($datetime) || ctype_digit((string) $datetime)) {
+ $timestamp = $datetime;
+ } else {
+ // handles just about any string representation of date/time (strtotime)
+ if (is_string($datetime) || empty($datetime)) {
+ if (!$timestamp = strtotime($datetime)) {
+ // if unix timestamp can't be created throw an exception
+ throw new qCal_DateTime_Exception("Invalid or ambiguous date/time string passed to qCal_DateTime::factory()");
+ }
+ }
+ }
+
+ if (!isset($timestamp)) {
+ throw new qCal_DateTime_Exception("Could not generate a qCal_DateTime object.");
+ }
+
+ list($year, $month, $day, $hour, $minute, $second) = explode("|", gmdate("Y|m|d|H|i|s", $timestamp));
+
+ // set the timezone back to what it was
+ date_default_timezone_set($tz);
+
+ return new qCal_DateTime($year, $month, $day, $hour, $minute, $second, $timezone);
+
+ }
+ /**
+ * Set the date component
+ */
+ protected function setDate(qCal_Date $date) {
+
+ $this->date = $date;
+
+ }
+ /**
+ * Set the time component
+ */
+ protected function setTime(qCal_Time $time) {
+
+ $this->time = $time;
+
+ }
+ /**
+ * Get time portion as object
+ */
+ public function getTime() {
+
+ return $this->time;
+
+ }
+ /**
+ * Get date portion as object
+ */
+ public function getDate() {
+
+ return $this->date;
+
+ }
+ /**
+ * Get unix timestamp
+ */
+ public function getUnixTimestamp($useOffset = true) {
+
+ return $this->date->getUnixTimestamp() + $this->time->getTimestamp($useOffset);
+
+ }
+ /**
+ * Set the format to use when outputting as a string
+ */
+ public function setFormat($format) {
+
+ $this->format = (string) $format;
+ return $this;
+
+ }
+ /**
+ * Format the date/time using PHP's date() function's meta-characters
+ * @todo It's obvious I need to find a better solution to formatting since I have repeated this method
+ * in three classes now...
+ */
+ public function format($format) {
+
+ $escape = false;
+ $meta = str_split($format);
+ $output = array();
+ foreach($meta as $char) {
+ if ($char == '\\') {
+ $escape = true;
+ continue;
+ }
+ if (!$escape && $this->convertChar($char) != $char) {
+ $output[] = $this->convertChar($char);
+ } else {
+ $output[] = $char;
+ }
+ // reset this to false after every iteration that wasn't "continued"
+ $escape = false;
+ }
+ return implode($output);
+
+ }
+ /**
+ * convert character
+ */
+ protected function convertChar($char) {
+
+ $char = $this->date->format($char);
+ $char = $this->time->format($char);
+ $char = $this->time->getTimezone()->format($char);
+ return $char;
+
+ }
+ /**
+ * Output date/time as string
+ */
+ public function __toString() {
+
+ return $this->format($this->format);
+
+ }
+ /**
+ * Get date/time as UTC
+ */
+ public function getUtc($humanReadable = false) {
+
+ if ($humanReadable) return gmdate('Y-m-d', $this->date->getUnixTimestamp()) . gmdate('\TH:i:s\Z', $this->time->getTimestamp());
+ else return gmdate('Ymd', $this->date->getUnixTimestamp()) . gmdate('\THis\Z', $this->time->getTimestamp());
+
+ }
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Duration.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Duration.php
new file mode 100644
index 00000000..36cc1de0
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Duration.php
@@ -0,0 +1,115 @@
+ 604800, 'D' => 86400, 'H' => 3600, 'M' => 60, 'S' => 1);
+ /**
+ * Duration in seconds
+ */
+ protected $duration;
+ /**
+ * If this is negative, this will be a minus symbol. Positive doesn't need a sign, so it is just null
+ */
+ protected $sign;
+ /**
+ * Constructor
+ */
+ public function __construct($duration = null) {
+
+ $this->setDuration($duration);
+
+ }
+ /**
+ * Set duration - accepts an integer (amount of seconds) or an icalendar-formatted duration string
+ */
+ public function setDuration($duration) {
+
+ $duration = strtoupper($duration);
+ // if plus or minus precedes number, remove it set in class
+ if (preg_match("/^[+-]/", (string) $duration, $matches)) {
+ if ($matches[0] == "-") $this->sign = "-";
+ $duration = str_split($duration);
+ array_shift($duration);
+ $duration = implode("", $duration);
+ }
+ if (ctype_digit($duration)) {
+ $this->duration = $duration;
+ } else {
+ // convert value to duration in seconds
+ preg_match('/^P([0-9]+[W])?([0-9]+[D])?T?([0-9]+[H])?([0-9]+[M])?([0-9]+[S])?$/i', $duration, $matches);
+ // remove first element (which is just entire the matched string)
+ array_shift($matches);
+ $seconds = 0;
+ foreach ($matches as $duration) {
+ if (empty($duration)) continue;
+ $seconds += $this->calculateSeconds($duration);
+ }
+ $this->duration = $seconds;
+ }
+ return $this;
+
+ }
+ /**
+ * Pass in a string like "15W" or "1D" and this will return how many seconds are in it
+ */
+ protected function calculateSeconds($duration) {
+
+ $amnt = preg_replace("/[^0-9]/i", "", $duration);
+ $inc = preg_replace("/[^A-Z]/i", "", $duration);
+ return $this->durations[$inc] * $amnt;
+
+ }
+ /**
+ * Converts seconds to an icalendar-formatted duration string
+ */
+ public function toICal() {
+
+ $total = $this->duration;
+ $return = "P";
+ // this is why order is important when defining $this->durations
+ foreach ($this->durations as $dur => $amnt) {
+ // how many "weeks" are in the value?
+ $quotient = (int) ($total / $amnt);
+ // get the remainder of the division
+ $remainder = $total - ($quotient*$amnt);
+ // now if we got a whole number as quotient, add this duration to the return string
+ if ($quotient) {
+ // if this is the first "time" duration, add the required T char
+ if ($dur == "H" || $dur == "M" || $dur == "S") {
+ if (!strpos($return, "T")) $return .= "T";
+ }
+ $return .= $quotient . $dur;
+ }
+ $total = $remainder;
+ }
+ return $this->sign . $return;
+
+ }
+ /**
+ * @todo Should this be the string representation? I dont really know.
+ */
+ public function __toString() {
+
+ return $this->toICal();
+
+ }
+ /**
+ * Get duration in seconds
+ */
+ public function getSeconds() {
+
+ return (integer) $this->sign . $this->duration;
+
+ }
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Exception.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Exception.php
new file mode 100644
index 00000000..a688c0dc
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Exception.php
@@ -0,0 +1,7 @@
+start = $start;
+ $this->end = $end;
+ if ($this->getSeconds() < 0) {
+ throw new qCal_DateTime_Exception_InvalidPeriod("The start date must come before the end date.");
+ }
+
+ }
+ /**
+ * Converts to how many seconds between the two. because this is the smallest increment
+ * used in this class, seconds are used to determine other increments
+ */
+ public function getSeconds() {
+
+ return $this->end->getUnixTimestamp() - $this->start->getUnixTimestamp();
+
+ }
+ /**
+ * Returns start date
+ */
+ public function getStart() {
+
+ return $this->start;
+
+ }
+ /**
+ * Returns end date
+ */
+ public function getEnd() {
+
+ return $this->end;
+
+ }
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Recur.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Recur.php
new file mode 100644
index 00000000..8e7877be
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Recur.php
@@ -0,0 +1,452 @@
+ 'Monday',
+ 'TU' => 'Tuesday',
+ 'WE' => 'Wednesday',
+ 'TH' => 'Thursday',
+ 'FR' => 'Friday',
+ 'SA' => 'Saturday',
+ 'SU' => 'Sunday',
+ );
+ /**
+ * @var qCal_Date The start date/time of the recurrence
+ */
+ protected $dtstart;
+ /**
+ * @var string frequency of the recurrence
+ */
+ protected $freq;
+ /**
+ * @var qCal_Date The date/time which the recurrence ends
+ */
+ protected $until;
+ /**
+ * @var integer The amount of recurrences
+ */
+ protected $count;
+ /**
+ * @var integer Interval of recurrence (for every 3 days, "3" would be the interval)
+ */
+ protected $interval;
+ /**
+ * @var integer|array An integer between 0 and 59 (for multiple, set as an array)
+ */
+ protected $bysecond;
+ /**
+ * @var integer|array An integer between 0 and 59 (or an array of integers for multiple)
+ */
+ protected $byminute;
+ /**
+ * @var integer|array An integer or array of integers between 0 and 23
+ */
+ protected $byhour;
+ /**
+ * @var string If present, represents the nth occurrence of a specific day within monthly or yearly
+ * so it can be something like +1MO (or simply 1MO) for the first monday within the month, whereas
+ * -1MO for the last monday of the month. Or it can be simply MO to represent every monday within the month
+ */
+ protected $byday;
+ /**
+ * @var integer|array An integer or array of integers. -31 to -1 or 1 to 31. -10 would mean the tenth to last
+ * day of the month. [1,5,-5] would be the 1st, 5th, and 5th to last days of the month
+ */
+ protected $bymonthday;
+ /**
+ * @var integer|array An integer or array of integers. -366 to -1 or 1 to 366. -306 represents the 306th to last
+ * day of the year (March 1st)
+ */
+ protected $byyearday;
+ /**
+ * @var integer|array An integer or array of integers. -53 to -1 or 1 to 53. Only valid for yearly rules.
+ * 3 represents the third week of the year.
+ */
+ protected $byweekno;
+ /**
+ * @var integer|array An integer or array of integers. 1 to 12. 3 would represent March
+ */
+ protected $bymonth;
+ /**
+ * @var integer If present, it indicates the nth occurrence of the specific occurrence within the set of
+ * events specified by this recurrence rule
+ */
+ protected $bysetpos;
+ /**
+ * @var string Must be one of the weekdays specified above (2 char). Specifies the day on which the work week
+ * starts. This is significant when a weekly rule has an interval greater than 1 and a byday rule part is specified.
+ * This is also significant when in a yearly rule when a byweekno rule part is specified. Defaults to "MO"
+ */
+ protected $wkst = "MO";
+ /**
+ * Constructor
+ * @param $freq string Must be one of the freqtypes specified above.
+ * @throws qCal_Date_Exception_InvalidRecur if a frequency other than those specified above is passed in
+ */
+ public function __construct($dtstart = null) {
+
+ $this->dtstart = is_null($dtstart) ? null : qCal_DateTime::factory($dtstart);
+
+ }
+ /**
+ * Specifies the date when the recurrence stops, inclusively. If not present, and there is no count specified,
+ * then the recurrence goes on "forever".
+ * This is a getter as well as a setter (if no arg is supplied, it is a getter)
+ * @param $until string|qCal_Date|DateTime If time is specified, it must be UTC
+ * @throws qCal_Date_Exception_InvalidRecur
+ * @return self
+ */
+ public function until($until = null) {
+
+ if (is_null($until)) return $this->until;
+ if ($this->count()) throw new qCal_DateTime_Exception_InvalidRecur('A recurrence count and an until date cannot both be specified');
+ $this->until = qCal_DateTime::factory($until);
+ return $this;
+
+ }
+ /**
+ * Specifies the amount of recurrences before the recurrence ends. If neither this nor "until" is specified,
+ * the recurrence repeats "forever".
+ * This is a getter as well as a setter (if no arg is supplied, it is a getter)
+ * @param $count integer The amount of recurrences before it stops
+ * @throws qCal_Date_Exception_InvalidRecur
+ * @return self
+ */
+ public function count($count = null) {
+
+ if (is_null($count)) return $this->count;
+ if ($this->until()) throw new qCal_DateTime_Exception_InvalidRecur('A recurrence count and an until date cannot both be specified');
+ $this->count = (integer) $count;
+ return $this;
+
+ }
+ /**
+ * Specifies the start of the work-week, which is Monday by default
+ */
+ public function wkst($wkst = null) {
+
+ if (is_null($wkst)) return $this->wkst;
+ $abbrs = array_keys($this->weekdays);
+ if (!in_array($wkst, $abbrs)) throw new qCal_DateTime_Exception_InvalidRecur('"' . $wkst . '" is not a valid week day, must be one of the following: ' . implode(', ', $abbrs));
+ $this->wkst = $wkst;
+ // @todo I wonder if re-sorting the weekdays array would help me in any way...
+
+ }
+ /**
+ * Specifies the interval of recurrences
+ * This is a getter as well as a setter (if no arg is supplied, it is a getter)
+ * @param $interval integer The interval of recurrences, for instance every "3" days
+ * @throws qCal_DateTime_Exception_InvalidRecur
+ * @return self
+ */
+ public function interval($interval = null) {
+
+ if (is_null($interval)) return $this->interval;
+ $this->interval = (integer) $interval;
+ return $this;
+
+ }
+ /**
+ * Specifies a rule which will happen on every nth second.
+ * This is a getter as well as a setter (if no arg is supplied, it is a getter)
+ * @param $second integer|array Can be an integer (or array of ints) between 0 and 59
+ * @throws qCal_DateTime_Exception_InvalidRecur
+ * @return self
+ */
+ public function bySecond($second = null) {
+
+ if (is_null($second)) return $this->bysecond;
+ if (!is_array($second)) $second = array($second);
+ $this->bysecond = $second;
+ return $this;
+
+ }
+ /**
+ * Specifies a rule which will happen on every nth minute
+ * This is a getter as well as a setter (if no arg is supplied, it is a getter)
+ * @param $minute integer|array Can be an integer (or array of ints) between 0 and 59
+ * @throws qCal_DateTime_Exception_InvalidRecur
+ * @return self
+ */
+ public function byMinute($minute = null) {
+
+ if (is_null($minute)) return $this->byminute;
+ if (!is_array($minute)) $minute = array($minute);
+ $this->byminute = $minute;
+ return $this;
+
+ }
+ /**
+ * Specifies a rule which will happen on every nth hour
+ * This is a getter as well as a setter (if no arg is supplied, it is a getter)
+ * @param $hour integer|array Can be an integer (or array of ints) between 0 and 23
+ * @throws qCal_DateTime_Exception_InvalidRecur
+ * @return self
+ */
+ public function byHour($hour = null) {
+
+ if (is_null($hour)) return $this->byhour;
+ if (!is_array($hour)) $hour = array($hour);
+ $this->byhour = $hour;
+ return $this;
+
+ }
+ /**
+ * Specifies a rule which will happen on whichever day is specified. For instance, "MO" would
+ * mean every monday.
+ * This is a getter as well as a setter (if no arg is supplied, it is a getter)
+ * Sets $this->byday into an array of arrays like array('SU' => 1) for '1SU' and array('SU' => 0) for 'SU'
+ * @param $day string|array Must be one of the 2-char week days specified above. Can be preceded by
+ * a positive or negative integer to represent, for instance, the third monday of the month (3MO) or second to last
+ * Sunday of the month (-2SU)
+ * @throws qCal_DateTime_Exception_InvalidRecur
+ * @return self
+ */
+ public function byDay($day = null) {
+
+ if (is_null($day)) {
+ $ret = array();
+ foreach ($this->byday as $val) {
+ $num = (current($val) == 0) ? "" : current($val);
+ $ret[] = $num . key($val);
+ }
+ return $ret;
+ }
+ if (!is_array($day)) $day = array($day);
+ $days = array();
+ foreach ($day as $d) {
+ // optional plus or minus followed by a series of digits as group 1
+ // two-character week day as group 2
+ if (preg_match('/^([+-]?[0-9]+)?(MO|TU|WE|TH|FR|SA|SU)$/', $d, $matches)) {
+ $num = ($matches[1] == "") ? "0" : $matches[1];
+ $wday = $matches[2];
+ if (substr($num, 0, 1) == "+") {
+ $num = substr($num, 1);
+ }
+ $days[] = array($wday => $num);
+ }
+ }
+ $this->byday = $days;
+ return $this;
+
+ }
+ /**
+ * Specifies a rule which will happen on the month days specified. For instance, 23 would mean the 23rd of every month.
+ * This is a getter as well as a setter (if no arg is supplied, it is a getter)
+ * @param integer|array Must be between 1 and 31 or -31 to 1 (or an array of those values)
+ * @throws qCal_DateTime_Exception_InvalidRecur
+ * @return self
+ */
+ public function byMonthDay($monthday = null) {
+
+ if (is_null($monthday)) return $this->bymonthday;
+ if (!is_array($monthday)) $monthday = array($monthday);
+ $this->bymonthday = $monthday;
+ return $this;
+
+ }
+ /**
+ * Specifies a rule which will happen on the nth day of the year
+ * This is a getter as well as a setter (if no arg is supplied, it is a getter)
+ * @param integer|array Must be between 1 and 366 or -366 to -1.
+ * @throws qCal_DateTime_Exception_InvalidRecur
+ * @return self
+ */
+ public function byYearDay($yearday = null) {
+
+ if (is_null($yearday)) return $this->byyearday;
+ if (!is_array($yearday)) $yearday = array($yearday);
+ $this->byyearday = $yearday;
+ return $this;
+
+ }
+ /**
+ * Specifies a rule which will happen on the nth week of the year
+ * This is a getter as well as a setter (if no arg is supplied, it is a getter)
+ * @param integer|array Must be between 1 and 53 or -53 to -1.
+ * @throws qCal_DateTime_Exception_InvalidRecur
+ * @return self
+ */
+ public function byWeekNo($weekno = null) {
+
+ if (is_null($weekno)) return $this->byweekno;
+ if (!is_array($weekno)) $weekno = array($weekno);
+ $this->byweekno = $weekno;
+ return $this;
+
+ }
+ /**
+ * Specifies a rule which will happen on the nth month of the year
+ * This is a getter as well as a setter (if no arg is supplied, it is a getter)
+ * @param integer|array Must be between 1 and 12
+ * @throws qCal_DateTime_Exception_InvalidRecur
+ * @return self
+ */
+ public function byMonth($month = null) {
+
+ if (is_null($month)) return $this->bymonth;
+ if (!is_array($month)) $month = array($month);
+ $this->bymonth = $month;
+ return $this;
+
+ }
+ /**
+ * Indicates the nth occurrence of the specific occurrence within the set of
+ * events specified by the rule.
+ * This is a getter as well as a setter (if no arg is supplied, it is a getter)
+ * @todo I don't really understand how this works... :( Figure out wtf it is for.
+ * @throws qCal_DateTime_Exception_InvalidRecur
+ * @return self
+ */
+ public function bySetPos($setpos = null) {
+
+ if (is_null($setpos)) return $this->bysetpos;
+ $this->bysetpos = (integer) $setpos;
+ return $this;
+
+ }
+ /**
+ * Factory method generates the correct recur type based on the string it is passed: "yearly, weekly, etc."
+ * @param string The frequency type of recurrence rule you want to generate
+ * @param mixed The start date/time for the recurrence. Accepts anything qCal_Date accepts
+ */
+ static public function factory($freq, $start) {
+
+ $freq = ucfirst(strtolower($freq));
+ $className = "qCal_DateTime_Recur_" . $freq;
+ $fileName = str_replace("_", DIRECTORY_SEPARATOR, $className) . ".php";
+ qCal_Loader::loadFile($fileName);
+ $class = new $className($start);
+ return $class;
+
+ }
+ /**
+ * Fetches instances of the recurrence rule in the given time period. Because recurrences
+ * could potentially go on forever, there is no way to fetch ALL instances of a recurrence rule
+ * other than providing a date range that spans the entire length of the recurrence.
+ *
+ * The way this will need to work is, depending on the frequency, I will find all possible
+ * occurrence of the rule. For instance, if this is a "monthly" rule, I'll find out which month
+ * to start in, then find all occurrence possible. Then narrow down by the other rules I guess.
+ *
+ * @idea Maybe I should build classes for each of the frequency types. That way I could loop over
+ * the object and get methods like qCal_DateTime_Recur_Monthly::isNthDay('SU') to find out what sunday
+ * of the month it is... stuff like that... I dunno... ?
+ *
+ * @throws qCal_DateTime_Exception_InvalidRecur
+ * @todo The giant switch in this method is a glaring code smell, but it works for now. I will refactor
+ * after version 0.1 and remove the switch (probably will implement qCal_DateTime_Recur_Yearly, qCal_DateTime_Recur_Monthly, etc.)
+ */
+ public function getRecurrences($start, $end) {
+
+ $start = qCal_DateTime::factory($start);
+ $end = qCal_DateTime::factory($end);
+ if ($start->getUnixTimestamp() > $end->getUnixTimestamp()) throw new qCal_DateTime_Exception_InvalidRecur('Start date must come before end date');
+ if (!$this->interval) throw new qCal_DateTime_Exception_InvalidRecur('You must specify an interval');
+
+ $rules = array(
+ 'bymonth' => array(),
+ 'byweekno' => array(),
+ 'byyearday' => array(),
+ 'byday' => array(),
+ );
+
+ // byMonth rules
+ if (is_array($this->bymonth)) {
+ foreach ($this->bymonth as $bymonth) {
+ $rules['bymonth'][] = new qCal_DateTime_Recur_Rule_ByMonth($bymonth);
+ }
+ }
+
+ // byWeekNo rules
+ if (is_array($this->byweekno)) {
+ foreach ($this->byweekno as $byweekno) {
+ $rules['byweekno'][] = new qCal_DateTime_Recur_Rule_ByWeekNo($byweekno);
+ }
+ }
+
+ // byYearDay rules
+ if (is_array($this->byyearday)) {
+ foreach ($this->byyearday as $byyearday) {
+ $rules['byyearday'][] = new qCal_DateTime_Recur_Rule_ByYearDay($byyearday);
+ }
+ }
+
+ // byMonthDay rules (these get applied to bymonth rules)
+ if (is_array($this->bymonthday)) {
+ foreach ($this->bymonthday as $bymonthday) {
+ $bmdrule = new qCal_DateTime_Recur_Rule_ByMonthDay($bymonthday);
+ foreach ($rules['bymonth'] as $bymonth) {
+ $bymonth->attach($bmdrule);
+ }
+ }
+ }
+
+ // byDay rules (these get applied to bymonth rules if they exist, otherwise simply to year)
+ if (is_array($this->byday)) {
+ foreach ($this->byday as $byday) {
+ $bdrule = new qCal_DateTime_Recur_Rule_ByDay($byday);
+ if (is_array($rules['bymonth']) && !empty($rules['bymonth'])) {
+ foreach ($rules['bymonth'] as $bymonth) {
+ $bymonth->attach($bdrule);
+ }
+ } else {
+ $rules['byday'][] = $bdrule;
+ }
+ }
+ }
+
+ // byHour rules (these get applied to each rule above)
+ if (is_array($this->byhour)) {
+ foreach ($this->byhour as $byhour) {
+ $bhrule = new qCal_DateTime_Recur_Rule_ByHour($byhour);
+ foreach ($rules as $type => $ruleset) {
+ foreach ($ruleset as $rule) {
+ $rule->attach($bhrule);
+ }
+ }
+ }
+ }
+
+ // byMinute rules (these get applied to each rule above)
+ if (is_array($this->byminute)) {
+ foreach ($this->byminute as $byminute) {
+ $bmrule = new qCal_DateTime_Recur_Rule_ByMinute($byminute);
+ foreach ($rules as $type => $ruleset) {
+ foreach ($ruleset as $rule) {
+ $rule->attach($bmrule);
+ }
+ }
+ }
+ }
+
+ // bySecond rules (these get applied to each rule above)
+ if (is_array($this->bysecond)) {
+ foreach ($this->bysecond as $bysecond) {
+ $bsrule = new qCal_DateTime_Recur_Rule_BySecond($bysecond);
+ foreach ($rules as $type => $ruleset) {
+ foreach ($ruleset as $rule) {
+ $rule->attach($bsrule);
+ }
+ }
+ }
+ }
+
+ return $this->doGetRecurrences($rules, $start, $end);
+
+ }
+ /**
+ * Each type of rule needs to determine its recurrences so this is left abstract
+ * to be implemented by children.
+ */
+ abstract protected function doGetRecurrences($rules, $start, $end);
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Recur/Daily.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Recur/Daily.php
new file mode 100644
index 00000000..5f6ce193
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Recur/Daily.php
@@ -0,0 +1,10 @@
+value = $value;
+
+ }
+ /**
+ * Attach rules to this rule. For instance, if this is a byMonth rule, then
+ * we can attach byDay rules like "-1SU" for the last Sunday of the month.
+ */
+ public function attach(qCal_DateTime_Recur_Rule $rule) {
+
+ $this->rules[] = $rule;
+
+ }
+ /**
+ * Creates the recurrences for this rule. Left to children to do this.
+ */
+ abstract public function getRecurrences();
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Recur/Rule/ByDay.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Recur/Rule/ByDay.php
new file mode 100644
index 00000000..0f92132c
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Recur/Rule/ByDay.php
@@ -0,0 +1,10 @@
+format('d');
+ $smonth = $start->format('m');
+ $syear = $start->format('Y');
+
+ // end day, year, and month
+ $eday = $end->format('d');
+ $emonth = $end->format('m');
+ $eyear = $end->format('Y');
+
+ // loop over years, by increment
+ $year = $syear;
+ while ($year <= $eyear) {
+
+ // if byMonth is specified...
+ if (count($this->byMonth())) {
+ // loop over each month
+ for ($month = 1; $month <= 12; $month++) {
+ // if this is the start year still and we haven't reached the start month, skip ahead
+ if ($year == $syear && $month < $smonth) {
+ continue;
+ }
+ // if this is the end year and we have passed the end month, break out of loop
+ if ($year == $eyear && $month > $emonth) {
+ break;
+ }
+ // if this is not one of the bymonths, continue as well
+ if (!in_array($month, $this->byMonth())) {
+ continue;
+ }
+ // now we need to loop over each day of the month to look for byday or bymonthday
+ $thismonth = new qCal_Date(); // used to determine total days in the current month
+ $thismonth->setDate($year, $month, 1);
+ $weekdays = array(
+ 'MO' => 0,
+ 'TU' => 0,
+ 'WE' => 0,
+ 'TH' => 0,
+ 'FR' => 0,
+ 'SA' => 0,
+ 'SU' => 0,
+ );
+ // @todo For now this only allows 1SU, SU, but not -1SU (no negatives for now)
+ for ($day = 1; $day <= $thismonth->format('t'); $day++) {
+ $alreadyadded = false;
+ $date = new qCal_Date;
+ $date->setDate($year, $month, $day);
+ $date->setTime(0, 0, 0);
+ $wdname = strtoupper(substr($date->format('l'), 0, 2));
+ // keep track of how many of each day of the week have gone by
+ $weekdays[$wdname]++;
+ // if byDay is specified...
+ // @todo this is inconsistent, I don't use the getter here because of its special functionality.
+ // I need to either remove the special functionality or not use getters elsewhere in this method
+ $byday = $this->byday;
+ if (count($byday)) {
+ // by day is broken into an array of arrays like array('TH' => 0), array('FR' => 1), array('MO' => -2) etc.
+ // with zero meaning every instance of that particular day should be included and number meaning the Nth of that day
+ foreach ($byday as $val) {
+ // if at least one of this wday has gone by...
+ $num = current($val);
+ if ($weekdays[$wdname] > 0) {
+ // check if it is the right week day and if a digit is specified (like 1SU) that it is checked as well
+ if ($wdname == key($val) && ($weekdays[$wdname] == $num || $num == 0)) {
+ $recurrences[] = $date;
+ $alreadyadded = true;
+ }
+ }
+ }
+ }
+
+ // if byMonthDay is specified...
+ if (count($this->byMonthDay())) {
+ foreach ($this->byMonthDay() as $mday) {
+ // only add this day if it hasn't been added already
+ if ($mday == $day && !$alreadyadded) {
+ $recurrences[] = $date;
+ }
+ }
+ }
+
+ // now loop over each hour and add hours
+ if (count($this->byHour())) {
+ $hourrecurrences = array();
+ foreach ($this->byHour() as $hour) {
+ $new = new qCal_Date();
+ $new = $new->copy($date);
+ $new->setTime($hour, 0, 0);
+ $hourrecurrences[] = $new;
+ }
+ }
+
+ // now loop over byHours and add byMinutes
+ if (count($this->byMinute())) {
+ if (!isset($minuterecurrences)) $minuterecurrences = array();
+ foreach ($this->byMinute() as $minute) {
+ $new = new qCal_Date();
+ $new = $new->copy($date);
+ $new->setTime(0, $minute, 0);
+ }
+ }
+
+ // now loop over byMinutes and add bySeconds
+
+ }
+ }
+ }
+
+ // if in the first year we don't find an instance, don't do the interval, just increment a year
+ if ($year == $syear && count($recurrences)) $year += $this->interval();
+ else ($year++);
+ }
+
+ // now loop over weeks to get byWeekNo
+
+ foreach ($recurrences as $date) {
+ // pr($date->format("r"));
+ }
+ // exit;
+
+ return $recurrences;
+ // for bymonth, it would make the most sense to loop over each month until the specified one
+ // is found. Then loop over each day to find its sub-rules.
+
+ // for byweekno, it would make the most sense to loop over each week until the specified one
+ // is found. Then apply any sub-rules (actually I'm not sure how byhour and its ilk would be applied in this situation... need to read the rfc)
+
+ }
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Timezone.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Timezone.php
new file mode 100644
index 00000000..ac7cb51b
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/DateTime/Timezone.php
@@ -0,0 +1,62 @@
+format($this->format);
+
+ }
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Exception.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Exception.php
new file mode 100644
index 00000000..b87fc565
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Exception.php
@@ -0,0 +1,11 @@
+options = array(
+ 'searchpath' => get_include_path(),
+ );
+ $this->options = array_merge($this->options, $options);
+
+ }
+ /**
+ * @todo What should this accept? filename? actual string content? either?
+ * @todo Maybe even create a parse() for raw string and a parseFile() for a file name?
+ */
+ public function parse($content, $lexer = null) {
+
+ if (is_null($lexer)) {
+ $lexer = new qCal_Parser_Lexer_iCalendar($content);
+ }
+ $this->lexer = $lexer;
+ return $this->doParse($this->lexer->tokenize());
+
+ }
+ /**
+ * Parse a file. The searchpath defaults to the include path. Also, if the filename
+ * provided is an absolute path, the searchpath is not used. This is determined by
+ * either the file starting with a forward slash, or a drive letter (for Windows)
+ * @todo Throw an exception if file doesn't exist
+ * @todo I'm not really sure that it should default to the include path. That's not really what the include path is for, is it?
+ * @todo Test for path starting with a drive letter for windows (or find a better way to detect that)
+ */
+ public function parseFile($filename) {
+
+ // @todo This is hacky... but it works
+ if (substr($filename, 0, 1) == '/' || substr($filename, 0, 3) == 'C:\\') {
+ if (file_exists($filename)) {
+ $content = file_get_contents($filename);
+ return $this->parse($content);
+ }
+ } else {
+ $paths = explode(PATH_SEPARATOR, $this->options['searchpath']);
+ foreach ($paths as $path) {
+ $fname = $path . DIRECTORY_SEPARATOR . $filename;
+ if (file_exists($fname)) {
+ $content = file_get_contents($fname);
+ return $this->parse($content);
+ }
+ }
+ }
+ throw new qCal_Exception_FileNotFound('File cannot be found: "' . $filename . '"');
+
+ }
+ /**
+ * Override doParse in a child class if necessary
+ */
+ protected function doParse($tokens) {
+
+ $properties = array();
+ foreach ($tokens['properties'] as $propertytoken) {
+ $params = array();
+ foreach ($propertytoken['params'] as $paramtoken) {
+ $params[$paramtoken['param']] = $paramtoken['value'];
+ }
+ try {
+ $properties[] = qCal_Property::factory($propertytoken['property'], $propertytoken['value'], $params);
+ } catch (qCal_Exception $e) {
+ // @todo There should be a better way of determining what went wrong during parsing/lexing than this
+ // do nothing...
+ // pr($e);
+ }
+ }
+ $component = qCal_Component::factory($tokens['component'], $properties);
+ foreach ($tokens['children'] as $child) {
+ $childcmpnt = $this->doParse($child);
+ $component->attach($childcmpnt);
+ }
+ return $component;
+
+ }
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Parser/Lexer.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Parser/Lexer.php
new file mode 100644
index 00000000..2de249a3
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Parser/Lexer.php
@@ -0,0 +1,34 @@
+content = $content;
+
+ }
+ /**
+ * Tokenize content into tokens that can be used to build iCalendar objects
+ */
+ abstract public function tokenize();
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Parser/Lexer/iCalendar.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Parser/Lexer/iCalendar.php
new file mode 100644
index 00000000..7e2d9cb6
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Parser/Lexer/iCalendar.php
@@ -0,0 +1,120 @@
+line_terminator = chr(13) . chr(10);
+
+ }
+ /**
+ * Return a list of tokens (to be fed to the parser)
+ * @returns array tokens
+ */
+ public function tokenize() {
+
+ $lines = $this->unfold($this->content);
+ // loop through chunks of input text by separating by properties and components
+ // and create tokens for each one, creating a multi-dimensional array of tokens to return
+ $stack = array();
+ foreach ($lines as $line) {
+ // begin a component
+ if (preg_match('#^BEGIN:([a-z]+)$#i', $line, $matches)) {
+ // create new array representing the new component
+ $array = array(
+ 'component' => $matches[1],
+ 'properties' => array(),
+ 'children' => array(),
+ );
+ $stack[] = $array;
+ } elseif (strpos($line, "END:") === 0) {
+ // end component, pop the stack
+ $child = array_pop($stack);
+ if (empty($stack)) {
+ $tokens = $child;
+ } else {
+ $parent =& $stack[count($stack)-1];
+ array_push($parent['children'], $child);
+ }
+ } else {
+ // continue component
+ if (preg_match('#^([^:]+):"?([^\n]+)?"?$#i', $line, $matches)) {
+ // @todo What do I do with empty values?
+ $value = isset($matches[2]) ? $matches[2] : "";
+ $component =& $stack[count($stack)-1];
+ // if line is a property line, start a new property, but first determine if there are any params
+ $property = $matches[1];
+ $params = array();
+ $propparts = explode(";", $matches[1]);
+ if (count($propparts) > 1) {
+ foreach ($propparts as $key => $part) {
+ // the first one is the property name
+ if ($key == 0) {
+ $property = $part;
+ } else {
+ // the rest are params
+ // @todo Quoted param values need to be taken care of...
+ list($paramname, $paramvalue) = explode("=", $part, 2);
+ $params[] = array(
+ 'param' => $paramname,
+ 'value' => $paramvalue,
+ );
+ }
+ }
+ }
+ $proparray = array(
+ 'property' => $property,
+ 'value' => $value,
+ 'params' => $params,
+ );
+ $component['properties'][] = $proparray;
+ }
+ }
+ }
+ return $tokens;
+
+ }
+ /**
+ * Unfold the file before trying to parse it
+ */
+ protected function unfold($content) {
+
+ $return = array();
+ $lines = explode($this->line_terminator, $content);
+ foreach ($lines as $line) {
+ $checkempty = trim($line);
+ if (empty($checkempty)) continue;
+ $chr1 = substr($line, 0, 1);
+ $therest = substr($line, 1);
+ // if character 1 is a whitespace character... (tab or space)
+ if ($chr1 == chr(9) || $chr1 == chr(32)) {
+ $return[count($return)-1] .= $therest;
+ } else {
+ $return[] = $line;
+ }
+ }
+ return $return;
+
+ }
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property.php
new file mode 100644
index 00000000..28143973
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property.php
@@ -0,0 +1,289 @@
+type)
+ * @todo Determine if there can be multiple params of the same name
+ */
+ public function __construct($value = null, $params = array()) {
+
+ if (is_null($this->name)) $this->name = $this->getPropertyNameFromClassName(get_class($this));
+ foreach ($params as $pname => $pval) {
+ $this->setParam($pname, $pval);
+ }
+ // this must be set after parameters because the VALUE parameter can affect it
+ $this->setValue($value);
+
+ }
+ /**
+ * Generates a qCal_Property class based on property name, params, and value
+ * which can come directly from an icalendar file
+ * @todo I need a way to detect INVALID properties as they are being parsed. This
+ * way there can be an option to NOT stop on errors. To just log and then continue.
+ */
+ static public function factory($name, $value, $params = array()) {
+
+ $className = self::getClassNameFromPropertyName($name);
+ $fileName = str_replace("_", DIRECTORY_SEPARATOR, $className) . ".php";
+ try {
+ qCal_Loader::loadFile($fileName);
+ $class = new $className($value, $params);
+ } catch (qCal_Exception_InvalidFile $e) {
+ // if there is no class available for this property, check if it is non-standard
+ $xname = strtoupper(substr($name, 0, 2));
+ // non-standard property
+ if ($xname == "X-") {
+ $class = new qCal_Property_NonStandard($value, $params, $name);
+ } else {
+ // if it's not a non-standard property, rethrow
+ throw $e;
+ }
+ }
+ return $class;
+
+ }
+ /**
+ * Returns the property name (formatted and exactly to spec)
+ * @return string
+ */
+ public function getName() {
+
+ return $this->name;
+
+ }
+ /**
+ * Returns the property value (as a string)
+ * If you want the actual object, use getValueObject()
+ * I wish I could just pass the object back and have php do some overloading magicness, but
+ * it doesn't know how :(
+ * @return string
+ */
+ public function getValue() {
+
+ return $this->value->__toString();
+
+ }
+ /**
+ * Just returns getValue()
+ */
+ public function __toString() {
+
+ return $this->getValue();
+
+ }
+ /**
+ * Returns raw value object (or for multi-value, an array)
+ * @return string
+ */
+ public function getValueObject() {
+
+ return $this->value;
+
+ }
+ /**
+ * Sets the property value
+ * @param mixed
+ */
+ public function setValue($value) {
+
+ // if value sent is null and this property doesn't have a default value,
+ // the property can't be created, so throw an invalidpropertyvalue exception
+ if (is_null($value)) {
+ if ($this->default === false) {
+ // this is caught by factory and reported as a conformance error
+ throw new qCal_Exception_InvalidPropertyValue($this->getName() . ' property must have a value');
+ } else {
+ $value = $this->default;
+ }
+ }
+ $this->value = $this->convertValue($value);
+ return $this;
+
+ }
+ /**
+ * Converts a value into whatever internal storage mechanism the property uses
+ */
+ protected function convertValue($value) {
+
+ return qCal_Value::factory($this->getType(), $value);
+
+ }
+ /**
+ * Returns the property type
+ * @return string
+ */
+ public function getType() {
+
+ return $this->type;
+
+ }
+ /**
+ * Check if this is a property of a certain component. Some properties
+ * can only be set on certain Components. This method looks inside this
+ * property's $allowedComponents and returns true if $component is allowed
+ *
+ * @return boolean True if this is a property of $component, false otherwise
+ * @param qCal_Component The component we're evaluating
+ **/
+ public function of(qCal_Component $component) {
+
+ return in_array($component->getName(), $this->allowedComponents);
+
+ }
+ /**
+ * Retreive the value of a parameter
+ *
+ * @return mixed parameter value
+ */
+ public function getParam($name) {
+
+ if (isset($this->params[strtoupper($name)])) {
+ return $this->params[strtoupper($name)];
+ }
+
+ }
+ /**
+ * Returns an array of all params
+ */
+ public function getParams() {
+
+ return $this->params;
+
+ }
+ /**
+ * Set the value of a parameter
+ */
+ public function setParam($name, $value) {
+
+ $name = strtoupper($name);
+ // if value param has been passed in, change the type of this property to its value
+ if ($name == "VALUE") {
+ $value = strtoupper($value);
+ $this->type = $value;
+ }
+ $this->params[$name] = $value;
+ return $this;
+
+ }
+ /**
+ * Determine's this property's name from the class name by adding a dash after
+ * every capital letter and upper-casing
+ *
+ * @return string The RFC property name
+ * @todo This method is flawed. The class name XLvFoo gets converted to X-L-VFOO when
+ * it should be X-LV-FOO
+ **/
+ protected function getPropertyNameFromClassName($classname) {
+
+ // determine the property name by class name
+ $parts = explode("_", $classname);
+ end($parts);
+ // find where capital letters are and insert dash
+ $chars = str_split(current($parts));
+ // make a copy @todo Why make a copy?
+ $newchars = $chars;
+ foreach ($chars as $pos => $char) {
+ // don't add a dash for the first letter
+ if (!$pos) continue;
+ $num = ord($char);
+ // if character is a capital letter
+ if ($num >= 65 && $num <= 90) {
+ // insert dash
+ array_splice($newchars, $pos, 0, '-');
+ }
+ }
+ return strtoupper(implode("", $newchars));
+
+ }
+ /**
+ * Determine's this property's class name from the property name
+ *
+ * @return string The property class name
+ **/
+ protected function getClassNameFromPropertyName($name) {
+
+ // remove dashes, capitalize properly
+ $parts = explode("-", $name);
+ $property = "";
+ foreach ($parts as $part) $property .= trim(ucfirst(strtolower($part)));
+ // get the class, and instantiate
+ $className = "qCal_Property_" . $property;
+ return $className;
+
+ }
+ /**
+ * Is this property allowed to be specified multiple times in a component?
+ * @return boolean
+ */
+ public function allowMultiple() {
+
+ return (boolean) $this->allowMultiple;
+
+ }
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Action.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Action.php
new file mode 100644
index 00000000..3606d1ea
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Action.php
@@ -0,0 +1,51 @@
+":Jim
+ * Dolittle\, ABC Industries\, +1-919-555-1234
+ *
+ * The following is an example of this property referencing a network
+ * resource, such as a vCard [RFC 2426] object containing the contact
+ * information:
+ *
+ * CONTACT;ALTREP="http://host.com/pdi/jdoe.vcf":Jim
+ * Dolittle\, ABC Industries\, +1-919-555-1234
+ */
+class qCal_Property_Contact extends qCal_Property {
+
+ protected $type = 'TEXT';
+ protected $allowedComponents = array('VEVENT','VTODO','VJOURNAL','VFREEBUSY');
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Created.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Created.php
new file mode 100644
index 00000000..7feb552b
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Created.php
@@ -0,0 +1,45 @@
+value as $value) {
+ $return[] = $value->__toString();
+ }
+ return implode(chr(44), $return);
+
+ }
+ /**
+ * Sets the value of this property. Overwrites any previous values. Use addValue to
+ * add rather than overwrite.
+ * @todo I'm not sure I like how this is done. Eventually I will come back to it.
+ */
+ public function setValue($value) {
+
+ if (!is_array($value)) {
+ $value = array($value);
+ }
+ // parent::setValue($value);
+ $this->value = array();
+ foreach ($value as $val) {
+ $this->value[] = $this->convertValue($val);
+ }
+ return $this;
+
+ }
+ /**
+ * Add a value to the array of values (rather than overwrite)
+ */
+ public function addValue($value) {
+
+ $this->value[] = $this->convertValue($value);
+ return $this;
+
+ }
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/NonStandard.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/NonStandard.php
new file mode 100644
index 00000000..a0b203a8
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/NonStandard.php
@@ -0,0 +1,70 @@
+name = strtoupper($name);
+
+ }
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Organizer.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Organizer.php
new file mode 100644
index 00000000..24ea907f
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Organizer.php
@@ -0,0 +1,89 @@
+
+ *
+ * RELATED-TO:<19960401-080045-4000F192713-0052@host1.com>
+ */
+class qCal_Property_RecurrenceId extends qCal_Property {
+
+ protected $type = 'TEXT';
+ protected $allowedComponents = array('VEVENT','VTODO','VJOURNAL');
+ protected $allowMultiple = true;
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Repeat.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Repeat.php
new file mode 100644
index 00000000..f77d655f
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Repeat.php
@@ -0,0 +1,47 @@
+ (1997 9:00 AM EDT)September 2-11
+ *
+ * Daily until December 24, 1997:
+ *
+ * DTSTART;TZID=US-Eastern:19970902T090000
+ * RRULE:FREQ=DAILY;UNTIL=19971224T000000Z
+ *
+ * ==> (1997 9:00 AM EDT)September 2-30;October 1-25
+ * (1997 9:00 AM EST)October 26-31;November 1-30;December 1-23
+ *
+ * Every other day - forever:
+ *
+ * DTSTART;TZID=US-Eastern:19970902T090000
+ * RRULE:FREQ=DAILY;INTERVAL=2
+ * ==> (1997 9:00 AM EDT)September2,4,6,8...24,26,28,30;
+ * October 2,4,6...20,22,24
+ * (1997 9:00 AM EST)October 26,28,30;November 1,3,5,7...25,27,29;
+ * Dec 1,3,...
+ *
+ * Every 10 days, 5 occurrences:
+ *
+ * DTSTART;TZID=US-Eastern:19970902T090000
+ * RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5
+ *
+ * ==> (1997 9:00 AM EDT)September 2,12,22;October 2,12
+ *
+ * Everyday in January, for 3 years:
+ *
+ * DTSTART;TZID=US-Eastern:19980101T090000
+ * RRULE:FREQ=YEARLY;UNTIL=20000131T090000Z;
+ * BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA
+ * or
+ * RRULE:FREQ=DAILY;UNTIL=20000131T090000Z;BYMONTH=1
+ *
+ * ==> (1998 9:00 AM EDT)January 1-31
+ * (1999 9:00 AM EDT)January 1-31
+ * (2000 9:00 AM EDT)January 1-31
+ *
+ * Weekly for 10 occurrences
+ *
+ * DTSTART;TZID=US-Eastern:19970902T090000
+ * RRULE:FREQ=WEEKLY;COUNT=10
+ *
+ * ==> (1997 9:00 AM EDT)September 2,9,16,23,30;October 7,14,21
+ * (1997 9:00 AM EST)October 28;November 4
+ *
+ * Weekly until December 24, 1997
+ *
+ * DTSTART;TZID=US-Eastern:19970902T090000
+ * RRULE:FREQ=WEEKLY;UNTIL=19971224T000000Z
+ *
+ * ==> (1997 9:00 AM EDT)September 2,9,16,23,30;October 7,14,21
+ * (1997 9:00 AM EST)October 28;November 4,11,18,25;
+ * December 2,9,16,23
+ * Every other week - forever:
+ *
+ * DTSTART;TZID=US-Eastern:19970902T090000
+ * RRULE:FREQ=WEEKLY;INTERVAL=2;WKST=SU
+ *
+ * ==> (1997 9:00 AM EDT)September 2,16,30;October 14
+ * (1997 9:00 AM EST)October 28;November 11,25;December 9,23
+ * (1998 9:00 AM EST)January 6,20;February
+ * ...
+ *
+ * Weekly on Tuesday and Thursday for 5 weeks:
+ *
+ * DTSTART;TZID=US-Eastern:19970902T090000
+ * RRULE:FREQ=WEEKLY;UNTIL=19971007T000000Z;WKST=SU;BYDAY=TU,TH
+ * or
+ * RRULE:FREQ=WEEKLY;COUNT=10;WKST=SU;BYDAY=TU,TH
+ *
+ * ==> (1997 9:00 AM EDT)September 2,4,9,11,16,18,23,25,30;October 2
+ *
+ * Every other week on Monday, Wednesday and Friday until December 24,
+ * 1997, but starting on Tuesday, September 2, 1997:
+ *
+ * DTSTART;TZID=US-Eastern:19970902T090000
+ * RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;
+ * BYDAY=MO,WE,FR
+ * ==> (1997 9:00 AM EDT)September 2,3,5,15,17,19,29;October
+ * 1,3,13,15,17
+ * (1997 9:00 AM EST)October 27,29,31;November 10,12,14,24,26,28;
+ * December 8,10,12,22
+ *
+ * Every other week on Tuesday and Thursday, for 8 occurrences:
+ *
+ * DTSTART;TZID=US-Eastern:19970902T090000
+ * RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=8;WKST=SU;BYDAY=TU,TH
+ *
+ * ==> (1997 9:00 AM EDT)September 2,4,16,18,30;October 2,14,16
+ *
+ * Monthly on the 1st Friday for ten occurrences:
+ *
+ * DTSTART;TZID=US-Eastern:19970905T090000
+ * RRULE:FREQ=MONTHLY;COUNT=10;BYDAY=1FR
+ *
+ * ==> (1997 9:00 AM EDT)September 5;October 3
+ * (1997 9:00 AM EST)November 7;Dec 5
+ * (1998 9:00 AM EST)January 2;February 6;March 6;April 3
+ * (1998 9:00 AM EDT)May 1;June 5
+ *
+ * Monthly on the 1st Friday until December 24, 1997:
+ *
+ * DTSTART;TZID=US-Eastern:19970905T090000
+ * RRULE:FREQ=MONTHLY;UNTIL=19971224T000000Z;BYDAY=1FR
+ *
+ * ==> (1997 9:00 AM EDT)September 5;October 3
+ * (1997 9:00 AM EST)November 7;December 5
+ *
+ * Every other month on the 1st and last Sunday of the month for 10
+ * occurrences:
+ *
+ * DTSTART;TZID=US-Eastern:19970907T090000
+ * RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU
+ *
+ * ==> (1997 9:00 AM EDT)September 7,28
+ * (1997 9:00 AM EST)November 2,30
+ * (1998 9:00 AM EST)January 4,25;March 1,29
+ * (1998 9:00 AM EDT)May 3,31
+ *
+ * Monthly on the second to last Monday of the month for 6 months:
+ *
+ * DTSTART;TZID=US-Eastern:19970922T090000
+ * RRULE:FREQ=MONTHLY;COUNT=6;BYDAY=-2MO
+ *
+ * ==> (1997 9:00 AM EDT)September 22;October 20
+ * (1997 9:00 AM EST)November 17;December 22
+ * (1998 9:00 AM EST)January 19;February 16
+ *
+ * Monthly on the third to the last day of the month, forever:
+ *
+ * DTSTART;TZID=US-Eastern:19970928T090000
+ * RRULE:FREQ=MONTHLY;BYMONTHDAY=-3
+ *
+ * ==> (1997 9:00 AM EDT)September 28
+ * (1997 9:00 AM EST)October 29;November 28;December 29
+ * (1998 9:00 AM EST)January 29;February 26
+ * ...
+ *
+ * Monthly on the 2nd and 15th of the month for 10 occurrences:
+ *
+ * DTSTART;TZID=US-Eastern:19970902T090000
+ * RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15
+ *
+ * ==> (1997 9:00 AM EDT)September 2,15;October 2,15
+ * (1997 9:00 AM EST)November 2,15;December 2,15
+ * (1998 9:00 AM EST)January 2,15
+ *
+ * Monthly on the first and last day of the month for 10 occurrences:
+ *
+ * DTSTART;TZID=US-Eastern:19970930T090000
+ * RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1
+ *
+ * ==> (1997 9:00 AM EDT)September 30;October 1
+ * (1997 9:00 AM EST)October 31;November 1,30;December 1,31
+ * (1998 9:00 AM EST)January 1,31;February 1
+ *
+ * Every 18 months on the 10th thru 15th of the month for 10
+ * occurrences:
+ *
+ * DTSTART;TZID=US-Eastern:19970910T090000
+ * RRULE:FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,
+ * 15
+ *
+ * ==> (1997 9:00 AM EDT)September 10,11,12,13,14,15
+ * (1999 9:00 AM EST)March 10,11,12,13
+ *
+ * Every Tuesday, every other month:
+ *
+ * DTSTART;TZID=US-Eastern:19970902T090000
+ * RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU
+ *
+ * ==> (1997 9:00 AM EDT)September 2,9,16,23,30
+ * (1997 9:00 AM EST)November 4,11,18,25
+ * (1998 9:00 AM EST)January 6,13,20,27;March 3,10,17,24,31
+ * ...
+ *
+ * Yearly in June and July for 10 occurrences:
+ *
+ * DTSTART;TZID=US-Eastern:19970610T090000
+ * RRULE:FREQ=YEARLY;COUNT=10;BYMONTH=6,7
+ * ==> (1997 9:00 AM EDT)June 10;July 10
+ * (1998 9:00 AM EDT)June 10;July 10
+ * (1999 9:00 AM EDT)June 10;July 10
+ * (2000 9:00 AM EDT)June 10;July 10
+ * (2001 9:00 AM EDT)June 10;July 10
+ * Note: Since none of the BYDAY, BYMONTHDAY or BYYEARDAY components
+ * are specified, the day is gotten from DTSTART
+ *
+ * Every other year on January, February, and March for 10 occurrences:
+ *
+ * DTSTART;TZID=US-Eastern:19970310T090000
+ * RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3
+ *
+ * ==> (1997 9:00 AM EST)March 10
+ * (1999 9:00 AM EST)January 10;February 10;March 10
+ * (2001 9:00 AM EST)January 10;February 10;March 10
+ * (2003 9:00 AM EST)January 10;February 10;March 10
+ *
+ * Every 3rd year on the 1st, 100th and 200th day for 10 occurrences:
+ *
+ * DTSTART;TZID=US-Eastern:19970101T090000
+ * RRULE:FREQ=YEARLY;INTERVAL=3;COUNT=10;BYYEARDAY=1,100,200
+ *
+ * ==> (1997 9:00 AM EST)January 1
+ * (1997 9:00 AM EDT)April 10;July 19
+ * (2000 9:00 AM EST)January 1
+ * (2000 9:00 AM EDT)April 9;July 18
+ * (2003 9:00 AM EST)January 1
+ * (2003 9:00 AM EDT)April 10;July 19
+ * (2006 9:00 AM EST)January 1
+ *
+ * Every 20th Monday of the year, forever:
+ * DTSTART;TZID=US-Eastern:19970519T090000
+ * RRULE:FREQ=YEARLY;BYDAY=20MO
+ *
+ * ==> (1997 9:00 AM EDT)May 19
+ * (1998 9:00 AM EDT)May 18
+ * (1999 9:00 AM EDT)May 17
+ * ...
+ *
+ * Monday of week number 20 (where the default start of the week is
+ * Monday), forever:
+ *
+ * DTSTART;TZID=US-Eastern:19970512T090000
+ * RRULE:FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO
+ *
+ * ==> (1997 9:00 AM EDT)May 12
+ * (1998 9:00 AM EDT)May 11
+ * (1999 9:00 AM EDT)May 17
+ * ...
+ *
+ * Every Thursday in March, forever:
+ *
+ * DTSTART;TZID=US-Eastern:19970313T090000
+ * RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH
+ *
+ * ==> (1997 9:00 AM EST)March 13,20,27
+ * (1998 9:00 AM EST)March 5,12,19,26
+ * (1999 9:00 AM EST)March 4,11,18,25
+ * ...
+ *
+ * Every Thursday, but only during June, July, and August, forever:
+ *
+ * DTSTART;TZID=US-Eastern:19970605T090000
+ * RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8
+ *
+ * ==> (1997 9:00 AM EDT)June 5,12,19,26;July 3,10,17,24,31;
+ * August 7,14,21,28
+ * (1998 9:00 AM EDT)June 4,11,18,25;July 2,9,16,23,30;
+ * August 6,13,20,27
+ * (1999 9:00 AM EDT)June 3,10,17,24;July 1,8,15,22,29;
+ * August 5,12,19,26
+ * ...
+ *
+ * Every Friday the 13th, forever:
+ *
+ * DTSTART;TZID=US-Eastern:19970902T090000
+ * EXDATE;TZID=US-Eastern:19970902T090000
+ * RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13
+ *
+ * ==> (1998 9:00 AM EST)February 13;March 13;November 13
+ * (1999 9:00 AM EDT)August 13
+ * (2000 9:00 AM EDT)October 13
+ * ...
+ *
+ * The first Saturday that follows the first Sunday of the month,
+ * forever:
+ *
+ * DTSTART;TZID=US-Eastern:19970913T090000
+ * RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13
+ *
+ * ==> (1997 9:00 AM EDT)September 13;October 11
+ * (1997 9:00 AM EST)November 8;December 13
+ * (1998 9:00 AM EST)January 10;February 7;March 7
+ * (1998 9:00 AM EDT)April 11;May 9;June 13...
+ * ...
+ *
+ * Every four years, the first Tuesday after a Monday in November,
+ * forever (U.S. Presidential Election day):
+ *
+ * DTSTART;TZID=US-Eastern:19961105T090000
+ * RRULE:FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,
+ * 5,6,7,8
+ *
+ * ==> (1996 9:00 AM EST)November 5
+ * (2000 9:00 AM EST)November 7
+ * (2004 9:00 AM EST)November 2
+ * ...
+ *
+ * The 3rd instance into the month of one of Tuesday, Wednesday or
+ * Thursday, for the next 3 months:
+ *
+ * DTSTART;TZID=US-Eastern:19970904T090000
+ * RRULE:FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3
+ *
+ * ==> (1997 9:00 AM EDT)September 4;October 7
+ * (1997 9:00 AM EST)November 6
+ *
+ * The 2nd to last weekday of the month:
+ *
+ * DTSTART;TZID=US-Eastern:19970929T090000
+ * RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2
+ *
+ * ==> (1997 9:00 AM EDT)September 29
+ * (1997 9:00 AM EST)October 30;November 27;December 30
+ * (1998 9:00 AM EST)January 29;February 26;March 30
+ * ...
+ *
+ * Every 3 hours from 9:00 AM to 5:00 PM on a specific day:
+ *
+ * DTSTART;TZID=US-Eastern:19970902T090000
+ * RRULE:FREQ=HOURLY;INTERVAL=3;UNTIL=19970902T170000Z
+ *
+ * ==> (September 2, 1997 EDT)09:00,12:00,15:00
+ *
+ * Every 15 minutes for 6 occurrences:
+ *
+ * DTSTART;TZID=US-Eastern:19970902T090000
+ * RRULE:FREQ=MINUTELY;INTERVAL=15;COUNT=6
+ *
+ * ==> (September 2, 1997 EDT)09:00,09:15,09:30,09:45,10:00,10:15
+ *
+ * Every hour and a half for 4 occurrences:
+ *
+ * DTSTART;TZID=US-Eastern:19970902T090000
+ * RRULE:FREQ=MINUTELY;INTERVAL=90;COUNT=4
+ *
+ * ==> (September 2, 1997 EDT)09:00,10:30;12:00;13:30
+ *
+ * Every 20 minutes from 9:00 AM to 4:40 PM every day:
+ *
+ * DTSTART;TZID=US-Eastern:19970902T090000
+ * RRULE:FREQ=DAILY;BYHOUR=9,10,11,12,13,14,15,16;BYMINUTE=0,20,40
+ * or
+ * RRULE:FREQ=MINUTELY;INTERVAL=20;BYHOUR=9,10,11,12,13,14,15,16
+ *
+ * ==> (September 2, 1997 EDT)9:00,9:20,9:40,10:00,10:20,
+ * ... 16:00,16:20,16:40
+ * (September 3, 1997 EDT)9:00,9:20,9:40,10:00,10:20,
+ * ...16:00,16:20,16:40
+ * ...
+ *
+ * An example where the days generated makes a difference because of
+ * WKST:
+ *
+ * DTSTART;TZID=US-Eastern:19970805T090000
+ * RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=MO
+ *
+ * ==> (1997 EDT)Aug 5,10,19,24
+ *
+ * changing only WKST from MO to SU, yields different results...
+ *
+ * DTSTART;TZID=US-Eastern:19970805T090000
+ * RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=SU
+ * ==> (1997 EDT)August 5,17,19,31
+ */
+class qCal_Property_Rrule extends qCal_Property {
+
+ protected $type = 'RECUR';
+ protected $allowedComponents = array('VEVENT','VTODO','VJOURNAL','VTIMEZONE','DAYLIGHT','STANDARD');
+ protected $allowMultiple = true;
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Sequence.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Sequence.php
new file mode 100644
index 00000000..f2380a99
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Property/Sequence.php
@@ -0,0 +1,94 @@
+
+ * ;Minimum iCalendar version needed to parse the iCalendar object
+ *
+ * maxver =
+ * ;Maximum iCalendar version needed to parse the iCalendar object
+ *
+ * Example: The following is an example of this property:
+ *
+ * VERSION:2.0
+ */
+class qCal_Property_Version extends qCal_Property {
+
+ protected $type = 'TEXT';
+ protected $allowedComponents = array('VCALENDAR');
+ protected $default = "2.0";
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Renderer.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Renderer.php
new file mode 100644
index 00000000..aedf7ac0
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Renderer.php
@@ -0,0 +1,6 @@
+getName() . self::LINE_ENDING;
+ foreach ($component->getProperties() as $property) {
+ if (is_array($property)) {
+ foreach ($property as $prop) {
+ $return .= $this->renderProperty($prop);
+ }
+ } else {
+ $return .= $this->renderProperty($property);
+ }
+ }
+ foreach ($component->getChildren() as $children) {
+ if (is_array($children)) {
+ foreach ($children as $child) {
+ $return .= $this->render($child);
+ }
+ } else {
+ $return .= $this->render($children);
+ }
+ }
+ return $return . "END:" . $component->getName() . self::LINE_ENDING;
+
+ }
+ /**
+ * Renders a property in accordance with rfc 2445
+ * @todo $proptype is created below and never used... wtf?
+ */
+ protected function renderProperty(qCal_Property $property) {
+
+ $propval = $property->getValue();
+ $params = $property->getParams();
+ $paramreturn = "";
+ foreach ($params as $paramname => $paramval) {
+ $paramreturn .= $this->renderParam($paramname, $paramval);
+ }
+ // if property has a "value" param, then use it as the type instead
+ $proptype = isset($params['VALUE']) ? $params['VALUE'] : $property->getType();
+ if ($property instanceof qCal_Property_MultiValue) {
+ $values = array();
+ foreach ($property->getValue() as $value) {
+ $values[] = $this->renderValue($property->getValue(), $proptype);
+ }
+ $value = implode(chr(44), $values);
+ } else {
+ $value = $this->renderValue($property->getValue(), $proptype);
+ }
+ $content = $property->getName() . $paramreturn . ":" . $value . self::LINE_ENDING;
+ return $this->fold($content);
+
+ }
+ /**
+ * Renders a value
+ */
+ protected function renderValue($value, $type) {
+
+ switch(strtoupper($type)) {
+ case "TEXT":
+ $value = str_replace(",", "\,", $value);
+ break;
+ }
+ return $value;
+
+ }
+ /**
+ * Renders a parameter
+ * RFC 2445 says if paramval contains COLON (US-ASCII decimal
+ * 58), SEMICOLON (US-ASCII decimal 59) or COMMA (US-ASCII decimal 44)
+ * character separators MUST be specified as quoted-string text values
+ */
+ protected function renderParam($name, $value) {
+
+ $invchars = array(chr(58),chr(59),chr(44));
+ $quote = false;
+ foreach ($invchars as $char) {
+ if (strstr($value, $char)) {
+ $quote = true;
+ break;
+ }
+ }
+ if ($quote) $value = '"' . $value . '"';
+ return ";" . $name . "=" . $value;
+
+ }
+
+ /**
+ * Text cannot exceed 75 octets. This method will "fold" long lines in accordance with RFC 2445
+ * @todo Make sure this is multi-byte safe
+ * @todo The file I downloaded from google used this same folding method (long lines went to 76)
+ * so until I see any different, I'm going to keep it at 76.
+ */
+ protected function fold($data) {
+
+ if (strlen($data) == (self::FOLD_LENGTH + strlen(self::LINE_ENDING))) return $data;
+ $apart = str_split($data, self::FOLD_LENGTH);
+ return implode(self::LINE_ENDING . " ", $apart);
+
+ }
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Time.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Time.php
new file mode 100644
index 00000000..0a4be942
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Time.php
@@ -0,0 +1,198 @@
+setTimezone($timezone)
+ ->setTime($hour, $minute, $second, $rollover);
+
+ }
+ /**
+ * Set the time
+ * @access protected This class is immutable, so this is protected. Only the constructor calls it.
+ */
+ protected function setTime($hour = null, $minute = null, $second = null, $rollover = null) {
+
+ if (is_null($hour)) {
+ $hour = gmdate("H");
+ }
+ if (is_null($minute)) {
+ $minute = gmdate("i");
+ }
+ if (is_null($second)) {
+ $second = gmdate("s");
+ }
+ if (is_null($rollover)) $rollover = false;
+ if (!$rollover) {
+ if ($hour > 23 || $minute > 59 || $second > 59) {
+ throw new qCal_DateTime_Exception_InvalidTime(sprintf("Invalid time specified for qCal_Time: \"%02d:%02d:%02d\"", $hour, $minute, $second));
+ }
+ }
+ // since PHP is incapable of storing a time without a date, we use the first day of
+ // the unix epoch so that we only have the amount of seconds since the zero of unix epoch
+ // we only use gm here because we don't want the server's timezone to interfere
+ $time = gmmktime($hour, $minute, $second, 1, 1, 1970);
+ $this->time = $time;
+ $formatString = "a|A|B|g|G|h|H|i|s|u";
+ $keys = explode("|", $formatString);
+ $vals = explode("|", gmdate($formatString, $this->getTimestamp(false)));
+ $this->timeArray = array_merge($this->timeArray, array_combine($keys, $vals));
+ return $this;
+
+ }
+ /**
+ * Set the timezone
+ */
+ protected function setTimezone($timezone) {
+
+ if (is_null($timezone) || !($timezone instanceof qCal_Timezone)) {
+ $timezone = qCal_Timezone::factory($timezone);
+ }
+ $this->timezone = $timezone;
+ return $this;
+
+ }
+ /**
+ * Get the timezone
+ */
+ public function getTimezone() {
+
+ return $this->timezone;
+
+ }
+ /**
+ * Generate a qCal_Time object via a string or a number of other methods
+ */
+ public static function factory($time, $timezone = null) {
+
+ if (is_null($timezone) || !($timezone instanceof qCal_Timezone)) {
+ $timezone = qCal_Timezone::factory($timezone);
+ }
+ // get the default timezone so we can set it back to it later
+ $tz = date_default_timezone_get();
+ // set the timezone to GMT temporarily
+ date_default_timezone_set("GMT");
+
+ if (is_integer($time)) {
+ // @todo Handle timestamps
+ // @maybe not...
+ }
+ if (is_string($time)) {
+ if ($time == "now") {
+ $time = new qCal_Time(null, null, null, $timezone);
+ } else {
+ $tstring = "01/01/1970 $time";
+ if (!$timestamp = strtotime($tstring)) {
+ // if unix timestamp can't be created throw an exception
+ throw new qCal_DateTime_Exception_InvalidTime("Invalid or ambiguous time string passed to qCal_Time::factory()");
+ }
+ list($hour, $minute, $second) = explode(":", gmdate("H:i:s", $timestamp));
+ $time = new qCal_Time($hour, $minute, $second, $timezone);
+ }
+ }
+
+ // set the timezone back to what it was
+ date_default_timezone_set($tz);
+
+ return $time;
+
+ }
+ /**
+ * Get the hour
+ */
+ public function getHour() {
+
+ return $this->timeArray['G'];
+
+ }
+ /**
+ * Get the minute
+ */
+ public function getMinute() {
+
+ return $this->timeArray['i'];
+
+ }
+ /**
+ * Get the second
+ */
+ public function getSecond() {
+
+ return $this->timeArray['s'];
+
+ }
+ /**
+ * Get the timestamp
+ */
+ public function getTimestamp($useOffset = true) {
+
+ $time = ($useOffset) ?
+ $this->time - $this->getTimezone()->getOffsetSeconds() :
+ $this->time;
+ return $time;
+
+ }
+ /**
+ * Set the format to use when outputting as a string
+ */
+ public function setFormat($format) {
+
+ $this->format = (string) $format;
+ return $this;
+
+ }
+ /**
+ * Output the object using PHP's date() function's meta-characters
+ */
+ public function format($format) {
+
+ $escape = false;
+ $meta = str_split($format);
+ $output = array();
+ foreach($meta as $char) {
+ if ($char == '\\') {
+ $escape = true;
+ continue;
+ }
+ if (!$escape && array_key_exists($char, $this->timeArray)) {
+ $output[] = $this->timeArray[$char];
+ } else {
+ $output[] = $char;
+ }
+ // reset this to false after every iteration that wasn't "continued"
+ $escape = false;
+ }
+ return implode($output);
+
+ }
+ /**
+ * Output the object as a string
+ */
+ public function __toString() {
+
+ return $this->format($this->format);
+
+ }
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Timezone.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Timezone.php
new file mode 100644
index 00000000..a1d9c225
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Timezone.php
@@ -0,0 +1,224 @@
+setName($name);
+ $this->setOffsetSeconds($offset);
+ if (is_null($abbreviation)) $abbreviation = $name;
+ $this->setAbbreviation($abbreviation);
+ $this->setIsDaylightSavings($daylightsavings);
+ $this->formatArray = array(
+ 'e' => $this->getName(),
+ 'I' => (integer) $this->isDaylightSavings(),
+ 'O' => $this->getOffsetHours(),
+ 'P' => $this->getOffset(),
+ 'T' => $this->getAbbreviation(),
+ 'Z' => $this->getOffsetSeconds(),
+ );
+
+ }
+ public function setName($name) {
+
+ $this->name = (string) $name;
+
+ }
+ public function setOffsetSeconds($offset) {
+
+ $this->offsetSeconds = (integer) $offset;
+
+ }
+ public function setAbbreviation($abbreviation) {
+
+ $this->abbreviation = (string) $abbreviation;
+
+ }
+ public function setIsDaylightSavings($daylightSavings = null) {
+
+ $this->isDaylightSavings = (boolean) $daylightSavings;
+
+ }
+ /**
+ * Generate a timezone from either an array of parameters, or a timezone
+ * name such as "America/Los_Angeles".
+ * @link http://php.net/manual/en/timezones.php A directory of valid timezones
+ * @todo This method is FUGLY. Rewrite it and make it make sense. This is sort of nonsensical.
+ */
+ public static function factory($timezone = null) {
+
+ if (is_array($timezone)) {
+ // remove anything irrelevant
+ $vals = array_intersect_key($timezone, array_flip(array('name','offsetSeconds','abbreviation','isDaylightSavings')));
+ if (!array_key_exists("name", $vals)) {
+ // @todo throw an exception or something
+ }
+ if (!array_key_exists("offsetSeconds", $vals)) {
+ // @todo throw an exception or something
+ }
+ $name = $vals['name'];
+ $offsetSeconds = $vals['offsetSeconds'];
+ $abbreviation = (array_key_exists('abbreviation', $vals)) ? $vals['abbreviation'] : null;
+ $isDaylightSavings = (array_key_exists('isDaylightSavings', $vals)) ? $vals['isDaylightSavings'] : null;
+ $timezone = new qCal_Timezone($name, $offsetSeconds, $abbreviation, $isDaylightSavings);
+ } else {
+ // get the timezone information out of the string
+ $defaultTz = date_default_timezone_get();
+
+ if (is_null($timezone)) $timezone = $defaultTz;
+
+ // if the timezone being set is invalid, we will get a PHP notice, so error is suppressed here
+ // @todo It would be more clean and probably more efficient to use php's error handling to throw an exception here...
+ if (is_string($timezone)) {
+ @date_default_timezone_set($timezone);
+ // if the function above didn't work, this will be true
+ if (date_default_timezone_get() != $timezone) {
+ // if the timezone requested is registered, use it
+ if (array_key_exists($timezone, self::$timezones)) {
+ $timezone = self::$timezones[$timezone];
+ } else {
+ // otherwise, throw an exception
+ throw new qCal_DateTime_Exception_InvalidTimezone("'$timezone' is not a valid timezone.");
+ }
+ } else {
+ // if the timezone specified was a valid (native php) timezone, use it
+ $name = date("e");
+ $offset = date("Z");
+ $abbr = date("T");
+ $ds = date("I");
+ $timezone = new qCal_Timezone($name, $offset, $abbr, $ds);
+ }
+ }
+
+ // now set it back to what it was...
+ date_default_timezone_set($defaultTz);
+ }
+ return $timezone;
+
+ }
+
+ public static function register(qCal_Timezone $timezone) {
+
+ self::$timezones[$timezone->getName()] = $timezone;
+
+ }
+
+ public static function unregister($timezone) {
+
+ unset(self::$timezones[(string) $timezone]);
+
+ }
+
+ public function getName() {
+
+ return $this->name;
+
+ }
+
+ public function getOffset() {
+
+ $seconds = $this->getOffsetSeconds();
+ $negpos = "+";
+ if ($seconds < 0) {
+ $negpos = "-";
+ }
+ $hours = (integer) ($seconds / 60 / 60);
+ $minutes = $hours * 60;
+ $minutes = ($seconds / 60) - $minutes;
+ return sprintf("%s%02d:%02d", $negpos, abs($hours), abs($minutes));
+
+ }
+
+ public function getOffsetHours() {
+
+ $seconds = $this->getOffsetSeconds();
+ $negpos = "+";
+ if ($seconds < 0) {
+ $negpos = "-";
+ }
+ $hours = (integer) ($seconds / 60 / 60);
+ $minutes = $hours * 60;
+ $minutes = ($seconds / 60) - $minutes;
+ return sprintf("%s%02d%02d", $negpos, abs($hours), abs($minutes));
+
+ }
+
+ public function getOffsetSeconds() {
+
+ return $this->offsetSeconds;
+
+ }
+
+ public function getAbbreviation() {
+
+ return $this->abbreviation;
+
+ }
+
+ public function isDaylightSavings() {
+
+ return $this->isDaylightSavings;
+
+ }
+
+ /**
+ * Set the format that should be used when calling either __toString() or format() without an argument.
+ * @param string $format
+ */
+ public function setFormat($format) {
+
+ $this->format = (string) $format;
+ return $this;
+
+ }
+
+ public function format($format) {
+
+ $escape = false;
+ $meta = str_split($format);
+ $output = array();
+ foreach($meta as $char) {
+ if ($char == '\\') {
+ $escape = true;
+ continue;
+ }
+ if (!$escape && array_key_exists($char, $this->formatArray)) {
+ $output[] = $this->formatArray[$char];
+ } else {
+ $output[] = $char;
+ }
+ // reset this to false after every iteration that wasn't "continued"
+ $escape = false;
+ }
+ return implode($output);
+
+ }
+
+ public function __toString() {
+
+ return $this->format($this->format);
+
+ }
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value.php
new file mode 100644
index 00000000..3e3563cd
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value.php
@@ -0,0 +1,98 @@
+setValue($value);
+
+ }
+ /**
+ * A factory for data type objects. Pass in a type and a value, and it will return the value
+ * casted to the proper type
+ */
+ public static function factory($type, $value) {
+
+ // remove dashes, capitalize properly
+ $parts = explode("-", $type);
+ $type = "";
+ foreach ($parts as $part) $type .= trim(ucfirst(strtolower($part)));
+ // get the class, and instantiate
+ $className = "qCal_Value_" . $type;
+ $class = new $className($value);
+ return $class;
+
+ }
+ /**
+ * Sets the value of this object. The beauty of using inheritence here is that I can store
+ * the value however I want for any value type, and then on __toString() I can return it how
+ * iCalendar specifies :)
+ */
+ public function setValue($value) {
+
+ $this->value = $this->doCast($value);
+ return $this;
+
+ }
+ /**
+ * Returns raw value (as it is stored)
+ */
+ public function getValue() {
+
+ return $this->value;
+
+ }
+ /**
+ * Casts $value to this data type
+ */
+ public function cast($value) {
+
+ return $this->doCast($value);
+
+ }
+ /**
+ * Returns the value as a string
+ */
+ public function __toString() {
+
+ return $this->toString($this->value);
+
+ }
+ /**
+ * Converts from native format to a string, __toString() calls this internally
+ */
+ protected function toString($value) {
+
+ return (string) $value;
+
+ }
+ /**
+ * This is left to be implemented by children classes, basically they
+ * implement this method to cast any input into their data type (from a string)
+ * @todo Change the name of this to something more appropriate, maybe toNative or something
+ */
+ abstract protected function doCast($value);
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Binary.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Binary.php
new file mode 100644
index 00000000..fb8b393c
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Binary.php
@@ -0,0 +1,69 @@
+
+ *
+ * qCal_DataType_Binary
+ * This object defines any binary object that may be attached to an
+ * icalendar file.
+ */
+class qCal_Value_Binary extends qCal_Value {
+
+ /**
+ * When the value of a binary property is requested, it will be returned as a base64 encoded string
+ * @todo Base64 is the only encoding supported by this standard, but the encoding=base64 parameter must be
+ * provided regardless.
+ */
+ protected function toString($value) {
+
+ return base64_encode($value);
+
+ }
+ /**
+ * Binary can be store as-is I believe, so don't change it
+ */
+ protected function doCast($value) {
+
+ return $value;
+
+ }
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Boolean.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Boolean.php
new file mode 100644
index 00000000..3d47a553
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Boolean.php
@@ -0,0 +1,47 @@
+format('Ymd');
+
+ }
+ /**
+ * This converts to a qCal_Date for internal storage
+ */
+ protected function doCast($value) {
+
+ $date = qCal_Date::factory($value);
+ return $date;
+
+ }
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/DateTime.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/DateTime.php
new file mode 100644
index 00000000..4f138ce0
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/DateTime.php
@@ -0,0 +1,134 @@
+format('Ymd\THis');
+
+ }
+ /**
+ * This converts to a qCal_Date for internal storage
+ */
+ protected function doCast($value) {
+
+ // @todo This may be the wrong place to do this...
+ if ($value instanceof qCal_DateTime) {
+ return $value;
+ }
+ $date = qCal_DateTime::factory($value);
+ return $date;
+
+ }
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Duration.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Duration.php
new file mode 100644
index 00000000..b0a86b08
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Duration.php
@@ -0,0 +1,67 @@
+toICal();
+
+ }
+ /**
+ * Convert to internal representation
+ */
+ protected function doCast($value) {
+
+ return new qCal_DateTime_Duration($value);
+
+ }
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Float.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Float.php
new file mode 100644
index 00000000..56474cac
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Float.php
@@ -0,0 +1,44 @@
+getUnixTimestamp() + $duration->getSeconds()); // @todo This needs to be updated once qCal_DateTime accepts timestamps
+ }
+ return new qCal_DateTime_Period($start, $end);
+
+ }
+ /**
+ * Convert to string - this converts to string into the UTC/UTC format
+ */
+ protected function toString($value) {
+
+ return $value->getStart()->getUtc() . "/"
+ . $value->getEnd()->getUtc();
+
+ }
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Recur.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Recur.php
new file mode 100644
index 00000000..97f6c5ce
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Recur.php
@@ -0,0 +1,279 @@
+format('His');
+
+ }
+ /**
+ * This converts to a qCal_Date for internal storage
+ */
+ protected function doCast($value) {
+
+ $date = qCal_Time::factory($value);
+ return $date;
+
+ }
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Uri.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Uri.php
new file mode 100644
index 00000000..03c3edc2
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/Uri.php
@@ -0,0 +1,56 @@
+
+ *
+ * Description: This data type might be used to reference binary
+ * information, for values that are large, or otherwise undesirable to
+ * include directly in the iCalendar object.
+ *
+ * The URI value formats in RFC 1738, RFC 2111 and any other IETF
+ * registered value format can be specified.
+ *
+ * Any IANA registered URI format can be used. These include, but are
+ * not limited to, those defined in RFC 1738 and RFC 2111.
+ *
+ * When a property parameter value is a URI value type, the URI MUST be
+ * specified as a quoted-string value.
+ *
+ * No additional content value encoding (i.e., BACKSLASH character
+ * encoding) is defined for this value type.
+ *
+ * Example: The following is a URI for a network file:
+ *
+ * http://host1.com/my-report.txt
+ */
+class qCal_Value_Uri extends qCal_Value {
+
+ protected function toString($value) {
+
+ return (string) $value;
+
+ }
+ /**
+ * @todo: implement this
+ */
+ protected function doCast($value) {
+
+ return $value;
+
+ }
+
+}
\ No newline at end of file
diff --git a/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/UtcOffset.php b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/UtcOffset.php
new file mode 100644
index 00000000..9d035533
--- /dev/null
+++ b/oss/vtiger/trunk/modules/Mobile/third-party/qCal/qCal/Value/UtcOffset.php
@@ -0,0 +1,50 @@
+