1.7 Developer Pre-Release
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
.htaccess
|
||||
php53.cgi
|
||||
include/ost-config.php
|
||||
*.sw[a-z]
|
||||
280
LICENSE.txt
Normal file
@@ -0,0 +1,280 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the 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 Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
38
README.md
Normal file
@@ -0,0 +1,38 @@
|
||||
osTicket
|
||||
========
|
||||
osTicket is a widely-used open source support ticket system. It seamlessly
|
||||
integrates inquiries created via email, phone and web-based forms into a
|
||||
simple easy-to-use multi-user web interface. Manage, organize and archive
|
||||
all your support requests and responses in one place while providing your
|
||||
customers with accountability and responsiveness they deserve.
|
||||
|
||||
How osTicket works for you
|
||||
--------------------------
|
||||
1. Users create tickets via your website, email, or phone
|
||||
1. Incoming tickets are saved and assigned to agents
|
||||
1. Agents help your users resolve their issues
|
||||
|
||||
osTicket is an attractive alternative to higher-cost and complex customer
|
||||
support systems; simple, lightweight, reliable, open source, web-based and
|
||||
easy to setup and use. The best part is, it's completely free.
|
||||
|
||||
Installation
|
||||
------------
|
||||
Download the
|
||||
[source](./zipball/master) from
|
||||
the GitHub page. Follow the instructions for installation on the osTicket
|
||||
[wiki](http://osticket.com/wiki/Installation).
|
||||
|
||||
Help
|
||||
----
|
||||
Visit the [wiki](http://osticket.com/wiki/Home) or the
|
||||
[forum](http://osticket.com/forums/). And if you'd like professional help
|
||||
managing your osTicket installation,
|
||||
[commercial support](http://osticket.com/support/) is available.
|
||||
|
||||
Contributing
|
||||
------------
|
||||
Create your own fork of the project and use
|
||||
[git-flow](https://github.com/nvie/gitflow) to create a new feature. Once
|
||||
the feature is published in your fork, send a pull request to begin the
|
||||
conversation of integrating your new feature into osTicket.
|
||||
85
api/api.inc.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/*********************************************************************
|
||||
api.inc.php
|
||||
|
||||
File included on every API page...handles security and abuse issues
|
||||
|
||||
Peter Rotich <peter@osticket.com>
|
||||
Copyright (c) 2006-2012 osTicket
|
||||
http://www.osticket.com
|
||||
|
||||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||||
See LICENSE.TXT for details.
|
||||
|
||||
vim: expandtab sw=4 ts=4 sts=4:
|
||||
**********************************************************************/
|
||||
//postfix exit codes see /usr/include/sysexits.h
|
||||
define('EX_DATAERR', 65); /* data format error */
|
||||
define('EX_NOINPUT', 66); /* cannot open input */
|
||||
define('EX_UNAVAILABLE', 69); /* service unavailable */
|
||||
define('EX_IOERR', 74); /* input/output error */
|
||||
define('EX_TEMPFAIL',75); /* temp failure; user is invited to retry */
|
||||
define('EX_NOPERM', 77); /* permission denied */
|
||||
define('EX_CONFIG', 78); /* configuration error */
|
||||
|
||||
define('EX_SUCCESS',0); /* success baby */
|
||||
|
||||
if(!file_exists('../main.inc.php')) exit(EX_CONFIG);
|
||||
require_once('../main.inc.php');
|
||||
if(!defined('INCLUDE_DIR')) exit(EX_CONFIG);
|
||||
|
||||
require_once(INCLUDE_DIR.'class.http.php');
|
||||
require_once(INCLUDE_DIR.'class.api.php');
|
||||
|
||||
define('OSTAPIINC',TRUE); // Define tag that included files can check
|
||||
|
||||
$remotehost=(isset($_SERVER['HTTP_HOST']) || isset($_SERVER['REMOTE_ADDR']))?TRUE:FALSE;
|
||||
/* API exit helper */
|
||||
function api_exit($code,$msg='') {
|
||||
global $remotehost,$cfg;
|
||||
|
||||
if($code!=EX_SUCCESS) {
|
||||
//Error occured...
|
||||
$_SESSION['api']['errors']+=1;
|
||||
$_SESSION['api']['time']=time();
|
||||
Sys::log(LOG_WARNING,"API error - code #$code",$msg);
|
||||
//echo "API Error:.$msg";
|
||||
}
|
||||
if($remotehost){
|
||||
switch($code) {
|
||||
case EX_SUCCESS:
|
||||
Http::response(200,$code,'text/plain');
|
||||
break;
|
||||
case EX_UNAVAILABLE:
|
||||
Http::response(405,$code,'text/plain');
|
||||
break;
|
||||
case EX_NOPERM:
|
||||
Http::response(403,$code,'text/plain');
|
||||
break;
|
||||
case EX_DATAERR:
|
||||
case EX_NOINPUT:
|
||||
default:
|
||||
Http::response(416,$code,'text/plain');
|
||||
}
|
||||
}
|
||||
exit($code);
|
||||
}
|
||||
|
||||
//Remote hosts need authorization.
|
||||
if($remotehost) {
|
||||
|
||||
$ip=$_SERVER['REMOTE_ADDR'];
|
||||
$key=$_SERVER['HTTP_USER_AGENT']; //pulling all tricks.
|
||||
//Upto 10 consecutive errors allowed...before a 5 minute timeout.
|
||||
//One more error during timeout and timeout starts a new clock
|
||||
if($_SESSION['api']['errors']>10 && (time()-$_SESSION['api']['time'])<=5*60) { // timeout!
|
||||
api_exit(EX_NOPERM,"Remote host [$ip] in timeout - error #".$_SESSION['api']['errors']);
|
||||
}
|
||||
//Check API key & ip
|
||||
if(!Validator::is_ip($ip) || !Api::validate($key,$ip)) {
|
||||
api_exit(EX_NOPERM,'Unknown remote host ['.$ip.'] or invalid API key ['.$key.']');
|
||||
}
|
||||
//At this point we know the remote host/IP is allowed.
|
||||
$_SESSION['api']['errors']=0; //clear errors for the session.
|
||||
}
|
||||
?>
|
||||
21
api/cron.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/*********************************************************************
|
||||
cron.php
|
||||
|
||||
File to handle cron job calls (local and remote).
|
||||
|
||||
Peter Rotich <peter@osticket.com>
|
||||
Copyright (c) 2006-2012 osTicket
|
||||
http://www.osticket.com
|
||||
|
||||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||||
See LICENSE.TXT for details.
|
||||
|
||||
vim: expandtab sw=4 ts=4 sts=4:
|
||||
**********************************************************************/
|
||||
@chdir(realpath(dirname(__FILE__)).'/'); //Change dir.
|
||||
require('api.inc.php');
|
||||
require_once(INCLUDE_DIR.'class.cron.php');
|
||||
Cron::run();
|
||||
Sys::log(LOG_DEBUG,'Cron Job','External cron job executed ['.$_SERVER['REMOTE_ADDR'].']');
|
||||
?>
|
||||
27
api/http.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/*********************************************************************
|
||||
api.php
|
||||
|
||||
Controller for the osTicket API
|
||||
|
||||
Jared Hancock
|
||||
Copyright (c) 2006-2012 osTicket
|
||||
http://www.osticket.com
|
||||
|
||||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||||
See LICENSE.TXT for details.
|
||||
|
||||
vim: expandtab sw=4 ts=4 sts=4:
|
||||
**********************************************************************/
|
||||
|
||||
chdir('..');
|
||||
include "main.inc.php";
|
||||
|
||||
# Include the main api urls
|
||||
require_once INCLUDE_DIR."class.dispatcher.php";
|
||||
$dispatcher = Dispatcher::include_urls("urls.conf.php");
|
||||
|
||||
# Call the respective function
|
||||
$dispatcher->resolve($_SERVER['PATH_INFO']);
|
||||
|
||||
?>
|
||||
3
api/index.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
header('Location: ../');
|
||||
?>
|
||||
132
api/pipe.php
Normal file
@@ -0,0 +1,132 @@
|
||||
#!/usr/bin/php -q
|
||||
<?php
|
||||
/*********************************************************************
|
||||
pipe.php
|
||||
|
||||
Converts piped emails to ticket. Both local and remote!
|
||||
|
||||
Peter Rotich <peter@osticket.com>
|
||||
Copyright (c) 2006-2012 osTicket
|
||||
http://www.osticket.com
|
||||
|
||||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||||
See LICENSE.TXT for details.
|
||||
|
||||
vim: expandtab sw=4 ts=4 sts=4:
|
||||
**********************************************************************/
|
||||
@chdir(realpath(dirname(__FILE__)).'/'); //Change dir.
|
||||
ini_set('memory_limit', '256M'); //The concern here is having enough mem for emails with attachments.
|
||||
require('api.inc.php');
|
||||
require_once(INCLUDE_DIR.'class.mailparse.php');
|
||||
require_once(INCLUDE_DIR.'class.email.php');
|
||||
|
||||
//Make sure piping is enabled!
|
||||
if(!$cfg->enableEmailPiping())
|
||||
api_exit(EX_UNAVAILABLE,'Email piping not enabled - check MTA settings.');
|
||||
//Get the input
|
||||
$data=isset($_SERVER['HTTP_HOST'])?file_get_contents('php://input'):file_get_contents('php://stdin');
|
||||
if(empty($data)){
|
||||
api_exit(EX_NOINPUT,'No data');
|
||||
}
|
||||
|
||||
//Parse the email.
|
||||
$parser= new Mail_Parse($data);
|
||||
if(!$parser->decode()){ //Decode...returns false on decoding errors
|
||||
api_exit(EX_DATAERR,'Email parse failed ['.$parser->getError()."]\n\n".$data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//Check from address. make sure it is not a banned address.
|
||||
$fromlist = $parser->getFromAddressList();
|
||||
//Check for parsing errors on FROM address.
|
||||
if(!$fromlist || PEAR::isError($fromlist)){
|
||||
api_exit(EX_DATAERR,'Invalid FROM address ['.$fromlist?$fromlist->getMessage():''."]\n\n".$data);
|
||||
}
|
||||
|
||||
$from=$fromlist[0]; //Default.
|
||||
foreach($fromlist as $fromobj){
|
||||
if(!Validator::is_email($fromobj->mailbox.'@'.$fromobj->host))
|
||||
continue;
|
||||
$from=$fromobj;
|
||||
break;
|
||||
}
|
||||
|
||||
//TO Address:Try to figure out the email associated with the message.
|
||||
$tolist = $parser->getToAddressList();
|
||||
foreach ($tolist as $toaddr){
|
||||
if(($emailId=Email::getIdByEmail($toaddr->mailbox.'@'.$toaddr->host))){
|
||||
//We've found target email.
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!$emailId && ($cclist=$parser->getCcAddressList())) {
|
||||
foreach ($cclist as $ccaddr){
|
||||
if(($emailId=Email::getIdByEmail($ccaddr->mailbox.'@'.$ccaddr->host))){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//TODO: Options to reject emails without a matching To address in db? May be it was Bcc? Current Policy: If you pipe, we accept policy
|
||||
|
||||
require_once(INCLUDE_DIR.'class.ticket.php'); //We now need this bad boy!
|
||||
|
||||
$var=array();
|
||||
$deptId=0;
|
||||
$name=trim($from->personal,'"');
|
||||
if($from->comment && $from->comment[0])
|
||||
$name.=' ('.$from->comment[0].')';
|
||||
$subj=utf8_encode($parser->getSubject());
|
||||
if(!($body=Format::stripEmptyLines($parser->getBody())) && $subj)
|
||||
$body=$subj;
|
||||
|
||||
$var['mid']=$parser->getMessageId();
|
||||
$var['email']=$from->mailbox.'@'.$from->host;
|
||||
$var['name']=$name?utf8_encode($name):$var['email'];
|
||||
$var['emailId']=$emailId?$emailId:$cfg->getDefaultEmailId();
|
||||
$var['subject']=$subj?$subj:'[No Subject]';
|
||||
$var['message']=utf8_encode(Format::stripEmptyLines($body));
|
||||
$var['header']=$parser->getHeader();
|
||||
$var['pri']=$cfg->useEmailPriority()?$parser->getPriority():0;
|
||||
|
||||
$ticket=null;
|
||||
if(preg_match ("[[#][0-9]{1,10}]",$var['subject'],$regs)) {
|
||||
$extid=trim(preg_replace("/[^0-9]/", "", $regs[0]));
|
||||
$ticket= new Ticket(Ticket::getIdByExtId($extid));
|
||||
//Allow mismatched emails?? For now hell NO.
|
||||
if(!is_object($ticket) || strcasecmp($ticket->getEmail(),$var['email']))
|
||||
$ticket=null;
|
||||
}
|
||||
$errors=array();
|
||||
$msgid=0;
|
||||
if(!$ticket){ //New tickets...
|
||||
# Apply filters against the new ticket
|
||||
$ef = new EmailFilter($var); $ef->apply($var);
|
||||
$ticket=Ticket::create($var,$errors,'email');
|
||||
if(!is_object($ticket) || $errors){
|
||||
api_exit(EX_DATAERR,'Ticket create Failed '.implode("\n",$errors)."\n\n");
|
||||
}
|
||||
$msgid=$ticket->getLastMsgId();
|
||||
}else{
|
||||
$message=$var['message'];
|
||||
//Strip quoted reply...TODO: figure out how mail clients do it without special tag..
|
||||
if($cfg->stripQuotedReply() && ($tag=$cfg->getReplySeparator()) && strpos($var['message'],$tag))
|
||||
list($message)=split($tag,$var['message']);
|
||||
//post message....postMessage does the cleanup.
|
||||
if(!($msgid=$ticket->postMessage($message,'Email',$var['mid'],$var['header']))) {
|
||||
api_exit(EX_DATAERR,"Unable to post message \n\n $message\n");
|
||||
}
|
||||
}
|
||||
//Ticket created...save attachments if enabled.
|
||||
if($cfg->allowEmailAttachments()) {
|
||||
if($attachments=$parser->getAttachments()){
|
||||
//print_r($attachments);
|
||||
foreach($attachments as $k=>$attachment){
|
||||
if($attachment['filename'] && $cfg->canUploadFileType($attachment['filename'])) {
|
||||
$ticket->saveAttachment($attachment['filename'],$attachment['body'],$msgid,'M');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
api_exit(EX_SUCCESS);
|
||||
?>
|
||||
11
api/urls.conf.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
# What about patterns("api/ticket.php:TicketController", ...) since if the
|
||||
# class is given as the prefix, it isn't defined in more than one file. This
|
||||
# would allow for speficying imports only if an item is defined in a
|
||||
# different class (with the array("class", "method") syntax)
|
||||
return patterns("api.ticket.php:TicketController",
|
||||
url_post("^/tickets\.(?P<format>xml|json)$", "create")
|
||||
);
|
||||
|
||||
?>
|
||||
29
assets/default/css/print.css
Normal file
@@ -0,0 +1,29 @@
|
||||
#header, #nav, #meta, #footer, #reply, #pagination, .reload, .refresh, form, .thread, hr, #kbAttachments, .back {
|
||||
display: none;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
caption {
|
||||
text-align: left;
|
||||
padding-bottom: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.message, .response {
|
||||
border-bottom: 1px solid #000;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.message th, .response th {
|
||||
font-size: 12pt;
|
||||
font-weight: bold;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
712
assets/default/css/theme.css
Normal file
@@ -0,0 +1,712 @@
|
||||
/* Based on Normalize.css - with tags we won't use removed. */
|
||||
html {
|
||||
font-size: 100%;
|
||||
overflow-y: scroll;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
line-height: 1.231;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body, input, select, textarea {
|
||||
font-family: sans-serif;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
b, strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 1em 40px;
|
||||
}
|
||||
|
||||
hr {
|
||||
display: block;
|
||||
height: 1px;
|
||||
border: 0;
|
||||
border-top: 1px solid #ccc;
|
||||
margin: 1em 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 85%;
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
margin: 1em 0;
|
||||
padding: 0 0 0 30px;
|
||||
}
|
||||
|
||||
img {
|
||||
border: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
form {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input, select, textarea {
|
||||
font-size: 100%;
|
||||
margin: 0;
|
||||
vertical-align: baseline;
|
||||
*vertical-align: middle;
|
||||
}
|
||||
|
||||
input {
|
||||
line-height: normal;
|
||||
*overflow: visible;
|
||||
}
|
||||
|
||||
table input {
|
||||
*overflow: auto;
|
||||
}
|
||||
|
||||
input[type="button"], input[type="reset"], input[type="submit"] {
|
||||
cursor: pointer;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
input[type="checkbox"], input[type="radio"] {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
input[type="search"] {
|
||||
-webkit-appearance: textfield;
|
||||
-moz-box-sizing: content-box;
|
||||
-webkit-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
vertical-align: top;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6, form, fieldset {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Typography */
|
||||
a {
|
||||
color: #0072bc;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #00AEEF;
|
||||
font-weight: normal;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 16px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* Helpers */
|
||||
.centered { text-align: center;}
|
||||
|
||||
.clear { clear:both; height: 1px; visibility: none;}
|
||||
|
||||
.hidden { display: none;}
|
||||
|
||||
.faded { color:#666;}
|
||||
|
||||
/* Pagination */
|
||||
#pagination {
|
||||
border: 0;
|
||||
margin: 0 0 40px 0;
|
||||
padding: 0;
|
||||
}
|
||||
#pagination li {
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 11px;
|
||||
list-style: none;
|
||||
display: inline;
|
||||
}
|
||||
#pagination a {
|
||||
margin-right: 2px;
|
||||
display: block;
|
||||
float: left;
|
||||
padding: 3px 6px;
|
||||
text-decoration: none;
|
||||
}
|
||||
#pagination a:hover {
|
||||
color: #ff0084;
|
||||
}
|
||||
#pagination .previousOff, #pagination .nextOff {
|
||||
color: #666;
|
||||
display: block;
|
||||
float: left;
|
||||
font-weight: bold;
|
||||
padding: 3px 4px;
|
||||
}
|
||||
#pagination .next a, #pagination .previous a {
|
||||
font-weight: bold;
|
||||
}
|
||||
#pagination .active {
|
||||
color: #000;
|
||||
font-weight: bold;
|
||||
margin-right: 2px;
|
||||
display: block;
|
||||
float: left;
|
||||
padding: 3px 6px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Alerts & Notices */
|
||||
|
||||
#msg_notice { margin: 0; padding: 5px 10px 5px 36px; height: 16px; line-height: 16px; margin-bottom: 10px; border: 1px solid #0a0; background: url('../images/icons/ok.png') 10px 50% no-repeat #e0ffe0; }
|
||||
|
||||
#msg_warning { margin: 0; padding: 5px 10px 5px 36px; height: 16px; line-height: 16px; margin-bottom: 10px; border: 1px solid #f26522; background: url('../images/icons/alert.png') 10px 50% no-repeat #ffffdd; }
|
||||
|
||||
#msg_error { margin: 0; padding: 5px 10px 5px 36px; height: 16px; line-height: 16px; margin-bottom: 10px; border: 1px solid #a00; background: url('../images/icons/error.png') 10px 50% no-repeat #fff0f0; }
|
||||
|
||||
|
||||
.warning {
|
||||
background: #ffc;
|
||||
font-style: italic;
|
||||
}
|
||||
.warning strong {
|
||||
text-transform: uppercase;
|
||||
color: #a00;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.error {
|
||||
color:#f00;
|
||||
}
|
||||
|
||||
.error input {
|
||||
border:1px solid #f00;}
|
||||
|
||||
/* Main layout */
|
||||
body {
|
||||
background: url('../images/page_bg.png') top left repeat-x #c8c8c8;
|
||||
}
|
||||
|
||||
#container {
|
||||
background: #fff;
|
||||
width: 840px;
|
||||
margin: 0 auto;
|
||||
box-shadow: 0 0 6px rgba(0, 0, 0, 0.5);
|
||||
-moz-box-shadow: 0 0 6px rgba(0, 0, 0, 0.5);
|
||||
-webkit-box-shadow: 0 0 6px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
#header {
|
||||
position: relative;
|
||||
height: 71px;
|
||||
padding: 0 20px;
|
||||
}
|
||||
#header #logo {
|
||||
width: 220px;
|
||||
height: 71px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#header p {
|
||||
width: 400px;
|
||||
text-align: right;
|
||||
margin: 0;
|
||||
padding: 10px 0;
|
||||
float: right;
|
||||
}
|
||||
|
||||
#nav {
|
||||
margin: 0 20px;
|
||||
padding: 2px 10px;
|
||||
height: 20px;
|
||||
background: url('../images/nav_bg.png') top left repeat-x;
|
||||
border-top: 1px solid #aaa;
|
||||
}
|
||||
#nav li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
display: inline;
|
||||
}
|
||||
#nav li a {
|
||||
display: block;
|
||||
width: auto;
|
||||
float: left;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
padding: 0 10px 0 32px;
|
||||
margin-left: 10px;
|
||||
color: #333;
|
||||
border-radius: 20px;
|
||||
-webkit-border-radius: 20px;
|
||||
-moz-border-radius: 20px;
|
||||
background-position: 10px 50%;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
#nav li a.active, #nav li a:hover {
|
||||
background-color: #dbefff;
|
||||
color: #000;
|
||||
}
|
||||
#nav li a:hover {
|
||||
background-color: #ededed;
|
||||
color: #0054a6;
|
||||
}
|
||||
#nav li a.home {
|
||||
background-image: url('../images/icons/home.png');
|
||||
}
|
||||
#nav li a.kb {
|
||||
background-image: url('../images/icons/kb.png');
|
||||
}
|
||||
#nav li a.new {
|
||||
background-image: url('../images/icons/new.png');
|
||||
}
|
||||
#nav li a.status {
|
||||
background-image: url('../images/icons/status.png');
|
||||
}
|
||||
#nav li a.tickets {
|
||||
background-image: url('../images/icons/tix.png');
|
||||
}
|
||||
|
||||
#content {
|
||||
padding: 20px 0;
|
||||
margin: 0 20px;
|
||||
background: url('../images/content_bg.png') top left repeat-x;
|
||||
}
|
||||
|
||||
#cnbg {
|
||||
padding: 5px 0;
|
||||
margin: 0 20px;
|
||||
height: auto !important;
|
||||
height: 350px;
|
||||
min-height: 350px;
|
||||
background: none;
|
||||
}
|
||||
|
||||
|
||||
#footer {
|
||||
text-align: center;
|
||||
font-size: 11px;
|
||||
color: #333;
|
||||
}
|
||||
#footer a {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
#footer p {
|
||||
margin: 10px 0 0 0;
|
||||
}
|
||||
#footer #poweredBy {
|
||||
display: block;
|
||||
width: 126px;
|
||||
height: 23px;
|
||||
outline: none;
|
||||
text-indent: -9999px;
|
||||
margin: 0 auto;
|
||||
background: url('../images/poweredby.png?1319571688') top left no-repeat;
|
||||
}
|
||||
|
||||
/* Landing page */
|
||||
#landing_page #new_ticket {
|
||||
margin-top: 40px;
|
||||
width: 295px;
|
||||
padding-left: 75px;
|
||||
float: left;
|
||||
background: url('../images/new_ticket_icon.png?1319577021') top left no-repeat;
|
||||
}
|
||||
#landing_page #new_ticket input[type=submit] {
|
||||
background: url('../images/open_ticket_btn.png?1319566422') top left no-repeat;
|
||||
}
|
||||
#landing_page #check_status {
|
||||
margin-top: 40px;
|
||||
width: 295px;
|
||||
padding-left: 75px;
|
||||
float: right;
|
||||
background: url('../images/check_status_icon.png?1319577072') top left no-repeat;
|
||||
}
|
||||
#landing_page #check_status input[type=submit] {
|
||||
background: url('../images/check_status_btn.png?1319571066') top left no-repeat;
|
||||
}
|
||||
#landing_page form div {
|
||||
margin-bottom: 20px;
|
||||
height: 60px;
|
||||
min-height: 60px;
|
||||
}
|
||||
#landing_page form input[type=submit] {
|
||||
display: block;
|
||||
width: 192px;
|
||||
height: 38px;
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-indent: -9999px;
|
||||
}
|
||||
|
||||
#faq {
|
||||
clear: both;
|
||||
margin: 0;
|
||||
padding: 5px;
|
||||
}
|
||||
#faq ol {
|
||||
font-size: 15px;
|
||||
margin-left: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
#faq ol li {
|
||||
list-style: none;
|
||||
margin: 0 0 10px 0;
|
||||
color: #999;
|
||||
}
|
||||
#faq ol li a {
|
||||
display: block;
|
||||
height: 16px;
|
||||
line-height: 16px;
|
||||
padding-left: 24px;
|
||||
background: url('../images/icons/page.png?1319579499') 0 50% no-repeat;
|
||||
}
|
||||
|
||||
/* Knowledgebase */
|
||||
#kb {
|
||||
margin: 2px 0;
|
||||
padding: 5px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#kb > li {
|
||||
margin: 0 0 5px 0;
|
||||
padding: 10px;
|
||||
width: auto;
|
||||
float: left;
|
||||
clear: both;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
#kb > li h4 {
|
||||
padding-bottom:3px;
|
||||
margin-bottom:3px;
|
||||
}
|
||||
|
||||
#kb > li h4 span {
|
||||
color:#666;
|
||||
}
|
||||
|
||||
#kb > li h4 a {
|
||||
font-size: 14px;
|
||||
padding-left: 24px;
|
||||
background: url('../images/icons/page.png?1319579499') 0 50% no-repeat;
|
||||
}
|
||||
|
||||
|
||||
#breadcrumbs {
|
||||
color: #333;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
#breadcrumbs a {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
/* New Ticket & Log In Forms */
|
||||
#ticketForm div, #clientLogin div {
|
||||
clear: both;
|
||||
padding: 3px 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
#ticketForm div label, #clientLogin div label {
|
||||
display: block;
|
||||
width: 140px;
|
||||
float: left;
|
||||
}
|
||||
#ticketForm div input, #ticketForm div textarea, #clientLogin div input, #clientLogin div textarea {
|
||||
width: auto;
|
||||
border: 1px solid #aaa;
|
||||
background: #fff;
|
||||
margin-right: 10px;
|
||||
display: block;
|
||||
float: left;
|
||||
}
|
||||
#ticketForm div select, #clientLogin div select {
|
||||
display: block;
|
||||
float: left;
|
||||
}
|
||||
#ticketForm div textarea, #clientLogin div textarea {
|
||||
width: 600px;
|
||||
}
|
||||
#ticketForm div em, #clientLogin div em {
|
||||
color: #777;
|
||||
}
|
||||
#ticketForm div .captcha, #clientLogin div .captcha {
|
||||
width: 88px;
|
||||
height: 31px;
|
||||
background: #000;
|
||||
display: block;
|
||||
float: left;
|
||||
margin-right: 20px;
|
||||
}
|
||||
#ticketForm div label.inline, #clientLogin div label.inline {
|
||||
width: auto;
|
||||
padding: 0 10px;
|
||||
}
|
||||
#ticketForm div label.required, #clientLogin div label.required {
|
||||
font-weight: bold;
|
||||
}
|
||||
#ticketForm div.captchaRow, #clientLogin div.captchaRow {
|
||||
line-height: 31px;
|
||||
}
|
||||
#ticketForm div.captchaRow input, #clientLogin div.captchaRow input {
|
||||
position: relative;
|
||||
top: 6px;
|
||||
}
|
||||
#ticketForm div.error input, #clientLogin div.error input {
|
||||
border: 1px solid #a00;
|
||||
}
|
||||
#ticketForm div.error label, #clientLogin div.error label {
|
||||
color: #a00;
|
||||
}
|
||||
#ticketForm p, #clientLogin p {
|
||||
clear: both;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#clientLogin {
|
||||
width: 400px;
|
||||
margin-top: 20px;
|
||||
padding: 10px 100px 10px 10px;
|
||||
border: 1px solid #ccc;
|
||||
background: url('../images/lock.png?1319655200') 440px 50% no-repeat #f6f6f6;
|
||||
}
|
||||
#clientLogin strong {
|
||||
font-size: 11px;
|
||||
color: #d00;
|
||||
display: block;
|
||||
padding-left: 140px;
|
||||
}
|
||||
#clientLogin #email {
|
||||
width: 250px;
|
||||
margin-right: 0;
|
||||
}
|
||||
#clientLogin #ticketno {
|
||||
width: 120px;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
/* Ticket List */
|
||||
.Icon {
|
||||
width: auto;
|
||||
padding-left: 20px;
|
||||
background-position: left center;
|
||||
background-repeat: no-repeat;
|
||||
color: #006699;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a.Icon:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.Icon.Ticket {
|
||||
background: url('../images/icons/ticket.gif?1319654018') 0 0 no-repeat;
|
||||
}
|
||||
|
||||
.Icon.webTicket {
|
||||
background: url('../images/icons/ticket_source_web.gif?1319654283') 0 0 no-repeat;
|
||||
}
|
||||
|
||||
.Icon.emailTicket {
|
||||
background: url('../images/icons/ticket_source_email.gif?1319654484') 0 0 no-repeat;
|
||||
}
|
||||
|
||||
.Icon.phoneTicket {
|
||||
background: url('../images/icons/ticket_source_phone.gif?1319654401') 0 0 no-repeat;
|
||||
}
|
||||
|
||||
.Icon.otherTicket {
|
||||
background: url('../images/icons/ticket_source_other.gif?1319654433') 0 0 no-repeat;
|
||||
}
|
||||
|
||||
.Icon.attachment {
|
||||
background-image: url('../images/icons/attachment.gif?1319556657');
|
||||
}
|
||||
|
||||
.Icon.file {
|
||||
background-image: url('../images/icons/attachment.gif?1319556657');
|
||||
}
|
||||
|
||||
.Icon.refresh {
|
||||
background-image: url('../images/icons/refresh.gif?1319556657');
|
||||
}
|
||||
|
||||
.Icon.thread {
|
||||
font-weight: bold;
|
||||
font-size: 1em;
|
||||
background-image: url('../images/icons/thread.gif?1319556657');
|
||||
}
|
||||
|
||||
#ticketTable {
|
||||
border: 1px solid #aaa;
|
||||
border-left: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
#ticketTable caption {
|
||||
padding: 5px;
|
||||
text-align: left;
|
||||
color: #000;
|
||||
background: #ddd;
|
||||
border: 1px solid #aaa;
|
||||
border-bottom: none;
|
||||
font-weight: bold;
|
||||
}
|
||||
#ticketTable th {
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
background: #e1f2ff;
|
||||
border: 1px solid #aaa;
|
||||
border-right: none;
|
||||
border-top: none;
|
||||
}
|
||||
#ticketTable th a {
|
||||
color: #000;
|
||||
}
|
||||
#ticketTable td {
|
||||
padding: 2px;
|
||||
border: 1px solid #aaa;
|
||||
border-right: none;
|
||||
border-top: none;
|
||||
}
|
||||
#ticketTable tr.alt td {
|
||||
background: #f9f9f9;
|
||||
}
|
||||
|
||||
#ticketSearchForm {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
padding: 0 0 5px 0;
|
||||
}
|
||||
|
||||
a.refresh {
|
||||
display: block;
|
||||
width: auto;
|
||||
float: right;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
padding: 0 10px 0 28px;
|
||||
border: 1px solid #aaa;
|
||||
margin-left: 10px;
|
||||
color: #333;
|
||||
background-position: 5px 50%;
|
||||
background-repeat: no-repeat;
|
||||
background-image: url('../images/icons/refresh.png?1319653435');
|
||||
}
|
||||
|
||||
.infoTable {
|
||||
background: #F4FAFF;
|
||||
}
|
||||
.infoTable th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#ticketThread table {
|
||||
margin-top: 10px;
|
||||
border: 1px solid #aaa;
|
||||
border-bottom: 2px solid #aaa;
|
||||
}
|
||||
#ticketThread table th {
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #aaa;
|
||||
font-size: 11pt;
|
||||
padding: 5px;
|
||||
}
|
||||
#ticketThread table td {
|
||||
padding: 5px;
|
||||
}
|
||||
#ticketThread .message th {
|
||||
background: #d8efff;
|
||||
}
|
||||
#ticketThread .response th {
|
||||
background: #ddd;
|
||||
}
|
||||
#ticketThread .info {
|
||||
padding: 5px;
|
||||
background: #f9f9f9;
|
||||
border-top: 1px solid #ddd;
|
||||
height: 16px;
|
||||
line-height: 16px;
|
||||
}
|
||||
#ticketThread .info a {
|
||||
display: inline-block;
|
||||
margin: 5px 20px 5px 0;
|
||||
padding-left: 24px;
|
||||
height: 16px;
|
||||
line-height: 16px;
|
||||
background-position: 0 50%;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
#ticketThread .info .pdf {
|
||||
background-image: url('../images/filetypes/pdf.png?1319636863');
|
||||
}
|
||||
|
||||
#reply {
|
||||
margin-top: 20px;
|
||||
padding: 10px 5px;
|
||||
background: #f9f9f9;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
#reply h2 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
#reply table {
|
||||
width: 800px;
|
||||
}
|
||||
#reply table td {
|
||||
vertical-align: top;
|
||||
}
|
||||
#reply textarea {
|
||||
width: 628px !important;
|
||||
}
|
||||
#reply input[type=text], #reply #response_options textarea {
|
||||
border: 1px solid #aaa;
|
||||
background: #fff;
|
||||
}
|
||||
#reply .attachments .uploads div {
|
||||
display: inline-block;
|
||||
padding-right: 20px;
|
||||
}
|
||||
#reply .file {
|
||||
display: inline-block;
|
||||
padding-left: 20px;
|
||||
margin-right: 20px;
|
||||
background: url('../images/icons/file.gif') 0 50% no-repeat;
|
||||
}
|
||||
BIN
assets/default/images/check_status_btn.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
BIN
assets/default/images/check_status_icon.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
assets/default/images/content_bg.png
Normal file
|
After Width: | Height: | Size: 962 B |
BIN
assets/default/images/filetypes/pdf.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/default/images/icons/alert.png
Executable file
|
After Width: | Height: | Size: 680 B |
BIN
assets/default/images/icons/attachment.gif
Normal file
|
After Width: | Height: | Size: 235 B |
BIN
assets/default/images/icons/error.png
Executable file
|
After Width: | Height: | Size: 730 B |
BIN
assets/default/images/icons/home.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/default/images/icons/kb.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/default/images/icons/lock.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/default/images/icons/new.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/default/images/icons/ok.png
Executable file
|
After Width: | Height: | Size: 812 B |
BIN
assets/default/images/icons/page.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/default/images/icons/refresh.gif
Normal file
|
After Width: | Height: | Size: 248 B |
BIN
assets/default/images/icons/refresh.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/default/images/icons/status.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/default/images/icons/thread.gif
Normal file
|
After Width: | Height: | Size: 274 B |
BIN
assets/default/images/icons/ticket.gif
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/default/images/icons/ticket_source_email.gif
Normal file
|
After Width: | Height: | Size: 658 B |
BIN
assets/default/images/icons/ticket_source_other.gif
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/default/images/icons/ticket_source_phone.gif
Normal file
|
After Width: | Height: | Size: 603 B |
BIN
assets/default/images/icons/ticket_source_web.gif
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/default/images/icons/tix.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/default/images/icons/tix_closed.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/default/images/lock.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
assets/default/images/logo.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
assets/default/images/nav_bg.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
assets/default/images/new_ticket_icon.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
assets/default/images/open_ticket_btn.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
assets/default/images/page_bg.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/default/images/poweredby.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
assets/default/images/support.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
65
client.inc.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/*********************************************************************
|
||||
client.inc.php
|
||||
|
||||
File included on every client page
|
||||
|
||||
Peter Rotich <peter@osticket.com>
|
||||
Copyright (c) 2006-2012 osTicket
|
||||
http://www.osticket.com
|
||||
|
||||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||||
See LICENSE.TXT for details.
|
||||
|
||||
vim: expandtab sw=4 ts=4 sts=4:
|
||||
**********************************************************************/
|
||||
if(!strcasecmp(basename($_SERVER['SCRIPT_NAME']),basename(__FILE__))) die('kwaheri rafiki!');
|
||||
|
||||
$thisdir=str_replace('\\\\', '/', realpath(dirname(__FILE__))).'/';
|
||||
if(!file_exists($thisdir.'main.inc.php')) die('Fatal Error.');
|
||||
|
||||
require_once($thisdir.'main.inc.php');
|
||||
|
||||
if(!defined('INCLUDE_DIR')) die('Fatal error');
|
||||
|
||||
/*Some more include defines specific to client only */
|
||||
define('CLIENTINC_DIR',INCLUDE_DIR.'client/');
|
||||
define('OSTCLIENTINC',TRUE);
|
||||
|
||||
define('ASSETS_PATH',ROOT_PATH.'assets/default/');
|
||||
|
||||
|
||||
//Check the status of the HelpDesk.
|
||||
if(!is_object($cfg) || !$cfg->getId() || $cfg->isHelpDeskOffline()) {
|
||||
include('./offline.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
//Forced upgrade? Version mismatch.
|
||||
if(defined('THIS_VERSION') && strcasecmp($cfg->getVersion(),THIS_VERSION)) {
|
||||
die('System is offline for an upgrade.');
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* include what is needed on client stuff */
|
||||
require_once(INCLUDE_DIR.'class.client.php');
|
||||
require_once(INCLUDE_DIR.'class.ticket.php');
|
||||
require_once(INCLUDE_DIR.'class.dept.php');
|
||||
|
||||
//clear some vars
|
||||
$errors=array();
|
||||
$msg='';
|
||||
$thisclient=null;
|
||||
//Make sure the user is valid..before doing anything else.
|
||||
if($_SESSION['_client']['userID'] && $_SESSION['_client']['key'])
|
||||
$thisclient = new ClientSession($_SESSION['_client']['userID'],$_SESSION['_client']['key']);
|
||||
|
||||
//is the user logged in?
|
||||
if($thisclient && $thisclient->getId() && $thisclient->isValid()){
|
||||
$thisclient->refreshSession();
|
||||
}
|
||||
|
||||
$nav = new UserNav($thisclient, 'home');
|
||||
?>
|
||||
BIN
images/bg.gif
Normal file
|
After Width: | Height: | Size: 49 B |
BIN
images/cal.gif
Normal file
|
After Width: | Height: | Size: 127 B |
BIN
images/captcha/bubbles.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
images/captcha/cottoncandy.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
images/captcha/crackle.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
images/captcha/grass.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
images/captcha/lines.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
images/captcha/ripple.png
Normal file
|
After Width: | Height: | Size: 921 B |
BIN
images/captcha/sand.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
images/captcha/silk.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
images/captcha/snakeskin.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
images/captcha/whirlpool.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
images/fibres.png
Normal file
|
After Width: | Height: | Size: 8.3 KiB |
BIN
images/home.gif
Normal file
|
After Width: | Height: | Size: 221 B |
BIN
images/icons/attachment.gif
Normal file
|
After Width: | Height: | Size: 235 B |
BIN
images/icons/refresh.gif
Normal file
|
After Width: | Height: | Size: 248 B |
BIN
images/icons/thread.gif
Normal file
|
After Width: | Height: | Size: 274 B |
BIN
images/icons/ticket.gif
Normal file
|
After Width: | Height: | Size: 210 B |
BIN
images/icons/ticket_source_email.gif
Normal file
|
After Width: | Height: | Size: 230 B |
BIN
images/icons/ticket_source_other.gif
Normal file
|
After Width: | Height: | Size: 210 B |
BIN
images/icons/ticket_source_phone.gif
Normal file
|
After Width: | Height: | Size: 228 B |
BIN
images/icons/ticket_source_web.gif
Normal file
|
After Width: | Height: | Size: 226 B |
BIN
images/lipsum.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
images/logo.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
images/logo2.jpg
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
images/logout.gif
Normal file
|
After Width: | Height: | Size: 338 B |
BIN
images/my_tickets.gif
Normal file
|
After Width: | Height: | Size: 126 B |
BIN
images/new_ticket.gif
Normal file
|
After Width: | Height: | Size: 120 B |
BIN
images/new_ticket_icon.jpg
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
images/poweredby.jpg
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
images/rainbow.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
images/refresh_btn.gif
Normal file
|
After Width: | Height: | Size: 929 B |
BIN
images/ticket_status.gif
Normal file
|
After Width: | Height: | Size: 123 B |
BIN
images/ticket_status_icon.jpg
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
images/verticalbar.jpg
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
images/view_closed_btn.gif
Normal file
|
After Width: | Height: | Size: 618 B |
BIN
images/view_open_btn.gif
Normal file
|
After Width: | Height: | Size: 578 B |
805
include/JSON.php
Normal file
@@ -0,0 +1,805 @@
|
||||
<?php
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* Converts to and from JSON format.
|
||||
*
|
||||
* JSON (JavaScript Object Notation) is a lightweight data-interchange
|
||||
* format. It is easy for humans to read and write. It is easy for machines
|
||||
* to parse and generate. It is based on a subset of the JavaScript
|
||||
* Programming Language, Standard ECMA-262 3rd Edition - December 1999.
|
||||
* This feature can also be found in Python. JSON is a text format that is
|
||||
* completely language independent but uses conventions that are familiar
|
||||
* to programmers of the C-family of languages, including C, C++, C#, Java,
|
||||
* JavaScript, Perl, TCL, and many others. These properties make JSON an
|
||||
* ideal data-interchange language.
|
||||
*
|
||||
* This package provides a simple encoder and decoder for JSON notation. It
|
||||
* is intended for use with client-side Javascript applications that make
|
||||
* use of HTTPRequest to perform server communication functions - data can
|
||||
* be encoded into JSON notation for use in a client-side javascript, or
|
||||
* decoded from incoming Javascript requests. JSON format is native to
|
||||
* Javascript, and can be directly eval()'ed with no further parsing
|
||||
* overhead
|
||||
*
|
||||
* All strings should be in ASCII or UTF-8 format!
|
||||
*
|
||||
* LICENSE: Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met: Redistributions of source code must retain the
|
||||
* above copyright notice, this list of conditions and the following
|
||||
* disclaimer. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||
* NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* @category
|
||||
* @package Services_JSON
|
||||
* @author Michal Migurski <mike-json@teczno.com>
|
||||
* @author Matt Knapp <mdknapp[at]gmail[dot]com>
|
||||
* @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
|
||||
* @copyright 2005 Michal Migurski
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php
|
||||
* @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
|
||||
*/
|
||||
|
||||
/**
|
||||
* Marker constant for Services_JSON::decode(), used to flag stack state
|
||||
*/
|
||||
define('SERVICES_JSON_SLICE', 1);
|
||||
|
||||
/**
|
||||
* Marker constant for Services_JSON::decode(), used to flag stack state
|
||||
*/
|
||||
define('SERVICES_JSON_IN_STR', 2);
|
||||
|
||||
/**
|
||||
* Marker constant for Services_JSON::decode(), used to flag stack state
|
||||
*/
|
||||
define('SERVICES_JSON_IN_ARR', 3);
|
||||
|
||||
/**
|
||||
* Marker constant for Services_JSON::decode(), used to flag stack state
|
||||
*/
|
||||
define('SERVICES_JSON_IN_OBJ', 4);
|
||||
|
||||
/**
|
||||
* Marker constant for Services_JSON::decode(), used to flag stack state
|
||||
*/
|
||||
define('SERVICES_JSON_IN_CMT', 5);
|
||||
|
||||
/**
|
||||
* Behavior switch for Services_JSON::decode()
|
||||
*/
|
||||
define('SERVICES_JSON_LOOSE_TYPE', 16);
|
||||
|
||||
/**
|
||||
* Behavior switch for Services_JSON::decode()
|
||||
*/
|
||||
define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
|
||||
|
||||
/**
|
||||
* Converts to and from JSON format.
|
||||
*
|
||||
* Brief example of use:
|
||||
*
|
||||
* <code>
|
||||
* // create a new instance of Services_JSON
|
||||
* $json = new Services_JSON();
|
||||
*
|
||||
* // convert a complexe value to JSON notation, and send it to the browser
|
||||
* $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
|
||||
* $output = $json->encode($value);
|
||||
*
|
||||
* print($output);
|
||||
* // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
|
||||
*
|
||||
* // accept incoming POST data, assumed to be in JSON notation
|
||||
* $input = file_get_contents('php://input', 1000000);
|
||||
* $value = $json->decode($input);
|
||||
* </code>
|
||||
*/
|
||||
class Services_JSON
|
||||
{
|
||||
/**
|
||||
* constructs a new JSON instance
|
||||
*
|
||||
* @param int $use object behavior flags; combine with boolean-OR
|
||||
*
|
||||
* possible values:
|
||||
* - SERVICES_JSON_LOOSE_TYPE: loose typing.
|
||||
* "{...}" syntax creates associative arrays
|
||||
* instead of objects in decode().
|
||||
* - SERVICES_JSON_SUPPRESS_ERRORS: error suppression.
|
||||
* Values which can't be encoded (e.g. resources)
|
||||
* appear as NULL instead of throwing errors.
|
||||
* By default, a deeply-nested resource will
|
||||
* bubble up with an error, so all return values
|
||||
* from encode() should be checked with isError()
|
||||
*/
|
||||
function Services_JSON($use = 0)
|
||||
{
|
||||
$this->use = $use;
|
||||
}
|
||||
|
||||
/**
|
||||
* convert a string from one UTF-16 char to one UTF-8 char
|
||||
*
|
||||
* Normally should be handled by mb_convert_encoding, but
|
||||
* provides a slower PHP-only method for installations
|
||||
* that lack the multibye string extension.
|
||||
*
|
||||
* @param string $utf16 UTF-16 character
|
||||
* @return string UTF-8 character
|
||||
* @access private
|
||||
*/
|
||||
function utf162utf8($utf16)
|
||||
{
|
||||
// oh please oh please oh please oh please oh please
|
||||
if(function_exists('mb_convert_encoding')) {
|
||||
return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
|
||||
}
|
||||
|
||||
$bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
|
||||
|
||||
switch(true) {
|
||||
case ((0x7F & $bytes) == $bytes):
|
||||
// this case should never be reached, because we are in ASCII range
|
||||
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
return chr(0x7F & $bytes);
|
||||
|
||||
case (0x07FF & $bytes) == $bytes:
|
||||
// return a 2-byte UTF-8 character
|
||||
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
return chr(0xC0 | (($bytes >> 6) & 0x1F))
|
||||
. chr(0x80 | ($bytes & 0x3F));
|
||||
|
||||
case (0xFFFF & $bytes) == $bytes:
|
||||
// return a 3-byte UTF-8 character
|
||||
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
return chr(0xE0 | (($bytes >> 12) & 0x0F))
|
||||
. chr(0x80 | (($bytes >> 6) & 0x3F))
|
||||
. chr(0x80 | ($bytes & 0x3F));
|
||||
}
|
||||
|
||||
// ignoring UTF-32 for now, sorry
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* convert a string from one UTF-8 char to one UTF-16 char
|
||||
*
|
||||
* Normally should be handled by mb_convert_encoding, but
|
||||
* provides a slower PHP-only method for installations
|
||||
* that lack the multibye string extension.
|
||||
*
|
||||
* @param string $utf8 UTF-8 character
|
||||
* @return string UTF-16 character
|
||||
* @access private
|
||||
*/
|
||||
function utf82utf16($utf8)
|
||||
{
|
||||
// oh please oh please oh please oh please oh please
|
||||
if(function_exists('mb_convert_encoding')) {
|
||||
return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
|
||||
}
|
||||
|
||||
switch(strlen($utf8)) {
|
||||
case 1:
|
||||
// this case should never be reached, because we are in ASCII range
|
||||
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
return $utf8;
|
||||
|
||||
case 2:
|
||||
// return a UTF-16 character from a 2-byte UTF-8 char
|
||||
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
return chr(0x07 & (ord($utf8{0}) >> 2))
|
||||
. chr((0xC0 & (ord($utf8{0}) << 6))
|
||||
| (0x3F & ord($utf8{1})));
|
||||
|
||||
case 3:
|
||||
// return a UTF-16 character from a 3-byte UTF-8 char
|
||||
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
return chr((0xF0 & (ord($utf8{0}) << 4))
|
||||
| (0x0F & (ord($utf8{1}) >> 2)))
|
||||
. chr((0xC0 & (ord($utf8{1}) << 6))
|
||||
| (0x7F & ord($utf8{2})));
|
||||
}
|
||||
|
||||
// ignoring UTF-32 for now, sorry
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* encodes an arbitrary variable into JSON format
|
||||
*
|
||||
* @param mixed $var any number, boolean, string, array, or object to be encoded.
|
||||
* see argument 1 to Services_JSON() above for array-parsing behavior.
|
||||
* if var is a strng, note that encode() always expects it
|
||||
* to be in ASCII or UTF-8 format!
|
||||
*
|
||||
* @return mixed JSON string representation of input var or an error if a problem occurs
|
||||
* @access public
|
||||
*/
|
||||
function encode($var)
|
||||
{
|
||||
switch (gettype($var)) {
|
||||
case 'boolean':
|
||||
return $var ? 'true' : 'false';
|
||||
|
||||
case 'NULL':
|
||||
return 'null';
|
||||
|
||||
case 'integer':
|
||||
return (int) $var;
|
||||
|
||||
case 'double':
|
||||
case 'float':
|
||||
return (float) $var;
|
||||
|
||||
case 'string':
|
||||
// STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
|
||||
$ascii = '';
|
||||
$strlen_var = strlen($var);
|
||||
|
||||
/*
|
||||
* Iterate over every character in the string,
|
||||
* escaping with a slash or encoding to UTF-8 where necessary
|
||||
*/
|
||||
for ($c = 0; $c < $strlen_var; ++$c) {
|
||||
|
||||
$ord_var_c = ord($var{$c});
|
||||
|
||||
switch (true) {
|
||||
case $ord_var_c == 0x08:
|
||||
$ascii .= '\b';
|
||||
break;
|
||||
case $ord_var_c == 0x09:
|
||||
$ascii .= '\t';
|
||||
break;
|
||||
case $ord_var_c == 0x0A:
|
||||
$ascii .= '\n';
|
||||
break;
|
||||
case $ord_var_c == 0x0C:
|
||||
$ascii .= '\f';
|
||||
break;
|
||||
case $ord_var_c == 0x0D:
|
||||
$ascii .= '\r';
|
||||
break;
|
||||
|
||||
case $ord_var_c == 0x22:
|
||||
case $ord_var_c == 0x2F:
|
||||
case $ord_var_c == 0x5C:
|
||||
// double quote, slash, slosh
|
||||
$ascii .= '\\'.$var{$c};
|
||||
break;
|
||||
|
||||
case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
|
||||
// characters U-00000000 - U-0000007F (same as ASCII)
|
||||
$ascii .= $var{$c};
|
||||
break;
|
||||
|
||||
case (($ord_var_c & 0xE0) == 0xC0):
|
||||
// characters U-00000080 - U-000007FF, mask 110XXXXX
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$char = pack('C*', $ord_var_c, ord($var{$c + 1}));
|
||||
$c += 1;
|
||||
$utf16 = $this->utf82utf16($char);
|
||||
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
||||
break;
|
||||
|
||||
case (($ord_var_c & 0xF0) == 0xE0):
|
||||
// characters U-00000800 - U-0000FFFF, mask 1110XXXX
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$char = pack('C*', $ord_var_c,
|
||||
ord($var{$c + 1}),
|
||||
ord($var{$c + 2}));
|
||||
$c += 2;
|
||||
$utf16 = $this->utf82utf16($char);
|
||||
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
||||
break;
|
||||
|
||||
case (($ord_var_c & 0xF8) == 0xF0):
|
||||
// characters U-00010000 - U-001FFFFF, mask 11110XXX
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$char = pack('C*', $ord_var_c,
|
||||
ord($var{$c + 1}),
|
||||
ord($var{$c + 2}),
|
||||
ord($var{$c + 3}));
|
||||
$c += 3;
|
||||
$utf16 = $this->utf82utf16($char);
|
||||
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
||||
break;
|
||||
|
||||
case (($ord_var_c & 0xFC) == 0xF8):
|
||||
// characters U-00200000 - U-03FFFFFF, mask 111110XX
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$char = pack('C*', $ord_var_c,
|
||||
ord($var{$c + 1}),
|
||||
ord($var{$c + 2}),
|
||||
ord($var{$c + 3}),
|
||||
ord($var{$c + 4}));
|
||||
$c += 4;
|
||||
$utf16 = $this->utf82utf16($char);
|
||||
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
||||
break;
|
||||
|
||||
case (($ord_var_c & 0xFE) == 0xFC):
|
||||
// characters U-04000000 - U-7FFFFFFF, mask 1111110X
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$char = pack('C*', $ord_var_c,
|
||||
ord($var{$c + 1}),
|
||||
ord($var{$c + 2}),
|
||||
ord($var{$c + 3}),
|
||||
ord($var{$c + 4}),
|
||||
ord($var{$c + 5}));
|
||||
$c += 5;
|
||||
$utf16 = $this->utf82utf16($char);
|
||||
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return '"'.$ascii.'"';
|
||||
|
||||
case 'array':
|
||||
/*
|
||||
* As per JSON spec if any array key is not an integer
|
||||
* we must treat the the whole array as an object. We
|
||||
* also try to catch a sparsely populated associative
|
||||
* array with numeric keys here because some JS engines
|
||||
* will create an array with empty indexes up to
|
||||
* max_index which can cause memory issues and because
|
||||
* the keys, which may be relevant, will be remapped
|
||||
* otherwise.
|
||||
*
|
||||
* As per the ECMA and JSON specification an object may
|
||||
* have any string as a property. Unfortunately due to
|
||||
* a hole in the ECMA specification if the key is a
|
||||
* ECMA reserved word or starts with a digit the
|
||||
* parameter is only accessible using ECMAScript's
|
||||
* bracket notation.
|
||||
*/
|
||||
|
||||
// treat as a JSON object
|
||||
if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
|
||||
$properties = array_map(array($this, 'name_value'),
|
||||
array_keys($var),
|
||||
array_values($var));
|
||||
|
||||
foreach($properties as $property) {
|
||||
if(Services_JSON::isError($property)) {
|
||||
return $property;
|
||||
}
|
||||
}
|
||||
|
||||
return '{' . join(',', $properties) . '}';
|
||||
}
|
||||
|
||||
// treat it like a regular array
|
||||
$elements = array_map(array($this, 'encode'), $var);
|
||||
|
||||
foreach($elements as $element) {
|
||||
if(Services_JSON::isError($element)) {
|
||||
return $element;
|
||||
}
|
||||
}
|
||||
|
||||
return '[' . join(',', $elements) . ']';
|
||||
|
||||
case 'object':
|
||||
$vars = get_object_vars($var);
|
||||
|
||||
$properties = array_map(array($this, 'name_value'),
|
||||
array_keys($vars),
|
||||
array_values($vars));
|
||||
|
||||
foreach($properties as $property) {
|
||||
if(Services_JSON::isError($property)) {
|
||||
return $property;
|
||||
}
|
||||
}
|
||||
|
||||
return '{' . join(',', $properties) . '}';
|
||||
|
||||
default:
|
||||
return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
|
||||
? 'null'
|
||||
: new Services_JSON_Error(gettype($var)." can not be encoded as JSON string");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* array-walking function for use in generating JSON-formatted name-value pairs
|
||||
*
|
||||
* @param string $name name of key to use
|
||||
* @param mixed $value reference to an array element to be encoded
|
||||
*
|
||||
* @return string JSON-formatted name-value pair, like '"name":value'
|
||||
* @access private
|
||||
*/
|
||||
function name_value($name, $value)
|
||||
{
|
||||
$encoded_value = $this->encode($value);
|
||||
|
||||
if(Services_JSON::isError($encoded_value)) {
|
||||
return $encoded_value;
|
||||
}
|
||||
|
||||
return $this->encode(strval($name)) . ':' . $encoded_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* reduce a string by removing leading and trailing comments and whitespace
|
||||
*
|
||||
* @param $str string string value to strip of comments and whitespace
|
||||
*
|
||||
* @return string string value stripped of comments and whitespace
|
||||
* @access private
|
||||
*/
|
||||
function reduce_string($str)
|
||||
{
|
||||
$str = preg_replace(array(
|
||||
|
||||
// eliminate single line comments in '// ...' form
|
||||
'#^\s*//(.+)$#m',
|
||||
|
||||
// eliminate multi-line comments in '/* ... */' form, at start of string
|
||||
'#^\s*/\*(.+)\*/#Us',
|
||||
|
||||
// eliminate multi-line comments in '/* ... */' form, at end of string
|
||||
'#/\*(.+)\*/\s*$#Us'
|
||||
|
||||
), '', $str);
|
||||
|
||||
// eliminate extraneous space
|
||||
return trim($str);
|
||||
}
|
||||
|
||||
/**
|
||||
* decodes a JSON string into appropriate variable
|
||||
*
|
||||
* @param string $str JSON-formatted string
|
||||
*
|
||||
* @return mixed number, boolean, string, array, or object
|
||||
* corresponding to given JSON input string.
|
||||
* See argument 1 to Services_JSON() above for object-output behavior.
|
||||
* Note that decode() always returns strings
|
||||
* in ASCII or UTF-8 format!
|
||||
* @access public
|
||||
*/
|
||||
function decode($str)
|
||||
{
|
||||
$str = $this->reduce_string($str);
|
||||
|
||||
switch (strtolower($str)) {
|
||||
case 'true':
|
||||
return true;
|
||||
|
||||
case 'false':
|
||||
return false;
|
||||
|
||||
case 'null':
|
||||
return null;
|
||||
|
||||
default:
|
||||
$m = array();
|
||||
|
||||
if (is_numeric($str)) {
|
||||
// Lookie-loo, it's a number
|
||||
|
||||
// This would work on its own, but I'm trying to be
|
||||
// good about returning integers where appropriate:
|
||||
// return (float)$str;
|
||||
|
||||
// Return float or int, as appropriate
|
||||
return ((float)$str == (integer)$str)
|
||||
? (integer)$str
|
||||
: (float)$str;
|
||||
|
||||
} elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
|
||||
// STRINGS RETURNED IN UTF-8 FORMAT
|
||||
$delim = substr($str, 0, 1);
|
||||
$chrs = substr($str, 1, -1);
|
||||
$utf8 = '';
|
||||
$strlen_chrs = strlen($chrs);
|
||||
|
||||
for ($c = 0; $c < $strlen_chrs; ++$c) {
|
||||
|
||||
$substr_chrs_c_2 = substr($chrs, $c, 2);
|
||||
$ord_chrs_c = ord($chrs{$c});
|
||||
|
||||
switch (true) {
|
||||
case $substr_chrs_c_2 == '\b':
|
||||
$utf8 .= chr(0x08);
|
||||
++$c;
|
||||
break;
|
||||
case $substr_chrs_c_2 == '\t':
|
||||
$utf8 .= chr(0x09);
|
||||
++$c;
|
||||
break;
|
||||
case $substr_chrs_c_2 == '\n':
|
||||
$utf8 .= chr(0x0A);
|
||||
++$c;
|
||||
break;
|
||||
case $substr_chrs_c_2 == '\f':
|
||||
$utf8 .= chr(0x0C);
|
||||
++$c;
|
||||
break;
|
||||
case $substr_chrs_c_2 == '\r':
|
||||
$utf8 .= chr(0x0D);
|
||||
++$c;
|
||||
break;
|
||||
|
||||
case $substr_chrs_c_2 == '\\"':
|
||||
case $substr_chrs_c_2 == '\\\'':
|
||||
case $substr_chrs_c_2 == '\\\\':
|
||||
case $substr_chrs_c_2 == '\\/':
|
||||
if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
|
||||
($delim == "'" && $substr_chrs_c_2 != '\\"')) {
|
||||
$utf8 .= $chrs{++$c};
|
||||
}
|
||||
break;
|
||||
|
||||
case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
|
||||
// single, escaped unicode character
|
||||
$utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
|
||||
. chr(hexdec(substr($chrs, ($c + 4), 2)));
|
||||
$utf8 .= $this->utf162utf8($utf16);
|
||||
$c += 5;
|
||||
break;
|
||||
|
||||
case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
|
||||
$utf8 .= $chrs{$c};
|
||||
break;
|
||||
|
||||
case ($ord_chrs_c & 0xE0) == 0xC0:
|
||||
// characters U-00000080 - U-000007FF, mask 110XXXXX
|
||||
//see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$utf8 .= substr($chrs, $c, 2);
|
||||
++$c;
|
||||
break;
|
||||
|
||||
case ($ord_chrs_c & 0xF0) == 0xE0:
|
||||
// characters U-00000800 - U-0000FFFF, mask 1110XXXX
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$utf8 .= substr($chrs, $c, 3);
|
||||
$c += 2;
|
||||
break;
|
||||
|
||||
case ($ord_chrs_c & 0xF8) == 0xF0:
|
||||
// characters U-00010000 - U-001FFFFF, mask 11110XXX
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$utf8 .= substr($chrs, $c, 4);
|
||||
$c += 3;
|
||||
break;
|
||||
|
||||
case ($ord_chrs_c & 0xFC) == 0xF8:
|
||||
// characters U-00200000 - U-03FFFFFF, mask 111110XX
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$utf8 .= substr($chrs, $c, 5);
|
||||
$c += 4;
|
||||
break;
|
||||
|
||||
case ($ord_chrs_c & 0xFE) == 0xFC:
|
||||
// characters U-04000000 - U-7FFFFFFF, mask 1111110X
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$utf8 .= substr($chrs, $c, 6);
|
||||
$c += 5;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $utf8;
|
||||
|
||||
} elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
|
||||
// array, or object notation
|
||||
|
||||
if ($str{0} == '[') {
|
||||
$stk = array(SERVICES_JSON_IN_ARR);
|
||||
$arr = array();
|
||||
} else {
|
||||
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
|
||||
$stk = array(SERVICES_JSON_IN_OBJ);
|
||||
$obj = array();
|
||||
} else {
|
||||
$stk = array(SERVICES_JSON_IN_OBJ);
|
||||
$obj = new stdClass();
|
||||
}
|
||||
}
|
||||
|
||||
array_push($stk, array('what' => SERVICES_JSON_SLICE,
|
||||
'where' => 0,
|
||||
'delim' => false));
|
||||
|
||||
$chrs = substr($str, 1, -1);
|
||||
$chrs = $this->reduce_string($chrs);
|
||||
|
||||
if ($chrs == '') {
|
||||
if (reset($stk) == SERVICES_JSON_IN_ARR) {
|
||||
return $arr;
|
||||
|
||||
} else {
|
||||
return $obj;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//print("\nparsing {$chrs}\n");
|
||||
|
||||
$strlen_chrs = strlen($chrs);
|
||||
|
||||
for ($c = 0; $c <= $strlen_chrs; ++$c) {
|
||||
|
||||
$top = end($stk);
|
||||
$substr_chrs_c_2 = substr($chrs, $c, 2);
|
||||
|
||||
if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
|
||||
// found a comma that is not inside a string, array, etc.,
|
||||
// OR we've reached the end of the character list
|
||||
$slice = substr($chrs, $top['where'], ($c - $top['where']));
|
||||
array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
|
||||
//print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
|
||||
|
||||
if (reset($stk) == SERVICES_JSON_IN_ARR) {
|
||||
// we are in an array, so just push an element onto the stack
|
||||
array_push($arr, $this->decode($slice));
|
||||
|
||||
} elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
|
||||
// we are in an object, so figure
|
||||
// out the property name and set an
|
||||
// element in an associative array,
|
||||
// for now
|
||||
$parts = array();
|
||||
|
||||
if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
|
||||
// "name":value pair
|
||||
$key = $this->decode($parts[1]);
|
||||
$val = $this->decode($parts[2]);
|
||||
|
||||
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
|
||||
$obj[$key] = $val;
|
||||
} else {
|
||||
$obj->$key = $val;
|
||||
}
|
||||
} elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
|
||||
// name:value pair, where name is unquoted
|
||||
$key = $parts[1];
|
||||
$val = $this->decode($parts[2]);
|
||||
|
||||
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
|
||||
$obj[$key] = $val;
|
||||
} else {
|
||||
$obj->$key = $val;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
|
||||
// found a quote, and we are not inside a string
|
||||
array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c}));
|
||||
//print("Found start of string at {$c}\n");
|
||||
|
||||
} elseif (($chrs{$c} == $top['delim']) &&
|
||||
($top['what'] == SERVICES_JSON_IN_STR) &&
|
||||
((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) {
|
||||
// found a quote, we're in a string, and it's not escaped
|
||||
// we know that it's not escaped becase there is _not_ an
|
||||
// odd number of backslashes at the end of the string so far
|
||||
array_pop($stk);
|
||||
//print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
|
||||
|
||||
} elseif (($chrs{$c} == '[') &&
|
||||
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
|
||||
// found a left-bracket, and we are in an array, object, or slice
|
||||
array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
|
||||
//print("Found start of array at {$c}\n");
|
||||
|
||||
} elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
|
||||
// found a right-bracket, and we're in an array
|
||||
array_pop($stk);
|
||||
//print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
|
||||
|
||||
} elseif (($chrs{$c} == '{') &&
|
||||
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
|
||||
// found a left-brace, and we are in an array, object, or slice
|
||||
array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
|
||||
//print("Found start of object at {$c}\n");
|
||||
|
||||
} elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
|
||||
// found a right-brace, and we're in an object
|
||||
array_pop($stk);
|
||||
//print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
|
||||
|
||||
} elseif (($substr_chrs_c_2 == '/*') &&
|
||||
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
|
||||
// found a comment start, and we are in an array, object, or slice
|
||||
array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
|
||||
$c++;
|
||||
//print("Found start of comment at {$c}\n");
|
||||
|
||||
} elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {
|
||||
// found a comment end, and we're in one now
|
||||
array_pop($stk);
|
||||
$c++;
|
||||
|
||||
for ($i = $top['where']; $i <= $c; ++$i)
|
||||
$chrs = substr_replace($chrs, ' ', $i, 1);
|
||||
|
||||
//print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (reset($stk) == SERVICES_JSON_IN_ARR) {
|
||||
return $arr;
|
||||
|
||||
} elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
|
||||
return $obj;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo Ultimately, this should just call PEAR::isError()
|
||||
*/
|
||||
function isError($data, $code = null)
|
||||
{
|
||||
if (class_exists('pear')) {
|
||||
return PEAR::isError($data, $code);
|
||||
} elseif (is_object($data) && (get_class($data) == 'services_json_error' ||
|
||||
is_subclass_of($data, 'services_json_error'))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (class_exists('PEAR_Error')) {
|
||||
|
||||
class Services_JSON_Error extends PEAR_Error
|
||||
{
|
||||
function Services_JSON_Error($message = 'unknown error', $code = null,
|
||||
$mode = null, $options = null, $userinfo = null)
|
||||
{
|
||||
parent::PEAR_Error($message, $code, $mode, $options, $userinfo);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/**
|
||||
* @todo Ultimately, this class shall be descended from PEAR_Error
|
||||
*/
|
||||
class Services_JSON_Error
|
||||
{
|
||||
function Services_JSON_Error($message = 'unknown error', $code = null,
|
||||
$mode = null, $options = null, $userinfo = null)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
253
include/PasswordHash.php
Normal file
@@ -0,0 +1,253 @@
|
||||
<?php
|
||||
#
|
||||
# Portable PHP password hashing framework.
|
||||
#
|
||||
# Version 0.3 / genuine.
|
||||
#
|
||||
# Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
|
||||
# the public domain. Revised in subsequent years, still public domain.
|
||||
#
|
||||
# There's absolutely no warranty.
|
||||
#
|
||||
# The homepage URL for this framework is:
|
||||
#
|
||||
# http://www.openwall.com/phpass/
|
||||
#
|
||||
# Please be sure to update the Version line if you edit this file in any way.
|
||||
# It is suggested that you leave the main version number intact, but indicate
|
||||
# your project name (after the slash) and add your own revision information.
|
||||
#
|
||||
# Please do not change the "private" password hashing method implemented in
|
||||
# here, thereby making your hashes incompatible. However, if you must, please
|
||||
# change the hash type identifier (the "$P$") to something different.
|
||||
#
|
||||
# Obviously, since this code is in the public domain, the above are not
|
||||
# requirements (there can be none), but merely suggestions.
|
||||
#
|
||||
class PasswordHash {
|
||||
var $itoa64;
|
||||
var $iteration_count_log2;
|
||||
var $portable_hashes;
|
||||
var $random_state;
|
||||
|
||||
function PasswordHash($iteration_count_log2, $portable_hashes)
|
||||
{
|
||||
$this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
||||
|
||||
if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
|
||||
$iteration_count_log2 = 8;
|
||||
$this->iteration_count_log2 = $iteration_count_log2;
|
||||
|
||||
$this->portable_hashes = $portable_hashes;
|
||||
|
||||
$this->random_state = microtime();
|
||||
if (function_exists('getmypid'))
|
||||
$this->random_state .= getmypid();
|
||||
}
|
||||
|
||||
function get_random_bytes($count)
|
||||
{
|
||||
$output = '';
|
||||
if (is_readable('/dev/urandom') &&
|
||||
($fh = @fopen('/dev/urandom', 'rb'))) {
|
||||
$output = fread($fh, $count);
|
||||
fclose($fh);
|
||||
}
|
||||
|
||||
if (strlen($output) < $count) {
|
||||
$output = '';
|
||||
for ($i = 0; $i < $count; $i += 16) {
|
||||
$this->random_state =
|
||||
md5(microtime() . $this->random_state);
|
||||
$output .=
|
||||
pack('H*', md5($this->random_state));
|
||||
}
|
||||
$output = substr($output, 0, $count);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
function encode64($input, $count)
|
||||
{
|
||||
$output = '';
|
||||
$i = 0;
|
||||
do {
|
||||
$value = ord($input[$i++]);
|
||||
$output .= $this->itoa64[$value & 0x3f];
|
||||
if ($i < $count)
|
||||
$value |= ord($input[$i]) << 8;
|
||||
$output .= $this->itoa64[($value >> 6) & 0x3f];
|
||||
if ($i++ >= $count)
|
||||
break;
|
||||
if ($i < $count)
|
||||
$value |= ord($input[$i]) << 16;
|
||||
$output .= $this->itoa64[($value >> 12) & 0x3f];
|
||||
if ($i++ >= $count)
|
||||
break;
|
||||
$output .= $this->itoa64[($value >> 18) & 0x3f];
|
||||
} while ($i < $count);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
function gensalt_private($input)
|
||||
{
|
||||
$output = '$P$';
|
||||
$output .= $this->itoa64[min($this->iteration_count_log2 +
|
||||
((PHP_VERSION >= '5') ? 5 : 3), 30)];
|
||||
$output .= $this->encode64($input, 6);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
function crypt_private($password, $setting)
|
||||
{
|
||||
$output = '*0';
|
||||
if (substr($setting, 0, 2) == $output)
|
||||
$output = '*1';
|
||||
|
||||
$id = substr($setting, 0, 3);
|
||||
# We use "$P$", phpBB3 uses "$H$" for the same thing
|
||||
if ($id != '$P$' && $id != '$H$')
|
||||
return $output;
|
||||
|
||||
$count_log2 = strpos($this->itoa64, $setting[3]);
|
||||
if ($count_log2 < 7 || $count_log2 > 30)
|
||||
return $output;
|
||||
|
||||
$count = 1 << $count_log2;
|
||||
|
||||
$salt = substr($setting, 4, 8);
|
||||
if (strlen($salt) != 8)
|
||||
return $output;
|
||||
|
||||
# We're kind of forced to use MD5 here since it's the only
|
||||
# cryptographic primitive available in all versions of PHP
|
||||
# currently in use. To implement our own low-level crypto
|
||||
# in PHP would result in much worse performance and
|
||||
# consequently in lower iteration counts and hashes that are
|
||||
# quicker to crack (by non-PHP code).
|
||||
if (PHP_VERSION >= '5') {
|
||||
$hash = md5($salt . $password, TRUE);
|
||||
do {
|
||||
$hash = md5($hash . $password, TRUE);
|
||||
} while (--$count);
|
||||
} else {
|
||||
$hash = pack('H*', md5($salt . $password));
|
||||
do {
|
||||
$hash = pack('H*', md5($hash . $password));
|
||||
} while (--$count);
|
||||
}
|
||||
|
||||
$output = substr($setting, 0, 12);
|
||||
$output .= $this->encode64($hash, 16);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
function gensalt_extended($input)
|
||||
{
|
||||
$count_log2 = min($this->iteration_count_log2 + 8, 24);
|
||||
# This should be odd to not reveal weak DES keys, and the
|
||||
# maximum valid value is (2**24 - 1) which is odd anyway.
|
||||
$count = (1 << $count_log2) - 1;
|
||||
|
||||
$output = '_';
|
||||
$output .= $this->itoa64[$count & 0x3f];
|
||||
$output .= $this->itoa64[($count >> 6) & 0x3f];
|
||||
$output .= $this->itoa64[($count >> 12) & 0x3f];
|
||||
$output .= $this->itoa64[($count >> 18) & 0x3f];
|
||||
|
||||
$output .= $this->encode64($input, 3);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
function gensalt_blowfish($input)
|
||||
{
|
||||
# This one needs to use a different order of characters and a
|
||||
# different encoding scheme from the one in encode64() above.
|
||||
# We care because the last character in our encoded string will
|
||||
# only represent 2 bits. While two known implementations of
|
||||
# bcrypt will happily accept and correct a salt string which
|
||||
# has the 4 unused bits set to non-zero, we do not want to take
|
||||
# chances and we also do not want to waste an additional byte
|
||||
# of entropy.
|
||||
$itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
|
||||
$output = '$2a$';
|
||||
$output .= chr(ord('0') + $this->iteration_count_log2 / 10);
|
||||
$output .= chr(ord('0') + $this->iteration_count_log2 % 10);
|
||||
$output .= '$';
|
||||
|
||||
$i = 0;
|
||||
do {
|
||||
$c1 = ord($input[$i++]);
|
||||
$output .= $itoa64[$c1 >> 2];
|
||||
$c1 = ($c1 & 0x03) << 4;
|
||||
if ($i >= 16) {
|
||||
$output .= $itoa64[$c1];
|
||||
break;
|
||||
}
|
||||
|
||||
$c2 = ord($input[$i++]);
|
||||
$c1 |= $c2 >> 4;
|
||||
$output .= $itoa64[$c1];
|
||||
$c1 = ($c2 & 0x0f) << 2;
|
||||
|
||||
$c2 = ord($input[$i++]);
|
||||
$c1 |= $c2 >> 6;
|
||||
$output .= $itoa64[$c1];
|
||||
$output .= $itoa64[$c2 & 0x3f];
|
||||
} while (1);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
function HashPassword($password)
|
||||
{
|
||||
$random = '';
|
||||
|
||||
if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) {
|
||||
$random = $this->get_random_bytes(16);
|
||||
$hash =
|
||||
crypt($password, $this->gensalt_blowfish($random));
|
||||
if (strlen($hash) == 60)
|
||||
return $hash;
|
||||
}
|
||||
|
||||
if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) {
|
||||
if (strlen($random) < 3)
|
||||
$random = $this->get_random_bytes(3);
|
||||
$hash =
|
||||
crypt($password, $this->gensalt_extended($random));
|
||||
if (strlen($hash) == 20)
|
||||
return $hash;
|
||||
}
|
||||
|
||||
if (strlen($random) < 6)
|
||||
$random = $this->get_random_bytes(6);
|
||||
$hash =
|
||||
$this->crypt_private($password,
|
||||
$this->gensalt_private($random));
|
||||
if (strlen($hash) == 34)
|
||||
return $hash;
|
||||
|
||||
# Returning '*' on error is safe here, but would _not_ be safe
|
||||
# in a crypt(3)-like function used _both_ for generating new
|
||||
# hashes and for validating passwords against existing hashes.
|
||||
return '*';
|
||||
}
|
||||
|
||||
function CheckPassword($password, $stored_hash)
|
||||
{
|
||||
$hash = $this->crypt_private($password, $stored_hash);
|
||||
if ($hash[0] == '*')
|
||||
$hash = crypt($password, $stored_hash);
|
||||
|
||||
return $hash == $stored_hash;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
32
include/ajax.config.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/*********************************************************************
|
||||
ajax.content.php
|
||||
|
||||
AJAX interface for content fetching...allowed methods.
|
||||
|
||||
Peter Rotich <peter@osticket.com>
|
||||
Copyright (c) 2006-2012 osTicket
|
||||
http://www.osticket.com
|
||||
|
||||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||||
See LICENSE.TXT for details.
|
||||
|
||||
vim: expandtab sw=4 ts=4 sts=4:
|
||||
**********************************************************************/
|
||||
|
||||
if(!defined('INCLUDE_DIR')) die('!');
|
||||
|
||||
class ConfigAjaxAPI extends AjaxController {
|
||||
|
||||
//config info UI might need.
|
||||
function ui() {
|
||||
global $thisstaff, $cfg;
|
||||
|
||||
$config=array('ticket_lock_time'=>($cfg->getLockTime()*3600),
|
||||
'max_attachments'=>$cfg->getMaxFileUploads(),
|
||||
'max_file_size'=>$cfg->getMaxFileSize());
|
||||
|
||||
return $this->json_encode($config);
|
||||
}
|
||||
}
|
||||
?>
|
||||
83
include/ajax.content.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
/*********************************************************************
|
||||
ajax.content.php
|
||||
|
||||
AJAX interface for content fetching...allowed methods.
|
||||
|
||||
Peter Rotich <peter@osticket.com>
|
||||
Copyright (c) 2006-2012 osTicket
|
||||
http://www.osticket.com
|
||||
|
||||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||||
See LICENSE.TXT for details.
|
||||
|
||||
vim: expandtab sw=4 ts=4 sts=4:
|
||||
**********************************************************************/
|
||||
|
||||
if(!defined('INCLUDE_DIR')) die('!');
|
||||
|
||||
class ContentAjaxAPI extends AjaxController {
|
||||
|
||||
function log($id) {
|
||||
|
||||
if($id && ($log=Log::lookup($id))) {
|
||||
$content=sprintf('<div style="width:500px;"> <strong>%s</strong><br><p>%s</p>
|
||||
<hr><strong>Log Date:</strong> <em>%s</em> <strong>IP Address:</strong> <em>%s</em></div>',
|
||||
$log->getTitle(),
|
||||
Format::display(str_replace(',',', ',$log->getText())),
|
||||
Format::db_daydatetime($log->getCreateDate()),
|
||||
$log->getIP());
|
||||
}else {
|
||||
$content='<div style="width:295px;"> <strong>Error:</strong>Unknown or invalid log ID</div>';
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
function ticket_variables() {
|
||||
|
||||
$content='<div style="width:600px;">
|
||||
<h2>Ticket Variables</h2>
|
||||
Please note that non-base variables depends on the context of use.
|
||||
<br>
|
||||
<table width="100%" border="0" cellspacing=1 cellpadding=2>
|
||||
<tr><td width="50%" valign="top"><b>Base Variables</b></td><td><b>Other Variables</b></td></tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
<table width="100%" border="0" cellspacing=1 cellpadding=1>
|
||||
<tr><td width="100">%id</td><td>Ticket ID (internal ID)</td></tr>
|
||||
<tr><td>%ticket</td><td>Ticket number (external ID)</td></tr>
|
||||
<tr><td>%email</td><td>Email address</td></tr>
|
||||
<tr><td>%name</td><td>Full name</td></tr>
|
||||
<tr><td>%subject</td><td>Subject</td></tr>
|
||||
<tr><td>%topic</td><td>Help topic (web only)</td></tr>
|
||||
<tr><td>%phone</td><td>Phone number | ext</td></tr>
|
||||
<tr><td>%status</td><td>Status</td></tr>
|
||||
<tr><td>%priority</td><td>Priority</td></tr>
|
||||
<tr><td>%dept</td><td>Department</td></tr>
|
||||
<tr><td>%assigned</td><td>Assigned staff or team (if any)</td></tr>
|
||||
<tr><td>%createdate</td><td>Date created</td></tr>
|
||||
<tr><td>%duedate</td><td>Due date</td></tr>
|
||||
<tr><td>%closedate</td><td>Date closed</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
<td valign="top">
|
||||
<table width="100%" border="0" cellspacing=1 cellpadding=1>
|
||||
<tr><td width="100">%message</td><td>Message (incoming)</td></tr>
|
||||
<tr><td>%response</td><td>Response (outgoing)</td></tr>
|
||||
<tr><td>%note</td><td>Internal/transfer note</td></tr>
|
||||
<tr><td>%staff</td><td>Staff\'s name (alert/notices)</td></tr>
|
||||
<tr><td>%assignee</td><td>Assigned staff</td></tr>
|
||||
<tr><td>%assigner</td><td>Staff assigning the ticket</td></tr>
|
||||
<tr><td>%url</td><td>osTicket\'s base url (FQDN)</td></tr>
|
||||
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>';
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
?>
|
||||
83
include/ajax.kbase.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
/*********************************************************************
|
||||
ajax.kbase.php
|
||||
|
||||
AJAX interface for knowledge base related...allowed methods.
|
||||
|
||||
Peter Rotich <peter@osticket.com>
|
||||
Copyright (c) 2006-2012 osTicket
|
||||
http://www.osticket.com
|
||||
|
||||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||||
See LICENSE.TXT for details.
|
||||
|
||||
vim: expandtab sw=4 ts=4 sts=4:
|
||||
**********************************************************************/
|
||||
if(!defined('INCLUDE_DIR')) die('!');
|
||||
|
||||
|
||||
class KbaseAjaxAPI extends AjaxController {
|
||||
|
||||
function cannedResp($id, $format='') {
|
||||
global $thisstaff, $_GET;
|
||||
|
||||
include_once(INCLUDE_DIR.'class.canned.php');
|
||||
|
||||
if(!$id || !($canned=Canned::lookup($id)) || !$canned->isEnabled())
|
||||
Http::response(404, 'No such premade reply');
|
||||
|
||||
//Load ticket.
|
||||
if($_GET['tid']) {
|
||||
include_once(INCLUDE_DIR.'class.ticket.php');
|
||||
$ticket = Ticket::lookup($_GET['tid']);
|
||||
}
|
||||
|
||||
switch($format) {
|
||||
case 'json':
|
||||
$resp['id'] = $canned->getId();
|
||||
$resp['ticket'] = $canned->getTitle();
|
||||
$resp['response'] = $ticket?$ticket->replaceTemplateVars($canned->getResponse()):$canned->getResponse();
|
||||
$resp['files'] = $canned->getAttachments();
|
||||
|
||||
|
||||
$response = $this->json_encode($resp);
|
||||
break;
|
||||
case 'txt':
|
||||
default:
|
||||
$response =$ticket?$ticket->replaceTemplateVars($canned->getResponse()):$canned->getResponse();
|
||||
}
|
||||
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
function faq($id, $format='html') {
|
||||
global $thisstaff; //XXX: user ajax->getThisStaff()
|
||||
include_once(INCLUDE_DIR.'class.faq.php');
|
||||
|
||||
if(!($faq=FAQ::lookup($id)))
|
||||
return null;
|
||||
|
||||
//TODO: $fag->getJSON() for json format.
|
||||
$resp = sprintf(
|
||||
'<div style="width:650px;">
|
||||
<strong>%s</strong><p>%s</p>
|
||||
<div class="faded">Last updated %s</div>
|
||||
<hr>
|
||||
<a href="faq.php?id=%d">View</a> | <a href="faq.php?id=%d">Attachments (%s)</a>',
|
||||
$faq->getQuestion(),
|
||||
Format::safe_html($faq->getAnswer()),
|
||||
Format::db_daydatetime($faq->getUpdateDate()),
|
||||
$faq->getId(),
|
||||
$faq->getId(),
|
||||
$faq->getNumAttachments());
|
||||
if($thisstaff && $thisstaff->canManageFAQ()) {
|
||||
$resp.=sprintf(' | <a href="faq.php?id=%d&a=edit">Edit</a>',$faq->getId());
|
||||
|
||||
}
|
||||
$resp.='</div>';
|
||||
|
||||
return $resp;
|
||||
}
|
||||
}
|
||||
?>
|
||||
164
include/ajax.tickets.php
Normal file
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
/*********************************************************************
|
||||
ajax.tickets.php
|
||||
|
||||
AJAX interface for tickets
|
||||
|
||||
Peter Rotich <peter@osticket.com>
|
||||
Copyright (c) 2006-2012 osTicket
|
||||
http://www.osticket.com
|
||||
|
||||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||||
See LICENSE.TXT for details.
|
||||
|
||||
vim: expandtab sw=4 ts=4 sts=4:
|
||||
**********************************************************************/
|
||||
|
||||
if(!defined('INCLUDE_DIR')) die('403');
|
||||
|
||||
include_once(INCLUDE_DIR.'class.ticket.php');
|
||||
|
||||
class TicketsAjaxAPI extends AjaxController {
|
||||
|
||||
function search() {
|
||||
|
||||
$limit = isset($_GET['limit']) ? (int) $_GET['limit']:25;
|
||||
$items=array();
|
||||
$ticketid=false;
|
||||
if(isset($_GET['id'])){
|
||||
$WHERE=' WHERE ticketID LIKE \''.db_input($_GET['id'], false).'%\'';
|
||||
$ticketid=true;
|
||||
}elseif(isset($_GET['email'])){
|
||||
$WHERE=' WHERE email LIKE \''.db_input(strtolower($_GET['email']), false).'%\'';
|
||||
}else{
|
||||
Http::response(400, "id or email argument is required");
|
||||
}
|
||||
$sql='SELECT DISTINCT ticketID,email,name FROM '.TICKET_TABLE.' '.$WHERE.' ORDER BY created LIMIT '.$limit;
|
||||
$res=db_query($sql);
|
||||
if($res && db_num_rows($res)){
|
||||
while(list($id,$email,$name)=db_fetch_row($res)) {
|
||||
$info=($ticketid)?$email:$id;
|
||||
$id=($ticketid)?$id:$email;
|
||||
# TODO: Return 'name' from email address if 'email' argument
|
||||
# specified?
|
||||
$items[] = array('id'=>$id, 'value'=>$id, 'info'=>$info,
|
||||
'name'=>$name);
|
||||
}
|
||||
}
|
||||
return $this->encode(array('results'=>$items));
|
||||
}
|
||||
|
||||
function acquireLock($tid) {
|
||||
global $cfg,$thisstaff;
|
||||
|
||||
if(!$tid or !is_numeric($tid) or !$thisstaff or !$cfg)
|
||||
return 0;
|
||||
|
||||
$ticket = Ticket::lookup($tid);
|
||||
|
||||
if(!$ticket || !$ticket->checkStaffAccess($thisstaff))
|
||||
return $this->json_encode(array('id'=>0, 'retry'=>false, 'msg'=>'Lock denied!'));
|
||||
|
||||
//is the ticket already locked?
|
||||
if($ticket->isLocked() && ($lock=$ticket->getLock()) && !$lock->isExpired()) {
|
||||
/*Note: Ticket->acquireLock does the same logic...but we need it here since we need to know who owns the lock up front*/
|
||||
//Ticket is locked by someone else.??
|
||||
if($lock->getStaffId()!=$thisstaff->getId())
|
||||
return $this->json_encode(array('id'=>0, 'retry'=>false, 'msg'=>'Unable to acquire lock.'));
|
||||
|
||||
//Ticket already locked by staff...try renewing it.
|
||||
$lock->renew(); //New clock baby!
|
||||
|
||||
return $this->json_encode(array('id'=>$lock->getId(), 'time'=>$lock->getTime()));
|
||||
}
|
||||
|
||||
//Ticket is not locked or the lock is expired...try locking it...
|
||||
if($lock=$ticket->acquireLock($thisstaff->getId(),$cfg->getLockTime())) //Set the lock.
|
||||
return $this->json_encode(array('id'=>$lock->getId(), 'time'=>$lock->getTime()));
|
||||
|
||||
//unable to obtain the lock..for some really weired reason!
|
||||
//Client should watch for possible loop on retries. Max attempts?
|
||||
return $this->json_encode(array('id'=>0, 'retry'=>true));
|
||||
}
|
||||
|
||||
function renewLock($tid, $id) {
|
||||
global $thisstaff;
|
||||
|
||||
if(!$id or !is_numeric($id) or !$thisstaff)
|
||||
return $this->json_encode(array('id'=>0, 'retry'=>true));
|
||||
|
||||
$lock= TicketLock::lookup($id);
|
||||
if(!$lock || !$lock->getStaffId() || $lock->isExpired()) //Said lock doesn't exist or is is expired
|
||||
return self::acquireLock($tid); //acquire the lock
|
||||
|
||||
if($lock->getStaffId()!=$thisstaff->getId()) //user doesn't own the lock anymore??? sorry...try to next time.
|
||||
return $this->json_encode(array('id'=>0, 'retry'=>false)); //Give up...
|
||||
|
||||
//Renew the lock.
|
||||
$lock->renew(); //Failure here is not an issue since the lock is not expired yet.. client need to check time!
|
||||
|
||||
return $this->json_encode(array('id'=>$lock->getId(), 'time'=>$lock->getTime()));
|
||||
}
|
||||
|
||||
function releaseLock($tid, $id=0) {
|
||||
global $thisstaff;
|
||||
|
||||
if($id && is_numeric($id)){ //Lock Id provided!
|
||||
|
||||
$lock = TicketLock::lookup($id, $tid);
|
||||
//Already gone?
|
||||
if(!$lock || !$lock->getStaffId() || $lock->isExpired()) //Said lock doesn't exist or is is expired
|
||||
return 1;
|
||||
|
||||
//make sure the user actually owns the lock before releasing it.
|
||||
return ($lock->getStaffId()==$thisstaff->getId() && $lock->release())?1:0;
|
||||
|
||||
}elseif($tid){ //release all the locks the user owns on the ticket.
|
||||
return TicketLock::removeStaffLocks($thisstaff->getId(),$tid)?1:0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function previewTicket ($tid) {
|
||||
|
||||
global $thisstaff;
|
||||
|
||||
|
||||
$ticket = new Ticket($tid);
|
||||
|
||||
$resp = sprintf(
|
||||
'<div style="width:500px;">
|
||||
<strong>Ticket #%d Preview</strong><br>INFO HERE!!',
|
||||
$ticket->getExtId());
|
||||
|
||||
$options[]=array('action'=>'Thread ('.$ticket->getThreadCount().')','url'=>"tickets.php?id=$tid");
|
||||
if($ticket->getNumNotes())
|
||||
$options[]=array('action'=>'Notes ('.$ticket->getNumNotes().')','url'=>"tickets.php?id=$tid#notes");
|
||||
|
||||
if($ticket->isOpen())
|
||||
$options[]=array('action'=>'Reply','url'=>"tickets.php?id=$tid#reply");
|
||||
|
||||
if($thisstaff->canAssignTickets())
|
||||
$options[]=array('action'=>($ticket->isAssigned()?'Reassign':'Assign'),'url'=>"tickets.php?id=$tid#assign");
|
||||
|
||||
if($thisstaff->canTransferTickets())
|
||||
$options[]=array('action'=>'Transfer','url'=>"tickets.php?id=$tid#transfer");
|
||||
|
||||
$options[]=array('action'=>'Post Note','url'=>"tickets.php?id=$tid#note");
|
||||
|
||||
if($options) {
|
||||
$resp.='<ul class="tip_menu">';
|
||||
foreach($options as $option) {
|
||||
$resp.=sprintf('<li><a href="%s">%s</a></li>',
|
||||
$option['url'],$option['action']);
|
||||
}
|
||||
$resp.='</ul>';
|
||||
}
|
||||
|
||||
$resp.='</div>';
|
||||
|
||||
return $resp;
|
||||
}
|
||||
}
|
||||
?>
|
||||
71
include/api.ticket.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
include_once "include/class.api.php";
|
||||
include_once "include/class.ticket.php";
|
||||
|
||||
class TicketController extends ApiController {
|
||||
|
||||
# Supported arguments -- anything else is an error. These items will be
|
||||
# inspected _after_ the fixup() method of the ApiXxxDataParser classes
|
||||
# so that all supported input formats should be supported
|
||||
function getRequestStructure($format) {
|
||||
$supported = array(
|
||||
"alert", "autorespond", "source",
|
||||
"name", "email", "subject", "phone", "phone_ext",
|
||||
"attachments" => array("*" =>
|
||||
array("name", "type", "data", "encoding")
|
||||
),
|
||||
"message", "ip"
|
||||
);
|
||||
if ($format == "xml") return array("ticket" => $supported);
|
||||
else return $supported;
|
||||
}
|
||||
|
||||
function create($format) {
|
||||
$this->requireApiKey();
|
||||
|
||||
# Parse request body
|
||||
$data = $this->getRequest($format);
|
||||
if ($format == "xml") $data = $data["ticket"];
|
||||
|
||||
# Pull off some meta-data
|
||||
$alert = $data['alert'] ? $data['alert'] : true;
|
||||
$autorespond = $data['autorespond'] ? $data['autorespond'] : true;
|
||||
$source = $data['source'] ? $data['source'] : 'API';
|
||||
|
||||
# TODO: Handle attachment encoding (base64)
|
||||
foreach ($data["attachments"] as $filename=>&$info) {
|
||||
if ($info["encoding"] == "base64") {
|
||||
# XXX: May fail on large inputs. See
|
||||
# http://us.php.net/manual/en/function.base64-decode.php#105512
|
||||
if (!($info["data"] = base64_decode($info["data"], true)))
|
||||
Http::response(400, sprintf(
|
||||
"%s: Poorly encoded base64 data",
|
||||
$filename));
|
||||
}
|
||||
$info['size'] = strlen($info['data']);
|
||||
}
|
||||
|
||||
# Create the ticket with the data (attempt to anyway)
|
||||
$errors = array();
|
||||
$ticket = Ticket::create($data, $errors, $source, $autorespond,
|
||||
$alert);
|
||||
|
||||
# Return errors (?)
|
||||
if (count($errors)) {
|
||||
Http::response(400, "Unable to create new ticket: validation errors:\n"
|
||||
. Format::array_implode(": ", "\n", $errors));
|
||||
} elseif (!$ticket) {
|
||||
Http::response(500, "Unable to create new ticket: unknown error");
|
||||
}
|
||||
|
||||
# Save attachment(s)
|
||||
foreach ($data["attachments"] as &$info)
|
||||
$ticket->saveAttachment($info, $ticket->getLastMsgId(), "M");
|
||||
|
||||
# All done. Return HTTP/201 --> Created
|
||||
Http::response(201, $ticket->getExtId());
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
53
include/class.ajax.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/*********************************************************************
|
||||
class.ajax.php
|
||||
|
||||
AjaxController class that is an extension of the ApiController class. It
|
||||
will be used to provide functionality common to all Ajax API calls
|
||||
|
||||
Jared Hancock
|
||||
Copyright (c) 2006-2012 osTicket
|
||||
http://www.osticket.com
|
||||
|
||||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||||
See LICENSE.TXT for details.
|
||||
|
||||
vim: expandtab sw=4 ts=4 sts=4:
|
||||
**********************************************************************/
|
||||
|
||||
require_once (INCLUDE_DIR.'class.api.php');
|
||||
|
||||
/**
|
||||
* AjaxController Class
|
||||
* A simple extension of the ApiController class that will assist in
|
||||
* providing functionality common to all Ajax call controllers. Any Ajax
|
||||
* call controller should inherit from this class in order to maintain
|
||||
* consistency.
|
||||
*/
|
||||
class AjaxController extends ApiController {
|
||||
function AjaxController() {
|
||||
# Security checks first
|
||||
# --> It is assumed that all AJAX calls will require a login. And
|
||||
# for now, since client logins are not yet supported, a staff
|
||||
# login will be required for AJAX calls.
|
||||
$this->staffOnly();
|
||||
}
|
||||
function staffOnly() {
|
||||
global $thisstaff;
|
||||
if(!$thisstaff || !$thisstaff->isValid()) {
|
||||
Http::response(401,'Access Denied. IP '.$_SERVER['REMOTE_ADDR']);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Convert a PHP array into a JSON-encoded string
|
||||
*/
|
||||
function json_encode($what) {
|
||||
require_once (INCLUDE_DIR.'class.json.php');
|
||||
$encoder = new JsonDataEncoder();
|
||||
return $encoder->encode($what);
|
||||
}
|
||||
|
||||
function encode($what) {
|
||||
return $this->json_encode($what);
|
||||
}
|
||||
}
|
||||
284
include/class.api.php
Normal file
@@ -0,0 +1,284 @@
|
||||
<?php
|
||||
/*********************************************************************
|
||||
class.api.php
|
||||
|
||||
API
|
||||
|
||||
Peter Rotich <peter@osticket.com>
|
||||
Copyright (c) 2006-2012 osTicket
|
||||
http://www.osticket.com
|
||||
|
||||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||||
See LICENSE.TXT for details.
|
||||
|
||||
vim: expandtab sw=4 ts=4 sts=4:
|
||||
**********************************************************************/
|
||||
class API {
|
||||
|
||||
var $id;
|
||||
|
||||
var $info;
|
||||
|
||||
function API($id){
|
||||
$this->id=0;
|
||||
$this->load($id);
|
||||
}
|
||||
|
||||
function load($id) {
|
||||
|
||||
$sql='SELECT * FROM '.API_KEY_TABLE.' WHERE id='.db_input($id);
|
||||
if(($res=db_query($sql)) && db_num_rows($res)) {
|
||||
$info=db_fetch_array($res);
|
||||
$this->id=$info['id'];
|
||||
$this->info=$info;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function reload() {
|
||||
return $this->load($this->getId());
|
||||
}
|
||||
|
||||
function getId(){
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
function getKey(){
|
||||
return $this->info['apikey'];
|
||||
}
|
||||
|
||||
function getIPAddr(){
|
||||
return $this->info['ipaddr'];
|
||||
}
|
||||
|
||||
function getNotes(){
|
||||
return $this->info['notes'];
|
||||
}
|
||||
|
||||
function isActive(){
|
||||
return ($this->info['isactive']);
|
||||
}
|
||||
|
||||
function update($vars,&$errors){
|
||||
if(API::save($this->getId(),$vars,$errors)){
|
||||
$this->reload();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function delete(){
|
||||
$sql='DELETE FROM '.API_KEY_TABLE.' WHERE id='.db_input($this->getId()).' LIMIT 1';
|
||||
return (db_query($sql) && ($num=db_affected_rows()));
|
||||
}
|
||||
|
||||
/** Static functions **/
|
||||
function add($vars,&$errors){
|
||||
return API::save(0,$vars,$errors);
|
||||
}
|
||||
|
||||
function validate($key,$ip){
|
||||
|
||||
$sql='SELECT id FROM '.API_KEY_TABLE.' WHERE ipaddr='.db_input($ip).' AND apikey='.db_input($key);
|
||||
return (($res=db_query($sql)) && db_num_rows($res));
|
||||
}
|
||||
|
||||
function getKeyByIPAddr($ip){
|
||||
|
||||
$sql='SELECT apikey FROM '.API_KEY_TABLE.' WHERE ipaddr='.db_input($ip);
|
||||
if(($res=db_query($sql)) && db_num_rows($res))
|
||||
list($key)=db_fetch_row($res);
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
function lookup($id){
|
||||
return ($id && is_numeric($id) && ($k= new API($id)) && $k->getId()==$id)?$k:null;
|
||||
}
|
||||
|
||||
function save($id,$vars,&$errors){
|
||||
|
||||
if(!$id) {
|
||||
if(!$vars['ipaddr'] || !Validator::is_ip($vars['ipaddr']))
|
||||
$errors['ipaddr']='Valid IP required';
|
||||
elseif(API::getKeyByIPAddr($vars['ipaddr']))
|
||||
$errors['ipaddr']='API key for the IP already exists';
|
||||
}
|
||||
|
||||
if($errors) return false;
|
||||
|
||||
|
||||
$sql=' updated=NOW() '.
|
||||
',isactive='.db_input($vars['isactive']).
|
||||
',notes='.db_input($vars['notes']);
|
||||
|
||||
if($id) {
|
||||
$sql='UPDATE '.API_KEY_TABLE.' SET '.$sql.' WHERE id='.db_input($id);
|
||||
if(db_query($sql))
|
||||
return true;
|
||||
|
||||
$errors['err']='Unable to update API key. Internal error occurred';
|
||||
}else{
|
||||
$sql='INSERT INTO '.API_KEY_TABLE.' SET '.$sql.',created=NOW() '.
|
||||
',ipaddr='.db_input($vars['ipaddr']).
|
||||
',apikey='.db_input(strtoupper(md5(time().$vars['ipaddr'].md5(Misc::randcode(16)))));
|
||||
if(db_query($sql) && ($id=db_insert_id()))
|
||||
return $id;
|
||||
|
||||
$errors['err']='Unable to add API key. Internal error';
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Controller for API methods. Provides methods to check to make sure the
|
||||
* API key was sent and that the Client-IP and API-Key have been registered
|
||||
* in the database, and methods for parsing and validating data sent in the
|
||||
* API request.
|
||||
*/
|
||||
class ApiController {
|
||||
function requireApiKey() {
|
||||
# Validate the API key -- required to be sent via the X-API-Key
|
||||
# header
|
||||
if (!isset($_SERVER['HTTP_X_API_KEY']))
|
||||
Http::response(403, "API key required");
|
||||
else if (!Api::validate($_SERVER['HTTP_X_API_KEY'],
|
||||
$_SERVER['REMOTE_ADDR']))
|
||||
Http::response(401,
|
||||
"API key not found or source IP not authorized");
|
||||
}
|
||||
/**
|
||||
* Retrieves the body of the API request and converts it to a common
|
||||
* hashtable. For JSON formats, this is mostly a noop, the conversion
|
||||
* work will be done for XML requests
|
||||
*/
|
||||
function getRequest($format) {
|
||||
if (!($stream = @fopen("php://input", "r")))
|
||||
Http::response(400, "Unable to read request body");
|
||||
if ($format == "xml") {
|
||||
if (!function_exists("xml_parser_create"))
|
||||
Http::response(500, "XML extension not supported");
|
||||
$tree = new ApiXmlDataParser();
|
||||
} elseif ($format == "json") {
|
||||
$tree = new ApiJsonDataParser();
|
||||
}
|
||||
if (!($data = $tree->parse($stream)))
|
||||
Http::response(400, $tree->lastError());
|
||||
$this->validate($data, $this->getRequestStructure($format));
|
||||
return $data;
|
||||
}
|
||||
/**
|
||||
* Structure to validate the request against -- must be overridden to be
|
||||
* useful
|
||||
*/
|
||||
function getRequestStructure($format) { return array(); }
|
||||
/**
|
||||
* Simple validation that makes sure the keys of a parsed request are
|
||||
* expected. It is assumed that the functions actually implementing the
|
||||
* API will further validate the contents of the request
|
||||
*/
|
||||
function validate($data, $structure, $prefix="") {
|
||||
foreach ($data as $key=>$info) {
|
||||
if (is_array($structure) and is_array($info)) {
|
||||
$search = isset($structure[$key]) ? $key : "*";
|
||||
if (isset($structure[$search])) {
|
||||
$this->validate($info, $structure[$search], "$prefix$key/");
|
||||
continue;
|
||||
}
|
||||
} elseif (in_array($key, $structure)) {
|
||||
continue;
|
||||
}
|
||||
Http::response(400, "$prefix$key: Unexpected data received");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
include_once "class.xml.php";
|
||||
class ApiXmlDataParser extends XmlDataParser {
|
||||
|
||||
function parse($stream) {
|
||||
return $this->fixup(parent::parse($stream));
|
||||
}
|
||||
/**
|
||||
* Perform simple operations to make data consistent between JSON and
|
||||
* XML data types
|
||||
*/
|
||||
function fixup($current) {
|
||||
if (!is_array($current))
|
||||
return $current;
|
||||
foreach ($current as $key=>&$value) {
|
||||
if ($key == "phone") {
|
||||
$current["phone_ext"] = $value["ext"]; # PHP [like] point
|
||||
$value = $value[":text"];
|
||||
} else if ($key == "alert") {
|
||||
$value = (bool)$value;
|
||||
} else if ($key == "autorespond") {
|
||||
$value = (bool)$value;
|
||||
} else if ($key == "attachments") {
|
||||
foreach ($value as &$info) {
|
||||
$info["data"] = $info[":text"];
|
||||
unset($info[":text"]);
|
||||
}
|
||||
unset($info);
|
||||
}
|
||||
if (is_array($value)) {
|
||||
$value = $this->fixup($value);
|
||||
}
|
||||
}
|
||||
return $current;
|
||||
}
|
||||
}
|
||||
|
||||
include_once "class.json.php";
|
||||
class ApiJsonDataParser extends JsonDataParser {
|
||||
function parse($stream) {
|
||||
return $this->fixup(parent::parse($stream));
|
||||
}
|
||||
function fixup($current) {
|
||||
if (!is_array($current))
|
||||
return $current;
|
||||
foreach ($current as $key=>&$value) {
|
||||
if ($key == "phone") {
|
||||
list($value,$current["phone_ext"])
|
||||
= explode("X", strtoupper($value), 2);
|
||||
} else if ($key == "alert") {
|
||||
$value = (bool)$value;
|
||||
} else if ($key == "autorespond") {
|
||||
$value = (bool)$value;
|
||||
} else if ($key == "attachments") {
|
||||
foreach ($value as &$info) {
|
||||
$data = reset($info);
|
||||
# PHP5: fopen("data://$data[5:]");
|
||||
if (substr($data, 0, 5) != "data:") {
|
||||
$info = array(
|
||||
"data" => $data,
|
||||
"type" => "text/plain",
|
||||
"name" => key($info));
|
||||
} else {
|
||||
$data = substr($data,5);
|
||||
list($meta, $contents) = explode(",", $data);
|
||||
list($type, $extra) = explode(";", $meta);
|
||||
$info = array(
|
||||
"data" => $contents,
|
||||
"type" => $type,
|
||||
"name" => key($info));
|
||||
if (substr($extra, -6) == "base64")
|
||||
$info["encoding"] = "base64";
|
||||
# TODO: Handle 'charset' hint in $extra
|
||||
}
|
||||
}
|
||||
unset($value);
|
||||
}
|
||||
if (is_array($value)) {
|
||||
$value = $this->fixup($value);
|
||||
}
|
||||
}
|
||||
return $current;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
103
include/class.attachment.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
/*********************************************************************
|
||||
class.attachment.php
|
||||
|
||||
Attachment Handler - mainly used for lookup...doesn't save!
|
||||
|
||||
Peter Rotich <peter@osticket.com>
|
||||
Copyright (c) 2006-2012 osTicket
|
||||
http://www.osticket.com
|
||||
|
||||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||||
See LICENSE.TXT for details.
|
||||
|
||||
vim: expandtab sw=4 ts=4 sts=4:
|
||||
**********************************************************************/
|
||||
require_once(INCLUDE_DIR.'class.ticket.php');
|
||||
require_once(INCLUDE_DIR.'class.file.php');
|
||||
|
||||
class Attachment {
|
||||
var $id;
|
||||
var $file_id;
|
||||
var $ticket_id;
|
||||
|
||||
var $info;
|
||||
|
||||
function Attachment($id,$tid=0) {
|
||||
|
||||
$sql='SELECT * FROM '.TICKET_ATTACHMENT_TABLE.' WHERE attach_id='.db_input($id);
|
||||
if($tid)
|
||||
$sql.=' AND ticket_id='.db_input($tid);
|
||||
|
||||
if(!($res=db_query($sql)) || !db_num_rows($res))
|
||||
return false;
|
||||
|
||||
$this->ht=db_fetch_array($res);
|
||||
|
||||
$this->id=$this->ht['attach_id'];
|
||||
$this->file_id=$this->ht['file_id'];
|
||||
$this->ticket_id=$this->ht['ticket_id'];
|
||||
|
||||
$this->file=null;
|
||||
$this->ticket=null;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function getId() {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
function getTicketId() {
|
||||
return $this->ticket_id;
|
||||
}
|
||||
|
||||
function getTicket() {
|
||||
if(!$this->ticket && $this->getTicketId())
|
||||
$this->ticket = Ticket::lookup($this->getTicketId());
|
||||
|
||||
return $this->ticket;
|
||||
}
|
||||
|
||||
function getFileId() {
|
||||
return $this->file_id;
|
||||
}
|
||||
|
||||
function getFile() {
|
||||
if(!$this->file && $this->getFileId())
|
||||
$this->file = AttachmentFile::lookup($this->getFileId());
|
||||
|
||||
return $this->file;
|
||||
}
|
||||
|
||||
function getCreateDate() {
|
||||
return $this->ht['created'];
|
||||
}
|
||||
|
||||
function getHashtable() {
|
||||
return $this->ht;
|
||||
}
|
||||
|
||||
function getInfo() {
|
||||
return $this->getHashtable();
|
||||
}
|
||||
|
||||
/* Static functions */
|
||||
function getIdByFileHash($hash, $tid=0) {
|
||||
$sql='SELECT attach_id FROM '.TICKET_ATTACHMENT_TABLE.' a '
|
||||
.' INNER JOIN '.FILE_TABLE.' f ON(f.id=a.file_id) '
|
||||
.' WHERE f.hash='.db_input($hash);
|
||||
if($tid)
|
||||
$sql.=' AND a.ticket_id='.db_input($tid);
|
||||
|
||||
return db_result(db_query($sql));
|
||||
}
|
||||
|
||||
function lookup($id,$tid=0) {
|
||||
$id=is_numeric($id)?$id:self::getIdByFileHash($hash,$tid);
|
||||
|
||||
return ($id && is_numeric($id) && ($attach = new Attachment($id,$tid)) && $attach->getId()==$id)?$attach:null;
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
66
include/class.banlist.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/*********************************************************************
|
||||
class.banlist.php
|
||||
|
||||
Banned email addresses handle.
|
||||
|
||||
Peter Rotich <peter@osticket.com>
|
||||
Copyright (c) 2006-2012 osTicket
|
||||
http://www.osticket.com
|
||||
|
||||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||||
See LICENSE.TXT for details.
|
||||
|
||||
vim: expandtab sw=4 ts=4 sts=4:
|
||||
**********************************************************************/
|
||||
|
||||
require_once "class.filter.php";
|
||||
|
||||
class Banlist {
|
||||
|
||||
function add($email,$submitter='') {
|
||||
return self::getSystemBanList()->addRule('email','equal',$email);
|
||||
}
|
||||
|
||||
function remove($email) {
|
||||
return self::getSystemBanList()->removeRule('email','equal',$email);
|
||||
}
|
||||
|
||||
function isbanned($email) {
|
||||
return EmailFilter::isBanned($email);
|
||||
}
|
||||
|
||||
function includes($email) {
|
||||
return self::getSystemBanList()->containsRule('email','equal',$email);
|
||||
}
|
||||
|
||||
function ensureSystemBanList() {
|
||||
|
||||
if (!($id=Filter::getIdByName('SYSTEM BAN LIST')))
|
||||
$id=self::createSystemBanList();
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
function createSystemBanList() {
|
||||
# XXX: Filter::create should return the ID!!!
|
||||
$errors=array();
|
||||
return Filter::create(array(
|
||||
'execorder' => 99,
|
||||
'name' => 'SYSTEM BAN LIST',
|
||||
'isactive' => 1,
|
||||
'match_all_rules' => false,
|
||||
'reject_email' => true,
|
||||
'rules' => array(),
|
||||
'notes' => 'Internal list for email banning. Do not remove'
|
||||
), $errors);
|
||||
}
|
||||
|
||||
function getSystemBanList() {
|
||||
return new Filter(self::ensureSystemBanList());
|
||||
}
|
||||
|
||||
function getFilter() {
|
||||
return self::getSystemBanList();
|
||||
}
|
||||
}
|
||||
272
include/class.canned.php
Normal file
@@ -0,0 +1,272 @@
|
||||
<?php
|
||||
/*********************************************************************
|
||||
class.canned.php
|
||||
|
||||
Canned Responses AKA Premade replies
|
||||
|
||||
Peter Rotich <peter@osticket.com>
|
||||
Copyright (c) 2006-2012 osTicket
|
||||
http://www.osticket.com
|
||||
|
||||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||||
See LICENSE.TXT for details.
|
||||
|
||||
vim: expandtab sw=4 ts=4 sts=4:
|
||||
**********************************************************************/
|
||||
include_once(INCLUDE_DIR.'class.file.php');
|
||||
|
||||
class Canned {
|
||||
var $id;
|
||||
var $ht;
|
||||
|
||||
var $attachments;
|
||||
|
||||
function Canned($id){
|
||||
$this->id=0;
|
||||
$this->load($id);
|
||||
}
|
||||
|
||||
function load($id=0) {
|
||||
|
||||
if(!$id && !($id=$this->getId()))
|
||||
return false;
|
||||
|
||||
$sql='SELECT canned.*, count(attach.file_id) as attachments '
|
||||
.' FROM '.CANNED_TABLE.' canned '
|
||||
.' LEFT JOIN '.CANNED_ATTACHMENT_TABLE.' attach ON (attach.canned_id=canned.canned_id) '
|
||||
.' WHERE canned.canned_id='.db_input($id);
|
||||
if(!($res=db_query($sql)) || !db_num_rows($res))
|
||||
return false;
|
||||
|
||||
|
||||
$this->ht = db_fetch_array($res);
|
||||
$this->id = $this->ht['canned_id'];
|
||||
$this->attachments = array();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function reload() {
|
||||
return $this->load();
|
||||
}
|
||||
|
||||
function getId(){
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
function isEnabled() {
|
||||
return ($this->ht['isenabled']);
|
||||
}
|
||||
|
||||
function isActive(){
|
||||
return $this->isEnabled();
|
||||
}
|
||||
|
||||
|
||||
function getTitle() {
|
||||
return $this->ht['title'];
|
||||
}
|
||||
|
||||
function getResponse() {
|
||||
return $this->ht['response'];
|
||||
}
|
||||
|
||||
function getReply() {
|
||||
return $this->getResponse();
|
||||
}
|
||||
|
||||
function getNotes() {
|
||||
return $this->ht['notes'];
|
||||
}
|
||||
|
||||
function getDeptId(){
|
||||
return $this->ht['dept_id'];
|
||||
}
|
||||
|
||||
function getHashtable() {
|
||||
return $this->ht;
|
||||
}
|
||||
|
||||
function getInfo() {
|
||||
return $this->getHashtable();
|
||||
}
|
||||
|
||||
function update($vars, &$errors) {
|
||||
|
||||
if(!$this->save($this->getId(),$vars,$errors))
|
||||
return false;
|
||||
|
||||
$this->reload();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function getNumAttachments() {
|
||||
return $this->ht['attachments'];
|
||||
}
|
||||
|
||||
function getAttachments() {
|
||||
|
||||
if(!$this->attachments && $this->getNumAttachments()) {
|
||||
|
||||
$sql='SELECT f.id, f.size, f.hash, f.name '
|
||||
.' FROM '.FILE_TABLE.' f '
|
||||
.' INNER JOIN '.CANNED_ATTACHMENT_TABLE.' a ON(f.id=a.file_id) '
|
||||
.' WHERE a.canned_id='.db_input($this->getId());
|
||||
|
||||
$this->attachments = array();
|
||||
if(($res=db_query($sql)) && db_num_rows($res)) {
|
||||
while($rec=db_fetch_array($res)) {
|
||||
$rec['key'] =md5($rec['id'].session_id().$rec['hash']);
|
||||
$this->attachments[] = $rec;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->attachments;
|
||||
}
|
||||
/*
|
||||
@files is an array - hash table of multiple attachments.
|
||||
*/
|
||||
function uploadAttachments($files) {
|
||||
|
||||
foreach($files as $file) {
|
||||
if(($fileId=is_numeric($file)?$file:AttachmentFile::upload($file)) && is_numeric($fileId)) {
|
||||
$sql ='INSERT INTO '.CANNED_ATTACHMENT_TABLE
|
||||
.' SET canned_id='.db_input($this->getId()).', file_id='.db_input($fileId);
|
||||
if(db_query($sql)) $i++;
|
||||
}
|
||||
}
|
||||
|
||||
if($i) $this->reload();
|
||||
|
||||
return $i;
|
||||
}
|
||||
|
||||
function deleteAttachment($fileId) {
|
||||
|
||||
$sql='DELETE FROM '.CANNED_ATTACHMENT_TABLE
|
||||
.' WHERE canned_id='.db_input($this->getId())
|
||||
.' AND file_id='.db_input($fileId)
|
||||
.' LIMIT 1';
|
||||
|
||||
if(!db_query($sql) || !db_affected_rows())
|
||||
return false;
|
||||
|
||||
|
||||
if(($file=AttachmentFile::lookup($fileId)) && !$file->isInuse())
|
||||
$file->delete();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function deleteAttachments(){
|
||||
|
||||
$deleted=0;
|
||||
if(($attachments = $this->getAttachments())) {
|
||||
foreach($attachments as $attachment)
|
||||
if($attachment['id'] && $this->deleteAttachment($attachment['id']))
|
||||
$deleted++;
|
||||
}
|
||||
|
||||
return $deleted;
|
||||
}
|
||||
|
||||
function delete(){
|
||||
|
||||
$sql='DELETE FROM '.CANNED_TABLE.' WHERE canned_id='.db_input($this->getId()).' LIMIT 1';
|
||||
if(db_query($sql) && ($num=db_affected_rows())) {
|
||||
$this->deleteAttachments();
|
||||
}
|
||||
|
||||
return $num;
|
||||
}
|
||||
|
||||
/*** Static functions ***/
|
||||
function lookup($id){
|
||||
return ($id && is_numeric($id) && ($c= new Canned($id)) && $c->getId()==$id)?$c:null;
|
||||
}
|
||||
|
||||
function create($vars,&$errors) {
|
||||
return self::save(0,$vars,$errors);
|
||||
}
|
||||
|
||||
function getIdByTitle($titke) {
|
||||
$sql='SELECT canned_id FROM '.CANNED_TABLE.' WHERE title='.db_input($title);
|
||||
if(($res=db_query($sql)) && db_num_rows($res))
|
||||
list($id)=db_fetch_row($res);
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
function getCannedResponses($deptId=0, $explicit=false) {
|
||||
|
||||
$sql='SELECT canned_id, title FROM '.CANNED_TABLE;
|
||||
if($deptId){
|
||||
$sql.=' WHERE dept_id='.db_input($deptId);
|
||||
if(!$explicit)
|
||||
$sql.=' OR dept_id=0';
|
||||
}
|
||||
$sql.=' ORDER BY title';
|
||||
|
||||
$responses = array();
|
||||
if(($res=db_query($sql)) && db_num_rows($res)) {
|
||||
while(list($id,$title)=db_fetch_row($res))
|
||||
$responses[$id]=$title;
|
||||
}
|
||||
|
||||
return $responses;
|
||||
}
|
||||
|
||||
function responsesByDeptId($deptId, $explicit=false) {
|
||||
return self::getCannedResponses($deptId, $explicit);
|
||||
}
|
||||
|
||||
function save($id,$vars,&$errors) {
|
||||
|
||||
//We're stripping html tags - until support is added to tickets.
|
||||
$vars['title']=Format::striptags(trim($vars['title']));
|
||||
$vars['response']=Format::striptags(trim($vars['response']));
|
||||
$vars['notes']=Format::striptags(trim($vars['notes']));
|
||||
|
||||
if($id && $id!=$vars['id'])
|
||||
$errors['err']='Internal error. Try again';
|
||||
|
||||
if(!$vars['title'])
|
||||
$errors['title']='Title required';
|
||||
elseif(strlen($vars['title'])<3)
|
||||
$errors['title']='Title is too short. 3 chars minimum';
|
||||
elseif(($cid=self::getIdByTitle($vars['title'])) && $cid!=$id)
|
||||
$errors['title']='Title already exists';
|
||||
|
||||
if(!$vars['response'])
|
||||
$errors['response']='Response text required';
|
||||
|
||||
if($errors) return false;
|
||||
|
||||
$sql=' updated=NOW() '.
|
||||
',dept_id='.db_input($vars['dept_id']?$vars['dept_id']:0).
|
||||
',isenabled='.db_input($vars['isenabled']).
|
||||
',title='.db_input($vars['title']).
|
||||
',response='.db_input($vars['response']).
|
||||
',notes='.db_input($vars['notes']);
|
||||
|
||||
if($id) {
|
||||
$sql='UPDATE '.CANNED_TABLE.' SET '.$sql.' WHERE canned_id='.db_input($id);
|
||||
if(db_query($sql))
|
||||
return true;
|
||||
|
||||
$errors['err']='Unable to update canned reply.';
|
||||
|
||||
} else {
|
||||
$sql='INSERT INTO '.CANNED_TABLE.' SET '.$sql.',created=NOW()';
|
||||
if(db_query($sql) && ($id=db_insert_id()))
|
||||
return $id;
|
||||
|
||||
$errors['err']='Unable to create the canned reply. Internal error';
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
?>
|
||||
54
include/class.captcha.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/*********************************************************************
|
||||
class.captcha.php
|
||||
|
||||
Very basic captcha class.
|
||||
|
||||
Peter Rotich <peter@osticket.com>
|
||||
Copyright (c) 2006-2012 osTicket
|
||||
http://www.osticket.com
|
||||
|
||||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||||
See LICENSE.TXT for details.
|
||||
|
||||
vim: expandtab sw=4 ts=4 sts=4:
|
||||
**********************************************************************/
|
||||
class Captcha {
|
||||
var $hash;
|
||||
var $bgimages=array('cottoncandy.png','grass.png','ripple.png','silk.png','whirlpool.png',
|
||||
'bubbles.png','crackle.png','lines.png','sand.png','snakeskin.png');
|
||||
var $font = 10;
|
||||
function Captcha($len=6,$font=7,$bg=''){
|
||||
|
||||
$this->hash = strtoupper(substr(md5(rand(0, 9999)),rand(0, 24),$len));
|
||||
$this->font = $font;
|
||||
|
||||
if($bg && !is_dir($bg)){ //bg file provided?
|
||||
$this->bgimg=$bg;
|
||||
}else{ //assume dir provided or defaults to local.
|
||||
$this->bgimg=rtrim($bg,'/').'/'.$this->bgimages[array_rand($this->bgimages, 1)];
|
||||
}
|
||||
}
|
||||
|
||||
function getImage(){
|
||||
|
||||
if(!extension_loaded('gd') || !function_exists('gd_info')) //GD ext required.
|
||||
return;
|
||||
|
||||
$_SESSION['captcha'] =''; //Clear
|
||||
|
||||
list($w,$h) = getimagesize($this->bgimg);
|
||||
$x = round(($w/2)-((strlen($this->hash)*imagefontwidth($this->font))/2), 1);
|
||||
$y = round(($h/2)-(imagefontheight($this->font)/2));
|
||||
|
||||
$img= imagecreatefrompng($this->bgimg);
|
||||
imagestring($img,$this->font, $x, $y,$this->hash,imagecolorallocate($img,0, 0, 0));
|
||||
|
||||
Header ("(captcha-content-type:) image/png");
|
||||
imagepng($img);
|
||||
imagedestroy($img);
|
||||
$_SESSION['captcha'] = md5($this->hash);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
167
include/class.category.php
Normal file
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
/*********************************************************************
|
||||
class.category.php
|
||||
|
||||
Backend support for article categories.
|
||||
|
||||
Copyright (c) 2006-2012 osTicket
|
||||
http://www.osticket.com
|
||||
|
||||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||||
See LICENSE.TXT for details.
|
||||
|
||||
vim: expandtab sw=4 ts=4 sts=4:
|
||||
**********************************************************************/
|
||||
|
||||
class Category {
|
||||
var $id;
|
||||
var $ht;
|
||||
|
||||
function Category($id) {
|
||||
$this->id=0;
|
||||
$this->load($id);
|
||||
}
|
||||
|
||||
function load($id) {
|
||||
|
||||
$sql=' SELECT cat.*,count(faq.faq_id) as faqs '
|
||||
.' FROM '.FAQ_CATEGORY_TABLE.' cat '
|
||||
.' LEFT JOIN '.FAQ_TABLE.' faq ON(faq.category_id=cat.category_id) '
|
||||
.' WHERE cat.category_id='.db_input($id)
|
||||
.' GROUP BY cat.category_id';
|
||||
|
||||
if (!($res=db_query($sql)) || !db_num_rows($res))
|
||||
return false;
|
||||
|
||||
$this->ht = db_fetch_array($res);
|
||||
$this->id = $this->ht['category_id'];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function reload() {
|
||||
return $this->load($this->getId());
|
||||
}
|
||||
|
||||
/* ------------------> Getter methods <--------------------- */
|
||||
function getId() { return $this->id; }
|
||||
function getName() { return $this->ht['name']; }
|
||||
function getNumFAQs() { return $this->ht['faqs']; }
|
||||
function getDescription() { return $this->ht['description']; }
|
||||
function getNotes() { return $this->ht['notes']; }
|
||||
function getCreateDate() { return $this->ht['created']; }
|
||||
function getUpdateDate() { return $this->ht['updated']; }
|
||||
|
||||
function isPublic() { return ($this->ht['ispublic']); }
|
||||
function getHashtable() { return $this->ht; }
|
||||
|
||||
/* ------------------> Setter methods <--------------------- */
|
||||
function setName($name) { $this->ht['name']=$name; }
|
||||
function setNotes($notes) { $this->ht['notes']=$notes; }
|
||||
function setDescription($desc) { $this->ht['description']=$desc; }
|
||||
|
||||
/* --------------> Database access methods <---------------- */
|
||||
function update($vars, &$errors) {
|
||||
|
||||
if(!$this->save($this->getId(), $vars, $errors))
|
||||
return false;
|
||||
|
||||
//TODO: move FAQs if requested.
|
||||
|
||||
$this->reload();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function delete() {
|
||||
|
||||
$sql='DELETE FROM '.FAQ_CATEGORY_TABLE
|
||||
.' WHERE category_id='.db_input($this->getId())
|
||||
.' LIMIT 1';
|
||||
if(db_query($sql) && ($num=db_affected_rows())) {
|
||||
db_query('DELETE FROM '.FAQ_TABLE
|
||||
.' WHERE category_id='.db_input($this->getId()));
|
||||
|
||||
}
|
||||
|
||||
return $num;
|
||||
}
|
||||
|
||||
/* ------------------> Static methods <--------------------- */
|
||||
|
||||
function lookup($id) {
|
||||
return ($id && is_numeric($id) && ($c = new Category($id)))?$c:null;
|
||||
}
|
||||
|
||||
function findIdByName($name) {
|
||||
$sql='SELECT category_id FROM '.FAQ_CATEGORY_TABLE.' WHERE name='.db_input($name);
|
||||
list($id) = db_fetch_row(db_query($sql));
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
function findByName($name) {
|
||||
if(($id=self::findIdByName($name)))
|
||||
return new Category($id);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function validate($vars, &$errors) {
|
||||
return self::save(0, $vars, $errors,true);
|
||||
}
|
||||
|
||||
function create($vars, &$errors) {
|
||||
return self::save(0, $vars, $errors);
|
||||
}
|
||||
|
||||
function save($id, $vars, &$errors, $validation=false) {
|
||||
|
||||
//Cleanup.
|
||||
$vars['name']=Format::striptags(trim($vars['name']));
|
||||
|
||||
//validate
|
||||
if($id && $id!=$vars['id'])
|
||||
$errors['err']='Internal error. Try again';
|
||||
|
||||
if(!$vars['name'])
|
||||
$errors['name']='Category name is required';
|
||||
elseif(strlen($vars['name'])<3)
|
||||
$errors['name']='Name is too short. 3 chars minimum';
|
||||
elseif(($cid=self::findIdByName($vars['name'])) && $cid!=$id)
|
||||
$errors['name']='Category already exists';
|
||||
|
||||
if(!$vars['description'])
|
||||
$errors['description']='Category description is required';
|
||||
|
||||
if($errors) return false;
|
||||
|
||||
/* validation only */
|
||||
if($validation) return true;
|
||||
|
||||
//save
|
||||
$sql=' updated=NOW() '.
|
||||
',ispublic='.db_input(isset($vars['ispublic'])?$vars['ispublic']:0).
|
||||
',name='.db_input($vars['name']).
|
||||
',description='.db_input(Format::safe_html($vars['description'])).
|
||||
',notes='.db_input($vars['notes']);
|
||||
|
||||
if($id) {
|
||||
$sql='UPDATE '.FAQ_CATEGORY_TABLE.' SET '.$sql.' WHERE category_id='.db_input($id);
|
||||
if(db_query($sql))
|
||||
return true;
|
||||
|
||||
$errors['err']='Unable to update FAQ category.';
|
||||
|
||||
} else {
|
||||
$sql='INSERT INTO '.FAQ_CATEGORY_TABLE.' SET '.$sql.',created=NOW()';
|
||||
if(db_query($sql) && ($id=db_insert_id()))
|
||||
return $id;
|
||||
|
||||
$errors['err']='Unable to create FAQ category. Internal error';
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
?>
|
||||
93
include/class.client.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
/*********************************************************************
|
||||
class.client.php
|
||||
|
||||
Handles everything about client
|
||||
|
||||
The class will undergo major changes one client's accounts are used.
|
||||
At the moment we will play off the email + ticket ID authentication.
|
||||
|
||||
Peter Rotich <peter@osticket.com>
|
||||
Copyright (c) 2006-2012 osTicket
|
||||
http://www.osticket.com
|
||||
|
||||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||||
See LICENSE.TXT for details.
|
||||
|
||||
vim: expandtab sw=4 ts=4 sts=4:
|
||||
**********************************************************************/
|
||||
|
||||
class Client {
|
||||
|
||||
|
||||
var $id;
|
||||
var $fullname;
|
||||
var $username;
|
||||
var $passwd;
|
||||
var $email;
|
||||
|
||||
|
||||
var $udata;
|
||||
var $ticket_id;
|
||||
var $ticketID;
|
||||
|
||||
function Client($email,$id){
|
||||
$this->id =0;
|
||||
$this->load($id,$email);
|
||||
}
|
||||
|
||||
function isClient(){
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
function load($id,$email=''){
|
||||
|
||||
$sql='SELECT ticket_id,ticketID,name,email FROM '.TICKET_TABLE.' WHERE ticketID='.db_input($id);
|
||||
if($email){ //don't validate...using whatever is entered.
|
||||
$sql.=' AND email='.db_input($email);
|
||||
}
|
||||
$res=db_query($sql);
|
||||
if(!$res || !db_num_rows($res))
|
||||
return NULL;
|
||||
|
||||
$row=db_fetch_array($res);
|
||||
$this->udata=$row;
|
||||
$this->id = $row['ticketID']; //placeholder
|
||||
$this->ticket_id = $row['ticket_id'];
|
||||
$this->ticketID = $row['ticketID'];
|
||||
$this->fullname = ucfirst($row['name']);
|
||||
$this->username = $row['email'];
|
||||
$this->email = $row['email'];
|
||||
|
||||
return($this->id);
|
||||
}
|
||||
|
||||
|
||||
function getId(){
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
function getEmail(){
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
function getUserName(){
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
function getName(){
|
||||
return $this->fullname;
|
||||
}
|
||||
|
||||
function getTicketID() {
|
||||
return $this->ticketID;
|
||||
}
|
||||
|
||||
/* ------------- Static ---------------*/
|
||||
function lookup($id, $email) {
|
||||
return ($id && is_numeric($id) && ($c=new Client($id,$email)) && $c->getId()==$id)?$c:null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
841
include/class.config.php
Normal file
@@ -0,0 +1,841 @@
|
||||
<?php
|
||||
/*********************************************************************
|
||||
class.config.php
|
||||
|
||||
osTicket config info manager.
|
||||
|
||||
Peter Rotich <peter@osticket.com>
|
||||
Copyright (c) 2006-2012 osTicket
|
||||
http://www.osticket.com
|
||||
|
||||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||||
See LICENSE.TXT for details.
|
||||
|
||||
vim: expandtab sw=4 ts=4 sts=4:
|
||||
**********************************************************************/
|
||||
|
||||
require_once(INCLUDE_DIR.'class.email.php');
|
||||
|
||||
class Config {
|
||||
|
||||
var $id=0;
|
||||
var $mysqltzoffset=0;
|
||||
var $config=array();
|
||||
|
||||
var $defaultDept; //Default Department
|
||||
var $defaultSLA; //Default SLA
|
||||
var $defaultEmail; //Default Email
|
||||
var $alertEmail; //Alert Email
|
||||
var $defaultSMTPEmail; //Default SMTP Email
|
||||
|
||||
function Config($id) {
|
||||
$this->load($id);
|
||||
}
|
||||
|
||||
function load($id) {
|
||||
|
||||
$sql='SELECT * FROM '.CONFIG_TABLE.' WHERE id='.db_input($id);
|
||||
if($id && ($res=db_query($sql)) && db_num_rows($res)) {
|
||||
$this->config=db_fetch_array($res);
|
||||
$this->id=$this->config['id'];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//Initialize some default values.
|
||||
function init() {
|
||||
list($mysqltz)=db_fetch_row(db_query('SELECT @@session.time_zone '));
|
||||
$this->setMysqlTZ($mysqltz);
|
||||
}
|
||||
|
||||
function reload() {
|
||||
if($this->load($this->id))
|
||||
$this->init();
|
||||
}
|
||||
|
||||
|
||||
function isHelpDeskOffline() {
|
||||
return !$this->isSystemOnline();
|
||||
}
|
||||
|
||||
function isSystemOnline() {
|
||||
return ($this->config['isonline']);
|
||||
}
|
||||
|
||||
function isKnowledgebaseEnabled() {
|
||||
|
||||
require_once(INCLUDE_DIR.'class.faq.php');
|
||||
return ($this->config['enable_kb'] && FAQ::countPublishedFAQs());
|
||||
}
|
||||
|
||||
function getVersion() {
|
||||
return '1.7 DPR';
|
||||
}
|
||||
|
||||
function getSchemaVersion() {
|
||||
return $this->config['schema_signature'];
|
||||
}
|
||||
|
||||
function setMysqlTZ($tz) {
|
||||
//TODO: Combine the 2 replace regex
|
||||
if($tz=='SYSTEM')
|
||||
$this->mysqltzoffset=preg_replace('/([+-]\d{2})(\d{2})/','\1',date('O'));
|
||||
else
|
||||
$this->mysqltzoffset=preg_replace('/([+-]\d{2})(:)(\d{2})/','\1',$tz);
|
||||
}
|
||||
|
||||
function getMysqlTZoffset() {
|
||||
return $this->mysqltzoffset;
|
||||
}
|
||||
|
||||
/* Date & Time Formats */
|
||||
function observeDaylightSaving() {
|
||||
return ($this->config['enable_daylight_saving']);
|
||||
}
|
||||
function getTimeFormat() {
|
||||
return $this->config['time_format'];
|
||||
}
|
||||
function getDateFormat() {
|
||||
return $this->config['date_format'];
|
||||
}
|
||||
|
||||
function getDateTimeFormat() {
|
||||
return $this->config['datetime_format'];
|
||||
}
|
||||
|
||||
function getDayDateTimeFormat() {
|
||||
return $this->config['daydatetime_format'];
|
||||
}
|
||||
|
||||
function getId() {
|
||||
return $this->config['id'];
|
||||
}
|
||||
|
||||
function getTitle() {
|
||||
return $this->config['helpdesk_title'];
|
||||
}
|
||||
|
||||
function getUrl() {
|
||||
return $this->config['helpdesk_url'];
|
||||
}
|
||||
|
||||
function getBaseUrl() { //Same as above with no trailing slash.
|
||||
return rtrim($this->getUrl(),'/');
|
||||
}
|
||||
|
||||
function getConfig() {
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
function getTZOffset() {
|
||||
return $this->config['timezone_offset'];
|
||||
}
|
||||
|
||||
function getPageSize() {
|
||||
return $this->config['max_page_size'];
|
||||
}
|
||||
|
||||
function getGracePeriod() {
|
||||
return $this->config['overdue_grace_period'];
|
||||
}
|
||||
|
||||
function getPasswdResetPeriod() {
|
||||
return $this->config['passwd_reset_period'];
|
||||
}
|
||||
|
||||
function getClientTimeout() {
|
||||
return $this->getClientSessionTimeout();
|
||||
}
|
||||
|
||||
function getClientSessionTimeout() {
|
||||
return $this->config['client_session_timeout']*60;
|
||||
}
|
||||
|
||||
function getClientLoginTimeout() {
|
||||
return $this->config['client_login_timeout']*60;
|
||||
}
|
||||
|
||||
function getClientMaxLogins() {
|
||||
return $this->config['client_max_logins'];
|
||||
}
|
||||
|
||||
function getStaffTimeout() {
|
||||
return $this->getStaffSessionTimeout();
|
||||
}
|
||||
|
||||
function getStaffSessionTimeout() {
|
||||
return $this->config['staff_session_timeout']*60;
|
||||
}
|
||||
|
||||
function getStaffLoginTimeout() {
|
||||
return $this->config['staff_login_timeout']*60;
|
||||
}
|
||||
|
||||
function getStaffMaxLogins() {
|
||||
return $this->config['staff_max_logins'];
|
||||
}
|
||||
|
||||
function getLockTime() {
|
||||
return $this->config['autolock_minutes'];
|
||||
}
|
||||
|
||||
function getDefaultDeptId() {
|
||||
return $this->config['default_dept_id'];
|
||||
}
|
||||
|
||||
function getDefaultDept() {
|
||||
|
||||
if(!$this->defaultDept && $this->getDefaultDeptId())
|
||||
$this->defaultDept=Dept::lookup($this->getDefaultDeptId());
|
||||
|
||||
return $this->defaultDept;
|
||||
}
|
||||
|
||||
function getDefaultEmailId() {
|
||||
return $this->config['default_email_id'];
|
||||
}
|
||||
|
||||
function getDefaultEmail() {
|
||||
|
||||
if(!$this->defaultEmail && $this->getDefaultEmailId())
|
||||
$this->defaultEmail=Email::lookup($this->getDefaultEmailId());
|
||||
|
||||
return $this->defaultEmail;
|
||||
}
|
||||
|
||||
function getDefaultEmailAddress() {
|
||||
$email=$this->getDefaultEmail();
|
||||
return $email?$email->getAddress():null;
|
||||
}
|
||||
|
||||
function getDefaultSLAId() {
|
||||
return $this->config['default_sla_id'];
|
||||
}
|
||||
|
||||
function getDefaultSLA() {
|
||||
|
||||
if(!$this->defaultSLA && $this->getDefaultSLAId())
|
||||
$this->defaultSLA=SLA::lookup($this->getDefaultSLAId());
|
||||
|
||||
return $this->defaultSLA;
|
||||
}
|
||||
|
||||
function getAlertEmailId() {
|
||||
return $this->config['alert_email_id'];
|
||||
}
|
||||
|
||||
function getAlertEmail() {
|
||||
|
||||
if(!$this->alertEmail && $this->config['alert_email_id'])
|
||||
$this->alertEmail= new Email($this->config['alert_email_id']);
|
||||
return $this->alertEmail;
|
||||
}
|
||||
|
||||
function getDefaultSMTPEmail() {
|
||||
|
||||
if(!$this->defaultSMTPEmail && $this->config['default_smtp_id'])
|
||||
$this->defaultSMTPEmail= new Email($this->config['default_smtp_id']);
|
||||
return $this->defaultSMTPEmail;
|
||||
}
|
||||
|
||||
function allowSMTPSpoofing() {
|
||||
return $this->config['spoof_default_smtp'];
|
||||
}
|
||||
|
||||
function getDefaultPriorityId() {
|
||||
return $this->config['default_priority_id'];
|
||||
}
|
||||
|
||||
function getDefaultTemplateId() {
|
||||
return $this->config['default_template_id'];
|
||||
}
|
||||
|
||||
function getDefaultTemplate() {
|
||||
|
||||
if(!$this->defaultTemplate && $this->getDefaultTemplateId())
|
||||
$this->defaultTemplate = Template::lookup($this->getDefaultTemplateId());
|
||||
|
||||
return $this->defaultTemplate;
|
||||
}
|
||||
|
||||
function getMaxOpenTickets() {
|
||||
return $this->config['max_open_tickets'];
|
||||
}
|
||||
|
||||
function getMaxFileSize() {
|
||||
return $this->config['max_file_size'];
|
||||
}
|
||||
|
||||
function getMaxFileUploads() {
|
||||
return $this->config['max_staff_file_uploads'];
|
||||
}
|
||||
|
||||
function getLogLevel() {
|
||||
return $this->config['log_level'];
|
||||
}
|
||||
|
||||
function getLogGracePeriod() {
|
||||
return $this->config['log_graceperiod'];
|
||||
}
|
||||
|
||||
function logTicketActivity() {
|
||||
return $this->config['log_ticket_activity'];
|
||||
}
|
||||
|
||||
function clickableURLS() {
|
||||
return ($this->config['clickable_urls']);
|
||||
}
|
||||
|
||||
function canFetchMail() {
|
||||
return ($this->config['enable_mail_fetch']);
|
||||
}
|
||||
|
||||
function enableStaffIPBinding() {
|
||||
return ($this->config['staff_ip_binding']);
|
||||
}
|
||||
|
||||
function enableCaptcha() {
|
||||
return (extension_loaded('gd') && function_exists('gd_info') && $this->config['enable_captcha']);
|
||||
}
|
||||
|
||||
function enableAutoCron() {
|
||||
return ($this->config['enable_auto_cron']);
|
||||
}
|
||||
|
||||
function enableEmailPiping() {
|
||||
return ($this->config['enable_email_piping']);
|
||||
}
|
||||
|
||||
function allowPriorityChange() {
|
||||
return ($this->config['allow_priority_change']);
|
||||
}
|
||||
|
||||
|
||||
function useEmailPriority() {
|
||||
return ($this->config['use_email_priority']);
|
||||
}
|
||||
|
||||
function getAdminEmail() {
|
||||
return $this->config['admin_email'];
|
||||
}
|
||||
|
||||
function getReplySeparator() {
|
||||
return $this->config['reply_separator'];
|
||||
}
|
||||
|
||||
function stripQuotedReply() {
|
||||
return ($this->config['strip_quoted_reply']);
|
||||
}
|
||||
|
||||
function saveEmailHeaders() {
|
||||
return true; //No longer an option...hint: big plans for headers coming!!
|
||||
}
|
||||
|
||||
function useRandomIds() {
|
||||
return ($this->config['random_ticket_ids']);
|
||||
}
|
||||
|
||||
/* autoresponders & Alerts */
|
||||
function autoRespONNewTicket() {
|
||||
return ($this->config['ticket_autoresponder']);
|
||||
}
|
||||
|
||||
function autoRespONNewMessage() {
|
||||
return ($this->config['message_autoresponder']);
|
||||
}
|
||||
|
||||
function notifyONNewStaffTicket() {
|
||||
return ($this->config['ticket_notice_active']);
|
||||
}
|
||||
|
||||
function alertONNewMessage() {
|
||||
return ($this->config['message_alert_active']);
|
||||
}
|
||||
|
||||
function alertLastRespondentONNewMessage() {
|
||||
return ($this->config['message_alert_laststaff']);
|
||||
}
|
||||
|
||||
function alertAssignedONNewMessage() {
|
||||
return ($this->config['message_alert_assigned']);
|
||||
}
|
||||
|
||||
function alertDeptManagerONNewMessage() {
|
||||
return ($this->config['message_alert_dept_manager']);
|
||||
}
|
||||
|
||||
function alertONNewNote() {
|
||||
return ($this->config['note_alert_active']);
|
||||
}
|
||||
|
||||
function alertLastRespondentONNewNote() {
|
||||
return ($this->config['note_alert_laststaff']);
|
||||
}
|
||||
|
||||
function alertAssignedONNewNote() {
|
||||
return ($this->config['note_alert_assigned']);
|
||||
}
|
||||
|
||||
function alertDeptManagerONNewNote() {
|
||||
return ($this->config['note_alert_dept_manager']);
|
||||
}
|
||||
|
||||
function alertONNewTicket() {
|
||||
return ($this->config['ticket_alert_active']);
|
||||
}
|
||||
|
||||
function alertAdminONNewTicket() {
|
||||
return ($this->config['ticket_alert_admin']);
|
||||
}
|
||||
|
||||
function alertDeptManagerONNewTicket() {
|
||||
return ($this->config['ticket_alert_dept_manager']);
|
||||
}
|
||||
|
||||
function alertDeptMembersONNewTicket() {
|
||||
return ($this->config['ticket_alert_dept_members']);
|
||||
}
|
||||
|
||||
function alertONTransfer() {
|
||||
return ($this->config['transfer_alert_active']);
|
||||
}
|
||||
|
||||
function alertAssignedONTransfer() {
|
||||
return ($this->config['transfer_alert_assigned']);
|
||||
}
|
||||
|
||||
function alertDeptManagerONTransfer() {
|
||||
return ($this->config['transfer_alert_dept_manager']);
|
||||
}
|
||||
|
||||
function alertDeptMembersONTransfer() {
|
||||
return ($this->config['transfer_alert_dept_members']);
|
||||
}
|
||||
|
||||
function alertONAssignment() {
|
||||
return ($this->config['assigned_alert_active']);
|
||||
}
|
||||
|
||||
function alertStaffONAssignment() {
|
||||
return ($this->config['assigned_alert_staff']);
|
||||
}
|
||||
|
||||
function alertTeamLeadONAssignment() {
|
||||
return ($this->config['assigned_alert_team_lead']);
|
||||
}
|
||||
|
||||
function alertTeamMembersONAssignment() {
|
||||
return ($this->config['assigned_alert_team_members']);
|
||||
}
|
||||
|
||||
|
||||
function alertONOverdueTicket() {
|
||||
return ($this->config['overdue_alert_active']);
|
||||
}
|
||||
|
||||
function alertAssignedONOverdueTicket() {
|
||||
return ($this->config['overdue_alert_assigned']);
|
||||
}
|
||||
|
||||
function alertDeptManagerONOverdueTicket() {
|
||||
return ($this->config['overdue_alert_dept_manager']);
|
||||
}
|
||||
|
||||
function alertDeptMembersONOverdueTicket() {
|
||||
return ($this->config['overdue_alert_dept_members']);
|
||||
}
|
||||
|
||||
function autoAssignReopenedTickets() {
|
||||
return ($this->config['auto_assign_reopened_tickets']);
|
||||
}
|
||||
|
||||
function showAssignedTickets() {
|
||||
return ($this->config['show_assigned_tickets']);
|
||||
}
|
||||
|
||||
function showAnsweredTickets() {
|
||||
return ($this->config['show_answered_tickets']);
|
||||
}
|
||||
|
||||
function hideStaffName() {
|
||||
return ($this->config['hide_staff_name']);
|
||||
}
|
||||
|
||||
function sendOverLimitNotice() {
|
||||
return ($this->config['overlimit_notice_active']);
|
||||
}
|
||||
|
||||
/* Error alerts sent to admin email when enabled */
|
||||
function alertONSQLError() {
|
||||
return ($this->config['send_sql_errors']);
|
||||
}
|
||||
function alertONLoginError() {
|
||||
return ($this->config['send_login_errors']);
|
||||
}
|
||||
|
||||
function alertONMailParseError() {
|
||||
return ($this->config['send_mailparse_errors']);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Attachments */
|
||||
|
||||
function emailAttachments() {
|
||||
return ($this->config['email_attachments']);
|
||||
}
|
||||
|
||||
function allowAttachments() {
|
||||
return ($this->config['allow_attachments']);
|
||||
}
|
||||
|
||||
function allowOnlineAttachments() {
|
||||
return ($this->allowAttachments() && $this->config['allow_online_attachments']);
|
||||
}
|
||||
|
||||
function allowAttachmentsOnlogin() {
|
||||
return ($this->allowOnlineAttachments() && $this->config['allow_online_attachments_onlogin']);
|
||||
}
|
||||
|
||||
function allowEmailAttachments() {
|
||||
return ($this->allowAttachments() && $this->config['allow_email_attachments']);
|
||||
}
|
||||
|
||||
function getUploadDir() {
|
||||
return $this->config['upload_dir'];
|
||||
}
|
||||
|
||||
//simply checking if destination dir is usable..nothing to do with permission to upload!
|
||||
function canUploadFiles() {
|
||||
$dir=$this->config['upload_dir'];
|
||||
return ($dir && is_writable($dir))?TRUE:FALSE;
|
||||
}
|
||||
|
||||
function canUploadFileType($filename) {
|
||||
$ext = strtolower(preg_replace("/.*\.(.{3,4})$/", "$1", $filename));
|
||||
$allowed=$this->config['allowed_filetypes']?array_map('trim',explode(',',strtolower($this->config['allowed_filetypes']))):null;
|
||||
return ($ext && is_array($allowed) && (in_array(".$ext",$allowed) || in_array(".*",$allowed)))?TRUE:FALSE;
|
||||
}
|
||||
|
||||
function updateSettings($vars,&$errors) {
|
||||
|
||||
if(!$vars || $errors)
|
||||
return false;
|
||||
|
||||
switch(strtolower($vars['t'])) {
|
||||
case 'general':
|
||||
return $this->updateGeneralSetting($vars,$errors);
|
||||
break;
|
||||
case 'dates':
|
||||
return $this->updateDateTimeSetting($vars,$errors);
|
||||
break;
|
||||
case 'tickets':
|
||||
return $this->updateTicketsSetting($vars,$errors);
|
||||
break;
|
||||
case 'emails':
|
||||
return $this->updateEmailsSetting($vars,$errors);
|
||||
break;
|
||||
case 'attachments':
|
||||
return $this->updateAttachmentsSetting($vars,$errors);
|
||||
break;
|
||||
case 'autoresponders':
|
||||
return $this->updateAutoresponderSetting($vars,$errors);
|
||||
break;
|
||||
case 'alerts':
|
||||
return $this->updateAlertsSetting($vars,$errors);
|
||||
break;
|
||||
case 'kb':
|
||||
return $this->updateKBSetting($vars,$errors);
|
||||
break;
|
||||
default:
|
||||
$errors['err']='Unknown setting option. Get technical support.';
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function updateGeneralSetting($vars,&$errors) {
|
||||
|
||||
$f=array();
|
||||
$f['helpdesk_url']=array('type'=>'string', 'required'=>1, 'error'=>'Helpdesk URl required');
|
||||
$f['helpdesk_title']=array('type'=>'string', 'required'=>1, 'error'=>'Helpdesk title required');
|
||||
$f['default_dept_id']=array('type'=>'int', 'required'=>1, 'error'=>'Default Dept. required');
|
||||
$f['default_template_id']=array('type'=>'int', 'required'=>1, 'error'=>'You must select template.');
|
||||
$f['staff_session_timeout']=array('type'=>'int', 'required'=>1, 'error'=>'Enter idle time in minutes');
|
||||
$f['client_session_timeout']=array('type'=>'int', 'required'=>1, 'error'=>'Enter idle time in minutes');
|
||||
|
||||
if(!Validator::process($f,$vars,$errors) || $errors)
|
||||
return false;
|
||||
|
||||
$sql='UPDATE '.CONFIG_TABLE.' SET updated=NOW() '.
|
||||
',isonline='.db_input($vars['isonline']).
|
||||
',helpdesk_title='.db_input($vars['helpdesk_title']).
|
||||
',helpdesk_url='.db_input($vars['helpdesk_url']).
|
||||
',default_dept_id='.db_input($vars['default_dept_id']).
|
||||
',default_template_id='.db_input($vars['default_template_id']).
|
||||
',max_page_size='.db_input($vars['max_page_size']).
|
||||
',log_level='.db_input($vars['log_level']).
|
||||
',log_graceperiod='.db_input($vars['log_graceperiod']).
|
||||
',passwd_reset_period='.db_input($vars['passwd_reset_period']).
|
||||
',staff_max_logins='.db_input($vars['staff_max_logins']).
|
||||
',staff_login_timeout='.db_input($vars['staff_login_timeout']).
|
||||
',staff_session_timeout='.db_input($vars['staff_session_timeout']).
|
||||
',staff_ip_binding='.db_input(isset($vars['staff_ip_binding'])?1:0).
|
||||
',client_max_logins='.db_input($vars['client_max_logins']).
|
||||
',client_login_timeout='.db_input($vars['client_login_timeout']).
|
||||
',client_session_timeout='.db_input($vars['client_session_timeout']).
|
||||
',clickable_urls='.db_input(isset($vars['clickable_urls'])?1:0).
|
||||
',enable_auto_cron='.db_input(isset($vars['enable_auto_cron'])?1:0).
|
||||
' WHERE id='.$this->getId();
|
||||
|
||||
return (db_query($sql));
|
||||
}
|
||||
|
||||
function updateDateTimeSetting($vars,&$errors) {
|
||||
|
||||
$f=array();
|
||||
$f['time_format']=array('type'=>'string', 'required'=>1, 'error'=>'Time format required');
|
||||
$f['date_format']=array('type'=>'string', 'required'=>1, 'error'=>'Date format required');
|
||||
$f['datetime_format']=array('type'=>'string', 'required'=>1, 'error'=>'Datetime format required');
|
||||
$f['daydatetime_format']=array('type'=>'string', 'required'=>1, 'error'=>'Day, Datetime format required');
|
||||
$f['default_timezone_id']=array('type'=>'int', 'required'=>1, 'error'=>'Default Timezone required');
|
||||
|
||||
if(!Validator::process($f,$vars,$errors) || $errors)
|
||||
return false;
|
||||
|
||||
$sql='UPDATE '.CONFIG_TABLE.' SET updated=NOW() '.
|
||||
',time_format='.db_input($vars['time_format']).
|
||||
',date_format='.db_input($vars['date_format']).
|
||||
',datetime_format='.db_input($vars['datetime_format']).
|
||||
',daydatetime_format='.db_input($vars['daydatetime_format']).
|
||||
',default_timezone_id='.db_input($vars['default_timezone_id']).
|
||||
',enable_daylight_saving='.db_input(isset($vars['enable_daylight_saving'])?1:0).
|
||||
' WHERE id='.$this->getId();
|
||||
|
||||
return (db_query($sql));
|
||||
}
|
||||
|
||||
function updateTicketsSetting($vars,&$errors) {
|
||||
|
||||
|
||||
$f=array();
|
||||
$f['default_sla_id']=array('type'=>'int', 'required'=>1, 'error'=>'Selection required');
|
||||
$f['default_priority_id']=array('type'=>'int', 'required'=>1, 'error'=>'Selection required');
|
||||
$f['max_open_tickets']=array('type'=>'int', 'required'=>1, 'error'=>'Enter valid numeric value');
|
||||
$f['autolock_minutes']=array('type'=>'int', 'required'=>1, 'error'=>'Enter lock time in minutes');
|
||||
|
||||
|
||||
if($vars['enable_captcha']) {
|
||||
if (!extension_loaded('gd'))
|
||||
$errors['enable_captcha']='The GD extension required';
|
||||
elseif(!function_exists('imagepng'))
|
||||
$errors['enable_captcha']='PNG support required for Image Captcha';
|
||||
}
|
||||
|
||||
if(!Validator::process($f,$vars,$errors) || $errors)
|
||||
return false;
|
||||
|
||||
$sql='UPDATE '.CONFIG_TABLE.' SET updated=NOW() '.
|
||||
',random_ticket_ids='.db_input($vars['random_ticket_ids']).
|
||||
',default_priority_id='.db_input($vars['default_priority_id']).
|
||||
',default_sla_id='.db_input($vars['default_sla_id']).
|
||||
',max_open_tickets='.db_input($vars['max_open_tickets']).
|
||||
',autolock_minutes='.db_input($vars['autolock_minutes']).
|
||||
',allow_priority_change='.db_input(isset($vars['allow_priority_change'])?1:0).
|
||||
',use_email_priority='.db_input(isset($vars['use_email_priority'])?1:0).
|
||||
',enable_captcha='.db_input(isset($vars['enable_captcha'])?1:0).
|
||||
',log_ticket_activity='.db_input(isset($vars['log_ticket_activity'])?1:0).
|
||||
',auto_assign_reopened_tickets='.db_input(isset($vars['auto_assign_reopened_tickets'])?1:0).
|
||||
',show_assigned_tickets='.db_input(isset($vars['show_assigned_tickets'])?1:0).
|
||||
',show_answered_tickets='.db_input(isset($vars['show_answered_tickets'])?1:0).
|
||||
',show_related_tickets='.db_input(isset($vars['show_related_tickets'])?1:0).
|
||||
',hide_staff_name='.db_input(isset($vars['hide_staff_name'])?1:0);
|
||||
|
||||
return (db_query($sql));
|
||||
}
|
||||
|
||||
|
||||
function updateEmailsSetting($vars,&$errors) {
|
||||
|
||||
$f=array();
|
||||
$f['default_email_id']=array('type'=>'int', 'required'=>1, 'error'=>'Default email required');
|
||||
$f['alert_email_id']=array('type'=>'int', 'required'=>1, 'error'=>'Selection required');
|
||||
$f['admin_email']=array('type'=>'email', 'required'=>1, 'error'=>'System admin email required');
|
||||
|
||||
if($vars['strip_quoted_reply'] && !$vars['reply_separator'])
|
||||
$errors['reply_separator']='Reply separator required to strip quoted reply.';
|
||||
|
||||
if($vars['admin_email'] && Email::getIdByEmail($vars['admin_email'])) //Make sure admin email is not also a system email.
|
||||
$errors['admin_email']='Email already setup as system email';
|
||||
|
||||
if(!Validator::process($f,$vars,$errors) || $errors)
|
||||
return false;
|
||||
|
||||
$sql='UPDATE '.CONFIG_TABLE.' SET updated=NOW() '.
|
||||
',default_email_id='.db_input($vars['default_email_id']).
|
||||
',alert_email_id='.db_input($vars['alert_email_id']).
|
||||
',default_smtp_id='.db_input($vars['default_smtp_id']).
|
||||
',admin_email='.db_input($vars['admin_email']).
|
||||
',enable_mail_polling='.db_input(isset($vars['enable_mail_polling'])?1:0).
|
||||
',enable_email_piping='.db_input(isset($vars['enable_email_piping'])?1:0).
|
||||
',strip_quoted_reply='.db_input(isset($vars['strip_quoted_reply'])?1:0).
|
||||
',reply_separator='.db_input($vars['reply_separator']);
|
||||
|
||||
return (db_query($sql));
|
||||
}
|
||||
|
||||
function updateAttachmentsSetting($vars,&$errors) {
|
||||
|
||||
|
||||
if($vars['allow_attachments']) {
|
||||
|
||||
if(!ini_get('file_uploads'))
|
||||
$errors['err']='The \'file_uploads\' directive is disabled in php.ini';
|
||||
|
||||
if(!is_numeric($vars['max_file_size']))
|
||||
$errors['max_file_size']='Maximum file size required';
|
||||
|
||||
if(!$vars['allowed_filetypes'])
|
||||
$errors['allowed_filetypes']='Allowed file extentions required';
|
||||
|
||||
if(!($maxfileuploads=ini_get('max_file_uploads')))
|
||||
$maxfileuploads=DEFAULT_MAX_FILE_UPLOADS;
|
||||
|
||||
if(!$vars['max_user_file_uploads'] || $vars['max_user_file_uploads']>$maxfileuploads)
|
||||
$errors['max_user_file_uploads']='Invalid selection';
|
||||
|
||||
if(!$vars['max_staff_file_uploads'] || $vars['max_staff_file_uploads']>$maxfileuploads)
|
||||
$errors['max_staff_file_uploads']='Invalid selection';
|
||||
}
|
||||
|
||||
if($errors) return false;
|
||||
|
||||
$sql= 'UPDATE '.CONFIG_TABLE.' SET updated=NOW() '.
|
||||
',allow_attachments='.db_input(isset($vars['allow_attachments'])?1:0).
|
||||
',allowed_filetypes='.db_input(strtolower(preg_replace("/\n\r|\r\n|\n|\r/", '',trim($vars['allowed_filetypes'])))).
|
||||
',max_file_size='.db_input($vars['max_file_size']).
|
||||
',max_user_file_uploads='.db_input($vars['max_user_file_uploads']).
|
||||
',max_staff_file_uploads='.db_input($vars['max_staff_file_uploads']).
|
||||
',email_attachments='.db_input(isset($vars['email_attachments'])?1:0).
|
||||
',allow_email_attachments='.db_input(isset($vars['allow_email_attachments'])?1:0).
|
||||
',allow_online_attachments='.db_input(isset($vars['allow_online_attachments'])?1:0).
|
||||
',allow_online_attachments_onlogin='.db_input(isset($vars['allow_online_attachments_onlogin'])?1:0).
|
||||
' WHERE id='.db_input($this->getId());
|
||||
|
||||
|
||||
return (db_query($sql));
|
||||
|
||||
}
|
||||
|
||||
|
||||
function updateAutoresponderSetting($vars,&$errors) {
|
||||
|
||||
if($errors) return false;
|
||||
|
||||
$sql= 'UPDATE '.CONFIG_TABLE.' SET updated=NOW() '.
|
||||
',ticket_autoresponder='.db_input($vars['ticket_autoresponder']).
|
||||
',message_autoresponder='.db_input($vars['message_autoresponder']).
|
||||
',ticket_notice_active='.db_input($vars['ticket_notice_active']).
|
||||
',overlimit_notice_active='.db_input($vars['overlimit_notice_active']);
|
||||
|
||||
return (db_query($sql));
|
||||
|
||||
}
|
||||
|
||||
|
||||
function updateKBSetting($vars,&$errors) {
|
||||
|
||||
if($errors) return false;
|
||||
|
||||
$sql= 'UPDATE '.CONFIG_TABLE.' SET updated=NOW() '.
|
||||
',enable_kb='.db_input(isset($vars['enable_kb'])?1:0).
|
||||
',enable_premade='.db_input(isset($vars['enable_premade'])?1:0);
|
||||
|
||||
return (db_query($sql));
|
||||
}
|
||||
|
||||
|
||||
function updateAlertsSetting($vars,&$errors) {
|
||||
|
||||
|
||||
if($vars['ticket_alert_active']
|
||||
&& (!isset($vars['ticket_alert_admin'])
|
||||
&& !isset($vars['ticket_alert_dept_manager'])
|
||||
&& !isset($vars['ticket_alert_dept_members']))) {
|
||||
$errors['ticket_alert_active']='Select recipient(s)';
|
||||
}
|
||||
if($vars['message_alert_active']
|
||||
&& (!isset($vars['message_alert_laststaff'])
|
||||
&& !isset($vars['message_alert_assigned'])
|
||||
&& !isset($vars['message_alert_dept_manager']))) {
|
||||
$errors['message_alert_active']='Select recipient(s)';
|
||||
}
|
||||
|
||||
if($vars['note_alert_active']
|
||||
&& (!isset($vars['note_alert_laststaff'])
|
||||
&& !isset($vars['note_alert_assigned'])
|
||||
&& !isset($vars['note_alert_dept_manager']))) {
|
||||
$errors['note_alert_active']='Select recipient(s)';
|
||||
}
|
||||
|
||||
if($vars['transfer_alert_active']
|
||||
&& (!isset($vars['transfer_alert_assigned'])
|
||||
&& !isset($vars['transfer_alert_dept_manager'])
|
||||
&& !isset($vars['transfer_alert_dept_members']))) {
|
||||
$errors['transfer_alert_active']='Select recipient(s)';
|
||||
}
|
||||
|
||||
if($vars['overdue_alert_active']
|
||||
&& (!isset($vars['overdue_alert_assigned'])
|
||||
&& !isset($vars['overdue_alert_dept_manager'])
|
||||
&& !isset($vars['overdue_alert_dept_members']))) {
|
||||
$errors['overdue_alert_active']='Select recipient(s)';
|
||||
}
|
||||
|
||||
if($vars['assigned_alert_active']
|
||||
&& (!isset($vars['assigned_alert_staff'])
|
||||
&& !isset($vars['assigned_alert_team_lead'])
|
||||
&& !isset($vars['assigned_alert_team_members']))) {
|
||||
$errors['assigned_alert_active']='Select recipient(s)';
|
||||
}
|
||||
|
||||
if($errors) return false;
|
||||
|
||||
$sql= 'UPDATE '.CONFIG_TABLE.' SET updated=NOW() '.
|
||||
',ticket_notice_active='.db_input($vars['ticket_notice_active']).
|
||||
',ticket_alert_active='.db_input($vars['ticket_alert_active']).
|
||||
',ticket_alert_admin='.db_input(isset($vars['ticket_alert_admin'])?1:0).
|
||||
',ticket_alert_dept_manager='.db_input(isset($vars['ticket_alert_dept_manager'])?1:0).
|
||||
',ticket_alert_dept_members='.db_input(isset($vars['ticket_alert_dept_members'])?1:0).
|
||||
',message_alert_active='.db_input($vars['message_alert_active']).
|
||||
',message_alert_laststaff='.db_input(isset($vars['message_alert_laststaff'])?1:0).
|
||||
',message_alert_assigned='.db_input(isset($vars['message_alert_assigned'])?1:0).
|
||||
',message_alert_dept_manager='.db_input(isset($vars['message_alert_dept_manager'])?1:0).
|
||||
',note_alert_active='.db_input($vars['note_alert_active']).
|
||||
',note_alert_laststaff='.db_input(isset($vars['note_alert_laststaff'])?1:0).
|
||||
',note_alert_assigned='.db_input(isset($vars['note_alert_assigned'])?1:0).
|
||||
',note_alert_dept_manager='.db_input(isset($vars['note_alert_dept_manager'])?1:0).
|
||||
',assigned_alert_active='.db_input($vars['assigned_alert_active']).
|
||||
',assigned_alert_staff='.db_input(isset($vars['assigned_alert_staff'])?1:0).
|
||||
',assigned_alert_team_lead='.db_input(isset($vars['assigned_alert_team_lead'])?1:0).
|
||||
',assigned_alert_team_members='.db_input(isset($vars['assigned_alert_team_members'])?1:0).
|
||||
',transfer_alert_active='.db_input($vars['transfer_alert_active']).
|
||||
',transfer_alert_assigned='.db_input(isset($vars['transfer_alert_assigned'])?1:0).
|
||||
',transfer_alert_dept_manager='.db_input(isset($vars['transfer_alert_dept_manager'])?1:0).
|
||||
',transfer_alert_dept_members='.db_input(isset($var['transfer_alert_dept_members'])?1:0).
|
||||
',overdue_alert_active='.db_input($vars['overdue_alert_active']).
|
||||
',overdue_alert_assigned='.db_input(isset($vars['overdue_alert_assigned'])?1:0).
|
||||
',overdue_alert_dept_manager='.db_input(isset($vars['overdue_alert_dept_manager'])?1:0).
|
||||
',overdue_alert_dept_members='.db_input(isset($var['overdue_alert_dept_members'])?1:0).
|
||||
',send_sys_errors='.db_input(isset($vars['send_sys_errors'])?1:0).
|
||||
',send_sql_errors='.db_input(isset($vars['send_sql_errors'])?1:0).
|
||||
',send_login_errors='.db_input(isset($vars['send_login_errors'])?1:0);
|
||||
|
||||
return (db_query($sql));
|
||||
|
||||
}
|
||||
}
|
||||
?>
|
||||
43
include/class.cron.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/*********************************************************************
|
||||
class.cron.php
|
||||
|
||||
Nothing special...just a central location for all cron calls.
|
||||
|
||||
Peter Rotich <peter@osticket.com>
|
||||
Copyright (c) 2006-2012 osTicket
|
||||
http://www.osticket.com
|
||||
|
||||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||||
See LICENSE.TXT for details.
|
||||
|
||||
TODO: The plan is to make cron jobs db based.
|
||||
|
||||
vim: expandtab sw=4 ts=4 sts=4:
|
||||
**********************************************************************/
|
||||
//TODO: Make it DB based!
|
||||
class Cron {
|
||||
|
||||
function MailFetcher() {
|
||||
require_once(INCLUDE_DIR.'class.mailfetch.php');
|
||||
MailFetcher::fetchMail(); //Fetch mail..frequency is limited by email account setting.
|
||||
}
|
||||
|
||||
function TicketMonitor() {
|
||||
require_once(INCLUDE_DIR.'class.ticket.php');
|
||||
require_once(INCLUDE_DIR.'class.lock.php');
|
||||
Ticket::checkOverdue(); //Make stale tickets overdue
|
||||
TicketLock::cleanup(); //Remove expired locks
|
||||
}
|
||||
|
||||
function PurgeLogs() {
|
||||
Sys::purgeLogs();
|
||||
}
|
||||
|
||||
function run(){ //called by outside cron NOT autocron
|
||||
Cron::MailFetcher();
|
||||
Cron::TicketMonitor();
|
||||
cron::PurgeLogs();
|
||||
}
|
||||
}
|
||||
?>
|
||||
318
include/class.dept.php
Normal file
@@ -0,0 +1,318 @@
|
||||
<?php
|
||||
/*********************************************************************
|
||||
class.dept.php
|
||||
|
||||
Department class
|
||||
|
||||
Peter Rotich <peter@osticket.com>
|
||||
Copyright (c) 2006-2012 osTicket
|
||||
http://www.osticket.com
|
||||
|
||||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||||
See LICENSE.TXT for details.
|
||||
|
||||
vim: expandtab sw=4 ts=4 sts=4:
|
||||
**********************************************************************/
|
||||
class Dept {
|
||||
var $id;
|
||||
|
||||
var $email;
|
||||
var $sla;
|
||||
var $manager;
|
||||
var $ht;
|
||||
|
||||
function Dept($id){
|
||||
$this->id=0;
|
||||
$this->load($id);
|
||||
}
|
||||
|
||||
function load($id=0) {
|
||||
global $cfg;
|
||||
|
||||
if(!$id && !($id=$this->getId()))
|
||||
return false;
|
||||
|
||||
$sql='SELECT dept.*,dept.dept_id as id,dept.dept_name as name, dept.dept_signature as signature, count(staff.staff_id) as users '
|
||||
.' FROM '.DEPT_TABLE.' dept '
|
||||
.' LEFT JOIN '.STAFF_TABLE.' staff ON (dept.dept_id=staff.dept_id) '
|
||||
.' WHERE dept.dept_id='.db_input($id)
|
||||
.' GROUP BY dept.dept_id';
|
||||
|
||||
if(!($res=db_query($sql)) || !db_num_rows($res))
|
||||
return false;
|
||||
|
||||
|
||||
|
||||
$this->ht=db_fetch_array($res);
|
||||
$this->id=$this->ht['dept_id'];
|
||||
$this->email=$this->sla=$this->manager=null;
|
||||
$this->getEmail(); //Auto load email struct.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function reload(){
|
||||
return $this->load();
|
||||
}
|
||||
|
||||
function getId(){
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
function getName(){
|
||||
return $this->ht['name'];
|
||||
}
|
||||
|
||||
|
||||
function getEmailId(){
|
||||
return $this->ht['email_id'];
|
||||
}
|
||||
|
||||
function getEmail(){
|
||||
|
||||
if(!$this->email && $this->getEmailId())
|
||||
$this->email=Email::lookup($this->getEmailId());
|
||||
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
function getNumStaff(){
|
||||
return $this->ht['users'];
|
||||
}
|
||||
|
||||
function getNumMembers(){
|
||||
return $this->getNumStaff();
|
||||
}
|
||||
|
||||
function getNumUsers(){
|
||||
return $this->getNumStaff();
|
||||
}
|
||||
|
||||
|
||||
function getSLAId(){
|
||||
return $this->ht['sla_id'];
|
||||
}
|
||||
|
||||
function getSLA(){
|
||||
|
||||
if(!$this->sla && $this->getSLAId())
|
||||
$this->sla=SLA::lookup($this->getSLAId());
|
||||
|
||||
return $this->sla;
|
||||
}
|
||||
|
||||
function getTemplateId() {
|
||||
return $this->ht['tpl_id'];
|
||||
}
|
||||
|
||||
function getTemplate() {
|
||||
|
||||
if(!$this->template && $this->getTemplateId())
|
||||
$this->template = Template::lookup($this->getTemplateId());
|
||||
|
||||
return $this->template;
|
||||
}
|
||||
|
||||
function getAutoRespEmail() {
|
||||
|
||||
if(!$this->autorespEmail && $this->ht['autoresp_email_id'] && ($email=Email::lookup($this->ht['autoresp_email_id'])))
|
||||
$this->autorespEmail=$email;
|
||||
else // Defualt to dept email if autoresp is not specified or deleted.
|
||||
$this->autorespEmail=$this->getEmail();
|
||||
|
||||
return $this->autorespEmail;
|
||||
}
|
||||
|
||||
function getEmailAddress() {
|
||||
if(($email=$this->getEmail()))
|
||||
return $email->getAddress();
|
||||
}
|
||||
|
||||
function getSignature() {
|
||||
return $this->ht['signature'];
|
||||
}
|
||||
|
||||
function canAppendSignature() {
|
||||
return ($this->getSignature() && $this->isPublic());
|
||||
}
|
||||
|
||||
function getManagerId(){
|
||||
return $this->ht['manager_id'];
|
||||
}
|
||||
|
||||
function getManager(){
|
||||
|
||||
if(!$this->manager && $this->getManagerId())
|
||||
$this->manager=Staff::lookup($this->getManagerId());
|
||||
|
||||
return $this->manager;
|
||||
}
|
||||
|
||||
function isPublic(){
|
||||
return ($this->ht['ispublic']);
|
||||
}
|
||||
|
||||
function autoRespONNewTicket(){
|
||||
return ($this->ht['ticket_auto_response']);
|
||||
}
|
||||
|
||||
function autoRespONNewMessage(){
|
||||
return ($this->ht['message_auto_response']);
|
||||
}
|
||||
|
||||
function noreplyAutoResp(){
|
||||
return ($this->ht['noreply_autoresp']);
|
||||
}
|
||||
|
||||
function getHashtable() {
|
||||
return $this->ht;
|
||||
}
|
||||
|
||||
function getInfo(){
|
||||
return $this->getHashtable();
|
||||
}
|
||||
|
||||
function update($vars,&$errors){
|
||||
|
||||
if($this->save($this->getId(),$vars,$errors)) {
|
||||
$this->reload();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function delete() {
|
||||
global $cfg;
|
||||
|
||||
if(!$cfg || $this->getId()==$cfg->getDefaultDeptId() || $this->getNumUsers())
|
||||
return 0;
|
||||
|
||||
$id=$this->getId();
|
||||
$sql='DELETE FROM '.DEPT_TABLE.' WHERE dept_id='.db_input($id).' LIMIT 1';
|
||||
if(db_query($sql) && ($num=db_affected_rows())){
|
||||
// DO SOME HOUSE CLEANING
|
||||
//Move tickets to default Dept. TODO: Move one ticket at a time and send alerts + log notes.
|
||||
db_query('UPDATE '.TICKET_TABLE.' SET dept_id='.db_input($cfg->getDefaultDeptId()).' WHERE dept_id='.db_input($id));
|
||||
//Move Dept members: This should never happen..since delete should be issued only to empty Depts...but check it anyways
|
||||
db_query('UPDATE '.STAFF_TABLE.' SET dept_id='.db_input($cfg->getDefaultDeptId()).' WHERE dept_id='.db_input($id));
|
||||
//make help topic using the dept default to default-dept.
|
||||
db_query('UPDATE '.TOPIC_TABLE.' SET dept_id='.db_input($cfg->getDefaultDeptId()).' WHERE dept_id='.db_input($id));
|
||||
|
||||
}
|
||||
|
||||
return $num;
|
||||
}
|
||||
|
||||
/*----Static functions-------*/
|
||||
function getIdByName($name) {
|
||||
$id=0;
|
||||
$sql ='SELECT dept_id FROM '.DEPT_TABLE.' WHERE dept_name='.db_input($name);
|
||||
if(($res=db_query($sql)) && db_num_rows($res))
|
||||
list($id)=db_fetch_row($res);
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
function lookup($id){
|
||||
return ($id && is_numeric($id) && ($dept = new Dept($id)) && $dept->getId()==$id)?$dept:null;
|
||||
}
|
||||
|
||||
function getNameById($id) {
|
||||
|
||||
if($id && ($dept=Dept::lookup($id)))
|
||||
$name= $dept->getName();
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
function getDefaultDeptName() {
|
||||
global $cfg;
|
||||
return ($cfg && $cfg->getDefaultDeptId() && ($name=Dept::getNameById($cfg->getDefaultDeptId())))?$name:null;
|
||||
}
|
||||
|
||||
function getDepartments( $publiconly=false) {
|
||||
|
||||
$depts=array();
|
||||
$sql ='SELECT dept_id, dept_name FROM '.DEPT_TABLE;
|
||||
if($publiconly)
|
||||
$sql.=' WHERE ispublic=1';
|
||||
|
||||
if(($res=db_query($sql)) && db_num_rows($res)) {
|
||||
while(list($id, $name)=db_fetch_row($res))
|
||||
$depts[$id] = $name;
|
||||
}
|
||||
|
||||
return $depts;
|
||||
}
|
||||
|
||||
function getPublicDepartments() {
|
||||
return self::getDepartments(true);
|
||||
}
|
||||
|
||||
function create($vars,&$errors) {
|
||||
return Dept::save(0,$vars,$errors);
|
||||
}
|
||||
|
||||
function save($id,$vars,&$errors) {
|
||||
global $cfg;
|
||||
|
||||
if($id && $id!=$vars['id'])
|
||||
$errors['err']='Missing or invalid Dept ID (internal error).';
|
||||
|
||||
if(!$vars['email_id'] || !is_numeric($vars['email_id']))
|
||||
$errors['email_id']='Email selection required';
|
||||
|
||||
if(!is_numeric($vars['tpl_id']))
|
||||
$errors['tpl_id']='Template selection required';
|
||||
|
||||
if(!$vars['name']){
|
||||
$errors['name']='Name required';
|
||||
}elseif(strlen($vars['name'])<4) {
|
||||
$errors['name']='Name is too short.';
|
||||
}elseif(($did=Dept::getIdByName($vars['name'])) && $did!=$id){
|
||||
$errors['name']='Department already exist';
|
||||
}
|
||||
|
||||
if(!$vars['ispublic'] && ($vars['id']==$cfg->getDefaultDeptId()))
|
||||
$errors['ispublic']='System default department can not be private';
|
||||
|
||||
if($errors) return false;
|
||||
|
||||
|
||||
$sql='SET updated=NOW() '
|
||||
.' ,ispublic='.db_input($vars['ispublic'])
|
||||
.' ,email_id='.db_input($vars['email_id'])
|
||||
.' ,tpl_id='.db_input($vars['tpl_id'])
|
||||
.' ,sla_id='.db_input($vars['sla_id'])
|
||||
.' ,autoresp_email_id='.db_input($vars['autoresp_email_id'])
|
||||
.' ,manager_id='.db_input($vars['manager_id']?$vars['manager_id']:0)
|
||||
.' ,dept_name='.db_input(Format::striptags($vars['name']))
|
||||
.' ,dept_signature='.db_input(Format::striptags($vars['signature']))
|
||||
.' ,ticket_auto_response='.db_input(isset($vars['ticket_auto_response'])?$vars['ticket_auto_response']:1)
|
||||
.' ,message_auto_response='.db_input(isset($vars['message_auto_response'])?$vars['message_auto_response']:1);
|
||||
|
||||
|
||||
if($id) {
|
||||
$sql='UPDATE '.DEPT_TABLE.' '.$sql.' WHERE dept_id='.db_input($id);
|
||||
if(db_query($sql) && db_affected_rows())
|
||||
return true;
|
||||
|
||||
$errors['err']='Unable to update '.Format::htmlchars($vars['name']).' Dept. Error occurred';
|
||||
|
||||
}else{
|
||||
$sql='INSERT INTO '.DEPT_TABLE.' '.$sql.',created=NOW()';
|
||||
if(db_query($sql) && ($id=db_insert_id()))
|
||||
return $id;
|
||||
|
||||
|
||||
$errors['err']='Unable to create department. Internal error';
|
||||
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
189
include/class.dispatcher.php
Normal file
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
/*********************************************************************
|
||||
class.dispatcher.php
|
||||
|
||||
Dispatcher that will read files with URL lists in them and attempt to
|
||||
match the URL requested to a function that should be invoked to handle
|
||||
the request.
|
||||
|
||||
Jared Hancock
|
||||
Copyright (c) 2006-2012 osTicket
|
||||
http://www.osticket.com
|
||||
|
||||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||||
See LICENSE.TXT for details.
|
||||
|
||||
vim: expandtab sw=4 ts=4 sts=4:
|
||||
**********************************************************************/
|
||||
|
||||
/**
|
||||
* URL resolver and dispatcher. It's meant to be quite lightweight, so the
|
||||
* functions aren't separated
|
||||
*/
|
||||
class Dispatcher {
|
||||
function Dispatcher($file=false) {
|
||||
$this->urls = array();
|
||||
$this->file = $file;
|
||||
}
|
||||
|
||||
function resolve($url, $args=null) {
|
||||
if ($this->file) { $this->lazy_load(); }
|
||||
# Support HTTP method emulation with the _method GET argument
|
||||
if (isset($_GET['_method'])) {
|
||||
$_SERVER['REQUEST_METHOD'] = strtoupper($_GET['method']);
|
||||
unset($_GET['_method']);
|
||||
}
|
||||
foreach ($this->urls as $matcher) {
|
||||
if ($matcher->matches($url)) {
|
||||
return $matcher->dispatch($url, $args);
|
||||
}
|
||||
}
|
||||
Http::response(400, "URL not supported");
|
||||
}
|
||||
/**
|
||||
* Returns the url for the given function and arguments (arguments
|
||||
* aren't declared, but will be handled
|
||||
*/
|
||||
function reverse($func) { }
|
||||
/**
|
||||
* Add the url to the list of supported URLs
|
||||
*/
|
||||
function append($url, $prefix=false) {
|
||||
if ($prefix) { $url->setPrefix($prefix); }
|
||||
array_push($this->urls, $url);
|
||||
}
|
||||
/**
|
||||
* Add the urls from another dispatcher onto this one
|
||||
*/
|
||||
function extend($dispatcher) {
|
||||
foreach ($dispatcher->urls as $url) { $this->append($url); }
|
||||
/* allow inlining / chaining */ return $this;
|
||||
}
|
||||
|
||||
/* static */ function include_urls($file, $absolute=false, $lazy=true) {
|
||||
if (!$absolute) {
|
||||
# Fetch the working path of the caller
|
||||
$bt = debug_backtrace();
|
||||
$file = dirname($bt[0]["file"]) . "/" . $file;
|
||||
}
|
||||
if ($lazy) return new Dispatcher($file);
|
||||
else return (include $file);
|
||||
}
|
||||
/**
|
||||
* The include_urls() method will create a new Dispatcher and set the
|
||||
* $this->file to where the file to be loaded is located. When this
|
||||
* dispatcher is first accessed, the file will be loaded.
|
||||
*/
|
||||
function lazy_load() {
|
||||
$this->extend(include $this->file);
|
||||
$this->file=false;
|
||||
}
|
||||
}
|
||||
|
||||
class UrlMatcher {
|
||||
function UrlMatcher($regex, $func, $args=false, $method=false) {
|
||||
# Add the slashes for the Perl syntax
|
||||
$this->regex = "@" . $regex . "@";
|
||||
$this->func = $func;
|
||||
$this->args = ($args) ? $args : array();
|
||||
$this->prefix = false;
|
||||
$this->method = $method;
|
||||
}
|
||||
|
||||
function setPrefix($prefix) { $this->prefix = $prefix; }
|
||||
|
||||
function matches($url) {
|
||||
if ($this->method && $_SERVER['REQUEST_METHOD'] != $this->method) {
|
||||
return false;
|
||||
}
|
||||
return preg_match($this->regex, $url, $this->matches) == 1;
|
||||
}
|
||||
|
||||
function dispatch($url, $prev_args=null) {
|
||||
# Remove named values from the match array
|
||||
$this->matches = array_flip(array_intersect(
|
||||
array_flip($this->matches), range(0,31)));
|
||||
if (@get_class($this->func) == "Dispatcher") {
|
||||
# Trim the leading match off the $url and call the
|
||||
# sub-dispatcher. This will be the case for lines in the URL
|
||||
# file like
|
||||
# url("^/blah", Dispatcher::include_urls("blah/urls.conf.php"))
|
||||
# Also, pass arguments matched so far (if any) to the receiving
|
||||
# resolve() method by merging the $prev_args into $this->matches
|
||||
# (excluding $this->matches[0], which is the matched URL at this
|
||||
# level)
|
||||
return $this->func->resolve(
|
||||
substr($url, strlen($this->matches[0])),
|
||||
array_merge(($prev_args) ? $prev_args : array(),
|
||||
array_slice($this->matches, 1)));
|
||||
} else {
|
||||
# Drop the first item of the matches array (which is the whole
|
||||
# matched url). Then merge in any initial arguments.
|
||||
array_shift($this->matches);
|
||||
# Prepend received arguments (from a parent Dispatcher). This is
|
||||
# different from the static args, which are postpended
|
||||
if (is_array($prev_args))
|
||||
$args = array_merge($prev_args, $this->matches);
|
||||
else $args = $this->matches;
|
||||
# Add in static args specified in the constructor
|
||||
$args = array_merge($args, $this->args);
|
||||
# Apply the $prefix given
|
||||
list($class, $func) = $this->apply_prefix();
|
||||
if ($class) {
|
||||
# Create instance of the class, which is the first item,
|
||||
# then call the method which is the second item
|
||||
$func = array(new $class, $func);
|
||||
}
|
||||
if (!is_callable($func))
|
||||
Http::response(500,
|
||||
'Dispatcher compile error. Function not callable');
|
||||
return call_user_func_array($func, $args);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* For the $prefix recieved by the constuctor, prepend it to the
|
||||
* received $class, if any, then make an import if necessary. Lastly,
|
||||
* return the appropriate $class, and $func that should be invoked to
|
||||
* dispatch the URL.
|
||||
*/
|
||||
function apply_prefix() {
|
||||
if (is_array($this->func)) { list($class, $func) = $this->func; }
|
||||
else { $func = $this->func; $class = ""; }
|
||||
$class = $this->prefix . $class;
|
||||
|
||||
if (strpos($class, ":")) {
|
||||
list($file, $class) = explode(":", $class, 2);
|
||||
include $file;
|
||||
}
|
||||
return array($class, $func);
|
||||
}
|
||||
}
|
||||
|
||||
function patterns($prefix) {
|
||||
$disp = new Dispatcher();
|
||||
for ($i=1, $k=func_num_args(); $i<$k; $i++) {
|
||||
# NOTE: that $prefix is added to each url rather than to the
|
||||
# dispatcher as a whole so that urls can be copied from one
|
||||
# dispatcher to another (via the ->extend() method) and
|
||||
# completely maintain their integrity
|
||||
$disp->append(func_get_arg($i), $prefix);
|
||||
}
|
||||
return $disp;
|
||||
}
|
||||
|
||||
function url($regex, $func, $args=false, $method=false) {
|
||||
return new UrlMatcher($regex, $func, $args, $method);
|
||||
}
|
||||
|
||||
function url_post($regex, $func, $args=false) {
|
||||
return url($regex, $func, $args, "POST");
|
||||
}
|
||||
|
||||
function url_get($regex, $func, $args=false) {
|
||||
return url($regex, $func, $args, "GET");
|
||||
}
|
||||
|
||||
function url_del($regex, $func, $args=false) {
|
||||
return url($regex, $func, $args, "DELETE");
|
||||
}
|
||||
?>
|
||||